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