E void FDECL(do_genocide, (int));
E void FDECL(punish, (struct obj *));
E void NDECL(unpunish);
-E boolean FDECL(cant_create, (int *, BOOLEAN_P));
+E boolean FDECL(cant_revive, (int *,BOOLEAN_P,struct obj *));
#ifdef WIZARD
E boolean NDECL(create_particular);
#endif
-/* SCCS Id: @(#)mondata.h 3.4 2003/01/08 */
+/* SCCS Id: @(#)mondata.h 3.4 2003/11/29 */
/* Copyright (c) 1989 Mike Threepoint */
/* NetHack may be freely redistributed. See license for details. */
(ptr) == &mons[PM_HUMAN])
/* return TRUE if the monster tends to revive */
#define is_reviver(ptr) (is_rider(ptr) || (ptr)->mlet == S_TROLL)
+/* monsters whose corpses and statues need special handling;
+ note that high priests and the Wizard of Yendor are flagged
+ as unique even though they really aren't; that's ok here */
+#define unique_corpstat(ptr) (((ptr)->geno & G_UNIQ) != 0)
/* this returns the light's range, or 0 if none; if we add more light emitting
monsters, we'll likely have to add a new light range field to mons[] */
-/* SCCS Id: @(#)bones.c 3.4 2003/09/06 */
+/* SCCS Id: @(#)bones.c 3.4 2003/11/29 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */
/* NetHack may be freely redistributed. See license for details. */
if (otmp->cobj)
resetobjs(otmp->cobj,restore);
- if (((otmp->otyp != CORPSE || otmp->corpsenm < SPECIAL_PM)
- && otmp->otyp != STATUE)
- && (!otmp->oartifact ||
- (restore && (exist_artifact(otmp->otyp, ONAME(otmp))
- || is_quest_artifact(otmp))))) {
- otmp->oartifact = 0;
- otmp->onamelth = 0;
- *ONAME(otmp) = '\0';
- } else if (otmp->oartifact && restore)
- artifact_exists(otmp,ONAME(otmp),TRUE);
- if (!restore) {
+ if (restore) {
+ /* artifact bookeeping needs to be done during
+ restore; other fixups are done while saving */
+ if (otmp->oartifact) {
+ if (exist_artifact(otmp->otyp, ONAME(otmp)) ||
+ is_quest_artifact(otmp)) {
+ /* prevent duplicate--revert to ordinary obj */
+ otmp->oartifact = 0;
+ otmp->onamelth = 0;
+ *ONAME(otmp) = '\0';
+ } else {
+ artifact_exists(otmp, ONAME(otmp), TRUE);
+ }
+ }
+ } else { /* saving */
/* do not zero out o_ids for ghost levels anymore */
if(objects[otmp->otyp].oc_uses_known) otmp->known = 0;
otmp->invlet = 0;
otmp->no_charge = 0;
+ /* strip user-supplied names */
+ /* Statue and some corpse names are left intact,
+ presumeably in case they came from score file.
+ [TODO: this ought to be done differently--names
+ which came from such a source or came from any
+ stoned or killed monster should be flagged in
+ some manner; then we could just check the flag
+ here and keep "real" names (dead pets, &c) while
+ discarding player notes attached to statues.] */
+ if (otmp->onamelth &&
+ !(otmp->oartifact || otmp->otyp == STATUE ||
+ (otmp->otyp == CORPSE &&
+ otmp->corpsenm >= SPECIAL_PM))) {
+ otmp->onamelth = 0;
+ *ONAME(otmp) = '\0';
+ }
+
if (otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe);
#ifdef MAIL
else if (otmp->otyp == SCR_MAIL) otmp->spe = 1;
else if (otmp->otyp == TIN) {
/* make tins of unique monster's meat be empty */
if (otmp->corpsenm >= LOW_PM &&
- (mons[otmp->corpsenm].geno & G_UNIQ))
+ unique_corpstat(&mons[otmp->corpsenm]))
otmp->corpsenm = NON_PM;
+ } else if (otmp->otyp == CORPSE ||
+ otmp->otyp == STATUE) {
+ int mnum = otmp->corpsenm;
+
+ /* Discard incarnation details of unique
+ monsters (by passing null instead of otmp
+ for object), shopkeepers (by passing false
+ for revival flag), temple priests, and
+ vault guards in order to prevent corpse
+ revival or statue reanimation. */
+ if (otmp->oattached == OATTACHED_MONST &&
+ cant_revive(&mnum, FALSE, (struct obj *)0))
+ otmp->oattached = OATTACHED_NOTHING;
} else if (otmp->otyp == AMULET_OF_YENDOR) {
/* no longer the real Amulet */
otmp->otyp = FAKE_AMULET_OF_YENDOR;
-/* SCCS Id: @(#)makemon.c 3.4 2003/09/06 */
+/* SCCS Id: @(#)makemon.c 3.4 2003/11/30 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
mtmp->m_id = context.ident++;
if (!mtmp->m_id) mtmp->m_id = context.ident++; /* ident overflowed */
set_mon_data(mtmp, ptr, 0);
- if (mtmp->data->msound == MS_LEADER)
+ if (mtmp->data->msound == MS_LEADER &&
+ quest_info(MS_LEADER) == mndx)
quest_status.leader_m_id = mtmp->m_id;
mtmp->mxlth = xlth;
mtmp->mnum = mndx;
LS_MONSTER, (genericptr_t)mtmp);
mitem = 0; /* extra inventory item for this monster */
+ mtmp->cham = CHAM_ORDINARY; /* default is "not a shapechanger" */
if ((mcham = pm_to_cham(mndx)) != CHAM_ORDINARY) {
- /* If you're protected with a ring, don't create
- * any shape-changing chameleons -dgk
- */
- if (Protection_from_shape_changers)
- mtmp->cham = CHAM_ORDINARY;
- else {
+ /* this is a shapechanger after all */
+ if (Protection_from_shape_changers) {
+ ; /* stuck in its natural form (CHAM_ORDINARY) */
+ } else {
+ /* new shapechanger starts out with random form
+ (this explicitly picks something from the normal
+ selection for current difficulty level rather
+ than from among shapechanger's preferred forms) */
mtmp->cham = mcham;
(void) newcham(mtmp, rndmonst(), FALSE, FALSE);
}
PM_SANDESTIN,
};
-#define KEEPTRAITS(mon) (mon->isshk || mon->mtame || \
- (mon->data->geno & G_UNIQ) || is_reviver(mon->data) || \
- (mon->m_id == quest_status.leader_m_id))
+/* for deciding whether corpse will carry along full monster data */
+#define KEEPTRAITS(mon) ((mon)->isshk || (mon)->mtame || \
+ unique_corpstat(mon->data) || \
+ is_reviver((mon)->data) || \
+ /* normally leader the will be unique, */ \
+ /* but he might have been polymorphed */ \
+ (mon)->m_id == quest_status.leader_m_id || \
+ /* special cancellation handling for these */ \
+ (dmgtype((mon)->data, AD_SEDU) || \
+ dmgtype((mon)->data, AD_SSEX)))
/* Creates a monster corpse, a "special" corpse, or nothing if it doesn't
* leave corpses. Monsters which leave "special" corpses should have
-/* SCCS Id: @(#)read.c 3.4 2003/10/22 */
+/* SCCS Id: @(#)read.c 3.4 2003/11/29 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
* one, the disoriented creature becomes a zombie
*/
boolean
-cant_create(mtype, revival)
+cant_revive(mtype, revival, from_obj)
int *mtype;
boolean revival;
+struct obj *from_obj;
{
/* SHOPKEEPERS can be revived now */
} else if (*mtype==PM_LONG_WORM_TAIL) { /* for create_particular() */
*mtype = PM_LONG_WORM;
return TRUE;
+ } else if (from_obj && unique_corpstat(&mons[*mtype]) &&
+ from_obj->oattached != OATTACHED_MONST) {
+ /* unique corpses (from bones or wizard mode wish) or
+ statues (bones or any wish) end up as shapechangers */
+ *mtype = PM_DOPPELGANGER;
+ return TRUE;
}
return FALSE;
}
pline(thats_enough_tries);
} else {
if (!randmonst) {
- (void) cant_create(&which, FALSE);
+ (void) cant_revive(&which, FALSE, (struct obj *)0);
whichpm = &mons[which];
}
for (i = 0; i <= multi; i++) {
int cause;
int *fail_reason;
{
- struct permonst *mptr;
+ int mnum = statue->corpsenm;
+ struct permonst *mptr = &mons[mnum];
struct monst *mon = 0;
struct obj *item;
coord cc;
- boolean historic = (Role_if(PM_ARCHEOLOGIST) && !context.mon_moving && (statue->spe & STATUE_HISTORIC));
+ boolean historic = (Role_if(PM_ARCHEOLOGIST) && !context.mon_moving &&
+ (statue->spe & STATUE_HISTORIC)),
+ use_saved_traits;
char statuename[BUFSZ];
Strcpy(statuename,the(xname(statue)));
- if (statue->oxlth && statue->oattached == OATTACHED_MONST) {
+ if (cant_revive(&mnum, TRUE, statue)) {
+ /* mnum has changed; we won't be animating this statue as itself */
+ if (mnum != PM_DOPPELGANGER) mptr = &mons[mnum];
+ use_saved_traits = FALSE;
+ } else if (is_golem(mptr) && cause == ANIMATE_SPELL) {
+ /* statue of any golem hit by stone-to-flesh becomes flesh golem */
+ mnum = PM_FLESH_GOLEM;
+ mptr = &mons[PM_FLESH_GOLEM];
+ use_saved_traits = FALSE;
+ } else {
+ use_saved_traits = (statue->oxlth &&
+ statue->oattached == OATTACHED_MONST);
+ }
+
+ if (use_saved_traits) {
+ /* restore a petrified monster */
cc.x = x, cc.y = y;
mon = montraits(statue, &cc);
- if (mon && mon->mtame && !mon->isminion)
- wary_dog(mon, TRUE);
- } else {
- /* statue of any golem hit with stone-to-flesh becomes flesh golem */
- if (is_golem(&mons[statue->corpsenm]) && cause == ANIMATE_SPELL)
- mptr = &mons[PM_FLESH_GOLEM];
- else
- mptr = &mons[statue->corpsenm];
- /*
- * Guard against someone wishing for a statue of a unique monster
- * (which is allowed in normal play) and then tossing it onto the
- * [detected or guessed] location of a statue trap. Normally the
- * uppermost statue is the one which would be activated.
- */
- if ((mptr->geno & G_UNIQ) && cause != ANIMATE_SPELL) {
- if (fail_reason) *fail_reason = AS_MON_IS_UNIQUE;
- return (struct monst *)0;
+ if (mon) {
+ if (mon->mtame && !mon->isminion)
+ wary_dog(mon, TRUE);
+ /* might be bringing quest leader back to life */
+ if (quest_status.leader_is_dead &&
+ /* leader_is_dead implies that leader_m_id is valid */
+ mon->m_id == quest_status.leader_m_id)
+ quest_status.leader_is_dead = FALSE;
}
- if (cause == ANIMATE_SPELL &&
- ((mptr->geno & G_UNIQ) || mptr->msound == MS_GUARDIAN)) {
- /* Statues of quest guardians or unique monsters
- * will not stone-to-flesh as the real thing.
- */
+ } else {
+ /* statues of unique monsters from bones or wishing end
+ up here (cant_revive() sets mnum to be doppelganger;
+ mptr reflects the original form for use by newcham()) */
+ if ((mnum == PM_DOPPELGANGER &&
+ mptr != &mons[PM_DOPPELGANGER]) ||
+ /* block quest guards from other roles */
+ (mptr->msound == MS_GUARDIAN &&
+ quest_info(MS_GUARDIAN) != mnum)) {
mon = makemon(&mons[PM_DOPPELGANGER], x, y,
- NO_MINVENT|MM_NOCOUNTBIRTH|MM_ADJACENTOK);
- if (mon) {
- /* makemon() will set mon->cham to
- * CHAM_ORDINARY if hero is wearing
- * ring of protection from shape changers
- * when makemon() is called, so we have to
- * check the field before calling newcham().
- */
- if (mon->cham == CHAM_DOPPELGANGER)
- (void) newcham(mon, mptr, FALSE, FALSE);
- }
+ NO_MINVENT | MM_NOCOUNTBIRTH | MM_ADJACENTOK);
+ /* if hero has protection from shape changers, cham field will
+ be CHAM_ORDINARY; otherwise, set form to match the statue */
+ if (mon && mon->cham != CHAM_ORDINARY)
+ (void) newcham(mon, mptr, FALSE, FALSE);
} else
mon = makemon(mptr, x, y, (cause == ANIMATE_SPELL) ?
- (NO_MINVENT | MM_ADJACENTOK) : NO_MINVENT);
+ (NO_MINVENT | MM_ADJACENTOK) : NO_MINVENT);
}
if (!mon) {
- if (fail_reason) *fail_reason = AS_NO_MON;
+ if (fail_reason)
+ *fail_reason = unique_corpstat(&mons[statue->corpsenm]) ?
+ AS_MON_IS_UNIQUE : AS_NO_MON;
return (struct monst *)0;
}
/* mimic statue becomes seen mimic; other hiders won't be hidden */
if (mon->m_ap_type) seemimic(mon);
else mon->mundetected = FALSE;
+ /* when reanimating a stoned monster, protection from shape changers
+ might be different now than it was when the monster was petrified */
+ if (use_saved_traits) restore_cham(mon);
+
if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) {
const char *comes_to_life = nonliving(mon->data) ?
"moves" : "comes to life";
register struct obj *obj;
{
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;
+
if(obj->otyp == CORPSE) {
int montype = obj->corpsenm;
xchar x, y;
x = new_xy.x, y = new_xy.y;
}
- if(cant_create(&montype, TRUE)) {
- /* make a zombie or worm instead */
- mtmp = makemon(&mons[montype], x, y,
- NO_MINVENT|MM_NOWAIT);
- if (mtmp) {
- mtmp->mhp = mtmp->mhpmax = 100;
- mon_adjust_speed(mtmp, 2, (struct obj *)0); /* MFAST */
+ 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 == CHAM_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)
+ coord xy;
+
+ xy.x = x; xy.y = y;
+ mtmp = montraits(obj, &xy);
+ if (mtmp) {
+ if (mtmp->mtame && !mtmp->isminion)
wary_dog(mtmp, TRUE);
+ /* might be reviving quest leader */
+ if (quest_status.leader_is_dead &&
+ /* _is_dead implies that _m_id is valid */
+ mtmp->m_id == quest_status.leader_m_id)
+ quest_status.leader_is_dead = FALSE;
+ }
} else
- mtmp = makemon(&mons[montype], x, y,
+ mtmp = makemon(&mons[montype], x, y,
NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH);
if (mtmp) {
if (obj->oxlth && (obj->oattached == OATTACHED_M_ID)) {
/* Monster retains its name */
if (obj->onamelth)
mtmp = christen_monst(mtmp, ONAME(obj));
- /* flag the quest leader as alive. */
- if (mtmp->data->msound == MS_LEADER || mtmp->m_id ==
- quest_status.leader_m_id)
- quest_status.leader_is_dead = FALSE;
}
}
if (mtmp) {
mtmp->mhp = eaten_stat(mtmp->mhp, obj);
/* track that this monster was revived at least once */
mtmp->mrevived = 1;
+ /* in case this was a shapechanger corpse and
+ Protection_from_shape_changers happens to be
+ different now than it was when the monster
+ was killed (probably a no-op here since
+ KEEPTRAITS() doesn't include shapechangers) */
+ restore_cham(mtmp);
if (recorporealization) {
/* If mtmp is revivification of former tame ghost*/