1 /* $NetBSD: hack.dog.c,v 1.11 2009/08/12 07:28:40 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 <sys/cdefs.h> 65 #ifndef lint 66 __RCSID("$NetBSD: hack.dog.c,v 1.11 2009/08/12 07:28:40 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 /* fall into next case */ 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 struct mkroom *croom = &rooms[0]; 479 while (croom->hx >= 0) { 480 if (croom->hx >= x - 1 && croom->lx <= x + 1 && 481 croom->hy >= y - 1 && croom->ly <= y + 1) 482 return (croom - rooms); 483 croom++; 484 } 485 #endif /* QUEST */ 486 return (-1); /* not in room or on door */ 487 } 488 489 int 490 tamedog(struct monst *mtmp, struct obj *obj) 491 { 492 struct monst *mtmp2; 493 494 if (flags.moonphase == FULL_MOON && night() && rn2(6)) 495 return (0); 496 497 /* If we cannot tame him, at least he's no longer afraid. */ 498 mtmp->mflee = 0; 499 mtmp->mfleetim = 0; 500 if (mtmp->mtame || mtmp->mfroz || 501 #ifndef NOWORM 502 mtmp->wormno || 503 #endif /* NOWORM */ 504 mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet)) 505 return (0); /* no tame long worms? */ 506 if (obj) { 507 if (dogfood(obj) >= MANFOOD) 508 return (0); 509 if (cansee(mtmp->mx, mtmp->my)) { 510 pline("%s devours the %s.", Monnam(mtmp), 511 objects[obj->otyp].oc_name); 512 } 513 obfree(obj, (struct obj *) 0); 514 } 515 mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); 516 *mtmp2 = *mtmp; 517 mtmp2->mxlth = sizeof(struct edog); 518 if (mtmp->mnamelth) 519 (void) strcpy(NAME(mtmp2), NAME(mtmp)); 520 initedog(mtmp2); 521 replmon(mtmp, mtmp2); 522 return (1); 523 } 524