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