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