1 /*        $NetBSD: hack.eat.c,v 1.13 2019/02/04 03:33:15 mrg Exp $    */
2 
3 /*
4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5  * Amsterdam
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * - Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * - Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * - Neither the name of the Stichting Centrum voor Wiskunde en
20  * Informatica, nor the names of its contributors may be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. The name of the author may not be used to endorse or promote products
50  *    derived from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __RCSID("$NetBSD: hack.eat.c,v 1.13 2019/02/04 03:33:15 mrg Exp $");
67 #endif                                  /* not lint */
68 
69 #include "hack.h"
70 #include "extern.h"
71 static char POISONOUS[] = "ADKSVabhks";
72 
73 /* hunger texts used on bottom line (each 8 chars long) */
74 #define   SATIATED  0
75 #define NOT_HUNGRY  1
76 #define   HUNGRY              2
77 #define   WEAK                3
78 #define   FAINTING  4
79 #define FAINTED               5
80 #define STARVED               6
81 
82 const char           *const hu_stat[] = {
83           "Satiated",
84           "        ",
85           "Hungry  ",
86           "Weak    ",
87           "Fainting",
88           "Fainted ",
89           "Starved "
90 };
91 
92 static int opentin(void);
93 static int Meatdone(void);
94 static int unfaint(void);
95 static void newuhs(boolean);
96 static int eatcorpse(struct obj *);
97 
98 void
init_uhunger(void)99 init_uhunger(void)
100 {
101           u.uhunger = 900;
102           u.uhs = NOT_HUNGRY;
103 }
104 
105 #define   TTSZ      SIZE(tintxts)
106 static const struct {
107           const char           *txt;
108           int             nut;
109 }               tintxts[] = {
110           { "It contains first quality peaches - what a surprise!", 40 },
111           { "It contains salmon - not bad!", 60 },
112           { "It contains apple juice - perhaps not what you hoped for.", 20 },
113           { "It contains some nondescript substance, tasting awfully.", 500 },
114           { "It contains rotten meat. You vomit.", -50 },
115           { "It turns out to be empty.", 0 }
116 };
117 
118 static struct {
119           struct obj     *tin;
120           int             usedtime, reqtime;
121 }               tin;
122 
123 static int
opentin(void)124 opentin(void)
125 {
126           int             r;
127 
128           if (!carried(tin.tin))        /* perhaps it was stolen? */
129                     return (0);         /* %% probably we should use tinoid */
130           if (tin.usedtime++ >= 50) {
131                     pline("You give up your attempt to open the tin.");
132                     return (0);
133           }
134           if (tin.usedtime < tin.reqtime)
135                     return (1);         /* still busy */
136 
137           pline("You succeed in opening the tin.");
138           useup(tin.tin);
139           r = rn2(2 * TTSZ);
140           if (r < TTSZ) {
141                     pline("%s", tintxts[r].txt);
142                     lesshungry(tintxts[r].nut);
143                     if (r == 1) {       /* SALMON */
144                               Glib = rnd(15);
145                               pline("Eating salmon made your fingers very slippery.");
146                     }
147           } else {
148                     pline("It contains spinach - this makes you feel like Popeye!");
149                     lesshungry(600);
150                     if (u.ustr < 118)
151                               u.ustr += rnd(((u.ustr < 17) ? 19 : 118) - u.ustr);
152                     if (u.ustr > u.ustrmax)
153                               u.ustrmax = u.ustr;
154                     flags.botl = 1;
155           }
156           return (0);
157 }
158 
159 static int
Meatdone(void)160 Meatdone(void)
161 {
162           u.usym = '@';
163           prme();
164           return 0;
165 }
166 
167 int
doeat(void)168 doeat(void)
169 {
170           struct obj     *otmp;
171           struct objclass *ftmp;
172           int tmp;
173 
174           /* Is there some food (probably a heavy corpse) here on the ground? */
175           if (!Levitation)
176                     for (otmp = fobj; otmp; otmp = otmp->nobj) {
177                               if (otmp->ox == u.ux && otmp->oy == u.uy &&
178                                   otmp->olet == FOOD_SYM) {
179                                         pline("There %s %s here; eat %s? [ny] ",
180                                               (otmp->quan == 1) ? "is" : "are",
181                                               doname(otmp),
182                                               (otmp->quan == 1) ? "it" : "one");
183                                         if (readchar() == 'y') {
184                                                   if (otmp->quan != 1)
185                                                             (void) splitobj(otmp, 1);
186                                                   freeobj(otmp);
187                                                   otmp = addinv(otmp);
188                                                   addtobill(otmp);
189                                                   goto gotit;
190                                         }
191                               }
192                     }
193           otmp = getobj("%", "eat");
194           if (!otmp)
195                     return (0);
196 gotit:
197           if (otmp->otyp == TIN) {
198                     if (uwep) {
199                               switch (uwep->otyp) {
200                               case CAN_OPENER:
201                                         tmp = 1;
202                                         break;
203                               case DAGGER:
204                               case CRYSKNIFE:
205                                         tmp = 3;
206                                         break;
207                               case PICK_AXE:
208                               case AXE:
209                                         tmp = 6;
210                                         break;
211                               default:
212                                         goto no_opener;
213                               }
214                               pline("Using your %s you try to open the tin.",
215                                     aobjnam(uwep, NULL));
216                     } else {
217           no_opener:
218                               pline("It is not so easy to open this tin.");
219                               if (Glib) {
220                                         pline("The tin slips out of your hands.");
221                                         if (otmp->quan > 1) {
222                                                   struct obj     *obj;
223 
224                                                   obj = splitobj(otmp, 1);
225                                                   if (otmp == uwep)
226                                                             setuwep(obj);
227                                         }
228                                         dropx(otmp);
229                                         return (1);
230                               }
231                               tmp = 10 + rn2(1 + 500 / ((int) (u.ulevel + u.ustr)));
232                     }
233                     tin.reqtime = tmp;
234                     tin.usedtime = 0;
235                     tin.tin = otmp;
236                     occupation = opentin;
237                     occtxt = "opening the tin";
238                     return (1);
239           }
240           ftmp = &objects[otmp->otyp];
241           multi = -ftmp->oc_delay;
242           if (otmp->otyp >= CORPSE && eatcorpse(otmp))
243                     goto eatx;
244           if (!rn2(7) && otmp->otyp != FORTUNE_COOKIE) {
245                     pline("Blecch!  Rotten food!");
246                     if (!rn2(4)) {
247                               pline("You feel rather light headed.");
248                               Confusion += d(2, 4);
249                     } else if (!rn2(4) && !Blind) {
250                               pline("Everything suddenly goes dark.");
251                               Blind = d(2, 10);
252                               seeoff(0);
253                     } else if (!rn2(3)) {
254                               if (Blind)
255                                         pline("The world spins and you slap against the floor.");
256                               else
257                                         pline("The world spins and goes dark.");
258                               nomul(-rnd(10));
259                               nomovemsg = "You are conscious again.";
260                     }
261                     lesshungry(ftmp->nutrition / 4);
262           } else {
263                     if (u.uhunger >= 1500) {
264                               pline("You choke over your food.");
265                               pline("You die...");
266                               killer = ftmp->oc_name;
267                               done("choked");
268                     }
269                     switch (otmp->otyp) {
270                     case FOOD_RATION:
271                               if (u.uhunger <= 200)
272                                         pline("That food really hit the spot!");
273                               else if (u.uhunger <= 700)
274                                         pline("That satiated your stomach!");
275                               else {
276                                         pline("You're having a hard time getting all that food down.");
277                                         multi -= 2;
278                               }
279                               lesshungry(ftmp->nutrition);
280                               if (multi < 0)
281                                         nomovemsg = "You finished your meal.";
282                               break;
283                     case TRIPE_RATION:
284                               pline("Yak - dog food!");
285                               more_experienced(1, 0);
286                               flags.botl = 1;
287                               if (rn2(2)) {
288                                         pline("You vomit.");
289                                         morehungry(20);
290                                         if (Sick) {
291                                                   Sick = 0; /* David Neves */
292                                                   pline("What a relief!");
293                                         }
294                               } else
295                                         lesshungry(ftmp->nutrition);
296                               break;
297                     default:
298                               if (otmp->otyp >= CORPSE)
299                                         pline("That %s tasted terrible!", ftmp->oc_name);
300                               else
301                                         pline("That %s was delicious!", ftmp->oc_name);
302                               lesshungry(ftmp->nutrition);
303                               if (otmp->otyp == DEAD_LIZARD && (Confusion > 2))
304                                         Confusion = 2;
305                               else
306 #ifdef QUEST
307                               if (otmp->otyp == CARROT && !Blind) {
308                                         u.uhorizon++;
309                                         setsee();
310                                         pline("Your vision improves.");
311                               } else
312 #endif    /* QUEST */
313                               if (otmp->otyp == FORTUNE_COOKIE) {
314                                         if (Blind) {
315                                                   pline("This cookie has a scrap of paper inside!");
316                                                   pline("What a pity, that you cannot read it!");
317                                         } else
318                                                   outrumor();
319                               } else if (otmp->otyp == LUMP_OF_ROYAL_JELLY) {
320                                         /* This stuff seems to be VERY healthy! */
321                                         if (u.ustrmax < 118)
322                                                   u.ustrmax++;
323                                         if (u.ustr < u.ustrmax)
324                                                   u.ustr++;
325                                         u.uhp += rnd(20);
326                                         if (u.uhp > u.uhpmax) {
327                                                   if (!rn2(17))
328                                                             u.uhpmax++;
329                                                   u.uhp = u.uhpmax;
330                                         }
331                                         heal_legs();
332                               }
333                               break;
334                     }
335           }
336 eatx:
337           if (multi < 0 && !nomovemsg) {
338                     static char     msgbuf[BUFSZ];
339                     (void) snprintf(msgbuf, sizeof(msgbuf),
340                                      "You finished eating the %s.",
341                                      ftmp->oc_name);
342                     nomovemsg = msgbuf;
343           }
344           useup(otmp);
345           return (1);
346 }
347 
348 /* called in hack.main.c */
349 void
gethungry(void)350 gethungry(void)
351 {
352           --u.uhunger;
353           if (moves % 2) {
354                     if (Regeneration)
355                               u.uhunger--;
356                     if (Hunger)
357                               u.uhunger--;
358                     /*
359                      * a3:  if(Hunger & LEFT_RING) u.uhunger--; if(Hunger &
360                      * RIGHT_RING) u.uhunger--; etc.
361                      */
362           }
363           if (moves % 20 == 0) {        /* jimt@asgb */
364                     if (uleft)
365                               u.uhunger--;
366                     if (uright)
367                               u.uhunger--;
368           }
369           newuhs(TRUE);
370 }
371 
372 /* called after vomiting and after performing feats of magic */
373 void
morehungry(int num)374 morehungry(int num)
375 {
376           u.uhunger -= num;
377           newuhs(TRUE);
378 }
379 
380 /* called after eating something (and after drinking fruit juice) */
381 void
lesshungry(int num)382 lesshungry(int num)
383 {
384           u.uhunger += num;
385           newuhs(FALSE);
386 }
387 
388 static int
unfaint(void)389 unfaint(void)
390 {
391           u.uhs = FAINTING;
392           flags.botl = 1;
393           return 0;
394 }
395 
396 static void
newuhs(boolean incr)397 newuhs(boolean incr)
398 {
399           int             newhs, h = u.uhunger;
400 
401           newhs = (h > 1000) ? SATIATED :
402                     (h > 150) ? NOT_HUNGRY :
403                     (h > 50) ? HUNGRY :
404                     (h > 0) ? WEAK : FAINTING;
405 
406           if (newhs == FAINTING) {
407                     if (u.uhs == FAINTED)
408                               newhs = FAINTED;
409                     if (u.uhs <= WEAK || rn2(20 - u.uhunger / 10) >= 19) {
410                               if (u.uhs != FAINTED && multi >= 0 /* %% */ ) {
411                                         pline("You faint from lack of food.");
412                                         nomul(-10 + (u.uhunger / 10));
413                                         nomovemsg = "You regain consciousness.";
414                                         afternmv = unfaint;
415                                         newhs = FAINTED;
416                               }
417                     } else if (u.uhunger < -(int) (200 + 25 * u.ulevel)) {
418                               u.uhs = STARVED;
419                               flags.botl = 1;
420                               bot();
421                               pline("You die from starvation.");
422                               done("starved");
423                     }
424           }
425           if (newhs != u.uhs) {
426                     if (newhs >= WEAK && u.uhs < WEAK)
427                               losestr(1);         /* this may kill you -- see below */
428                     else if (newhs < WEAK && u.uhs >= WEAK && u.ustr < u.ustrmax)
429                               losestr(-1);
430                     switch (newhs) {
431                     case HUNGRY:
432                               pline((!incr) ? "You only feel hungry now." :
433                                     (u.uhunger < 145) ? "You feel hungry." :
434                                     "You are beginning to feel hungry.");
435                               break;
436                     case WEAK:
437                               pline((!incr) ? "You feel weak now." :
438                                     (u.uhunger < 45) ? "You feel weak." :
439                                     "You are beginning to feel weak.");
440                               break;
441                     }
442                     u.uhs = newhs;
443                     flags.botl = 1;
444                     if (u.uhp < 1) {
445                               pline("You die from hunger and exhaustion.");
446                               killer = "exhaustion";
447                               done("starved");
448                     }
449           }
450 }
451 
452 #define   CORPSE_I_TO_C(otyp) (char) ((otyp >= DEAD_ACID_BLOB)\
453                          ?  'a' + (otyp - DEAD_ACID_BLOB)\
454                          :    '@' + (otyp - DEAD_HUMAN))
455 int
poisonous(struct obj * otmp)456 poisonous(struct obj *otmp)
457 {
458           return (strchr(POISONOUS, CORPSE_I_TO_C(otmp->otyp)) != 0);
459 }
460 
461 /* returns 1 if some text was printed */
462 static int
eatcorpse(struct obj * otmp)463 eatcorpse(struct obj *otmp)
464 {
465           char            let = CORPSE_I_TO_C(otmp->otyp);
466           int             tp = 0;
467           if (let != 'a' && moves > otmp->age + 50 + rn2(100)) {
468                     tp++;
469                     pline("Ulch -- that meat was tainted!");
470                     pline("You get very sick.");
471                     Sick = 10 + rn2(10);
472                     u.usick_cause = objects[otmp->otyp].oc_name;
473           } else if (strchr(POISONOUS, let) && rn2(5)) {
474                     tp++;
475                     pline("Ecch -- that must have been poisonous!");
476                     if (!Poison_resistance) {
477                               losestr(rnd(4));
478                               losehp(rnd(15), "poisonous corpse");
479                     } else
480                               pline("You don't seem affected by the poison.");
481           } else if (strchr("ELNOPQRUuxz", let) && rn2(5)) {
482                     tp++;
483                     pline("You feel sick.");
484                     losehp(rnd(8), "cadaver");
485           }
486           switch (let) {
487           case 'L':
488           case 'N':
489           case 't':
490                     Teleportation |= INTRINSIC;
491                     break;
492           case 'W':
493                     pluslvl();
494                     break;
495           case 'n':
496                     u.uhp = u.uhpmax;
497                     flags.botl = 1;
498                     /* FALLTHROUGH */
499           case '@':
500                     pline("You cannibal! You will be sorry for this!");
501                     /* not tp++; */
502                     /* FALLTHROUGH */
503           case 'd':
504                     Aggravate_monster |= INTRINSIC;
505                     break;
506           case 'I':
507                     if (!Invis) {
508                               Invis = 50 + rn2(100);
509                               if (!See_invisible)
510                                         newsym(u.ux, u.uy);
511                     } else {
512                               Invis |= INTRINSIC;
513                               See_invisible |= INTRINSIC;
514                     }
515                     /* FALLTHROUGH */
516           case 'y':
517 #ifdef QUEST
518                     u.uhorizon++;
519 #endif    /* QUEST */
520                     /* FALLTHROUGH */
521           case 'B':
522                     Confusion = 50;
523                     break;
524           case 'D':
525                     Fire_resistance |= INTRINSIC;
526                     break;
527           case 'E':
528                     Telepat |= INTRINSIC;
529                     break;
530           case 'F':
531           case 'Y':
532                     Cold_resistance |= INTRINSIC;
533                     break;
534           case 'k':
535           case 's':
536                     Poison_resistance |= INTRINSIC;
537                     break;
538           case 'c':
539                     pline("You turn to stone.");
540                     killer = "dead cockatrice";
541                     done("died");
542                     break;
543           case 'a':
544                     if (Stoned) {
545                               pline("What a pity - you just destroyed a future piece of art!");
546                               tp++;
547                               Stoned = 0;
548                     }
549                     break;
550           case 'M':
551                     pline("You cannot resist the temptation to mimic a treasure chest.");
552                     tp++;
553                     nomul(-30);
554                     afternmv = Meatdone;
555                     nomovemsg = "You now again prefer mimicking a human.";
556                     u.usym = '$';
557                     prme();
558                     break;
559           }
560           return (tp);
561 }
562