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