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