/* SCCS Id: @(#)dog.c 3.3 1999/10/20 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" #include "edog.h" #ifdef OVLB STATIC_DCL int NDECL(pet_type); void initedog(mtmp) register struct monst *mtmp; { mtmp->mtame = is_domestic(mtmp->data) ? 10 : 5; mtmp->mpeaceful = 1; set_malign(mtmp); /* recalc alignment now that it's tamed */ mtmp->mleashed = 0; mtmp->meating = 0; EDOG(mtmp)->droptime = 0; EDOG(mtmp)->dropdist = 10000; EDOG(mtmp)->apport = 10; EDOG(mtmp)->whistletime = 0; EDOG(mtmp)->hungrytime = 1000 + monstermoves; EDOG(mtmp)->ogoal.x = -1; /* force error if used before set */ EDOG(mtmp)->ogoal.y = -1; EDOG(mtmp)->abuse = 0; EDOG(mtmp)->revivals = 0; EDOG(mtmp)->mhpmax_penalty = 0; EDOG(mtmp)->killed_by_u = 0; } STATIC_OVL int pet_type() { if (urole.petnum != NON_PM) return (urole.petnum); else if (preferred_pet == 'c') return (PM_KITTEN); else if (preferred_pet == 'd') return (PM_LITTLE_DOG); else return (rn2(2) ? PM_KITTEN : PM_LITTLE_DOG); } struct monst * make_familiar(otmp,x,y,quietly) register struct obj *otmp; xchar x, y; boolean quietly; { struct permonst *pm; struct monst *mtmp = 0; int chance, trycnt = 100; do { if (otmp) { pm = &mons[otmp->corpsenm]; /* Figurine; otherwise spell */ } else if (!rn2(3)) { pm = &mons[pet_type()]; } else { pm = rndmonst(); if (!pm) { if (!quietly) There("seems to be nothing available for a familiar."); break; } } mtmp = makemon(pm, x, y, MM_EDOG); if (otmp && !mtmp) { /* monster was genocided or square occupied */ if (!quietly) pline_The("figurine writhes and then shatters into pieces!"); break; } } while (!mtmp && --trycnt > 0); if (!mtmp) return (struct monst *)0; initedog(mtmp); mtmp->msleeping = 0; if (otmp) { /* figurine; resulting monster might not become a pet */ chance = rn2(10); /* 0==tame, 1==peaceful, 2==hostile */ if (chance > 2) chance = otmp->blessed ? 0 : !otmp->cursed ? 1 : 2; /* 0,1,2: b=80%,10,10; nc=10%,80,10; c=10%,10,80 */ if (chance > 0) { mtmp->mtame = 0; /* not tame after all */ if (chance == 2) { /* hostile (cursed figurine) */ if (!quietly) You("get a bad feeling about this."); mtmp->mpeaceful = 0; } } /* if figurine has been named, give same name to the monster */ if (otmp->onamelth) mtmp = christen_monst(mtmp, ONAME(otmp)); } set_malign(mtmp); /* more alignment changes */ newsym(mtmp->mx, mtmp->my); /* must wield weapon immediately since pets will otherwise drop it */ if (mtmp->mtame && attacktype(mtmp->data, AT_WEAP)) { mtmp->weapon_check = NEED_HTH_WEAPON; (void) mon_wield_item(mtmp); } return mtmp; } struct monst * makedog() { register struct monst *mtmp; #ifdef STEED register struct obj *otmp; #endif const char *petname; int pettype; static int petname_used = 0; pettype = pet_type(); if (pettype == PM_LITTLE_DOG) petname = dogname; else if (pettype == PM_PONY) petname = horsename; else petname = catname; /* default pet names */ if (!*petname && pettype == PM_LITTLE_DOG) { /* All of these names were for dogs. */ if(Role_if(PM_CAVEMAN)) petname = "Slasher"; /* The Warrior */ if(Role_if(PM_SAMURAI)) petname = "Hachi"; /* Shibuya Station */ if(Role_if(PM_BARBARIAN)) petname = "Idefix"; /* Obelix */ if(Role_if(PM_RANGER)) petname = "Sirius"; /* Orion's dog */ } mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG); if(!mtmp) return((struct monst *) 0); /* pets were genocided */ #ifdef STEED /* Horses already wear a saddle */ if (pettype == PM_PONY && !!(otmp = mksobj(SADDLE, TRUE, FALSE))) { if (mpickobj(mtmp, otmp)) panic("merged saddle?"); mtmp->misc_worn_check |= W_SADDLE; otmp->dknown = otmp->bknown = otmp->rknown = 1; otmp->owornmask = W_SADDLE; otmp->leashmon = mtmp->m_id; update_mon_intrinsics(mtmp, otmp, TRUE); } #endif if (!petname_used++ && *petname) mtmp = christen_monst(mtmp, petname); initedog(mtmp); return(mtmp); } /* record `last move time' for all monsters prior to level save so that mon_arrive() can catch up for lost time when they're restored later */ void update_mlstmv() { struct monst *mon; /* monst->mlstmv used to be updated every time `monst' actually moved, but that is no longer the case so we just do a blanket assignment */ for (mon = fmon; mon; mon = mon->nmon) if (!DEADMONSTER(mon)) mon->mlstmv = monstermoves; } void losedogs() { register struct monst *mtmp, *mtmp0 = 0, *mtmp2; while ((mtmp = mydogs) != 0) { mydogs = mtmp->nmon; mon_arrive(mtmp, TRUE); } for(mtmp = migrating_mons; mtmp; mtmp = mtmp2) { mtmp2 = mtmp->nmon; if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) { if(mtmp == migrating_mons) migrating_mons = mtmp->nmon; else mtmp0->nmon = mtmp->nmon; mon_arrive(mtmp, FALSE); } else mtmp0 = mtmp; } } /* called from resurrect() in addition to losedogs() */ void mon_arrive(mtmp, with_you) struct monst *mtmp; boolean with_you; { struct trap *t; xchar xlocale, ylocale, xyloc, xyflags, wander; int num_segs; mtmp->nmon = fmon; fmon = mtmp; if (mtmp->isshk) set_residency(mtmp, FALSE); num_segs = mtmp->wormno; /* baby long worms have no tail so don't use is_longworm() */ if ((mtmp->data == &mons[PM_LONG_WORM]) && #ifdef DCC30_BUG (mtmp->wormno = get_wormno(), mtmp->wormno != 0)) #else (mtmp->wormno = get_wormno()) != 0) #endif { initworm(mtmp, num_segs); /* tail segs are not yet initialized or displayed */ } else mtmp->wormno = 0; /* some monsters might need to do something special upon arrival _after_ the current level has been fully set up; see dochug() */ mtmp->mstrategy |= STRAT_ARRIVE; /* make sure mnexto(rloc_to(set_apparxy())) doesn't use stale data */ mtmp->mux = u.ux, mtmp->muy = u.uy; xyloc = mtmp->mtrack[0].x; xyflags = mtmp->mtrack[0].y; xlocale = mtmp->mtrack[1].x; ylocale = mtmp->mtrack[1].y; mtmp->mtrack[0].x = mtmp->mtrack[0].y = 0; mtmp->mtrack[1].x = mtmp->mtrack[1].y = 0; #ifdef STEED if (mtmp == u.usteed) return; /* don't place steed on the map */ #endif if (with_you) { /* When a monster accompanies you, sometimes it will arrive at your intended destination and you'll end up next to that spot. This code doesn't control the final outcome; goto_level(do.c) decides who ends up at your target spot when there is a monster there too. */ if (!MON_AT(u.ux, u.uy) && !rn2(mtmp->mtame ? 10 : mtmp->mpeaceful ? 5 : 2)) rloc_to(mtmp, u.ux, u.uy); else mnexto(mtmp); return; } /* * The monster arrived on this level independently of the player. * Its coordinate fields were overloaded for use as flags that * specify its final destination. */ if (mtmp->mlstmv < monstermoves - 1L) { /* heal monster for time spent in limbo */ long nmv = monstermoves - 1L - mtmp->mlstmv; mon_catchup_elapsed_time(mtmp, nmv); mtmp->mlstmv = monstermoves - 1L; /* let monster move a bit on new level (see placement code below) */ wander = (xchar) min(nmv, 8); } else wander = 0; switch (xyloc) { case MIGR_APPROX_XY: /* {x,y}locale set above */ break; case MIGR_EXACT_XY: wander = 0; break; case MIGR_NEAR_PLAYER: xlocale = u.ux, ylocale = u.uy; break; case MIGR_STAIRS_UP: xlocale = xupstair, ylocale = yupstair; break; case MIGR_STAIRS_DOWN: xlocale = xdnstair, ylocale = ydnstair; break; case MIGR_LADDER_UP: xlocale = xupladder, ylocale = yupladder; break; case MIGR_LADDER_DOWN: xlocale = xdnladder, ylocale = ydnladder; break; case MIGR_SSTAIRS: xlocale = sstairs.sx, ylocale = sstairs.sy; break; case MIGR_PORTAL: if (In_endgame(&u.uz)) { /* there is no arrival portal for endgame levels */ /* BUG[?]: for simplicity, this code relies on the fact that we know that the current endgame levels always build upwards and never have any exclusion subregion inside their TELEPORT_REGION settings. */ xlocale = rn1(updest.hx - updest.lx + 1, updest.lx); ylocale = rn1(updest.hy - updest.ly + 1, updest.ly); break; } /* find the arrival portal */ for (t = ftrap; t; t = t->ntrap) if (t->ttyp == MAGIC_PORTAL) break; if (t) { xlocale = t->tx, ylocale = t->ty; break; } else { impossible("mon_arrive: no corresponding portal?"); } /*FALLTHRU*/ default: case MIGR_RANDOM: xlocale = ylocale = 0; break; } if (xlocale && wander) { /* monster moved a bit; pick a nearby location */ /* mnearto() deals w/stone, et al */ char *r = in_rooms(xlocale, ylocale, 0); if (r && *r) { coord c; /* somexy() handles irregular rooms */ if (somexy(&rooms[*r - ROOMOFFSET], &c)) xlocale = c.x, ylocale = c.y; else xlocale = ylocale = 0; } else { /* not in a room */ int i, j; i = max(1, xlocale - wander); j = min(COLNO-1, xlocale + wander); xlocale = rn1(j - i, i); i = max(0, ylocale - wander); j = min(ROWNO-1, ylocale + wander); ylocale = rn1(j - i, i); } } /* moved a bit */ mtmp->mx = 0; /*(already is 0)*/ mtmp->my = xyflags; if (xlocale) (void) mnearto(mtmp, xlocale, ylocale, FALSE); else rloc(mtmp); } /* heal monster for time spent elsewhere */ void mon_catchup_elapsed_time(mtmp, nmv) struct monst *mtmp; long nmv; /* number of moves */ { int imv = 0; /* avoid zillions of casts and lint warnings */ #if defined(DEBUG) || defined(BETA) if (nmv < 0L) { /* crash likely... */ panic("catchup from future time?"); /*NOTREACHED*/ return; } else if (nmv == 0L) { /* safe, but should'nt happen */ impossible("catchup from now?"); } else #endif if (nmv >= LARGEST_INT) /* paranoia */ imv = LARGEST_INT - 1; else imv = (int)nmv; /* might stop being afraid, blind or frozen */ /* set to 1 and allow final decrement in movemon() */ if (mtmp->mblinded) { if (imv >= (int) mtmp->mblinded) mtmp->mblinded = 1; else mtmp->mblinded -= imv; } if (mtmp->mfrozen) { if (imv >= (int) mtmp->mfrozen) mtmp->mfrozen = 1; else mtmp->mfrozen -= imv; } if (mtmp->mfleetim) { if (imv >= (int) mtmp->mfleetim) mtmp->mfleetim = 1; else mtmp->mfleetim -= imv; } /* might recover from temporary trouble */ if (mtmp->mtrapped && rn2(imv + 1) > 40/2) mtmp->mtrapped = 0; if (mtmp->mconf && rn2(imv + 1) > 50/2) mtmp->mconf = 0; if (mtmp->mstun && rn2(imv + 1) > 10/2) mtmp->mstun = 0; /* might finish eating or be able to use special ability again */ if (imv > mtmp->meating) mtmp->meating = 0; else mtmp->meating -= imv; if (imv > mtmp->mspec_used) mtmp->mspec_used = 0; else mtmp->mspec_used -= imv; /* reduce tameness for every 150 moves you are separated */ if (mtmp->mtame) { int wilder = (imv + 75) / 150; if (mtmp->mtame > wilder) mtmp->mtame -= wilder; /* less tame */ else if (mtmp->mtame > rn2(wilder)) mtmp->mtame = 0; /* untame */ else mtmp->mtame = mtmp->mpeaceful = 0; /* hostile! */ } /* check to see if it would have died as a pet; if so, go wild instead * of dying the next time we call dog_move() */ if (mtmp->mtame && !mtmp->isminion && (carnivorous(mtmp->data) || herbivorous(mtmp->data))) { struct edog *edog = EDOG(mtmp); if ((monstermoves > edog->hungrytime + 500 && mtmp->mhp < 3) || (monstermoves > edog->hungrytime + 750)) mtmp->mtame = mtmp->mpeaceful = 0; } /* recover lost hit points */ if (!regenerates(mtmp->data)) imv /= 20; if (mtmp->mhp + imv >= mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; else mtmp->mhp += imv; } #endif /* OVLB */ #ifdef OVL2 /* called when you move to another level */ void keepdogs(pets_only) boolean pets_only; /* true for ascension or final escape */ { register struct monst *mtmp, *mtmp2; register struct obj *obj; int num_segs; boolean stay_behind; for (mtmp = fmon; mtmp; mtmp = mtmp2) { mtmp2 = mtmp->nmon; if (DEADMONSTER(mtmp)) continue; if (pets_only && !mtmp->mtame) continue; if (((monnear(mtmp, u.ux, u.uy) && levl_follower(mtmp)) || #ifdef STEED (mtmp == u.usteed) || #endif /* the wiz will level t-port from anywhere to chase the amulet; if you don't have it, will chase you only if in range. -3. */ (u.uhave.amulet && mtmp->iswiz)) && !mtmp->msleeping && mtmp->mcanmove) { stay_behind = FALSE; if (mtmp->mtame && mtmp->meating) { if (canseemon(mtmp)) pline("%s is still eating.", Monnam(mtmp)); stay_behind = TRUE; } else if (mon_has_amulet(mtmp)) { if (canseemon(mtmp)) pline("%s seems very disoriented for a moment.", Monnam(mtmp)); stay_behind = TRUE; } if (stay_behind #ifdef STEED && mtmp != u.usteed #endif ) { if (mtmp->mleashed) { pline("%s leash suddenly comes loose.", humanoid(mtmp->data) ? (mtmp->female ? "Her" : "His") : "Its"); m_unleash(mtmp); } continue; } if (mtmp->isshk) set_residency(mtmp, TRUE); if (mtmp->wormno) { register int cnt; /* NOTE: worm is truncated to # segs = max wormno size */ cnt = count_wsegs(mtmp); num_segs = min(cnt, MAX_NUM_WORMS - 1); wormgone(mtmp); } else num_segs = 0; /* set minvent's obj->no_charge to 0 */ for(obj = mtmp->minvent; obj; obj = obj->nobj) { if (Has_contents(obj)) picked_container(obj); /* does the right thing */ obj->no_charge = 0; } relmon(mtmp); newsym(mtmp->mx,mtmp->my); mtmp->mx = mtmp->my = 0; /* avoid mnexto()/MON_AT() problem */ mtmp->wormno = num_segs; mtmp->mlstmv = monstermoves; mtmp->nmon = mydogs; mydogs = mtmp; } else if (mtmp->iswiz) { /* we want to be able to find him when his next resurrection chance comes up, but have him resume his present location if player returns to this level before that time */ migrate_to_level(mtmp, ledger_no(&u.uz), MIGR_EXACT_XY, (coord *)0); } else if (mtmp->mleashed) { /* this can happen if your quest leader ejects you from the "home" level while a leashed pet isn't next to you */ pline("%s leash goes slack.", s_suffix(Monnam(mtmp))); m_unleash(mtmp); } } } #endif /* OVL2 */ #ifdef OVLB void migrate_to_level(mtmp, tolev, xyloc, cc) register struct monst *mtmp; xchar tolev; /* destination level */ xchar xyloc; /* MIGR_xxx destination xy location: */ coord *cc; /* optional destination coordinates */ { register struct obj *obj; d_level new_lev; xchar xyflags; int num_segs = 0; /* count of worm segments */ if (mtmp->isshk) set_residency(mtmp, TRUE); if (mtmp->wormno) { register int cnt; /* **** NOTE: worm is truncated to # segs = max wormno size **** */ cnt = count_wsegs(mtmp); num_segs = min(cnt, MAX_NUM_WORMS - 1); wormgone(mtmp); } /* set minvent's obj->no_charge to 0 */ for(obj = mtmp->minvent; obj; obj = obj->nobj) { if (Has_contents(obj)) picked_container(obj); /* does the right thing */ obj->no_charge = 0; } relmon(mtmp); mtmp->nmon = migrating_mons; migrating_mons = mtmp; if (mtmp->mleashed) { m_unleash(mtmp); mtmp->mtame--; pline_The("leash comes off!"); } newsym(mtmp->mx,mtmp->my); new_lev.dnum = ledger_to_dnum((xchar)tolev); new_lev.dlevel = ledger_to_dlev((xchar)tolev); /* overload mtmp->[mx,my], mtmp->[mux,muy], and mtmp->mtrack[] as */ /* destination codes (setup flag bits before altering mx or my) */ xyflags = (depth(&new_lev) < depth(&u.uz)); /* 1 => up */ if (In_W_tower(mtmp->mx, mtmp->my, &u.uz)) xyflags |= 2; mtmp->wormno = num_segs; mtmp->mlstmv = monstermoves; mtmp->mtrack[1].x = cc ? cc->x : mtmp->mx; mtmp->mtrack[1].y = cc ? cc->y : mtmp->my; mtmp->mtrack[0].x = xyloc; mtmp->mtrack[0].y = xyflags; mtmp->mux = new_lev.dnum; mtmp->muy = new_lev.dlevel; mtmp->mx = mtmp->my = 0; /* this implies migration */ } #endif /* OVLB */ #ifdef OVL1 /* return quality of food; the lower the better */ /* fungi will eat even tainted food */ int dogfood(mon,obj) struct monst *mon; register struct obj *obj; { boolean carni = carnivorous(mon->data); boolean herbi = herbivorous(mon->data); struct permonst *fptr = &mons[obj->corpsenm]; if (is_quest_artifact(obj) || obj_resists(obj, 0, 95)) return (obj->cursed ? TABU : APPORT); switch(obj->oclass) { case FOOD_CLASS: if (obj->otyp == CORPSE && ((touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) || is_rider(fptr))) return TABU; /* Ghouls only eat old corpses... yum! */ if (mon->data == &mons[PM_GHOUL]) return (obj->otyp == CORPSE && obj->age+50 <= monstermoves) ? DOGFOOD : TABU; if (!carni && !herbi) return (obj->cursed ? UNDEF : APPORT); switch (obj->otyp) { case TRIPE_RATION: case MEATBALL: case MEAT_RING: case MEAT_STICK: case HUGE_CHUNK_OF_MEAT: return (carni ? DOGFOOD : MANFOOD); case EGG: if (touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) return POISON; return (carni ? CADAVER : MANFOOD); case CORPSE: if ((peek_at_iced_corpse_age(obj)+50 <= monstermoves && obj->corpsenm != PM_LIZARD && obj->corpsenm != PM_LICHEN && mon->data->mlet != S_FUNGUS) || (acidic(&mons[obj->corpsenm]) && !resists_acid(mon)) || (poisonous(&mons[obj->corpsenm]) && !resists_poison(mon))) return POISON; else if (vegan(fptr)) return (herbi ? CADAVER : MANFOOD); else return (carni ? CADAVER : MANFOOD); case CLOVE_OF_GARLIC: return (is_undead(mon->data) ? TABU : (herbi ? ACCFOOD : MANFOOD)); case TIN: return (metallivorous(mon->data) ? ACCFOOD : MANFOOD); case APPLE: case CARROT: return (herbi ? DOGFOOD : MANFOOD); case BANANA: return ((mon->data->mlet == S_YETI) ? DOGFOOD : (herbi ? ACCFOOD : MANFOOD)); default: return (obj->otyp > SLIME_MOLD ? (carni ? ACCFOOD : MANFOOD) : (herbi ? ACCFOOD : MANFOOD)); } default: if (obj->otyp == AMULET_OF_STRANGULATION || obj->otyp == RIN_SLOW_DIGESTION) return (TABU); if (hates_silver(mon->data) && objects[obj->otyp].oc_material == SILVER) return(TABU); if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj)) return(ACCFOOD); if (metallivorous(mon->data) && is_metallic(obj)) /* Non-rustproofed ferrous based metals are preferred. */ return(objects[obj->otyp].oc_material == IRON && !obj->oerodeproof ? DOGFOOD : ACCFOOD); if(!obj->cursed && obj->oclass != BALL_CLASS && obj->oclass != CHAIN_CLASS) return(APPORT); /* fall into next case */ case ROCK_CLASS: return(UNDEF); } } #endif /* OVL1 */ #ifdef OVLB struct monst * tamedog(mtmp, obj) register struct monst *mtmp; register struct obj *obj; { register struct monst *mtmp2; /* The Wiz, Medusa and the quest nemeses aren't even made peaceful. */ if (mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA] || (mtmp->data->mflags3 & M3_WANTSARTI)) return((struct monst *)0); /* worst case, at least it'll be peaceful. */ mtmp->mpeaceful = 1; set_malign(mtmp); if(flags.moonphase == FULL_MOON && night() && rn2(6) && obj && mtmp->data->mlet == S_DOG) return((struct monst *)0); /* If we cannot tame it, at least it's no longer afraid. */ mtmp->mflee = 0; mtmp->mfleetim = 0; /* make grabber let go now, whether it becomes tame or not */ if (mtmp == u.ustuck) { if (u.uswallow) expels(mtmp, mtmp->data, TRUE); else if (!(Upolyd && sticks(youmonst.data))) unstuck(mtmp); } /* feeding it treats makes it tamer */ if (mtmp->mtame && obj) { int tasty; if (mtmp->mcanmove && !mtmp->mconf && !mtmp->meating && ((tasty = dogfood(mtmp, obj)) == DOGFOOD || (tasty <= ACCFOOD && EDOG(mtmp)->hungrytime <= monstermoves))) { /* pet will "catch" and eat this thrown food */ if (canseemon(mtmp)) { boolean big_corpse = (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM && mons[obj->corpsenm].msize > mtmp->data->msize); pline("%s catches %s%s", Monnam(mtmp), the(xname(obj)), !big_corpse ? "." : ", or vice versa!"); } else if (cansee(mtmp->mx,mtmp->my)) pline("%s stops.", The(xname(obj))); /* dog_eat expects a floor object */ place_object(obj, mtmp->mx, mtmp->my); (void) dog_eat(mtmp, obj, mtmp->mx, mtmp->my, FALSE); /* eating might have killed it, but that doesn't matter here; a non-null result suppresses "miss" message for thrown food and also implies that the object has been deleted */ return mtmp; } else return (struct monst *)0; } if (mtmp->mtame || !mtmp->mcanmove || /* monsters with conflicting structures cannot be tamed */ mtmp->isshk || mtmp->isgd || mtmp->ispriest || mtmp->isminion || is_covetous(mtmp->data) || is_human(mtmp->data) || (is_demon(mtmp->data) && !is_demon(youmonst.data)) || (obj && dogfood(mtmp, obj) >= MANFOOD)) return (struct monst *)0; /* make a new monster which has the pet extension */ mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); *mtmp2 = *mtmp; mtmp2->mxlth = sizeof(struct edog); if (mtmp->mnamelth) Strcpy(NAME(mtmp2), NAME(mtmp)); initedog(mtmp2); replmon(mtmp, mtmp2); /* `mtmp' is now obsolete */ if (obj) { /* thrown food */ /* defer eating until the edog extension has been set up */ place_object(obj, mtmp2->mx, mtmp2->my); /* put on floor */ /* devour the food (might grow into larger, genocided monster) */ if (dog_eat(mtmp2, obj, mtmp2->mx, mtmp2->my, TRUE) == 2) return mtmp2; /* oops, it died... */ /* `obj' is now obsolete */ } newsym(mtmp2->mx, mtmp2->my); if (attacktype(mtmp2->data, AT_WEAP)) { mtmp2->weapon_check = NEED_HTH_WEAPON; (void) mon_wield_item(mtmp2); } return(mtmp2); } /* * Called during pet revival or pet life-saving. * If you killed the pet, it revives wild. * If you abused the pet a lot while alive, it revives wild. * If you abused the pet at all while alive, it revives untame. * If the pet wasn't abused and was very tame, it might revive tame. */ void wary_dog(mtmp, quietly) struct monst *mtmp; boolean quietly; { int has_edog; if (!mtmp->mtame) return; has_edog = !mtmp->isminion; if (has_edog && ((EDOG(mtmp)->killed_by_u == 1) || (EDOG(mtmp)->abuse > 2))) { mtmp->mpeaceful = mtmp->mtame = 0; if (EDOG(mtmp)->abuse >= 0 && EDOG(mtmp)->abuse < 10) if (!rn2(EDOG(mtmp)->abuse + 1)) mtmp->mpeaceful = 1; if(!quietly && cansee(mtmp->mx, mtmp->my)) { if (haseyes(youmonst.data)) { if (haseyes(mtmp->data)) pline("%s %s to look you in the %s.", Monnam(mtmp), mtmp->mpeaceful ? "seems unable" : "refuses", body_part(EYE)); else pline("%s avoids your gaze.", Monnam(mtmp)); } } } else { /* chance it goes wild anyway - Pet Semetary */ if (!rn2(mtmp->mtame)) { mtmp->mpeaceful = mtmp->mtame = 0; } } /* if its still a pet, start a clean pet-slate now */ if (has_edog && mtmp->mtame) { EDOG(mtmp)->revivals++; EDOG(mtmp)->killed_by_u = 0; EDOG(mtmp)->abuse = 0; } } void abuse_dog(mtmp) struct monst *mtmp; { if (!mtmp->mtame) return; if (Aggravate_monster || Conflict) mtmp->mtame /=2; else mtmp->mtame--; if (mtmp->mtame && !mtmp->isminion) EDOG(mtmp)->abuse++; if (mtmp->mtame && rn2(mtmp->mtame)) yelp(mtmp); else growl(mtmp); /* give them a moment's worry */ if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); } #endif /* OVLB */ /*dog.c*/