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