1 | /* SCCS Id: @(#)dog.c 3.3 1999/10/20 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* NetHack may be freely redistributed. See license for details. */ 4 | 5 | #include "hack.h" 6 | #include "edog.h" 7 | 8 | #ifdef OVLB 9 | 10 | STATIC_DCL int NDECL(pet_type); 11 | 12 | void 13 | initedog(mtmp) 14 | register struct monst *mtmp; 15 | { 16 | mtmp->mtame = is_domestic(mtmp->data) ? 10 : 5; 17 | mtmp->mpeaceful = 1; 18 | set_malign(mtmp); /* recalc alignment now that it's tamed */ 19 | mtmp->mleashed = 0; 20 | mtmp->meating = 0; 21 | EDOG(mtmp)->droptime = 0; 22 | EDOG(mtmp)->dropdist = 10000; 23 | EDOG(mtmp)->apport = 10; 24 | EDOG(mtmp)->whistletime = 0; 25 | EDOG(mtmp)->hungrytime = 1000 + monstermoves; 26 | EDOG(mtmp)->ogoal.x = -1; /* force error if used before set */ 27 | EDOG(mtmp)->ogoal.y = -1; 28 | EDOG(mtmp)->abuse = 0; 29 | EDOG(mtmp)->revivals = 0; 30 | EDOG(mtmp)->mhpmax_penalty = 0; 31 | EDOG(mtmp)->killed_by_u = 0; 32 | } 33 | 34 | STATIC_OVL int 35 | pet_type() 36 | { 37 | if (urole.petnum != NON_PM) 38 | return (urole.petnum); 39 | else if (preferred_pet == 'c') 40 | return (PM_KITTEN); 41 | else if (preferred_pet == 'd') 42 | return (PM_LITTLE_DOG); 43 | else 44 | return (rn2(2) ? PM_KITTEN : PM_LITTLE_DOG); 45 | } 46 | 47 | struct monst * 48 | make_familiar(otmp,x,y,quietly) 49 | register struct obj *otmp; 50 | xchar x, y; 51 | boolean quietly; 52 | { 53 | struct permonst *pm; 54 | struct monst *mtmp = 0; 55 | int chance, trycnt = 100; 56 | 57 | do { 58 | if (otmp) { 59 | pm = &mons[otmp->corpsenm]; /* Figurine; otherwise spell */ 60 | } else if (!rn2(3)) { 61 | pm = &mons[pet_type()]; 62 | } else { 63 | pm = rndmonst(); 64 | if (!pm) { 65 | if (!quietly) 66 | There("seems to be nothing available for a familiar."); 67 | break; 68 | } 69 | } 70 | 71 | mtmp = makemon(pm, x, y, MM_EDOG); 72 | if (otmp && !mtmp) { /* monster was genocided or square occupied */ 73 | if (!quietly) 74 | pline_The("figurine writhes and then shatters into pieces!"); 75 | break; 76 | } 77 | } while (!mtmp && --trycnt > 0); 78 | 79 | if (!mtmp) return (struct monst *)0; 80 | 81 | initedog(mtmp); 82 | mtmp->msleeping = 0; 83 | if (otmp) { /* figurine; resulting monster might not become a pet */ 84 | chance = rn2(10); /* 0==tame, 1==peaceful, 2==hostile */ 85 | if (chance > 2) chance = otmp->blessed ? 0 : !otmp->cursed ? 1 : 2; 86 | /* 0,1,2: b=80%,10,10; nc=10%,80,10; c=10%,10,80 */ 87 | if (chance > 0) { 88 | mtmp->mtame = 0; /* not tame after all */ 89 | if (chance == 2) { /* hostile (cursed figurine) */ 90 | if (!quietly) 91 | You("get a bad feeling about this."); 92 | mtmp->mpeaceful = 0; 93 | } 94 | } 95 | /* if figurine has been named, give same name to the monster */ 96 | if (otmp->onamelth) 97 | mtmp = christen_monst(mtmp, ONAME(otmp)); 98 | } 99 | set_malign(mtmp); /* more alignment changes */ 100 | newsym(mtmp->mx, mtmp->my); 101 | 102 | /* must wield weapon immediately since pets will otherwise drop it */ 103 | if (mtmp->mtame && attacktype(mtmp->data, AT_WEAP)) { 104 | mtmp->weapon_check = NEED_HTH_WEAPON; 105 | (void) mon_wield_item(mtmp); 106 | } 107 | return mtmp; 108 | } 109 | 110 | struct monst * 111 | makedog() 112 | { 113 | register struct monst *mtmp; 114 | #ifdef STEED 115 | register struct obj *otmp; 116 | #endif 117 | const char *petname; 118 | int pettype; 119 | static int petname_used = 0; 120 | 121 | pettype = pet_type(); 122 | if (pettype == PM_LITTLE_DOG) 123 | petname = dogname; 124 | else if (pettype == PM_PONY) 125 | petname = horsename; 126 | else 127 | petname = catname; 128 | 129 | /* default pet names */ 130 | if (!*petname && pettype == PM_LITTLE_DOG) { 131 | /* All of these names were for dogs. */ 132 | if(Role_if(PM_CAVEMAN)) petname = "Slasher"; /* The Warrior */ 133 | if(Role_if(PM_SAMURAI)) petname = "Hachi"; /* Shibuya Station */ 134 | if(Role_if(PM_BARBARIAN)) petname = "Idefix"; /* Obelix */ 135 | if(Role_if(PM_RANGER)) petname = "Sirius"; /* Orion's dog */ 136 | } 137 | 138 | mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG); 139 | 140 | if(!mtmp) return((struct monst *) 0); /* pets were genocided */ 141 | 142 | #ifdef STEED 143 | /* Horses already wear a saddle */ 144 | if (pettype == PM_PONY && !!(otmp = mksobj(SADDLE, TRUE, FALSE))) { 145 | if (mpickobj(mtmp, otmp)) 146 | panic("merged saddle?"); 147 | mtmp->misc_worn_check |= W_SADDLE; 148 | otmp->dknown = otmp->bknown = otmp->rknown = 1; 149 | otmp->owornmask = W_SADDLE; 150 | otmp->leashmon = mtmp->m_id; 151 | update_mon_intrinsics(mtmp, otmp, TRUE); 152 | } 153 | #endif 154 | 155 | if (!petname_used++ && *petname) 156 | mtmp = christen_monst(mtmp, petname); 157 | 158 | initedog(mtmp); 159 | return(mtmp); 160 | } 161 | 162 | /* record `last move time' for all monsters prior to level save so that 163 | mon_arrive() can catch up for lost time when they're restored later */ 164 | void 165 | update_mlstmv() 166 | { 167 | struct monst *mon; 168 | 169 | /* monst->mlstmv used to be updated every time `monst' actually moved, 170 | but that is no longer the case so we just do a blanket assignment */ 171 | for (mon = fmon; mon; mon = mon->nmon) 172 | if (!DEADMONSTER(mon)) mon->mlstmv = monstermoves; 173 | } 174 | 175 | void 176 | losedogs() 177 | { 178 | register struct monst *mtmp, *mtmp0 = 0, *mtmp2; 179 | 180 | while ((mtmp = mydogs) != 0) { 181 | mydogs = mtmp->nmon; 182 | mon_arrive(mtmp, TRUE); 183 | } 184 | 185 | for(mtmp = migrating_mons; mtmp; mtmp = mtmp2) { 186 | mtmp2 = mtmp->nmon; 187 | if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) { 188 | if(mtmp == migrating_mons) 189 | migrating_mons = mtmp->nmon; 190 | else 191 | mtmp0->nmon = mtmp->nmon; 192 | mon_arrive(mtmp, FALSE); 193 | } else 194 | mtmp0 = mtmp; 195 | } 196 | } 197 | 198 | /* called from resurrect() in addition to losedogs() */ 199 | void 200 | mon_arrive(mtmp, with_you) 201 | struct monst *mtmp; 202 | boolean with_you; 203 | { 204 | struct trap *t; 205 | xchar xlocale, ylocale, xyloc, xyflags, wander; 206 | int num_segs; 207 | 208 | mtmp->nmon = fmon; 209 | fmon = mtmp; 210 | if (mtmp->isshk) 211 | set_residency(mtmp, FALSE); 212 | 213 | num_segs = mtmp->wormno; 214 | /* baby long worms have no tail so don't use is_longworm() */ 215 | if ((mtmp->data == &mons[PM_LONG_WORM]) && 216 | #ifdef DCC30_BUG 217 | (mtmp->wormno = get_wormno(), mtmp->wormno != 0)) 218 | #else 219 | (mtmp->wormno = get_wormno()) != 0) 220 | #endif 221 | { 222 | initworm(mtmp, num_segs); 223 | /* tail segs are not yet initialized or displayed */ 224 | } else mtmp->wormno = 0; 225 | 226 | /* some monsters might need to do something special upon arrival 227 | _after_ the current level has been fully set up; see dochug() */ 228 | mtmp->mstrategy |= STRAT_ARRIVE; 229 | 230 | /* make sure mnexto(rloc_to(set_apparxy())) doesn't use stale data */ 231 | mtmp->mux = u.ux, mtmp->muy = u.uy; 232 | xyloc = mtmp->mtrack[0].x; 233 | xyflags = mtmp->mtrack[0].y; 234 | xlocale = mtmp->mtrack[1].x; 235 | ylocale = mtmp->mtrack[1].y; 236 | mtmp->mtrack[0].x = mtmp->mtrack[0].y = 0; 237 | mtmp->mtrack[1].x = mtmp->mtrack[1].y = 0; 238 | 239 | #ifdef STEED 240 | if (mtmp == u.usteed) 241 | return; /* don't place steed on the map */ 242 | #endif 243 | if (with_you) { 244 | /* When a monster accompanies you, sometimes it will arrive 245 | at your intended destination and you'll end up next to 246 | that spot. This code doesn't control the final outcome; 247 | goto_level(do.c) decides who ends up at your target spot 248 | when there is a monster there too. */ 249 | if (!MON_AT(u.ux, u.uy) && 250 | !rn2(mtmp->mtame ? 10 : mtmp->mpeaceful ? 5 : 2)) 251 | rloc_to(mtmp, u.ux, u.uy); 252 | else 253 | mnexto(mtmp); 254 | return; 255 | } 256 | /* 257 | * The monster arrived on this level independently of the player. 258 | * Its coordinate fields were overloaded for use as flags that 259 | * specify its final destination. 260 | */ 261 | 262 | if (mtmp->mlstmv < monstermoves - 1L) { 263 | /* heal monster for time spent in limbo */ 264 | long nmv = monstermoves - 1L - mtmp->mlstmv; 265 | 266 | mon_catchup_elapsed_time(mtmp, nmv); 267 | mtmp->mlstmv = monstermoves - 1L; 268 | 269 | /* let monster move a bit on new level (see placement code below) */ 270 | wander = (xchar) min(nmv, 8); 271 | } else 272 | wander = 0; 273 | 274 | switch (xyloc) { 275 | case MIGR_APPROX_XY: /* {x,y}locale set above */ 276 | break; 277 | case MIGR_EXACT_XY: wander = 0; 278 | break; 279 | case MIGR_NEAR_PLAYER: xlocale = u.ux, ylocale = u.uy; 280 | break; 281 | case MIGR_STAIRS_UP: xlocale = xupstair, ylocale = yupstair; 282 | break; 283 | case MIGR_STAIRS_DOWN: xlocale = xdnstair, ylocale = ydnstair; 284 | break; 285 | case MIGR_LADDER_UP: xlocale = xupladder, ylocale = yupladder; 286 | break; 287 | case MIGR_LADDER_DOWN: xlocale = xdnladder, ylocale = ydnladder; 288 | break; 289 | case MIGR_SSTAIRS: xlocale = sstairs.sx, ylocale = sstairs.sy; 290 | break; 291 | case MIGR_PORTAL: 292 | if (In_endgame(&u.uz)) { 293 | /* there is no arrival portal for endgame levels */ 294 | /* BUG[?]: for simplicity, this code relies on the fact 295 | that we know that the current endgame levels always 296 | build upwards and never have any exclusion subregion 297 | inside their TELEPORT_REGION settings. */ 298 | xlocale = rn1(updest.hx - updest.lx + 1, updest.lx); 299 | ylocale = rn1(updest.hy - updest.ly + 1, updest.ly); 300 | break; 301 | } 302 | /* find the arrival portal */ 303 | for (t = ftrap; t; t = t->ntrap) 304 | if (t->ttyp == MAGIC_PORTAL) break; 305 | if (t) { 306 | xlocale = t->tx, ylocale = t->ty; 307 | break; 308 | } else { 309 | impossible("mon_arrive: no corresponding portal?"); 310 | } /*FALLTHRU*/ 311 | default: 312 | case MIGR_RANDOM: xlocale = ylocale = 0; 313 | break; 314 | } 315 | 316 | if (xlocale && wander) { 317 | /* monster moved a bit; pick a nearby location */ 318 | /* mnearto() deals w/stone, et al */ 319 | char *r = in_rooms(xlocale, ylocale, 0); 320 | if (r && *r) { 321 | coord c; 322 | /* somexy() handles irregular rooms */ 323 | if (somexy(&rooms[*r - ROOMOFFSET], &c)) 324 | xlocale = c.x, ylocale = c.y; 325 | else 326 | xlocale = ylocale = 0; 327 | } else { /* not in a room */ 328 | int i, j; 329 | i = max(1, xlocale - wander); 330 | j = min(COLNO-1, xlocale + wander); 331 | xlocale = rn1(j - i, i); 332 | i = max(0, ylocale - wander); 333 | j = min(ROWNO-1, ylocale + wander); 334 | ylocale = rn1(j - i, i); 335 | } 336 | } /* moved a bit */ 337 | 338 | mtmp->mx = 0; /*(already is 0)*/ 339 | mtmp->my = xyflags; 340 | if (xlocale) 341 | (void) mnearto(mtmp, xlocale, ylocale, FALSE); 342 | else 343 | rloc(mtmp); 344 | } 345 | 346 | /* heal monster for time spent elsewhere */ 347 | void 348 | mon_catchup_elapsed_time(mtmp, nmv) 349 | struct monst *mtmp; 350 | long nmv; /* number of moves */ 351 | { 352 | int imv = 0; /* avoid zillions of casts and lint warnings */ 353 | 354 | #if defined(DEBUG) || defined(BETA) 355 | if (nmv < 0L) { /* crash likely... */ 356 | panic("catchup from future time?"); 357 | /*NOTREACHED*/ 358 | return; 359 | } else if (nmv == 0L) { /* safe, but should'nt happen */ 360 | impossible("catchup from now?"); 361 | } else 362 | #endif 363 | if (nmv >= LARGEST_INT) /* paranoia */ 364 | imv = LARGEST_INT - 1; 365 | else 366 | imv = (int)nmv; 367 | 368 | /* might stop being afraid, blind or frozen */ 369 | /* set to 1 and allow final decrement in movemon() */ 370 | if (mtmp->mblinded) { 371 | if (imv >= (int) mtmp->mblinded) mtmp->mblinded = 1; 372 | else mtmp->mblinded -= imv; 373 | } 374 | if (mtmp->mfrozen) { 375 | if (imv >= (int) mtmp->mfrozen) mtmp->mfrozen = 1; 376 | else mtmp->mfrozen -= imv; 377 | } 378 | if (mtmp->mfleetim) { 379 | if (imv >= (int) mtmp->mfleetim) mtmp->mfleetim = 1; 380 | else mtmp->mfleetim -= imv; 381 | } 382 | 383 | /* might recover from temporary trouble */ 384 | if (mtmp->mtrapped && rn2(imv + 1) > 40/2) mtmp->mtrapped = 0; 385 | if (mtmp->mconf && rn2(imv + 1) > 50/2) mtmp->mconf = 0; 386 | if (mtmp->mstun && rn2(imv + 1) > 10/2) mtmp->mstun = 0; 387 | 388 | /* might finish eating or be able to use special ability again */ 389 | if (imv > mtmp->meating) mtmp->meating = 0; 390 | else mtmp->meating -= imv; 391 | if (imv > mtmp->mspec_used) mtmp->mspec_used = 0; 392 | else mtmp->mspec_used -= imv; 393 | 394 | /* reduce tameness for every 150 moves you are separated */ 395 | if (mtmp->mtame) { 396 | int wilder = (imv + 75) / 150; 397 | if (mtmp->mtame > wilder) mtmp->mtame -= wilder; /* less tame */ 398 | else if (mtmp->mtame > rn2(wilder)) mtmp->mtame = 0; /* untame */ 399 | else mtmp->mtame = mtmp->mpeaceful = 0; /* hostile! */ 400 | } 401 | /* check to see if it would have died as a pet; if so, go wild instead 402 | * of dying the next time we call dog_move() 403 | */ 404 | if (mtmp->mtame && !mtmp->isminion && 405 | (carnivorous(mtmp->data) || herbivorous(mtmp->data))) { 406 | struct edog *edog = EDOG(mtmp); 407 | 408 | if ((monstermoves > edog->hungrytime + 500 && mtmp->mhp < 3) || 409 | (monstermoves > edog->hungrytime + 750)) 410 | mtmp->mtame = mtmp->mpeaceful = 0; 411 | } 412 | 413 | /* recover lost hit points */ 414 | if (!regenerates(mtmp->data)) imv /= 20; 415 | if (mtmp->mhp + imv >= mtmp->mhpmax) 416 | mtmp->mhp = mtmp->mhpmax; 417 | else mtmp->mhp += imv; 418 | } 419 | 420 | #endif /* OVLB */ 421 | #ifdef OVL2 422 | 423 | /* called when you move to another level */ 424 | void 425 | keepdogs(pets_only) 426 | boolean pets_only; /* true for ascension or final escape */ 427 | { 428 | register struct monst *mtmp, *mtmp2; 429 | register struct obj *obj; 430 | int num_segs; 431 | boolean stay_behind; 432 | 433 | for (mtmp = fmon; mtmp; mtmp = mtmp2) { 434 | mtmp2 = mtmp->nmon; 435 | if (DEADMONSTER(mtmp)) continue; 436 | if (pets_only && !mtmp->mtame) continue; 437 | if (((monnear(mtmp, u.ux, u.uy) && levl_follower(mtmp)) || 438 | #ifdef STEED 439 | (mtmp == u.usteed) || 440 | #endif 441 | /* the wiz will level t-port from anywhere to chase 442 | the amulet; if you don't have it, will chase you 443 | only if in range. -3. */ 444 | (u.uhave.amulet && mtmp->iswiz)) 445 | && !mtmp->msleeping && mtmp->mcanmove) { 446 | stay_behind = FALSE; 447 | if (mtmp->mtame && mtmp->meating) { 448 | if (canseemon(mtmp)) 449 | pline("%s is still eating.", Monnam(mtmp)); 450 | stay_behind = TRUE; 451 | } else if (mon_has_amulet(mtmp)) { 452 | if (canseemon(mtmp)) 453 | pline("%s seems very disoriented for a moment.", 454 | Monnam(mtmp)); 455 | stay_behind = TRUE; 456 | } 457 | if (stay_behind 458 | #ifdef STEED 459 | && mtmp != u.usteed 460 | #endif 461 | ) { 462 | if (mtmp->mleashed) { 463 | pline("%s leash suddenly comes loose.", 464 | humanoid(mtmp->data) 465 | ? (mtmp->female ? "Her" : "His") 466 | : "Its"); 467 | m_unleash(mtmp); 468 | } 469 | continue; 470 | } 471 | if (mtmp->isshk) 472 | set_residency(mtmp, TRUE); 473 | 474 | if (mtmp->wormno) { 475 | register int cnt; 476 | /* NOTE: worm is truncated to # segs = max wormno size */ 477 | cnt = count_wsegs(mtmp); 478 | num_segs = min(cnt, MAX_NUM_WORMS - 1); 479 | wormgone(mtmp); 480 | } else num_segs = 0; 481 | 482 | /* set minvent's obj->no_charge to 0 */ 483 | for(obj = mtmp->minvent; obj; obj = obj->nobj) { 484 | if (Has_contents(obj)) 485 | picked_container(obj); /* does the right thing */ 486 | obj->no_charge = 0; 487 | } 488 | 489 | relmon(mtmp); 490 | newsym(mtmp->mx,mtmp->my); 491 | mtmp->mx = mtmp->my = 0; /* avoid mnexto()/MON_AT() problem */ 492 | mtmp->wormno = num_segs; 493 | mtmp->mlstmv = monstermoves; 494 | mtmp->nmon = mydogs; 495 | mydogs = mtmp; 496 | } else if (mtmp->iswiz) { 497 | /* we want to be able to find him when his next resurrection 498 | chance comes up, but have him resume his present location 499 | if player returns to this level before that time */ 500 | migrate_to_level(mtmp, ledger_no(&u.uz), 501 | MIGR_EXACT_XY, (coord *)0); 502 | } else if (mtmp->mleashed) { 503 | /* this can happen if your quest leader ejects you from the 504 | "home" level while a leashed pet isn't next to you */ 505 | pline("%s leash goes slack.", s_suffix(Monnam(mtmp))); 506 | m_unleash(mtmp); 507 | } 508 | } 509 | } 510 | 511 | #endif /* OVL2 */ 512 | #ifdef OVLB 513 | 514 | void 515 | migrate_to_level(mtmp, tolev, xyloc, cc) 516 | register struct monst *mtmp; 517 | xchar tolev; /* destination level */ 518 | xchar xyloc; /* MIGR_xxx destination xy location: */ 519 | coord *cc; /* optional destination coordinates */ 520 | { 521 | register struct obj *obj; 522 | d_level new_lev; 523 | xchar xyflags; 524 | int num_segs = 0; /* count of worm segments */ 525 | 526 | if (mtmp->isshk) 527 | set_residency(mtmp, TRUE); 528 | 529 | if (mtmp->wormno) { 530 | register int cnt; 531 | /* **** NOTE: worm is truncated to # segs = max wormno size **** */ 532 | cnt = count_wsegs(mtmp); 533 | num_segs = min(cnt, MAX_NUM_WORMS - 1); 534 | wormgone(mtmp); 535 | } 536 | 537 | /* set minvent's obj->no_charge to 0 */ 538 | for(obj = mtmp->minvent; obj; obj = obj->nobj) { 539 | if (Has_contents(obj)) 540 | picked_container(obj); /* does the right thing */ 541 | obj->no_charge = 0; 542 | } 543 | 544 | relmon(mtmp); 545 | mtmp->nmon = migrating_mons; 546 | migrating_mons = mtmp; 547 | if (mtmp->mleashed) { 548 | m_unleash(mtmp); 549 | mtmp->mtame--; 550 | pline_The("leash comes off!"); 551 | } 552 | newsym(mtmp->mx,mtmp->my); 553 | 554 | new_lev.dnum = ledger_to_dnum((xchar)tolev); 555 | new_lev.dlevel = ledger_to_dlev((xchar)tolev); 556 | /* overload mtmp->[mx,my], mtmp->[mux,muy], and mtmp->mtrack[] as */ 557 | /* destination codes (setup flag bits before altering mx or my) */ 558 | xyflags = (depth(&new_lev) < depth(&u.uz)); /* 1 => up */ 559 | if (In_W_tower(mtmp->mx, mtmp->my, &u.uz)) xyflags |= 2; 560 | mtmp->wormno = num_segs; 561 | mtmp->mlstmv = monstermoves; 562 | mtmp->mtrack[1].x = cc ? cc->x : mtmp->mx; 563 | mtmp->mtrack[1].y = cc ? cc->y : mtmp->my; 564 | mtmp->mtrack[0].x = xyloc; 565 | mtmp->mtrack[0].y = xyflags; 566 | mtmp->mux = new_lev.dnum; 567 | mtmp->muy = new_lev.dlevel; 568 | mtmp->mx = mtmp->my = 0; /* this implies migration */ 569 | } 570 | 571 | #endif /* OVLB */ 572 | #ifdef OVL1 573 | 574 | /* return quality of food; the lower the better */ 575 | /* fungi will eat even tainted food */ 576 | int 577 | dogfood(mon,obj) 578 | struct monst *mon; 579 | register struct obj *obj; 580 | { 581 | boolean carni = carnivorous(mon->data); 582 | boolean herbi = herbivorous(mon->data); 583 | struct permonst *fptr = &mons[obj->corpsenm]; 584 | 585 | if (is_quest_artifact(obj) || obj_resists(obj, 0, 95)) 586 | return (obj->cursed ? TABU : APPORT); 587 | 588 | switch(obj->oclass) { 589 | case FOOD_CLASS: 590 | if (obj->otyp == CORPSE && 591 | ((touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) 592 | || is_rider(fptr))) 593 | return TABU; 594 | 595 | /* Ghouls only eat old corpses... yum! */ 596 | if (mon->data == &mons[PM_GHOUL]) 597 | return (obj->otyp == CORPSE && obj->age+50 <= monstermoves) ? 598 | DOGFOOD : TABU; 599 | 600 | if (!carni && !herbi) 601 | return (obj->cursed ? UNDEF : APPORT); 602 | 603 | switch (obj->otyp) { 604 | case TRIPE_RATION: 605 | case MEATBALL: 606 | case MEAT_RING: 607 | case MEAT_STICK: 608 | case HUGE_CHUNK_OF_MEAT: 609 | return (carni ? DOGFOOD : MANFOOD); 610 | case EGG: 611 | if (touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) 612 | return POISON; 613 | return (carni ? CADAVER : MANFOOD); 614 | case CORPSE: 615 | if ((peek_at_iced_corpse_age(obj)+50 <= monstermoves 616 | && obj->corpsenm != PM_LIZARD 617 | && obj->corpsenm != PM_LICHEN 618 | && mon->data->mlet != S_FUNGUS) || 619 | (acidic(&mons[obj->corpsenm]) && !resists_acid(mon)) || 620 | (poisonous(&mons[obj->corpsenm]) && 621 | !resists_poison(mon))) 622 | return POISON; 623 | else if (vegan(fptr)) 624 | return (herbi ? CADAVER : MANFOOD); 625 | else return (carni ? CADAVER : MANFOOD); 626 | case CLOVE_OF_GARLIC: 627 | return (is_undead(mon->data) ? TABU : 628 | (herbi ? ACCFOOD : MANFOOD)); 629 | case TIN: 630 | return (metallivorous(mon->data) ? ACCFOOD : MANFOOD); 631 | case APPLE: 632 | case CARROT: 633 | return (herbi ? DOGFOOD : MANFOOD); 634 | case BANANA: 635 | return ((mon->data->mlet == S_YETI) ? DOGFOOD : 636 | (herbi ? ACCFOOD : MANFOOD)); 637 | default: 638 | return (obj->otyp > SLIME_MOLD ? 639 | (carni ? ACCFOOD : MANFOOD) : 640 | (herbi ? ACCFOOD : MANFOOD)); 641 | } 642 | default: 643 | if (obj->otyp == AMULET_OF_STRANGULATION || 644 | obj->otyp == RIN_SLOW_DIGESTION) 645 | return (TABU); 646 | if (hates_silver(mon->data) && 647 | objects[obj->otyp].oc_material == SILVER) 648 | return(TABU); 649 | if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj)) 650 | return(ACCFOOD); 651 | if (metallivorous(mon->data) && is_metallic(obj)) 652 | /* Non-rustproofed ferrous based metals are preferred. */ 653 | return(objects[obj->otyp].oc_material == IRON && 654 | !obj->oerodeproof ? DOGFOOD : ACCFOOD); 655 | if(!obj->cursed && obj->oclass != BALL_CLASS && 656 | obj->oclass != CHAIN_CLASS) 657 | return(APPORT); 658 | /* fall into next case */ 659 | case ROCK_CLASS: 660 | return(UNDEF); 661 | } 662 | } 663 | 664 | #endif /* OVL1 */ 665 | #ifdef OVLB 666 | 667 | struct monst * 668 | tamedog(mtmp, obj) 669 | register struct monst *mtmp; 670 | register struct obj *obj; 671 | { 672 | register struct monst *mtmp2; 673 | 674 | /* The Wiz, Medusa and the quest nemeses aren't even made peaceful. */ 675 | if (mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA] 676 | || (mtmp->data->mflags3 & M3_WANTSARTI)) 677 | return((struct monst *)0); 678 | 679 | /* worst case, at least it'll be peaceful. */ 680 | mtmp->mpeaceful = 1; 681 | set_malign(mtmp); 682 | if(flags.moonphase == FULL_MOON && night() && rn2(6) && obj 683 | && mtmp->data->mlet == S_DOG) 684 | return((struct monst *)0); 685 | 686 | /* If we cannot tame it, at least it's no longer afraid. */ 687 | mtmp->mflee = 0; 688 | mtmp->mfleetim = 0; 689 | 690 | /* make grabber let go now, whether it becomes tame or not */ 691 | if (mtmp == u.ustuck) { 692 | if (u.uswallow) 693 | expels(mtmp, mtmp->data, TRUE); 694 | else if (!(Upolyd && sticks(youmonst.data))) 695 | unstuck(mtmp); 696 | } 697 | 698 | /* feeding it treats makes it tamer */ 699 | if (mtmp->mtame && obj) { 700 | int tasty; 701 | 702 | if (mtmp->mcanmove && !mtmp->mconf && !mtmp->meating && 703 | ((tasty = dogfood(mtmp, obj)) == DOGFOOD || 704 | (tasty <= ACCFOOD && EDOG(mtmp)->hungrytime <= monstermoves))) { 705 | /* pet will "catch" and eat this thrown food */ 706 | if (canseemon(mtmp)) { 707 | boolean big_corpse = (obj->otyp == CORPSE && 708 | obj->corpsenm >= LOW_PM && 709 | mons[obj->corpsenm].msize > mtmp->data->msize); 710 | pline("%s catches %s%s", 711 | Monnam(mtmp), the(xname(obj)), 712 | !big_corpse ? "." : ", or vice versa!"); 713 | } else if (cansee(mtmp->mx,mtmp->my)) 714 | pline("%s stops.", The(xname(obj))); 715 | /* dog_eat expects a floor object */ 716 | place_object(obj, mtmp->mx, mtmp->my); 717 | (void) dog_eat(mtmp, obj, mtmp->mx, mtmp->my, FALSE); 718 | /* eating might have killed it, but that doesn't matter here; 719 | a non-null result suppresses "miss" message for thrown 720 | food and also implies that the object has been deleted */ 721 | return mtmp; 722 | } else 723 | return (struct monst *)0; 724 | } 725 | 726 | if (mtmp->mtame || !mtmp->mcanmove || 727 | /* monsters with conflicting structures cannot be tamed */ 728 | mtmp->isshk || mtmp->isgd || mtmp->ispriest || mtmp->isminion || 729 | is_covetous(mtmp->data) || is_human(mtmp->data) || 730 | (is_demon(mtmp->data) && !is_demon(youmonst.data)) || 731 | (obj && dogfood(mtmp, obj) >= MANFOOD)) return (struct monst *)0; 732 | 733 | /* make a new monster which has the pet extension */ 734 | mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); 735 | *mtmp2 = *mtmp; 736 | mtmp2->mxlth = sizeof(struct edog); 737 | if (mtmp->mnamelth) Strcpy(NAME(mtmp2), NAME(mtmp)); 738 | initedog(mtmp2); 739 | replmon(mtmp, mtmp2); 740 | /* `mtmp' is now obsolete */ 741 | 742 | if (obj) { /* thrown food */ 743 | /* defer eating until the edog extension has been set up */ 744 | place_object(obj, mtmp2->mx, mtmp2->my); /* put on floor */ 745 | /* devour the food (might grow into larger, genocided monster) */ 746 | if (dog_eat(mtmp2, obj, mtmp2->mx, mtmp2->my, TRUE) == 2) 747 | return mtmp2; /* oops, it died... */ 748 | /* `obj' is now obsolete */ 749 | } 750 | 751 | newsym(mtmp2->mx, mtmp2->my); 752 | if (attacktype(mtmp2->data, AT_WEAP)) { 753 | mtmp2->weapon_check = NEED_HTH_WEAPON; 754 | (void) mon_wield_item(mtmp2); 755 | } 756 | return(mtmp2); 757 | } 758 | 759 | /* 760 | * Called during pet revival or pet life-saving. 761 | * If you killed the pet, it revives wild. 762 | * If you abused the pet a lot while alive, it revives wild. 763 | * If you abused the pet at all while alive, it revives untame. 764 | * If the pet wasn't abused and was very tame, it might revive tame. 765 | */ 766 | void 767 | wary_dog(mtmp, quietly) 768 | struct monst *mtmp; 769 | boolean quietly; 770 | { 771 | int has_edog; 772 | 773 | if (!mtmp->mtame) return; 774 | has_edog = !mtmp->isminion; 775 | if (has_edog && 776 | ((EDOG(mtmp)->killed_by_u == 1) || (EDOG(mtmp)->abuse > 2))) { 777 | mtmp->mpeaceful = mtmp->mtame = 0; 778 | if (EDOG(mtmp)->abuse >= 0 && EDOG(mtmp)->abuse < 10) 779 | if (!rn2(EDOG(mtmp)->abuse + 1)) mtmp->mpeaceful = 1; 780 | if(!quietly && cansee(mtmp->mx, mtmp->my)) { 781 | if (haseyes(youmonst.data)) { 782 | if (haseyes(mtmp->data)) 783 | pline("%s %s to look you in the %s.", 784 | Monnam(mtmp), 785 | mtmp->mpeaceful ? "seems unable" : 786 | "refuses", 787 | body_part(EYE)); 788 | else 789 | pline("%s avoids your gaze.", 790 | Monnam(mtmp)); 791 | } 792 | } 793 | } else { 794 | /* chance it goes wild anyway - Pet Semetary */ 795 | if (!rn2(mtmp->mtame)) { 796 | mtmp->mpeaceful = mtmp->mtame = 0; 797 | } 798 | } 799 | /* if its still a pet, start a clean pet-slate now */ 800 | if (has_edog && mtmp->mtame) { 801 | EDOG(mtmp)->revivals++; 802 | EDOG(mtmp)->killed_by_u = 0; 803 | EDOG(mtmp)->abuse = 0; 804 | } 805 | } 806 | 807 | void 808 | abuse_dog(mtmp) 809 | struct monst *mtmp; 810 | { 811 | if (!mtmp->mtame) return; 812 | 813 | if (Aggravate_monster || Conflict) mtmp->mtame /=2; 814 | else mtmp->mtame--; 815 | 816 | if (mtmp->mtame && !mtmp->isminion) 817 | EDOG(mtmp)->abuse++; 818 | 819 | if (mtmp->mtame && rn2(mtmp->mtame)) yelp(mtmp); 820 | else growl(mtmp); /* give them a moment's worry */ 821 | 822 | if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); 823 | } 824 | 825 | #endif /* OVLB */ 826 | 827 | /*dog.c*/