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