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