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