1 /* $NetBSD: hack.dog.c,v 1.6 2003/04/02 18:36:36 jsm 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.6 2003/04/02 18:36:36 jsm 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 (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 (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 > 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->data->mlevel >= mdat->mlevel + 2 || 370 mtmp2->data->mlet == 'c') 371 continue; 372 if (after) 373 return (0); /* hit only once each move */ 374 375 if (hitmm(mtmp, mtmp2) == 1 && rn2(4) && 376 mtmp2->mlstmv != moves && 377 hitmm(mtmp2, mtmp) == 2) 378 return (2); 379 return (0); 380 } 381 /* dog avoids traps */ 382 /* but perhaps we have to pass a trap in order to follow @ */ 383 if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) { 384 if (!trap->tseen && rn2(40)) 385 continue; 386 if (rn2(10)) 387 continue; 388 } 389 /* dog eschewes cursed objects */ 390 /* but likes dog food */ 391 obj = fobj; 392 while (obj) { 393 if (obj->ox != nx || obj->oy != ny) 394 goto nextobj; 395 if (obj->cursed) 396 goto nxti; 397 if (obj->olet == FOOD_SYM && 398 (otyp = dogfood(obj)) < MANFOOD && 399 (otyp < ACCFOOD || edog->hungrytime <= moves)) { 400 /* 401 * Note: our dog likes the food so much that 402 * he might eat it even when it conceals a 403 * cursed object 404 */ 405 nix = nx; 406 niy = ny; 407 chi = i; 408 eatobj: 409 edog->eattime = 410 moves + obj->quan * objects[obj->otyp].oc_delay; 411 if (edog->hungrytime < moves) 412 edog->hungrytime = moves; 413 edog->hungrytime += 414 5 * obj->quan * objects[obj->otyp].nutrition; 415 mtmp->mconf = 0; 416 if (cansee(nix, niy)) 417 pline("%s ate %s.", Monnam(mtmp), doname(obj)); 418 /* perhaps this was a reward */ 419 if (otyp != CADAVER) 420 edog->apport += 200 / (edog->dropdist + moves - edog->droptime); 421 delobj(obj); 422 goto newdogpos; 423 } 424 nextobj: 425 obj = obj->nobj; 426 } 427 428 for (j = 0; j < MTSZ && j < cnt - 1; j++) 429 if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) 430 if (rn2(4 * (cnt - j))) 431 goto nxti; 432 433 /* 434 * Some stupid C compilers cannot compute the whole 435 * expression at once. 436 */ 437 nearer = GDIST(nx, ny); 438 nearer -= GDIST(nix, niy); 439 nearer *= appr; 440 if ((nearer == 0 && !rn2(++chcnt)) || nearer < 0 || 441 (nearer > 0 && !whappr && 442 ((omx == nix && omy == niy && !rn2(3)) 443 || !rn2(12)) 444 )) { 445 nix = nx; 446 niy = ny; 447 if (nearer < 0) 448 chcnt = 0; 449 chi = i; 450 } 451 nxti: ; 452 } 453 newdogpos: 454 if (nix != omx || niy != omy) { 455 if (info[chi] & ALLOW_U) { 456 (void) hitu(mtmp, d(mdat->damn, mdat->damd) + 1); 457 return (0); 458 } 459 mtmp->mx = nix; 460 mtmp->my = niy; 461 for (j = MTSZ - 1; j > 0; j--) 462 mtmp->mtrack[j] = mtmp->mtrack[j - 1]; 463 mtmp->mtrack[0].x = omx; 464 mtmp->mtrack[0].y = omy; 465 } 466 if (mintrap(mtmp) == 2) /* he died */ 467 return (2); 468 pmon(mtmp); 469 return (1); 470 } 471 472 /* return roomnumber or -1 */ 473 int 474 inroom(x, y) 475 xchar x, 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(mtmp, obj) 491 struct monst *mtmp; 492 struct obj *obj; 493 { 494 struct monst *mtmp2; 495 496 if (flags.moonphase == FULL_MOON && night() && rn2(6)) 497 return (0); 498 499 /* If we cannot tame him, at least he's no longer afraid. */ 500 mtmp->mflee = 0; 501 mtmp->mfleetim = 0; 502 if (mtmp->mtame || mtmp->mfroz || 503 #ifndef NOWORM 504 mtmp->wormno || 505 #endif /* NOWORM */ 506 mtmp->isshk || mtmp->isgd || strchr(" &@12", mtmp->data->mlet)) 507 return (0); /* no tame long worms? */ 508 if (obj) { 509 if (dogfood(obj) >= MANFOOD) 510 return (0); 511 if (cansee(mtmp->mx, mtmp->my)) { 512 pline("%s devours the %s.", Monnam(mtmp), 513 objects[obj->otyp].oc_name); 514 } 515 obfree(obj, (struct obj *) 0); 516 } 517 mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); 518 *mtmp2 = *mtmp; 519 mtmp2->mxlth = sizeof(struct edog); 520 if (mtmp->mnamelth) 521 (void) strcpy(NAME(mtmp2), NAME(mtmp)); 522 initedog(mtmp2); 523 replmon(mtmp, mtmp2); 524 return (1); 525 } 526