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