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