X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=advent.h;h=0cbe7fb01648d73d01153f33e9edf10d2c60f237;hb=5d3205e1e9e29bc477e7f785a263cd43dd9cd1bd;hp=a7f5ac4c71d1814a1b653271e942c0a41b174e2e;hpb=7eaefce61d74fbc73daabd3f42f048038366b5ad;p=open-adventure.git diff --git a/advent.h b/advent.h index a7f5ac4..0cbe7fb 100644 --- a/advent.h +++ b/advent.h @@ -1,17 +1,31 @@ +/* + * Dungeon types and macros. + * + * SPDX-FileCopyrightText: 1977, 2005 by Will Crowther and Don Woods + * SPDX-FileCopyrightText: 2017 by Eric S. Raymond + * SPDX-License-Identifier: BSD-2-Clause + */ #include #include #include #include +#include #include "dungeon.h" +/* LCG PRNG parameters tested against + * Knuth vol. 2. by the original authors */ +#define LCG_A 1093L +#define LCG_C 221587L +#define LCG_M 1048576L + #define LINESIZE 1024 +#define TOKLEN 5 // # outputting characters in a token */ #define NDWARVES 6 // number of dwarves #define PIRATE NDWARVES // must be NDWARVES-1 when zero-origin #define DALTLC LOC_NUGGET // alternate dwarf location -#define INVLIMIT 7 // inverntory limit (# of objects) +#define INVLIMIT 7 // inventory limit (# of objects) #define INTRANSITIVE -1 // illegal object number -#define SPECIALBASE 300 // base number of special rooms #define GAMELIMIT 330 // base limit of turns #define NOVICELIMIT 1000 // limit of turns for novice #define WARNTIME 30 // late game starts at game.limit-this @@ -19,47 +33,107 @@ #define PANICTIME 15 // time left after closing #define BATTERYLIFE 2500 // turn limit increment from batteries #define WORD_NOT_FOUND -1 // "Word not found" flag value for the vocab hash functions. +#define WORD_EMPTY 0 // "Word empty" flag value for the vocab hash functions +#define PIT_KILL_PROB 35 // Percentage probability of dying from fall in pit. #define CARRIED -1 // Player is toting it #define READ_MODE "rb" // b is not needed for POSIX but harmless #define WRITE_MODE "wb" // b is not needed for POSIX but harmless +/* Special object-state values - integers > 0 are object-specific */ +#define STATE_NOTFOUND -1 // 'Not found" state of treasures +#define STATE_FOUND 0 // After discovered, before messed with +#define STATE_IN_CAVITY 1 // State value common to all gemstones + +/* Special fixed object-state values - integers > 0 are location */ +#define IS_FIXED -1 +#define IS_FREE 0 + +#ifndef FOUNDBOOL +/* (ESR) It is fitting that translation of the original ADVENT should + * have left us a maze of twisty little conditionals that resists all + * understanding. Setting and use of what is now the per-object state + * member (which used to be an array of its own) is our mystery. This + * state tangles together information about whether the object is a + * treasure, whether the player has seen it yet, and its activation + * state. + * + * Things we think we know: + * + * STATE_NOTFOUND is only set on treasures. Non-treasures start the + * game in STATE_FOUND. + * + * PROP_STASHED is supposed to map a state property value to a + * negative range, where the object cannot be picked up but the value + * can be recovered later. Various objects get this peoperty when + * the cave starts to close. On;y seems to be signifucant for the bird + * and readable objects, notably the clam/oyster - but the code around + * those test is difficult to read. */ +#define PROP_STASHED(obj) (-1 - game.objects[obj].prop) +#define PROP_IS_STASHED(obj) (game.objects[obj].prop < STATE_NOTFOUND) +#define PROP_IS_NOTFOUND(obj) (game.objects[obj].prop == STATE_NOTFOUND) +#define PROP_IS_FOUND(obj) (game.objects[obj].prop == STATE_FOUND) +#define PROP_IS_STASHED_OR_UNSEEN(obj) (game.objects[obj].prop < 0) +#define PROP_SET_FOUND(obj) (game.objects[obj].prop = STATE_FOUND) +#define PROP_SET_NOT_FOUND(obj) (game.objects[obj].prop = STATE_NOTFOUND) +#define PROP_IS_NOTFOUND2(g, o) (g.objects[o].prop == STATE_NOTFOUND) +#define PROP_IS_INVALID(val) (val < -MAX_STATE - 1 || val > MAX_STATE) +#else +/* (ESR) Only the boldest of adventurers will explore here. This + * alternate set of definitions for the macros above was an attempt to + * break from out of the state encoding a per-object "found" member + * telling whether or not the player has seen the object. These is one + * accompanying change in misc.c. What's broken when you try to use + * thus is PROP_IS_STASHED_OR_UNSEEN. + */ +#define PROP_STASHED(obj) (-game.objects[obj].prop) +#define PROP_IS_STASHED(obj) (game.objects[obj].prop < 0) +#define PROP_IS_NOTFOUND(obj) (!game.objects[obj].found) +#define PROP_IS_FOUND(obj) (game.objects[obj].found && game.objects[obj].prop == 0) +#define PROP_IS_STASHED_OR_UNSEEN(obj) (!game.objects[obj].found || game.objects[obj].prop < 0) +#define PROP_SET_FOUND(obj) do {game.objects[obj].found = true; game.objects[obj].prop = STATE_FOUND;} while(0) +#define PROP_SET_NOT_FOUND(obj) game.objects[obj].found = false +#define PROP_IS_NOTFOUND2(g, o) (!g.objects[o].found) +#define PROP_IS_INVALID(val) (val < -MAX_STATE || val > MAX_STATE) +#endif + +#define PROMPT "> " + /* + * DESTROY(N) = Get rid of an item by putting it in LOC_NOWHERE * MOD(N,M) = Arithmetic modulus + * TOTING(OBJ) = true if the OBJ is being carried * AT(OBJ) = true if on either side of two-placed object - * CNDBIT(L,N) = true if COND(L) has bit n set (bit 0 is units bit) - * DARK(LOC) = true if location "LOC" is dark - * FORCED(LOC) = true if LOC moves without asking for input (COND=2) - * FOREST(LOC) = true if LOC is part of the forest - * GSTONE(OBJ) = true if OBJ is a gemstone * HERE(OBJ) = true if the OBJ is at "LOC" (or is being carried) + * CNDBIT(L,N) = true if COND(L) has bit n set (bit 0 is units bit) * LIQUID() = object number of liquid in bottle * LIQLOC(LOC) = object number of liquid (if any) at LOC + * FORCED(LOC) = true if LOC moves without asking for input (COND=2) + * DARK(LOC) = true if location "LOC" is dark * PCT(N) = true N% of the time (N integer from 0 to 100) - * TOTING(OBJ) = true if the OBJ is being carried */ + * GSTONE(OBJ) = true if OBJ is a gemstone + * FOREST(LOC) = true if LOC is part of the forest + * OUTSID(LOC) = true if location not in the cave + * INSIDE(LOC) = true if location is in the cave or the building at the beginning of the game + * INDEEP(LOC) = true if location is in the Hall of Mists or deeper + * BUG(X) = report bug and exit + */ #define DESTROY(N) move(N, LOC_NOWHERE) #define MOD(N,M) ((N) % (M)) -#define TOTING(OBJ) (game.place[OBJ] == CARRIED) -#define AT(OBJ) (game.place[OBJ] == game.loc || game.fixed[OBJ] == game.loc) +#define TOTING(OBJ) (game.objects[OBJ].place == CARRIED) +#define AT(OBJ) (game.objects[OBJ].place == game.loc || game.objects[OBJ].fixed == game.loc) #define HERE(OBJ) (AT(OBJ) || TOTING(OBJ)) -#define LIQ2(PBOTL) ((1-(PBOTL))*WATER+((PBOTL)/2)*(WATER+OIL)) -#define LIQUID() (LIQ2(game.prop[BOTTLE]<0 ? -1-game.prop[BOTTLE] : game.prop[BOTTLE])) -#define LIQLOC(LOC) (LIQ2((MOD(conditions[LOC]/2*2,8)-5)*MOD(conditions[LOC]/4,2)+1)) #define CNDBIT(L,N) (tstbit(conditions[L],N)) +#define LIQUID() (game.objects[BOTTLE].prop == WATER_BOTTLE? WATER : game.objects[BOTTLE].prop == OIL_BOTTLE ? OIL : NO_OBJECT ) +#define LIQLOC(LOC) (CNDBIT((LOC),COND_FLUID)? CNDBIT((LOC),COND_OILY) ? OIL : WATER : NO_OBJECT) #define FORCED(LOC) CNDBIT(LOC, COND_FORCED) -#define DARK(DUMMY) ((!tstbit(conditions[game.loc],COND_LIT)) && (game.prop[LAMP] == LAMP_DARK || !HERE(LAMP))) +#define DARK(DUMMY) (!CNDBIT(game.loc,COND_LIT) && (game.objects[LAMP].prop == LAMP_DARK || !HERE(LAMP))) #define PCT(N) (randrange(100) < (N)) #define GSTONE(OBJ) ((OBJ) == EMERALD || (OBJ) == RUBY || (OBJ) == AMBER || (OBJ) == SAPPH) #define FOREST(LOC) CNDBIT(LOC, COND_FOREST) -#define SPECIAL(LOC) ((LOC) > SPECIALBASE) #define OUTSID(LOC) (CNDBIT(LOC, COND_ABOVE) || FOREST(LOC)) -#define INDEEP(LOC) ((LOC) >= LOC_MISTHALL && !OUTSID(LOC)) +#define INSIDE(LOC) (!OUTSID(LOC) || LOC == LOC_BUILDING) +#define INDEEP(LOC) CNDBIT((LOC),COND_DEEP) #define BUG(x) bug(x, #x) -#define MOTION_WORD(n) ((n) + 0) -#define OBJECT_WORD(n) ((n) + 1000) -#define ACTION_WORD(n) ((n) + 2000) -#define SPECIAL_WORD(n) ((n) + 3000) -#define PROMOTE_WORD(n) ((n) + 1000) -#define DEMOTE_WORD(n) ((n) - 1000) enum bugtype { SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST, @@ -79,43 +153,48 @@ enum termination {endgame, quitgame, scoregame}; enum speechpart {unknown, intransitive, transitive}; +typedef enum {NO_WORD_TYPE, MOTION, OBJECT, ACTION, NUMERIC} word_type_t; + +typedef enum scorebonus {none, splatter, defeat, victory} score_t; + /* Phase codes for action returns. * These were at one time FORTRAN line numbers. - * The values don't matter, but perturb their order at your peril. */ -enum phase_codes { +typedef enum { GO_TERMINATE, GO_MOVE, GO_TOP, GO_CLEAROBJ, GO_CHECKHINT, - GO_CHECKFOO, - GO_DIRECTION, - GO_LOOKUP, GO_WORD2, - GO_SPECIALS, GO_UNKNOWN, - GO_ACTION, GO_DWARFWAKE, -}; +} phase_codes_t; -typedef long token_t; // word token - someday this will be char[TOKLEN+1] -typedef long vocab_t; // index into a vocabulary array */ +typedef int vocab_t; // index into a vocabulary array */ +typedef int verb_t; // index into an actions array */ +typedef int obj_t; // index into the object array */ +typedef int loc_t; // index into the locations array */ +typedef int turn_t; // turn counter or threshold */ struct game_t { - unsigned long lcg_a, lcg_c, lcg_m, lcg_x; - long abbnum; // How often to print non-abbreviated descriptions - long blklin; - long bonus; - long chloc; - long chloc2; - long clock1; // # turns from finding last treasure till closing - long clock2; // # turns from first warning till blinding flash + int32_t lcg_x; + int abbnum; // How often to print int descriptions + score_t bonus; // What kind of finishing bonus we are getting + loc_t chloc; // pirate chest location + loc_t chloc2; // pirate chest alternate location + turn_t clock1; // # turns from finding last treasure to close + turn_t clock2; // # turns from warning till blinding flash bool clshnt; // has player read the clue in the endgame? bool closed; // whether we're all the way closed bool closng; // whether it's closing time yet - long conds; // min value for cond(loc) if loc has any hints - long detail; + bool lmwarn; // has player been warned about lamp going dim? + bool novice; // asked for instructions at start-up? + bool panic; // has player found out he's trapped? + bool wzdark; // whether the loc he's leaving was dark + bool blooded; // has player drunk of dragon's blood? + int conds; // min value for cond[loc] if loc has any hints + int detail; // level of detail in descriptions /* dflag controls the level of activation of dwarves: * 0 No dwarf stuff yet (wait until reaches Hall Of Mists) @@ -123,47 +202,58 @@ struct game_t { * 2 Met first dwarf, others start moving, no knives thrown yet * 3 A knife has been thrown (first set always misses) * 3+ Dwarves are mad (increases their accuracy) */ - long dflag; - - long dkill; - long dtotal; - long foobar; // current progress in saying "FEE FIE FOE FOO". - long holdng; // number of objects being carried - long iwest; // How many times he's said "west" instead of "w" - long knfloc; // 0 if no knife here, loc if knife , -1 after caveat - long limit; // lifetime of lamp (not set here) - bool lmwarn; // has player been warned about lamp going dim? - long loc; - long newloc; - bool novice; // asked for instructions at start-up? - long numdie; // number of times killed so far - long oldloc; - long oldlc2; - long oldobj; - bool panic; // has player found out he's trapped in the cave? - long saved; // point penalty for saves - long tally; - long thresh; - long trndex; - long trnluz; // # points lost so far due to number of turns used - long turns; // how many commands he's given (ignores yes/no) - bool wzdark; // whether the loc he's leaving was dark - char zzword[6]; // randomly generated magic word from bird - bool blooded; // has player drunk of dragon's blood? - long abbrev[NLOCATIONS + 1]; - long atloc[NLOCATIONS + 1]; - long dseen[NDWARVES + 1]; // true if dwarf has seen him - long dloc[NDWARVES + 1]; // location of dwarves, initially hard-wired in - long odloc[NDWARVES + 1]; // prior loc of each dwarf, initially garbage - long fixed[NOBJECTS + 1]; - long link[NOBJECTS * 2 + 1]; - long place[NOBJECTS + 1]; - long hinted[NHINTS]; // hintlc[i] is how long he's been at LOC with cond bit i - long hintlc[NHINTS]; // hinted[i] is true iff hint i has been used. - long prop[NOBJECTS + 1]; + int dflag; + + int dkill; // dwarves killed + int dtotal; // total dwarves (including pirate) in loc + int foobar; // progress in saying "FEE FIE FOE FOO". + int holdng; // number of objects being carried + int igo; // # uses of "go" instead of a direction + int iwest; // # times he's said "west" instead of "w" + loc_t knfloc; // knife location; LOC_NOWERE if none, -1 after caveat + turn_t limit; // lifetime of lamp + loc_t loc; // where player is now + loc_t newloc; // where player is going + turn_t numdie; // number of times killed so far + loc_t oldloc; // where player was + loc_t oldlc2; // where player was two moves ago + obj_t oldobj; // last object player handled + int saved; // point penalty for saves + int tally; // count of treasures gained + int thresh; // current threshold for endgame scoring tier + bool seenbigwords; // have we red the graffiti in the Giant's Room? + turn_t trnluz; // # points lost so far due to turns used + turn_t turns; // counts commands given (ignores yes/no) + char zzword[TOKLEN + 1]; // randomly generated magic word from bird + struct { + int abbrev; // has location been seen? + int atloc; // head of object linked list per location + } locs[NLOCATIONS + 1]; + struct { + int seen; // true if dwarf has seen him + loc_t loc; // location of dwarves, initially hard-wired in + loc_t oldloc; // prior loc of each dwarf, initially garbage + } dwarves[NDWARVES + 1]; + struct { +#ifdef FOUNDBOOL + bool found; // has the location of this object bween found? +#endif + loc_t fixed; // fixed location of object (if not IS_FREE) + int prop; // object state */ + loc_t place; // location of object + } objects[NOBJECTS + 1]; + struct { + bool used; // hints[i].used = true iff hint i has been used. +#ifndef FOUNDBOOL + int lc; // hints[i].lc = show int at LOC with cond bit i +#else + int lc; // hints[i].lc = show int at LOC with cond bit i +#endif + } hints[NHINTS]; + obj_t link[NOBJECTS * 2 + 1];// object-list links }; -/* +/* * Game application settings - settings, but not state of the game, per se. * This data is not saved in a saved game. */ @@ -171,62 +261,99 @@ struct settings_t { FILE *logfp; bool oldstyle; bool prompt; + char **argv; + int argc; + int optind; + FILE *scriptfp; + int debug; }; -struct command_t { +typedef struct { + char raw[LINESIZE]; + vocab_t id; + word_type_t type; +} command_word_t; + +typedef enum {EMPTY, RAW, TOKENIZED, GIVEN, PREPROCESSED, PROCESSING, EXECUTED} command_state_t; + +typedef struct { enum speechpart part; - vocab_t verb; - vocab_t obj; - token_t wd1, wd1x; - token_t wd2, wd2x; + command_word_t word[2]; + verb_t verb; + obj_t obj; + command_state_t state; +} command_t; + +/* + * Bump on save format change. + * + * Note: Verify that the tests run clean before bumping this, then rebuild the check + * files afterwards. Otherwise you will get a spurious failure due to the old version + * having been generated into a check file. + */ +#define SAVE_VERSION 30 + +/* + * Goes at start of file so saves can be identified by file(1) and the like. + */ +#define ADVENT_MAGIC "open-adventure\n" + +/* + * If you change the first three members, the resume function may not properly + * reject saves from older versions. Later members can change, but bump the version + * when you do that. + */ +struct save_t { + char magic[sizeof(ADVENT_MAGIC)]; + int32_t version; + int32_t canary; + struct game_t game; }; extern struct game_t game; +extern struct save_t save; extern struct settings_t settings; -extern char* xstrdup(const char* s); -extern void* xmalloc(size_t size); -extern void packed_to_token(long, char token[]); -extern long token_to_packed(const char token[6]); -extern void tokenize(char*, long tokens[4]); -extern void vspeak(const char*, va_list); -extern bool wordeq(token_t, token_t); -extern bool wordempty(token_t); -extern void wordclear(token_t *); +extern char *myreadline(const char *); +extern bool get_command_input(command_t *); +extern void clear_command(command_t *); extern void speak(const char*, ...); -extern void pspeak(vocab_t, enum speaktype, int, ...); +extern void sspeak(int msg, ...); +extern void pspeak(vocab_t, enum speaktype, bool, int, ...); extern void rspeak(vocab_t, ...); extern void echo_input(FILE*, const char*, const char*); -extern int word_count(char*); -extern char* get_input(void); -extern bool silent_yes(void); -extern bool yes(const char*, const char*, const char*); -extern int get_motion_vocab_id(const char*); -extern int get_object_vocab_id(const char*); -extern int get_action_vocab_id(const char*); -extern int get_special_vocab_id(const char*); -extern long get_vocab_id(const char*); -extern void juggle(long); -extern void move(long, long); -extern long put(long, long, long); -extern void carry(long, long); -extern void drop(long, long); -extern long atdwrf(long); -extern long setbit(long); -extern bool tstbit(long, int); -extern void make_zzword(char*); -extern void set_seed(long); -extern unsigned long get_next_lcg_value(void); -extern long randrange(long); -extern long score(enum termination); +extern bool silent_yes_or_no(void); +extern bool yes_or_no(const char*, const char*, const char*); +extern void juggle(obj_t); +extern void move(obj_t, loc_t); +extern void put(obj_t, loc_t, int); +extern void carry(obj_t, loc_t); +extern void drop(obj_t, loc_t); +extern int atdwrf(loc_t); +extern int setbit(int); +extern bool tstbit(int, int); +extern void set_seed(int32_t); +extern int32_t randrange(int32_t); +extern int score(enum termination); extern void terminate(enum termination) __attribute__((noreturn)); -extern int savefile(FILE *, long); +extern int savefile(FILE *); +#if defined ADVENT_AUTOSAVE +extern void autosave(void); +#endif extern int suspend(void); extern int resume(void); extern int restore(FILE *); -extern long initialise(void); -extern int action(struct command_t *command); +extern int initialise(void); +extern phase_codes_t action(command_t); +extern void state_change(obj_t, int); +extern bool is_valid(struct game_t); +extern void bug(enum bugtype, const char *) __attribute__((__noreturn__)); -void bug(enum bugtype, const char *) __attribute__((__noreturn__)); +/* represent an empty command word */ +static const command_word_t empty_command_word = { + .raw = "", + .id = WORD_EMPTY, + .type = NO_WORD_TYPE, +}; /* end */