Merge branch 'vendor/BZIP'
[games.git] / games / hack / hack.eat.c
1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2 /* hack.eat.c - version 1.0.3 */
3 /* $FreeBSD: src/games/hack/hack.eat.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */
4 /* $DragonFly: src/games/hack/hack.eat.c,v 1.5 2006/08/21 19:45:32 pavalos Exp $ */
5
6 #include "hack.h"
7 char POISONOUS[] = "ADKSVabhks";
8
9 static bool     opentin(void);
10 static void     Meatdone(void);
11 static void     unfaint(void);
12 static void     newuhs(bool);
13 static int      eatcorpse(struct obj *);
14
15 /* hunger texts used on bottom line (each 8 chars long) */
16 #define SATIATED        0
17 #define NOT_HUNGRY      1
18 #define HUNGRY          2
19 #define WEAK            3
20 #define FAINTING        4
21 #define FAINTED         5
22 #define STARVED         6
23
24 const char *hu_stat[] = {
25         "Satiated",
26         "        ",
27         "Hungry  ",
28         "Weak    ",
29         "Fainting",
30         "Fainted ",
31         "Starved "
32 };
33
34 void
35 init_uhunger(void)
36 {
37         u.uhunger = 900;
38         u.uhs = NOT_HUNGRY;
39 }
40
41 #define TTSZ    SIZE(tintxts)
42 struct { const char *txt; int nut; } tintxts[] = {
43         { "It contains first quality peaches - what a surprise!", 40 },
44         { "It contains salmon - not bad!", 60 },
45         { "It contains apple juice - perhaps not what you hoped for.", 20 },
46         { "It contains some nondescript substance, tasting awfully.", 500 },
47         { "It contains rotten meat. You vomit.", -50 },
48         { "It turns out to be empty.", 0 }
49 };
50
51 static struct {
52         struct obj *tin;
53         int usedtime, reqtime;
54 } tin;
55
56 static bool
57 opentin(void)
58 {
59         int r;
60
61         if (!carried(tin.tin))  /* perhaps it was stolen? */
62                 return (0);     /* %% probably we should use tinoid */
63         if (tin.usedtime++ >= 50) {
64                 pline("You give up your attempt to open the tin.");
65                 return (0);
66         }
67         if (tin.usedtime < tin.reqtime)
68                 return (1);     /* still busy */
69
70         pline("You succeed in opening the tin.");
71         useup(tin.tin);
72         r = rn2(2 * TTSZ);
73         if (r < TTSZ) {
74                 pline(tintxts[r].txt);
75                 lesshungry(tintxts[r].nut);
76                 if (r == 1) {   /* SALMON */
77                         Glib = rnd(15);
78                         pline("Eating salmon made your fingers very slippery.");
79                 }
80         } else {
81                 pline("It contains spinach - this makes you feel like Popeye!");
82                 lesshungry(600);
83                 if (u.ustr < 118)
84                         u.ustr += rnd(((u.ustr < 17) ? 19 : 118) - u.ustr);
85                 if (u.ustr > u.ustrmax)
86                         u.ustrmax = u.ustr;
87                 flags.botl = 1;
88         }
89         return (0);
90 }
91
92 static void
93 Meatdone(void)
94 {
95         u.usym = '@';
96         prme();
97 }
98
99 int
100 doeat(void)
101 {
102         struct obj *otmp;
103         struct objclass *ftmp;
104         int tmp;
105
106         /* Is there some food (probably a heavy corpse) here on the ground? */
107         if (!Levitation)
108                 for (otmp = fobj; otmp; otmp = otmp->nobj) {
109                         if (otmp->ox == u.ux && otmp->oy == u.uy &&
110                             otmp->olet == FOOD_SYM) {
111                                 pline("There %s %s here; eat %s? [ny] ",
112                                       (otmp->quan == 1) ? "is" : "are",
113                                       doname(otmp),
114                                       (otmp->quan == 1) ? "it" : "one");
115                                 if (readchar() == 'y') {
116                                         if (otmp->quan != 1)
117                                                 splitobj(otmp, 1);
118                                         freeobj(otmp);
119                                         otmp = addinv(otmp);
120                                         addtobill(otmp);
121                                         goto gotit;
122                                 }
123                         }
124                 }
125
126         otmp = getobj("%", "eat");
127         if (!otmp)
128                 return (0);
129 gotit:
130         if (otmp->otyp == TIN) {
131                 if (uwep) {
132                         switch (uwep->otyp) {
133                         case CAN_OPENER:
134                                 tmp = 1;
135                                 break;
136                         case DAGGER:
137                         case CRYSKNIFE:
138                                 tmp = 3;
139                                 break;
140                         case PICK_AXE:
141                         case AXE:
142                                 tmp = 6;
143                                 break;
144                         default:
145                                 goto no_opener;
146                         }
147                         pline("Using your %s you try to open the tin.",
148                               aobjnam(uwep, NULL));
149                 } else {
150 no_opener:
151                         pline("It is not so easy to open this tin.");
152                         if (Glib) {
153                                 pline("The tin slips out of your hands.");
154                                 if (otmp->quan > 1) {
155                                         struct obj *obj;
156
157                                         obj = splitobj(otmp, 1);
158                                         if (otmp == uwep)
159                                                 setuwep(obj);
160                                 }
161                                 dropx(otmp);
162                                 return (1);
163                         }
164                         tmp = 10 + rn2(1 + 500 / ((int)(u.ulevel + u.ustr)));
165                 }
166                 tin.reqtime = tmp;
167                 tin.usedtime = 0;
168                 tin.tin = otmp;
169                 occupation = opentin;
170                 occtxt = "opening the tin";
171                 return (1);
172         }
173         ftmp = &objects[otmp->otyp];
174         multi = -ftmp->oc_delay;
175         if (otmp->otyp >= CORPSE && eatcorpse(otmp))
176                 goto eatx;
177         if (!rn2(7) && otmp->otyp != FORTUNE_COOKIE) {
178                 pline("Blecch!  Rotten food!");
179                 if (!rn2(4)) {
180                         pline("You feel rather light headed.");
181                         Confusion += d(2, 4);
182                 } else if (!rn2(4) && !Blind) {
183                         pline("Everything suddenly goes dark.");
184                         Blind = d(2, 10);
185                         seeoff(0);
186                 } else if (!rn2(3)) {
187                         if (Blind)
188                                 pline("The world spins and you slap against the floor.");
189                         else
190                                 pline("The world spins and goes dark.");
191                         nomul(-rnd(10));
192                         nomovemsg = "You are conscious again.";
193                 }
194                 lesshungry(ftmp->nutrition / 4);
195         } else {
196                 if (u.uhunger >= 1500) {
197                         pline("You choke over your food.");
198                         pline("You die...");
199                         killer = ftmp->oc_name;
200                         done("choked");
201                 }
202                 switch (otmp->otyp) {
203                 case FOOD_RATION:
204                         if (u.uhunger <= 200)
205                                 pline("That food really hit the spot!");
206                         else if (u.uhunger <= 700)
207                                 pline("That satiated your stomach!");
208                         else {
209                                 pline("You're having a hard time getting all that food down.");
210                                 multi -= 2;
211                         }
212                         lesshungry(ftmp->nutrition);
213                         if (multi < 0)
214                                 nomovemsg = "You finished your meal.";
215                         break;
216                 case TRIPE_RATION:
217                         pline("Yak - dog food!");
218                         more_experienced(1, 0);
219                         flags.botl = 1;
220                         if (rn2(2)) {
221                                 pline("You vomit.");
222                                 morehungry(20);
223                                 if (Sick) {
224                                         Sick = 0;       /* David Neves */
225                                         pline("What a relief!");
226                                 }
227                         } else
228                                 lesshungry(ftmp->nutrition);
229                         break;
230                 default:
231                         if (otmp->otyp >= CORPSE)
232                                 pline("That %s tasted terrible!", ftmp->oc_name);
233                         else
234                                 pline("That %s was delicious!", ftmp->oc_name);
235                         lesshungry(ftmp->nutrition);
236                         if (otmp->otyp == DEAD_LIZARD && (Confusion > 2))
237                                 Confusion = 2;
238                         else
239 #ifdef QUEST
240                         if (otmp->otyp == CARROT && !Blind) {
241                                 u.uhorizon++;
242                                 setsee();
243                                 pline("Your vision improves.");
244                         } else
245 #endif /* QUEST */
246                         if (otmp->otyp == FORTUNE_COOKIE) {
247                                 if (Blind) {
248                                         pline("This cookie has a scrap of paper inside!");
249                                         pline("What a pity, that you cannot read it!");
250                                 } else
251                                         outrumor();
252                         } else if (otmp->otyp == LUMP_OF_ROYAL_JELLY) {
253                                 /* This stuff seems to be VERY healthy! */
254                                 if (u.ustrmax < 118)
255                                         u.ustrmax++;
256                                 if (u.ustr < u.ustrmax)
257                                         u.ustr++;
258                                 u.uhp += rnd(20);
259                                 if (u.uhp > u.uhpmax) {
260                                         if (!rn2(17))
261                                                 u.uhpmax++;
262                                         u.uhp = u.uhpmax;
263                                 }
264                                 heal_legs();
265                         }
266                         break;
267                 }
268         }
269 eatx:
270         if (multi < 0 && !nomovemsg) {
271                 static char msgbuf[BUFSZ];
272                 sprintf(msgbuf, "You finished eating the %s.", ftmp->oc_name);
273                 nomovemsg = msgbuf;
274         }
275         useup(otmp);
276         return (1);
277 }
278
279 /* called in hack.main.c */
280 void
281 gethungry(void)
282 {
283         --u.uhunger;
284         if (moves % 2) {
285                 if (Regeneration)
286                         u.uhunger--;
287                 if (Hunger)
288                         u.uhunger--;
289                 /* a3:  if (Hunger & LEFT_RING) u.uhunger--;
290                  *      if (Hunger & RIGHT_RING) u.uhunger--;
291                  * etc.
292                  */
293         }
294         if (moves % 20 == 0) {          /* jimt@asgb */
295                 if (uleft)
296                         u.uhunger--;
297                 if (uright)
298                         u.uhunger--;
299         }
300         newuhs(TRUE);
301 }
302
303 /* called after vomiting and after performing feats of magic */
304 void
305 morehungry(int num)
306 {
307         u.uhunger -= num;
308         newuhs(TRUE);
309 }
310
311 /* called after eating something (and after drinking fruit juice) */
312 void
313 lesshungry(int num)
314 {
315         u.uhunger += num;
316         newuhs(FALSE);
317 }
318
319 static void
320 unfaint(void)
321 {
322         u.uhs = FAINTING;
323         flags.botl = 1;
324 }
325
326 static void
327 newuhs(bool incr)
328 {
329         int newhs, h = u.uhunger;
330
331         newhs = (h > 1000) ? SATIATED :
332                 (h > 150) ? NOT_HUNGRY :
333                 (h > 50) ? HUNGRY :
334                 (h > 0) ? WEAK : FAINTING;
335
336         if (newhs == FAINTING) {
337                 if (u.uhs == FAINTED)
338                         newhs = FAINTED;
339                 if (u.uhs <= WEAK || rn2(20 - u.uhunger / 10) >= 19) {
340                         if (u.uhs != FAINTED && multi >= 0 /* %% */) {
341                                 pline("You faint from lack of food.");
342                                 nomul(-10 + (u.uhunger / 10));
343                                 nomovemsg = "You regain consciousness.";
344                                 afternmv = unfaint;
345                                 newhs = FAINTED;
346                         }
347                 } else if (u.uhunger < -(int)(200 + 25 * u.ulevel)) {
348                         u.uhs = STARVED;
349                         flags.botl = 1;
350                         bot();
351                         pline("You die from starvation.");
352                         done("starved");
353                 }
354         }
355
356         if (newhs != u.uhs) {
357                 if (newhs >= WEAK && u.uhs < WEAK)
358                         losestr(1);     /* this may kill you -- see below */
359                 else if (newhs < WEAK && u.uhs >= WEAK && u.ustr < u.ustrmax)
360                         losestr(-1);
361                 switch (newhs) {
362                 case HUNGRY:
363                         pline((!incr) ? "You only feel hungry now." :
364                               (u.uhunger < 145) ? "You feel hungry." :
365                               "You are beginning to feel hungry.");
366                         break;
367                 case WEAK:
368                         pline((!incr) ? "You feel weak now." :
369                               (u.uhunger < 45) ? "You feel weak." :
370                               "You are beginning to feel weak.");
371                         break;
372                 }
373                 u.uhs = newhs;
374                 flags.botl = 1;
375                 if (u.uhp < 1) {
376                         pline("You die from hunger and exhaustion.");
377                         killer = "exhaustion";
378                         done("starved");
379                 }
380         }
381 }
382
383 #define CORPSE_I_TO_C(otyp)     (char)((otyp >= DEAD_ACID_BLOB)\
384                                        ?  'a' + (otyp - DEAD_ACID_BLOB)\
385                                        :  '@' + (otyp - DEAD_HUMAN))
386 bool
387 poisonous(struct obj *otmp)
388 {
389         return (strchr(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0);
390 }
391
392 /* returns 1 if some text was printed */
393 static int
394 eatcorpse(struct obj *otmp)
395 {
396         char let = CORPSE_I_TO_C(otmp->otyp);
397         int tp = 0;
398
399         if (let != 'a' && moves > otmp->age + 50 + rn2(100)) {
400                 tp++;
401                 pline("Ulch -- that meat was tainted!");
402                 pline("You get very sick.");
403                 Sick = 10 + rn2(10);
404                 u.usick_cause = objects[otmp->otyp].oc_name;
405         } else if (strchr(POISONOUS, let) && rn2(5)) {
406                 tp++;
407                 pline("Ecch -- that must have been poisonous!");
408                 if (!Poison_resistance) {
409                         losestr(rnd(4));
410                         losehp(rnd(15), "poisonous corpse");
411                 } else
412                         pline("You don't seem affected by the poison.");
413         } else if (strchr("ELNOPQRUuxz", let) && rn2(5)) {
414                 tp++;
415                 pline("You feel sick.");
416                 losehp(rnd(8), "cadaver");
417         }
418         switch (let) {
419         case 'L':
420         case 'N':
421         case 't':
422                 Teleportation |= INTRINSIC;
423                 break;
424         case 'W':
425                 pluslvl();
426                 break;
427         case 'n':
428                 u.uhp = u.uhpmax;
429                 flags.botl = 1;
430         /* fall into next case */
431         case '@':
432                 pline("You cannibal! You will be sorry for this!");
433         /* not tp++; */
434         /* fall into next case */
435         case 'd':
436                 Aggravate_monster |= INTRINSIC;
437                 break;
438         case 'I':
439                 if (!Invis) {
440                         Invis = 50 + rn2(100);
441                         if (!See_invisible)
442                                 newsym(u.ux, u.uy);
443                 } else {
444                         Invis |= INTRINSIC;
445                         See_invisible |= INTRINSIC;
446                 }
447         /* fall into next case */
448         case 'y':
449 #ifdef QUEST
450                 u.uhorizon++;
451 #endif /* QUEST */
452         /* fall into next case */
453         case 'B':
454                 Confusion = 50;
455                 break;
456         case 'D':
457                 Fire_resistance |= INTRINSIC;
458                 break;
459         case 'E':
460                 Telepat |= INTRINSIC;
461                 break;
462         case 'F':
463         case 'Y':
464                 Cold_resistance |= INTRINSIC;
465                 break;
466         case 'k':
467         case 's':
468                 Poison_resistance |= INTRINSIC;
469                 break;
470         case 'c':
471                 pline("You turn to stone.");
472                 killer = "dead cockatrice";
473                 done("died");
474         /* NOTREACHED */
475         case 'a':
476                 if (Stoned) {
477                         pline("What a pity - you just destroyed a future piece of art!");
478                         tp++;
479                         Stoned = 0;
480                 }
481                 break;
482         case 'M':
483                 pline("You cannot resist the temptation to mimic a treasure chest.");
484                 tp++;
485                 nomul(-30);
486                 afternmv = Meatdone;
487                 nomovemsg = "You now again prefer mimicking a human.";
488                 u.usym = '$';
489                 prme();
490                 break;
491         }
492         return (tp);
493 }