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