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