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