-static bool do_command()
-/* Get and execute a command */
-{
- long kmod, defn;
- static long igo = 0;
- static struct command_t command;
- command.verb = 0;
-
- /* 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, COND_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)) {
- playermove(HERE);
- return true;
- }
- if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
- rspeak(SAYS_PLUGH);
-
- listobjects();
-
-L2012:
- game.oldobj = command.obj;
-
-L2600:
- checkhints();
-
- /* If closing time, check for any objects being toted with
- * game.prop < 0 and stash them. This way objects won't be
- * described until they've been picked up and put down
- * separate from their respective piles. */
- if (game.closed) {
- if (game.prop[OYSTER] < 0 && TOTING(OYSTER))
- pspeak(OYSTER, look, 1, true);
- for (size_t i = 1; i <= NOBJECTS; i++) {
- if (TOTING(i) && game.prop[i] < 0)
- game.prop[i] = STASHED(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 */
- char* input;
- char inputbuf[LINESIZE];
-
- for (;;) {
- input = get_input();
- if (input == NULL)
- return (false);
- if (word_count(input) > 2) {
- rspeak(TWO_WORDS);
- free(input);
- continue;
- }
- if (strcmp(input, "") != 0)
- break;
- free(input);
- }
-
- strncpy(inputbuf, input, LINESIZE - 1);
- free(input);
-
- tokenize(inputbuf, &command);
-
- char word1[TOKLEN + 1];
- char word2[TOKLEN + 1];
- packed_to_token(command.wd1, word1);
- packed_to_token(command.wd2, word2);
- command.id1 = get_vocab_id(word1);
- command.id2 = get_vocab_id(word2);
-
-L2607:
- ++game.turns;
-
- /* If a turn threshold has been met, apply penalties and tell
- * the player about it. */
- for (int i = 0; i < NTHRESHOLDS; ++i) {
- if (game.turns == turn_thresholds[i].threshold + 1) {
- game.trnluz += turn_thresholds[i].point_loss;
- speak(turn_thresholds[i].message);
- }
- }
-
- if (command.verb == SAY && command.id2 != WORD_NOT_FOUND && command.id2 != WORD_EMPTY)
- command.verb = 0;
- if (command.verb == SAY) {
- command.part = transitive;
- goto Laction;
- }
- if (closecheck()) {
- if (game.closed)
- return true;
- } else
- lampcheck();
-
- if (command.id1 == ENTER && (command.id2 == STREAM ||
- command.id2 == PROMOTE_WORD(WATER))) {
- if (LIQLOC(game.loc) == WATER) {
- rspeak(FEET_WET);
- } else {
- rspeak(WHERE_QUERY);
- }
- goto L2012;
- }
- if (command.id1 == ENTER && command.id2 != WORD_NOT_FOUND && command.id2 != WORD_EMPTY) {
- /* command.wd1 = command.wd2; */
- /* wordclear(&command.wd2); */
- command.id1 = command.id2;
- command.id2 = WORD_EMPTY;
- } else {
- /* FIXME: Magic numbers related to vocabulary */
- if (!((command.id1 != PROMOTE_WORD(WATER) && command.id1 != PROMOTE_WORD(OIL)) ||
- (command.id2 != PROMOTE_WORD(PLANT) && command.id2 != PROMOTE_WORD(DOOR)))) {
- if (AT(DEMOTE_WORD(command.id2)))
- command.wd2 = token_to_packed("POUR");
- }
- if (command.id1 == PROMOTE_WORD(CAGE) && command.id2 == PROMOTE_WORD(BIRD) && HERE(CAGE) && HERE(BIRD))
- command.wd1 = token_to_packed("CATCH");
- }
-L2620:
- if (wordeq(command.wd1, token_to_packed("WEST"))) {
- ++game.iwest;
- if (game.iwest == 10)
- rspeak(W_IS_WEST);
- }
- if (wordeq(command.wd1, token_to_packed("GO")) && !wordempty(command.wd2)) {
- if (++igo == 10)
- rspeak(GO_UNNEEDED);
- }
-Lookup:
- packed_to_token(command.wd1, word1);
- defn = get_vocab_id(word1);
- if (defn == WORD_NOT_FOUND) {
- /* Gee, I don't understand. */
- if (fallback_handler(inputbuf))
- continue;
- sspeak(DONT_KNOW, command.raw1);
- goto L2600;
- }
- /* FIXME: magic numbers related to vocabulary */
- kmod = MOD(defn, 1000);
- switch (defn / 1000) {
- case 0:
- playermove(kmod);
- return true;
- case 1:
- command.part = unknown;
- command.obj = kmod;
- break;
- case 2:
- command.part = intransitive;
- command.verb = kmod;
- break;
- case 3:
- speak(specials[kmod].message);
- goto L2012;
- default:
- BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
- }
-
-Laction:
- switch (action(&command)) {
- case GO_TERMINATE:
- return true;
- case GO_MOVE:
- playermove(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. */
- command.wd1 = command.wd2;
- strcpy(command.raw1, command.raw2);
- wordclear(&command.wd2);
- command.raw2[0] = '\0';
- goto L2620;
- case GO_UNKNOWN:
- /* Random intransitive verbs come here. Clear obj just in case
- * (see attack()). */
- command.raw1[0] = toupper(command.raw1[0]);
- sspeak(DO_WHAT, command.raw1);
- command.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); // LCOV_EXCL_LINE
- }
- }
+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))) {
+ game.objects[i].prop =
+ OBJECT_STASHED(i);
+ }
+ }
+ }
+
+ /* Check to see if the room is dark. */
+ game.wzdark = DARK(game.loc);
+
+ /* 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);