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