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