static int fill(verb_t, obj_t);
-static int attack(struct command_t *command)
+static int attack(command_t command)
/* Attack. Assume target if unambiguous. "Throw" also links here.
* Attackable objects fall into two categories: enemies (snake,
* dwarf, etc.) and others (bird, clam, machine). Ambiguous if 2
* enemies, or no enemies but 2 others. */
{
- verb_t verb = command->verb;
- obj_t obj = command->obj;
+ verb_t verb = command.verb;
+ obj_t obj = command.obj;
if (obj == INTRANSITIVE) {
int changes = 0;
return GO_CLEAROBJ;
}
-static int bigwords(long id)
+static int bigwords(vocab_t id)
/* FEE FIE FOE FOO (AND FUM). Advance to next state if given in proper order.
* Look up foo in special section of vocab to determine which word we've got.
* Last word zips the eggs back to the giant room (unless already there). */
}
static int brief(void)
-/* Brief. Intransitive only. Suppress long descriptions after first time. */
+/* Brief. Intransitive only. Suppress full descriptions after first time. */
{
game.abbnum = 10000;
game.detail = 3;
switch (game.prop[BEAR]) {
// LCOV_EXCL_START
case BEAR_DEAD:
- /* Can't be reached as long as the only way for the bear to die
- * is from a bridge collapse. Leave in in case this changes, but
+ /* Can't be reached until the bear can die in some way other
+ * than a bridge collapse. Leave in in case this changes, but
* exclude from coverage testing. */
game.fixed[BEAR] = IS_FIXED;
break;
static int listen(void)
/* Listen. Intransitive only. Print stuff based on object sound proprties. */
{
- long sound = locations[game.loc].sound;
+ vocab_t sound = locations[game.loc].sound;
if (sound != SILENT) {
rspeak(sound);
if (!locations[game.loc].loud)
* depending on whether player has drunk dragon's blood. */
if (i == BIRD)
mi += 3 * game.blooded;
- long packed_zzword = token_to_packed(game.zzword);
- pspeak(i, hear, mi, true, packed_zzword);
+ pspeak(i, hear, mi, true, game.zzword);
rspeak(NO_MESSAGE);
if (i == BIRD && mi == BIRD_ENDSTATE)
DESTROY(BIRD);
return GO_CLEAROBJ;
}
-static int read(struct command_t command)
+static int read(command_t command)
/* Read. Print stuff based on objtxt. Oyster (?) is special case. */
{
if (command.obj == INTRANSITIVE) {
}
if (DARK(game.loc)) {
- sspeak(NO_SEE, command.raw1);
+ sspeak(NO_SEE, command.word[0].raw);
} else if (command.obj == OYSTER && !game.clshnt && game.closed) {
game.clshnt = yes(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]);
} else if (objects[command.obj].texts[0] == NULL ||
return GO_CLEAROBJ;
}
-static int say(struct command_t *command)
+static int say(command_t command)
/* Say. Echo WD2. Magic words override. */
{
- long wd;
- enum wordtype type;
- get_vocab_metadata(command->raw2, &wd, &type);
- if (wd == XYZZY ||
- wd == PLUGH ||
- wd == PLOVER ||
- wd == FEE ||
- wd == FIE ||
- wd == FOE ||
- wd == FOO ||
- wd == FUM ||
- wd == PART) {
- return GO_WORD2;
- }
- sspeak(OKEY_DOKEY, command->raw2);
+ if (command.word[1].type == MOTION &&
+ (command.word[1].id == XYZZY ||
+ command.word[1].id == PLUGH ||
+ command.word[1].id == PLOVER)) {
+ return GO_WORD2;
+ }
+ if (command.word[1].type == ACTION && command.word[1].id == PART)
+ return reservoir();
+
+ if (command.word[1].type == ACTION &&
+ (command.word[1].id == FEE ||
+ command.word[1].id == FIE ||
+ command.word[1].id == FOE ||
+ command.word[1].id == FOO ||
+ command.word[1].id == FUM ||
+ command.word[1].id == PART)) {
+ return bigwords(command.word[1].id);
+ }
+ sspeak(OKEY_DOKEY, command.word[1].raw);
return GO_CLEAROBJ;
}
-static int throw_support(long spk)
+static int throw_support(vocab_t spk)
{
rspeak(spk);
drop(AXE, game.loc);
return GO_MOVE;
}
-static int throw (struct command_t *command)
+static int throw (command_t command)
/* Throw. Same as discard unless axe. Then same as attack except
* ignore bird, and if dwarf is present then one might be killed.
* (Only way to do so!) Axe also special for dragon, bear, and
* troll. Treasures special for troll. */
{
- if (!TOTING(command->obj)) {
- speak(actions[command->verb].message);
+ if (!TOTING(command.obj)) {
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
- if (objects[command->obj].is_treasure && AT(TROLL)) {
+ if (objects[command.obj].is_treasure && AT(TROLL)) {
/* Snarf a treasure for the troll. */
- drop(command->obj, LOC_NOWHERE);
+ drop(command.obj, LOC_NOWHERE);
move(TROLL, LOC_NOWHERE);
move(TROLL + NOBJECTS, IS_FREE);
drop(TROLL2, objects[TROLL].plac);
rspeak(TROLL_SATISFIED);
return GO_CLEAROBJ;
}
- if (command->obj == FOOD && HERE(BEAR)) {
+ if (command.obj == FOOD && HERE(BEAR)) {
/* But throwing food is another story. */
- command->obj = BEAR;
- return (feed(command->verb, command->obj));
+ command.obj = BEAR;
+ return (feed(command.verb, command.obj));
}
- if (command->obj != AXE)
- return (discard(command->verb, command->obj));
+ if (command.obj != AXE)
+ return (discard(command.verb, command.obj));
else {
if (atdwrf(game.loc) <= 0) {
if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS)
state_change(AXE, AXE_LOST);
return GO_CLEAROBJ;
}
- command->obj = INTRANSITIVE;
+ command.obj = INTRANSITIVE;
return (attack(command));
}
if (randrange(NDWARVES + 1) < game.dflag) {
return throw_support(DWARF_DODGES);
} else {
- long i = atdwrf(game.loc);
+ int i = atdwrf(game.loc);
game.dseen[i] = false;
game.dloc[i] = LOC_NOWHERE;
return throw_support((++game.dkill == 1) ?
}
}
+static int seed(verb_t verb, const char *arg)
+/* Set seed */
+{
+ int seed = atoi(arg);
+ speak(actions[verb].message, seed);
+ set_seed(seed);
+ --game.turns;
+ return GO_TOP;
+}
+
+static int waste(verb_t verb, turn_t turns)
+/* Burn turns */
+{
+ game.limit -= turns;
+ speak(actions[verb].message, (int)game.limit);
+ return GO_TOP;
+}
+
static int wave(verb_t verb, obj_t obj)
/* Wave. No effect unless waving rod at fissure or at bird. */
{
}
}
-int action(struct command_t *command)
+int 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.
*/
{
- if (command->part == unknown) {
+ /* 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
* 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))
+ 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 = DEPRESSION;
- }
- if (game.loc == LOC_COBBLE ||
- game.loc == LOC_DEBRIS ||
- game.loc == LOC_AWKWARD ||
- game.loc == LOC_BIRD ||
- game.loc == LOC_PITTOP) {
- command->obj = ENTRANCE;
- }
- } else if (command->obj == DWARF && atdwrf(game.loc) > 0)
+ else if (command.obj == DWARF && atdwrf(game.loc) > 0)
/* FALL THROUGH */;
- else if ((LIQUID() == command->obj && HERE(BOTTLE)) ||
- command->obj == LIQLOC(game.loc))
+ else if ((LIQUID() == command.obj && HERE(BOTTLE)) ||
+ command.obj == LIQLOC(game.loc))
/* FALL THROUGH */;
- else if (command->obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) {
- command->obj = URN;
+ else if (command.obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) {
+ command.obj = URN;
/* FALL THROUGH */;
- } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) {
- command->obj = PLANT2;
+ } else if (command.obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) {
+ command.obj = PLANT2;
/* FALL THROUGH */;
- } else if (command->obj == KNIFE && game.knfloc == game.loc) {
+ } 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;
+ } else if (command.obj == ROD && HERE(ROD2)) {
+ command.obj = ROD2;
/* FALL THROUGH */;
- } else if ((command->verb == FIND ||
- command->verb == INVENTORY) && (command->id2 == WORD_EMPTY || command->id2 == WORD_NOT_FOUND))
+ } 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->raw1);
+ sspeak(NO_SEE, command.word[0].raw);
return GO_CLEAROBJ;
}
- if (command->id2 != WORD_EMPTY && command->id2 != WORD_NOT_FOUND)
- return GO_WORD2;
- if (command->verb != 0)
- command->part = transitive;
+ if (command.verb != 0)
+ command.part = transitive;
}
- switch (command->part) {
+ switch (command.part) {
case intransitive:
- if (command->raw2[0] != '\0' && command->verb != SAY)
+ if (command.word[1].raw[0] != '\0' && command.verb != SAY)
return GO_WORD2;
- if (command->verb == SAY)
- command->obj = command->wd2;
- if (command->obj == NO_OBJECT ||
- command->obj == INTRANSITIVE) {
+ 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) {
+ switch (command.verb) {
case CARRY:
- return vcarry(command->verb, INTRANSITIVE);
+ return vcarry(command.verb, INTRANSITIVE);
case DROP:
return GO_UNKNOWN;
case SAY:
return GO_UNKNOWN;
case UNLOCK:
- return lock(command->verb, INTRANSITIVE);
+ return lock(command.verb, INTRANSITIVE);
case NOTHING: {
rspeak(OK_MAN);
return (GO_CLEAROBJ);
}
case LOCK:
- return lock(command->verb, INTRANSITIVE);
+ return lock(command.verb, INTRANSITIVE);
case LIGHT:
- return light(command->verb, INTRANSITIVE);
+ return light(command.verb, INTRANSITIVE);
case EXTINGUISH:
- return extinguish(command->verb, INTRANSITIVE);
+ return extinguish(command.verb, INTRANSITIVE);
case WAVE:
return GO_UNKNOWN;
case TAME:
return GO_UNKNOWN;
case GO: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case ATTACK:
- command->obj = INTRANSITIVE;
+ command.obj = INTRANSITIVE;
return attack(command);
case POUR:
- return pour(command->verb, INTRANSITIVE);
+ return pour(command.verb, INTRANSITIVE);
case EAT:
- return eat(command->verb, INTRANSITIVE);
+ return eat(command.verb, INTRANSITIVE);
case DRINK:
- return drink(command->verb, INTRANSITIVE);
+ return drink(command.verb, INTRANSITIVE);
case RUB:
return GO_UNKNOWN;
case THROW:
case FEED:
return GO_UNKNOWN;
case FILL:
- return fill(command->verb, INTRANSITIVE);
+ return fill(command.verb, INTRANSITIVE);
case BLAST:
blast();
return GO_CLEAROBJ;
case FOE:
case FOO:
case FUM:
- return bigwords(command->id1);
+ return bigwords(command.word[0].id);
case BRIEF:
return brief();
case READ:
- command->obj = INTRANSITIVE;
- return read(*command);
+ command.obj = INTRANSITIVE;
+ return read(command);
case BREAK:
return GO_UNKNOWN;
case WAKE:
case RESUME:
return resume();
case FLY:
- return fly(command->verb, INTRANSITIVE);
+ 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) {
+ switch (command.verb) {
case CARRY:
- return vcarry(command->verb, command->obj);
+ return vcarry(command.verb, command.obj);
case DROP:
- return discard(command->verb, command->obj);
+ return discard(command.verb, command.obj);
case SAY:
return say(command);
case UNLOCK:
- return lock(command->verb, command->obj);
+ return lock(command.verb, command.obj);
case NOTHING: {
rspeak(OK_MAN);
return (GO_CLEAROBJ);
}
case LOCK:
- return lock(command->verb, command->obj);
+ return lock(command.verb, command.obj);
case LIGHT:
- return light(command->verb, command->obj);
+ return light(command.verb, command.obj);
case EXTINGUISH:
- return extinguish(command->verb, command->obj);
+ return extinguish(command.verb, command.obj);
case WAVE:
- return wave(command->verb, command->obj);
+ return wave(command.verb, command.obj);
case TAME: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case GO: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case ATTACK:
return attack(command);
case POUR:
- return pour(command->verb, command->obj);
+ return pour(command.verb, command.obj);
case EAT:
- return eat(command->verb, command->obj);
+ return eat(command.verb, command.obj);
case DRINK:
- return drink(command->verb, command->obj);
+ return drink(command.verb, command.obj);
case RUB:
- return rub(command->verb, command->obj);
+ return rub(command.verb, command.obj);
case THROW:
- return throw (command);
+ return throw(command);
case QUIT: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case FIND:
- return find(command->verb, command->obj);
+ return find(command.verb, command.obj);
case INVENTORY:
- return find(command->verb, command->obj);
+ return find(command.verb, command.obj);
case FEED:
- return feed(command->verb, command->obj);
+ return feed(command.verb, command.obj);
case FILL:
- return fill(command->verb, command->obj);
+ return fill(command.verb, command.obj);
case BLAST:
blast();
return GO_CLEAROBJ;
case SCORE: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case FEE:
case FOE:
case FOO:
case FUM: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case BRIEF: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case READ:
- return read(*command);
+ return read(command);
case BREAK:
- return vbreak(command->verb, command->obj);
+ return vbreak(command.verb, command.obj);
case WAKE:
- return wake(command->verb, command->obj);
+ return wake(command.verb, command.obj);
case SAVE: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case RESUME: {
- speak(actions[command->verb].message);
+ speak(actions[command.verb].message);
return GO_CLEAROBJ;
}
case FLY:
- return fly(command->verb, command->obj);
+ return fly(command.verb, command.obj);
case LISTEN: {
- speak(actions[command->verb].message);
+ 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->raw1);
- return GO_CHECKHINT;
+ sspeak(WHAT_DO, command.word[0].raw);
+ return GO_CLEAROBJ;
default: // LCOV_EXCL_LINE
BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
}