-int action(struct command_t *command)
-/* Analyse a verb. Remember what it was, go back for object if second word
- * unless verb is "say", which snarfs arbitrary second word.
- */
-{
- token_t spk = actions[command->verb].message;
-
- if (command->part == unknown) {
- /* Analyse an object word. See if the thing is here, whether
- * we've got a verb yet, and so on. Object must be here
- * unless verb is "find" or "invent(ory)" (and no new verb
- * yet to be analysed). Water and oil are also funny, since
- * they are never actually dropped at any location, but might
- * be here inside the bottle or urn or as a feature of the
- * location. */
- if (HERE(command->obj))
- /* FALL THROUGH */;
- else if (command->obj == GRATE) {
- if (game.loc == LOC_START || game.loc == LOC_VALLEY || game.loc == LOC_SLIT)
- command->obj = DPRSSN;
- if (game.loc == LOC_COBBLE || game.loc == LOC_DEBRIS || game.loc == LOC_AWKWARD ||
- game.loc == LOC_BIRD || game.loc == LOC_PITTOP)
- command->obj = ENTRNC;
- } else if (command->obj == DWARF && atdwrf(game.loc) > 0)
- /* FALL THROUGH */;
- else if ((LIQUID() == command->obj && HERE(BOTTLE)) || command->obj == LIQLOC(game.loc))
- /* FALL THROUGH */;
- else if (command->obj == OIL && HERE(URN) && game.prop[URN] != 0) {
- command->obj = URN;
- /* FALL THROUGH */;
- } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) {
- command->obj = PLANT2;
- /* FALL THROUGH */;
- } else if (command->obj == KNIFE && game.knfloc == game.loc) {
- game.knfloc = -1;
- spk = KNIVES_VANISH;
- rspeak(spk);
- return GO_CLEAROBJ;
- } else if (command->obj == ROD && HERE(ROD2)) {
- command->obj = ROD2;
- /* FALL THROUGH */;
- } else if ((command->verb == FIND || command->verb == INVENTORY) && command->wd2 <= 0)
- /* FALL THROUGH */;
- else {
- rspeak(NO_SEE, command->wd1, command->wd1x);
- return GO_CLEAROBJ;
- }
-
- if (command->wd2 > 0)
- return GO_WORD2;
- if (command->verb != 0)
- command->part = transitive;
- }
-
- switch (command->part) {
- case intransitive:
- if (command->wd2 > 0 && command->verb != SAY)
- return GO_WORD2;
- if (command->verb == SAY)
- command->obj = command->wd2;
- if (command->obj == 0 || command->obj == INTRANSITIVE) {
- /* Analyse an intransitive verb (ie, no object given yet). */
- switch (command->verb) {
- case CARRY:
- return vcarry(command->verb, INTRANSITIVE);
- case DROP:
- return GO_UNKNOWN;
- case SAY:
- return GO_UNKNOWN;
- case UNLOCK:
- return lock(command->verb, INTRANSITIVE);
- case NOTHING: {
- rspeak(OK_MAN);
- return (GO_CLEAROBJ);
- }
- case LOCK:
- return lock(command->verb, INTRANSITIVE);
- case LIGHT:
- return light(command->verb, INTRANSITIVE);
- case EXTINGUISH:
- return extinguish(command->verb, INTRANSITIVE);
- case WAVE:
- return GO_UNKNOWN;
- case TAME:
- return GO_UNKNOWN;
- case GO: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case ATTACK:
- return attack(command);
- case POUR:
- return pour(command->verb, command->obj);
- case EAT:
- return eat(command->verb, INTRANSITIVE);
- case DRINK:
- return drink(command->verb, command->obj);
- case RUB:
- return GO_UNKNOWN;
- case THROW:
- return GO_UNKNOWN;
- case QUIT:
- return quit();
- case FIND:
- return GO_UNKNOWN;
- case INVENTORY:
- return inven();
- case FEED:
- return GO_UNKNOWN;
- case FILL:
- return fill(command->verb, command->obj);
- case BLAST:
- blast();
- return GO_CLEAROBJ;
- case SCORE:
- score(scoregame);
- return GO_CLEAROBJ;
- case GIANTWORDS:
- return bigwords(command->wd1);
- case BRIEF:
- return brief();
- case READ:
- command->obj = INTRANSITIVE;
- return read(*command);
- case BREAK:
- return GO_UNKNOWN;
- case WAKE:
- return GO_UNKNOWN;
- case SAVE:
- return suspend();
- case RESUME:
- return resume();
- case FLY:
- return fly(command->verb, INTRANSITIVE);
- case LISTEN:
- return listen();
- case PART:
- return reservoir();
- default:
- BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- }
- }
- /* FALLTHRU */
- case transitive:
- /* Analyse a transitive verb. */
- switch (command->verb) {
- case CARRY:
- return vcarry(command->verb, command->obj);
- case DROP:
- return discard(command->verb, command->obj, false);
- case SAY:
- return say(command);
- case UNLOCK:
- return lock(command->verb, command->obj);
- case NOTHING: {
- rspeak(OK_MAN);
- return (GO_CLEAROBJ);
- }
- case LOCK:
- return lock(command->verb, command->obj);
- case LIGHT:
- return light(command->verb, command->obj);
- case EXTINGUISH:
- return extinguish(command->verb, command->obj);
- case WAVE:
- return wave(command->verb, command->obj);
- case TAME: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case GO: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case ATTACK:
- return attack(command);
- case POUR:
- return pour(command->verb, command->obj);
- case EAT:
- return eat(command->verb, command->obj);
- case DRINK:
- return drink(command->verb, command->obj);
- case RUB:
- return rub(command->verb, command->obj);
- case THROW:
- return throw (command);
- case QUIT: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case FIND:
- return find(command->verb, command->obj);
- case INVENTORY:
- return find(command->verb, command->obj);
- case FEED:
- return feed(command->verb, command->obj);
- case FILL:
- return fill(command->verb, command->obj);
- case BLAST:
- blast();
- return GO_CLEAROBJ;
- case SCORE: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case GIANTWORDS: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case BRIEF: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case READ:
- return read(*command);
- case BREAK:
- return vbreak(command->verb, command->obj);
- case WAKE:
- return wake(command->verb, command->obj);
- case SAVE: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case RESUME: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case FLY:
- return fly(command->verb, command->obj);
- case LISTEN: {
- rspeak(spk);
- return GO_CLEAROBJ;
- }
- case PART:
- return reservoir();
- default:
- BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
- }
- case unknown:
- /* Unknown verb, couldn't deduce object - might need hint */
- rspeak(WHAT_DO, command->wd1, command->wd1x);
- return GO_CHECKHINT;
- default:
- BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
- }
+phase_codes_t action(command_t command) {
+ /* Analyse a verb. Remember what it was, go back for object if second
+ * word unless verb is "say", which snarfs arbitrary second word.
+ */
+ /* Previously, actions that result in a message, but don't do anything
+ * further were called "specials". Now they're handled here as normal
+ * actions. If noaction is true, then we spit out the message and return
+ */
+ if (actions[command.verb].noaction) {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+
+ if (command.part == unknown) {
+ /* Analyse an object word. See if the thing is here, whether
+ * we've got a verb yet, and so on. Object must be here
+ * unless verb is "find" or "invent(ory)" (and no new verb
+ * yet to be analysed). Water and oil are also funny, since
+ * they are never actually dropped at any location, but might
+ * be here inside the bottle or urn or as a feature of the
+ * location. */
+ if (HERE(command.obj)) {
+ /* FALL THROUGH */;
+ } else if (command.obj == DWARF && atdwrf(game.loc) > 0) {
+ /* FALL THROUGH */;
+ } else if (!game.closed &&
+ ((LIQUID() == command.obj && HERE(BOTTLE)) ||
+ command.obj == LIQLOC(game.loc))) {
+ /* FALL THROUGH */;
+ } else if (command.obj == OIL && HERE(URN) &&
+ game.objects[URN].prop != URN_EMPTY) {
+ command.obj = URN;
+ /* FALL THROUGH */;
+ } else if (command.obj == PLANT && AT(PLANT2) &&
+ game.objects[PLANT2].prop != PLANT_THIRSTY) {
+ command.obj = PLANT2;
+ /* FALL THROUGH */;
+ } else if (command.obj == KNIFE && game.knfloc == game.loc) {
+ game.knfloc = -1;
+ rspeak(KNIVES_VANISH);
+ return GO_CLEAROBJ;
+ } else if (command.obj == ROD && HERE(ROD2)) {
+ command.obj = ROD2;
+ /* FALL THROUGH */;
+ } else if ((command.verb == FIND ||
+ command.verb == INVENTORY) &&
+ (command.word[1].id == WORD_EMPTY ||
+ command.word[1].id == WORD_NOT_FOUND)) {
+ /* FALL THROUGH */;
+ } else {
+ sspeak(NO_SEE, command.word[0].raw);
+ return GO_CLEAROBJ;
+ }
+
+ if (command.verb != 0) {
+ command.part = transitive;
+ }
+ }
+
+ switch (command.part) {
+ case intransitive:
+ if (command.word[1].raw[0] != '\0' && command.verb != SAY) {
+ return GO_WORD2;
+ }
+ if (command.verb == SAY) {
+ /* KEYS is not special, anything not NO_OBJECT or
+ * INTRANSITIVE will do here. We're preventing
+ * interpretation as an intransitive verb when the word
+ * is unknown. */
+ command.obj =
+ command.word[1].raw[0] != '\0' ? KEYS : NO_OBJECT;
+ }
+ if (command.obj == NO_OBJECT || command.obj == INTRANSITIVE) {
+ /* Analyse an intransitive verb (ie, no object given
+ * yet). */
+ switch (command.verb) {
+ case CARRY:
+ return vcarry(command.verb, INTRANSITIVE);
+ case DROP:
+ return GO_UNKNOWN;
+ case SAY:
+ return GO_UNKNOWN;
+ case UNLOCK:
+ return lock(command.verb, INTRANSITIVE);
+ case NOTHING: {
+ rspeak(OK_MAN);
+ return (GO_CLEAROBJ);
+ }
+ case LOCK:
+ return lock(command.verb, INTRANSITIVE);
+ case LIGHT:
+ return light(command.verb, INTRANSITIVE);
+ case EXTINGUISH:
+ return extinguish(command.verb, INTRANSITIVE);
+ case WAVE:
+ return GO_UNKNOWN;
+ case TAME:
+ return GO_UNKNOWN;
+ case GO: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case ATTACK:
+ command.obj = INTRANSITIVE;
+ return attack(command);
+ case POUR:
+ return pour(command.verb, INTRANSITIVE);
+ case EAT:
+ return eat(command.verb, INTRANSITIVE);
+ case DRINK:
+ return drink(command.verb, INTRANSITIVE);
+ case RUB:
+ return GO_UNKNOWN;
+ case THROW:
+ return GO_UNKNOWN;
+ case QUIT:
+ return quit();
+ case FIND:
+ return GO_UNKNOWN;
+ case INVENTORY:
+ return inven();
+ case FEED:
+ return GO_UNKNOWN;
+ case FILL:
+ return fill(command.verb, INTRANSITIVE);
+ case BLAST:
+ blast();
+ return GO_CLEAROBJ;
+ case SCORE:
+ score(scoregame);
+ return GO_CLEAROBJ;
+ case FEE:
+ case FIE:
+ case FOE:
+ case FOO:
+ case FUM:
+ return bigwords(command.word[0].id);
+ case BRIEF:
+ return brief();
+ case READ:
+ command.obj = INTRANSITIVE;
+ return read(command);
+ case BREAK:
+ return GO_UNKNOWN;
+ case WAKE:
+ return GO_UNKNOWN;
+ case SAVE:
+ return suspend();
+ case RESUME:
+ return resume();
+ case FLY:
+ return fly(command.verb, INTRANSITIVE);
+ case LISTEN:
+ return listen();
+ case PART:
+ return reservoir();
+ case SEED:
+ case WASTE:
+ rspeak(NUMERIC_REQUIRED);
+ return GO_TOP;
+ default: // LCOV_EXCL_LINE
+ BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ }
+ /* FALLTHRU */
+ case transitive:
+ /* Analyse a transitive verb. */
+ switch (command.verb) {
+ case CARRY:
+ return vcarry(command.verb, command.obj);
+ case DROP:
+ return discard(command.verb, command.obj);
+ case SAY:
+ return say(command);
+ case UNLOCK:
+ return lock(command.verb, command.obj);
+ case NOTHING: {
+ rspeak(OK_MAN);
+ return (GO_CLEAROBJ);
+ }
+ case LOCK:
+ return lock(command.verb, command.obj);
+ case LIGHT:
+ return light(command.verb, command.obj);
+ case EXTINGUISH:
+ return extinguish(command.verb, command.obj);
+ case WAVE:
+ return wave(command.verb, command.obj);
+ case TAME: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case GO: {
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ }
+ case ATTACK:
+ return attack(command);
+ case POUR:
+ return pour(command.verb, command.obj);
+ case EAT:
+ return eat(command.verb, command.obj);
+ case DRINK:
+ return drink(command.verb, command.obj);
+ case RUB:
+ return rub(command.verb, command.obj);
+ case THROW:
+ return throwit(command);
+ case QUIT:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ case FIND:
+ return find(command.verb, command.obj);
+ case INVENTORY:
+ return find(command.verb, command.obj);
+ case FEED:
+ return feed(command.verb, command.obj);
+ case FILL:
+ return fill(command.verb, command.obj);
+ case BLAST:
+ blast();
+ return GO_CLEAROBJ;
+ case SCORE:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ case FEE:
+ case FIE:
+ case FOE:
+ case FOO:
+ case FUM:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ case BRIEF:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ case READ:
+ return read(command);
+ case BREAK:
+ return vbreak(command.verb, command.obj);
+ case WAKE:
+ return wake(command.verb, command.obj);
+ case SAVE:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ case RESUME:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ case FLY:
+ return fly(command.verb, command.obj);
+ case LISTEN:
+ speak(actions[command.verb].message);
+ return GO_CLEAROBJ;
+ // LCOV_EXCL_START
+ // This case should never happen - here only as placeholder
+ case PART:
+ return reservoir();
+ // LCOV_EXCL_STOP
+ case SEED:
+ return seed(command.verb, command.word[1].raw);
+ case WASTE:
+ return waste(command.verb,
+ (turn_t)atol(command.word[1].raw));
+ default: // LCOV_EXCL_LINE
+ BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
+ }
+ case unknown:
+ /* Unknown verb, couldn't deduce object - might need hint */
+ sspeak(WHAT_DO, command.word[0].raw);
+ return GO_CHECKHINT;
+ default: // LCOV_EXCL_LINE
+ BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
+ }