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