-static bool do_command(FILE *cmdin)
-/* Get and execute a command */
-{
- long verb = 0, V1, V2;
- long kmod, defn;
- static long igo = 0;
- static long obj = 0;
- enum speechpart part;
-
- /* Can't leave cave once it's closing (except by main office). */
- if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
- RSPEAK(EXIT_CLOSED);
- game.newloc = game.loc;
- if (!game.panic)game.clock2 = PANICTIME;
- game.panic = true;
- }
-
- /* See if a dwarf has seen him and has come from where he
- * wants to go. If so, the dwarf's blocking his way. If
- * coming from place forbidden to pirate (dwarves rooted in
- * place) let him get out (and attacked). */
- if (game.newloc != game.loc && !FORCED(game.loc) && !CNDBIT(game.loc, NOARRR)) {
- for (size_t i = 1; i <= NDWARVES - 1; i++) {
- if (game.odloc[i] == game.newloc && game.dseen[i]) {
- game.newloc = game.loc;
- RSPEAK(DWARF_BLOCK);
- break;
- }
- }
- }
- game.loc = game.newloc;
-
- if (!dwarfmove())
- croak();
-
- /* Describe the current location and (maybe) get next command. */
-
- for (;;) {
- if (game.loc == 0)
- croak();
- const char* msg = locations[game.loc].description.small;
- if (MOD(game.abbrev[game.loc], game.abbnum) == 0 || msg == 0)
- msg = locations[game.loc].description.big;
- if (!FORCED(game.loc) && DARK(game.loc)) {
- /* The easiest way to get killed is to fall into a pit in
- * pitch darkness. */
- if (game.wzdark && PCT(35)) {
- RSPEAK(PIT_FALL);
- game.oldlc2 = game.loc;
- croak();
- continue; /* back to top of main interpreter loop */
- }
- msg = arbitrary_messages[PITCH_DARK];
- }
- if (TOTING(BEAR))RSPEAK(TAME_BEAR);
- speak(msg);
- if (FORCED(game.loc)) {
- if (playermove(verb, 1))
- return true;
- else
- continue; /* back to top of main interpreter loop */
- }
- if (game.loc == 33 && PCT(25) && !game.closng)RSPEAK(SAYS_PLUGH);
-
- listobjects();
-
-L2012:
- verb = 0;
- game.oldobj = obj;
- obj = 0;
-
-L2600:
- checkhints();
-
- /* If closing time, check for any objects being toted with
- * game.prop < 0 and set the prop to -1-game.prop. This way
- * objects won't be described until they've been picked up
- * and put down separate from their respective piles. Don't
- * tick game.clock1 unless well into cave (and not at Y2). */
- if (game.closed) {
- if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
- PSPEAK(OYSTER, 1);
- for (size_t i = 1; i <= NOBJECTS; i++) {
- if (TOTING(i) && game.prop[i] < 0)
- game.prop[i] = -1 - game.prop[i];
- }
- }
- game.wzdark = DARK(game.loc);
- if (game.knfloc > 0 && game.knfloc != game.loc)
- game.knfloc = 0;
-
- /* This is where we get a new command from the user */
- if (!GETIN(cmdin, &WD1, &WD1X, &WD2, &WD2X))
- return false;
-
- /* Every input, check "game.foobar" flag. If zero, nothing's
- * going on. If pos, make neg. If neg, he skipped a word,
- * so make it zero. */
-L2607:
- game.foobar = (game.foobar > 0 ? -game.foobar : 0);
- ++game.turns;
- if (game.turns == game.thresh) {
- speak(turn_threshold_messages[game.trndex]);
- game.trnluz = game.trnluz + TRNVAL[game.trndex] / 100000;
- ++game.trndex;
- game.thresh = -1;
- if (game.trndex <= TRNVLS)
- game.thresh = MOD(TRNVAL[game.trndex], 100000) + 1;
- }
- if (verb == SAY && WD2 > 0)
- verb = 0;
- if (verb == SAY) {
- part = transitive;
- goto Laction;
- }
- if (closecheck()) {
- if (game.closed)
- return true;
- } else
- lampcheck();
-
- V1 = VOCAB(WD1, -1);
- V2 = VOCAB(WD2, -1);
- if (V1 == ENTER && (V2 == STREAM || V2 == 1000 + WATER)) {
- if (LIQLOC(game.loc) == WATER) {
- RSPEAK(FEET_WET);
- } else {
- RSPEAK(WHERE_QUERY);
- }
- goto L2012;
- }
- if (V1 == ENTER && WD2 > 0) {
- WD1 = WD2;
- WD1X = WD2X;
- WD2 = 0;
- } else {
- if (!((V1 != 1000 + WATER && V1 != 1000 + OIL) ||
- (V2 != 1000 + PLANT && V2 != 1000 + DOOR))) {
- if (AT(V2 - 1000))
- WD2 = MAKEWD(16152118);
- }
- if (V1 == 1000 + CAGE && V2 == 1000 + BIRD && HERE(CAGE) && HERE(BIRD))
- WD1 = MAKEWD(301200308);
- }
-L2620:
- if (WD1 == MAKEWD(23051920)) {
- ++game.iwest;
- if (game.iwest == 10)
- RSPEAK(W_IS_WEST);
- }
- if (WD1 == MAKEWD( 715) && WD2 != 0) {
- if (++igo == 10)
- RSPEAK(GO_UNNEEDED);
- }
-Lookup:
- defn = VOCAB(WD1, -1);
- if (defn == -1) {
- /* Gee, I don't understand. */
- if (fallback_handler(rawbuf))
- continue;
- SETPRM(1, WD1, WD1X);
- RSPEAK(DONT_KNOW);
- goto L2600;
- }
- kmod = MOD(defn, 1000);
- switch (defn / 1000) {
- case 0:
- if (playermove(verb, kmod))
- return true;
- else
- continue; /* back to top of main interpreter loop */
- case 1:
- part = unknown;
- obj = kmod;
- break;
- case 2:
- part = intransitive;
- verb = kmod;
- break;
- case 3:
- RSPEAK(kmod);
- goto L2012;
- default:
- BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3);
- }
-
-Laction:
- switch (action(cmdin, part, verb, obj)) {
- case GO_TERMINATE:
- return true;
- case GO_MOVE:
- playermove(verb, NUL);
- return true;
- case GO_TOP:
- continue; /* back to top of main interpreter loop */
- case GO_CLEAROBJ:
- goto L2012;
- case GO_CHECKHINT:
- goto L2600;
- case GO_CHECKFOO:
- goto L2607;
- case GO_LOOKUP:
- goto Lookup;
- case GO_WORD2:
- /* Get second word for analysis. */
- WD1 = WD2;
- WD1X = WD2X;
- WD2 = 0;
- goto L2620;
- case GO_UNKNOWN:
- /* Random intransitive verbs come here. Clear obj just in case
- * (see attack()). */
- SETPRM(1, WD1, WD1X);
- RSPEAK(DO_WHAT);
- obj = 0;
- goto L2600;
- case GO_DWARFWAKE:
- /* Oh dear, he's disturbed the dwarves. */
- RSPEAK(DWARVES_AWAKEN);
- terminate(endgame);
- default:
- BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH);
- }
- }
+static bool do_command(void) {
+ /* Get and execute a command */
+ static command_t command;
+ clear_command(&command);
+
+ /* Describe the current location and (maybe) get next command. */
+ while (command.state != EXECUTED) {
+ describe_location();
+
+ if (FORCED(game.loc)) {
+ playermove(HERE);
+ return true;
+ }
+
+ listobjects();
+
+ /* Command not yet given; keep getting commands from user
+ * until valid command is both given and executed. */
+ clear_command(&command);
+ while (command.state <= GIVEN) {
+
+ if (game.closed) {
+ /* If closing time, check for any stashed
+ * objects being toted and unstash them. This
+ * way objects won't be described until they've
+ * been picked up and put down separate from
+ * their respective piles. */
+ if ((OBJECT_IS_NOTFOUND(OYSTER) ||
+ OBJECT_IS_STASHED(OYSTER)) &&
+ TOTING(OYSTER)) {
+ pspeak(OYSTER, look, true, 1);
+ }
+ for (size_t i = 1; i <= NOBJECTS; i++) {
+ if (TOTING(i) &&
+ (OBJECT_IS_NOTFOUND(i) ||
+ OBJECT_IS_STASHED(i))) {
+ OBJECT_STASHIFY(
+ i, game.objects[i].prop);
+ }
+ }
+ }
+
+ /* Check to see if the room is dark. */
+ game.wzdark = IS_DARK_HERE();
+
+ /* If the knife is not here it permanently disappears.
+ * Possibly this should fire if the knife is here but
+ * the room is dark? */
+ if (game.knfloc > LOC_NOWHERE &&
+ game.knfloc != game.loc) {
+ game.knfloc = LOC_NOWHERE;
+ }
+
+ /* Check some for hints, get input from user, increment
+ * turn, and pre-process commands. Keep going until
+ * pre-processing is done. */
+ while (command.state < PREPROCESSED) {
+ checkhints();
+
+ /* Get command input from user */
+ if (!get_command_input(&command)) {
+ return false;
+ }
+
+ /* Every input, check "foobar" flag. If zero,
+ * nothing's going on. If pos, make neg. If neg,
+ * he skipped a word, so make it zero.
+ */
+ game.foobar = (game.foobar > WORD_EMPTY)
+ ? -game.foobar
+ : WORD_EMPTY;
+
+ ++game.turns;
+ preprocess_command(&command);
+ }
+
+ /* check if game is closed, and exit if it is */
+ if (closecheck()) {
+ return true;
+ }
+
+ /* loop until all words in command are processed */
+ while (command.state == PREPROCESSED) {
+ command.state = PROCESSING;
+
+ if (command.word[0].id == WORD_NOT_FOUND) {
+ /* Gee, I don't understand. */
+ sspeak(DONT_KNOW, command.word[0].raw);
+ clear_command(&command);
+ continue;
+ }
+
+ /* Give user hints of shortcuts */
+ if (strncasecmp(command.word[0].raw, "west",
+ sizeof("west")) == 0) {
+ if (++game.iwest == 10) {
+ rspeak(W_IS_WEST);
+ }
+ }
+ if (strncasecmp(command.word[0].raw, "go",
+ sizeof("go")) == 0 &&
+ command.word[1].id != WORD_EMPTY) {
+ if (++game.igo == 10) {
+ rspeak(GO_UNNEEDED);
+ }
+ }
+
+ switch (command.word[0].type) {
+ case MOTION:
+ playermove(command.word[0].id);
+ command.state = EXECUTED;
+ continue;
+ case OBJECT:
+ command.part = unknown;
+ command.obj = command.word[0].id;
+ break;
+ case ACTION:
+ if (command.word[1].type == NUMERIC) {
+ command.part = transitive;
+ } else {
+ command.part = intransitive;
+ }
+ command.verb = command.word[0].id;
+ break;
+ case NUMERIC:
+ if (!settings.oldstyle) {
+ sspeak(DONT_KNOW,
+ command.word[0].raw);
+ clear_command(&command);
+ continue;
+ }
+ break; // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ case NO_WORD_TYPE: // LCOV_EXCL_LINE
+ BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
+ }
+
+ switch (action(command)) {
+ case GO_TERMINATE:
+ command.state = EXECUTED;
+ break;
+ case GO_MOVE:
+ playermove(NUL);
+ command.state = EXECUTED;
+ break;
+ case GO_WORD2:
+#ifdef GDEBUG
+ printf("Word shift\n");
+#endif /* GDEBUG */
+ /* Get second word for analysis. */
+ command.word[0] = command.word[1];
+ command.word[1] = empty_command_word;
+ command.state = PREPROCESSED;
+ break;
+ case GO_UNKNOWN:
+ /* Random intransitive verbs come here.
+ * Clear obj just in case (see
+ * attack()). */
+ command.word[0].raw[0] =
+ toupper(command.word[0].raw[0]);
+ sspeak(DO_WHAT, command.word[0].raw);
+ command.obj = NO_OBJECT;
+
+ /* object cleared; we need to go back to
+ * the preprocessing step */
+ command.state = GIVEN;
+ break;
+ case GO_CHECKHINT: // FIXME: re-name to be more
+ // contextual; this was
+ // previously a label
+ command.state = GIVEN;
+ break;
+ case GO_DWARFWAKE:
+ /* Oh dear, he's disturbed the dwarves.
+ */
+ rspeak(DWARVES_AWAKEN);
+ terminate(endgame);
+ case GO_CLEAROBJ: // FIXME: re-name to be more
+ // contextual; this was
+ // previously a label
+ clear_command(&command);
+ break;
+ case GO_TOP: // FIXME: re-name to be more
+ // contextual; this was previously
+ // a label
+ break;
+ default: // LCOV_EXCL_LINE
+ BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
+ }
+ } /* while command has not been fully processed */
+ } /* while command is not yet given */
+ } /* while command is not executed */
+
+ /* command completely executed; we return true. */
+ return true;
+}
+
+/*
+ * MAIN PROGRAM
+ *
+ * Adventure (rev 2: 20 treasures)
+ * History: Original idea & 5-treasure version (adventures) by Willie Crowther
+ * 15-treasure version (adventure) by Don Woods, April-June 1977
+ * 20-treasure version (rev 2) by Don Woods, August 1978
+ * Errata fixed: 78/12/25
+ * Revived 2017 as Open Adventure.
+ */
+
+int main(int argc, char *argv[]) {
+ int ch;
+
+ /* Options. */
+
+#if defined ADVENT_AUTOSAVE
+ const char *opts = "dl:oa:";
+ const char *usage =
+ "Usage: %s [-l logfilename] [-o] [-a filename] [script...]\n";
+ FILE *rfp = NULL;
+ const char *autosave_filename = NULL;
+#elif !defined ADVENT_NOSAVE
+ const char *opts = "dl:or:";
+ const char *usage = "Usage: %s [-l logfilename] [-o] [-r "
+ "restorefilename] [script...]\n";
+ FILE *rfp = NULL;
+#else
+ const char *opts = "dl:o";
+ const char *usage = "Usage: %s [-l logfilename] [-o] [script...]\n";
+#endif
+ while ((ch = getopt(argc, argv, opts)) != EOF) {
+ switch (ch) {
+ case 'd': // LCOV_EXCL_LINE
+ settings.debug += 1; // LCOV_EXCL_LINE
+ break; // LCOV_EXCL_LINE
+ case 'l':
+ settings.logfp = fopen(optarg, "w");
+ if (settings.logfp == NULL) {
+ fprintf(
+ stderr,
+ "advent: can't open logfile %s for write\n",
+ optarg);
+ }
+ signal(SIGINT, sig_handler);
+ break;
+ case 'o':
+ settings.oldstyle = true;
+ settings.prompt = false;
+ break;
+#ifdef ADVENT_AUTOSAVE
+ case 'a':
+ rfp = fopen(optarg, READ_MODE);
+ autosave_filename = optarg;
+ signal(SIGHUP, sig_handler);
+ signal(SIGTERM, sig_handler);
+ break;
+#elif !defined ADVENT_NOSAVE
+ case 'r':
+ rfp = fopen(optarg, "r");
+ if (rfp == NULL) {
+ fprintf(stderr,
+ "advent: can't open save file %s for "
+ "read\n",
+ optarg);
+ }
+ break;
+#endif
+ default:
+ fprintf(stderr, usage, argv[0]);
+ fprintf(stderr, " -l create a log file of your "
+ "game named as specified'\n");
+ fprintf(stderr,
+ " -o 'oldstyle' (no prompt, no command "
+ "editing, displays 'Initialising...')\n");
+#if defined ADVENT_AUTOSAVE
+ fprintf(stderr, " -a automatic save/restore "
+ "from specified saved game file\n");
+#elif !defined ADVENT_NOSAVE
+ fprintf(stderr, " -r restore from specified "
+ "saved game file\n");
+#endif
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ /* copy invocation line part after switches */
+ settings.argc = argc - optind;
+ settings.argv = argv + optind;
+ settings.optind = 0;
+
+ /* Initialize game variables */
+ int seedval = initialise();
+
+#if !defined ADVENT_NOSAVE
+ if (!rfp) {
+ game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU],
+ arbitrary_messages[CAVE_NEARBY],
+ arbitrary_messages[NO_MESSAGE]);
+ if (game.novice) {
+ game.limit = NOVICELIMIT;
+ }
+ } else {
+ restore(rfp);
+#if defined ADVENT_AUTOSAVE
+ score(scoregame);
+#endif
+ }
+#if defined ADVENT_AUTOSAVE
+ if (autosave_filename != NULL) {
+ if ((autosave_fp = fopen(autosave_filename, WRITE_MODE)) ==
+ NULL) {
+ perror(autosave_filename);
+ return EXIT_FAILURE;
+ }
+ autosave();
+ }
+#endif
+#else
+ game.novice = yes_or_no(arbitrary_messages[WELCOME_YOU],
+ arbitrary_messages[CAVE_NEARBY],
+ arbitrary_messages[NO_MESSAGE]);
+ if (game.novice) {
+ game.limit = NOVICELIMIT;
+ }
+#endif
+
+ if (settings.logfp) {
+ fprintf(settings.logfp, "seed %d\n", seedval);
+ }
+
+ /* interpret commands until EOF or interrupt */
+ for (;;) {
+ // if we're supposed to move, move
+ if (!do_move()) {
+ continue;
+ }
+
+ // get command
+ if (!do_command()) {
+ break;
+ }
+ }
+ /* show score and exit */
+ terminate(quitgame);