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