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