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
|
the item properly, possibly leading to various strange behaviors
|
||||||
avoid a panic splitbill when shopkeeper is trapped by the door
|
avoid a panic splitbill when shopkeeper is trapped by the door
|
||||||
grammar tidbit for message given when eating tainted meat is also cannibalism
|
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
|
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 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(get_container_location, (struct obj *obj, int *, int *));
|
||||||
E struct monst *FDECL(montraits, (struct obj *,coord *));
|
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 int FDECL(unturn_dead, (struct monst *));
|
||||||
E void FDECL(cancel_item, (struct obj *));
|
E void FDECL(cancel_item, (struct obj *));
|
||||||
E boolean FDECL(drain_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. */
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||||
/* NetHack may be freely redistributed. See license for details. */
|
/* 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 */
|
/* container_where is the outermost container's location even if nested */
|
||||||
if (container_where == OBJ_MINVENT && mtmp2) mcarry = mtmp2;
|
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) {
|
if (mtmp) {
|
||||||
chewed = (mtmp->mhp < mtmp->mhpmax);
|
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. */
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||||
/* NetHack may be freely redistributed. See license for details. */
|
/* NetHack may be freely redistributed. See license for details. */
|
||||||
|
|
||||||
@@ -403,7 +403,7 @@ int mm_flags;
|
|||||||
if (enexto(&cc, mm->x, mm->y, mdat) &&
|
if (enexto(&cc, mm->x, mm->y, mdat) &&
|
||||||
(!revive_corpses ||
|
(!revive_corpses ||
|
||||||
!(otmp = sobj_at(CORPSE, cc.x, cc.y)) ||
|
!(otmp = sobj_at(CORPSE, cc.x, cc.y)) ||
|
||||||
!revive(otmp)))
|
!revive(otmp, FALSE)))
|
||||||
(void) makemon(mdat, cc.x, cc.y, mm_flags);
|
(void) makemon(mdat, cc.x, cc.y, mm_flags);
|
||||||
}
|
}
|
||||||
level.flags.graveyard = TRUE; /* reduced chance for undead corpse */
|
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. */
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||||
/* NetHack may be freely redistributed. See license for details. */
|
/* 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
|
* Perhaps x, y is not needed if we can use get_obj_location() to find
|
||||||
* the statue's location... ???
|
* 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 *
|
struct monst *
|
||||||
animate_statue(statue, x, y, cause, fail_reason)
|
animate_statue(statue, x, y, cause, fail_reason)
|
||||||
@@ -392,15 +404,16 @@ int *fail_reason;
|
|||||||
{
|
{
|
||||||
int mnum = statue->corpsenm;
|
int mnum = statue->corpsenm;
|
||||||
struct permonst *mptr = &mons[mnum];
|
struct permonst *mptr = &mons[mnum];
|
||||||
struct monst *mon = 0;
|
struct monst *mon = 0, *shkp;
|
||||||
struct obj *item;
|
struct obj *item;
|
||||||
coord cc;
|
coord cc;
|
||||||
boolean historic = (Role_if(PM_ARCHEOLOGIST) && !context.mon_moving &&
|
boolean historic = (Role_if(PM_ARCHEOLOGIST) &&
|
||||||
(statue->spe & STATUE_HISTORIC)),
|
(statue->spe & STATUE_HISTORIC) != 0),
|
||||||
use_saved_traits;
|
use_saved_traits;
|
||||||
char statuename[BUFSZ];
|
const char *comes_to_life;
|
||||||
|
char statuename[BUFSZ], tmpbuf[BUFSZ];
|
||||||
Strcpy(statuename,the(xname(statue)));
|
static const char historic_statue_is_gone[] =
|
||||||
|
"that the historic statue is now gone";
|
||||||
|
|
||||||
if (cant_revive(&mnum, TRUE, statue)) {
|
if (cant_revive(&mnum, TRUE, statue)) {
|
||||||
/* mnum has changed; we won't be animating this statue as itself */
|
/* mnum has changed; we won't be animating this statue as itself */
|
||||||
@@ -449,10 +462,7 @@ int *fail_reason;
|
|||||||
return (struct monst *)0;
|
return (struct monst *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* in case statue is wielded and hero zaps stone-to-flesh at self */
|
/* a non-montraits() statue might specify gender */
|
||||||
if (statue->owornmask) remove_worn_item(statue, TRUE);
|
|
||||||
|
|
||||||
/* allow statues to be of a specific gender */
|
|
||||||
if (statue->spe & STATUE_MALE)
|
if (statue->spe & STATUE_MALE)
|
||||||
mon->female = FALSE;
|
mon->female = FALSE;
|
||||||
else if (statue->spe & STATUE_FEMALE)
|
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 has been named, give same name to the monster */
|
||||||
if (statue->onamelth)
|
if (statue->onamelth)
|
||||||
mon = christen_monst(mon, ONAME(statue));
|
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 */
|
/* transfer any statue contents to monster's inventory */
|
||||||
while ((item = statue->cobj) != 0) {
|
while ((item = statue->cobj) != 0) {
|
||||||
obj_extract_self(item);
|
obj_extract_self(item);
|
||||||
(void) add_to_minv(mon, item);
|
(void) add_to_minv(mon, item);
|
||||||
}
|
}
|
||||||
m_dowear(mon, TRUE);
|
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);
|
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 */
|
/* avoid hiding under nothing */
|
||||||
if (x == u.ux && y == u.uy &&
|
if (x == u.ux && y == u.uy &&
|
||||||
Upolyd && hides_under(youmonst.data) && !OBJ_AT(x, y))
|
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. */
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
||||||
/* NetHack may be freely redistributed. See license for details. */
|
/* 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.
|
* successful. Note: this does NOT use up the corpse if it fails.
|
||||||
*/
|
*/
|
||||||
struct monst *
|
struct monst *
|
||||||
revive(obj)
|
revive(corpse, by_hero)
|
||||||
register struct obj *obj;
|
struct obj *corpse;
|
||||||
|
boolean by_hero;
|
||||||
{
|
{
|
||||||
register struct monst *mtmp = (struct monst *)0;
|
struct monst *mtmp = 0;
|
||||||
struct permonst *mptr;
|
struct permonst *mptr;
|
||||||
struct obj *container = (struct obj *)0;
|
struct obj *container;
|
||||||
int container_nesting = 0;
|
coord xy;
|
||||||
schar savetame = 0;
|
xchar x, y;
|
||||||
boolean recorporealization = FALSE;
|
int montype, container_nesting = 0;
|
||||||
boolean in_container = FALSE;
|
|
||||||
|
|
||||||
if(obj->otyp == CORPSE) {
|
if (corpse->otyp != CORPSE) {
|
||||||
int montype = obj->corpsenm;
|
impossible("Attempting to revive %s?", xname(corpse));
|
||||||
xchar x, y;
|
return (struct monst *)0;
|
||||||
|
}
|
||||||
|
|
||||||
if (obj->where == OBJ_CONTAINED) {
|
x = y = 0;
|
||||||
/* deal with corpses in [possibly nested] containers */
|
if (corpse->where != OBJ_CONTAINED) {
|
||||||
struct monst *carrier;
|
/* only for invent, minvent, or floor */
|
||||||
int holder = 0;
|
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;
|
container = corpse->ocontainer;
|
||||||
carrier = get_container_location(container, &holder,
|
carrier = get_container_location(container, &holder,
|
||||||
&container_nesting);
|
&container_nesting);
|
||||||
switch(holder) {
|
switch (holder) {
|
||||||
case OBJ_MINVENT:
|
case OBJ_MINVENT:
|
||||||
x = carrier->mx; y = carrier->my;
|
x = carrier->mx, y = carrier->my;
|
||||||
in_container = TRUE;
|
break;
|
||||||
break;
|
case OBJ_INVENT:
|
||||||
case OBJ_INVENT:
|
x = u.ux, y = u.uy;
|
||||||
x = u.ux; y = u.uy;
|
break;
|
||||||
in_container = TRUE;
|
case OBJ_FLOOR:
|
||||||
break;
|
(void) get_obj_location(corpse, &x, &y, CONTAINED_TOO);
|
||||||
case OBJ_FLOOR:
|
break;
|
||||||
if (!get_obj_location(obj, &x, &y, CONTAINED_TOO))
|
default:
|
||||||
return (struct monst *) 0;
|
break; /* x,y are 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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
|
STATIC_OVL void
|
||||||
@@ -762,7 +788,7 @@ struct monst *mon;
|
|||||||
if (youseeit) Strcpy(corpse, corpse_xname(otmp, TRUE));
|
if (youseeit) Strcpy(corpse, corpse_xname(otmp, TRUE));
|
||||||
|
|
||||||
/* for a merged group, only one is revived; should this be fixed? */
|
/* 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;
|
++res;
|
||||||
if (youseeit) {
|
if (youseeit) {
|
||||||
if (!once++) Strcpy(owner,
|
if (!once++) Strcpy(owner,
|
||||||
@@ -1577,29 +1603,30 @@ struct obj *obj, *otmp;
|
|||||||
break;
|
break;
|
||||||
case WAN_UNDEAD_TURNING:
|
case WAN_UNDEAD_TURNING:
|
||||||
case SPE_TURN_UNDEAD:
|
case SPE_TURN_UNDEAD:
|
||||||
if (obj->otyp == EGG)
|
if (obj->otyp == EGG) {
|
||||||
revive_egg(obj);
|
revive_egg(obj);
|
||||||
else {
|
} else if (obj->otyp == CORPSE) {
|
||||||
int corpsenm = (obj->otyp == CORPSE) ?
|
int corpsenm = corpse_revive_type(obj);
|
||||||
corpse_revive_type(obj) : 0;
|
|
||||||
res = !!revive(obj);
|
res = !!revive(obj, TRUE);
|
||||||
if (res && corpsenm && Role_if(PM_HEALER)) {
|
if (res && Role_if(PM_HEALER)) {
|
||||||
boolean u_noticed = FALSE;
|
boolean u_noticed = FALSE;
|
||||||
if (Hallucination && !Deaf) {
|
|
||||||
You_hear("the sound of a defibrillator.");
|
if (Hallucination && !Deaf) {
|
||||||
u_noticed = TRUE;
|
You_hear("the sound of a defibrillator.");
|
||||||
} else if (!Blind) {
|
u_noticed = TRUE;
|
||||||
You("observe %s %s change dramatically.",
|
} else if (!Blind) {
|
||||||
s_suffix(an(mons[corpsenm].mname)),
|
You("observe %s %s change dramatically.",
|
||||||
nonliving(&mons[corpsenm]) ?
|
s_suffix(an(mons[corpsenm].mname)),
|
||||||
|
nonliving(&mons[corpsenm]) ?
|
||||||
"motility" : "health");
|
"motility" : "health");
|
||||||
u_noticed = TRUE;
|
u_noticed = TRUE;
|
||||||
}
|
|
||||||
if (u_noticed) {
|
|
||||||
makeknown(otmp->otyp);
|
|
||||||
exercise(A_WIS, TRUE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (u_noticed) {
|
||||||
|
makeknown(otmp->otyp);
|
||||||
|
exercise(A_WIS, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WAN_OPENING:
|
case WAN_OPENING:
|
||||||
|
|||||||
Reference in New Issue
Block a user