#define DIM(a) (sizeof(a)/sizeof(a[0]))
+#if defined ADVENT_AUTOSAVE
+static FILE* autosave_fp;
+void autosave(void)
+{
+ if (autosave_fp != NULL) {
+ rewind(autosave_fp);
+ savefile(autosave_fp, /* version (auto): */0);
+ fflush(autosave_fp);
+ }
+}
+#endif
+
// LCOV_EXCL_START
// exclude from coverage analysis because it requires interactivity to test
static void sig_handler(int signo)
if (settings.logfp != NULL)
fflush(settings.logfp);
}
- exit(EXIT_FAILURE);
-}
-// LCOV_EXCL_STOP
-
-/*
- * 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.
- */
-
-static bool do_command(void);
-static bool do_move(void);
-
-int main(int argc, char *argv[])
-{
- int ch;
- /* Options. */
-
-#ifndef ADVENT_NOSAVE
- const char* opts = "l:or:";
- const char* usage = "Usage: %s [-l logfilename] [-o] [-r restorefilename] [script...]\n";
- FILE *rfp = NULL;
-#else
- const char* opts = "l:o";
- const char* usage = "Usage: %s [-l logfilename] [-o] [script...]\n";
+#if defined ADVENT_AUTOSAVE
+ if (signo == SIGHUP || signo == SIGTERM)
+ autosave();
#endif
- while ((ch = getopt(argc, argv, opts)) != EOF) {
- switch (ch) {
- 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;
-#ifndef 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");
-#ifndef ADVENT_NOSAVE
- fprintf(stderr,
- " -r restore from specified saved game file\n");
-#endif
- exit(EXIT_FAILURE);
- break;
- }
- }
-
- /* copy inncation line part after switches */
- settings.argc = argc - optind;
- settings.argv = argv + optind;
- settings.optind = 0;
-
- /* Initialize game variables */
- int seedval = initialise();
-
-#ifndef ADVENT_NOSAVE
- if (!rfp) {
- game.novice = yes(arbitrary_messages[WELCOME_YOU], arbitrary_messages[CAVE_NEARBY], arbitrary_messages[NO_MESSAGE]);
- if (game.novice)
- game.limit = NOVICELIMIT;
- } else {
- restore(rfp);
- }
-#else
- game.novice = yes(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);
+ exit(EXIT_FAILURE);
}
+// LCOV_EXCL_STOP
char *myreadline(const char *prompt)
{
/*
- * This function isbn't required for gameplay, readline() straight
- * up would suffice for tat. It's where we interpret command-line
+ * This function isn't required for gameplay, readline() straight
+ * up would suffice for that. It's where we interpret command-line
* logfiles for testing purposes.
*/
/* Normal case - no script arguments */
- if (settings.argc == 0)
- return readline(prompt);
+ if (settings.argc == 0) {
+ char *ln = readline(prompt);
+ if (ln == NULL) {
+ fputs(prompt, stdout);
+ }
+ return ln;
+ }
- char *buf = malloc(BUFSIZ);
+ char *buf = malloc(LINESIZE + 1);
for (;;) {
- if (settings.scriptfp == NULL || feof(settings.scriptfp)) {
- if (settings.optind >= settings.argc) {
- free(buf);
- return NULL;
- }
-
- char *next = settings.argv[settings.optind++];
-
- if (settings.scriptfp != NULL && feof(settings.scriptfp))
- fclose(settings.scriptfp);
- if (strcmp(next, "-") == 0)
- settings.scriptfp = stdin; // LCOV_EXCL_LINE
- else
- settings.scriptfp = fopen(next, "r");
- }
+ if (settings.scriptfp == NULL || feof(settings.scriptfp)) {
+ if (settings.optind >= settings.argc) {
+ free(buf);
+ return NULL;
+ }
+
+ char *next = settings.argv[settings.optind++];
+
+ if (settings.scriptfp != NULL && feof(settings.scriptfp))
+ fclose(settings.scriptfp);
+ if (strcmp(next, "-") == 0)
+ settings.scriptfp = stdin; // LCOV_EXCL_LINE
+ else
+ settings.scriptfp = fopen(next, "r");
+ }
- if (isatty(fileno(settings.scriptfp))) {
- free(buf); // LCOV_EXCL_LINE
- return readline(prompt); // LCOV_EXCL_LINE
- } else {
- char *ln = fgets(buf, BUFSIZ-1, settings.scriptfp);
- if (ln != NULL) {
- fputs(PROMPT, stdout);
- fputs(ln, stdout);
+ if (isatty(fileno(settings.scriptfp))) {
+ free(buf); // LCOV_EXCL_LINE
+ return readline(prompt); // LCOV_EXCL_LINE
+ } else {
+ char *ln = fgets(buf, LINESIZE, settings.scriptfp);
+ if (ln != NULL) {
+ fputs(prompt, stdout);
+ fputs(ln, stdout);
return ln;
- }
- }
+ }
+ }
}
return NULL;
game.hintlc[hint] = 0;
return;
default: // LCOV_EXCL_LINE
- // Should never hap[pen
+ // Should never happen
BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
}
/* Fall through to hint display */
game.hintlc[hint] = 0;
- if (!yes(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
+ if (!yes_or_no(hints[hint].question, arbitrary_messages[NO_MESSAGE], arbitrary_messages[OK_MAN]))
return;
rspeak(HINT_COST, hints[hint].penalty, hints[hint].penalty);
- game.hinted[hint] = yes(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
+ game.hinted[hint] = yes_or_no(arbitrary_messages[WANT_HINT], hints[hint].hint, arbitrary_messages[OK_MAN]);
if (game.hinted[hint] && game.limit > WARNTIME)
game.limit += WARNTIME * hints[hint].penalty;
}
* death and exit. */
rspeak(DEATH_CLOSING);
terminate(endgame);
- } else if (!yes(query, yes_response, arbitrary_messages[OK_MAN])
+ } else if (!yes_or_no(query, yes_response, arbitrary_messages[OK_MAN])
|| game.numdie == NDEATHS) {
- /* Player is asked if he wants to try again. If not, or if
+ /* Player is asked if he wants to try again. If not, or if
* he's already used all of his lives, we end the game */
terminate(endgame);
} else {
- /* If player wishes to continue, we empty the liquids in the
- * user's inventory, turn off the lamp, and drop all items
+ /* If player wishes to continue, we empty the liquids in the
+ * user's inventory, turn off the lamp, and drop all items
* where he died. */
game.place[WATER] = game.place[OIL] = LOC_NOWHERE;
if (TOTING(LAMP))
}
}
-static void describe_location(void)
+static void describe_location(void)
/* Describe the location to the user */
{
const char* msg = locations[game.loc].description.small;
-
+
if (MOD(game.abbrev[game.loc], game.abbnum) == 0 ||
msg == NO_MESSAGE)
msg = locations[game.loc].description.big;
rspeak(TAME_BEAR);
speak(msg);
-
+
if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
rspeak(SAYS_PLUGH);
}
/* Look for a way to fulfil the motion verb passed in - travel_entry indexes
* the beginning of the motion entries for here (game.loc). */
for (;;) {
- if (T_TERMINATE(travel[travel_entry]) ||
+ if ((travel[travel_entry].motion == HERE) ||
travel[travel_entry].motion == motion)
break;
if (travel[travel_entry].stop) {
static bool preprocess_command(command_t *command)
/* Pre-processes a command input to see if we need to tease out a few specific cases:
- * - "enter water" or "enter stream":
- * wierd specific case that gets the user wet, and then kicks us back to get another command
+ * - "enter water" or "enter stream":
+ * weird specific case that gets the user wet, and then kicks us back to get another command
* - <object> <verb>:
- * Irregular form of input, but should be allowed. We switch back to <verb> <object> form for
- * furtherprocessing.
+ * Irregular form of input, but should be allowed. We switch back to <verb> <object> form for
+ * further processing.
* - "grate":
- * If in location with grate, we move to that grate. If we're in a number of other places,
+ * If in location with grate, we move to that grate. If we're in a number of other places,
* we move to the entrance.
* - "water plant", "oil plant", "water door", "oil door":
* Change to "pour water" or "pour oil" based on context
* - "cage bird":
* If bird is present, we change to "carry bird"
*
- * Returns true if pre-processing is complete, and we're ready to move to the primary command
+ * Returns true if pre-processing is complete, and we're ready to move to the primary command
* processing, false otherwise. */
{
if (command->word[0].type == MOTION && command->word[0].id == ENTER
if (game.loc == LOC_COBBLE ||
game.loc == LOC_DEBRIS ||
game.loc == LOC_AWKWARD ||
- game.loc == LOC_BIRD ||
+ game.loc == LOC_BIRDCHAMBER ||
game.loc == LOC_PITTOP) {
command->word[0].id = ENTRANCE;
}
}
- if ((command->word[0].id == WATER || command->word[0].id == OIL) &&
+ if ((command->word[0].id == WATER || command->word[0].id == OIL) &&
(command->word[1].id == PLANT || command->word[1].id == DOOR)) {
if (AT(command->word[1].id)) {
command->word[1] = command->word[0];
}
/* If no word type is given for the first word, we assume it's a motion. */
- if(command->word[0].type == NO_WORD_TYPE)
+ if (command->word[0].type == NO_WORD_TYPE)
command->word[0].type = MOTION;
-
+
command->state = PREPROCESSED;
return true;
}
return false;
}
-static bool do_move(void)
+static bool do_move(void)
/* Actually execute the move to the new location and dwarf movement */
{
/* Can't leave cave once it's closing (except by main office). */
return true;
}
-static bool do_command()
+static bool do_command(void)
/* Get and execute a command */
{
static command_t command;
listobjects();
- /* Command not yet given; keep getting commands from user
+ /* 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 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 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.prop[OYSTER] < 0 && TOTING(OYSTER))
pspeak(OYSTER, look, true, 1);
for (size_t i = 1; i <= NOBJECTS; i++) {
}
}
- /* Check to see if the room is dark. If the knife is here,
+ /* Check to see if the room is dark. If the knife is here,
* and it's dark, the knife permanently disappears */
game.wzdark = DARK(game.loc);
if (game.knfloc != LOC_NOWHERE && game.knfloc != game.loc)
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() )
+ if (closecheck() )
return true;
- /* loop until all words in command are procesed */
+ /* loop until all words in command are processed */
while (command.state == PREPROCESSED ) {
command.state = PROCESSING;
}
break;// LCOV_EXCL_LINE
default: // LCOV_EXCL_LINE
- case NO_WORD_TYPE: // LCOV_EXCL_LINE
+ case NO_WORD_TYPE: // LCOV_EXCL_LINE
BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
}
default: // LCOV_EXCL_LINE
BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
}
- } /* while command has nob been fully processed */
+ } /* while command has not been fully processed */
} /* while command is not yet given */
} /* while command is not executed */
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':
+ settings.debug +=1;
+ break;
+ 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);
+}
+
/* end */