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