Remove testing for intransitive seperate from NO_OBJECT in attact()
[open-adventure.git] / actions.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3 #include "advent.h"
4 #include "dungeon.h"
5
6 static int fill(token_t, token_t);
7
8 static void state_change(long obj, long state)
9 {
10     game.prop[obj] = state;
11     pspeak(obj, change, state, true);
12 }
13
14 static int attack(struct command_t *command)
15 /*  Attack.  Assume target if unambiguous.  "Throw" also links here.
16  *  Attackable objects fall into two categories: enemies (snake,
17  *  dwarf, etc.)  and others (bird, clam, machine).  Ambiguous if 2
18  *  enemies, or no enemies but 2 others. */
19 {
20     vocab_t verb = command->verb;
21     vocab_t obj = command->obj;
22
23     long spk = actions[verb].message;
24     if (obj == NO_OBJECT || obj == INTRANSITIVE) {
25         int changes = 0;
26         if (atdwrf(game.loc) > 0) {
27             obj = DWARF;
28             ++changes;
29         }
30         if (HERE(SNAKE)) {
31             obj = SNAKE;
32             ++changes;
33         }
34         if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) {
35             obj = DRAGON;
36             ++changes;
37         }
38         if (AT(TROLL)) {
39             obj = TROLL;
40             ++changes;
41         }
42         if (AT(OGRE)) {
43             obj = OGRE;
44             ++changes;
45         }
46         if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) {
47             obj = BEAR;
48             ++changes;
49         }
50         /* check for low-priority targets */
51         if (obj == NO_OBJECT) {
52             /* Can't attack bird or machine by throwing axe. */
53             if (HERE(BIRD) && verb != THROW) {
54                 obj = BIRD;
55                 ++changes;
56             }
57             if (HERE(VEND) && verb != THROW) {
58                 obj = VEND;
59                 ++changes;
60             }
61             /* Clam and oyster both treated as clam for intransitive case;
62              * no harm done. */
63             if (HERE(CLAM) || HERE(OYSTER)) {
64                 obj = CLAM;
65                 ++changes;
66             }
67         }
68         if (changes >= 2)
69             return GO_UNKNOWN;
70
71     }
72     if (obj == BIRD) {
73         if (game.closed) {
74             rspeak(UNHAPPY_BIRD);
75             return GO_CLEAROBJ;
76         }
77         DESTROY(BIRD);
78         spk = BIRD_DEAD;
79     } else if (obj == VEND) {
80         state_change(VEND,
81                      game.prop[VEND] == VEND_BLOCKS ? VEND_UNBLOCKS : VEND_BLOCKS);
82         return GO_CLEAROBJ;
83     }
84
85     if (obj == NO_OBJECT)
86         spk = NO_TARGET;
87     if (obj == CLAM || obj == OYSTER)
88         spk = SHELL_IMPERVIOUS;
89     if (obj == SNAKE)
90         spk = SNAKE_WARNING;
91     if (obj == DWARF)
92         spk = BARE_HANDS_QUERY;
93     if (obj == DWARF && game.closed)
94         return GO_DWARFWAKE;
95     if (obj == DRAGON)
96         spk = ALREADY_DEAD;
97     if (obj == TROLL)
98         spk = ROCKY_TROLL;
99     if (obj == OGRE)
100         spk = OGRE_DODGE;
101     if (obj == OGRE && atdwrf(game.loc) > 0) {
102         rspeak(spk);
103         rspeak(KNIFE_THROWN);
104         DESTROY(OGRE);
105         int dwarves = 0;
106         for (int i = 1; i < PIRATE; i++) {
107             if (game.dloc[i] == game.loc) {
108                 ++dwarves;
109                 game.dloc[i] = LOC_LONGWEST;
110                 game.dseen[i] = false;
111             }
112         }
113         spk = (dwarves > 1) ? OGRE_PANIC1 : OGRE_PANIC2;
114     } else if (obj == BEAR) {
115         switch (game.prop[BEAR]) {
116         case UNTAMED_BEAR:
117             spk = BEAR_HANDS;
118             break;
119         case SITTING_BEAR:
120             spk = BEAR_CONFUSED;
121             break;
122         case CONTENTED_BEAR:
123             spk = BEAR_CONFUSED;
124             break;
125         case BEAR_DEAD:
126             spk = ALREADY_DEAD;
127             break;
128         }
129     } else if (obj == DRAGON && game.prop[DRAGON] == DRAGON_BARS) {
130         /*  Fun stuff for dragon.  If he insists on attacking it, win!
131          *  Set game.prop to dead, move dragon to central loc (still
132          *  fixed), move rug there (not fixed), and move him there,
133          *  too.  Then do a null motion to get new description. */
134         rspeak(BARE_HANDS_QUERY);
135         if (silent_yes()) {
136             // FIXME: setting wd1 is a workaround for broken logic
137             command->wd1 = token_to_packed("Y");
138         } else {
139             // FIXME: setting wd1 is a workaround for broken logic
140             command->wd1 = token_to_packed("N");
141             return GO_CHECKFOO;
142         }
143         state_change(DRAGON, DRAGON_DEAD);
144         game.prop[RUG] = RUG_FLOOR;
145         /* FIXME: Arithmetic on location values */
146         int k = (objects[DRAGON].plac + objects[DRAGON].fixd) / 2;
147         move(DRAGON + NOBJECTS, -1);
148         move(RUG + NOBJECTS, 0);
149         move(DRAGON, k);
150         move(RUG, k);
151         drop(BLOOD, k);
152         for (obj = 1; obj <= NOBJECTS; obj++) {
153             if (game.place[obj] == objects[DRAGON].plac || game.place[obj] == objects[DRAGON].fixd)
154                 move(obj, k);
155         }
156         game.loc = k;
157         return GO_MOVE;
158     }
159
160     rspeak(spk);
161     return GO_CLEAROBJ;
162 }
163
164 static int bigwords(token_t foo)
165 /*  FEE FIE FOE FOO (AND FUM).  Advance to next state if given in proper order.
166  *  Look up foo in special section of vocab to determine which word we've got.
167  *  Last word zips the eggs back to the giant room (unless already there). */
168 {
169     char word[6];
170     packed_to_token(foo, word);
171     int k = (int) get_special_vocab_id(word);
172     int spk = NOTHING_HAPPENS;
173     if (game.foobar != 1 - k) {
174         if (game.foobar != 0 && game.loc == LOC_GIANTROOM)
175             spk = START_OVER;
176         rspeak(spk);
177         return GO_CLEAROBJ;
178     } else {
179         game.foobar = k;
180         if (k != 4) {
181             rspeak(OK_MAN);
182             return GO_CLEAROBJ;
183         }
184         game.foobar = 0;
185         if (game.place[EGGS] == objects[EGGS].plac || (TOTING(EGGS) && game.loc == objects[EGGS].plac)) {
186             rspeak(spk);
187             return GO_CLEAROBJ;
188         } else {
189             /*  Bring back troll if we steal the eggs back from him before
190              *  crossing. */
191             if (game.place[EGGS] == LOC_NOWHERE && game.place[TROLL] == LOC_NOWHERE && game.prop[TROLL] == TROLL_UNPAID)
192                 game.prop[TROLL] = TROLL_PAIDONCE;
193             k = EGGS_DONE;
194             if (HERE(EGGS))
195                 k = EGGS_VANISHED;
196             if (game.loc == objects[EGGS].plac)
197                 k = EGGS_HERE;
198             move(EGGS, objects[EGGS].plac);
199             pspeak(EGGS, look, k, true);
200             return GO_CLEAROBJ;
201         }
202     }
203 }
204
205 static int bivalve(token_t verb, token_t obj)
206 /* Clam/oyster actions */
207 {
208     int spk;
209     bool is_oyster = (obj == OYSTER);
210     spk = is_oyster ? OYSTER_OPENS : PEARL_FALLS;
211     if (TOTING(obj))
212         spk = is_oyster ? DROP_OYSTER : DROP_CLAM;
213     if (!TOTING(TRIDENT))
214         spk = is_oyster ? OYSTER_OPENER : CLAM_OPENER;
215     if (verb == LOCK)
216         spk = HUH_MAN;
217     if (spk == PEARL_FALLS) {
218         DESTROY(CLAM);
219         drop(OYSTER, game.loc);
220         drop(PEARL, LOC_CULDESAC);
221     }
222     rspeak(spk);
223     return GO_CLEAROBJ;
224 }
225
226 static void blast(void)
227 /*  Blast.  No effect unless you've got dynamite, which is a neat trick! */
228 {
229     if (game.prop[ROD2] < 0 || !game.closed)
230         rspeak(REQUIRES_DYNAMITE);
231     else {
232         game.bonus = VICTORY_MESSAGE;
233         if (game.loc == LOC_NE)
234             game.bonus = DEFEAT_MESSAGE;
235         if (HERE(ROD2))
236             game.bonus = SPLATTER_MESSAGE;
237         rspeak(game.bonus);
238         terminate(endgame);
239     }
240 }
241
242 static int vbreak(token_t verb, token_t obj)
243 /*  Break.  Only works for mirror in repository and, of course, the vase. */
244 {
245     if (obj == MIRROR) {
246         if (game.closed) {
247             rspeak(BREAK_MIRROR);
248             return GO_DWARFWAKE;
249         } else {
250             rspeak(TOO_FAR);
251             return GO_CLEAROBJ;
252         }
253     }
254     if (obj == VASE && game.prop[VASE] == VASE_WHOLE) {
255         if (TOTING(VASE))
256             drop(VASE, game.loc);
257         state_change(VASE, VASE_BROKEN);
258         game.fixed[VASE] = -1;
259         return GO_CLEAROBJ;
260     }
261     rspeak(actions[verb].message);
262     return (GO_CLEAROBJ);
263 }
264
265 static int brief(void)
266 /*  Brief.  Intransitive only.  Suppress long descriptions after first time. */
267 {
268     game.abbnum = 10000;
269     game.detail = 3;
270     rspeak(BRIEF_CONFIRM);
271     return GO_CLEAROBJ;
272 }
273
274 static int vcarry(token_t verb, token_t obj)
275 /*  Carry an object.  Special cases for bird and cage (if bird in cage, can't
276  *  take one without the other).  Liquids also special, since they depend on
277  *  status of bottle.  Also various side effects, etc. */
278 {
279     int spk;
280     if (obj == INTRANSITIVE) {
281         /*  Carry, no object given yet.  OK if only one object present. */
282         if (game.atloc[game.loc] == 0 ||
283             game.link[game.atloc[game.loc]] != 0 ||
284             atdwrf(game.loc) > 0)
285             return GO_UNKNOWN;
286         obj = game.atloc[game.loc];
287     }
288
289     if (TOTING(obj)) {
290         rspeak(ALREADY_CARRYING);
291         return GO_CLEAROBJ;
292     }
293     spk = YOU_JOKING;
294     if (obj == PLANT && game.prop[PLANT] <= 0)
295         spk = DEEP_ROOTS;
296     if (obj == BEAR && game.prop[BEAR] == SITTING_BEAR)
297         spk = BEAR_CHAINED;
298     if (obj == CHAIN && game.prop[BEAR] != UNTAMED_BEAR)
299         spk = STILL_LOCKED;
300     if (obj == URN)
301         spk = URN_NOBUDGE;
302     if (obj == CAVITY)
303         spk = DOUGHNUT_HOLES;
304     if (obj == BLOOD)
305         spk = FEW_DROPS;
306     if (obj == RUG && game.prop[RUG] == RUG_HOVER)
307         spk = RUG_HOVERS;
308     if (obj == SIGN)
309         spk = HAND_PASSTHROUGH;
310     if (obj == MESSAG) {
311         rspeak(REMOVE_MESSAGE);
312         DESTROY(MESSAG);
313         return GO_CLEAROBJ;
314     }
315     if (game.fixed[obj] != 0) {
316         rspeak(spk);
317         return GO_CLEAROBJ;
318     }
319     if (obj == WATER || obj == OIL) {
320         if (!HERE(BOTTLE) || LIQUID() != obj) {
321             if (TOTING(BOTTLE) && game.prop[BOTTLE] == EMPTY_BOTTLE)
322                 return (fill(verb, BOTTLE));
323             else {
324                 if (game.prop[BOTTLE] != EMPTY_BOTTLE)
325                     spk = BOTTLE_FULL;
326                 if (!TOTING(BOTTLE))
327                     spk = NO_CONTAINER;
328                 rspeak(spk);
329                 return GO_CLEAROBJ;
330             }
331         }
332         obj = BOTTLE;
333     }
334
335     spk = CARRY_LIMIT;
336     if (game.holdng >= INVLIMIT) {
337         rspeak(spk);
338         return GO_CLEAROBJ;
339     } else if (obj == BIRD && game.prop[BIRD] != BIRD_CAGED && -1 - game.prop[BIRD] != BIRD_CAGED) {
340         if (game.prop[BIRD] == BIRD_FOREST_UNCAGED) {
341             DESTROY(BIRD);
342             rspeak(BIRD_CRAP);
343             return GO_CLEAROBJ;
344         }
345         if (!TOTING(CAGE))
346             spk = CANNOT_CARRY;
347         if (TOTING(ROD))
348             spk = BIRD_EVADES;
349         if (spk == CANNOT_CARRY || spk == BIRD_EVADES) {
350             rspeak(spk);
351             return GO_CLEAROBJ;
352         }
353         game.prop[BIRD] = BIRD_CAGED;
354     }
355     if ((obj == BIRD || obj == CAGE) && (game.prop[BIRD] == BIRD_CAGED || -1 - game.prop[BIRD] == 1))
356         carry(BIRD + CAGE - obj, game.loc);
357     carry(obj, game.loc);
358     if (obj == BOTTLE && LIQUID() != 0)
359         game.place[LIQUID()] = CARRIED;
360     if (GSTONE(obj) && game.prop[obj] != 0) {
361         game.prop[obj] = STATE_GROUND;
362         game.prop[CAVITY] = CAVITY_EMPTY;
363     }
364     rspeak(OK_MAN);
365     return GO_CLEAROBJ;
366 }
367
368 static int chain(token_t verb)
369 /* Do something to the bear's chain */
370 {
371     int spk;
372     if (verb != LOCK) {
373         spk = CHAIN_UNLOCKED;
374         if (game.prop[BEAR] == UNTAMED_BEAR)
375             spk = BEAR_BLOCKS;
376         if (game.prop[CHAIN] == CHAIN_HEAP)
377             spk = ALREADY_UNLOCKED;
378         if (spk != CHAIN_UNLOCKED) {
379             rspeak(spk);
380             return GO_CLEAROBJ;
381         }
382         game.prop[CHAIN] = CHAIN_HEAP;
383         game.fixed[CHAIN] = CHAIN_HEAP;
384         if (game.prop[BEAR] != BEAR_DEAD)
385             game.prop[BEAR] = CONTENTED_BEAR;
386         /* FIXME: Arithmetic on state numbers */
387         game.fixed[BEAR] = 2 - game.prop[BEAR];
388     } else {
389         spk = CHAIN_LOCKED;
390         if (game.prop[CHAIN] != CHAIN_HEAP)
391             spk = ALREADY_LOCKED;
392         if (game.loc != objects[CHAIN].plac)
393             spk = NO_LOCKSITE;
394         if (spk != CHAIN_LOCKED) {
395             rspeak(spk);
396             return GO_CLEAROBJ;
397         }
398         game.prop[CHAIN] = CHAIN_FIXED;
399         if (TOTING(CHAIN))
400             drop(CHAIN, game.loc);
401         game.fixed[CHAIN] = -1;
402     }
403     rspeak(spk);
404     return GO_CLEAROBJ;
405 }
406
407 static int discard(token_t verb, token_t obj, bool just_do_it)
408 /*  Discard object.  "Throw" also comes here for most objects.  Special cases for
409  *  bird (might attack snake or dragon) and cage (might contain bird) and vase.
410  *  Drop coins at vending machine for extra batteries. */
411 {
412     int spk = actions[verb].message;
413     if (!just_do_it) {
414         if (TOTING(ROD2) && obj == ROD && !TOTING(ROD))
415             obj = ROD2;
416         if (!TOTING(obj)) {
417             rspeak(spk);
418             return GO_CLEAROBJ;
419         }
420         if (obj == BIRD && HERE(SNAKE)) {
421             rspeak(BIRD_ATTACKS);
422             if (game.closed)
423                 return GO_DWARFWAKE;
424             DESTROY(SNAKE);
425             /* Set game.prop for use by travel options */
426             game.prop[SNAKE] = SNAKE_CHASED;
427
428         } else if ((GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != CAVITY_FULL)) {
429             rspeak(GEM_FITS);
430             game.prop[obj] = 1;
431             game.prop[CAVITY] = CAVITY_FULL;
432             if (HERE(RUG) && ((obj == EMERALD && game.prop[RUG] != RUG_HOVER) || (obj == RUBY &&
433                               game.prop[RUG] == RUG_HOVER))) {
434                 spk = RUG_RISES;
435                 if (TOTING(RUG))
436                     spk = RUG_WIGGLES;
437                 if (obj == RUBY)
438                     spk = RUG_SETTLES;
439                 rspeak(spk);
440                 if (spk != RUG_WIGGLES) {
441                     /* FIXME: Arithmetic on state numbers */
442                     int k = 2 - game.prop[RUG];
443                     game.prop[RUG] = k;
444                     if (k == 2)
445                         k = objects[SAPPH].plac;
446                     move(RUG + NOBJECTS, k);
447                 }
448             }
449         } else if (obj == COINS && HERE(VEND)) {
450             DESTROY(COINS);
451             drop(BATTERY, game.loc);
452             pspeak(BATTERY, look, FRESH_BATTERIES, true);
453             return GO_CLEAROBJ;
454         } else if (obj == BIRD && AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS) {
455             rspeak(BIRD_BURNT);
456             DESTROY(BIRD);
457             return GO_CLEAROBJ;
458         } else if (obj == BEAR && AT(TROLL)) {
459             rspeak(TROLL_SCAMPERS);
460             move(TROLL, LOC_NOWHERE);
461             move(TROLL + NOBJECTS, LOC_NOWHERE);
462             move(TROLL2, objects[TROLL].plac);
463             move(TROLL2 + NOBJECTS, objects[TROLL].fixd);
464             juggle(CHASM);
465             game.prop[TROLL] = TROLL_GONE;
466         } else if (obj != VASE || game.loc == objects[PILLOW].plac) {
467             rspeak(OK_MAN);
468         } else {
469             game.prop[VASE] = VASE_BROKEN;
470             if (AT(PILLOW))
471                 game.prop[VASE] = VASE_WHOLE;
472             pspeak(VASE, look, game.prop[VASE] + 1, true);
473             if (game.prop[VASE] != VASE_WHOLE)
474                 game.fixed[VASE] = -1;
475         }
476     }
477     int k = LIQUID();
478     if (k == obj)
479         obj = BOTTLE;
480     if (obj == BOTTLE && k != 0)
481         game.place[k] = LOC_NOWHERE;
482     if (obj == CAGE && game.prop[BIRD] == BIRD_CAGED)
483         drop(BIRD, game.loc);
484     drop(obj, game.loc);
485     if (obj != BIRD)
486         return GO_CLEAROBJ;
487     game.prop[BIRD] = BIRD_UNCAGED;
488     if (FOREST(game.loc))
489         game.prop[BIRD] = BIRD_FOREST_UNCAGED;
490     return GO_CLEAROBJ;
491 }
492
493 static int drink(token_t verb, token_t obj)
494 /*  Drink.  If no object, assume water and look for it here.  If water is in
495  *  the bottle, drink that, else must be at a water loc, so drink stream. */
496 {
497     if (obj == NO_OBJECT && LIQLOC(game.loc) != WATER && (LIQUID() != WATER || !HERE(BOTTLE)))
498         return GO_UNKNOWN;
499     if (obj != BLOOD) {
500         if (obj != NO_OBJECT && obj != WATER) {
501             rspeak(RIDICULOUS_ATTEMPT);
502         } else if (LIQUID() == WATER && HERE(BOTTLE)) {
503             game.prop[BOTTLE] = EMPTY_BOTTLE;
504             game.place[WATER] = LOC_NOWHERE;
505             rspeak(BOTTLE_EMPTY);
506         } else {
507             rspeak(actions[verb].message);
508         }
509     } else {
510         DESTROY(BLOOD);
511         state_change(DRAGON, DRAGON_BLOODLESS);
512         game.blooded = true;
513     }
514     return GO_CLEAROBJ;
515 }
516
517 static int eat(token_t verb, token_t obj)
518 /*  Eat.  Intransitive: assume food if present, else ask what.  Transitive: food
519  *  ok, some things lose appetite, rest are ridiculous. */
520 {
521     int spk = actions[verb].message;
522     if (obj == INTRANSITIVE) {
523         if (!HERE(FOOD))
524             return GO_UNKNOWN;
525         DESTROY(FOOD);
526         spk = THANKS_DELICIOUS;
527     } else {
528         if (obj == FOOD) {
529             DESTROY(FOOD);
530             spk = THANKS_DELICIOUS;
531         }
532         if (obj == BIRD || obj == SNAKE || obj == CLAM || obj == OYSTER || obj ==
533             DWARF || obj == DRAGON || obj == TROLL || obj == BEAR || obj ==
534             OGRE)
535             spk = LOST_APPETITE;
536     }
537     rspeak(spk);
538     return GO_CLEAROBJ;
539 }
540
541 static int extinguish(token_t verb, int obj)
542 /* Extinguish.  Lamp, urn, dragon/volcano (nice try). */
543 {
544     if (obj == INTRANSITIVE) {
545         if (HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT)
546             obj = LAMP;
547         if (HERE(URN) && game.prop[URN] == URN_LIT)
548             obj = URN;
549         if (obj == INTRANSITIVE ||
550             (HERE(LAMP) && game.prop[LAMP] == LAMP_BRIGHT &&
551              HERE(URN) && game.prop[URN] == URN_LIT))
552             return GO_UNKNOWN;
553     }
554
555     if (obj == URN) {
556         if (game.prop[URN] != URN_EMPTY) {
557             state_change(URN, URN_DARK);
558         } else {
559             pspeak(URN, change, URN_DARK, true);
560         }
561
562     } else if (obj == LAMP) {
563         state_change(LAMP, LAMP_DARK);
564         rspeak(DARK(game.loc) ? PITCH_DARK : NO_MESSAGE);
565     } else if (obj == DRAGON || obj == VOLCANO) {
566         rspeak(BEYOND_POWER);
567
568     } else {
569         rspeak(actions[verb].message);
570     }
571     return GO_CLEAROBJ;
572 }
573
574 static int feed(token_t verb, token_t obj)
575 /*  Feed.  If bird, no seed.  Snake, dragon, troll: quip.  If dwarf, make him
576  *  mad.  Bear, special. */
577 {
578     int spk = actions[verb].message;
579     if (obj == BIRD) {
580         rspeak(BIRD_PINING);
581         return GO_CLEAROBJ;
582     } else if (obj == SNAKE || obj == DRAGON || obj == TROLL) {
583         spk = NOTHING_EDIBLE;
584         if (obj == DRAGON && game.prop[DRAGON] != DRAGON_BARS)
585             spk = RIDICULOUS_ATTEMPT;
586         if (obj == TROLL)
587             spk = TROLL_VICES;
588         if (obj == SNAKE && !game.closed && HERE(BIRD)) {
589             DESTROY(BIRD);
590             spk = BIRD_DEVOURED;
591         }
592     } else if (obj == DWARF) {
593         if (HERE(FOOD)) {
594             game.dflag += 2;
595             spk = REALLY_MAD;
596         }
597     } else if (obj == BEAR) {
598         if (game.prop[BEAR] == UNTAMED_BEAR)
599             spk = NOTHING_EDIBLE;
600         if (game.prop[BEAR] == BEAR_DEAD)
601             spk = RIDICULOUS_ATTEMPT;
602         if (HERE(FOOD)) {
603             DESTROY(FOOD);
604             game.prop[BEAR] = SITTING_BEAR;
605             game.fixed[AXE] = 0;
606             game.prop[AXE] = 0;
607             spk = BEAR_TAMED;
608         }
609     } else if (obj == OGRE) {
610         if (HERE(FOOD))
611             spk = OGRE_FULL;
612     } else {
613         spk = AM_GAME;
614     }
615     rspeak(spk);
616     return GO_CLEAROBJ;
617 }
618
619 int fill(token_t verb, token_t obj)
620 /*  Fill.  Bottle or urn must be empty, and liquid available.  (Vase
621  *  is nasty.) */
622 {
623     int k;
624     int spk = actions[verb].message;
625     if (obj == VASE) {
626         spk = ARENT_CARRYING;
627         if (LIQLOC(game.loc) == 0)
628             spk = FILL_INVALID;
629         if (LIQLOC(game.loc) == 0 || !TOTING(VASE)) {
630             rspeak(spk);
631             return GO_CLEAROBJ;
632         }
633         rspeak(SHATTER_VASE);
634         game.prop[VASE] = VASE_BROKEN;
635         game.fixed[VASE] = -1;
636         return (discard(verb, obj, true));
637     } else if (obj == URN) {
638         spk = FULL_URN;
639         if (game.prop[URN] != URN_EMPTY) {
640             rspeak(spk);
641             return GO_CLEAROBJ;
642         }
643         spk = FILL_INVALID;
644         k = LIQUID();
645         if (k == 0 || !HERE(BOTTLE)) {
646             rspeak(spk);
647             return GO_CLEAROBJ;
648         }
649         game.place[k] = LOC_NOWHERE;
650         game.prop[BOTTLE] = EMPTY_BOTTLE;
651         if (k == OIL)
652             game.prop[URN] = URN_DARK;
653         spk = WATER_URN + game.prop[URN];
654         rspeak(spk);
655         return GO_CLEAROBJ;
656     } else if (obj != NO_OBJECT && obj != BOTTLE) {
657         rspeak(spk);
658         return GO_CLEAROBJ;
659     } else if (obj == NO_OBJECT && !HERE(BOTTLE))
660         return GO_UNKNOWN;
661     spk = BOTTLED_WATER;
662     if (LIQLOC(game.loc) == 0)
663         spk = NO_LIQUID;
664     if (HERE(URN) && game.prop[URN] != URN_EMPTY)
665         spk = URN_NOPOUR;
666     if (LIQUID() != 0)
667         spk = BOTTLE_FULL;
668     if (spk == BOTTLED_WATER) {
669         /* FIXME: Arithmetic on property values */
670         game.prop[BOTTLE] = MOD(conditions[game.loc], 4) / 2 * 2;
671         k = LIQUID();
672         if (TOTING(BOTTLE))
673             game.place[k] = CARRIED;
674         if (k == OIL)
675             spk = BOTTLED_OIL;
676     }
677     rspeak(spk);
678     return GO_CLEAROBJ;
679 }
680
681 static int find(token_t verb, token_t obj)
682 /* Find.  Might be carrying it, or it might be here.  Else give caveat. */
683 {
684     int spk = actions[verb].message;
685     if (AT(obj) ||
686         (LIQUID() == obj && AT(BOTTLE)) ||
687         obj == LIQLOC(game.loc) ||
688         (obj == DWARF && atdwrf(game.loc) > 0))
689         spk = YOU_HAVEIT;
690     if (game.closed)
691         spk = NEEDED_NEARBY;
692     if (TOTING(obj))
693         spk = ALREADY_CARRYING;
694     rspeak(spk);
695     return GO_CLEAROBJ;
696 }
697
698 static int fly(token_t verb, token_t obj)
699 /* Fly.  Snide remarks unless hovering rug is here. */
700 {
701     int spk = actions[verb].message;
702     if (obj == INTRANSITIVE) {
703         if (game.prop[RUG] != RUG_HOVER)
704             spk = RUG_NOTHING2;
705         if (!HERE(RUG))
706             spk = FLAP_ARMS;
707         if (spk == RUG_NOTHING2 || spk == FLAP_ARMS) {
708             rspeak(spk);
709             return GO_CLEAROBJ;
710         }
711         obj = RUG;
712     }
713
714     if (obj != RUG) {
715         rspeak(spk);
716         return GO_CLEAROBJ;
717     }
718     spk = RUG_NOTHING1;
719     if (game.prop[RUG] != RUG_HOVER) {
720         rspeak(spk);
721         return GO_CLEAROBJ;
722     }
723     game.oldlc2 = game.oldloc;
724     game.oldloc = game.loc;
725     /* FIXME: Arithmetic on location values */
726     game.newloc = game.place[RUG] + game.fixed[RUG] - game.loc;
727     spk = RUG_GOES;
728     if (game.prop[SAPPH] >= 0)
729         spk = RUG_RETURNS;
730     rspeak(spk);
731     return GO_TERMINATE;
732 }
733
734 static int inven(void)
735 /* Inventory. If object, treat same as find.  Else report on current burden. */
736 {
737     int spk = NO_CARRY;
738     for (int i = 1; i <= NOBJECTS; i++) {
739         if (i == BEAR || !TOTING(i))
740             continue;
741         if (spk == NO_CARRY)
742             rspeak(NOW_HOLDING);
743         pspeak(i, touch, -1, false);
744         spk = NO_MESSAGE;
745     }
746     if (TOTING(BEAR))
747         spk = TAME_BEAR;
748     rspeak(spk);
749     return GO_CLEAROBJ;
750 }
751
752 static int light(token_t verb, token_t obj)
753 /*  Light.  Applicable only to lamp and urn. */
754 {
755     if (obj == INTRANSITIVE) {
756         if (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0)
757             obj = LAMP;
758         if (HERE(URN) && game.prop[URN] == URN_DARK)
759             obj =  URN;
760         if (obj == INTRANSITIVE ||
761             (HERE(LAMP) && game.prop[LAMP] == LAMP_DARK && game.limit >= 0 &&
762              HERE(URN) && game.prop[URN] == URN_DARK))
763             return GO_UNKNOWN;
764     }
765
766     if (obj == URN) {
767         state_change(URN, game.prop[URN] == URN_EMPTY ? URN_EMPTY : URN_LIT);
768         return GO_CLEAROBJ;
769     } else {
770         if (obj != LAMP) {
771             rspeak(actions[verb].message);
772             return GO_CLEAROBJ;
773         }
774         if (game.limit < 0) {
775             rspeak(LAMP_OUT);
776             return GO_CLEAROBJ;
777         }
778         state_change(LAMP, LAMP_BRIGHT);
779         if (game.wzdark)
780             return GO_TOP;
781         else
782             return GO_CLEAROBJ;
783     }
784 }
785
786 static int listen(void)
787 /*  Listen.  Intransitive only.  Print stuff based on objsnd/locsnd. */
788 {
789     long k;
790     int spk = ALL_SILENT;
791     k = locations[game.loc].sound;
792     if (k != SILENT) {
793         rspeak(k);
794         if (locations[game.loc].loud)
795             return GO_CLEAROBJ;
796         else
797             spk = NO_MESSAGE;
798     }
799     for (int i = 1; i <= NOBJECTS; i++) {
800         if (!HERE(i) || objects[i].sounds[0] == NULL || game.prop[i] < 0)
801             continue;
802         int mi =  game.prop[i];
803         if (i == BIRD)
804             mi += 3 * game.blooded;
805         long packed_zzword = token_to_packed(game.zzword);
806         pspeak(i, hear, mi, true, packed_zzword);
807         spk = NO_MESSAGE;
808         /* FIXME: Magic number, sensitive to bird state logic */
809         if (i == BIRD && game.prop[i] == 5)
810             DESTROY(BIRD);
811     }
812     rspeak(spk);
813     return GO_CLEAROBJ;
814 }
815
816 static int lock(token_t verb, token_t obj)
817 /* Lock, unlock, no object given.  Assume various things if present. */
818 {
819     int spk = actions[verb].message;
820     if (obj == INTRANSITIVE) {
821         spk = NOTHING_LOCKED;
822         if (HERE(CLAM))
823             obj = CLAM;
824         if (HERE(OYSTER))
825             obj = OYSTER;
826         if (AT(DOOR))
827             obj = DOOR;
828         if (AT(GRATE))
829             obj = GRATE;
830         if (HERE(CHAIN))
831             obj = CHAIN;
832         if (obj == NO_OBJECT || obj == INTRANSITIVE) {
833             rspeak(spk);
834             return GO_CLEAROBJ;
835         }
836     }
837
838     /*  Lock, unlock object.  Special stuff for opening clam/oyster
839      *  and for chain. */
840     if (obj == CLAM || obj == OYSTER)
841         return bivalve(verb, obj);
842     if (obj == DOOR)
843         spk = RUSTY_DOOR;
844     if (obj == DOOR && game.prop[DOOR] == DOOR_UNRUSTED)
845         spk = OK_MAN;
846     if (obj == CAGE)
847         spk = NO_LOCK;
848     if (obj == KEYS)
849         spk = CANNOT_UNLOCK;
850     if (obj == GRATE || obj == CHAIN) {
851         spk = NO_KEYS;
852         if (HERE(KEYS)) {
853             if (obj == CHAIN)
854                 return chain(verb);
855             if (game.closng) {
856                 spk = EXIT_CLOSED;
857                 if (!game.panic)
858                     game.clock2 = PANICTIME;
859                 game.panic = true;
860             } else {
861                 state_change(GRATE, (verb == LOCK) ? GRATE_CLOSED : GRATE_OPEN);
862                 return GO_CLEAROBJ;
863             }
864         }
865     }
866     rspeak(spk);
867     return GO_CLEAROBJ;
868 }
869
870 static int pour(token_t verb, token_t obj)
871 /*  Pour.  If no object, or object is bottle, assume contents of bottle.
872  *  special tests for pouring water or oil on plant or rusty door. */
873 {
874     int spk = actions[verb].message;
875     if (obj == BOTTLE || obj == NO_OBJECT)
876         obj = LIQUID();
877     if (obj == NO_OBJECT)
878         return GO_UNKNOWN;
879     if (!TOTING(obj)) {
880         rspeak(spk);
881         return GO_CLEAROBJ;
882     }
883     spk = CANT_POUR;
884     if (obj != OIL && obj != WATER) {
885         rspeak(spk);
886         return GO_CLEAROBJ;
887     }
888     if (HERE(URN) && game.prop[URN] == URN_EMPTY)
889         return fill(verb, URN);
890     game.prop[BOTTLE] = EMPTY_BOTTLE;
891     game.place[obj] = LOC_NOWHERE;
892     spk = GROUND_WET;
893     if (!(AT(PLANT) || AT(DOOR))) {
894         rspeak(spk);
895         return GO_CLEAROBJ;
896     }
897     if (!AT(DOOR)) {
898         spk = SHAKING_LEAVES;
899         if (obj != WATER) {
900             rspeak(spk);
901             return GO_CLEAROBJ;
902         }
903         pspeak(PLANT, look, game.prop[PLANT] + 3, true);
904         game.prop[PLANT] = MOD(game.prop[PLANT] + 1, 3);
905         game.prop[PLANT2] = game.prop[PLANT];
906         return GO_MOVE;
907     } else {
908         state_change(DOOR, (obj == OIL) ? DOOR_UNRUSTED : DOOR_RUSTED);
909         return GO_CLEAROBJ;
910     }
911 }
912
913 static int quit(void)
914 /*  Quit.  Intransitive only.  Verify intent and exit if that's what he wants. */
915 {
916     if (yes(arbitrary_messages[REALLY_QUIT], arbitrary_messages[OK_MAN], arbitrary_messages[OK_MAN]))
917         terminate(quitgame);
918     return GO_CLEAROBJ;
919 }
920
921 static int read(struct command_t command)
922 /*  Read.  Print stuff based on objtxt.  Oyster (?) is special case. */
923 {
924     if (command.obj == INTRANSITIVE) {
925         command.obj = 0;
926         for (int i = 1; i <= NOBJECTS; i++) {
927             if (HERE(i) && objects[i].texts[0] != NULL && game.prop[i] >= 0)
928                 command.obj = command.obj * NOBJECTS + i;
929         }
930         if (command.obj > NOBJECTS || command.obj == 0 || DARK(game.loc))
931             return GO_UNKNOWN;
932     }
933
934     if (DARK(game.loc)) {
935         rspeak(NO_SEE, command.wd1, command.wd1x);
936     } else if (command.obj == OYSTER && !game.clshnt && game.closed) {
937         game.clshnt = yes(arbitrary_messages[CLUE_QUERY], arbitrary_messages[WAYOUT_CLUE], arbitrary_messages[OK_MAN]);
938     } else if (objects[command.obj].texts[0] == NULL || game.prop[command.obj] < 0) {
939         rspeak(actions[command.verb].message);
940     } else
941         pspeak(command.obj, study, game.prop[command.obj], true);
942     return GO_CLEAROBJ;
943 }
944
945 static int reservoir(void)
946 /*  Z'ZZZ (word gets recomputed at startup; different each game). */
947 {
948     if (!AT(RESER) && game.loc != game.fixed[RESER] - 1) {
949         rspeak(NOTHING_HAPPENS);
950         return GO_CLEAROBJ;
951     } else {
952         pspeak(RESER, look, game.prop[RESER] + 1, true);
953         game.prop[RESER] = 1 - game.prop[RESER];
954         if (AT(RESER))
955             return GO_CLEAROBJ;
956         else {
957             game.oldlc2 = game.loc;
958             game.newloc = 0;
959             rspeak(NOT_BRIGHT);
960             return GO_TERMINATE;
961         }
962     }
963 }
964
965 static int rub(token_t verb, token_t obj)
966 /* Rub.  Yields various snide remarks except for lit urn. */
967 {
968     if (obj == URN && game.prop[URN] == URN_LIT) {
969         DESTROY(URN);
970         drop(AMBER, game.loc);
971         game.prop[AMBER] = AMBER_IN_ROCK;
972         --game.tally;
973         drop(CAVITY, game.loc);
974         rspeak(URN_GENIES);
975     } else if (obj != LAMP) {
976         rspeak(PECULIAR_NOTHING);
977     } else {
978         rspeak(actions[verb].message);
979     }
980     return GO_CLEAROBJ;
981 }
982
983 static int say(struct command_t *command)
984 /* Say.  Echo WD2 (or WD1 if no WD2 (SAY WHAT?, etc.).)  Magic words override. */
985 {
986     long a = command->wd1, b = command->wd1x;
987     if (command->wd2 > 0) {
988         a = command->wd2;
989         b = command->wd2x;
990         command->wd1 = command->wd2;
991     }
992     char word1[6];
993     packed_to_token(command->wd1, word1);
994     int wd = (int) get_vocab_id(word1);
995     /* FIXME: magic numbers */
996     if (wd == MOTION_WORD(XYZZY) || wd == MOTION_WORD(PLUGH) || wd == MOTION_WORD(PLOVER) || wd == ACTION_WORD(GIANTWORDS) || wd == ACTION_WORD(PART)) {
997         /* FIXME: scribbles on the interpreter's command block */
998         wordclear(&command->wd2);
999         return GO_LOOKUP;
1000     }
1001     rspeak(OKEY_DOKEY, a, b);
1002     return GO_CLEAROBJ;
1003 }
1004
1005 static int throw_support(long spk)
1006 {
1007     rspeak(spk);
1008     drop(AXE, game.loc);
1009     return GO_MOVE;
1010 }
1011
1012 static int throw (struct command_t *command)
1013 /*  Throw.  Same as discard unless axe.  Then same as attack except
1014  *  ignore bird, and if dwarf is present then one might be killed.
1015  *  (Only way to do so!)  Axe also special for dragon, bear, and
1016  *  troll.  Treasures special for troll. */
1017 {
1018     if (TOTING(ROD2) && command->obj == ROD && !TOTING(ROD))
1019         command->obj = ROD2;
1020     if (!TOTING(command->obj)) {
1021         rspeak(actions[command->verb].message);
1022         return GO_CLEAROBJ;
1023     }
1024     if (objects[command->obj].is_treasure && AT(TROLL)) {
1025         /*  Snarf a treasure for the troll. */
1026         drop(command->obj, LOC_NOWHERE);
1027         move(TROLL, LOC_NOWHERE);
1028         move(TROLL + NOBJECTS, LOC_NOWHERE);
1029         drop(TROLL2, objects[TROLL].plac);
1030         drop(TROLL2 + NOBJECTS, objects[TROLL].fixd);
1031         juggle(CHASM);
1032         rspeak(TROLL_SATISFIED);
1033         return GO_CLEAROBJ;
1034     }
1035     if (command->obj == FOOD && HERE(BEAR)) {
1036         /* But throwing food is another story. */
1037         command->obj = BEAR;
1038         return (feed(command->verb, command->obj));
1039     }
1040     if (command->obj != AXE)
1041         return (discard(command->verb, command->obj, false));
1042     else {
1043         if (atdwrf(game.loc) <= 0) {
1044             if (AT(DRAGON) && game.prop[DRAGON] == DRAGON_BARS)
1045                 return throw_support(DRAGON_SCALES);
1046             if (AT(TROLL))
1047                 return throw_support(TROLL_RETURNS);
1048             else if (AT(OGRE))
1049                 return throw_support(OGRE_DODGE);
1050             else if (HERE(BEAR) && game.prop[BEAR] == UNTAMED_BEAR) {
1051                 /* This'll teach him to throw the axe at the bear! */
1052                 drop(AXE, game.loc);
1053                 game.fixed[AXE] = -1;
1054                 juggle(BEAR);
1055                 state_change(AXE, AXE_LOST);
1056                 return GO_CLEAROBJ;
1057             }
1058             command->obj = NO_OBJECT;
1059             return (attack(command));
1060         }
1061
1062         if (randrange(NDWARVES + 1) < game.dflag) {
1063             return throw_support(DWARF_DODGES);
1064         } else {
1065             long i = atdwrf(game.loc);
1066             game.dseen[i] = false;
1067             game.dloc[i] = 0;
1068             return throw_support((++game.dkill == 1)
1069                                  ? DWARF_SMOKE : KILLED_DWARF);
1070         }
1071     }
1072 }
1073
1074 static int wake(token_t verb, token_t obj)
1075 /* Wake.  Only use is to disturb the dwarves. */
1076 {
1077     if (obj != DWARF || !game.closed) {
1078         rspeak(actions[verb].message);
1079         return GO_CLEAROBJ;
1080     } else {
1081         rspeak(PROD_DWARF);
1082         return GO_DWARFWAKE;
1083     }
1084 }
1085
1086 static token_t birdspeak(void)
1087 {
1088     switch (game.prop[BIRD]) {
1089     case BIRD_CAGED:
1090         return CAGE_FLY;
1091     default:
1092         return FREE_FLY;
1093     }
1094 }
1095
1096 static int wave(token_t verb, token_t obj)
1097 /* Wave.  No effect unless waving rod at fissure or at bird. */
1098 {
1099     if (obj != ROD ||
1100         !TOTING(obj) ||
1101         (!HERE(BIRD) && (game.closng || !AT(FISSURE)))) {
1102         rspeak(((!TOTING(obj)) && (obj != ROD || !TOTING(ROD2))) ? ARENT_CARRYING : actions[verb].message);
1103         return GO_CLEAROBJ;
1104     }
1105
1106     if (game.prop[BIRD] == BIRD_UNCAGED && game.loc == game.place[STEPS] && game.prop[JADE] < 0) {
1107         drop(JADE, game.loc);
1108         game.prop[JADE] = 0;
1109         --game.tally;
1110         rspeak(NECKLACE_FLY);
1111         return GO_CLEAROBJ;
1112     } else {
1113         if (game.closed) {
1114             rspeak(birdspeak());
1115             return GO_DWARFWAKE;
1116         }
1117         if (game.closng || !AT(FISSURE)) {
1118             rspeak(birdspeak());
1119             return GO_CLEAROBJ;
1120         }
1121         if (HERE(BIRD))
1122             rspeak(birdspeak());
1123
1124         /* FIXME: Arithemetic on property values */
1125         game.prop[FISSURE] = 1 - game.prop[FISSURE];
1126         pspeak(FISSURE, look, 2 - game.prop[FISSURE], true);
1127         return GO_CLEAROBJ;
1128     }
1129 }
1130
1131 int action(struct command_t *command)
1132 /*  Analyse a verb.  Remember what it was, go back for object if second word
1133  *  unless verb is "say", which snarfs arbitrary second word.
1134  */
1135 {
1136     if (command->part == unknown) {
1137         /*  Analyse an object word.  See if the thing is here, whether
1138          *  we've got a verb yet, and so on.  Object must be here
1139          *  unless verb is "find" or "invent(ory)" (and no new verb
1140          *  yet to be analysed).  Water and oil are also funny, since
1141          *  they are never actually dropped at any location, but might
1142          *  be here inside the bottle or urn or as a feature of the
1143          *  location. */
1144         if (HERE(command->obj))
1145             /* FALL THROUGH */;
1146         else if (command->obj == GRATE) {
1147             if (game.loc == LOC_START || game.loc == LOC_VALLEY || game.loc == LOC_SLIT)
1148                 command->obj = DPRSSN;
1149             if (game.loc == LOC_COBBLE || game.loc == LOC_DEBRIS || game.loc == LOC_AWKWARD ||
1150                 game.loc == LOC_BIRD || game.loc == LOC_PITTOP)
1151                 command->obj = ENTRNC;
1152         } else if (command->obj == DWARF && atdwrf(game.loc) > 0)
1153             /* FALL THROUGH */;
1154         else if ((LIQUID() == command->obj && HERE(BOTTLE)) || command->obj == LIQLOC(game.loc))
1155             /* FALL THROUGH */;
1156         else if (command->obj == OIL && HERE(URN) && game.prop[URN] != 0) {
1157             command->obj = URN;
1158             /* FALL THROUGH */;
1159         } else if (command->obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) {
1160             command->obj = PLANT2;
1161             /* FALL THROUGH */;
1162         } else if (command->obj == KNIFE && game.knfloc == game.loc) {
1163             game.knfloc = -1;
1164             rspeak(KNIVES_VANISH);
1165             return GO_CLEAROBJ;
1166         } else if (command->obj == ROD && HERE(ROD2)) {
1167             command->obj = ROD2;
1168             /* FALL THROUGH */;
1169         } else if ((command->verb == FIND || command->verb == INVENTORY) && command->wd2 <= 0)
1170             /* FALL THROUGH */;
1171         else {
1172             rspeak(NO_SEE, command->wd1, command->wd1x);
1173             return GO_CLEAROBJ;
1174         }
1175
1176         if (command->wd2 > 0)
1177             return GO_WORD2;
1178         if (command->verb != 0)
1179             command->part = transitive;
1180     }
1181
1182     switch (command->part) {
1183     case intransitive:
1184         if (command->wd2 > 0 && command->verb != SAY)
1185             return GO_WORD2;
1186         if (command->verb == SAY)
1187             command->obj = command->wd2;
1188         if (command->obj == 0 || command->obj == INTRANSITIVE) {
1189             /*  Analyse an intransitive verb (ie, no object given yet). */
1190             switch (command->verb) {
1191             case CARRY:
1192                 return vcarry(command->verb, INTRANSITIVE);
1193             case  DROP:
1194                 return GO_UNKNOWN;
1195             case  SAY:
1196                 return GO_UNKNOWN;
1197             case  UNLOCK:
1198                 return lock(command->verb, INTRANSITIVE);
1199             case  NOTHING: {
1200                 rspeak(OK_MAN);
1201                 return (GO_CLEAROBJ);
1202             }
1203             case  LOCK:
1204                 return lock(command->verb, INTRANSITIVE);
1205             case  LIGHT:
1206                 return light(command->verb, INTRANSITIVE);
1207             case  EXTINGUISH:
1208                 return extinguish(command->verb, INTRANSITIVE);
1209             case  WAVE:
1210                 return GO_UNKNOWN;
1211             case  TAME:
1212                 return GO_UNKNOWN;
1213             case GO: {
1214                 rspeak(actions[command->verb].message);
1215                 return GO_CLEAROBJ;
1216             }
1217             case ATTACK:
1218                 return attack(command);
1219             case POUR:
1220                 return pour(command->verb, command->obj);
1221             case EAT:
1222                 return eat(command->verb, INTRANSITIVE);
1223             case DRINK:
1224                 return drink(command->verb, command->obj);
1225             case RUB:
1226                 return GO_UNKNOWN;
1227             case THROW:
1228                 return GO_UNKNOWN;
1229             case QUIT:
1230                 return quit();
1231             case FIND:
1232                 return GO_UNKNOWN;
1233             case INVENTORY:
1234                 return inven();
1235             case FEED:
1236                 return GO_UNKNOWN;
1237             case FILL:
1238                 return fill(command->verb, command->obj);
1239             case BLAST:
1240                 blast();
1241                 return GO_CLEAROBJ;
1242             case SCORE:
1243                 score(scoregame);
1244                 return GO_CLEAROBJ;
1245             case GIANTWORDS:
1246                 return bigwords(command->wd1);
1247             case BRIEF:
1248                 return brief();
1249             case READ:
1250                 command->obj = INTRANSITIVE;
1251                 return read(*command);
1252             case BREAK:
1253                 return GO_UNKNOWN;
1254             case WAKE:
1255                 return GO_UNKNOWN;
1256             case SAVE:
1257                 return suspend();
1258             case RESUME:
1259                 return resume();
1260             case FLY:
1261                 return fly(command->verb, INTRANSITIVE);
1262             case LISTEN:
1263                 return listen();
1264             case PART:
1265                 return reservoir();
1266             default:
1267                 BUG(INTRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
1268             }
1269         }
1270     /* FALLTHRU */
1271     case transitive:
1272         /*  Analyse a transitive verb. */
1273         switch (command->verb) {
1274         case  CARRY:
1275             return vcarry(command->verb, command->obj);
1276         case  DROP:
1277             return discard(command->verb, command->obj, false);
1278         case  SAY:
1279             return say(command);
1280         case  UNLOCK:
1281             return lock(command->verb, command->obj);
1282         case  NOTHING: {
1283             rspeak(OK_MAN);
1284             return (GO_CLEAROBJ);
1285         }
1286         case  LOCK:
1287             return lock(command->verb, command->obj);
1288         case LIGHT:
1289             return light(command->verb, command->obj);
1290         case EXTINGUISH:
1291             return extinguish(command->verb, command->obj);
1292         case WAVE:
1293             return wave(command->verb, command->obj);
1294         case TAME: {
1295             rspeak(actions[command->verb].message);
1296             return GO_CLEAROBJ;
1297         }
1298         case GO: {
1299             rspeak(actions[command->verb].message);
1300             return GO_CLEAROBJ;
1301         }
1302         case ATTACK:
1303             return attack(command);
1304         case POUR:
1305             return pour(command->verb, command->obj);
1306         case EAT:
1307             return eat(command->verb, command->obj);
1308         case DRINK:
1309             return drink(command->verb, command->obj);
1310         case RUB:
1311             return rub(command->verb, command->obj);
1312         case THROW:
1313             return throw (command);
1314         case QUIT: {
1315             rspeak(actions[command->verb].message);
1316             return GO_CLEAROBJ;
1317         }
1318         case FIND:
1319             return find(command->verb, command->obj);
1320         case INVENTORY:
1321             return find(command->verb, command->obj);
1322         case FEED:
1323             return feed(command->verb, command->obj);
1324         case FILL:
1325             return fill(command->verb, command->obj);
1326         case BLAST:
1327             blast();
1328             return GO_CLEAROBJ;
1329         case SCORE: {
1330             rspeak(actions[command->verb].message);
1331             return GO_CLEAROBJ;
1332         }
1333         case GIANTWORDS: {
1334             rspeak(actions[command->verb].message);
1335             return GO_CLEAROBJ;
1336         }
1337         case BRIEF: {
1338             rspeak(actions[command->verb].message);
1339             return GO_CLEAROBJ;
1340         }
1341         case READ:
1342             return read(*command);
1343         case BREAK:
1344             return vbreak(command->verb, command->obj);
1345         case WAKE:
1346             return wake(command->verb, command->obj);
1347         case SAVE: {
1348             rspeak(actions[command->verb].message);
1349             return GO_CLEAROBJ;
1350         }
1351         case RESUME: {
1352             rspeak(actions[command->verb].message);
1353             return GO_CLEAROBJ;
1354         }
1355         case FLY:
1356             return fly(command->verb, command->obj);
1357         case LISTEN: {
1358             rspeak(actions[command->verb].message);
1359             return GO_CLEAROBJ;
1360         }
1361         case PART:
1362             return reservoir();
1363         default:
1364             BUG(TRANSITIVE_ACTION_VERB_EXCEEDS_GOTO_LIST); // LCOV_EXCL_LINE
1365         }
1366     case unknown:
1367         /* Unknown verb, couldn't deduce object - might need hint */
1368         rspeak(WHAT_DO, command->wd1, command->wd1x);
1369         return GO_CHECKHINT;
1370     default:
1371         BUG(SPEECHPART_NOT_TRANSITIVE_OR_INTRANSITIVE_OR_UNKNOWN); // LCOV_EXCL_LINE
1372     }
1373 }