X-Git-Url: https://jxself.org/git/?p=open-adventure.git;a=blobdiff_plain;f=main.c;h=1c1c6c4385299a0e7986fd2c91751d666dfb6761;hp=f24c5bc4162cb6480b3f232d69f338b86d663204;hb=9475552be419a8a018a27d524f6cdf64c0e26e4b;hpb=3a722da650b8f5a758f9b661c26271e714989d08 diff --git a/main.c b/main.c index f24c5bc..1c1c6c4 100644 --- a/main.c +++ b/main.c @@ -1,16 +1,7 @@ /* - * There used to be a note that said this: - * - * The author - Don Woods - apologises for the style of the code; it - * is a result of running the original Fortran IV source through a - * home-brew Fortran-to-C converter. - * - * Now that the code has been restructured into something much closer - * to idiomatic C, the following is more appropriate: - * - * ESR apologizes for the remaing gotos (now confined to one function - * in this file - there used to be over 350 of them, *everywhere*). - * Applying the Structured Program Theorem can be hard. + * Copyright (c) 1977, 2005 by Will Crowther and Don Woods + * Copyright (c) 2017 by Eric S. Raymond + * SPDX-License-Identifier: BSD-2-clause */ #include @@ -85,7 +76,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "advent: can't open save file %s for read\n", optarg); - signal(SIGINT, sig_handler); break; #endif default: @@ -133,23 +123,6 @@ int main(int argc, char *argv[]) terminate(quitgame); } -static bool fallback_handler(struct command_t command) -/* fallback handler for commands not handled by FORTRANish parser */ -{ - long sv; - char buf[LINESIZE]; - sprintf(buf, "%s %s", command.raw1, command.raw2); - - if (sscanf(buf, "seed %ld", &sv) == 1) { - set_seed(sv); - printf("Seed set to %ld\n", sv); - // autogenerated, so don't charge user time for it. - --game.turns; - return true; - } - return false; -} - /* Check if this loc is eligible for any hints. If been here long * enough, display. Ignore "HINTS" < 4 (special stuff, see database * notes). */ @@ -223,9 +196,8 @@ static void checkhints(void) break; game.hintlc[hint] = 0; return; - default: + default: // LCOV_EXCL_LINE BUG(HINT_NUMBER_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE - break; } /* Fall through to hint display */ @@ -239,6 +211,8 @@ static void checkhints(void) } } } + + } static bool spotted_by_pirate(int i) @@ -315,7 +289,7 @@ static bool dwarfmove(void) /* Dwarves move. Return true if player survives, false if he dies. */ { int kk, stick, attack; - long tk[21]; + loc_t tk[21]; /* Dwarf stuff. See earlier comments for description of * variables. Remember sixth dwarf is pirate and is thus @@ -397,7 +371,7 @@ static bool dwarfmove(void) else if (j > 1 && game.newloc == tk[j - 1]) continue; else if (j >= DIM(tk) - 1) - /* This can't actually happen. */ + /* This can't actually happen. */ continue; // LCOV_EXCL_LINE else if (game.newloc == game.dloc[i]) continue; @@ -448,7 +422,7 @@ static bool dwarfmove(void) rspeak(stick > 1 ? MULTIPLE_HITS : (stick == 1 ? ONE_HIT : NONE_HIT), stick); } else { rspeak(KNIFE_THROWN); - rspeak(MISSES_YOU); + rspeak(stick ? GETS_YOU : MISSES_YOU); } if (stick == 0) return true; @@ -475,10 +449,11 @@ static bool dwarfmove(void) * building (and heaven help him if he tries to xyzzy back into the * cave without the lamp!). game.oldloc is zapped so he can't just * "retreat". */ - static void croak(void) /* Okay, he's dead. Let's get on with it. */ { + if (game.numdie < 0) + game.numdie = 0; // LCOV_EXCL_LINE const char* query = obituaries[game.numdie].query; const char* yes_response = obituaries[game.numdie].yes_response; ++game.numdie; @@ -487,8 +462,8 @@ static void croak(void) * death and exit. */ rspeak(DEATH_CLOSING); terminate(endgame); - } else if (game.numdie == NDEATHS || - !yes(query, yes_response, arbitrary_messages[OK_MAN])) + } else if ( !yes(query, yes_response, arbitrary_messages[OK_MAN]) + || game.numdie == NDEATHS) terminate(endgame); else { game.place[WATER] = game.place[OIL] = LOC_NOWHERE; @@ -505,7 +480,7 @@ static void croak(void) } } -static bool traveleq(long a, long b) +static bool traveleq(int a, int b) /* Are two travel entries equal for purposes of skip after failed condition? */ { return (travel[a].condtype == travel[b].condtype) @@ -522,7 +497,6 @@ static bool traveleq(long a, long b) * 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) { int scratchloc, travel_entry = tkey[game.loc]; @@ -540,40 +514,39 @@ static void playermove( int motion) motion = game.oldlc2; game.oldlc2 = game.oldloc; game.oldloc = game.loc; - int spk = 0; - if (motion == game.loc) - spk = FORGOT_PATH; - if (CNDBIT(game.loc, COND_NOBACK)) - spk = TWIST_TURN; - if (spk == 0) { - int te_tmp = 0; - for (;;) { - enum desttype_t desttype = travel[travel_entry].desttype; - scratchloc = travel[travel_entry].destval; - if (desttype != dest_goto || scratchloc != motion) { - if (desttype == dest_goto) { - if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == motion) - te_tmp = travel_entry; - } - if (!travel[travel_entry].stop) { - ++travel_entry; /* go to next travel entry for this location */ - continue; - } - /* we've reached the end of travel entries for game.loc */ - travel_entry = te_tmp; - if (travel_entry == 0) { - rspeak(NOT_CONNECTED); - return; - } - } + if (CNDBIT(game.loc, COND_NOBACK)) { + rspeak(TWIST_TURN); + return; + } + if (motion == game.loc) { + rspeak(FORGOT_PATH); + return; + } - motion = travel[travel_entry].motion; - travel_entry = tkey[game.loc]; - break; /* fall through to ordinary travel */ + int te_tmp = 0; + for (;;) { + enum desttype_t desttype = travel[travel_entry].desttype; + scratchloc = travel[travel_entry].destval; + if (desttype != dest_goto || scratchloc != motion) { + if (desttype == dest_goto) { + if (FORCED(scratchloc) && travel[tkey[scratchloc]].destval == motion) + te_tmp = travel_entry; + } + if (!travel[travel_entry].stop) { + ++travel_entry; /* go to next travel entry for this location */ + continue; + } + /* we've reached the end of travel entries for game.loc */ + travel_entry = te_tmp; + if (travel_entry == 0) { + rspeak(NOT_CONNECTED); + return; + } } - } else { - rspeak(spk); - return; + + motion = travel[travel_entry].motion; + travel_entry = tkey[game.loc]; + break; /* fall through to ordinary travel */ } } else if (motion == LOOK) { /* Look. Can't give more detail. Pretend it wasn't dark @@ -668,7 +641,7 @@ static void playermove( int motion) /* We arrive here on conditional failure. * Skip to next non-matching destination */ - long te_tmp = travel_entry; + int te_tmp = travel_entry; do { if (travel[te_tmp].stop) BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE @@ -712,14 +685,16 @@ static void playermove( int motion) * to get it out. Having dropped it, go back and * pretend he wasn't carrying it after all. */ drop(EMERALD, game.loc); - int te_tmp = travel_entry; - do { - if (travel[te_tmp].stop) - BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE - ++te_tmp; - } while - (traveleq(travel_entry, te_tmp)); - travel_entry = te_tmp; + { + int te_tmp = travel_entry; + do { + if (travel[te_tmp].stop) + BUG(CONDITIONAL_TRAVEL_ENTRY_WITH_NO_ALTERATION); // LCOV_EXCL_LINE + ++te_tmp; + } while + (traveleq(travel_entry, te_tmp)); + travel_entry = te_tmp; + } continue; /* goto L12 */ case 3: /* Special travel 3. Troll bridge. Must be done @@ -733,7 +708,7 @@ static void playermove( int motion) * game.prop[TROLL]=TROLL_UNPAID.) Special stuff * for bear. */ if (game.prop[TROLL] == TROLL_PAIDONCE) { - pspeak(TROLL, look, TROLL_PAIDONCE, true); + pspeak(TROLL, look, true, TROLL_PAIDONCE); game.prop[TROLL] = TROLL_UNPAID; move(TROLL2, LOC_NOWHERE); move(TROLL2 + NOBJECTS, IS_FREE); @@ -757,7 +732,7 @@ static void playermove( int motion) croak(); return; } - default: + default: // LCOV_EXCL_LINE BUG(SPECIAL_TRAVEL_500_GT_L_GT_300_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE } } @@ -767,6 +742,50 @@ static void playermove( int motion) (false); } +static void lampcheck(void) +/* Check game limit and lamp timers */ +{ + if (game.prop[LAMP] == LAMP_BRIGHT) + --game.limit; + + /* Another way we can force an end to things is by having the + * lamp give out. When it gets close, we come here to warn him. + * First following arm checks if the lamp and fresh batteries are + * here, in which case we replace the batteries and continue. + * Second is for other cases of lamp dying. Even after it goes + * out, he can explore outside for a while if desired. */ + if (game.limit <= WARNTIME) { + if (HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) { + rspeak(REPLACE_BATTERIES); + game.prop[BATTERY] = DEAD_BATTERIES; +#ifdef __unused__ + /* This code from the original game seems to have been faulty. + * No tests ever passed the guard, and with the guard removed + * the game hangs when the lamp limit is reached. + */ + if (TOTING(BATTERY)) + drop(BATTERY, game.loc); +#endif + game.limit += BATTERYLIFE; + game.lmwarn = false; + } else if (!game.lmwarn && HERE(LAMP)) { + game.lmwarn = true; + if (game.prop[BATTERY] == DEAD_BATTERIES) + rspeak(MISSING_BATTERIES); + else if (game.place[BATTERY] == LOC_NOWHERE) + rspeak(LAMP_DIM); + else + rspeak(GET_BATTERIES); + } + } + if (game.limit == 0) { + game.limit = -1; + game.prop[LAMP] = LAMP_DARK; + if (HERE(LAMP)) + rspeak(LAMP_OUT); + } +} + static bool closecheck(void) /* Handle the closing of the cave. The cave closes "clock1" turns * after the last treasure has been located (including the pirate's @@ -834,7 +853,7 @@ static bool closecheck(void) rspeak(CAVE_CLOSING); game.clock1 = -1; game.closng = true; - return true; + return game.closed; } else if (game.clock1 < 0) --game.clock2; if (game.clock2 == 0) { @@ -883,50 +902,13 @@ static bool closecheck(void) rspeak(CAVE_CLOSED); game.closed = true; - return true; + return game.closed; } + lampcheck(); return false; } -static void lampcheck(void) -/* Check game limit and lamp timers */ -{ - if (game.prop[LAMP] == LAMP_BRIGHT) - --game.limit; - - /* Another way we can force an end to things is by having the - * lamp give out. When it gets close, we come here to warn him. - * First following arm checks if the lamp and fresh batteries are - * here, in which case we replace the batteries and continue. - * Second is for other cases of lamp dying. Eve after it goes - * out, he can explore outside for a while if desired. */ - if (game.limit <= WARNTIME) { - if (HERE(BATTERY) && game.prop[BATTERY] == FRESH_BATTERIES && HERE(LAMP)) { - rspeak(REPLACE_BATTERIES); - game.prop[BATTERY] = DEAD_BATTERIES; - if (TOTING(BATTERY)) - drop(BATTERY, game.loc); - game.limit += BATTERYLIFE; - game.lmwarn = false; - } else if (!game.lmwarn && HERE(LAMP)) { - game.lmwarn = true; - if (game.prop[BATTERY] == DEAD_BATTERIES) - rspeak(MISSING_BATTERIES); - else if (game.place[BATTERY] == LOC_NOWHERE) - rspeak(LAMP_DIM); - else - rspeak(GET_BATTERIES); - } - } - if (game.limit == 0) { - game.limit = -1; - game.prop[LAMP] = LAMP_DARK; - if (HERE(LAMP)) - rspeak(LAMP_OUT); - } -} - static void listobjects(void) /* Print out descriptions of objects at this location. If * not closing and property value is negative, tally off @@ -939,7 +921,7 @@ static void listobjects(void) if (!DARK(game.loc)) { ++game.abbrev[game.loc]; for (int i = game.atloc[game.loc]; i != 0; i = game.link[i]) { - long obj = i; + obj_t obj = i; if (obj > NOBJECTS) obj = obj - NOBJECTS; if (obj == STEPS && TOTING(NUGGET)) @@ -973,53 +955,108 @@ static void listobjects(void) kk = (game.loc == game.fixed[STEPS]) ? STEPS_UP : STEPS_DOWN; - pspeak(obj, look, kk, true); + pspeak(obj, look, true, kk); } } } -static bool get_command_input(struct command_t *command) +void clear_command(command_t *cmd) { - char inputbuf[LINESIZE]; - char word1[TOKLEN + 1]; - char word2[TOKLEN + 1]; - char* input; + cmd->verb = ACT_NULL; + game.oldobj = cmd->obj; + cmd->obj = NO_OBJECT; +} - for (;;) { - input = get_input(); - if (input == NULL) - return false; - if (word_count(input) > 2) { - rspeak(TWO_WORDS); - free(input); - continue; +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); } - if (strcmp(input, "") != 0) - break; - free(input); } +} - strncpy(inputbuf, input, LINESIZE - 1); - free(input); - - tokenize(inputbuf, command); +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 + * - : + * Irregular form of input, but should be allowed. We switch back to form for + * furtherprocessing. + * - "grate": + * 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 + * processing, false otherwise. */ +{ + if (command->word[0].type == MOTION && command->word[0].id == ENTER + && (command->word[1].id == STREAM || command->word[1].id == WATER)) { + if (LIQLOC(game.loc) == WATER) + rspeak(FEET_WET); + else + rspeak(WHERE_QUERY); + } else { + if (command->word[0].type == OBJECT) { + /* From OV to VO form */ + if (command->word[1].type == ACTION) { + command_word_t stage = command->word[0]; + command->word[0] = command->word[1]; + command->word[1] = stage; + } - packed_to_token(command->wd1, word1); - packed_to_token(command->wd2, word2); - command->id1 = get_vocab_id(word1); - command->id2 = get_vocab_id(word2); + if (command->word[0].id == GRATE) { + command->word[0].type = MOTION; + if (game.loc == LOC_START || + game.loc == LOC_VALLEY || + game.loc == LOC_SLIT) { + command->word[0].id = DEPRESSION; + } + if (game.loc == LOC_COBBLE || + game.loc == LOC_DEBRIS || + game.loc == LOC_AWKWARD || + game.loc == LOC_BIRD || + game.loc == LOC_PITTOP) { + command->word[0].id = ENTRANCE; + } + } + 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]; + command->word[0].id = POUR; + command->word[0].type = ACTION; + strncpy(command->word[0].raw, "pour", LINESIZE - 1); + } + } + if (command->word[0].id == CAGE && command->word[1].id == BIRD && HERE(CAGE) && HERE(BIRD)) { + command->word[0].id = CARRY; + command->word[0].type = ACTION; + } + } - return true; + return true; + } + return false; } static bool do_command() /* Get and execute a command */ { - long kmod, defn; - static struct command_t command; - char word1[TOKLEN + 1]; - - command.verb = 0; + static command_t command; + bool command_given = false; + bool command_executed = false; /* Can't leave cave once it's closing (except by main office). */ if (OUTSID(game.newloc) && game.newloc != 0 && game.closng) { @@ -1049,7 +1086,6 @@ static bool do_command() croak(); /* Describe the current location and (maybe) get next command. */ - for (;;) { if (game.loc == 0) croak(); @@ -1079,138 +1115,123 @@ static bool do_command() rspeak(SAYS_PLUGH); listobjects(); + clear_command(&command); -Lclearobj: - game.oldobj = command.obj; - - 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; + // 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(); - // Get command input from user - if (!get_command_input(&command)) - return false; + game.wzdark = DARK(game.loc); + if (game.knfloc > 0 && game.knfloc != game.loc) + game.knfloc = 0; -Lclosecheck: - ++game.turns; + // Get command input from user + if (!get_command_input(&command)) + return false; - if (closecheck()) { - if (game.closed) + ++game.turns; + } while ( !preprocess_command(&command)); + command_given = true; + + // check if game is closed + if (closecheck() ) 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); + // loop until all words in command are procesed + do { + // assume all words in command are processed, until proven otherwise + command_executed = true; + + 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; + } - goto Lclearobj; - } - if (command.id1 == ENTER && command.id2 != WORD_NOT_FOUND && command.id2 != WORD_EMPTY) { - 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"); - } -Lookup: - if (wordeq(command.wd1, token_to_packed("WEST"))) { - if (++game.iwest == 10) - rspeak(W_IS_WEST); - } - if (wordeq(command.wd1, token_to_packed("GO")) && !wordempty(command.wd2)) { - if (++game.igo == 10) - rspeak(GO_UNNEEDED); - } - packed_to_token(command.wd1, word1); - defn = get_vocab_id(word1); - if (defn == WORD_NOT_FOUND) { - if (fallback_handler(command)) - continue; - /* Gee, I don't understand. */ - sspeak(DONT_KNOW, command.raw1); - goto Lclearobj; - } - /* 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 Lclearobj; - default: - BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE - } + // 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 (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_CHECKFOO: - goto Lclosecheck; - case GO_LOOKUP: - goto Lookup; - case GO_WORD2: - /* Get second word for analysis. */ - command.wd1 = command.wd2; - strncpy(command.raw1, command.raw2, LINESIZE - 1); - wordclear(&command.wd2); - command.raw2[0] = '\0'; - goto Lookup; - 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; - // Fallthrough - case GO_CHECKHINT: // Fallthrough - case GO_CLEAROBJ: - goto Lclearobj; - 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 - } + switch (command.word[0].type) { + case NO_WORD_TYPE: // FIXME: treating NO_WORD_TYPE as a motion word is confusing + case MOTION: + playermove(command.word[0].id); + return true; + 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); + command_given = false; + continue; + } + break; + default: // LCOV_EXCL_LINE + BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE + } + + 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: + clear_command(&command); + /* FALL THROUGH */ + case GO_CHECKHINT: + command_given = false; + 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_executed = false; + 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; + break; + case GO_DWARFWAKE: + /* Oh dear, he's disturbed the dwarves. */ + rspeak(DWARVES_AWAKEN); + terminate(endgame); + default: // LCOV_EXCL_LINE + BUG(ACTION_RETURNED_PHASE_CODE_BEYOND_END_OF_SWITCH); // LCOV_EXCL_LINE + } + } while (!command_executed); + } while (!command_given); } }