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