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