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