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