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