fix #U782 - undead turning in a shop [trunk only]
Reported last December by <email deleted>. Using
a wand or spell of undead turning inside a shop used up corpses without
checking whether they were owned by the shop. Although the report didn't
mention it, using stone-to-flesh on statues had the same problem.
I'm not completely satisfied by some aspects of this code, but if I
don't commit what I've got now I probably never would. My original notes
are lost; I thought that there were some additional fixes present, but
looking at these diffs I don't see anything else significant enough to
warrant mention in the fixes file.
This commit is contained in:
@@ -56,6 +56,7 @@ destroying a worn item via dipping in burning oil would not unwear/unwield
|
||||
the item properly, possibly leading to various strange behaviors
|
||||
avoid a panic splitbill when shopkeeper is trapped by the door
|
||||
grammar tidbit for message given when eating tainted meat is also cannibalism
|
||||
charge for reviving a shop owned corpse or reanimating a shop owned statue
|
||||
|
||||
|
||||
Platform- and/or Interface-Specific Fixes
|
||||
|
||||
@@ -2422,7 +2422,7 @@ E boolean FDECL(get_obj_location, (struct obj *,xchar *,xchar *,int));
|
||||
E boolean FDECL(get_mon_location, (struct monst *,xchar *,xchar *,int));
|
||||
E struct monst *FDECL(get_container_location, (struct obj *obj, int *, int *));
|
||||
E struct monst *FDECL(montraits, (struct obj *,coord *));
|
||||
E struct monst *FDECL(revive, (struct obj *));
|
||||
E struct monst *FDECL(revive, (struct obj *,BOOLEAN_P));
|
||||
E int FDECL(unturn_dead, (struct monst *));
|
||||
E void FDECL(cancel_item, (struct obj *));
|
||||
E boolean FDECL(drain_item, (struct obj *));
|
||||
|
||||
4
src/do.c
4
src/do.c
@@ -1,4 +1,4 @@
|
||||
/* SCCS Id: @(#)do.c 3.4 2003/12/17 */
|
||||
/* SCCS Id: @(#)do.c 3.4 2004/09/10 */
|
||||
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||
/* NetHack may be freely redistributed. See license for details. */
|
||||
|
||||
@@ -1511,7 +1511,7 @@ struct obj *corpse;
|
||||
/* container_where is the outermost container's location even if nested */
|
||||
if (container_where == OBJ_MINVENT && mtmp2) mcarry = mtmp2;
|
||||
}
|
||||
mtmp = revive(corpse); /* corpse is gone if successful */
|
||||
mtmp = revive(corpse, FALSE); /* corpse is gone if successful */
|
||||
|
||||
if (mtmp) {
|
||||
chewed = (mtmp->mhp < mtmp->mhpmax);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* SCCS Id: @(#)mkroom.c 3.4 2001/09/06 */
|
||||
/* SCCS Id: @(#)mkroom.c 3.4 2004/06/10 */
|
||||
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||
/* NetHack may be freely redistributed. See license for details. */
|
||||
|
||||
@@ -403,7 +403,7 @@ int mm_flags;
|
||||
if (enexto(&cc, mm->x, mm->y, mdat) &&
|
||||
(!revive_corpses ||
|
||||
!(otmp = sobj_at(CORPSE, cc.x, cc.y)) ||
|
||||
!revive(otmp)))
|
||||
!revive(otmp, FALSE)))
|
||||
(void) makemon(mdat, cc.x, cc.y, mm_flags);
|
||||
}
|
||||
level.flags.graveyard = TRUE; /* reduced chance for undead corpse */
|
||||
|
||||
109
src/trap.c
109
src/trap.c
@@ -1,4 +1,4 @@
|
||||
/* SCCS Id: @(#)trap.c 3.4 2004/08/23 */
|
||||
/* SCCS Id: @(#)trap.c 3.4 2004/09/10 */
|
||||
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||
/* NetHack may be freely redistributed. See license for details. */
|
||||
|
||||
@@ -382,6 +382,18 @@ boolean td; /* td == TRUE : trap door or hole */
|
||||
*
|
||||
* Perhaps x, y is not needed if we can use get_obj_location() to find
|
||||
* the statue's location... ???
|
||||
*
|
||||
* Sequencing matters:
|
||||
* create monster; if it fails, give up with statue intact;
|
||||
* give "statue comes to life" message;
|
||||
* if statue belongs to shop, have shk give "you owe" message;
|
||||
* transfer statue contents to monster (after stolen_value());
|
||||
* delete statue.
|
||||
* [This ordering means that if the statue ends up wearing a cloak of
|
||||
* invisibility or a mummy wrapping, the visibility checks might be
|
||||
* wrong, but to avoid that we'd have to clone the statue contents
|
||||
* first in order to give them to the monster before checking their
|
||||
* shop status--it's not worth the hassle.]
|
||||
*/
|
||||
struct monst *
|
||||
animate_statue(statue, x, y, cause, fail_reason)
|
||||
@@ -392,15 +404,16 @@ int *fail_reason;
|
||||
{
|
||||
int mnum = statue->corpsenm;
|
||||
struct permonst *mptr = &mons[mnum];
|
||||
struct monst *mon = 0;
|
||||
struct monst *mon = 0, *shkp;
|
||||
struct obj *item;
|
||||
coord cc;
|
||||
boolean historic = (Role_if(PM_ARCHEOLOGIST) && !context.mon_moving &&
|
||||
(statue->spe & STATUE_HISTORIC)),
|
||||
boolean historic = (Role_if(PM_ARCHEOLOGIST) &&
|
||||
(statue->spe & STATUE_HISTORIC) != 0),
|
||||
use_saved_traits;
|
||||
char statuename[BUFSZ];
|
||||
|
||||
Strcpy(statuename,the(xname(statue)));
|
||||
const char *comes_to_life;
|
||||
char statuename[BUFSZ], tmpbuf[BUFSZ];
|
||||
static const char historic_statue_is_gone[] =
|
||||
"that the historic statue is now gone";
|
||||
|
||||
if (cant_revive(&mnum, TRUE, statue)) {
|
||||
/* mnum has changed; we won't be animating this statue as itself */
|
||||
@@ -449,10 +462,7 @@ int *fail_reason;
|
||||
return (struct monst *)0;
|
||||
}
|
||||
|
||||
/* in case statue is wielded and hero zaps stone-to-flesh at self */
|
||||
if (statue->owornmask) remove_worn_item(statue, TRUE);
|
||||
|
||||
/* allow statues to be of a specific gender */
|
||||
/* a non-montraits() statue might specify gender */
|
||||
if (statue->spe & STATUE_MALE)
|
||||
mon->female = FALSE;
|
||||
else if (statue->spe & STATUE_FEMALE)
|
||||
@@ -460,40 +470,65 @@ int *fail_reason;
|
||||
/* if statue has been named, give same name to the monster */
|
||||
if (statue->onamelth)
|
||||
mon = christen_monst(mon, ONAME(statue));
|
||||
/* mimic statue becomes seen mimic; other hiders won't be hidden */
|
||||
if (mon->m_ap_type) seemimic(mon);
|
||||
else mon->mundetected = FALSE;
|
||||
|
||||
comes_to_life = !canspotmon(mon) ? "disappears" :
|
||||
(nonliving(mon->data) || is_vampshifter(mon)) ?
|
||||
"moves" : "comes to life";
|
||||
if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) {
|
||||
/* "the|your|Manlobbi's statue [of a wombat]" */
|
||||
Sprintf(statuename, "%s%s", shk_your(tmpbuf, statue),
|
||||
(cause == ANIMATE_SPELL) ? xname(statue) : "statue");
|
||||
pline("%s %s!", upstart(statuename), comes_to_life);
|
||||
} else if (cause == ANIMATE_SHATTER) {
|
||||
if (cansee(x, y))
|
||||
Sprintf(statuename, "%s%s", shk_your(tmpbuf, statue),
|
||||
xname(statue));
|
||||
else
|
||||
Strcpy(statuename, "a statue");
|
||||
pline("Instead of shattering, %s suddenly %s!",
|
||||
statuename, comes_to_life);
|
||||
} else { /* cause == ANIMATE_NORMAL */
|
||||
You("find %s posing as a statue.",
|
||||
canspotmon(mon) ? a_monnam(mon) : something);
|
||||
stop_occupation();
|
||||
}
|
||||
|
||||
/* if this isn't caused by a monster using a wand of striking,
|
||||
there might be consequences for the hero */
|
||||
if (!context.mon_moving) {
|
||||
/* if statue is owned by a shop, hero will have to pay for it;
|
||||
stolen_value gives a message (about debt or use of credit)
|
||||
which refers to "it" so needs to follow a message describing
|
||||
the object ("the statue comes to life" one above) */
|
||||
if (cause != ANIMATE_NORMAL && costly_spot(x, y) &&
|
||||
(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0)
|
||||
(void) stolen_value(statue, x, y,
|
||||
(boolean)shkp->mpeaceful, FALSE);
|
||||
|
||||
if (historic) {
|
||||
You_feel("guilty %s.", historic_statue_is_gone);
|
||||
adjalign(-1);
|
||||
}
|
||||
} else {
|
||||
if (historic && cansee(x, y))
|
||||
You_feel("regret %s.", historic_statue_is_gone);
|
||||
/* no alignment penalty */
|
||||
}
|
||||
|
||||
/* transfer any statue contents to monster's inventory */
|
||||
while ((item = statue->cobj) != 0) {
|
||||
obj_extract_self(item);
|
||||
(void) add_to_minv(mon, item);
|
||||
}
|
||||
m_dowear(mon, TRUE);
|
||||
/* in case statue is wielded and hero zaps stone-to-flesh at self */
|
||||
if (statue->owornmask) remove_worn_item(statue, TRUE);
|
||||
/* statue no longer exists */
|
||||
delobj(statue);
|
||||
|
||||
/* mimic statue becomes seen mimic; other hiders won't be hidden */
|
||||
if (mon->m_ap_type) seemimic(mon);
|
||||
else mon->mundetected = FALSE;
|
||||
|
||||
if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) {
|
||||
const char *comes_to_life = (nonliving(mon->data) ||
|
||||
is_vampshifter(mon)) ?
|
||||
"moves" : "comes to life";
|
||||
if (cause == ANIMATE_SPELL)
|
||||
pline("%s %s!", upstart(statuename),
|
||||
canspotmon(mon) ? comes_to_life : "disappears");
|
||||
else
|
||||
pline_The("statue %s!",
|
||||
canspotmon(mon) ? comes_to_life : "disappears");
|
||||
if (historic) {
|
||||
You_feel("guilty that the historic statue is now gone.");
|
||||
adjalign(-1);
|
||||
}
|
||||
} else if (cause == ANIMATE_SHATTER)
|
||||
pline("Instead of shattering, the statue suddenly %s!",
|
||||
canspotmon(mon) ? "comes to life" : "disappears");
|
||||
else { /* cause == ANIMATE_NORMAL */
|
||||
You("find %s posing as a statue.",
|
||||
canspotmon(mon) ? a_monnam(mon) : something);
|
||||
stop_occupation();
|
||||
}
|
||||
/* avoid hiding under nothing */
|
||||
if (x == u.ux && y == u.uy &&
|
||||
Upolyd && hides_under(youmonst.data) && !OBJ_AT(x, y))
|
||||
|
||||
387
src/zap.c
387
src/zap.c
@@ -1,4 +1,4 @@
|
||||
/* SCCS Id: @(#)zap.c 3.4 2004/08/02 */
|
||||
/* SCCS Id: @(#)zap.c 3.4 2004/09/10 */
|
||||
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||
/* NetHack may be freely redistributed. See license for details. */
|
||||
|
||||
@@ -563,168 +563,194 @@ int *container_nesting;
|
||||
* successful. Note: this does NOT use up the corpse if it fails.
|
||||
*/
|
||||
struct monst *
|
||||
revive(obj)
|
||||
register struct obj *obj;
|
||||
revive(corpse, by_hero)
|
||||
struct obj *corpse;
|
||||
boolean by_hero;
|
||||
{
|
||||
register struct monst *mtmp = (struct monst *)0;
|
||||
struct permonst *mptr;
|
||||
struct obj *container = (struct obj *)0;
|
||||
int container_nesting = 0;
|
||||
schar savetame = 0;
|
||||
boolean recorporealization = FALSE;
|
||||
boolean in_container = FALSE;
|
||||
struct monst *mtmp = 0;
|
||||
struct permonst *mptr;
|
||||
struct obj *container;
|
||||
coord xy;
|
||||
xchar x, y;
|
||||
int montype, container_nesting = 0;
|
||||
|
||||
if(obj->otyp == CORPSE) {
|
||||
int montype = obj->corpsenm;
|
||||
xchar x, y;
|
||||
if (corpse->otyp != CORPSE) {
|
||||
impossible("Attempting to revive %s?", xname(corpse));
|
||||
return (struct monst *)0;
|
||||
}
|
||||
|
||||
if (obj->where == OBJ_CONTAINED) {
|
||||
/* deal with corpses in [possibly nested] containers */
|
||||
struct monst *carrier;
|
||||
int holder = 0;
|
||||
x = y = 0;
|
||||
if (corpse->where != OBJ_CONTAINED) {
|
||||
/* only for invent, minvent, or floor */
|
||||
container = 0;
|
||||
(void) get_obj_location(corpse, &x, &y, 0);
|
||||
} else {
|
||||
/* deal with corpses in [possibly nested] containers */
|
||||
struct monst *carrier;
|
||||
int holder = OBJ_FREE;
|
||||
|
||||
container = obj->ocontainer;
|
||||
carrier = get_container_location(container, &holder,
|
||||
&container_nesting);
|
||||
switch(holder) {
|
||||
case OBJ_MINVENT:
|
||||
x = carrier->mx; y = carrier->my;
|
||||
in_container = TRUE;
|
||||
break;
|
||||
case OBJ_INVENT:
|
||||
x = u.ux; y = u.uy;
|
||||
in_container = TRUE;
|
||||
break;
|
||||
case OBJ_FLOOR:
|
||||
if (!get_obj_location(obj, &x, &y, CONTAINED_TOO))
|
||||
return (struct monst *) 0;
|
||||
in_container = TRUE;
|
||||
break;
|
||||
default:
|
||||
return (struct monst *)0;
|
||||
}
|
||||
} else {
|
||||
/* only for invent, minvent, or floor */
|
||||
if (!get_obj_location(obj, &x, &y, 0))
|
||||
return (struct monst *) 0;
|
||||
}
|
||||
if (in_container) {
|
||||
/* Rules for revival from containers:
|
||||
- the container cannot be locked
|
||||
- the container cannot be heavily nested (>2 is arbitrary)
|
||||
- the container cannot be a statue or bag of holding
|
||||
(except in very rare cases for the latter)
|
||||
*/
|
||||
if (!x || !y || container->olocked || container_nesting > 2 ||
|
||||
container->otyp == STATUE ||
|
||||
(container->otyp == BAG_OF_HOLDING && rn2(40)))
|
||||
return (struct monst *)0;
|
||||
}
|
||||
|
||||
if (MON_AT(x,y)) {
|
||||
coord new_xy;
|
||||
|
||||
if (enexto(&new_xy, x, y, &mons[montype]))
|
||||
x = new_xy.x, y = new_xy.y;
|
||||
}
|
||||
|
||||
mptr = &mons[montype];
|
||||
if (cant_revive(&montype, TRUE, obj)) {
|
||||
/* make a zombie or doppelganger instead */
|
||||
mtmp = makemon(&mons[montype], x, y,
|
||||
NO_MINVENT | MM_NOWAIT);
|
||||
if (mtmp) {
|
||||
if (mtmp->cham == PM_DOPPELGANGER) {
|
||||
/* change shape to match the corpse */
|
||||
(void) newcham(mtmp, mptr, FALSE, FALSE);
|
||||
} else if (mtmp->data->mlet == S_ZOMBIE) {
|
||||
mtmp->mhp = mtmp->mhpmax = 100;
|
||||
mon_adjust_speed(mtmp, 2, (struct obj *)0); /* MFAST */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (obj->oxlth && (obj->oattached == OATTACHED_MONST)) {
|
||||
coord xy;
|
||||
|
||||
xy.x = x; xy.y = y;
|
||||
mtmp = montraits(obj, &xy);
|
||||
if (mtmp && mtmp->mtame && !mtmp->isminion)
|
||||
wary_dog(mtmp, TRUE);
|
||||
} else
|
||||
mtmp = makemon(&mons[montype], x, y,
|
||||
NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH);
|
||||
if (mtmp) {
|
||||
if (obj->oxlth && (obj->oattached == OATTACHED_M_ID)) {
|
||||
unsigned m_id;
|
||||
struct monst *ghost;
|
||||
(void) memcpy((genericptr_t)&m_id,
|
||||
(genericptr_t)obj->oextra, sizeof(m_id));
|
||||
ghost = find_mid(m_id, FM_FMON);
|
||||
if (ghost && ghost->data == &mons[PM_GHOST]) {
|
||||
int x2, y2;
|
||||
x2 = ghost->mx; y2 = ghost->my;
|
||||
if (ghost->mtame)
|
||||
savetame = ghost->mtame;
|
||||
if (canseemon(ghost))
|
||||
pline("%s is suddenly drawn into its former body!",
|
||||
Monnam(ghost));
|
||||
mondead(ghost);
|
||||
recorporealization = TRUE;
|
||||
newsym(x2, y2);
|
||||
}
|
||||
/* don't mess with obj->oxlth here */
|
||||
obj->oattached = OATTACHED_NOTHING;
|
||||
}
|
||||
/* Monster retains its name */
|
||||
if (obj->onamelth)
|
||||
mtmp = christen_monst(mtmp, ONAME(obj));
|
||||
}
|
||||
}
|
||||
if (mtmp) {
|
||||
if (obj->oeaten)
|
||||
mtmp->mhp = eaten_stat(mtmp->mhp, obj);
|
||||
/* track that this monster was revived at least once */
|
||||
mtmp->mrevived = 1;
|
||||
|
||||
if (recorporealization) {
|
||||
/* If mtmp is revivification of former tame ghost*/
|
||||
if (savetame) {
|
||||
struct monst *mtmp2 = tamedog(mtmp, (struct obj *)0);
|
||||
if (mtmp2) {
|
||||
mtmp2->mtame = savetame;
|
||||
mtmp = mtmp2;
|
||||
}
|
||||
}
|
||||
/* was ghost, now alive, it's all very confusing */
|
||||
mtmp->mconf = 1;
|
||||
}
|
||||
|
||||
switch (obj->where) {
|
||||
case OBJ_INVENT:
|
||||
useup(obj);
|
||||
break;
|
||||
case OBJ_FLOOR:
|
||||
/* in case MON_AT+enexto for invisible mon */
|
||||
x = obj->ox, y = obj->oy;
|
||||
/* not useupf(), which charges */
|
||||
if (obj->quan > 1L)
|
||||
obj = splitobj(obj, 1L);
|
||||
delobj(obj);
|
||||
newsym(x, y);
|
||||
break;
|
||||
case OBJ_MINVENT:
|
||||
m_useup(obj->ocarry, obj);
|
||||
break;
|
||||
case OBJ_CONTAINED:
|
||||
obj_extract_self(obj);
|
||||
obfree(obj, (struct obj *) 0);
|
||||
break;
|
||||
default:
|
||||
panic("revive");
|
||||
}
|
||||
}
|
||||
container = corpse->ocontainer;
|
||||
carrier = get_container_location(container, &holder,
|
||||
&container_nesting);
|
||||
switch (holder) {
|
||||
case OBJ_MINVENT:
|
||||
x = carrier->mx, y = carrier->my;
|
||||
break;
|
||||
case OBJ_INVENT:
|
||||
x = u.ux, y = u.uy;
|
||||
break;
|
||||
case OBJ_FLOOR:
|
||||
(void) get_obj_location(corpse, &x, &y, CONTAINED_TOO);
|
||||
break;
|
||||
default:
|
||||
break; /* x,y are 0 */
|
||||
}
|
||||
return mtmp;
|
||||
}
|
||||
if (!x || !y ||
|
||||
/* Rules for revival from containers:
|
||||
- the container cannot be locked
|
||||
- the container cannot be heavily nested (>2 is arbitrary)
|
||||
- the container cannot be a statue or bag of holding
|
||||
(except in very rare cases for the latter)
|
||||
*/
|
||||
(container &&
|
||||
(container->olocked || container_nesting > 2 ||
|
||||
container->otyp == STATUE ||
|
||||
(container->otyp == BAG_OF_HOLDING && rn2(40)))))
|
||||
return (struct monst *)0;
|
||||
|
||||
/* record the object's location now that we're sure where it is */
|
||||
corpse->ox = x, corpse->oy = y;
|
||||
|
||||
/* prepare for the monster */
|
||||
montype = corpse->corpsenm;
|
||||
mptr = &mons[montype];
|
||||
/* [should probably handle recorporealization first; if corpse and
|
||||
ghost are at same location, revived creature shouldn't be bumped
|
||||
to an adjacent spot by ghost which joins with it] */
|
||||
if (MON_AT(x,y)) {
|
||||
if (enexto(&xy, x, y, mptr))
|
||||
x = xy.x, y = xy.y;
|
||||
}
|
||||
|
||||
if (cant_revive(&montype, TRUE, corpse)) {
|
||||
/* make a zombie or doppelganger instead */
|
||||
/* note: montype has changed; mptr keeps old value for newcham() */
|
||||
mtmp = makemon(&mons[montype], x, y, NO_MINVENT|MM_NOWAIT);
|
||||
if (mtmp) {
|
||||
corpse->oattached = OATTACHED_NOTHING; /* skip ghost handling */
|
||||
if (mtmp->cham == PM_DOPPELGANGER) {
|
||||
/* change shape to match the corpse */
|
||||
(void) newcham(mtmp, mptr, FALSE, FALSE);
|
||||
} else if (mtmp->data->mlet == S_ZOMBIE) {
|
||||
mtmp->mhp = mtmp->mhpmax = 100;
|
||||
mon_adjust_speed(mtmp, 2, (struct obj *)0); /* MFAST */
|
||||
}
|
||||
}
|
||||
} else if (corpse->oxlth && corpse->oattached == OATTACHED_MONST) {
|
||||
/* use saved traits */
|
||||
xy.x = x, xy.y = y;
|
||||
mtmp = montraits(corpse, &xy);
|
||||
if (mtmp && mtmp->mtame && !mtmp->isminion)
|
||||
wary_dog(mtmp, TRUE);
|
||||
} else {
|
||||
/* make a new monster */
|
||||
mtmp = makemon(mptr, x, y, NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH);
|
||||
}
|
||||
if (!mtmp) return (struct monst *)0;
|
||||
|
||||
/* if this is caused by the hero there might be a shop charge */
|
||||
if (by_hero) {
|
||||
struct monst *shkp = 0;
|
||||
|
||||
x = corpse->ox, y = corpse->oy;
|
||||
if (costly_spot(x, y))
|
||||
shkp = shop_keeper(*in_rooms(x, y, SHOPBASE));
|
||||
|
||||
if (cansee(x, y))
|
||||
pline_The("%s glows iridescently.", cxname(corpse));
|
||||
else if (shkp)
|
||||
/* need some prior description of the corpse since
|
||||
stolen_value() will refer to the object as "it" */
|
||||
pline("A corpse is resuscitated.");
|
||||
|
||||
if (shkp)
|
||||
(void) stolen_value(corpse, x, y, (boolean)shkp->mpeaceful, FALSE);
|
||||
|
||||
/* [we don't give any comparable message about the corpse for
|
||||
the !by_hero case because caller might have already done so] */
|
||||
}
|
||||
|
||||
/* handle recorporealization of an active ghost */
|
||||
if (corpse->oxlth && corpse->oattached == OATTACHED_M_ID) {
|
||||
unsigned m_id;
|
||||
struct monst *ghost, *mtmp2;
|
||||
struct obj *otmp;
|
||||
|
||||
(void) memcpy((genericptr_t)&m_id,
|
||||
(genericptr_t)corpse->oextra, sizeof m_id);
|
||||
ghost = find_mid(m_id, FM_FMON);
|
||||
if (ghost && ghost->data == &mons[PM_GHOST]) {
|
||||
if (canseemon(ghost))
|
||||
pline("%s is suddenly drawn into its former body!",
|
||||
Monnam(ghost));
|
||||
/* transfer the ghost's inventory along with it */
|
||||
while ((otmp = ghost->minvent) != 0) {
|
||||
obj_extract_self(otmp);
|
||||
add_to_minv(mtmp, otmp);
|
||||
}
|
||||
/* tame the revived monster if its ghost was tame */
|
||||
if (ghost->mtame && !mtmp->mtame) {
|
||||
mtmp2 = tamedog(mtmp, (struct obj *)0);
|
||||
if (mtmp2) {
|
||||
/* ghost's edog data is ignored */
|
||||
mtmp2->mtame = ghost->mtame;
|
||||
mtmp = mtmp2;
|
||||
}
|
||||
}
|
||||
/* was ghost, now alive, it's all very confusing */
|
||||
mtmp->mconf = 1;
|
||||
/* separate ghost monster no longer exists */
|
||||
mongone(ghost);
|
||||
}
|
||||
corpse->oattached = OATTACHED_NOTHING;
|
||||
}
|
||||
|
||||
/* monster retains its name */
|
||||
if (corpse->onamelth)
|
||||
mtmp = christen_monst(mtmp, ONAME(corpse));
|
||||
/* partially eaten corpse yields wounded monster */
|
||||
if (corpse->oeaten)
|
||||
mtmp->mhp = eaten_stat(mtmp->mhp, corpse);
|
||||
/* track that this monster was revived at least once */
|
||||
mtmp->mrevived = 1;
|
||||
|
||||
/* finally, get rid of the corpse--it's gone now */
|
||||
switch (corpse->where) {
|
||||
case OBJ_INVENT:
|
||||
useup(corpse);
|
||||
break;
|
||||
case OBJ_FLOOR:
|
||||
/* in case MON_AT+enexto for invisible mon */
|
||||
x = corpse->ox, y = corpse->oy;
|
||||
/* not useupf(), which charges */
|
||||
if (corpse->quan > 1L)
|
||||
corpse = splitobj(corpse, 1L);
|
||||
delobj(corpse);
|
||||
newsym(x, y);
|
||||
break;
|
||||
case OBJ_MINVENT:
|
||||
m_useup(corpse->ocarry, corpse);
|
||||
break;
|
||||
case OBJ_CONTAINED:
|
||||
obj_extract_self(corpse);
|
||||
obfree(corpse, (struct obj *)0);
|
||||
break;
|
||||
default:
|
||||
panic("revive");
|
||||
}
|
||||
|
||||
return mtmp;
|
||||
}
|
||||
|
||||
STATIC_OVL void
|
||||
@@ -762,7 +788,7 @@ struct monst *mon;
|
||||
if (youseeit) Strcpy(corpse, corpse_xname(otmp, TRUE));
|
||||
|
||||
/* for a merged group, only one is revived; should this be fixed? */
|
||||
if ((mtmp2 = revive(otmp)) != 0) {
|
||||
if ((mtmp2 = revive(otmp, !context.mon_moving)) != 0) {
|
||||
++res;
|
||||
if (youseeit) {
|
||||
if (!once++) Strcpy(owner,
|
||||
@@ -1577,29 +1603,30 @@ struct obj *obj, *otmp;
|
||||
break;
|
||||
case WAN_UNDEAD_TURNING:
|
||||
case SPE_TURN_UNDEAD:
|
||||
if (obj->otyp == EGG)
|
||||
revive_egg(obj);
|
||||
else {
|
||||
int corpsenm = (obj->otyp == CORPSE) ?
|
||||
corpse_revive_type(obj) : 0;
|
||||
res = !!revive(obj);
|
||||
if (res && corpsenm && Role_if(PM_HEALER)) {
|
||||
boolean u_noticed = FALSE;
|
||||
if (Hallucination && !Deaf) {
|
||||
You_hear("the sound of a defibrillator.");
|
||||
u_noticed = TRUE;
|
||||
} else if (!Blind) {
|
||||
You("observe %s %s change dramatically.",
|
||||
s_suffix(an(mons[corpsenm].mname)),
|
||||
nonliving(&mons[corpsenm]) ?
|
||||
if (obj->otyp == EGG) {
|
||||
revive_egg(obj);
|
||||
} else if (obj->otyp == CORPSE) {
|
||||
int corpsenm = corpse_revive_type(obj);
|
||||
|
||||
res = !!revive(obj, TRUE);
|
||||
if (res && Role_if(PM_HEALER)) {
|
||||
boolean u_noticed = FALSE;
|
||||
|
||||
if (Hallucination && !Deaf) {
|
||||
You_hear("the sound of a defibrillator.");
|
||||
u_noticed = TRUE;
|
||||
} else if (!Blind) {
|
||||
You("observe %s %s change dramatically.",
|
||||
s_suffix(an(mons[corpsenm].mname)),
|
||||
nonliving(&mons[corpsenm]) ?
|
||||
"motility" : "health");
|
||||
u_noticed = TRUE;
|
||||
}
|
||||
if (u_noticed) {
|
||||
makeknown(otmp->otyp);
|
||||
exercise(A_WIS, TRUE);
|
||||
}
|
||||
u_noticed = TRUE;
|
||||
}
|
||||
if (u_noticed) {
|
||||
makeknown(otmp->otyp);
|
||||
exercise(A_WIS, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WAN_OPENING:
|
||||
|
||||
Reference in New Issue
Block a user