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