From 7d67e0255aa744273c440b508de237abf82fa4bd Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:48 +0000 Subject: [PATCH] *** empty log message *** --- src/dog.c | 846 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 846 insertions(+) create mode 100644 src/dog.c diff --git a/src/dog.c b/src/dog.c new file mode 100644 index 000000000..d5266c477 --- /dev/null +++ b/src/dog.c @@ -0,0 +1,846 @@ +/* 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; + mtmp->mavenge = 0; + 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 + /* monster won't follow if it hasn't noticed you yet */ + && !(mtmp->mstrategy & STRAT_WAITFORU)) { + 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; + } else if (mtmp->mtame && mtmp->mtrapped) { + if (canseemon(mtmp)) + pline("%s is still trapped.", 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) && (is_rustprone(obj) || mon->data != &mons[PM_RUST_MONSTER])) { + /* Non-rustproofed ferrous based metals are preferred. */ + return((is_rustprone(obj) && !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 (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); + + /* 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 && mtmp->mleashed) { + pline("%s breaks loose of %s leash!", Monnam(mtmp), mhis(mtmp)); + m_unleash(mtmp); + } + + /* don't make a sound if pet is in the middle of leaving the level */ + /* newsym isn't necessary in this case either */ + if (mtmp->mx != 0) { + 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*/