*** empty log message ***
This commit is contained in:
846
src/dog.c
Normal file
846
src/dog.c
Normal file
@@ -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*/
|
||||
Reference in New Issue
Block a user