xref: /dragonfly/games/hack/hack.dog.c (revision 479ab7f0)
1 /*	$NetBSD: hack.dog.c,v 1.12 2011/08/07 06:03:45 dholland 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 #include "hack.mfndpos.h"
67 #include "def.edog.h"
68 #include "def.mkroom.h"
69 
70 const struct permonst li_dog =
71 {"little dog", 'd', 2, 18, 6, 1, 6, sizeof(struct edog)};
72 const struct permonst dog =
73 {"dog", 'd', 4, 16, 5, 1, 6, sizeof(struct edog)};
74 const struct permonst la_dog =
75 {"large dog", 'd', 6, 15, 4, 2, 4, sizeof(struct edog)};
76 
77 static void initedog(struct monst *);
78 static int dogfood(struct obj *);
79 
80 void
81 makedog(void)
82 {
83 	struct monst   *mtmp = makemon(&li_dog, u.ux, u.uy);
84 	if (!mtmp)
85 		return;		/* dogs were genocided */
86 	initedog(mtmp);
87 }
88 
89 static void
90 initedog(struct monst *mtmp)
91 {
92 	mtmp->mtame = mtmp->mpeaceful = 1;
93 	EDOG(mtmp)->hungrytime = 1000 + moves;
94 	EDOG(mtmp)->eattime = 0;
95 	EDOG(mtmp)->droptime = 0;
96 	EDOG(mtmp)->dropdist = 10000;
97 	EDOG(mtmp)->apport = 10;
98 	EDOG(mtmp)->whistletime = 0;
99 }
100 
101 /* attach the monsters that went down (or up) together with @ */
102 struct monst   *mydogs = 0;
103 struct monst   *fallen_down = 0;/* monsters that fell through a trapdoor */
104 /* they will appear on the next level @ goes to, even if he goes up! */
105 
106 void
107 losedogs(void)
108 {
109 	struct monst   *mtmp;
110 	while ((mtmp = mydogs) != NULL) {
111 		mydogs = mtmp->nmon;
112 		mtmp->nmon = fmon;
113 		fmon = mtmp;
114 		mnexto(mtmp);
115 	}
116 	while ((mtmp = fallen_down) != NULL) {
117 		fallen_down = mtmp->nmon;
118 		mtmp->nmon = fmon;
119 		fmon = mtmp;
120 		rloc(mtmp);
121 	}
122 }
123 
124 void
125 keepdogs(void)
126 {
127 	struct monst   *mtmp;
128 	for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
129 		if (dist(mtmp->mx, mtmp->my) < 3 && follower(mtmp)
130 		    && !mtmp->msleep && !mtmp->mfroz) {
131 			relmon(mtmp);
132 			mtmp->nmon = mydogs;
133 			mydogs = mtmp;
134 			unpmon(mtmp);
135 			keepdogs();	/* we destroyed the link, so use
136 					 * recursion */
137 			return;	/* (admittedly somewhat primitive) */
138 		}
139 }
140 
141 void
142 fall_down(struct monst *mtmp)
143 {
144 	relmon(mtmp);
145 	mtmp->nmon = fallen_down;
146 	fallen_down = mtmp;
147 	unpmon(mtmp);
148 	mtmp->mtame = 0;
149 }
150 
151 /* return quality of food; the lower the better */
152 #define	DOGFOOD	0
153 #define	CADAVER	1
154 #define	ACCFOOD	2
155 #define	MANFOOD	3
156 #define	APPORT	4
157 #define	POISON	5
158 #define	UNDEF	6
159 static int
160 dogfood(struct obj *obj)
161 {
162 	switch (obj->olet) {
163 	case FOOD_SYM:
164 		return (
165 			(obj->otyp == TRIPE_RATION) ? DOGFOOD :
166 			(obj->otyp < CARROT) ? ACCFOOD :
167 			(obj->otyp < CORPSE) ? MANFOOD :
168 			(poisonous(obj) || obj->age + 50 <= moves ||
169 			 obj->otyp == DEAD_COCKATRICE)
170 			? POISON : CADAVER
171 			);
172 	default:
173 		if (!obj->cursed)
174 			return (APPORT);
175 		/* FALLTHROUGH */
176 	case BALL_SYM:
177 	case CHAIN_SYM:
178 	case ROCK_SYM:
179 		return (UNDEF);
180 	}
181 }
182 
183 /* return 0 (no move), 1 (move) or 2 (dead) */
184 int
185 dog_move(struct monst *mtmp, int after)
186 {
187 	int             nx, ny, omx, omy, appr, nearer, j;
188 	int             udist, chi = 0, i, whappr;
189 	struct monst   *mtmp2;
190 	const struct permonst *mdat = mtmp->data;
191 	struct edog    *edog = EDOG(mtmp);
192 	struct obj     *obj;
193 	struct trap    *trap;
194 	xchar           cnt, chcnt, nix, niy;
195 	schar           dogroom, uroom;
196 	xchar           gx = 0, gy = 0, gtyp, otyp;	/* current goal */
197 	coord           poss[9];
198 	int             info[9];
199 #define GDIST(x,y) ((x-gx)*(x-gx) + (y-gy)*(y-gy))
200 #define DDIST(x,y) ((x-omx)*(x-omx) + (y-omy)*(y-omy))
201 
202 	if (moves <= edog->eattime)
203 		return (0);	/* dog is still eating */
204 	omx = mtmp->mx;
205 	omy = mtmp->my;
206 	whappr = (moves - EDOG(mtmp)->whistletime < 5);
207 	if (moves > edog->hungrytime + 500 && !mtmp->mconf) {
208 		mtmp->mconf = 1;
209 		mtmp->mhpmax /= 3;
210 		if (mtmp->mhp > mtmp->mhpmax)
211 			mtmp->mhp = mtmp->mhpmax;
212 		if (cansee(omx, omy))
213 			pline("%s is confused from hunger.", Monnam(mtmp));
214 		else
215 			pline("You feel worried about %s.", monnam(mtmp));
216 	} else if (moves > edog->hungrytime + 750 || mtmp->mhp < 1) {
217 		if (cansee(omx, omy))
218 			pline("%s dies from hunger.", Monnam(mtmp));
219 		else
220 			pline("You have a sad feeling for a moment, then it passes.");
221 		mondied(mtmp);
222 		return (2);
223 	}
224 	dogroom = inroom(omx, omy);
225 	uroom = inroom(u.ux, u.uy);
226 	udist = dist(omx, omy);
227 
228 	/* maybe we tamed him while being swallowed --jgm */
229 	if (!udist)
230 		return (0);
231 
232 	/* if we are carrying sth then we drop it (perhaps near @) */
233 	/* Note: if apport == 1 then our behaviour is independent of udist */
234 	if (mtmp->minvent) {
235 		if (!rn2(udist) || !rn2((int) edog->apport))
236 			if ((unsigned) rn2(10) < edog->apport) {
237 				relobj(mtmp, (int) mtmp->minvis);
238 				if (edog->apport > 1)
239 					edog->apport--;
240 				edog->dropdist = udist;	/* hpscdi!jon */
241 				edog->droptime = moves;
242 			}
243 	} else {
244 		if ((obj = o_at(omx, omy)) != NULL)
245 			if (!strchr("0_", obj->olet)) {
246 				if ((otyp = dogfood(obj)) <= CADAVER) {
247 					nix = omx;
248 					niy = omy;
249 					goto eatobj;
250 				}
251 				if (obj->owt < 10 * mtmp->data->mlevel)
252 					if ((unsigned) rn2(20) < edog->apport + 3)
253 						if (rn2(udist) || !rn2((int) edog->apport)) {
254 							freeobj(obj);
255 							unpobj(obj);
256 							/*
257 							 * if(levl[omx][omy].s
258 							 * crsym ==
259 							 * obj->olet)
260 							 * newsym(omx,omy);
261 							 */
262 							mpickobj(mtmp, obj);
263 						}
264 			}
265 	}
266 
267 	/* first we look for food */
268 	gtyp = UNDEF;		/* no goal as yet */
269 #ifdef LINT
270 	gx = gy = 0;		/* suppress 'used before set' message */
271 #endif	/* LINT */
272 	for (obj = fobj; obj; obj = obj->nobj) {
273 		otyp = dogfood(obj);
274 		if (otyp > gtyp || otyp == UNDEF)
275 			continue;
276 		if (inroom(obj->ox, obj->oy) != dogroom)
277 			continue;
278 		if (otyp < MANFOOD &&
279 		    (dogroom >= 0 || DDIST(obj->ox, obj->oy) < 10)) {
280 			if (otyp < gtyp || (otyp == gtyp &&
281 				 DDIST(obj->ox, obj->oy) < DDIST(gx, gy))) {
282 				gx = obj->ox;
283 				gy = obj->oy;
284 				gtyp = otyp;
285 			}
286 		} else if (gtyp == UNDEF && dogroom >= 0 &&
287 			   uroom == dogroom &&
288 			   !mtmp->minvent && edog->apport > (unsigned)rn2(8)) {
289 			gx = obj->ox;
290 			gy = obj->oy;
291 			gtyp = APPORT;
292 		}
293 	}
294 	if (gtyp == UNDEF ||
295 	  (gtyp != DOGFOOD && gtyp != APPORT && moves < edog->hungrytime)) {
296 		if (dogroom < 0 || dogroom == uroom) {
297 			gx = u.ux;
298 			gy = u.uy;
299 #ifndef QUEST
300 		} else {
301 			int             tmp = rooms[dogroom].fdoor;
302 			cnt = rooms[dogroom].doorct;
303 
304 			gx = gy = FAR;	/* random, far away */
305 			while (cnt--) {
306 				if (dist(gx, gy) >
307 				    dist(doors[tmp].x, doors[tmp].y)) {
308 					gx = doors[tmp].x;
309 					gy = doors[tmp].y;
310 				}
311 				tmp++;
312 			}
313 			/* here gx == FAR e.g. when dog is in a vault */
314 			if (gx == FAR || (gx == omx && gy == omy)) {
315 				gx = u.ux;
316 				gy = u.uy;
317 			}
318 #endif	/* QUEST */
319 		}
320 		appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
321 		if (after && udist <= 4 && gx == u.ux && gy == u.uy)
322 			return (0);
323 		if (udist > 1) {
324 			if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
325 			    whappr ||
326 			    (mtmp->minvent && rn2((int) edog->apport)))
327 				appr = 1;
328 		}
329 		/* if you have dog food he'll follow you more closely */
330 		if (appr == 0) {
331 			obj = invent;
332 			while (obj) {
333 				if (obj->otyp == TRIPE_RATION) {
334 					appr = 1;
335 					break;
336 				}
337 				obj = obj->nobj;
338 			}
339 		}
340 	} else
341 		appr = 1;	/* gtyp != UNDEF */
342 	if (mtmp->mconf)
343 		appr = 0;
344 
345 	if (gx == u.ux && gy == u.uy && (dogroom != uroom || dogroom < 0)) {
346 		coord          *cp;
347 		cp = gettrack(omx, omy);
348 		if (cp) {
349 			gx = cp->x;
350 			gy = cp->y;
351 		}
352 	}
353 	nix = omx;
354 	niy = omy;
355 	cnt = mfndpos(mtmp, poss, info, ALLOW_M | ALLOW_TRAPS);
356 	chcnt = 0;
357 	chi = -1;
358 	for (i = 0; i < cnt; i++) {
359 		nx = poss[i].x;
360 		ny = poss[i].y;
361 		if (info[i] & ALLOW_M) {
362 			mtmp2 = m_at(nx, ny);
363 			if (mtmp2 == NULL)
364 				panic("error in dog_move");
365 			if (mtmp2->data->mlevel >= mdat->mlevel + 2 ||
366 			    mtmp2->data->mlet == 'c')
367 				continue;
368 			if (after)
369 				return (0);	/* hit only once each move */
370 
371 			if (hitmm(mtmp, mtmp2) == 1 && rn2(4) &&
372 			    mtmp2->mlstmv != moves &&
373 			    hitmm(mtmp2, mtmp) == 2)
374 				return (2);
375 			return (0);
376 		}
377 		/* dog avoids traps */
378 		/* but perhaps we have to pass a trap in order to follow @ */
379 		if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) {
380 			if (!trap->tseen && rn2(40))
381 				continue;
382 			if (rn2(10))
383 				continue;
384 		}
385 		/* dog eschewes cursed objects */
386 		/* but likes dog food */
387 		obj = fobj;
388 		while (obj) {
389 			if (obj->ox != nx || obj->oy != ny)
390 				goto nextobj;
391 			if (obj->cursed)
392 				goto nxti;
393 			if (obj->olet == FOOD_SYM &&
394 			    (otyp = dogfood(obj)) < MANFOOD &&
395 			    (otyp < ACCFOOD || edog->hungrytime <= moves)) {
396 				/*
397 				 * Note: our dog likes the food so much that
398 				 * he might eat it even when it conceals a
399 				 * cursed object
400 				 */
401 				nix = nx;
402 				niy = ny;
403 				chi = i;
404 		eatobj:
405 				edog->eattime =
406 					moves + obj->quan * objects[obj->otyp].oc_delay;
407 				if (edog->hungrytime < moves)
408 					edog->hungrytime = moves;
409 				edog->hungrytime +=
410 					5 * obj->quan * objects[obj->otyp].nutrition;
411 				mtmp->mconf = 0;
412 				if (cansee(nix, niy))
413 					pline("%s ate %s.", Monnam(mtmp), doname(obj));
414 				/* perhaps this was a reward */
415 				if (otyp != CADAVER)
416 					edog->apport += 200 / (edog->dropdist + moves - edog->droptime);
417 				delobj(obj);
418 				goto newdogpos;
419 			}
420 	nextobj:
421 			obj = obj->nobj;
422 		}
423 
424 		for (j = 0; j < MTSZ && j < cnt - 1; j++)
425 			if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
426 				if (rn2(4 * (cnt - j)))
427 					goto nxti;
428 
429 		/*
430 		 * Some stupid C compilers cannot compute the whole
431 		 * expression at once.
432 		 */
433 		nearer = GDIST(nx, ny);
434 		nearer -= GDIST(nix, niy);
435 		nearer *= appr;
436 		if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 ||
437 		    (nearer > 0 && !whappr &&
438 		     ((omx == nix && omy == niy && !rn2(3))
439 		      || !rn2(12))
440 		     )) {
441 			nix = nx;
442 			niy = ny;
443 			if (nearer < 0)
444 				chcnt = 0;
445 			chi = i;
446 		}
447 nxti:		;
448 	}
449 newdogpos:
450 	if (nix != omx || niy != omy) {
451 		if (info[chi] & ALLOW_U) {
452 			(void) hitu(mtmp, d(mdat->damn, mdat->damd) + 1);
453 			return (0);
454 		}
455 		mtmp->mx = nix;
456 		mtmp->my = niy;
457 		for (j = MTSZ - 1; j > 0; j--)
458 			mtmp->mtrack[j] = mtmp->mtrack[j - 1];
459 		mtmp->mtrack[0].x = omx;
460 		mtmp->mtrack[0].y = omy;
461 	}
462 	if (mintrap(mtmp) == 2)	/* he died */
463 		return (2);
464 	pmon(mtmp);
465 	return (1);
466 }
467 
468 /* return roomnumber or -1 */
469 int
470 inroom(xchar x, xchar y)
471 {
472 #ifndef QUEST
473 	int pos = 0;
474 
475 	while (rooms[pos].hx >= 0) {
476 		if (rooms[pos].hx >= x - 1 && rooms[pos].lx <= x + 1 &&
477 		    rooms[pos].hy >= y - 1 && rooms[pos].ly <= y + 1)
478 			return pos;
479 		pos++;
480 	}
481 #endif	/* QUEST */
482 	return (-1);		/* not in room or on door */
483 }
484 
485 int
486 tamedog(struct monst *mtmp, struct obj *obj)
487 {
488 	struct monst   *mtmp2;
489 
490 	if (flags.moonphase == FULL_MOON && night() && rn2(6))
491 		return (0);
492 
493 	/* If we cannot tame him, at least he's no longer afraid. */
494 	mtmp->mflee = 0;
495 	mtmp->mfleetim = 0;
496 	if (mtmp->mtame || mtmp->mfroz ||
497 #ifndef NOWORM
498 	    mtmp->wormno ||
499 #endif	/* NOWORM */
500 	    mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet))
501 		return (0);	/* no tame long worms? */
502 	if (obj) {
503 		if (dogfood(obj) >= MANFOOD)
504 			return (0);
505 		if (cansee(mtmp->mx, mtmp->my)) {
506 			pline("%s devours the %s.", Monnam(mtmp),
507 			      objects[obj->otyp].oc_name);
508 		}
509 		obfree(obj, (struct obj *) 0);
510 	}
511 	mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
512 	*mtmp2 = *mtmp;
513 	mtmp2->mxlth = sizeof(struct edog);
514 	if (mtmp->mnamelth)
515 		(void) strcpy(NAME(mtmp2), NAME(mtmp));
516 	initedog(mtmp2);
517 	replmon(mtmp, mtmp2);
518 	return (1);
519 }
520