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