Files
nethack/include/rm.h
2026-01-11 11:42:16 +02:00

536 lines
20 KiB
C

/* NetHack 3.7 rm.h $NHDT-Date: 1745114235 2025/04/19 17:57:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.120 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Pasi Kallinen, 2017. */
/* NetHack may be freely redistributed. See license for details. */
#ifndef RM_H
#define RM_H
/*
* The dungeon presentation graphics code and data structures were rewritten
* and generalized for NetHack's release 2 by Eric S. Raymond (eric@snark)
* building on Don G Kneller's MS-DOS implementation. See drawing.c for
* the code that permits the user to set the contents of the symbol structure.
*
* The door representation was changed by Ari
* Huttunen(ahuttune@niksula.hut.fi)
*/
/*
* TLCORNER TDWALL TRCORNER
* +- -+- -+
* | | |
*
* TRWALL CROSSWALL TLWALL HWALL
* | | |
* +- -+- -+ ---
* | | |
*
* BLCORNER TUWALL BRCORNER VWALL
* | | | |
* +- -+- -+ |
*/
/*
* Level location types, values for 'level.locations[x][y].typ'.
*
* These are different from display symbols and while there is
* similarity between the cmap subset of those, there isn't any
* one-to-one correspondence between the two encodings. For instance,
* the DOOR type has multiple symbols (closed|open|gone); so does the
* STAIRS type (down|up). Conversely, DRAWBRIDGE_UP represents the
* location of the projected span if the drawbridge were down but
* there is no symbol for that; it is displayed as water, ice, lava, or
* floor depending on what is at that spot when the bridge is 'closed'.
*
* [Some debugging code in src/display.c defines array type_names[]
* which contains an entry for each of these, so needs to be kept in
* sync if any new types are added or existing ones renumbered. The
* #terrain command also has a menu choice to display each map spot by
* a letter derived from these numeric values and another choice to
* display a legend showing the letter-to-type correspondence. If any
* types are added, removed, or reordered, that needs to be updated to
* keep in synch.]
*/
enum levl_typ_types {
STONE = 0,
VWALL = 1,
HWALL = 2,
TLCORNER = 3,
TRCORNER = 4,
BLCORNER = 5,
BRCORNER = 6,
CROSSWALL = 7, /* For pretty mazes and special levels */
TUWALL = 8,
TDWALL = 9,
TLWALL = 10,
TRWALL = 11,
DBWALL = 12,
TREE = 13, /* KMH */
SDOOR = 14,
SCORR = 15,
POOL = 16,
MOAT = 17, /* pool that doesn't boil, adjust messages */
WATER = 18,
DRAWBRIDGE_UP = 19,
LAVAPOOL = 20,
LAVAWALL = 21,
IRONBARS = 22, /* KMH */
DOOR = 23,
CORR = 24,
ROOM = 25,
STAIRS = 26,
LADDER = 27,
FOUNTAIN = 28,
THRONE = 29,
SINK = 30,
GRAVE = 31,
ALTAR = 32,
ICE = 33,
DRAWBRIDGE_DOWN = 34,
AIR = 35,
CLOUD = 36,
MAX_TYPE = 37,
MATCH_WALL = 38,
INVALID_TYPE = 127
};
/*
* Avoid using the level types in inequalities:
* these types are subject to change.
* Instead, use one of the macros below.
*/
#define IS_WALL(typ) ((typ) && (typ) <= DBWALL)
#define IS_STWALL(typ) ((typ) <= DBWALL) /* STONE <= (typ) <= DBWALL */
#define IS_OBSTRUCTED(typ) ((typ) < POOL) /* absolutely nonaccessible */
#define IS_SDOOR(typ) ((typ) == SDOOR)
#define IS_DOOR(typ) ((typ) == DOOR)
#define IS_DOORJOIN(typ) (IS_OBSTRUCTED(typ) || (typ) == IRONBARS)
#define IS_TREE(typ) \
((typ) == TREE || (svl.level.flags.arboreal && (typ) == STONE))
#define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */
#define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM, STAIRS, furniture.. */
#define ZAP_POS(typ) ((typ) >= POOL)
#define SPACE_POS(typ) ((typ) > DOOR)
#define IS_POOL(typ) ((typ) >= POOL && (typ) <= DRAWBRIDGE_UP)
#define IS_LAVA(typ) ((typ) == LAVAPOOL || (typ) == LAVAWALL)
#define IS_THRONE(typ) ((typ) == THRONE)
#define IS_FOUNTAIN(typ) ((typ) == FOUNTAIN)
#define IS_SINK(typ) ((typ) == SINK)
#define IS_GRAVE(typ) ((typ) == GRAVE)
#define IS_ALTAR(typ) ((typ) == ALTAR)
#define IS_DRAWBRIDGE(typ) \
((typ) == DRAWBRIDGE_UP || (typ) == DRAWBRIDGE_DOWN)
#define IS_FURNITURE(typ) ((typ) >= STAIRS && (typ) <= ALTAR)
#define IS_AIR(typ) ((typ) == AIR || (typ) == CLOUD)
#define IS_SOFT(typ) ((typ) == AIR || (typ) == CLOUD || IS_POOL(typ))
#define IS_WATERWALL(typ) ((typ) == WATER)
/* for surface checks when it's unknown whether a drawbridge is involved;
drawbridge_up is the spot in front of a closed drawbridge and not the
current surface at that spot; caveat: this evaluates its arguments more
than once and might make a function call */
#define SURFACE_AT(x,y) \
((levl[x][y].typ == DRAWBRIDGE_UP) \
? db_under_typ(levl[x][y].drawbridgemask) \
: levl[x][y].typ)
/*
* The structure describing a coordinate position.
* Before adding fields, remember that this will significantly affect
* the size of temporary files and save files.
*
* Also remember that the run-length encoding for some ports in save.c
* must be updated to consider the field.
*/
struct rm {
int glyph; /* what the hero thinks is there */
schar typ; /* what is really there [why is this signed?] */
uchar seenv; /* seen vector */
Bitfield(flags, 5); /* extra information for typ */
Bitfield(horizontal, 1); /* wall/door/etc is horiz. (more typ info) */
Bitfield(lit, 1); /* speed hack for lit rooms */
Bitfield(waslit, 1); /* remember if a location was lit */
Bitfield(roomno, 6); /* room # for special rooms */
Bitfield(edge, 1); /* marks boundaries for special rooms*/
Bitfield(candig, 1); /* Exception to Can_dig_down; was a trapdoor */
};
/*
* rm flags field overloads:
*
* +-------------+-------------+------------+------------+------------+
* | bit5 | bit4 | bit3 | bit2 | bit1 |
* | 0x10 | 0x8 | 0x4 | 0x2 | 0x1 |
* +-------------+-------------+------------+------------+------------+
* wall |W_NONPASSWALL|W_NONDIGGABLE| W_MASK | W_MASK | W_MASK |
* door |D_TRAPPED | D_LOCKED | D_CLOSED | D_ISOPEN | D_BROKEN |
* |D_WARNED | | | | |
* sdoor |D_TRAPPED | D_LOCKED | W_MASK | W_MASK | W_MASK |
* drawbr. |DB_FLOOR | DB_ICE | DB_LAVA | DB_DIR | DB_DIR |
* sink | | | S_LRING | S_LDWASHER | S_LPUDDING |
* tree | | | | TREE_SWARM | TREE_LOOTED|
* throne | | | | | T_LOOTED |
* fountain| | | | F_WARNED | F_LOOTED |
* ladder | | | | LA_DOWN | LA_UP |
* pool |ICED_MOAT | ICED_POOL | | | |
* grave | | | | | emptygrave |
* altar |AM_SANCTUM | AM_SHRINE | AM_MASK | AM_MASK | AM_MASK |
* | | | | | |
* +-------------+-------------+------------+------------+------------+
*
*
* If these get changed or expanded, make sure wizard-mode wishing becomes
* aware of the new usage
*
* Note: secret doors (SDOOR) want to use both rm.doormask and
* rm.wall_info but those both overload rm.flags. SDOOR only
* has 2 states (closed or locked). However, it can't specify
* D_CLOSED due to that conflicting with WM_MASK (below). When
* a secret door is revealed, the door gets set to D_CLOSED iff
* it isn't set to D_LOCKED (see cvt_sdoor_to_door() in detect.c).
*
* D_LOCKED conflicts with W_NONDIGGABLE but the latter is not
* expected to be used on door locations.
* D_TRAPPED conflicts with W_NONPASSWALL.
* D_SECRET would not fit within struct rm's 5-bit 'flags' field.
*/
#define doormask flags /* door, sdoor (note conflict with wall_info) */
#define altarmask flags /* alignment and maybe temple */
#define wall_info flags /* wall, sdoor (note conflict with doormask) */
#define ladder flags /* up or down */
#define drawbridgemask flags /* what's underneath when the span is open */
#define looted flags /* used for throne, tree, fountain, sink, door */
#define icedpool flags /* used for ice (in case it melts) */
#define emptygrave flags /* no corpse in grave */
/* candig is used for floor trap locations so is available for overload
on walls, doors, secret doors, and furniture */
#define arboreal_sdoor candig
/*
* The 5 possible states of doors.
* For historical reasons they are numbered as mask bits rather than 0..4.
* The trapped flag is OR'd onto the state and only valid if that state
* is closed or locked.
* The no-door state allows egress when moving diagonally, others do not.
*/
#define D_NODOOR 0x00
#define D_BROKEN 0x01
#define D_ISOPEN 0x02
#define D_CLOSED 0x04
#define D_LOCKED 0x08
#define D_TRAPPED 0x10
#define D_SECRET 0x20 /* only used by sp_lev.c, NOT in rm-struct */
/*
* Thrones should only be looted once.
*/
#define T_LOOTED 1
/*
* Trees have more than one kick result.
*/
#define TREE_LOOTED 1
#define TREE_SWARM 2
/*
* Fountains have limits, and special warnings.
*/
#define F_LOOTED 1
#define F_WARNED 2
#define FOUNTAIN_IS_WARNED(x, y) (levl[x][y].looted & F_WARNED)
#define FOUNTAIN_IS_LOOTED(x, y) (levl[x][y].looted & F_LOOTED)
#define SET_FOUNTAIN_WARNED(x, y) levl[x][y].looted |= F_WARNED;
#define SET_FOUNTAIN_LOOTED(x, y) levl[x][y].looted |= F_LOOTED;
#define CLEAR_FOUNTAIN_WARNED(x, y) levl[x][y].looted &= ~F_WARNED;
#define CLEAR_FOUNTAIN_LOOTED(x, y) levl[x][y].looted &= ~F_LOOTED;
/*
* doors are even worse :-) The special warning has a side effect
* of instantly trapping the door, and if it was defined as trapped,
* the guards consider that you have already been warned!
*/
#define D_WARNED 16
/*
* Sinks have 3 different types of loot that shouldn't be abused
*/
#define S_LPUDDING 1
#define S_LDWASHER 2
#define S_LRING 4
/*
* The four directions for a DrawBridge.
*/
#define DB_NORTH 0
#define DB_SOUTH 1
#define DB_EAST 2
#define DB_WEST 3
#define DB_DIR 3 /* mask for direction */
/*
* What's under a drawbridge.
*/
#define DB_MOAT 0
#define DB_LAVA 4
#define DB_ICE 8
#define DB_FLOOR 16
#define DB_UNDER 28 /* mask for underneath */
/*
* Wall information. Nondiggable also applies to iron bars.
*/
#define WM_MASK 0x07 /* wall mode (bottom three bits) */
#define W_NONDIGGABLE 0x08
#define W_NONPASSWALL 0x10
/*
* Ladders (in Vlad's tower) may be up or down.
*/
#define LA_UP 1
#define LA_DOWN 2
/*
* Room areas may be iced pools
*/
#define ICED_POOL 8
#define ICED_MOAT 16
/* light states for terrain replacements, for set_levltyp_lit */
#define SET_LIT_RANDOM -1
#define SET_LIT_NOCHANGE -2
#define CAN_OVERWRITE_TERRAIN(ttyp) \
(iflags.debug_overwrite_stairs || !((ttyp) == LADDER || (ttyp) == STAIRS))
/*
* Add wall angle viewing by defining "modes" for each wall type. Each
* mode describes which parts of a wall are finished (seen as wall)
* and which are unfinished (seen as rock).
*
* We use the bottom 3 bits of the flags field for the mode. This comes
* in conflict with secret doors, but we avoid problems because until
* a secret door becomes discovered, we know what sdoor's bottom three
* bits are.
*
* The following should cover all of the cases.
*
* type mode Examples: R=rock, F=finished
* ----- ---- ----------------------------
* WALL: 0 none hwall, mode 1
* 1 left/top (1/2 rock) RRR
* 2 right/bottom (1/2 rock) ---
* FFF
*
* CORNER: 0 none trcorn, mode 2
* 1 outer (3/4 rock) FFF
* 2 inner (1/4 rock) F+-
* F|R
*
* TWALL: 0 none tlwall, mode 3
* 1 long edge (1/2 rock) F|F
* 2 bottom left (on a tdwall) -+F
* 3 bottom right (on a tdwall) R|F
*
* CRWALL: 0 none crwall, mode 5
* 1 top left (1/4 rock) R|F
* 2 top right (1/4 rock) -+-
* 3 bottom left (1/4 rock) F|R
* 4 bottom right (1/4 rock)
* 5 top left & bottom right (1/2 rock)
* 6 bottom left & top right (1/2 rock)
*/
#define WM_W_LEFT 1 /* vertical or horizontal wall */
#define WM_W_RIGHT 2
#define WM_W_TOP WM_W_LEFT
#define WM_W_BOTTOM WM_W_RIGHT
#define WM_C_OUTER 1 /* corner wall */
#define WM_C_INNER 2
#define WM_T_LONG 1 /* T wall */
#define WM_T_BL 2
#define WM_T_BR 3
#define WM_X_TL 1 /* cross wall */
#define WM_X_TR 2
#define WM_X_BL 3
#define WM_X_BR 4
#define WM_X_TLBR 5
#define WM_X_BLTR 6
/*
* Seen vector values. The seen vector is an array of 8 bits, one for each
* octant around a given center x:
*
* 0 1 2
* 7 x 3
* 6 5 4
*
* In the case of walls, a single wall square can be viewed from 8 possible
* directions. If we know the type of wall and the directions from which
* it has been seen, then we can determine what it looks like to the hero.
*/
#define SV0 ((seenV) 0x01)
#define SV1 ((seenV) 0x02)
#define SV2 ((seenV) 0x04)
#define SV3 ((seenV) 0x08)
#define SV4 ((seenV) 0x10)
#define SV5 ((seenV) 0x20)
#define SV6 ((seenV) 0x40)
#define SV7 ((seenV) 0x80)
#define SVALL ((seenV) 0xFF)
/* horizontal applies to walls, doors (including sdoor); also to iron bars
even though they don't have separate symbols for horizontal and vertical */
#define blessedftn horizontal /* a fountain that grants attribs */
#define disturbed horizontal /* kicking or engraving on a grave's headstone
* has summoned a ghoul */
struct damage {
struct damage *next;
long when, cost;
coord place;
schar typ; /* from struct rm */
uchar flags; /* also from struct rm; an unsigned 5-bit field there */
};
/* for bones levels: identify the dead character, who might have died on
an existing bones level; if so, most recent victim will be first in list */
struct cemetery {
struct cemetery *next; /* next struct is previous dead character... */
/* "svp.plname" + "-ROLe" + "-RACe" + "-GENder" + "-ALIgnment" + \0 */
char who[PL_NSIZ + 4 * (1 + 3) + 1];
/* death reason, same as in score/log file */
char how[100 + 1]; /* [DTHSZ+1] */
/* date+time in string of digits rather than binary */
char when[4 + 2 + 2 + 2 + 2 + 2 + 1]; /* "YYYYMMDDhhmmss\0" */
/* final resting place spot */
coordxy frpx, frpy;
boolean bonesknown;
};
struct levelflags {
uchar nfountains; /* number of fountains on level */
uchar nsinks; /* number of sinks on the level */
/* Several flags that give hints about what's on the level */
Bitfield(has_shop, 1);
Bitfield(has_vault, 1);
Bitfield(has_zoo, 1);
Bitfield(has_court, 1);
Bitfield(has_morgue, 1);
Bitfield(has_beehive, 1);
Bitfield(has_barracks, 1);
Bitfield(has_temple, 1);
Bitfield(has_swamp, 1);
Bitfield(noteleport, 1);
Bitfield(hardfloor, 1);
Bitfield(nommap, 1);
Bitfield(hero_memory, 1); /* hero has memory */
Bitfield(shortsighted, 1); /* monsters are shortsighted */
Bitfield(graveyard, 1); /* has_morgue, but remains set */
Bitfield(sokoban_rules, 1); /* fill pits and holes w/ boulders */
Bitfield(is_maze_lev, 1);
Bitfield(is_cavernous_lev, 1);
Bitfield(arboreal, 1); /* Trees replace rock */
Bitfield(has_town, 1); /* level contains a town */
Bitfield(wizard_bones, 1); /* set if level came from a bones file
* which was created in wizard mode (or
* normal mode descendant of such) */
Bitfield(corrmaze, 1); /* Whether corridors are used for the maze
* rather than ROOM */
Bitfield(rndmongen, 1); /* random monster generation allowed? */
Bitfield(deathdrops, 1); /* monsters may drop corpses/death drops */
Bitfield(noautosearch, 1); /* automatic searching disabled */
Bitfield(fumaroles, 1); /* lava emits poison gas at random */
Bitfield(stormy, 1); /* clouds create lightning bolts at random */
schar temperature; /* +1 == hot, -1 == cold */
};
typedef struct {
struct rm locations[COLNO][ROWNO];
struct obj *objects[COLNO][ROWNO];
struct monst *monsters[COLNO][ROWNO];
struct obj *objlist;
struct obj *buriedobjlist;
struct monst *monlist;
struct damage *damagelist;
struct cemetery *bonesinfo;
struct levelflags flags;
} dlevel_t;
/*
* Macros for compatibility with old code. Someday these will go away.
*/
#define levl svl.level.locations
#define fobj svl.level.objlist
#define fmon svl.level.monlist
/*
* Convert a trap number into the defsym graphics array.
* Convert a defsym number into a trap number.
* Assumes that arrow trap will always be the first trap.
*/
#define trap_to_defsym(t) (S_arrow_trap + (t) - 1)
#define defsym_to_trap(d) ((d) - S_arrow_trap + 1)
#define OBJ_AT(x, y) (svl.level.objects[x][y] != (struct obj *) 0)
/*
* Macros for encapsulation of level.monsters references.
*/
#if 0
/* these wouldn't allow buried monster and surface monster at same location */
#define MON_AT(x, y) \
(svl.level.monsters[x][y] && !svl.level.monsters[x][y]->mburied)
#define MON_BURIED_AT(x, y) \
(svl.level.monsters[x][y] && svl.level.monsters[x][y]->mburied)
#define m_at(x, y) \
(MON_AT(x, y) ? svl.level.monsters[x][y] : (struct monst *) 0)
#define m_buried_at(x, y) \
(MON_BURIED_AT(x, y) ? svl.level.monsters[x][y] : (struct monst *) 0)
#else /* without 'mburied' */
#define MON_AT(x, y) (svl.level.monsters[x][y] != (struct monst *) 0)
#define m_at(x, y) (svl.level.monsters[x][y])
#define m_buried_at(x, y) ((struct monst *) 0)
#endif
#ifdef EXTRA_SANITY_CHECKS
#define place_worm_seg(m, x, y) \
do { \
if (svl.level.monsters[x][y] && svl.level.monsters[x][y] != m) \
impossible("place_worm_seg over mon"); \
svl.level.monsters[x][y] = m; \
} while(0)
#define remove_monster(x, y) \
do { \
if (!svl.level.monsters[x][y]) \
impossible("no monster to remove"); \
svl.level.monsters[x][y] = (struct monst *) 0; \
} while(0)
#else
#define place_worm_seg(m, x, y) svl.level.monsters[x][y] = m
#define remove_monster(x, y) svl.level.monsters[x][y] = (struct monst *) 0
#endif
/* restricted movement, potential luck penalties */
#define Sokoban svl.level.flags.sokoban_rules
/*
* These prototypes are in extern.h but some of the code which uses them
* includes config.h instead of hack.h so doesn't see extern.h.
*/
/* ### drawing.c ### */
extern int def_char_to_objclass(char);
extern int def_char_to_monclass(char);
extern int def_char_is_furniture(char);
#endif /* RM_H */