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