Modified action() to take take a copy of command rather than a pointer.

It should be safer now that we're not scribbling all over command in
action(), as it's closer to pure. Also fixed a bug in say.
This commit is contained in:
Aaron Traas 2017-07-21 18:22:55 -04:00
parent 30f13c6c25
commit ca5374edc3
4 changed files with 114 additions and 111 deletions

211
actions.c
View file

@ -6,14 +6,14 @@
static int fill(verb_t, obj_t); static int fill(verb_t, obj_t);
static int attack(struct command_t *command) static int attack(struct command_t command)
/* Attack. Assume target if unambiguous. "Throw" also links here. /* Attack. Assume target if unambiguous. "Throw" also links here.
* Attackable objects fall into two categories: enemies (snake, * Attackable objects fall into two categories: enemies (snake,
* dwarf, etc.) and others (bird, clam, machine). Ambiguous if 2 * dwarf, etc.) and others (bird, clam, machine). Ambiguous if 2
* enemies, or no enemies but 2 others. */ * enemies, or no enemies but 2 others. */
{ {
verb_t verb = command->verb; verb_t verb = command.verb;
obj_t obj = command->obj; obj_t obj = command.obj;
if (obj == INTRANSITIVE) { if (obj == INTRANSITIVE) {
int changes = 0; int changes = 0;
@ -1141,25 +1141,28 @@ static int rub(verb_t verb, obj_t obj)
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
static int say(struct command_t *command) static int say(struct command_t command)
/* Say. Echo WD2. Magic words override. */ /* Say. Echo WD2. Magic words override. */
{ {
if (command->type2 == MOTION && if (command.type2 == MOTION &&
(command->id2 == XYZZY || (command.id2 == XYZZY ||
command->id2 == PLUGH || command.id2 == PLUGH ||
command->id2 == PLOVER)) { command.id2 == PLOVER)) {
return GO_WORD2; return GO_WORD2;
} }
if (command->type2 == ACTION && if (command.type2 == ACTION && command.id2 == PART)
(command->id2 == FEE || return reservoir();
command->id2 == FIE ||
command->id2 == FOE || if (command.type2 == ACTION &&
command->id2 == FOO || (command.id2 == FEE ||
command->id2 == FUM || command.id2 == FIE ||
command->id2 == PART)) { command.id2 == FOE ||
return GO_WORD2; command.id2 == FOO ||
command.id2 == FUM ||
command.id2 == PART)) {
return bigwords(command.id2);
} }
sspeak(OKEY_DOKEY, command->raw2); sspeak(OKEY_DOKEY, command.raw2);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
@ -1170,19 +1173,19 @@ static int throw_support(vocab_t spk)
return GO_MOVE; return GO_MOVE;
} }
static int throw (struct command_t *command) static int throw (struct command_t command)
/* Throw. Same as discard unless axe. Then same as attack except /* Throw. Same as discard unless axe. Then same as attack except
* ignore bird, and if dwarf is present then one might be killed. * ignore bird, and if dwarf is present then one might be killed.
* (Only way to do so!) Axe also special for dragon, bear, and * (Only way to do so!) Axe also special for dragon, bear, and
* troll. Treasures special for troll. */ * troll. Treasures special for troll. */
{ {
if (!TOTING(command->obj)) { if (!TOTING(command.obj)) {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; 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. */ /* Snarf a treasure for the troll. */
drop(command->obj, LOC_NOWHERE); drop(command.obj, LOC_NOWHERE);
move(TROLL, LOC_NOWHERE); move(TROLL, LOC_NOWHERE);
move(TROLL + NOBJECTS, IS_FREE); move(TROLL + NOBJECTS, IS_FREE);
drop(TROLL2, objects[TROLL].plac); drop(TROLL2, objects[TROLL].plac);
@ -1191,13 +1194,13 @@ static int throw (struct command_t *command)
rspeak(TROLL_SATISFIED); rspeak(TROLL_SATISFIED);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
if (command->obj == FOOD && HERE(BEAR)) { if (command.obj == FOOD && HERE(BEAR)) {
/* But throwing food is another story. */ /* But throwing food is another story. */
command->obj = BEAR; command.obj = BEAR;
return (feed(command->verb, command->obj)); return (feed(command.verb, command.obj));
} }
if (command->obj != AXE) if (command.obj != AXE)
return (discard(command->verb, command->obj)); return (discard(command.verb, command.obj));
else { else {
if (atdwrf(game.loc) <= 0) { if (atdwrf(game.loc) <= 0) {
if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS)
@ -1214,7 +1217,7 @@ static int throw (struct command_t *command)
state_change(AXE, AXE_LOST); state_change(AXE, AXE_LOST);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
command->obj = INTRANSITIVE; command.obj = INTRANSITIVE;
return (attack(command)); return (attack(command));
} }
@ -1308,7 +1311,7 @@ static int wave(verb_t verb, obj_t obj)
} }
} }
int action(struct command_t *command) int action(struct command_t command)
/* Analyse a verb. Remember what it was, go back for object if second word /* Analyse a verb. Remember what it was, go back for object if second word
* unless verb is "say", which snarfs arbitrary second word. * unless verb is "say", which snarfs arbitrary second word.
*/ */
@ -1316,12 +1319,12 @@ int action(struct command_t *command)
/* Previously, actions that result in a message, but don't do anything /* Previously, actions that result in a message, but don't do anything
* further were called "specials". Now they're handled here as normal * further were called "specials". Now they're handled here as normal
* actions. If noaction is true, then we spit out the message and return */ * actions. If noaction is true, then we spit out the message and return */
if (actions[command->verb].noaction) { if (actions[command.verb].noaction) {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
if (command->part == unknown) { if (command.part == unknown) {
/* Analyse an object word. See if the thing is here, whether /* Analyse an object word. See if the thing is here, whether
* we've got a verb yet, and so on. Object must be here * we've got a verb yet, and so on. Object must be here
* unless verb is "find" or "invent(ory)" (and no new verb * unless verb is "find" or "invent(ory)" (and no new verb
@ -1329,88 +1332,88 @@ int action(struct command_t *command)
* they are never actually dropped at any location, but might * they are never actually dropped at any location, but might
* be here inside the bottle or urn or as a feature of the * be here inside the bottle or urn or as a feature of the
* location. */ * location. */
if (HERE(command->obj)) if (HERE(command.obj))
/* FALL THROUGH */; /* FALL THROUGH */;
else if (command->obj == DWARF && atdwrf(game.loc) > 0) else if (command.obj == DWARF && atdwrf(game.loc) > 0)
/* FALL THROUGH */; /* FALL THROUGH */;
else if ((LIQUID() == command->obj && HERE(BOTTLE)) || else if ((LIQUID() == command.obj && HERE(BOTTLE)) ||
command->obj == LIQLOC(game.loc)) command.obj == LIQLOC(game.loc))
/* FALL THROUGH */; /* FALL THROUGH */;
else if (command->obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) { else if (command.obj == OIL && HERE(URN) && game.prop[URN] != URN_EMPTY) {
command->obj = URN; command.obj = URN;
/* FALL THROUGH */; /* FALL THROUGH */;
} else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) { } else if (command.obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != PLANT_THIRSTY) {
command->obj = PLANT2; command.obj = PLANT2;
/* FALL THROUGH */; /* FALL THROUGH */;
} else if (command->obj == KNIFE && game.knfloc == game.loc) { } else if (command.obj == KNIFE && game.knfloc == game.loc) {
game.knfloc = -1; game.knfloc = -1;
rspeak(KNIVES_VANISH); rspeak(KNIVES_VANISH);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} else if (command->obj == ROD && HERE(ROD2)) { } else if (command.obj == ROD && HERE(ROD2)) {
command->obj = ROD2; command.obj = ROD2;
/* FALL THROUGH */; /* FALL THROUGH */;
} else if ((command->verb == FIND || } else if ((command.verb == FIND ||
command->verb == INVENTORY) && (command->id2 == WORD_EMPTY || command->id2 == WORD_NOT_FOUND)) command.verb == INVENTORY) && (command.id2 == WORD_EMPTY || command.id2 == WORD_NOT_FOUND))
/* FALL THROUGH */; /* FALL THROUGH */;
else { else {
sspeak(NO_SEE, command->raw1); sspeak(NO_SEE, command.raw1);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
if (command->id2 != WORD_EMPTY && command->id2 != WORD_NOT_FOUND) if (command.id2 != WORD_EMPTY && command.id2 != WORD_NOT_FOUND)
return GO_WORD2; return GO_WORD2;
if (command->verb != 0) if (command.verb != 0)
command->part = transitive; command.part = transitive;
} }
switch (command->part) { switch (command.part) {
case intransitive: case intransitive:
if (command->raw2[0] != '\0' && command->verb != SAY) if (command.raw2[0] != '\0' && command.verb != SAY)
return GO_WORD2; return GO_WORD2;
if (command->verb == SAY) if (command.verb == SAY)
/* KEYS is not special, anything not NO_OBJECT or INTRANSITIVE /* KEYS is not special, anything not NO_OBJECT or INTRANSITIVE
* will do here. We're preventing interpretation as an intransitive * will do here. We're preventing interpretation as an intransitive
* verb when the word is unknown. */ * verb when the word is unknown. */
command->obj = command->raw2[0] != '\0' ? KEYS : NO_OBJECT; command.obj = command.raw2[0] != '\0' ? KEYS : NO_OBJECT;
if (command->obj == NO_OBJECT || if (command.obj == NO_OBJECT ||
command->obj == INTRANSITIVE) { command.obj == INTRANSITIVE) {
/* Analyse an intransitive verb (ie, no object given yet). */ /* Analyse an intransitive verb (ie, no object given yet). */
switch (command->verb) { switch (command.verb) {
case CARRY: case CARRY:
return vcarry(command->verb, INTRANSITIVE); return vcarry(command.verb, INTRANSITIVE);
case DROP: case DROP:
return GO_UNKNOWN; return GO_UNKNOWN;
case SAY: case SAY:
return GO_UNKNOWN; return GO_UNKNOWN;
case UNLOCK: case UNLOCK:
return lock(command->verb, INTRANSITIVE); return lock(command.verb, INTRANSITIVE);
case NOTHING: { case NOTHING: {
rspeak(OK_MAN); rspeak(OK_MAN);
return (GO_CLEAROBJ); return (GO_CLEAROBJ);
} }
case LOCK: case LOCK:
return lock(command->verb, INTRANSITIVE); return lock(command.verb, INTRANSITIVE);
case LIGHT: case LIGHT:
return light(command->verb, INTRANSITIVE); return light(command.verb, INTRANSITIVE);
case EXTINGUISH: case EXTINGUISH:
return extinguish(command->verb, INTRANSITIVE); return extinguish(command.verb, INTRANSITIVE);
case WAVE: case WAVE:
return GO_UNKNOWN; return GO_UNKNOWN;
case TAME: case TAME:
return GO_UNKNOWN; return GO_UNKNOWN;
case GO: { case GO: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case ATTACK: case ATTACK:
command->obj = INTRANSITIVE; command.obj = INTRANSITIVE;
return attack(command); return attack(command);
case POUR: case POUR:
return pour(command->verb, INTRANSITIVE); return pour(command.verb, INTRANSITIVE);
case EAT: case EAT:
return eat(command->verb, INTRANSITIVE); return eat(command.verb, INTRANSITIVE);
case DRINK: case DRINK:
return drink(command->verb, INTRANSITIVE); return drink(command.verb, INTRANSITIVE);
case RUB: case RUB:
return GO_UNKNOWN; return GO_UNKNOWN;
case THROW: case THROW:
@ -1424,7 +1427,7 @@ int action(struct command_t *command)
case FEED: case FEED:
return GO_UNKNOWN; return GO_UNKNOWN;
case FILL: case FILL:
return fill(command->verb, INTRANSITIVE); return fill(command.verb, INTRANSITIVE);
case BLAST: case BLAST:
blast(); blast();
return GO_CLEAROBJ; return GO_CLEAROBJ;
@ -1436,12 +1439,12 @@ int action(struct command_t *command)
case FOE: case FOE:
case FOO: case FOO:
case FUM: case FUM:
return bigwords(command->id1); return bigwords(command.id1);
case BRIEF: case BRIEF:
return brief(); return brief();
case READ: case READ:
command->obj = INTRANSITIVE; command.obj = INTRANSITIVE;
return read(*command); return read(command);
case BREAK: case BREAK:
return GO_UNKNOWN; return GO_UNKNOWN;
case WAKE: case WAKE:
@ -1451,7 +1454,7 @@ int action(struct command_t *command)
case RESUME: case RESUME:
return resume(); return resume();
case FLY: case FLY:
return fly(command->verb, INTRANSITIVE); return fly(command.verb, INTRANSITIVE);
case LISTEN: case LISTEN:
return listen(); return listen();
case PART: case PART:
@ -1467,64 +1470,64 @@ int action(struct command_t *command)
/* FALLTHRU */ /* FALLTHRU */
case transitive: case transitive:
/* Analyse a transitive verb. */ /* Analyse a transitive verb. */
switch (command->verb) { switch (command.verb) {
case CARRY: case CARRY:
return vcarry(command->verb, command->obj); return vcarry(command.verb, command.obj);
case DROP: case DROP:
return discard(command->verb, command->obj); return discard(command.verb, command.obj);
case SAY: case SAY:
return say(command); return say(command);
case UNLOCK: case UNLOCK:
return lock(command->verb, command->obj); return lock(command.verb, command.obj);
case NOTHING: { case NOTHING: {
rspeak(OK_MAN); rspeak(OK_MAN);
return (GO_CLEAROBJ); return (GO_CLEAROBJ);
} }
case LOCK: case LOCK:
return lock(command->verb, command->obj); return lock(command.verb, command.obj);
case LIGHT: case LIGHT:
return light(command->verb, command->obj); return light(command.verb, command.obj);
case EXTINGUISH: case EXTINGUISH:
return extinguish(command->verb, command->obj); return extinguish(command.verb, command.obj);
case WAVE: case WAVE:
return wave(command->verb, command->obj); return wave(command.verb, command.obj);
case TAME: { case TAME: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case GO: { case GO: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case ATTACK: case ATTACK:
return attack(command); return attack(command);
case POUR: case POUR:
return pour(command->verb, command->obj); return pour(command.verb, command.obj);
case EAT: case EAT:
return eat(command->verb, command->obj); return eat(command.verb, command.obj);
case DRINK: case DRINK:
return drink(command->verb, command->obj); return drink(command.verb, command.obj);
case RUB: case RUB:
return rub(command->verb, command->obj); return rub(command.verb, command.obj);
case THROW: case THROW:
return throw (command); return throw(command);
case QUIT: { case QUIT: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case FIND: case FIND:
return find(command->verb, command->obj); return find(command.verb, command.obj);
case INVENTORY: case INVENTORY:
return find(command->verb, command->obj); return find(command.verb, command.obj);
case FEED: case FEED:
return feed(command->verb, command->obj); return feed(command.verb, command.obj);
case FILL: case FILL:
return fill(command->verb, command->obj); return fill(command.verb, command.obj);
case BLAST: case BLAST:
blast(); blast();
return GO_CLEAROBJ; return GO_CLEAROBJ;
case SCORE: { case SCORE: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case FEE: case FEE:
@ -1532,45 +1535,45 @@ int action(struct command_t *command)
case FOE: case FOE:
case FOO: case FOO:
case FUM: { case FUM: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case BRIEF: { case BRIEF: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case READ: case READ:
return read(*command); return read(command);
case BREAK: case BREAK:
return vbreak(command->verb, command->obj); return vbreak(command.verb, command.obj);
case WAKE: case WAKE:
return wake(command->verb, command->obj); return wake(command.verb, command.obj);
case SAVE: { case SAVE: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case RESUME: { case RESUME: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case FLY: case FLY:
return fly(command->verb, command->obj); return fly(command.verb, command.obj);
case LISTEN: { case LISTEN: {
speak(actions[command->verb].message); speak(actions[command.verb].message);
return GO_CLEAROBJ; return GO_CLEAROBJ;
} }
case PART: case PART:
return reservoir(); return reservoir();
case SEED: case SEED:
return seed(command->verb, command->raw2); return seed(command.verb, command.raw2);
case WASTE: case WASTE:
return waste(command->verb, (turn_t)atol(command->raw2)); return waste(command.verb, (turn_t)atol(command.raw2));
default: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE
BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
} }
case unknown: case unknown:
/* Unknown verb, couldn't deduce object - might need hint */ /* Unknown verb, couldn't deduce object - might need hint */
sspeak(WHAT_DO, command->raw1); sspeak(WHAT_DO, command.raw1);
return GO_CHECKHINT; return GO_CHECKHINT;
default: // LCOV_EXCL_LINE default: // LCOV_EXCL_LINE
BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE

View file

@ -225,7 +225,7 @@ extern int suspend(void);
extern int resume(void); extern int resume(void);
extern int restore(FILE *); extern int restore(FILE *);
extern long initialise(void); extern long initialise(void);
extern int action(struct command_t *command); extern int action(struct command_t command);
extern void state_change(obj_t, int); extern void state_change(obj_t, int);

2
main.c
View file

@ -1139,7 +1139,7 @@ Lookup:
BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE BUG(VOCABULARY_TYPE_N_OVER_1000_NOT_BETWEEN_0_AND_3); // LCOV_EXCL_LINE
} }
switch (action(&command)) { switch (action(command)) {
case GO_TERMINATE: case GO_TERMINATE:
return true; return true;
case GO_MOVE: case GO_MOVE:

View file

@ -124,19 +124,19 @@ Nothing happens.
> say fee > say fee
I don't know how. OK
> say fie > say fie
I don't know how. OK
> say foe > say foe
I don't know how. OK
> say fum > say fum
I don't know how. Nothing happens.
> in > in