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