xref: /netbsd/games/hack/hack.eat.c (revision 9a8e2de4)
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