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