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