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