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