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