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