xref: /dragonfly/games/hack/hack.eat.c (revision 37de577a)
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
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
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
155 Meatdone(void)
156 {
157 	u.usym = '@';
158 	prme();
159 	return 0;
160 }
161 
162 int
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
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
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
377 lesshungry(int num)
378 {
379 	u.uhunger += num;
380 	newuhs(FALSE);
381 }
382 
383 static int
384 unfaint(void)
385 {
386 	u.uhs = FAINTING;
387 	flags.botl = 1;
388 	return 0;
389 }
390 
391 static void
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
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
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