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