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