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