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