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