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