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