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