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