Code simplifications based on abolishing 2011.
[open-adventure.git] / actions.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3 #include "advent.h"
4 #include "database.h"
5
6 /*
7  * Action handlers.  Eventually we'll do lookup through a method table
8  * that calls these.  Absolutely nothing like the original FORTRAN.
9  */
10
11 static int fill(long);
12
13 static int attack(FILE *input, long verb, long obj)
14 /*  Attack.  Assume target if unambiguous.  "Throw" also links here.
15  *  Attackable objects fall into two categories: enemies (snake,
16  *  dwarf, etc.)  and others (bird, clam, machine).  Ambiguous if 2
17  *  enemies, or no enemies but 2 others. */
18 {
19     int i =ATDWRF(game.loc);
20     if (obj == 0) {
21         if (i > 0)
22             obj=DWARF;
23         if (HERE(SNAKE))obj=obj*NOBJECTS+SNAKE;
24         if (AT(DRAGON) && game.prop[DRAGON] == 0)obj=obj*NOBJECTS+DRAGON;
25         if (AT(TROLL))obj=obj*NOBJECTS+TROLL;
26         if (AT(OGRE))obj=obj*NOBJECTS+OGRE;
27         if (HERE(BEAR) && game.prop[BEAR] == 0)obj=obj*NOBJECTS+BEAR;
28         if (obj > NOBJECTS) return(8000);
29         if (obj == 0) {
30             /* Can't attack bird or machine by throwing axe. */
31             if (HERE(BIRD) && verb != THROW)obj=BIRD;
32             if (HERE(VEND) && verb != THROW)obj=obj*NOBJECTS+VEND;
33             /* Clam and oyster both treated as clam for intransitive case;
34              * no harm done. */
35             if (HERE(CLAM) || HERE(OYSTER))obj=NOBJECTS*obj+CLAM;
36             if (obj > NOBJECTS) return(8000);
37         }
38     }
39     if (obj == BIRD) {
40         SPK=137;
41         if (game.closed)
42         {
43             RSPEAK(SPK);
44             return 2012;
45         }
46         DSTROY(BIRD);
47         game.prop[BIRD]=0;
48         SPK=45;
49     }
50     if (obj == VEND) {
51         PSPEAK(VEND,game.prop[VEND]+2);
52         game.prop[VEND]=3-game.prop[VEND];
53         return(2012);
54     }
55
56     if (obj == 0)SPK=44;
57     if (obj == CLAM || obj == OYSTER)SPK=150;
58     if (obj == SNAKE)SPK=46;
59     if (obj == DWARF)SPK=49;
60     if (obj == DWARF && game.closed) return(19000);
61     if (obj == DRAGON)SPK=167;
62     if (obj == TROLL)SPK=157;
63     if (obj == OGRE)SPK=203;
64     if (obj == OGRE && i > 0) {
65         RSPEAK(SPK);
66         RSPEAK(6);
67         DSTROY(OGRE);
68         int k=0;
69         for (i=1; i < PIRATE; i++) {
70             if (game.dloc[i] == game.loc) {
71                 ++k;
72                 game.dloc[i]=61;
73                 game.dseen[i]=false;
74             }
75         }
76         SPK=SPK+1+1/k;
77         RSPEAK(SPK);
78         return 2012;
79     }
80     if (obj == BEAR)SPK=165+(game.prop[BEAR]+1)/2;
81     if (obj != DRAGON || game.prop[DRAGON] != 0) {RSPEAK(SPK); return 2012;}
82     /*  Fun stuff for dragon.  If he insists on attacking it, win!
83      *  Set game.prop to dead, move dragon to central loc (still
84      *  fixed), move rug there (not fixed), and move him there,
85      *  too.  Then do a null motion to get new description. */
86     RSPEAK(49);
87     GETIN(input,&WD1,&WD1X,&WD2,&WD2X);
88     if (WD1 != MAKEWD(25) && WD1 != MAKEWD(250519))
89         return(2607);
90     PSPEAK(DRAGON,3);
91     game.prop[DRAGON]=1;
92     game.prop[RUG]=0;
93     int k=(PLAC[DRAGON]+FIXD[DRAGON])/2;
94     MOVE(DRAGON+NOBJECTS,-1);
95     MOVE(RUG+NOBJECTS,0);
96     MOVE(DRAGON,k);
97     MOVE(RUG,k);
98     DROP(BLOOD,k);
99     for (obj=1; obj<=NOBJECTS; obj++) {
100         if (game.place[obj] == PLAC[DRAGON] || game.place[obj] == FIXD[DRAGON])
101             MOVE(obj,k);
102     }
103     game.loc=k;
104     K=NUL;      /* FIXME: error if removed */
105     return(8);
106 }
107
108 static int bigwords(long foo)
109 /*  FEE FIE FOE FOO (AND FUM).  Advance to next state if given in proper order.
110  *  Look up WD1 in section 3 of vocab to determine which word we've got.  Last
111  *  word zips the eggs back to the giant room (unless already there). */
112 {
113     int k=VOCAB(foo,3);
114     SPK=42;
115     if (game.foobar != 1-k) {
116         if (game.foobar != 0)SPK=151;
117         RSPEAK(SPK);
118         return 2012;
119     } else {
120         game.foobar=k;
121         if (k != 4) return(2009);
122         game.foobar=0;
123         if (game.place[EGGS]==PLAC[EGGS] || (TOTING(EGGS) && game.loc==PLAC[EGGS])) {
124             RSPEAK(SPK);
125             return 2012;
126         }
127         /*  Bring back troll if we steal the eggs back from him before
128          *  crossing. */
129         if (game.place[EGGS]==0 && game.place[TROLL]==0 && game.prop[TROLL]==0)
130             game.prop[TROLL]=1;
131         k=2;
132         if (HERE(EGGS))k=1;
133         if (game.loc == PLAC[EGGS])k=0;
134         MOVE(EGGS,PLAC[EGGS]);
135         PSPEAK(EGGS,k);
136         return(2012);
137     }
138 }
139
140 static int bivalve(token_t verb, token_t obj)
141 /* Clam/oyster actions */
142 {
143     int k=0;
144     if (obj == OYSTER)k=1;
145     SPK=124+k;
146     if (TOTING(obj))SPK=120+k;
147     if (!TOTING(TRIDNT))SPK=122+k;
148     if (verb == LOCK)SPK=61;
149     if (SPK == 124) {
150         DSTROY(CLAM);
151         DROP(OYSTER,game.loc);
152         DROP(PEARL,105);
153     }
154     RSPEAK(SPK);
155     return 2012;
156 }
157
158 static int blast(void)
159 /*  Blast.  No effect unless you've got dynamite, which is a neat trick! */
160 {
161     if (game.prop[ROD2] < 0 || !game.closed) {RSPEAK(SPK); return 2012;}
162     game.bonus=133;
163     if (game.loc == 115)game.bonus=134;
164     if (HERE(ROD2))game.bonus=135;
165     RSPEAK(game.bonus);
166     score(0);
167 }
168
169 static int vbreak(token_t obj)
170 /*  Break.  Only works for mirror in repository and, of course, the vase. */
171 {
172     if (obj == MIRROR)SPK=148;
173     if (obj == VASE && game.prop[VASE] == 0) {
174         SPK=198;
175         if (TOTING(VASE))DROP(VASE,game.loc);
176         game.prop[VASE]=2;
177         game.fixed[VASE]= -1;
178     } else {
179         if (obj == MIRROR && game.closed) {
180             SPK=197;
181             return(18999);
182         }
183     }
184     RSPEAK(SPK);
185     return 2012;
186 }
187
188 static int brief(void)
189 /*  Brief.  Intransitive only.  Suppress long descriptions after first time. */
190 {
191     SPK=156;
192     game.abbnum=10000;
193     game.detail=3;
194     RSPEAK(SPK);
195     return 2012;
196 }
197
198 static int carry(long obj)
199 /*  Carry an object.  Special cases for bird and cage (if bird in cage, can't
200  *  take one without the other).  Liquids also special, since they depend on
201  *  status of bottle.  Also various side effects, etc. */
202 {
203     if (obj == INTRANSITIVE) {
204         /*  Carry, no object given yet.  OK if only one object present. */
205         if(game.atloc[game.loc] == 0 ||
206            game.link[game.atloc[game.loc]] != 0 ||
207            ATDWRF(game.loc) > 0)
208             return(8000);
209         obj=game.atloc[game.loc];
210     }
211
212     if (TOTING(obj)) {RSPEAK(SPK); return 2012;}
213     SPK=25;
214     if (obj == PLANT && game.prop[PLANT] <= 0)SPK=115;
215     if (obj == BEAR && game.prop[BEAR] == 1)SPK=169;
216     if (obj == CHAIN && game.prop[BEAR] != 0)SPK=170;
217     if (obj == URN)SPK=215;
218     if (obj == CAVITY)SPK=217;
219     if (obj == BLOOD)SPK=239;
220     if (obj == RUG && game.prop[RUG] == 2)SPK=222;
221     if (obj == SIGN)SPK=196;
222     if (obj == MESSAG) {
223         SPK=190;
224         DSTROY(MESSAG);
225     }
226     if (game.fixed[obj] != 0) {
227         RSPEAK(SPK);
228         return 2012;
229     }
230     if (obj == WATER || obj == OIL) {
231         if (!HERE(BOTTLE) || LIQUID() != obj) {
232             if (TOTING(BOTTLE) && game.prop[BOTTLE] == 1)
233                 return(fill(BOTTLE));
234             if (game.prop[BOTTLE] != 1)SPK=105;
235             if (!TOTING(BOTTLE))SPK=104;
236             RSPEAK(SPK);
237             return 2012;
238         }
239         obj = BOTTLE;
240     }
241
242     SPK=92;
243     if (game.holdng >= INVLIMIT) {
244         RSPEAK(SPK);
245         return 2012;
246     }
247     else if (obj == BIRD && game.prop[BIRD] != 1 && -1-game.prop[BIRD] != 1) {
248         if (game.prop[BIRD] == 2) {
249             DSTROY(BIRD);
250             RSPEAK(238);
251             return 2012;
252         }
253         if (!TOTING(CAGE))SPK=27;
254         if (TOTING(ROD))SPK=26;
255         if (SPK/2 == 13) {
256             RSPEAK(SPK);
257             return 2012;
258         }
259         game.prop[BIRD]=1;
260     }
261     if ((obj==BIRD || obj==CAGE) && (game.prop[BIRD]==1 || -1-game.prop[BIRD]==1))
262         CARRY(BIRD+CAGE-obj,game.loc);
263     CARRY(obj,game.loc);
264     if (obj == BOTTLE && LIQUID() != 0)
265         game.place[LIQUID()] = -1;
266     if (!GSTONE(obj) || game.prop[obj] == 0)
267         return(2009);
268     game.prop[obj]=0;
269     game.prop[CAVITY]=1;
270     return(2009);
271 }
272
273 static int chain(token_t verb)
274 /* Do something to the bear's chain */
275 {
276     if (verb != LOCK) {
277         SPK=171;
278         if (game.prop[BEAR] == 0)SPK=41;
279         if (game.prop[CHAIN] == 0)SPK=37;
280         if (SPK != 171) {RSPEAK(SPK); return 2012;}
281         game.prop[CHAIN]=0;
282         game.fixed[CHAIN]=0;
283         if (game.prop[BEAR] != 3)game.prop[BEAR]=2;
284         game.fixed[BEAR]=2-game.prop[BEAR];
285     } else {
286         SPK=172;
287         if (game.prop[CHAIN] != 0)SPK=34;
288         if (game.loc != PLAC[CHAIN])SPK=173;
289         if (SPK != 172) {RSPEAK(SPK); return 2012;}
290         game.prop[CHAIN]=2;
291         if (TOTING(CHAIN))DROP(CHAIN,game.loc);
292         game.fixed[CHAIN]= -1;
293     }
294     RSPEAK(SPK);
295     return 2012;
296 }
297
298 static int discard(long obj, bool just_do_it)
299 /*  Discard object.  "Throw" also comes here for most objects.  Special cases for
300  *  bird (might attack snake or dragon) and cage (might contain bird) and vase.
301  *  Drop coins at vending machine for extra batteries. */
302 {
303     if (!just_do_it) {
304         if (TOTING(ROD2) && obj == ROD && !TOTING(ROD))obj=ROD2;
305         if (!TOTING(obj)) {RSPEAK(SPK); return 2012;}
306         if (obj == BIRD && HERE(SNAKE)) {
307             RSPEAK(30);
308             if (game.closed) return(19000);
309             DSTROY(SNAKE);
310             /* Set game.prop for use by travel options */
311             game.prop[SNAKE]=1;
312
313         } else if ((GSTONE(obj) && AT(CAVITY) && game.prop[CAVITY] != 0)) {
314             RSPEAK(218);
315             game.prop[obj]=1;
316             game.prop[CAVITY]=0;
317             if (HERE(RUG) && ((obj == EMRALD && game.prop[RUG] != 2) || (obj == RUBY &&
318                                                                          game.prop[RUG] == 2))) {
319                 SPK=219;
320                 if (TOTING(RUG))SPK=220;
321                 if (obj == RUBY)SPK=221;
322                 RSPEAK(SPK);
323                 if (SPK != 220) {
324                     K=2-game.prop[RUG];
325                     game.prop[RUG]=K;
326                     if (K == 2)K=PLAC[SAPPH];
327                     MOVE(RUG+NOBJECTS,K);
328                 }
329             }
330         } else if (obj == COINS && HERE(VEND)) {
331             DSTROY(COINS);
332             DROP(BATTER,game.loc);
333             PSPEAK(BATTER,0);
334             return(2012);
335         } else if (obj == BIRD && AT(DRAGON) && game.prop[DRAGON] == 0) {
336             RSPEAK(154);
337             DSTROY(BIRD);
338             game.prop[BIRD]=0;
339             return(2012);
340         } else if (obj == BEAR && AT(TROLL)) {
341             RSPEAK(163);
342             MOVE(TROLL,0);
343             MOVE(TROLL+NOBJECTS,0);
344             MOVE(TROLL2,PLAC[TROLL]);
345             MOVE(TROLL2+NOBJECTS,FIXD[TROLL]);
346             JUGGLE(CHASM);
347             game.prop[TROLL]=2;
348         } else if (obj != VASE || game.loc == PLAC[PILLOW]) {
349             RSPEAK(54);
350         } else {
351             game.prop[VASE]=2;
352             if (AT(PILLOW))game.prop[VASE]=0;
353             PSPEAK(VASE,game.prop[VASE]+1);
354             if (game.prop[VASE] != 0)game.fixed[VASE]= -1;
355         }
356     }
357     K=LIQUID();
358     if (K == obj)obj=BOTTLE;
359     if (obj == BOTTLE && K != 0)game.place[K]=0;
360     if (obj == CAGE && game.prop[BIRD] == 1)DROP(BIRD,game.loc);
361     DROP(obj,game.loc);
362     if (obj != BIRD) return(2012);
363     game.prop[BIRD]=0;
364     if (FOREST(game.loc))game.prop[BIRD]=2;
365     return(2012);
366 }
367
368 static int drink(token_t obj)
369 /*  Drink.  If no object, assume water and look for it here.  If water is in
370  *  the bottle, drink that, else must be at a water loc, so drink stream. */
371 {
372     if (obj == 0 && LIQLOC(game.loc) != WATER && (LIQUID() != WATER || !HERE(BOTTLE)))
373         return(8000);
374     if (obj != BLOOD) {
375         if (obj != 0 && obj != WATER)SPK=110;
376         if (SPK == 110 || LIQUID() != WATER || !HERE(BOTTLE)) {RSPEAK(SPK); return 2012;}
377         game.prop[BOTTLE]=1;
378         game.place[WATER]=0;
379         SPK=74;
380         RSPEAK(SPK);
381         return 2012;
382     } else {
383         DSTROY(BLOOD);
384         game.prop[DRAGON]=2;
385         OBJSND[BIRD]=OBJSND[BIRD]+3;
386         SPK=240;
387         RSPEAK(SPK);
388         return 2012;
389     }
390 }
391
392 static int eat(token_t obj)
393 /*  Eat.  Intransitive: assume food if present, else ask what.  Transitive: food
394  *  ok, some things lose appetite, rest are ridiculous. */
395 {
396     if (obj == INTRANSITIVE) {
397         if (!HERE(FOOD))
398             return(8000);
399         DSTROY(FOOD);
400         SPK=72;
401     } else {
402         if (obj == FOOD) {
403             DSTROY(FOOD);
404             SPK=72;
405         }
406         if (obj == BIRD || obj == SNAKE || obj == CLAM || obj == OYSTER || obj ==
407            DWARF || obj == DRAGON || obj == TROLL || obj == BEAR || obj ==
408            OGRE)SPK=71;
409     }
410     RSPEAK(SPK);
411     return 2012;
412 }
413
414 static int extinguish(int obj)
415 /* Extinguish.  Lamp, urn, dragon/volcano (nice try). */
416 {
417     if (obj == INTRANSITIVE) {
418         if (HERE(LAMP) && game.prop[LAMP] == 1)obj=LAMP;
419         if (HERE(URN) && game.prop[URN] == 2)obj=obj*NOBJECTS+URN;
420         if (obj == INTRANSITIVE || obj == 0 || obj > NOBJECTS) return(8000);
421     }
422
423     if (obj == URN) {
424         game.prop[URN]=game.prop[URN]/2;
425         SPK=210;
426         RSPEAK(SPK);
427         return 2012;
428     }
429     else if (obj == LAMP) {
430         game.prop[LAMP]=0;
431         RSPEAK(40);
432         if (DARK(game.loc))
433             RSPEAK(16);
434         return(2012);
435     }
436     else if (obj == DRAGON || obj == VOLCAN)
437         SPK=146;
438     RSPEAK(SPK);
439     return 2012;
440 }
441
442 static int feed(long obj)
443 /*  Feed.  If bird, no seed.  Snake, dragon, troll: quip.  If dwarf, make him
444  *  mad.  Bear, special. */
445 {
446     if (obj == BIRD) {
447         SPK=100;
448         RSPEAK(SPK);
449         return 2012;
450     }
451
452     if (!(obj != SNAKE && obj != DRAGON && obj != TROLL)) {
453         SPK=102;
454         if (obj == DRAGON && game.prop[DRAGON] != 0)SPK=110;
455         if (obj == TROLL)SPK=182;
456         if (obj != SNAKE || game.closed || !HERE(BIRD))
457             {RSPEAK(SPK); return 2012;}
458         SPK=101;
459         DSTROY(BIRD);
460         game.prop[BIRD]=0;
461         RSPEAK(SPK);
462         return 2012;
463     }
464
465     if (obj == DWARF) {
466         if (!HERE(FOOD))
467             {RSPEAK(SPK); return 2012;}
468         SPK=103;
469         game.dflag=game.dflag+2;
470         RSPEAK(SPK);
471         return 2012;
472     }
473
474     if (obj == BEAR) {
475         if (game.prop[BEAR] == 0)SPK=102;
476         if (game.prop[BEAR] == 3)SPK=110;
477         if (!HERE(FOOD)) {
478             RSPEAK(SPK);
479             return 2012;
480         }
481         DSTROY(FOOD);
482         game.prop[BEAR]=1;
483         game.fixed[AXE]=0;
484         game.prop[AXE]=0;
485         SPK=168;
486         RSPEAK(SPK);
487         return 2012;
488     }
489
490     if (obj == OGRE) {
491         if (HERE(FOOD))
492             SPK=202;
493         RSPEAK(SPK);
494         return 2012;
495     }
496
497     SPK=14;
498     RSPEAK(SPK);
499     return 2012;
500 }
501
502 int fill(long obj)
503 /*  Fill.  Bottle or urn must be empty, and liquid available.  (Vase
504  *  is nasty.) */
505 {
506     int k;
507     if (obj == VASE) {
508         SPK=29;
509         if (LIQLOC(game.loc) == 0)SPK=144;
510         if (LIQLOC(game.loc) == 0 || !TOTING(VASE)) {
511             RSPEAK(SPK);
512             return 2012;
513         }
514         RSPEAK(145);
515         game.prop[VASE]=2;
516         game.fixed[VASE]= -1;
517         return(discard(obj, true));
518     }
519
520     if (obj == URN){
521         SPK=213;
522         if (game.prop[URN] != 0) {RSPEAK(SPK); return 2012;}
523         SPK=144;
524         k=LIQUID();
525         if (k == 0 || !HERE(BOTTLE)) {RSPEAK(SPK); return 2012;}
526         game.place[k]=0;
527         game.prop[BOTTLE]=1;
528         if (k == OIL)game.prop[URN]=1;
529         SPK=211+game.prop[URN];
530         RSPEAK(SPK);
531         return 2012;
532     }
533
534     if (obj != 0 && obj != BOTTLE) {
535         RSPEAK(SPK);
536         return 2012;
537     }
538     if (obj == 0 && !HERE(BOTTLE))
539         return(8000);
540     SPK=107;
541     if (LIQLOC(game.loc) == 0)
542         SPK=106;
543     if (HERE(URN) && game.prop[URN] != 0)
544         SPK=214;
545     if (LIQUID() != 0)
546         SPK=105;
547     if (SPK != 107)
548         {RSPEAK(SPK); return 2012;}
549     game.prop[BOTTLE]=MOD(COND[game.loc],4)/2*2;
550     k=LIQUID();
551     if (TOTING(BOTTLE))
552         game.place[k]= -1;
553     if (k == OIL)
554         SPK=108;
555     RSPEAK(SPK);
556     return 2012;
557 }
558
559 static int find(token_t obj)
560 /* Find.  Might be carrying it, or it might be here.  Else give caveat. */
561 {
562     if (AT(obj) ||
563        (LIQUID() == obj && AT(BOTTLE)) ||
564        obj == LIQLOC(game.loc) ||
565        (obj == DWARF && ATDWRF(game.loc) > 0))
566         SPK=94;
567     if (game.closed)SPK=138;
568     if (TOTING(obj))SPK=24;
569     RSPEAK(SPK);
570     return 2012;
571 }
572
573 static int fly(token_t obj)
574 /* Fly.  Snide remarks unless hovering rug is here. */
575 {
576     if (obj == INTRANSITIVE) {
577         if (game.prop[RUG] != 2)SPK=224;
578         if (!HERE(RUG))SPK=225;
579         if (SPK/2 == 112) {
580             RSPEAK(SPK);
581             return 2012;
582         }
583         obj=RUG;
584     }
585
586     if (obj != RUG) {
587         RSPEAK(SPK);
588         return 2012;
589     }
590     SPK=223;
591     if (game.prop[RUG] != 2) {RSPEAK(SPK); return 2012;}
592     game.oldlc2=game.oldloc;
593     game.oldloc=game.loc;
594     game.newloc=game.place[RUG]+game.fixed[RUG]-game.loc;
595     SPK=226;
596     if (game.prop[SAPPH] >= 0)SPK=227;
597     RSPEAK(SPK);
598     return(2);
599 }
600     
601 static int inven(token_t obj)
602 /* Inventory. If object, treat same as find.  Else report on current burden. */
603 {
604     int i;
605     SPK=98;
606     for (i=1; i<=NOBJECTS; i++) {
607         if (i == BEAR || !TOTING(i))
608             continue;
609         if (SPK == 98)RSPEAK(99);
610         game.blklin=false;
611         PSPEAK(i,-1);
612         game.blklin=true;
613         SPK=0;
614     }
615     if (TOTING(BEAR))
616         SPK=141;
617     RSPEAK(SPK);
618     return 2012;
619 }
620
621 int light(token_t obj)
622 /*  Light.  Applicable only to lamp and urn. */
623 {
624     if (obj == INTRANSITIVE) {
625         if (HERE(LAMP) && game.prop[LAMP] == 0 && game.limit >= 0)obj=LAMP;
626         if (HERE(URN) && game.prop[URN] == 1)obj=obj*NOBJECTS+URN;
627         if (obj == INTRANSITIVE || obj == 0 || obj > NOBJECTS) return(8000);
628     }
629
630     if (obj == URN) {
631         SPK=38;
632         if (game.prop[URN] == 0)
633             {RSPEAK(SPK); return 2012;}
634         SPK=209;
635         game.prop[URN]=2;
636         RSPEAK(SPK);
637         return 2012;
638     } else {
639         if (obj != LAMP)
640         {
641             RSPEAK(SPK);
642             return 2012;
643         }
644         SPK=184;
645         if (game.limit < 0) {
646             RSPEAK(SPK);
647             return 2012;
648         }
649         game.prop[LAMP]=1;
650         RSPEAK(39);
651         if (game.wzdark)
652             return(2000);
653         return(2012);
654     }    
655 }
656
657 static int listen(void)
658 /*  Listen.  Intransitive only.  Print stuff based on objsnd/locsnd. */
659 {
660     int i, k;
661     SPK=228;
662     k=LOCSND[game.loc];
663     if (k != 0) {
664         RSPEAK(labs(k));
665         if (k < 0) return(2012);
666         SPK=0;
667     }
668     SETPRM(1,game.zzword,0);
669     for (i=1; i<=NOBJECTS; i++) {
670         if (!HERE(i) || OBJSND[i] == 0 || game.prop[i] < 0)
671             continue;
672         PSPEAK(i,OBJSND[i]+game.prop[i]);
673         SPK=0;
674         if (i == BIRD && OBJSND[i]+game.prop[i] == 8)
675             DSTROY(BIRD);
676     }
677     RSPEAK(SPK);
678     return 2012;
679 }
680
681 static int lock(token_t verb, token_t obj)
682 /* Lock, unlock, no object given.  Assume various things if present. */
683 {
684     int k;
685     if (obj == INTRANSITIVE) {
686         SPK=28;
687         if (HERE(CLAM))obj=CLAM;
688         if (HERE(OYSTER))obj=OYSTER;
689         if (AT(DOOR))obj=DOOR;
690         if (AT(GRATE))obj=GRATE;
691         if (obj != 0 && HERE(CHAIN)) return(8000);
692         if (HERE(CHAIN))obj=CHAIN;
693         if (obj == 0) {RSPEAK(SPK); return 2012;}
694     }
695         
696     /*  Lock, unlock object.  Special stuff for opening clam/oyster
697      *  and for chain. */
698     if (obj == CLAM || obj == OYSTER)
699         return bivalve(verb, obj);
700     if (obj == DOOR)SPK=111;
701     if (obj == DOOR && game.prop[DOOR] == 1)SPK=54;
702     if (obj == CAGE)SPK=32;
703     if (obj == KEYS)SPK=55;
704     if (obj == GRATE || obj == CHAIN)SPK=31;
705     if (SPK != 31 || !HERE(KEYS)) {
706         RSPEAK(SPK);
707         return 2012;
708     }
709     if (obj == CHAIN)
710         return chain(verb);
711     if (game.closng) {
712         SPK=130;
713         if (!game.panic)game.clock2=15;
714         game.panic=true;
715         RSPEAK(SPK);
716         return 2012;
717     }
718     SPK=34+game.prop[GRATE];
719     game.prop[GRATE]=1;
720     if (verb == LOCK)game.prop[GRATE]=0;
721     SPK=SPK+2*game.prop[GRATE];
722     RSPEAK(SPK);
723     return 2012;
724 }
725
726 static int pour(token_t obj)
727 /*  Pour.  If no object, or object is bottle, assume contents of bottle.
728  *  special tests for pouring water or oil on plant or rusty door. */
729 {
730     if (obj == BOTTLE || obj == 0)obj=LIQUID();
731     if (obj == 0) return(8000);
732     if (!TOTING(obj)) {RSPEAK(SPK); return 2012;}
733     SPK=78;
734     if (obj != OIL && obj != WATER) {RSPEAK(SPK); return 2012;}
735     if (HERE(URN) && game.prop[URN] == 0)
736         return fill(URN);
737     game.prop[BOTTLE]=1;
738     game.place[obj]=0;
739     SPK=77;
740     if (!(AT(PLANT) || AT(DOOR)))
741         {RSPEAK(SPK); return 2012;}
742     if (!AT(DOOR)) {
743         SPK=112;
744         if (obj != WATER) {RSPEAK(SPK); return 2012;}
745         PSPEAK(PLANT,game.prop[PLANT]+3);
746         game.prop[PLANT]=MOD(game.prop[PLANT]+1,3);
747         game.prop[PLANT2]=game.prop[PLANT];
748         K=NUL;
749         return(8);
750     } else {
751         game.prop[DOOR]=0;
752         if (obj == OIL)game.prop[DOOR]=1;
753         SPK=113+game.prop[DOOR];
754         RSPEAK(SPK);
755         return 2012;
756     }
757 }
758
759 static int quit(FILE *input)
760 /*  Quit.  Intransitive only.  Verify intent and exit if that's what he wants. */
761 {
762     if (YES(input,22,54,54))
763         score(1);
764     return(2012);
765 }
766
767 static int read(FILE *input, token_t obj)
768 /*  Read.  Print stuff based on objtxt.  Oyster (?) is special case. */
769 {
770     int i;
771     if (obj == INTRANSITIVE) {
772         obj = 0;
773         for (i=1; i<=NOBJECTS; i++) {
774             if (HERE(i) && OBJTXT[i] != 0 && game.prop[i] >= 0)
775                 obj = obj * NOBJECTS + i;
776         }
777         if (obj > NOBJECTS || obj == 0 || DARK(game.loc)) return(8000);
778     }
779         
780     if (DARK(game.loc)) {
781         SETPRM(1,WD1,WD1X);
782         RSPEAK(256);
783         return(2012);
784     }
785     if (OBJTXT[obj] == 0 || game.prop[obj] < 0)
786         {RSPEAK(SPK); return 2012;}
787     if (obj == OYSTER && !game.clshnt) {
788         game.clshnt=YES(input,192,193,54);
789         return(2012);
790     }
791     PSPEAK(obj,OBJTXT[obj]+game.prop[obj]);
792     return(2012);
793 }
794
795 static int reservoir(void)
796 /*  Z'ZZZ (word gets recomputed at startup; different each game). */
797 {
798     if (!AT(RESER) && game.loc != game.fixed[RESER]-1) {RSPEAK(SPK); return 2012;}
799     PSPEAK(RESER,game.prop[RESER]+1);
800     game.prop[RESER]=1-game.prop[RESER];
801     if (AT(RESER)) return(2012);
802     game.oldlc2=game.loc;
803     game.newloc=0;
804     RSPEAK(241);
805     return(2);
806 }
807
808 static int rub(token_t obj)
809 /* Rub.  Yields various snide remarks except for lit urn. */
810 {
811     if (obj != LAMP)SPK=76;
812     if (obj != URN || game.prop[URN] != 2) {RSPEAK(SPK); return 2012;}
813     DSTROY(URN);
814     DROP(AMBER,game.loc);
815     game.prop[AMBER]=1;
816     --game.tally;
817     DROP(CAVITY,game.loc);
818     SPK=216;
819     RSPEAK(SPK);
820     return 2012;
821 }
822
823 static int say(void)
824 /* SAY.  Echo WD2 (or WD1 if no WD2 (SAY WHAT?, etc.).)  Magic words override. */
825 {
826     /* FIXME: ugly use of globals */
827     SETPRM(1,WD2,WD2X); if (WD2 <= 0)SETPRM(1,WD1,WD1X);
828     if (WD2 > 0)WD1=WD2;
829     int wd=VOCAB(WD1,-1);
830     if (wd == 62 || wd == 65 || wd == 71 || wd == 2025 || wd == 2034) {
831         WD2=0;
832         return(2630);
833     }
834     RSPEAK(258);
835     return 2012;
836
837 }
838
839 static int throw_support(long spk)
840 {
841     RSPEAK(spk);
842     DROP(AXE,game.loc);
843     K=NUL;
844     return(8);
845 }
846
847 static int throw(FILE *cmdin, long verb, long obj)
848 /*  Throw.  Same as discard unless axe.  Then same as attack except
849  *  ignore bird, and if dwarf is present then one might be killed.
850  *  (Only way to do so!)  Axe also special for dragon, bear, and
851  *  troll.  Treasures special for troll. */
852 {
853     if (TOTING(ROD2) && obj == ROD && !TOTING(ROD))obj=ROD2;
854     if (!TOTING(obj))
855         {RSPEAK(SPK); return 2012;}
856     if (obj >= 50 && obj <= MAXTRS && AT(TROLL)) {
857         SPK=159;
858         /*  Snarf a treasure for the troll. */
859         DROP(obj,0);
860         MOVE(TROLL,0);
861         MOVE(TROLL+NOBJECTS,0);
862         DROP(TROLL2,PLAC[TROLL]);
863         DROP(TROLL2+NOBJECTS,FIXD[TROLL]);
864         JUGGLE(CHASM);
865         RSPEAK(SPK);
866         return 2012;
867     }
868     if (obj == FOOD && HERE(BEAR)) {
869     /* But throwing food is another story. */
870         obj=BEAR;
871         return(feed(obj));
872     }
873     if (obj != AXE)
874         return(discard(obj, false));
875     int i=ATDWRF(game.loc);
876     if (i <= 0) {
877         if (AT(DRAGON) && game.prop[DRAGON] == 0) {
878             SPK=152;
879             return throw_support(SPK);
880         }
881         if (AT(TROLL)) {
882             SPK=158;
883             return throw_support(SPK);
884         }
885         if (AT(OGRE)) {
886             SPK=203;
887             return throw_support(SPK);
888         }
889         if (HERE(BEAR) && game.prop[BEAR] == 0) {
890             /* This'll teach him to throw the axe at the bear! */
891             SPK=164;
892             DROP(AXE,game.loc);
893             game.fixed[AXE]= -1;
894             game.prop[AXE]=1;
895             JUGGLE(BEAR);
896             {RSPEAK(SPK); return 2012;}
897         }
898         return(attack(cmdin, verb, 0));
899     }
900
901     if (randrange(NDWARVES+1) < game.dflag) {
902         SPK=48;
903         return throw_support(SPK);
904     }
905     game.dseen[i]=false;
906     game.dloc[i]=0;
907     SPK=47;
908     ++game.dkill;
909     if (game.dkill == 1)SPK=149;
910
911     return throw_support(SPK);
912 }
913
914 static int vscore(void)
915 /* Score.  Call scoring routine but tell it to return. */
916 {
917     score(-1);
918     return 2012;
919 }
920
921 static int wake(token_t obj)
922 /* Wake.  Only use is to disturb the dwarves. */
923 {
924     if (obj != DWARF || !game.closed) {RSPEAK(SPK); return 2012;}
925     SPK=199;
926     return(18999);
927 }
928
929 static int wave(token_t obj)
930 /* Wave.  No effect unless waving rod at fissure or at bird. */
931 {
932     if ((!TOTING(obj)) && (obj != ROD || !TOTING(ROD2)))SPK=29;
933     if (obj != ROD ||
934        !TOTING(obj) ||
935        (!HERE(BIRD) && (game.closng || !AT(FISSUR)))) {
936         RSPEAK(SPK);
937         return 2012;
938     }
939     if (HERE(BIRD))SPK=206+MOD(game.prop[BIRD],2);
940     if (SPK == 206 && game.loc == game.place[STEPS] && game.prop[JADE] < 0) {
941         DROP(JADE,game.loc);
942         game.prop[JADE]=0;
943         --game.tally;
944         SPK=208;
945         RSPEAK(SPK);
946         return 2012;
947     } else {
948         if (game.closed) return(18999);
949         if (game.closng || !AT(FISSUR)) {RSPEAK(SPK); return 2012;}
950         if (HERE(BIRD))RSPEAK(SPK);
951         game.prop[FISSUR]=1-game.prop[FISSUR];
952         PSPEAK(FISSUR,2-game.prop[FISSUR]);
953         return 2012;
954     }
955 }
956
957 int action(FILE *input, enum speechpart part, long verb, long obj)
958 /*  Analyse a verb.  Remember what it was, go back for object if second word
959  *  unless verb is "say", which snarfs arbitrary second word.
960  */
961 {
962     int kk;
963
964     if (part == unknown)
965     {
966         /*  Analyse an object word.  See if the thing is here, whether
967          *  we've got a verb yet, and so on.  Object must be here
968          *  unless verb is "find" or "invent(ory)" (and no new verb
969          *  yet to be analysed).  Water and oil are also funny, since
970          *  they are never actually dropped at any location, but might
971          *  be here inside the bottle or urn or as a feature of the
972          *  location. */
973         if (HERE(obj))
974             /* FALL THROUGH */;
975         else if (obj == GRATE) {
976             if (game.loc == 1 || game.loc == 4 || game.loc == 7)
977                 obj=DPRSSN;
978             if (game.loc > 9 && game.loc < 15)
979                 obj=ENTRNC;
980             if (obj != GRATE)
981                 return(8);
982         }
983         else if (obj == DWARF && ATDWRF(game.loc) > 0)
984             /* FALL THROUGH */;
985         else if ((LIQUID() == obj && HERE(BOTTLE)) || obj == LIQLOC(game.loc))
986             /* FALL THROUGH */;
987         else if (obj == OIL && HERE(URN) && game.prop[URN] != 0) {
988             obj=URN;
989             /* FALL THROUGH */;
990         }
991         else if (obj == PLANT && AT(PLANT2) && game.prop[PLANT2] != 0) {
992             obj=PLANT2;
993             /* FALL THROUGH */;
994         }
995         else if (obj == KNIFE && game.knfloc == game.loc) {
996             game.knfloc= -1;
997             SPK=116;
998             {RSPEAK(SPK); return 2012;}
999         }
1000         else if (obj == ROD && HERE(ROD2)) {
1001             obj=ROD2;
1002             /* FALL THROUGH */;
1003         }
1004         else if ((verb == FIND || verb == INVENT) && WD2 <= 0)
1005             /* FALL THROUGH */;
1006         else {
1007             SETPRM(1,WD1,WD1X);
1008             RSPEAK(256);
1009             return(2012);
1010         }
1011
1012         if (WD2 > 0)
1013             return(2800);
1014         if (verb != 0)
1015             part = transitive;
1016     }
1017
1018     switch(part)
1019     {
1020         case intransitive:
1021             SPK=ACTSPK[verb];
1022             if (WD2 > 0 && verb != SAY) return(2800);
1023             if (verb == SAY)obj=WD2;
1024             if (obj == 0) {
1025                 /*  Analyse an intransitive verb (ie, no object given yet). */
1026                 switch (verb-1) {
1027                     case  0: /* CARRY */ return carry(INTRANSITIVE);
1028                     case  1: /* DROP  */ return(8000); 
1029                     case  2: /* SAY   */ return(8000); 
1030                     case  3: /* UNLOC */ return lock(verb, INTRANSITIVE);    
1031                     case  4: /* NOTHI */ return(2009); 
1032                     case  5: /* LOCK  */ return lock(verb, INTRANSITIVE);    
1033                     case  6: /* LIGHT */ return light(INTRANSITIVE);    
1034                     case  7: /* EXTIN */ return extinguish(INTRANSITIVE);    
1035                     case  8: /* WAVE  */ return(8000); 
1036                     case  9: /* CALM  */ return(8000); 
1037                     case 10: /* WALK  */ {RSPEAK(SPK); return 2012;} 
1038                     case 11: /* ATTAC */ return attack(input, verb, obj);   
1039                     case 12: /* POUR  */ return pour(obj);   
1040                     case 13: /* EAT   */ return eat(INTRANSITIVE);   
1041                     case 14: /* DRINK */ return drink(obj);   
1042                     case 15: /* RUB   */ return(8000); 
1043                     case 16: /* TOSS  */ return(8000); 
1044                     case 17: /* QUIT  */ return quit(input);   
1045                     case 18: /* FIND  */ return(8000); 
1046                     case 19: /* INVEN */ return inven(obj);   
1047                     case 20: /* FEED  */ return(8000); 
1048                     case 21: /* FILL  */ return fill(obj);   
1049                     case 22: /* BLAST */ return blast();   
1050                     case 23: /* SCOR  */ return vscore();   
1051                     case 24: /* FOO   */ return bigwords(WD1);   
1052                     case 25: /* BRIEF */ return brief();   
1053                     case 26: /* READ  */ return read(input, INTRANSITIVE);   
1054                     case 27: /* BREAK */ return(8000); 
1055                     case 28: /* WAKE  */ return(8000); 
1056                     case 29: /* SUSP  */ return saveresume(input, false);   
1057                     case 30: /* RESU  */ return saveresume(input, true);   
1058                     case 31: /* FLY   */ return fly(INTRANSITIVE);   
1059                     case 32: /* LISTE */ return listen();   
1060                     case 33: /* ZZZZ  */ return reservoir();   
1061                 }
1062                 BUG(23);
1063             }
1064             /* FALLTHRU */
1065         case transitive:
1066             /*  Analyse a transitive verb. */
1067             switch (verb-1) {
1068                 case  0: /* CARRY */ return carry(obj);    
1069                 case  1: /* DROP  */ return discard(obj, false);    
1070                 case  2: /* SAY   */ return say();    
1071                 case  3: /* UNLOC */ return lock(verb, obj);    
1072                 case  4: /* NOTHI */ return(2009); 
1073                 case  5: /* LOCK  */ return lock(verb, obj);    
1074                 case  6: /* LIGHT */ return light(obj);    
1075                 case  7: /* EXTI  */ return extinguish(obj);    
1076                 case  8: /* WAVE  */ return wave(obj);    
1077                 case  9: /* CALM  */ {RSPEAK(SPK); return 2012;} 
1078                 case 10: /* WALK  */ {RSPEAK(SPK); return 2012;} 
1079                 case 11: /* ATTAC */ return attack(input, verb, obj);   
1080                 case 12: /* POUR  */ return pour(obj);   
1081                 case 13: /* EAT   */ return eat(obj);   
1082                 case 14: /* DRINK */ return drink(obj);   
1083                 case 15: /* RUB   */ return rub(obj);   
1084                 case 16: /* TOSS  */ return throw(input, verb, obj);   
1085                 case 17: /* QUIT  */ {RSPEAK(SPK); return 2012;} 
1086                 case 18: /* FIND  */ return find(obj);   
1087                 case 19: /* INVEN */ return find(obj);   
1088                 case 20: /* FEED  */ return feed(obj);   
1089                 case 21: /* FILL  */ return fill(obj);   
1090                 case 22: /* BLAST */ return blast();   
1091                 case 23: /* SCOR  */ {RSPEAK(SPK); return 2012;} 
1092                 case 24: /* FOO   */ {RSPEAK(SPK); return 2012;} 
1093                 case 25: /* BRIEF */ {RSPEAK(SPK); return 2012;} 
1094                 case 26: /* READ  */ return read(input, obj);   
1095                 case 27: /* BREAK */ return vbreak(obj);   
1096                 case 28: /* WAKE  */ return wake(obj);   
1097                 case 29: /* SUSP  */ {RSPEAK(SPK); return 2012;} 
1098                 case 30: /* RESU  */ {RSPEAK(SPK); return 2012;} 
1099                 case 31: /* FLY   */ return fly(obj);   
1100                 case 32: /* LISTE */ {RSPEAK(SPK); return 2012;} 
1101                 case 33: /* ZZZZ  */ return reservoir();   
1102             }
1103             BUG(24);
1104         case unknown:
1105             /* Unknown verb, couldn't deduce object - might need hint */
1106             SETPRM(1,WD1,WD1X);
1107             RSPEAK(255);
1108             return(2600);
1109     default:
1110             BUG(99);
1111     }
1112 }