}
}
+static void describe_location(void) {
+ 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;
+
+ if (!FORCED(game.loc) && DARK(game.loc)) {
+ msg = arbitrary_messages[PITCH_DARK];
+ }
+
+ if (TOTING(BEAR))
+ rspeak(TAME_BEAR);
+
+ speak(msg);
+
+ if (game.loc == LOC_Y2 && PCT(25) && !game.closng) // FIXME: magic number
+ rspeak(SAYS_PLUGH);
+}
+
+
static bool traveleq(int a, int b)
/* Are two travel entries equal for purposes of skip after failed condition? */
{
* does, game.newloc will be limbo, and game.oldloc will be what killed
* him, so we need game.oldlc2, which is the last place he was
* safe.) */
-static void playermove( int motion)
+static void playermove(int motion)
{
int scratchloc, travel_entry = tkey[game.loc];
game.newloc = game.loc;
void clear_command(command_t *cmd)
{
cmd->verb = ACT_NULL;
+ cmd->part = unknown;
game.oldobj = cmd->obj;
cmd->obj = NO_OBJECT;
-}
-
-void close_cleanup_before_command(void)
-/* 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, true, 1);
- for (size_t i = 1; i <= NOBJECTS; i++) {
- if (TOTING(i) && game.prop[i] < 0)
- game.prop[i] = STASHED(i);
- }
- }
+ cmd->state = EMPTY;
}
bool preprocess_command(command_t *command)
command->word[0].type = ACTION;
}
}
-
+ command->state = PREPROCESSED;
return true;
}
return false;
/* Get and execute a command */
{
static command_t command;
- bool command_given = false;
- bool command_executed = false;
+ command.state = EMPTY;
/* Can't leave cave once it's closing (except by main office). */
if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) {
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 (game.loc == LOC_NOWHERE) {
+ croak();
+ }
+
+ /* The easiest way to get killed is to fall into a pit in
+ * pitch darkness. */
+ if (!FORCED(game.loc) && DARK(game.loc) && game.wzdark && PCT(35)) { // FIXME: magic number
+ rspeak(PIT_FALL);
+ game.oldlc2 = game.loc;
+ croak();
+ return true;
+ }
+
+ /* Describe the current location and (maybe) get next command. */
+ while (command.state != EXECUTED) {
+ describe_location();
+
if (FORCED(game.loc)) {
playermove(HERE);
return true;
}
- if (game.loc == LOC_Y2 && PCT(25) && !game.closng)
- rspeak(SAYS_PLUGH);
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 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++) {
+ if (TOTING(i) && game.prop[i] < 0)
+ game.prop[i] = STASHED(i);
+ }
+ }
- // Keep getting commands from user until valid command is both given and executed.
- do {
- // Check some quick-time game state items, get input from user, increment turn,
- // and pre-process commands. Keep going until pre-processing is done.
- do {
- checkhints();
- close_cleanup_before_command();
+ /* 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)
+ game.knfloc = LOC_NOWHERE;
- game.wzdark = DARK(game.loc);
- if (game.knfloc > 0 && game.knfloc != game.loc)
- game.knfloc = 0;
+ /* 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
+ /* Get command input from user */
if (!get_command_input(&command))
return false;
++game.turns;
- } while ( !preprocess_command(&command));
- command_given = true;
+ preprocess_command(&command);
+ }
- // check if game is closed
+ /* check if game is closed, and exit if it is */
if (closecheck() )
return true;
- // loop until all words in command are procesed
- do {
- // assume all words in command are processed, until proven otherwise
- command_executed = true;
+ /* loop until all words in command are procesed */
+ 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);
- command_given = false;
continue;
}
- // Give user hints of shortcuts
+ /* Give user hints of shortcuts */
if (strncasecmp(command.word[0].raw, "west", sizeof("west")) == 0) {
if (++game.iwest == 10)
rspeak(W_IS_WEST);
case NO_WORD_TYPE: // FIXME: treating NO_WORD_TYPE as a motion word is confusing
case MOTION:
playermove(command.word[0].id);
- return true;
+ command.state = EXECUTED;
+ continue;
case OBJECT:
command.part = unknown;
command.obj = command.word[0].id;
if (!settings.oldstyle) {
sspeak(DONT_KNOW, command.word[0].raw);
clear_command(&command);
- command_given = false;
continue;
}
break;
switch (action(command)) {
case GO_TERMINATE:
- return true;
+ command.state = EXECUTED;
+ break;
case GO_MOVE:
playermove(NUL);
- return true;
- case GO_TOP:
- continue; /* back to top of main interpreter loop */
- case GO_CLEAROBJ:
- clear_command(&command);
- /* FALL THROUGH */
- case GO_CHECKHINT:
- command_given = false;
+ command.state = EXECUTED;
break;
case GO_WORD2:
#ifdef GDEBUG
/* Get second word for analysis. */
command.word[0] = command.word[1];
command.word[1] = empty_command_word;
- command_executed = false;
+ 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 = 0;
- command_given = false;
+ command.obj = NO_OBJECT;
+
+ /* object cleared; we need to go back to the preprocessing step */
+ 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;
+ case GO_CHECKHINT: // FIXME: re-name to be more contextual; this was previously a label
+ command.state = GIVEN;
+ break;
default: // LCOV_EXCL_LINE
BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE
}
- } while (!command_executed);
- } while (!command_given);
- }
+ } /* while command has nob been fully processed */
+ } /* while command is not yet given */
+ } /* while command is not executed */
+
+ /* command completely executed; we return true. */
+ return true;
}
/* end */