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