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