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