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