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