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