Add ARCHITECTURE.md Part 1: complete map of the C program
Documents the entire Rogue 5.4.4 C codebase ahead of the Go port: program flow, source file inventory, the full type system (THING union, PLACE map, rooms, stats, obj_info, daemons), constants and flag bits, the macro layer, a census of every global variable, and a function-by-function catalog of all 36 source files.
This commit is contained in:
884
ARCHITECTURE.md
Normal file
884
ARCHITECTURE.md
Normal file
@@ -0,0 +1,884 @@
|
|||||||
|
# Rogue 5.4.4 — Architecture
|
||||||
|
|
||||||
|
This document has two parts:
|
||||||
|
|
||||||
|
- **Part 1 — The C Program**: a complete structural map of the classic C
|
||||||
|
codebase (every file, every function, every type, every global) as it exists
|
||||||
|
on the `modern-rogue` branch.
|
||||||
|
- **Part 2 — The Go Port Design**: the object-oriented architecture for the Go
|
||||||
|
port (`sneak.berlin/go/rogue`), including how every C construct maps onto Go
|
||||||
|
types and methods.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Part 1 — The C Program
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
Rogue 5.4.4 is a single-process, single-threaded, turn-based terminal game of
|
||||||
|
~16,800 lines of pre-C99 C across 36 files. Its architecture is that of a
|
||||||
|
classic 1980s BSD game:
|
||||||
|
|
||||||
|
- **All state is global.** ~120 global variables (defined in `extern.c`,
|
||||||
|
declared in `rogue.h`/`extern.h`) hold the entire game state: the player,
|
||||||
|
the level map, monster and object lists, UI state, and configuration.
|
||||||
|
- **One data type to rule them all.** A single C `union thing` (`THING`)
|
||||||
|
represents both creatures (player/monsters) and objects (weapons, potions,
|
||||||
|
...), discriminated by which union arm the code chooses to access. Doubly
|
||||||
|
linked lists of THINGs (via embedded `l_next`/`l_prev` pointers) form the
|
||||||
|
monster list, the level-object list, and every creature's pack.
|
||||||
|
- **The screen is the data structure.** Much game logic reads what character
|
||||||
|
is displayed at a map cell (via curses `mvinch` or the `places[]` shadow
|
||||||
|
map) to decide behavior — walls, doors, items, and monsters are identified
|
||||||
|
by their display glyph.
|
||||||
|
- **Turn engine via daemons and fuses.** A 20-slot table of function pointers
|
||||||
|
runs recurring per-turn callbacks ("daemons": healing, hunger, wandering
|
||||||
|
monsters) and one-shot countdowns ("fuses": potion effects wearing off),
|
||||||
|
each in a BEFORE or AFTER phase around the player's command.
|
||||||
|
- **Determinism by seeded LCG.** All randomness flows through `rnd()` using a
|
||||||
|
hand-rolled linear congruential generator (`seed = seed*11109+13849`), so a
|
||||||
|
given seed (dungeon number `dnum`) reproduces a dungeon exactly.
|
||||||
|
|
||||||
|
### Program flow
|
||||||
|
|
||||||
|
```
|
||||||
|
main()
|
||||||
|
├─ parse args/env (SEED, ROGUEOPTS, save file to restore, -s scores, -d death demo)
|
||||||
|
├─ init_probs/init_player/init_names/init_colors/init_stones/init_materials
|
||||||
|
├─ curses initscr(), setup() signals/tty
|
||||||
|
├─ new_level() # generate level 1
|
||||||
|
├─ start_daemon(runners/doctor/stomach/swander...), fuse(...)
|
||||||
|
└─ playit()
|
||||||
|
└─ while (playing)
|
||||||
|
command()
|
||||||
|
├─ do_daemons(BEFORE); do_fuses(BEFORE)
|
||||||
|
├─ status(); readchar() # input (with repeat counts, run prefixes)
|
||||||
|
├─ switch(ch) → do_move/search/quaff/read_scroll/wield/...
|
||||||
|
└─ if (after): do_daemons(AFTER); do_fuses(AFTER)
|
||||||
|
# the `runners` AFTER daemon moves all monsters
|
||||||
|
death()/total_winner()/quit() → score() → exit
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Source file inventory
|
||||||
|
|
||||||
|
| File | Lines | Role |
|
||||||
|
|---|---|---|
|
||||||
|
| `rogue.h` | 753 | Master header: all constants, macros, types, extern declarations |
|
||||||
|
| `extern.h` | 197 | Platform feature detection, mdport/mach_dep declarations |
|
||||||
|
| `score.h` | 26 | Scoreboard entry struct |
|
||||||
|
| `main.c` | 395 | Entry point, RNG, main loop, signals |
|
||||||
|
| `command.c` | 820 | Keyboard command dispatch |
|
||||||
|
| `io.c` | 277 | Message line, status line, input |
|
||||||
|
| `init.c` | 447 | Per-game randomization and data-table setup |
|
||||||
|
| `extern.c` | 391 | All global variable definitions + data tables |
|
||||||
|
| `new_level.c` | 231 | Level orchestration, item placement, treasure rooms |
|
||||||
|
| `rooms.c` | 472 | Room and maze generation, room enter/leave |
|
||||||
|
| `passages.c` | 424 | Corridor generation, passage numbering |
|
||||||
|
| `move.c` | 425 | Player movement, traps |
|
||||||
|
| `chase.c` | 541 | Monster AI (chase/flee/targeting), visibility |
|
||||||
|
| `monsters.c` | 252 | Monster creation, saving throws |
|
||||||
|
| `fight.c` | 686 | Combat resolution, special monster attacks |
|
||||||
|
| `things.c` | 713 | Item naming, random item creation, discoveries |
|
||||||
|
| `pack.c` | 503 | Inventory management, item selection UI |
|
||||||
|
| `potions.c` | 375 | 14 potion effects |
|
||||||
|
| `scrolls.c` | 329 | 18 scroll effects |
|
||||||
|
| `rings.c` | 204 | 14 ring types, wearing/removal |
|
||||||
|
| `sticks.c` | 431 | 14 wand/staff types, bolts |
|
||||||
|
| `weapons.c` | 288 | 9 weapon types, throwing |
|
||||||
|
| `armor.c` | 89 | 8 armor types, wearing |
|
||||||
|
| `misc.c` | 597 | look() display update, direction input, eat, level-up |
|
||||||
|
| `daemon.c` | 181 | Daemon/fuse scheduler |
|
||||||
|
| `daemons.c` | 295 | The 11 daemon/fuse callbacks |
|
||||||
|
| `list.c` | 113 | THING linked-list allocation |
|
||||||
|
| `rip.c` | 448 | Tombstone, victory, scoreboard display |
|
||||||
|
| `save.c` | 390 | Save/restore, XOR-encrypted I/O, score file I/O |
|
||||||
|
| `state.c` | 2134 | Hand-written serialization of every global |
|
||||||
|
| `options.c` | 501 | Options screen, ROGUEOPTS parsing |
|
||||||
|
| `wizard.c` | 284 | Debug/wizard commands, item identification |
|
||||||
|
| `mach_dep.c` | 457 | Score file locking, tty/signal setup, vestigial load checks |
|
||||||
|
| `mdport.c` | 1432 | Platform abstraction (the 900-line `md_readchar` keyboard decoder) |
|
||||||
|
| `xcrypt.c` | 707 | DES crypt(3) for the wizard password only |
|
||||||
|
| `vers.c` | 17 | Version string + XOR key strings |
|
||||||
|
|
||||||
|
## 3. Type system
|
||||||
|
|
||||||
|
### 3.1 `coord` — position
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct { int x; int y; } coord;
|
||||||
|
```
|
||||||
|
|
||||||
|
Used for every position: hero, monsters, room corners, exits, deltas.
|
||||||
|
|
||||||
|
### 3.2 `str_t` — strength
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef unsigned int str_t; /* strength 3..31 */
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 `struct stats` — fighting statistics (player and monsters)
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct stats {
|
||||||
|
str_t s_str; /* strength (player: 3-31; monsters: fixed 10) */
|
||||||
|
int s_exp; /* experience value/points */
|
||||||
|
int s_lvl; /* level of mastery */
|
||||||
|
int s_arm; /* armor class (lower = better) */
|
||||||
|
int s_hpt; /* current hit points */
|
||||||
|
char s_dmg[13]; /* damage dice string, e.g. "1x4/1x2" */
|
||||||
|
int s_maxhp; /* max hit points */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 `union thing` (`THING`) — the universal entity
|
||||||
|
|
||||||
|
The central type: a union of a creature arm (`_t`) and an object arm (`_o`),
|
||||||
|
with the two link pointers common to both. Access is via field macros
|
||||||
|
(`t_pos` → `_t._t_pos`, `o_type` → `_o._o_type`, ...).
|
||||||
|
|
||||||
|
```c
|
||||||
|
union thing {
|
||||||
|
struct { /* creature (player/monster) */
|
||||||
|
union thing *_l_next, *_l_prev; /* list links */
|
||||||
|
coord _t_pos; /* position */
|
||||||
|
bool _t_turn; /* slowed: is it our turn to move */
|
||||||
|
char _t_type; /* monster letter 'A'-'Z'; player '@' */
|
||||||
|
char _t_disguise; /* what a xeroc looks like */
|
||||||
|
char _t_oldch; /* map char under the monster */
|
||||||
|
coord *_t_dest; /* chase target (points at hero or gold) */
|
||||||
|
short _t_flags; /* ISRUN|ISHELD|ISINVIS|... state bits */
|
||||||
|
struct stats _t_stats;
|
||||||
|
struct room *_t_room; /* room the creature is in */
|
||||||
|
union thing *_t_pack; /* carried items (list) */
|
||||||
|
int _t_reserved; /* save-file fixup index */
|
||||||
|
} _t;
|
||||||
|
struct { /* object */
|
||||||
|
union thing *_l_next, *_l_prev; /* list links */
|
||||||
|
int _o_type; /* POTION/SCROLL/WEAPON/... glyph */
|
||||||
|
coord _o_pos; /* where it lies on the floor */
|
||||||
|
char *_o_text; /* scroll gibberish text */
|
||||||
|
int _o_launch; /* weapon needed to launch it */
|
||||||
|
char _o_packch; /* inventory letter */
|
||||||
|
char _o_damage[8]; /* wield damage "3x4" */
|
||||||
|
char _o_hurldmg[8]; /* thrown damage */
|
||||||
|
int _o_count; /* stack count */
|
||||||
|
int _o_which; /* subtype index (P_HEALING, MACE...) */
|
||||||
|
int _o_hplus, _o_dplus; /* hit/damage enchantment */
|
||||||
|
int _o_arm; /* armor class / charges / gold value (o_charges, o_goldval alias) */
|
||||||
|
int _o_flags; /* ISCURSED|ISKNOW|... */
|
||||||
|
int _o_group; /* stack group id */
|
||||||
|
char *_o_label; /* player-applied name */
|
||||||
|
} _o;
|
||||||
|
};
|
||||||
|
typedef union thing THING;
|
||||||
|
```
|
||||||
|
|
||||||
|
Key consequences the port must respect:
|
||||||
|
|
||||||
|
- `winat(y,x)` display logic: a monster's *disguise* is shown, not its type.
|
||||||
|
- `o_arm` is overloaded three ways: armor class (armor), charge count
|
||||||
|
(sticks, via `o_charges`), gold value (gold piles, via `o_goldval`).
|
||||||
|
- `t_dest` aliases *other entities' interiors*: it points at `hero`
|
||||||
|
(player position), a room's `r_gold` coordinate, or another monster's
|
||||||
|
`t_pos`. The save format encodes this as (kind, index) pairs.
|
||||||
|
|
||||||
|
### 3.5 `PLACE` — one map cell, and the `places[]` map
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
char p_ch; /* base map character (wall, floor, door, item glyph) */
|
||||||
|
char p_flags; /* F_PASS|F_SEEN|F_DROPPED|F_LOCKED|F_REAL|F_PNUM|F_TMASK */
|
||||||
|
THING *p_monst; /* monster standing here, if any */
|
||||||
|
} PLACE;
|
||||||
|
|
||||||
|
PLACE places[MAXLINES*MAXCOLS]; /* column-major: INDEX(y,x) = &places[(x<<5)+y] */
|
||||||
|
```
|
||||||
|
|
||||||
|
Access macros: `chat(y,x)` (char), `flat(y,x)` (flags), `moat(y,x)` (monster),
|
||||||
|
`winat(y,x)` (what's visible: monster disguise or map char). Note the
|
||||||
|
column-major `(x << 5) + y` layout (32 = MAXLINES rows per column).
|
||||||
|
|
||||||
|
### 3.6 `struct room` — rooms and passages
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct room {
|
||||||
|
coord r_pos; /* upper-left corner */
|
||||||
|
coord r_max; /* size */
|
||||||
|
coord r_gold; /* gold location */
|
||||||
|
int r_goldval;
|
||||||
|
short r_flags; /* ISDARK | ISGONE | ISMAZE */
|
||||||
|
int r_nexits;
|
||||||
|
coord r_exit[12]; /* door positions */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`rooms[MAXROOMS=9]` on a 3×3 grid; `passages[MAXPASS=13]` reuses the same
|
||||||
|
struct for corridor networks (exits = doors touched, flags = ISGONE|ISDARK).
|
||||||
|
|
||||||
|
### 3.7 `struct monster` — bestiary entry
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct monster {
|
||||||
|
char *m_name; /* "aquator", "bat", ... */
|
||||||
|
int m_carry; /* % chance of carrying an item */
|
||||||
|
short m_flags; /* ISMEAN|ISFLY|ISREGEN|ISGREED|ISINVIS|CANHUH */
|
||||||
|
struct stats m_stats; /* starting stats incl. damage string */
|
||||||
|
};
|
||||||
|
extern struct monster monsters[26]; /* indexed by letter - 'A' */
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.8 `struct obj_info` — item class descriptor
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct obj_info {
|
||||||
|
char *oi_name; /* true name ("healing", "mace") */
|
||||||
|
int oi_prob; /* generation probability (converted to cumulative) */
|
||||||
|
int oi_worth; /* score value */
|
||||||
|
char *oi_guess; /* player's "call it" name */
|
||||||
|
bool oi_know; /* has the player identified this type */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Seven tables: `things[7]` (category weights), `pot_info[14]`, `scr_info[18]`,
|
||||||
|
`ring_info[14]`, `ws_info[14]`, `weap_info[9+1]`, `arm_info[8]`.
|
||||||
|
|
||||||
|
### 3.9 `struct delayed_action` — daemon/fuse slot
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct delayed_action {
|
||||||
|
int d_type; /* EMPTY / BEFORE / AFTER */
|
||||||
|
void (*d_func)(int); /* callback — identity of the entry */
|
||||||
|
int d_arg;
|
||||||
|
int d_time; /* DAEMON sentinel (-1) or countdown */
|
||||||
|
} d_list[MAXDAEMONS=20];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.10 Others
|
||||||
|
|
||||||
|
- `struct h_list { char h_ch; char *h_desc; bool h_print; }` — help table rows.
|
||||||
|
- `STONE { char *st_name; int st_value; }` — ring stones with worth.
|
||||||
|
- `SCORE` (score.h) — scoreboard entry (uid, score, flags, monster, name, level, time).
|
||||||
|
- `OPTION` (options.c) — option descriptor with get/put function pointers.
|
||||||
|
- `PACT` (potions.c) — potion type → flag/fuse-callback/duration.
|
||||||
|
- `SPOT` (rooms.c) — maze generation cell.
|
||||||
|
|
||||||
|
## 4. Constants and enumerations
|
||||||
|
|
||||||
|
All are `#define`s in rogue.h:
|
||||||
|
|
||||||
|
- **Geometry:** NUMLINES 24, NUMCOLS 80, MAXLINES 32, MAXCOLS 80, STATLINE 23.
|
||||||
|
- **Limits:** MAXROOMS 9, MAXTHINGS 9, MAXOBJ 9, MAXPACK 23, MAXTRAPS 10,
|
||||||
|
MAXPASS 13, MAXDAEMONS 20, AMULETLEVEL 26, NUMTHINGS 7, BORE_LEVEL 50.
|
||||||
|
- **Object type glyphs (`o_type`/map chars):** PASSAGE `#`, DOOR `+`, FLOOR
|
||||||
|
`.`, PLAYER `@`, TRAP `^`, STAIRS `%`, GOLD `*`, POTION `!`, SCROLL `?`,
|
||||||
|
MAGIC `$`, FOOD `:`, WEAPON `)`, ARMOR `]`, AMULET `,`, RING `=`, STICK `/`;
|
||||||
|
pseudo-types CALLABLE -1, R_OR_S -2 for item-prompt filters.
|
||||||
|
- **Subtype enums:** 8 traps (T_DOOR..T_MYST), 14 potions (P_*), 18 scrolls
|
||||||
|
(S_*), 9 weapons (MACE..SPEAR, +FLAME pseudo), 8 armors (LEATHER..PLATE_MAIL),
|
||||||
|
14 rings (R_*), 14 sticks (WS_*).
|
||||||
|
- **Creature flag bits:** CANHUH, CANSEE, ISBLIND, ISCANC, ISLEVIT, ISFOUND,
|
||||||
|
ISGREED, ISHASTE, ISTARGET, ISHELD, ISHUH, ISINVIS, ISMEAN, ISHALU, ISREGEN,
|
||||||
|
ISRUN, SEEMONST, ISFLY, ISSLOW (note deliberate collisions: ISCANC=ISLEVIT,
|
||||||
|
ISMEAN=ISHALU, SEEMONST=ISFLY — one set applies to monsters, other to hero).
|
||||||
|
- **Object flag bits:** ISCURSED, ISKNOW, ISMISL, ISMANY, ISFOUND (shared),
|
||||||
|
ISPROT.
|
||||||
|
- **Room flags:** ISDARK, ISGONE, ISMAZE.
|
||||||
|
- **Place flags:** F_PASS 0x80, F_SEEN 0x40, F_DROPPED/F_LOCKED 0x20,
|
||||||
|
F_REAL 0x10, F_PNUM 0x0f (passage number), F_TMASK 0x07 (trap type).
|
||||||
|
- **Durations/tuning:** HEALTIME 30, HUHDURATION 20, SEEDURATION 850,
|
||||||
|
HUNGERTIME 1300, MORETIME 150, STOMACHSIZE 2000, STARVETIME 850,
|
||||||
|
BOLT_LENGTH 6, LAMPDIST 3, BEARTIME/SLEEPTIME/HOLDTIME/WANDERTIME/BEFORE/
|
||||||
|
AFTER as `spread()` randomized durations.
|
||||||
|
- **Saving throws:** VS_POISON 0, VS_PARALYZATION 0, VS_DEATH 0, VS_BREATH 2,
|
||||||
|
VS_MAGIC 3.
|
||||||
|
- **Get-function returns:** NORM 0, QUIT 1, MINUS 2. **Inventory styles:**
|
||||||
|
INV_OVER 0, INV_SLOW 1, INV_CLEAR 2.
|
||||||
|
|
||||||
|
## 5. The macro layer
|
||||||
|
|
||||||
|
rogue.h defines a thick macro layer the port must translate to methods:
|
||||||
|
|
||||||
|
| Macro | Expansion | Port meaning |
|
||||||
|
|---|---|---|
|
||||||
|
| `when` / `otherwise` / `until(e)` | `break;case` / `break;default` / `while(!e)` | plain Go control flow |
|
||||||
|
| `hero` | `player.t_pos` | `g.Player.Pos` |
|
||||||
|
| `pstats` | `player.t_stats` | `g.Player.Stats` |
|
||||||
|
| `pack` | `player.t_pack` | `g.Player.Pack` |
|
||||||
|
| `proom` | `player.t_room` | `g.Player.Room` |
|
||||||
|
| `max_hp` | `player.t_stats.s_maxhp` | `g.Player.Stats.MaxHP` |
|
||||||
|
| `on(thing,flag)` | flag bit test | `t.Flags.Has(...)` |
|
||||||
|
| `INDEX/chat/flat/moat(y,x)` | places[] cell access | `g.Level.At(p).Ch/Flags/Monster` |
|
||||||
|
| `winat(y,x)` | monster disguise or map char | `g.Level.VisibleChar(p)` |
|
||||||
|
| `ce(a,b)` | coord equality | `a == b` (value type) |
|
||||||
|
| `ISRING(h,r)` / `ISWEARING(r)` | ring-worn tests | `g.Player.IsWearing(ring)` |
|
||||||
|
| `ISMULT(type)` | stackable type test | method on ObjType |
|
||||||
|
| `GOLDCALC` | `rnd(50+10*level)+2` | method on Level |
|
||||||
|
| `RN` | LCG step | `Rng.Next()` |
|
||||||
|
| `DISTANCE/dist` | squared distance | geometry helper |
|
||||||
|
| `attach/detach/free_list` | list ops via `&list` | slice operations |
|
||||||
|
|
||||||
|
## 6. Global variable census
|
||||||
|
|
||||||
|
Everything below lives in `extern.c` (game state) or as file-scope statics
|
||||||
|
(module state). In the Go port **all of these become fields of the
|
||||||
|
`RogueGame` struct** (user directive: no package-level globals).
|
||||||
|
|
||||||
|
- **Player & world:** `player` (THING), `places[]`, `rooms[9]`, `passages[13]`,
|
||||||
|
`mlist`, `lvl_obj`, `stairs`, `level`, `max_level`, `purse`, `amulet`,
|
||||||
|
`cur_armor`, `cur_weapon`, `cur_ring[2]`, `food_left`, `hungry_state`,
|
||||||
|
`ntraps`, `max_stats`, `oldpos`, `oldrp`, `delta`, `take`.
|
||||||
|
- **RNG/identity:** `seed`, `dnum`, `whoami`, `fruit`, `home`, `file_name`.
|
||||||
|
- **UI/messaging:** `huh`, `prbuf`, `mpos`, `msgbuf`+`newpos` (io.c statics),
|
||||||
|
`hw`, status-line shadow statics (io.c), discovery pagination statics
|
||||||
|
(things.c).
|
||||||
|
- **Command engine:** `count`, `after`, `again`, `door_stop`, `firstmove`,
|
||||||
|
`running`, `runch`, `last_comm`/`last_dir`/`last_pick` (+ `l_*` backups),
|
||||||
|
`no_command`, `no_move`, `quiet`, `to_death`, `kamikaze`, `max_hit`,
|
||||||
|
`has_hit`, `move_on`, `dir_ch`, `countch/direction/newcount` (command.c
|
||||||
|
statics).
|
||||||
|
- **Options:** `terse`, `fight_flush`, `jump`, `see_floor`, `passgo`,
|
||||||
|
`tombstone`, `inv_type`, `inv_describe`, `lower_msg`, `msg_esc`,
|
||||||
|
`save_msg`, `q_comm`, `stat_msg`.
|
||||||
|
- **Item identity tables:** `p_colors[]`, `s_names[]`, `r_stones[]`,
|
||||||
|
`ws_made[]`, `ws_type[]`, `a_class[]`, 7 obj_info tables, `pack_used[26]`,
|
||||||
|
`group`, `inpack`, `n_objs`, `no_food`.
|
||||||
|
- **Daemons:** `d_list[20]`, `between` (daemons.c static).
|
||||||
|
- **Scores/system:** `scoreboard`, `noscore`, `lastscore`, `numscores`,
|
||||||
|
`allscore`, `wizard`, `playing`, `in_shell`, `got_ltc`, `orig_dsusp`,
|
||||||
|
`seenstairs`, `vf_hit`, `e_levels[]`.
|
||||||
|
- **Static name data (immutable):** `monsters[26]`, `rainbow[]`, `stones[]`,
|
||||||
|
`wood[]`, `metal[]`, `sylls[]`, `inv_t_name[]`, `tr_name[]`, `helpstr[]`,
|
||||||
|
`release`, `encstr`, `statlist`, `version`, `h_names[]`/`m_names[]`
|
||||||
|
(fight.c), `init_dam[]` (weapons.c), `lvl_mons[]`/`wand_mons[]`
|
||||||
|
(monsters.c), `uses[]` (rings.c), `p_actions[]` (potions.c).
|
||||||
|
|
||||||
|
## 7. Function catalog by file
|
||||||
|
|
||||||
|
### main.c (396 lines) — Entry point and main game loop control
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `main` | `int main(int argc, char **argv, char **envp)` | Entry point; initializes game state, terminal, initializes all subsystems (probabilities, player, names, colors, stones, materials), sets up windows and daemons, then calls `playit()`. Sets globals: `wizard`, `whoami`, `dnum`, `seed`, `file_name`, `home` |
|
||||||
|
| `endit` | `void endit(int sig)` | Signal handler for abnormal termination; calls `fatal()` with goodbye message |
|
||||||
|
| `fatal` | `void fatal(char *s)` | Prints message to status line, refreshes screen, ends curses, and exits |
|
||||||
|
| `rnd` | `int rnd(int range)` | Returns random number 0 to range-1 using macro `RN`; core randomness function |
|
||||||
|
| `roll` | `int roll(int number, int sides)` | Simulates rolling dice; calls `rnd()` for each die |
|
||||||
|
| `tstp` | `void tstp(int ignored)` | Signal handler for SIGTSTP/SIGCONT; saves cursor position, suspends curses, calls `md_tstpsignal()` and `md_tstpresume()`, restores screen state |
|
||||||
|
| `playit` | `void playit()` | Main game loop; runs daemons/fuses before and after each turn, reads `ROGUEOPTS` to set options, loops on `playing` flag calling `command()` |
|
||||||
|
| `quit` | `void quit(int sig)` | Confirm quit prompt; on 'y' displays final score and calls `score()`, reads/writes globals `purse`, `q_comm`, `count`, `to_death` |
|
||||||
|
| `leave` | `void leave(int sig)` | Quick exit during error; ends curses and exits cleanly |
|
||||||
|
| `shell` | `void shell()` | Shell escape; saves cursor, ends curses, calls `md_shellescape()`, restores curses; reads/writes global `in_shell` |
|
||||||
|
| `my_exit` | `void my_exit(int st)` | Proper exit wrapper; calls `resetltchars()` then `exit()` |
|
||||||
|
|
||||||
|
**Notes:** Globals initialized here from environment: `whoami`, `dnum`, `seed`, `file_name`, `home`. Signal handlers registered: `endit()`, `tstp()`, `quit()`, `leave()`. Heavy curses use (`initscr()`, `endwin()`, `newwin()`). Platform shims: `md_init()`, `md_normaluser()`, `md_tstpsignal()`, `md_tstpresume()`.
|
||||||
|
|
||||||
|
### command.c (821 lines) — User command processing and game actions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `command` | `void command()` | Main command dispatcher; reads user input, handles counts/prefixes (digits for repeat), processes movement (hjklyubn), items (pick, drop, quaff, read, eat, wield, wear, take_off, ring_on, ring_off), special commands (search, help, identify, etc.). Uses static variables `countch`, `direction`, `newcount`. Handles daemons/fuses before/after. Modifies globals: `running`, `count`, `after`, `to_death`, `noscore`, `wizard`, `last_comm`, `last_dir`, etc. |
|
||||||
|
| `illcom` | `void illcom(int ch)` | Error handler for illegal commands; displays message via `msg()`, modifies `save_msg`, `count` |
|
||||||
|
| `search` | `void search()` | Search current square and adjacent 8; finds hidden doors/traps/passages with probability adjusted for hallucination/blindness; modifies `running`, `count` on discovery |
|
||||||
|
| `help` | `void help()` | Displays command help; reads from `helpstr[]` array, uses `hw` window for full help display |
|
||||||
|
| `identify` | `void identify()` | Identify objects/monsters by character; uses local `ident_list[]` and `monsters[]` array |
|
||||||
|
| `d_level` | `void d_level()` | Go down stairs; checks `levit_check()`, verifies stairs at current position, increments `level`, calls `new_level()` |
|
||||||
|
| `u_level` | `void u_level()` | Go up stairs; checks for stairs, checks `amulet` flag, decrements `level`, checks win condition `level==0`, calls `new_level()` |
|
||||||
|
| `levit_check` | `bool levit_check()` | Check levitation status; returns TRUE if player has ISLEVIT flag (and messages that you float up) |
|
||||||
|
| `call` | `void call()` | Name/rename items; calls `get_item()`, prompts for new name, allocates memory via `malloc()`, updates `oi_guess` in object info structures |
|
||||||
|
| `current` | `void current(THING *cur, char *how, char *where)` | Print currently equipped item (weapon/armor/rings); formats output using `inv_name()` |
|
||||||
|
|
||||||
|
**Notes:** Static variables `countch`, `direction`, `newcount` in `command()` track repeat count state. Uses `when` macro for switch cases. Wizard mode (MASTER) adds debug commands (Ctrl-key commands: create object, show map, teleport, etc.). Command parsing via nested switch statements. Uses `readchar()` for input, `msg()`/`addmsg()` for output.
|
||||||
|
|
||||||
|
### io.c (278 lines) — Input/output and terminal display
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `msg` | `int msg(char *fmt, ...)` | Display message at top of screen; variadic wrapper using `va_list`, calls `doadd()`, `endmsg()`. Returns ESCAPE or ~ESCAPE. Clears line if format is empty string |
|
||||||
|
| `addmsg` | `void addmsg(char *fmt, ...)` | Append to current message buffer; variadic, calls `doadd()` |
|
||||||
|
| `endmsg` | `int endmsg()` | Finish message display; handles "--More--" continuation, auto-capitalizes first character unless `lower_msg`, saves message to `huh` for redisplay |
|
||||||
|
| `doadd` | `void doadd(char *fmt, va_list args)` | Internal: vsprintf into message buffer; checks overflow, concatenates to `msgbuf` |
|
||||||
|
| `step_ok` | `int step_ok(int ch)` | Check if terrain character allows stepping; returns FALSE for walls/space, TRUE for floor/doors/items (non-alpha chars) |
|
||||||
|
| `readchar` | `char readchar()` | Read character with error checking; calls `md_readchar()`, intercepts Ctrl-C (sends to `quit()`) |
|
||||||
|
| `status` | `void status()` | Display status line at STATLINE; shows level/gold/HP/armor/strength/experience/hunger; uses static tracking variables (`s_hp`, `s_pur`, etc.) to avoid redundant redraws |
|
||||||
|
| `wait_for` | `void wait_for(int ch)` | Block until specific character typed (or newline if ch is newline) |
|
||||||
|
| `show_win` | `void show_win(char *message)` | Display message in `hw` window and wait for space; used for full-screen displays |
|
||||||
|
|
||||||
|
**Notes:** Static `msgbuf[2*MAXMSG+1]`, `newpos` track accumulating multi-part messages (`addmsg`/`msg`/`endmsg` protocol). `huh[]` global stores last message for the `Ctrl-R` repeat command. Status line optimized with 8 static shadow variables to minimize terminal writes.
|
||||||
|
|
||||||
|
### init.c (448 lines) — Initialization of game state and object tables
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `init_player` | `void init_player()` | Roll up player character; copies `max_stats` to `pstats`, creates starting items (food, ring mail +1, mace +1/+1, bow +1, 25-39 arrows), initializes `food_left`, sets `cur_armor` and `cur_weapon` |
|
||||||
|
| `init_colors` | `void init_colors()` | Randomize potion colors; assigns `p_colors[]` randomly from `rainbow[]` list, no duplicates |
|
||||||
|
| `init_names` | `void init_names()` | Generate random scroll names from `sylls[]` syllable fragments; stores mallocated names in `s_names[]` |
|
||||||
|
| `init_stones` | `void init_stones()` | Randomize ring stones; assigns `r_stones[]` from `stones[]` array (no duplicates), adds stone value to `ring_info[].oi_worth` |
|
||||||
|
| `init_materials` | `void init_materials()` | Initialize wand/staff materials; randomly picks from `metal[]` (wand) or `wood[]` (staff), sets `ws_type[]` and `ws_made[]` |
|
||||||
|
| `sumprobs` | `void sumprobs(struct obj_info *info, int bound)` | Converts item probability arrays to cumulative distributions (running sum) |
|
||||||
|
| `init_probs` | `void init_probs()` | Calls `sumprobs()` for all seven obj_info tables |
|
||||||
|
| `badcheck` | `void badcheck(char *name, struct obj_info *info, int bound)` | (MASTER) Verifies probabilities sum to 100; aborts with message if not |
|
||||||
|
| `pick_color` | `char *pick_color(char *col)` | Returns given color, or a random one if hero is hallucinating |
|
||||||
|
|
||||||
|
**Notes:** Static data defined here: `rainbow[]` (27 colors) + `cNCOLORS`, `sylls[]` (syllables), `stones[]` (26 STONE entries with worth) + `cNSTONES`, `wood[]` (33 woods) + `cNWOOD`, `metal[]` (22 metals) + `cNMETAL`. The name arrays `p_colors[]`, `s_names[]`, `r_stones[]`, `ws_type[]`, `ws_made[]` are defined in extern.c but populated here at game start (per-game randomization of item appearance).
|
||||||
|
|
||||||
|
### extern.c (392 lines) — Global variable definitions
|
||||||
|
|
||||||
|
**Boolean flags:** `after` (want after-daemons), `again` (repeat last command), `seenstairs`, `amulet` (found the Amulet), `door_stop` (stop running at doors), `fight_flush` (flush typeahead on fight), `firstmove`, `got_ltc`, `has_hit`, `in_shell`, `inv_describe`, `jump` (show running as jumps), `kamikaze`, `lower_msg`, `move_on`, `msg_esc`, `passgo` (follow passage turns), `playing`, `q_comm`, `running`, `save_msg`, `see_floor`, `stat_msg`, `terse`, `to_death`, `tombstone`, `wizard` (MASTER), `pack_used[26]`.
|
||||||
|
|
||||||
|
**Char/string state:** `dir_ch`, `file_name[MAXSTR]`, `huh[MAXSTR]` (last message), `prbuf[2*MAXSTR]` (sprintf scratch), `runch`, `take`, `whoami[MAXSTR]`, `fruit[MAXSTR]` ("slime-mold"), `home[MAXSTR]`, `l_last_comm`, `l_last_dir`, `last_comm`, `last_dir`; appearance-name arrays `p_colors[MAXPOTIONS]`, `r_stones[MAXRINGS]`, `s_names[MAXSCROLLS]`, `ws_made[MAXSTICKS]`, `ws_type[MAXSTICKS]`; fixed string tables `inv_t_name[]` (Overwrite/Slow/Clear), `tr_name[]` (8 trap names), `release` (version).
|
||||||
|
|
||||||
|
**Int state:** `n_objs`, `ntraps`, `hungry_state` (0-3), `inpack`, `inv_type`, `level`, `max_hit`, `max_level`, `mpos` (cursor pos on msg line), `no_food`, `count` (command repeat), `food_left`, `lastscore`, `no_command` (turns asleep), `no_move` (turns held), `noscore`, `purse` (gold), `quiet` (rest turns), `vf_hit` (flytrap hits), `dnum` (dungeon number), `seed` (RNG seed), `numscores`, `a_class[MAXARMORS]` (armor class table: 8 5 6 4 3 3 4 3... actually per-type base AC), `e_levels[]` (experience thresholds: 10 20 40 80 ... doubling to level 19), `orig_dsusp`.
|
||||||
|
|
||||||
|
**Coords/structural:** `delta` (last direction), `oldpos`, `stairs`; `places[MAXLINES*MAXCOLS]` (the level map: PLACE = ch, flags, monster ptr); `player` (THING); `cur_armor`, `cur_ring[2]`, `cur_weapon`, `l_last_pick`, `last_pick` (THING ptrs); `lvl_obj` (level object list), `mlist` (monster list).
|
||||||
|
|
||||||
|
**Tables:** `rooms[MAXROOMS]`, `passages[MAXPASS]` (12 passage "rooms"), `max_stats` (initial player stats: str 16, 0 exp, lvl 1, ac 10, 12 hp, "1x4"), `oldrp`, `monsters[26]` (full A-Z monster table with names, carry %, flags, stats), 7 `obj_info` tables: `things[NUMTHINGS]` (potion 26%, scroll 36%, food 16%, weapon 7%, armor 7%, ring 4%, stick 4%), `pot_info[MAXPOTIONS]`, `scr_info[MAXSCROLLS]`, `ring_info[MAXRINGS]`, `ws_info[MAXSTICKS]`, `weap_info[MAXWEAPONS+1]`, `arm_info[MAXARMORS]` — each entry: name, probability, worth, guess ptr, know flag. `helpstr[]` (help text). `hw` (WINDOW* scratch), `scoreboard` (FILE*).
|
||||||
|
|
||||||
|
**Notes:** Pure data file, no functions. The `monsters[]` table is the canonical A-Z bestiary (Aquator, Bat, Centaur, Dragon, Emu, Venus Flytrap, Griffin, Hobgoblin, Ice monster, Jabberwock, Kestrel, Leprechaun, Medusa, Nymph, Orc, Phantom, Quagga, Rattlesnake, Snake, Troll, Black unicorn, Vampire, Wraith, Xeroc, Yeti, Zombie) with per-monster carry probability, flag word (ISMEAN/ISFLY/ISREGEN/ISGREED/ISINVIS/CANHUH), and stats (exp, level, AC, HP dice, damage strings).
|
||||||
|
|
||||||
|
### mach_dep.c (458 lines) — Installation and machine-dependent setup
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `init_check` | `void init_check()` | Check if system load/user count too high (compile-time optional; mostly vestigial) |
|
||||||
|
| `open_score` | `void open_score()` | Open score file for read/write; creates if missing; sets global `scoreboard` FILE* |
|
||||||
|
| `setup` | `void setup()` | Install signal handlers (`md_onsignal_autosave`), start checkout timer, set curses raw/noecho modes, fetch local tty chars |
|
||||||
|
| `getltchars` | `void getltchars()` | Save original tty dsusp char for later restore; sets `got_ltc`, `orig_dsusp` |
|
||||||
|
| `resetltchars` | `void resetltchars()` | Restore original tty chars (on exit/shell) |
|
||||||
|
| `playltchars` | `void playltchars()` | Set tty chars to in-game values |
|
||||||
|
| `start_score` | `void start_score()` | Stop checkout timer before scoring (vestigial) |
|
||||||
|
| `is_symlink` | `bool is_symlink(char *sp)` | lstat() check whether path is a symbolic link (save-file anti-cheat) |
|
||||||
|
| `too_much`/`author`/`checkout`/`chmsg`/`ucount` | various | Compile-time-guarded system-load limiting and author-exemption checks (vestigial on modern systems) |
|
||||||
|
| `lock_sc` | `bool lock_sc()` | Lock score file via lock file with retry/prompt and stale-lock aging |
|
||||||
|
| `unlock_sc` | `void unlock_sc()` | Release score file lock |
|
||||||
|
| `flush_type` | `void flush_type()` | Flush keyboard typeahead buffer (`flushinp()`) |
|
||||||
|
|
||||||
|
**Notes:** Almost everything here is either curses/tty plumbing or vestigial 1980s multi-user courtesy features. A Go port needs only: score-file open/lock (use `os.OpenFile` + advisory locking or just best-effort), signal handling (`os/signal`), and typeahead flush (tcell handles).
|
||||||
|
|
||||||
|
### mdport.c (1433 lines) — Platform abstraction layer for Unix/Windows/DOS
|
||||||
|
|
||||||
|
All functions are platform shims replaceable with Go stdlib:
|
||||||
|
|
||||||
|
| Function | Go equivalent |
|
||||||
|
|---|---|
|
||||||
|
| `md_init` | none needed (tcell handles terminal init) |
|
||||||
|
| `md_onsignal_default/exit/autosave` | `os/signal.Notify` |
|
||||||
|
| `md_hasclreol`, `md_putchar`, `md_raw_standout/standend` | tcell rendering |
|
||||||
|
| `md_unlink`, `md_unlink_open_file`, `md_chmod` | `os.Remove`, `os.Chmod` |
|
||||||
|
| `md_normaluser` | omit (no setuid in Go port) |
|
||||||
|
| `md_getuid`, `md_getpid` | `os.Getuid()`, `os.Getpid()` |
|
||||||
|
| `md_getusername`, `md_getrealname` | `os/user.Current()` |
|
||||||
|
| `md_gethomedir` | `os.UserHomeDir()` |
|
||||||
|
| `md_sleep` | `time.Sleep` |
|
||||||
|
| `md_getshell`, `md_shellescape` | `os/exec` (or omit shell escape) |
|
||||||
|
| `md_crypt`, `md_getpass` | not needed (wizard password via env; no crypt) |
|
||||||
|
| `md_erasechar`, `md_killchar`, `md_dsuspchar`, `md_setdsuspchar`, `md_suspchar` | tcell key events |
|
||||||
|
| `md_readchar` | tcell `PollEvent` (the 900-line escape-sequence state machine disappears entirely) |
|
||||||
|
| `md_loadav`, `md_ignoreallsignals`, `md_tstp*`, checkout timers | omit / `os/signal` |
|
||||||
|
|
||||||
|
**Notes:** The dominant complexity is `md_readchar()` — a 900-line keypad/escape-sequence decoder state machine for a dozen terminal types. tcell's event model replaces all of it. Nothing in this file needs a line-by-line port.
|
||||||
|
|
||||||
|
### vers.c (18 lines) — Version and build information
|
||||||
|
|
||||||
|
| Variable | Value | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `release` | "5.4.4" | Version number string (also written into save files for validation) |
|
||||||
|
| `encstr` | obfuscated bytes | XOR key stream #1 for save/score encryption |
|
||||||
|
| `statlist` | obfuscated bytes | XOR key stream #2 for save/score encryption |
|
||||||
|
| `version` | "rogue (rogueforge) 09/05/07" | Full version/build string |
|
||||||
|
|
||||||
|
**Notes:** `encstr`/`statlist` are the two XOR key strings consumed by `encwrite()`/`encread()` in save.c.
|
||||||
|
|
||||||
|
### new_level.c (232 lines) — Dig and draw a new level
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `new_level` | `void new_level()` | Initializes a new dungeon level by clearing previous monsters and objects, drawing rooms and passages, placing traps and stairs, positioning monsters in their rooms, and placing the player. Updates max_level and resets hero position. |
|
||||||
|
| `rnd_room` | `int rnd_room()` | Selects and returns a random room index that actually exists (where ISGONE flag is not set). |
|
||||||
|
| `put_things` | `void put_things()` | Places potions, scrolls, and other items on the current level with 36% probability for each of MAXOBJ slots. Handles treasure rooms (1 in TREAS_ROOM chance) and places the amulet at level AMULETLEVEL if not yet found. |
|
||||||
|
| `treas_room` | `void treas_room()` | Creates a treasure room by selecting a random existing room and filling it with MINTREAS to MAXTREAS treasure items plus monsters from the next level down (incremented temporarily). |
|
||||||
|
|
||||||
|
**Notes:** File-scope constants: TREAS_ROOM (1/20 chance), MAXTREAS (10 items max), MINTREAS (2 items min), MAXTRIES (10 tries limit). No static tables. Uses macros: chat() for character at position, flat() for flags, F_REAL flag. Key side effects: modifies global level, max_level, places[], mlist; calls helper functions do_rooms(), do_passages(), put_things().
|
||||||
|
|
||||||
|
### rooms.c (473 lines) — Create the layout for the new level
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `do_rooms` | `void do_rooms()` | Creates all rooms on a level by partitioning space into 3x3 grid and generating random room sizes/positions. Marks some rooms as ISGONE, sets ISDARK and ISMAZE flags, places gold and monsters in each room based on probabilities. |
|
||||||
|
| `draw_room` | `void draw_room(struct room *rp)` | Draws walls (-, \|) around a room and lays down FLOOR characters inside; handles maze rooms by calling do_maze() instead. |
|
||||||
|
| `vert` | `void vert(struct room *rp, int startx)` | Draws a vertical wall line of '\|' characters from top to bottom of room. |
|
||||||
|
| `horiz` | `void horiz(struct room *rp, int starty)` | Draws a horizontal wall line of '-' characters across room width. |
|
||||||
|
| `do_maze` | `void do_maze(struct room *rp)` | Generates a maze within a room using recursive depth-first digging. Initializes maze SPOT array tracking visited positions and exits, sets up starting position, and calls dig() to carve passages. |
|
||||||
|
| `dig` | `void dig(int y, int x)` | Recursively digs maze passages by choosing random unvisited neighbors, accounting for exits, and calling putpass() to mark passage cells. Returns when no more valid neighbors exist. |
|
||||||
|
| `accnt_maze` | `void accnt_maze(int y, int x, int ny, int nx)` | Records a maze exit connection between adjacent SPOT cells, checking for duplicates before adding. |
|
||||||
|
| `rnd_pos` | `void rnd_pos(struct room *rp, coord *cp)` | Fills coord with random x,y within room bounds (excluding walls). |
|
||||||
|
| `find_floor` | `bool find_floor(struct room *rp, coord *cp, int limit, bool monst)` | Finds a valid floor/passage spot in given room or random room (if rp==NULL) within limit attempts. Returns TRUE if found, FALSE if limit exceeded. If monst==TRUE, checks for monster-valid position (empty with step_ok() char). |
|
||||||
|
| `enter_room` | `void enter_room(coord *cp)` | Called when player enters a room; sets proom, opens doors, and redraws non-dark rooms by displaying correct characters and monsters. Updates terminal display. |
|
||||||
|
| `leave_room` | `void leave_room(coord *cp)` | Called when player exits a room; clears room display by replacing FLOOR with space or passage chars, handles monsters with standout mode, restores doors. |
|
||||||
|
|
||||||
|
**Notes:** File-scope static variables: Maxy, Maxx, Starty, Startx (maze bounds), and maze[NUMLINES/3+1][NUMCOLS/3+1] array (2D array of SPOT structures tracking maze generation). SPOT typedef contains: nexits (count of exits), exits[4] (array of exit coordinates), used (boolean flag). Uses "until" macro with do-while loops for retries. Key macros: chat() and flat() for accessing places[] array, FLOOR/DOOR/PASSAGE character constants, ISMAZE/ISDARK/ISGONE room flags.
|
||||||
|
|
||||||
|
### passages.c (425 lines) — Draw the connecting passages
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `do_passages` | `void do_passages()` | Draws all passages on a level using a connectivity graph algorithm. Builds spanning tree connecting all MAXROOMS rooms, then adds 0-4 random extra connections. Initializes rdes[] array and calls conn() to draw each passage, then calls passnum() to number them. |
|
||||||
|
| `conn` | `void conn(int r1, int r2)` | Draws a corridor between two rooms, determining direction (right or down based on room indices), finding door positions, and drawing passage tiles with optional turn points. Places DOOR characters at room entries. |
|
||||||
|
| `putpass` | `void putpass(coord *cp)` | Sets F_PASS flag at position and marks as PASSAGE character; randomly makes secret passages (unreal) at deeper levels. |
|
||||||
|
| `door` | `void door(struct room *rm, coord *cp)` | Records door position in room's r_exit array, increments r_nexits. For non-maze rooms, randomly creates secret doors (\|/- unreal) or regular DOOR characters. |
|
||||||
|
| `add_pass` | `void add_pass()` | [MASTER ifdef] Wizard command that displays all passages and doors on screen by iterating places[] and showing passages as PASSAGE, secret doors with standout. |
|
||||||
|
| `passnum` | `void passnum()` | Assigns passage numbers (0 to MAXPASS-1) by iterating through room exits and calling numpass() on each, resetting passage r_nexits. Uses static pnum and newpnum tracking. |
|
||||||
|
| `numpass` | `void numpass(int y, int x)` | Recursively numbers a passageway region by flood-fill; sets F_PNUM bits in flat flags, records door/secret door exits in passages[] array, and recurses on adjacent cells. |
|
||||||
|
|
||||||
|
**Notes:** File-scope static variables: pnum (current passage number), newpnum (boolean flag). Static rdes[] array contains MAXROOMS room description structures with conn[] (adjacency matrix for possible connections), isconn[] (boolean array of actual connections made), and ingraph (boolean). The connectivity matrix hardcodes a 3x3 room grid topology. Uses macros: chat()/flat() for places[] access, F_PASS/F_PNUM flags, INDEX() macro for array indexing, ce() for coordinate equality testing.
|
||||||
|
|
||||||
|
### move.c (426 lines) — Hero movement commands
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `do_run` | `void do_run(char ch)` | Initiates running mode by setting running=TRUE, runch=ch (direction), and after=FALSE. |
|
||||||
|
| `do_move` | `void do_move(int dy, int dx)` | Main movement handler; validates move legality (bounds, diagonal ok), handles confused movement, checks for traps/doors/monsters. Updates hero position, calls fight() for combat, handles room changes, updates display. Complex switch on destination cell character (DOOR/TRAP/PASSAGE/FLOOR/STAIRS/monster). |
|
||||||
|
| `turn_ok` | `bool turn_ok(int y, int x)` | Checks if position is a DOOR or both F_REAL and F_PASS flags set (valid turning point in corridors during runs). |
|
||||||
|
| `turnref` | `void turnref()` | Marks position F_SEEN and optionally refreshes screen if jump mode enabled (for passage turn display). |
|
||||||
|
| `door_open` | `void door_open(struct room *rp)` | Illuminates a room by iterating through all positions and waking any monsters found (calling wake_monster()). |
|
||||||
|
| `be_trapped` | `char be_trapped(coord *tc)` | Handles trap activation with switch on trap type: T_DOOR (level down), T_BEAR (no_move delay), T_MYST (random messages), T_SLEEP (no_command delay), T_ARROW (damage/rolls), T_TELEP (teleport), T_DART (poisoned damage), T_RUST (armor damage). Returns trap type. |
|
||||||
|
| `rndmove` | `coord *rndmove(THING *who)` | Generates random movement by adding rnd(3)-1 to current position; validates legality with diag_ok() and step_ok(), returns position or original if invalid. |
|
||||||
|
| `rust_armor` | `void rust_armor(THING *arm)` | Decrements armor AC (increases o_arm value) unless protected or wearing R_SUSTARM ring, with message output varying by terse flag. Returns if armor NULL, not ARMOR type, or already very rusty. |
|
||||||
|
|
||||||
|
**Notes:** File-scope global variable: nh (coord holding new hero position). Uses macros: winat() for visible character, chat() for actual cell character, flat() for flags, moat() for monster at position, INDEX() for place lookup. Trap type constants: T_DOOR, T_BEAR (BEARTIME), T_MYST, T_SLEEP (SLEEPTIME), T_ARROW, T_TELEP, T_DART, T_RUST. Uses "when" macro for compact case statements and "goto over" for retry after confused move calculation.
|
||||||
|
|
||||||
|
### chase.c (542 lines) — Code for one creature to chase another
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `runners` | `void runners()` | Iterates through all monsters in mlist and moves each running monster (ISRUN flag) via move_monst(), handling fast monsters with second move and target flag updates. Clears has_hit message flag. |
|
||||||
|
| `move_monst` | `int move_monst(THING *tp)` | Executes one monster turn: calls do_chase() once for ISSLOW/ISHASTE alternate turns, toggles t_turn flag. Returns -1 if monster died. |
|
||||||
|
| `relocate` | `void relocate(THING *th, coord *new_loc)` | Updates monster position from th->t_pos to new_loc, refreshes moat() pointers, calls set_oldch() for display character, updates room pointer, recalculates destination if room changed, and redraws monster on screen. |
|
||||||
|
| `do_chase` | `int do_chase(THING *th)` | Primary chase logic: finds room of chaser and chasee, navigates to target by exiting current room and approaching chasee. For dragons, checks line-of-sight and fires flame bolt at hero if in range (DRAGONSHOT odds). Calls chase() to find best move, handles combat when goal reached, calls relocate(). Returns 0. |
|
||||||
|
| `set_oldch` | `void set_oldch(THING *tp, coord *cp)` | Sets tp->t_oldch to the screen character at cp for proper redrawing; accounts for darkness, lamp range, and blind status to choose correct background. |
|
||||||
|
| `see_monst` | `bool see_monst(THING *mp)` | Returns TRUE if hero can see monster: checks ISBLIND, ISINVIS (requires CANSEE), LAMPDIST proximity, and room visibility (same room and not dark). |
|
||||||
|
| `runto` | `void runto(coord *runner)` | Sets monster at runner position to ISRUN flag, clears ISHELD, sets t_dest via find_dest(), starts monster chasing. |
|
||||||
|
| `chase` | `bool chase(THING *tp, coord *ee)` | Finds best move for tp toward target ee; if confused (ISHUH), makes random move via rndmove(); otherwise iterates 3x3 surrounding cells to find closest approach, avoiding scare scrolls and Xerocs. Returns TRUE if still chasing (curdist != 0), FALSE if reached goal. |
|
||||||
|
| `roomin` | `struct room *roomin(coord *cp)` | Locates which room contains coordinate cp by checking F_PASS flag first (returns passage room), then iterating rooms[] for overlap; returns NULL/aborts if not found. |
|
||||||
|
| `diag_ok` | `bool diag_ok(coord *sp, coord *ep)` | Validates move from sp to ep: checks bounds and allows non-diagonals always; for diagonal moves, checks that both adjacent cells (same row and same column) are step_ok(). |
|
||||||
|
| `cansee` | `bool cansee(int y, int x)` | Returns TRUE if hero can see coordinate: checks ISBLIND, LAMPDIST range with passage blocking checks, or same room with lit/lit conditions. |
|
||||||
|
| `find_dest` | `coord *find_dest(THING *tp)` | Determines monster target: returns &hero if carrying probability is low, monster in proom, or monster is visible; otherwise searches lvl_obj for items in monster's room not already targeted by other monsters, with probability check; defaults to &hero. |
|
||||||
|
| `dist` | `int dist(int y1, int x1, int y2, int x2)` | Returns squared Euclidean distance (x delta squared plus y delta squared) for comparison purposes. |
|
||||||
|
| `dist_cp` | `int dist_cp(coord *c1, coord *c2)` | Wrapper calling dist() with coordinate components extracted from c1 and c2. |
|
||||||
|
|
||||||
|
**Notes:** File-scope static variable: ch_ret (coord holding where chasing takes you, returned by chase() and used by do_chase()). DRAGONSHOT macro constant (5, odds for dragon breath). Uses macros: moat() for monster at position, chat()/flat() for cell access, on() for flag testing, ce() for coordinate equality. Key dragon logic: checks 8-directional line-of-sight (row match, column match, or diagonal), BOLT_LENGTH range squared, and calls fire_bolt(). Monster types referenced: 'D' (dragon), 'P' (invisible stalker), 'B' (bat), 'F' (flytrap), 'X' (xeroc).
|
||||||
|
|
||||||
|
### monsters.c (253 lines) — File with various monster functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `randmonster` | `char randmonster(bool wander)` | Selects a monster type from either lvl_mons[] or wand_mons[] array based on wander flag. Calculates index from level offset with noise (rnd(10)-6), clamps to valid range, skips nulls in array. Returns monster letter 'A'-'Z'. |
|
||||||
|
| `new_monster` | `void new_monster(THING *tp, char type, coord *cp)` | Initializes new monster: sets type, disguise, position; attaches to mlist; looks up stats from monsters[] table using type-'A' index; adjusts level based on depth (level > AMULETLEVEL); sets flags, hit points via roll(). If ISWEARING R_AGGR ring, calls runto(). For 'X' (xeroc), randomizes disguise. |
|
||||||
|
| `exp_add` | `int exp_add(THING *tp)` | Calculates experience reward based on monster level and max HP; modifies by 20x for level > 9, 4x for level > 6, and divides HP by 6 (or 8 if level 1). |
|
||||||
|
| `wanderer` | `void wanderer()` | Creates a wandering monster at random floor position in different room than proom. Allocates THING via new_item(), calls randmonster(TRUE), new_monster(), displays if SEEMONST, and sets running via runto(). |
|
||||||
|
| `wake_monster` | `THING *wake_monster(int y, int x)` | Activates monster at position: checks 1/3 chance to start chasing if ISMEAN and conditions met (not held, not wearing stealth, etc.); handles Medusa ('M') gaze attack (confusion if not saved). For ISGREED monsters, sets guard target to gold position or hero. Returns monster pointer. |
|
||||||
|
| `give_pack` | `void give_pack(THING *tp)` | If at max_level and random check (%) against m_carry probability succeeds, attaches one random item to monster's pack via new_thing(). |
|
||||||
|
| `save_throw` | `int save_throw(int which, THING *tp)` | Calculates saving throw: need value = 14 + which - (s_lvl / 2), rolls 1d20, returns TRUE if roll >= need. |
|
||||||
|
| `save` | `int save(int which)` | Hero's save check: for VS_MAGIC, reduces difficulty by protection ring bonuses (left and right ring armor values), then calls save_throw() with &player. |
|
||||||
|
|
||||||
|
**Notes:** File-scope static arrays: lvl_mons[] (26 monsters by level 0-25: K E B S H I R O Z L C Q A N Y F T W P X U M V G J D), wand_mons[] (same with 0s for F U D where wands unavailable). Monster type encoding: 'A' through 'Z' map to indices 0-25 via t_type-'A'. The monsters[] global array (defined elsewhere) is indexed this way. Key monster types: 'M' (Medusa), 'X' (Xeroc), 'F' (Flytrap), 'D' (Dragon). References AMULETLEVEL constant for depth-scaling experience. Uses macros: ISWEARING() for ring checks, on() for flag testing, rnd() for probabilities, roll() for hit dice.
|
||||||
|
|
||||||
|
### wizard.c (285 lines) — Special wizard commands (some also non-wizard under strange circumstances)
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `whatis` | `void whatis(bool insist, int type)` | Identifies an item from pack by prompting with get_item() and setting ISKNOW flag. Calls set_know() with appropriate info array (scr_info, pot_info, ws_info, ring_info) based on o_type. If insist, loops until valid selection or user cancels. |
|
||||||
|
| `set_know` | `void set_know(THING *obj, struct obj_info *info)` | Sets oi_know=TRUE for object type in info array, sets obj ISKNOW flag, and frees/clears any oi_guess string for that type. |
|
||||||
|
| `type_name` | `char *type_name(int type)` | Returns string description for object type by searching static tlist[] array: "potion", "scroll", "food", "ring, wand or staff", "ring", "wand or staff", "weapon", "suit of armor". |
|
||||||
|
| `create_obj` | `void create_obj()` | [MASTER ifdef] Wizard command to create arbitrary items: prompts for type and which (0-f hex), handles blessing for weapons/armor/rings, calls init_weapon() or sets o_arm, processes special ring logic, calls fix_stick() for staves, and adds to pack. |
|
||||||
|
| `teleport` | `void teleport()` | Instantly moves hero to random valid floor position, redraws old position with floor_at(), updates proom if changed rooms (leave_room/enter_room), and resets movement/combat flags (no_move, count, running). Resets ISHELD flag if held by flytrap (resets vf_hit and damage dice). |
|
||||||
|
| `passwd` | `int passwd()` | [MASTER ifdef] Prompts for wizard password, reads line with backspace/kill character handling, returns TRUE if strcmp(input, md_crypt(input, "mT")) matches PASSWD constant. |
|
||||||
|
| `show_map` | `void show_map()` | [MASTER ifdef] Displays full level map in hw window: iterates places[] drawing each cell with standout for non-real (F_REAL unset) cells, then shows "---More---" prompt. |
|
||||||
|
|
||||||
|
**Notes:** File-scope static data: tlist[] array in type_name() with h_list structures mapping type constants to descriptions. Functions marked [MASTER ifdef] are wizard-only debug/cheat commands (create_obj, passwd, show_map). The teleport() function is non-wizard and can be invoked by T_TELEP trap or other game mechanics. Uses macros: on() for flag testing, chat()/flat()/moat() for places[] access, INDEX() for place lookup, ISWEARING() for ring checks. References global variables: pack (player inventory), hero (player position), proom (player's current room), player (player THING), monsters[] (for name lookups in wizard feedback).
|
||||||
|
|
||||||
|
### things.c (714 lines) — Item naming and creation
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `inv_name` | `char* inv_name(THING *obj, bool drop)` | Formats item name for inventory display; handles all item types with modifiers like "(being worn)" or "(weapon in hand)"; writes to global `prbuf` |
|
||||||
|
| `drop` | `void drop()` | Player drops item from pack; checks floor availability, calls `leave_pack()`, attaches to level object list, updates display and global `amulet` flag |
|
||||||
|
| `dropcheck` | `bool dropcheck(THING *obj)` | Validates item can be dropped (checks cursed status and equipment status); handles ring stat adjustments like R_ADDSTR; returns FALSE if cursed |
|
||||||
|
| `new_thing` | `THING* new_thing()` | Creates random item for dungeon level; probabilistically selects type (potion/scroll/food/weapon/armor/ring/stick); initializes enchantments and cursing |
|
||||||
|
| `pick_one` | `int pick_one(struct obj_info *info, int nitems)` | Selects random item from cumulative probability table; returns index of selected item |
|
||||||
|
| `discovered` | `void discovered()` | Prompts player for which item type to list discoveries for; calls `print_disc()` and `end_line()` |
|
||||||
|
| `print_disc` | `void print_disc(char type)` | Lists all discovered items of given type (known or guessed); builds temporary THING for name formatting |
|
||||||
|
| `set_order` | `void set_order(int *order, int numthings)` | Fisher-Yates shuffle of array indices for randomized discovery list order |
|
||||||
|
| `add_line` | `char add_line(char *fmt, char *arg)` | Adds line to discovery window display with pagination; handles INV_SLOW vs INV_OVER modes; manages window creation and "Press space" prompts |
|
||||||
|
| `end_line` | `void end_line()` | Finalizes discovery list display; clears pagination state |
|
||||||
|
| `nothing` | `char* nothing(char type)` | Constructs "nothing found" message for specific item type |
|
||||||
|
| `nameit` | `void nameit(THING *obj, char *type, char *which, struct obj_info *op, char *(*prfunc)(THING *))` | Helper to format potion/ring/stick names with color/material and charge status via function pointer |
|
||||||
|
| `nullstr` | `char* nullstr(THING *ignored)` | Returns empty string; used as null function pointer in `nameit()` calls |
|
||||||
|
| `pr_list` | `void pr_list()` | MASTER-only: lists all possible items by type for wizard debugging |
|
||||||
|
| `pr_spec` | `void pr_spec(struct obj_info *info, int nitems)` | MASTER-only: prints all items in category with probability percentages |
|
||||||
|
|
||||||
|
**Notes:** Static file-scope variables `line_cnt`, `newpage`, `lastfmt`, `lastarg` manage discovery display pagination. Function-pointer callback pattern in `nameit()` (`nullstr`, `ring_num`, `charge_str`).
|
||||||
|
|
||||||
|
### pack.c (504 lines) — Pack management and item selection interface
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `add_pack` | `void add_pack(THING *obj, bool silent)` | Adds item to player pack or picks up from floor; handles grouped items by incrementing count; keeps pack sorted by type; updates monster dest pointers and `amulet` flag |
|
||||||
|
| `pack_room` | `bool pack_room(bool from_floor, THING *obj)` | Validates pack space (`inpack` vs MAXPACK); removes from floor and updates display if successful |
|
||||||
|
| `leave_pack` | `THING* leave_pack(THING *obj, bool newobj, bool all)` | Removes item from pack; decrements `inpack`; splits stacks if not removing all; updates `pack_used` |
|
||||||
|
| `pack_char` | `char pack_char()` | Returns next unused pack slot letter (a-z); marks in `pack_used` array |
|
||||||
|
| `inventory` | `bool inventory(THING *list, int type)` | Lists pack items filtered by type; uses `add_line()` pagination; sets `n_objs` |
|
||||||
|
| `pick_up` | `void pick_up(char ch)` | Handles item pickup by type (gold vs objects); calls `add_pack()` or `money()` |
|
||||||
|
| `move_msg` | `void move_msg(THING *obj)` | Prints message for stepping onto object without picking it up (move_on mode) |
|
||||||
|
| `picky_inven` | `void picky_inven()` | Shows single item from pack by inventory letter |
|
||||||
|
| `get_item` | `THING* get_item(char *purpose, int type)` | Prompts player to select item from pack by letter or '*' for full list; handles ESCAPE and `again` repeat via `last_pick` |
|
||||||
|
| `money` | `void money(int value)` | Adds/subtracts gold from `purse`; updates display floor character |
|
||||||
|
| `floor_ch` | `char floor_ch()` | Returns appropriate floor display character based on room state and `show_floor()` |
|
||||||
|
| `floor_at` | `char floor_at()` | Returns character at hero position accounting for floor visibility |
|
||||||
|
| `reset_last` | `void reset_last()` | Resets saved last command state (`last_comm`, `last_dir`, `last_pick`) from backup copies |
|
||||||
|
|
||||||
|
**Notes:** Pack is a linked list anchored at global `pack` (macro for `player.t_pack`); pack letters stored in `o_packch`, allocation tracked in `pack_used[26]`. Grouped items (arrows etc.) stack by `o_group`. Uses `goto` for pack insertion flow.
|
||||||
|
|
||||||
|
### potions.c (376 lines) — Potion effects
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `quaff` | `void quaff()` | Drinks potion from pack; switch dispatching all 14 potion types (confusion, LSD, poison, strength, see-invisible, healing, monster-find, magic-find, raise-level, extra-heal, haste, restore, blind, levitate); calls `do_pot()` for fuse-based effects; updates `pot_info` known/guess state |
|
||||||
|
| `is_magic` | `bool is_magic(THING *obj)` | Tests if object radiates magic (enchanted armor/weapons, or any potion/scroll/stick/ring/amulet) |
|
||||||
|
| `invis_on` | `void invis_on()` | Enables CANSEE flag; re-displays invisible monsters |
|
||||||
|
| `turn_see` | `bool turn_see(bool turn_off)` | Toggles SEEMONST magic monster detection; redisplays monsters; handles hallucination display |
|
||||||
|
| `seen_stairs` | `bool seen_stairs()` | Checks if stairs are visible on map, under hero, or held by a visible monster |
|
||||||
|
| `raise_level` | `void raise_level()` | Sets experience to next level threshold; calls `check_level()` |
|
||||||
|
| `do_pot` | `void do_pot(int type, bool knowit)` | Standard potion setup: marks known, sets player flag from `p_actions[]` table, starts fuse or lengthens if already active |
|
||||||
|
|
||||||
|
**Notes:** `PACT` struct array `p_actions[]` maps potion types → player flag, cleanup daemon function pointer (`unconfuse`, `come_down`, `sight`, etc.), and duration.
|
||||||
|
|
||||||
|
### scrolls.c (330 lines) — Scroll effects
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `read_scroll` | `void read_scroll()` | Reads scroll from pack; switch on all 18 scroll types: confuse-monster (sets CANHUH on hero), magic-map (reveals level), hold-monster, sleep-self, enchant-armor, identify x5 varieties, scare-monster (message only when read), food-detection, teleport, enchant-weapon, create-monster, remove-curse, aggravate, protect-armor; calls `call_it()` to offer naming |
|
||||||
|
| `uncurse` | `void uncurse(THING *obj)` | Clears ISCURSED flag on item |
|
||||||
|
|
||||||
|
**Notes:** S_MAP iterates the entire level updating passage/door/floor tiles into seen state; S_HOLD scans 3x3 neighborhood for monsters to hold; S_CREATE places monster adjacent; S_SCARE when *picked up* is handled in pack code (scroll on floor scares monsters via chase.c check).
|
||||||
|
|
||||||
|
### rings.c (205 lines) — Ring equipping and effects
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `ring_on` | `void ring_on()` | Equips ring on left or right hand; calls `gethand()` if needed; switch dispatch for immediate effects (R_ADDSTR strength bonus, R_SEEINVIS, R_AGGR awakening) |
|
||||||
|
| `ring_off` | `void ring_off()` | Removes ring; validates which hand; `dropcheck()` for curse validation |
|
||||||
|
| `gethand` | `int gethand()` | Prompts player for left or right hand; loops until valid L/R or ESCAPE |
|
||||||
|
| `ring_eat` | `int ring_eat(int hand)` | Returns extra food consumption for ring on given hand; static `uses[]` table maps ring type to hunger impact (R_DIGEST negative/saves, R_REGEN +2, etc.) |
|
||||||
|
| `ring_num` | `char* ring_num(THING *obj)` | Returns bonus string "[+n]" if known (protection/add-str/add-dam/add-hit rings) |
|
||||||
|
|
||||||
|
**Notes:** Static `uses[]` array encodes per-ring metabolism cost. Rings worn tracked in `cur_ring[LEFT]`/`cur_ring[RIGHT]`.
|
||||||
|
|
||||||
|
### sticks.c (432 lines) — Wand and staff zapping
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `fix_stick` | `void fix_stick(THING *cur)` | Initializes wand/staff damage strings and charges; staffs 2x3 melee damage, wands 1x1; WS_LIGHT gets 10-19 charges, others 3-7 |
|
||||||
|
| `do_zap` | `void do_zap()` | Zaps wand in aimed direction; switch over all 14 stick types: light (illuminate room), drain-life, invisibility (make monster invisible), polymorph, teleport-away, teleport-to, cancellation, magic missile, haste-monster, slow-monster, lightning/fire/cold bolts, nop; decrements charges |
|
||||||
|
| `drain` | `void drain()` | Drains half hero's HP and spreads equal damage across all monsters in room/passage; static `drainee[]` target array |
|
||||||
|
| `fire_bolt` | `void fire_bolt(coord *start, coord *dir, char *name)` | Fires elemental bolt: travels BOLT_LENGTH, bounces off walls (reversing delta components), damages monsters (with save for half... actually save to avoid), can hit hero on bounce-back; animates with directional characters (| - / \\); dragons immune to flame |
|
||||||
|
| `charge_str` | `char* charge_str(THING *obj)` | Returns "[n charges]" string if known |
|
||||||
|
|
||||||
|
**Notes:** `fire_bolt` uses static `bolt` THING and `spotpos[]` path array; bounce logic flips direction on wall hit. WS_POLYMORPH preserves monster's pack through transformation. Used both by player zaps and dragon breath ("flame").
|
||||||
|
|
||||||
|
### weapons.c (289 lines) — Weapon initialization and throwing
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `missile` | `void missile(int ydelta, int xdelta)` | Throws selected weapon: validates, leaves pack (splitting stacks), animates via `do_motion()`, tests hit with `hit_monster()`, falls to ground on miss |
|
||||||
|
| `do_motion` | `void do_motion(THING *obj, int ydelta, int xdelta)` | Animates projectile across screen one cell at a time until blocked (wall/door/monster) |
|
||||||
|
| `fall` | `void fall(THING *obj, bool pr)` | Places fallen projectile on ground via `fallpos()`; attaches to `lvl_obj` or discards if no space |
|
||||||
|
| `init_weapon` | `void init_weapon(THING *weap, int which)` | Sets weapon properties from static `init_dam[]` table (wield damage, hurl damage, launcher, flags); arrows/darts/shurikens get count 8-15 and shared group id |
|
||||||
|
| `hit_monster` | `int hit_monster(int y, int x, THING *obj)` | Wrapper calling `fight()` for missile combat at position |
|
||||||
|
| `num` | `char* num(int n1, int n2, char type)` | Formats bonus as "+h,+d" / "+a" strings for inventory |
|
||||||
|
| `wield` | `void wield()` | Equips weapon; validates not wearing/cursed current; updates `cur_weapon` |
|
||||||
|
| `fallpos` | `bool fallpos(coord *pos, coord *newpos)` | Finds random empty adjacent square (9 cells) for dropped item; reservoir-samples among valid spots |
|
||||||
|
|
||||||
|
**Notes:** Static `init_dam[]` table: mace 2x4, sword 3x4, bow 1x1, arrow 1x1/2x3 (launched by bow), dagger 1x6/1x4, two-handed sword 4x4, dart 1x1/1x3, shuriken 1x2/2x4, spear 2x3/1x6. Global `group` counter assigns stack ids.
|
||||||
|
|
||||||
|
### armor.c (90 lines) — Armor wearing
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `wear` | `void wear()` | Equips armor from pack; rejects if already wearing; sets ISKNOW; calls `waste_time()` (1 turn) |
|
||||||
|
| `take_off` | `void take_off()` | Removes armor; validates exists and not cursed via `dropcheck()` |
|
||||||
|
| `waste_time` | `void waste_time()` | Runs one turn of daemons/fuses (BEFORE and AFTER) without player action — lets hunger/healing tick during multi-turn actions |
|
||||||
|
|
||||||
|
### misc.c (598 lines) — Look/display update, direction input, utilities
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `look` | `void look(bool wakeup)` | Core visibility update: redraws area around player (3x3 in corridors/dark, or whole room), erases old lamp area, handles door-stop run termination logic, wakes monsters if wakeup, applies hallucination via `trip_ch()`; maintains `oldpos`/`oldrp` |
|
||||||
|
| `trip_ch` | `int trip_ch(int y, int x, int ch)` | If hallucinating, replaces item characters with random `rnd_thing()` character |
|
||||||
|
| `erase_lamp` | `void erase_lamp(coord *pos, struct room *rp)` | Erases 3x3 lamp-lit area around old position in dark rooms |
|
||||||
|
| `show_floor` | `bool show_floor()` | Returns whether floor under hero should be shown (dark room + not blind + see_floor option) |
|
||||||
|
| `find_obj` | `THING* find_obj(int y, int x)` | Linear search of `lvl_obj` for object at position (fatal error if none in MASTER) |
|
||||||
|
| `eat` | `void eat()` | Eats food from pack; sets `food_left` to HUNGERTIME (with random reduction); 30% chance fruit vs ration message; may grant experience |
|
||||||
|
| `check_level` | `void check_level()` | Checks experience against `e_levels[]`; on level-up increases HP by roll(diff,10) and messages "Welcome to level N" |
|
||||||
|
| `chg_str` | `void chg_str(int amt)` | Modifies strength with R_SUSTSTR/ring accounting; tracks `max_stats.s_str` |
|
||||||
|
| `add_str` | `void add_str(str_t *sp, int amt)` | Adds to strength value with bounds (min 3, max 31) |
|
||||||
|
| `add_haste` | `bool add_haste(bool potion)` | Adds ISHASTE; if already hasted, faint 1-8 turns instead; starts `nohaste` fuse if potion |
|
||||||
|
| `aggravate` | `void aggravate()` | Calls `runto()` on all monsters — everything wakes and hunts |
|
||||||
|
| `vowelstr` | `char* vowelstr(char *str)` | Returns "n" if string starts with vowel (for "a"/"an") |
|
||||||
|
| `is_current` | `bool is_current(THING *obj)` | Checks if item is currently equipped; messages "in use" |
|
||||||
|
| `get_dir` | `bool get_dir()` | Prompts for direction key (hjklyubn); sets `delta`/`dir_ch`; handles `again` repeat; randomizes if confused/hallucinating; FALSE on ESCAPE |
|
||||||
|
| `sign` | `int sign(int nm)` | Returns -1/0/1 |
|
||||||
|
| `spread` | `int spread(int nm)` | Returns nm ±10% random spread (used for durations) |
|
||||||
|
| `call_it` | `void call_it(struct obj_info *info)` | Prompts player to name an unidentified item; stores in `oi_guess` |
|
||||||
|
| `rnd_thing` | `char rnd_thing()` | Picks random thing display character (includes AMULET only at deep levels); static `thing_list[]` |
|
||||||
|
| `choose_str` | `char* choose_str(char *ts, char *ns)` | Returns first string if hallucinating, else second |
|
||||||
|
|
||||||
|
**Notes:** `look()` is the heart of FOV/display maintenance and the trickiest function here (passage-count door-stop logic using `sumhero`/`diffhero` diagonal tests). Static `last_dt` in `get_dir()` for repeats.
|
||||||
|
|
||||||
|
### daemon.c (182 lines) — Daemon and fuse scheduler
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `d_slot` | `struct delayed_action* d_slot()` | Finds first EMPTY slot in `d_list[20]` |
|
||||||
|
| `find_slot` | `struct delayed_action* find_slot(void (*func)())` | Finds occupied slot by function pointer |
|
||||||
|
| `start_daemon` | `void start_daemon(void (*func)(int), int arg, int type)` | Registers repeating daemon (d_time = DAEMON sentinel) |
|
||||||
|
| `kill_daemon` | `void kill_daemon(void (*func)())` | Removes daemon by function pointer |
|
||||||
|
| `do_daemons` | `void do_daemons(int flag)` | Runs all daemons matching BEFORE/AFTER phase |
|
||||||
|
| `fuse` | `void fuse(void (*func)(int), int arg, int time, int type)` | Registers one-shot countdown fuse |
|
||||||
|
| `lengthen` | `void lengthen(void (*func)(), int xtime)` | Extends existing fuse's remaining time |
|
||||||
|
| `extinguish` | `void extinguish(void (*func)())` | Cancels fuse by function pointer |
|
||||||
|
| `do_fuses` | `void do_fuses(int flag)` | Decrements matching fuses; fires and empties any reaching zero |
|
||||||
|
|
||||||
|
**Notes:** `d_list[MAXDAEMONS=20]` of `struct delayed_action {type, func ptr, arg, time}`. Function pointers are the identity of daemons/fuses — the Go port must replace this with named identifiers (also required by save format, which already maps func ptrs to small ints).
|
||||||
|
|
||||||
|
### daemons.c (296 lines) — Daemon and fuse callbacks
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `doctor` | `void doctor()` | Healing daemon: heals based on level and `quiet` rest counter; R_REGEN ring bonus; caps at max_hp |
|
||||||
|
| `swander` | `void swander()` | Fuse that starts the `rollwand` daemon |
|
||||||
|
| `rollwand` | `void rollwand()` | Every turn after 4: 1-in-6 roll to spawn wandering monster via `wanderer()`, then re-fuse `swander` |
|
||||||
|
| `unconfuse` | `void unconfuse()` | Fuse: clears ISHUH; message |
|
||||||
|
| `unsee` | `void unsee()` | Fuse: clears CANSEE; re-hides invisible monsters |
|
||||||
|
| `sight` | `void sight()` | Fuse: cures blindness; re-lights room |
|
||||||
|
| `nohaste` | `void nohaste()` | Fuse: clears ISHASTE |
|
||||||
|
| `stomach` | `void stomach()` | Hunger daemon: decrements `food_left` (+1 per ring worn, adjusted by ring_eat); thresholds → hungry (300)/weak (150)/faint (0 = random paralysis, starvation death) |
|
||||||
|
| `come_down` | `void come_down()` | Fuse: ends hallucination; kills `visuals` daemon; redraws everything correctly |
|
||||||
|
| `visuals` | `void visuals()` | Hallucination daemon: continuously randomizes displayed item/monster glyphs |
|
||||||
|
| `land` | `void land()` | Fuse: ends levitation |
|
||||||
|
|
||||||
|
**Notes:** Static `between` counter in `rollwand()`. These 11 functions are exactly the set the save format maps to integer ids 1-9 (+2).
|
||||||
|
|
||||||
|
### list.c (114 lines) — Doubly-linked list allocation
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `_detach` | `void _detach(THING **list, THING *item)` | Removes item from linked list (head pointer updated via double pointer) |
|
||||||
|
| `_attach` | `void _attach(THING **list, THING *item)` | Pushes item onto head of list |
|
||||||
|
| `_free_list` | `void _free_list(THING **ptr)` | Discards entire list |
|
||||||
|
| `discard` | `void discard(THING *item)` | free()s one THING |
|
||||||
|
| `new_item` | `THING* new_item()` | calloc()s a zeroed THING with NULL links |
|
||||||
|
|
||||||
|
**Notes:** Macro API `attach(a,b)`/`detach(a,b)`/`free_list(a)` pass `&list`. In Go this becomes slices or container/list — the port will use slices of typed pointers.
|
||||||
|
|
||||||
|
### fight.c (687 lines) — Combat system
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `fight` | `int fight(coord *mp, THING *weap, bool thrown)` | Player attacks monster at coordinates with optional weapon. Xeroc disguise revelation, hit/miss messaging, confuse-monster ring/scroll effect (CANHUH), monster death via `killed()` |
|
||||||
|
| `attack` | `int attack(THING *mp)` | Monster attacks player. Handles damage, player death, and per-monster special effects: A=rust armor, I=freeze (no_command), R=weaken strength (poison, save or -1 STR), W=wraith XP drain, V=vampire maxhp drain, F=flytrap hold (accumulating vf_hit damage), L=leprechaun gold steal, N=nymph item steal |
|
||||||
|
| `set_mname` | `char *set_mname(THING *tp)` | Returns display name: "something" if unseen, random monster if hallucinating, else "the <name>"; static buffer |
|
||||||
|
| `swing` | `int swing(int at_lvl, int op_arm, int wplus)` | d20 to-hit roll: `res + wplus >= (20 - at_lvl) - op_arm` |
|
||||||
|
| `roll_em` | `bool roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl)` | Rolls damage for an attack. Parses damage strings "NxM/NxM", strength to-hit/damage bonus tables, launcher matching for hurled ammo, ring add-hit/add-dam for player; applies to defender's `s_hpt` (min 0). Returns true if any swing hit |
|
||||||
|
| `prname` | `char *prname(char *mname, bool upper)` | Combatant name: "you" for NULL, optionally capitalized; static buffer |
|
||||||
|
| `thunk` | `void thunk(THING *weap, char *mname, bool noend)` | "the dagger hits <m>" message for thrown weapons |
|
||||||
|
| `hit` | `void hit(char *er, char *ee, bool noend)` | Prints hit message from `h_names[]` variants (terse-aware) |
|
||||||
|
| `miss` | `void miss(char *er, char *ee, bool noend)` | Prints miss message from `m_names[]` variants |
|
||||||
|
| `bounce` | `void bounce(THING *weap, char *mname, bool noend)` | "the dagger misses" message for thrown weapons |
|
||||||
|
| `remove_mon` | `void remove_mon(coord *mp, THING *tp, bool waskill)` | Removes monster from level: drops its pack (fall if killed), clears map cell + moat, detaches from mlist, resets to_death/kamikaze if it was target |
|
||||||
|
| `killed` | `void killed(THING *tp, bool pr)` | Awards experience, special deaths (F releases held hero, L drops gold), removes monster, "defeated the X" message, `check_level()` |
|
||||||
|
|
||||||
|
**Notes:** Static strength adjustment tables: `str_plus[]` (to-hit bonus for STR 3-31: -7..+3) and `add_dam[]` (damage bonus: -7..+6). `h_names[]`/`m_names[]` message variant arrays. Careful NULL discipline where L/N monsters remove themselves during their own attack (use-after-free avoidance).
|
||||||
|
|
||||||
|
### rip.c (449 lines) — Death, victory, and scoreboard
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `score` | `void score(int amount, int flags, char monst)` | Scoreboard maintenance: reads top-ten via `rd_score()`, inserts player entry if it qualifies (one score per uid unless allscore), prints ranked list with standout for new entry, writes back via `wr_score()` under `lock_sc()` |
|
||||||
|
| `death` | `void death(char monst)` | Player death: purse -= 10%, draws tombstone ASCII art with name/gold/killer/year, calls `score()`, exits |
|
||||||
|
| `center` | `int center(char *str)` | Column to center a string on the tombstone |
|
||||||
|
| `total_winner` | `void total_winner()` | Victory: banner, itemized inventory with gold values (worth calculations per item type: weapons +bonus*100, armor by AC delta, unidentified halved, etc.), score(flags=2), exit |
|
||||||
|
| `killname` | `char *killname(char monst, bool doart)` | Death cause → string: A-Z monster names; lowercase specials from `nlist[]` (a=arrow, b=bolt, d=dart, h=hypothermia, s=starvation) |
|
||||||
|
| `death_monst` | `char death_monst()` | Random death cause for the -d demo flag |
|
||||||
|
|
||||||
|
**Notes:** Static `rip[]` tombstone art; `nlist[]` special death causes. SCORE fields: uid, score, flags (0=killed 1=quit 2=winner 3=killed-with-amulet), monster, name, level, time. Scoreboard file XOR-encrypted with same scheme as saves.
|
||||||
|
|
||||||
|
### save.c (391 lines) — Save/restore and encrypted I/O
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `save_game` | `void save_game()` | Interactive save: prompt for filename (default `file_name`), confirm overwrite, open and `save_file()`, exit |
|
||||||
|
| `auto_save` | `void auto_save(int sig)` | SIGHUP/SIGTERM handler: silently saves to `file_name` and exits |
|
||||||
|
| `save_file` | `void save_file(FILE *savef)` | Writes version string + "LINES x COLS" header (encrypted), then `rs_save_file()`; chmod 0400; exits |
|
||||||
|
| `restore` | `bool restore(char *file, char **envp)` | Validates version + screen size, rejects symlinks/multi-link files (anti-cheat), deletes save file (no re-use), `rs_restore_file()`, re-enters `playit()` |
|
||||||
|
| `encwrite` | `size_t encwrite(char *start, size_t size, FILE *outf)` | XOR-encrypts stream: byte ^ encstr[i] ^ statlist[j] ^ feedback byte, cycling both key strings |
|
||||||
|
| `encread` | `size_t encread(char *start, size_t size, FILE *inf)` | Mirror decryption of `encwrite` |
|
||||||
|
| `rd_score` | `void rd_score(SCORE *top_ten)` | Reads/decrypts scoreboard entries (name string + sscanf'd numeric line) |
|
||||||
|
| `wr_score` | `void wr_score(SCORE *top_ten)` | Writes/encrypts scoreboard entries |
|
||||||
|
|
||||||
|
**Notes:** Save anti-cheat: file deleted on restore, symlink/hardlink rejected. Go port decision point: keep byte-compatible format vs. clean format (see Part 2).
|
||||||
|
|
||||||
|
### state.c (2135 lines) — Save-state serialization layer
|
||||||
|
|
||||||
|
Machine-generated-style marshaling of every global into an endian-stable, XOR-encrypted binary stream with per-block magic markers (0xABCD0001-style RSID_* ids).
|
||||||
|
|
||||||
|
| Family | Functions | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Primitives | `rs_write/rs_read` (raw), `_int`, `_short`, `_uint`, `_chars`, `_boolean(s)`, `_shorts`, `_marker` | Little-endian normalized; count-prefixed arrays; `format_error`/`read_error`/`write_error` flags |
|
||||||
|
| Strings | `_string`, `_new_string`, `_string_index`, `_strings` | Length-prefixed; `_string_index` stores index into a master table (e.g. rainbow colors) instead of text |
|
||||||
|
| Game types | `_coord`, `_str_t`, `_stats`, `_room(s)`, `_monsters`, `_obj_info`, `_daemons`, `_window` | `_daemons` maps function pointers ↔ integer ids (rollwand=1, doctor=2, stomach=3, runners=4, swander=5, nohaste=6, unconfuse=7, unsee=8, sight=9); `_window` dumps curses screen contents cell by cell |
|
||||||
|
| THINGs | `_object`, `_object_list`, `_object_reference`, `_thing`, `_thing_list`, `rs_fix_thing(_list)` | Lists serialized with size prefix and rebuilt; intra-structure pointers (cur_weapon, monster t_dest) stored as list indices; monster chase targets encoded as (kind, index) pairs and fixed up post-load via `t_reserved` |
|
||||||
|
| Map | `_places` | All MAXLINES*MAXCOLS cells: ch, flags, monster reference index |
|
||||||
|
| Helpers | `get_list_item`, `find_list_ptr`, `list_size`, `find_room_coord`, `find_thing_coord`, `find_object_coord` | Pointer→index translation for serialization |
|
||||||
|
| Top level | `rs_save_file`, `rs_restore_file` | Fixed ordered sequence of ALL globals: 27 bools, ~25 ints, strings/name arrays, coords, player THING, equipment references, lvl_obj list, mlist, places[], rooms/passages, monsters[], all 7 obj_info tables, d_list daemons, and the curses screen |
|
||||||
|
|
||||||
|
**Notes:** This file exists because C can't reflect. The Go port replaces nearly all of it with `encoding/gob` (or JSON) over a serializable snapshot struct — only the pointer→index encoding of references (equipment, chase targets) needs explicit code.
|
||||||
|
|
||||||
|
### options.c (502 lines) — Options menu and ROGUEOPTS parsing
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `option` | `void option()` | Interactive options screen editing the `optlist[]` entries in place; per-option get/put function pointers; MINUS backs up, ESCAPE quits |
|
||||||
|
| `pr_optname` | `void pr_optname(OPTION *op)` | Prints "prompt (name): " |
|
||||||
|
| `put_bool` / `put_str` / `put_inv_t` | `void put_*(void *)` | Display current value (True/False, string, inventory-type name) |
|
||||||
|
| `get_bool` | `int get_bool(void *vp, WINDOW *win)` | Interactive boolean input (t/f/Enter/Esc/-) |
|
||||||
|
| `get_sf` | `int get_sf(void *vp, WINDOW *win)` | see_floor wrapper around get_bool with lamp redraw side effects |
|
||||||
|
| `get_str` | `int get_str(void *vopt, WINDOW *win)` | Line editor: backspace, kill-char, ~ home expansion; MAXINP=50 cap |
|
||||||
|
| `get_inv_t` | `int get_inv_t(void *vp, WINDOW *win)` | Inventory style selection (Overwrite/Slow/Clear) |
|
||||||
|
| `get_num` | `int get_num(void *vp, WINDOW *win)` | (MASTER) numeric via get_str + atoi |
|
||||||
|
| `parse_opts` | `void parse_opts(char *str)` | Parses ROGUEOPTS env var: comma-separated `flag`, `noflag`, `name=value` |
|
||||||
|
| `strucpy` | `void strucpy(char *s1, char *s2, int len)` | Copies only printable chars, capped |
|
||||||
|
|
||||||
|
**Notes:** `OPTION` struct: name, prompt, value pointer, put function pointer, get function pointer. Options: terse, flush, jump, seefloor, passgo, tombstone, inven, name, fruit, file. In Go this becomes a slice of interface-typed option descriptors or simple closures.
|
||||||
|
|
||||||
|
### xcrypt.c (708 lines) — DES crypt(3) implementation
|
||||||
|
|
||||||
|
Full portable DES implementation (`xcrypt(key, setting)`) with precomputed permutation tables (~100KB of lookup tables), supporting classic 2-char-salt crypt and extended-format crypt.
|
||||||
|
|
||||||
|
**Usage in the game:** only `md_crypt()` → wizard-password check (`passwd()` in wizard.c). NOT used for save encryption (that's the simple XOR in save.c). The Go port can drop this file entirely and gate wizard mode on an environment variable instead.
|
||||||
|
|
||||||
|
### score.h (27 lines) — Scoreboard entry structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct sc_ent {
|
||||||
|
unsigned int sc_uid; /* user id */
|
||||||
|
int sc_score; /* final gold */
|
||||||
|
unsigned int sc_flags; /* 0=killed, 1=quit, 2=winner, 3=killed with amulet */
|
||||||
|
unsigned short sc_monster; /* killer: A-Z monster, or a/b/d/h/s special */
|
||||||
|
char sc_name[MAXSTR]; /* player name */
|
||||||
|
int sc_level; /* deepest level */
|
||||||
|
unsigned int sc_time; /* timestamp */
|
||||||
|
};
|
||||||
|
typedef struct sc_ent SCORE;
|
||||||
|
```
|
||||||
|
|
||||||
|
Declares `rd_score(SCORE*)` / `wr_score(SCORE*)` (implemented in save.c).
|
||||||
Reference in New Issue
Block a user