stone-to-flesh vs golem statues and figurines (trunk only)

Back in <email deleted> in #U1216
suggested that statues of stone golems which are hit by stone-to-flesh
spell should leave flesh golem corpses instead of meatballs.  But they
should actually have revived as flesh golems.  Stone-to-flesh revival of
golem statues and golem figurines didn't work as intended because golems
other than flesh or leather were considered to be vegan/vegatarian, which
caused them to produce meat rather than living monsters.  Also, S-to-F of
a leather golem statue worked as intended and created a flesh golem, but
S-to-F of a leather golem firugine created a leather golem out of stone
object.  This fixes it so that any type of golem statue or golem figurine
revives as a flesh golem.

     Two other bugs noticed and fixed:  (1) S-to-F cast on self while a
figurine of a non-veggy monster was "worn" in one of the three weapon
slots triggered an "extract_nobj: object lost" panic similar to several
similar cases which were recently fixed (that was 3.4.3; for development
code, it gave "obfree: deleting worn obj" warning instead).  (2) S-to-F
activating a shop-owned figurine didn't charge for it.
This commit is contained in:
nethack.rankin
2007-04-01 03:32:53 +00:00
parent e91ff47ff0
commit d8a0f73481
3 changed files with 129 additions and 101 deletions

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)trap.c 3.5 2007/02/10 */
/* SCCS Id: @(#)trap.c 3.5 2007/03/30 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -458,7 +458,7 @@ int *fail_reason;
coord cc;
boolean historic = (Role_if(PM_ARCHEOLOGIST) &&
(statue->spe & STATUE_HISTORIC) != 0),
use_saved_traits;
golem_xform = FALSE, use_saved_traits;
const char *comes_to_life;
char statuename[BUFSZ], tmpbuf[BUFSZ];
static const char historic_statue_is_gone[] =
@@ -470,9 +470,10 @@ int *fail_reason;
use_saved_traits = FALSE;
} else if (is_golem(mptr) && cause == ANIMATE_SPELL) {
/* statue of any golem hit by stone-to-flesh becomes flesh golem */
golem_xform = (mptr != &mons[PM_FLESH_GOLEM]);
mnum = PM_FLESH_GOLEM;
mptr = &mons[PM_FLESH_GOLEM];
use_saved_traits = FALSE;
use_saved_traits = (has_omonst(statue) && !golem_xform);
} else {
use_saved_traits = has_omonst(statue);
}
@@ -530,6 +531,7 @@ int *fail_reason;
}
comes_to_life = !canspotmon(mon) ? "disappears" :
golem_xform ? "turns into flesh" :
(nonliving(mon->data) || is_vampshifter(mon)) ?
"moves" : "comes to life";
if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) {

220
src/zap.c
View File

@@ -21,6 +21,7 @@ extern boolean m_using;
STATIC_DCL void FDECL(polyuse, (struct obj *,int,int));
STATIC_DCL void FDECL(create_polymon, (struct obj *,int));
STATIC_DCL int FDECL(stone_to_flesh_obj, (struct obj *));
STATIC_DCL boolean FDECL(zap_updown, (struct obj *));
STATIC_DCL int FDECL(zhitm, (struct monst *,int,int,struct obj **));
STATIC_DCL void FDECL(zhitu, (int,int,const char *,XCHAR_P,XCHAR_P));
@@ -1475,7 +1476,6 @@ poly_obj(obj, id)
/* ** we are now done adjusting the object ** */
/* swap otmp for obj */
replace_object(obj, otmp);
if (obj_location == OBJ_INVENT) {
@@ -1524,6 +1524,121 @@ poly_obj(obj, id)
return otmp;
}
/* stone-to-flesh spell hits and maybe transforms or animates obj */
STATIC_OVL int
stone_to_flesh_obj(obj)
struct obj *obj;
{
int res = 1; /* affected object by default */
struct permonst *ptr;
struct monst *mon;
struct obj *item;
xchar oox, ooy;
boolean smell = FALSE, golem_xform = FALSE;
if (objects[obj->otyp].oc_material != MINERAL &&
objects[obj->otyp].oc_material != GEMSTONE) return 0;
(void) get_obj_location(obj, &oox, &ooy, 0);
/* add more if stone objects are added.. */
switch (objects[obj->otyp].oc_class) {
case ROCK_CLASS: /* boulders and statues */
case TOOL_CLASS: /* figurines */
if (obj->otyp == BOULDER) {
obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT);
smell = TRUE;
} else if (obj->otyp == STATUE || obj->otyp == FIGURINE) {
ptr = &mons[obj->corpsenm];
if (is_golem(ptr)) {
golem_xform = (ptr != &mons[PM_FLESH_GOLEM]);
} else if (vegetarian(ptr)) {
/* Don't animate monsters that aren't flesh */
obj = poly_obj(obj, MEATBALL);
smell = TRUE;
break;
}
if (obj->otyp == STATUE) {
/* animate_statue() forces all golems to become flesh golems */
mon = animate_statue(obj, oox, ooy, ANIMATE_SPELL, (int *)0);
} else { /* (obj->otyp == FIGURINE) */
if (golem_xform) ptr = &mons[PM_FLESH_GOLEM];
mon = makemon(ptr, oox, ooy, NO_MINVENT);
if (mon) {
if (costly_spot(oox, ooy) && !obj->no_charge) {
if (costly_spot(u.ux, u.uy))
addtobill(obj, carried(obj), FALSE, FALSE);
else
stolen_value(obj, oox, ooy, TRUE, FALSE);
}
if (obj->timed) obj_stop_timers(obj);
if (carried(obj))
useup(obj);
else
delobj(obj);
if (cansee(mon->mx, mon->my))
pline_The("figurine %sanimates!",
golem_xform ? "turns to flesh and " : "");
}
}
if (mon) {
ptr = mon->data;
/* this golem handling is redundant... */
if (is_golem(ptr) && ptr != &mons[PM_FLESH_GOLEM])
(void)newcham(mon, &mons[PM_FLESH_GOLEM], TRUE, FALSE);
} else if ((ptr->geno & (G_NOCORPSE|G_UNIQ)) != 0) {
/* didn't revive but can't leave corpse either */
res = 0;
} else {
/* unlikely to get here since genociding monsters also
sets the G_NOCORPSE flag; drop statue's contents */
while ((item = obj->cobj) != 0) {
bypass_obj(item); /* make stone-to-flesh miss it */
obj_extract_self(item);
place_object(item, oox, ooy);
}
obj = poly_obj(obj, CORPSE);
}
} else { /* miscellaneous tool or unexpected rock... */
res = 0;
}
break;
/* maybe add weird things to become? */
case RING_CLASS: /* some of the rings are stone */
obj = poly_obj(obj, MEAT_RING);
smell = TRUE;
break;
case WAND_CLASS: /* marble wand */
obj = poly_obj(obj, MEAT_STICK);
smell = TRUE;
break;
case GEM_CLASS: /* stones & gems */
obj = poly_obj(obj, MEATBALL);
smell = TRUE;
break;
case WEAPON_CLASS: /* crysknife */
/* fall through */
default:
res = 0;
break;
}
if (smell) {
/* non-meat eaters smell meat, meat eaters smell its flavor;
monks are considered non-meat eaters regardless of behavior;
other roles are non-meat eaters if they haven't broken
vegetarian conduct yet (or if poly'd into non-carnivorous/
non-omnivorous form, regardless of whether it's herbivorous,
non-eating, or something stranger) */
if (Role_if(PM_MONK) || !u.uconduct.unvegetarian ||
!carnivorous(youmonst.data))
Norep("You smell the odor of meat.");
else
Norep("You smell a delicious smell.");
}
newsym(oox, ooy);
return res;
}
/*
* Object obj was hit by the effect of the wand/spell otmp. Return
* non-zero if the wand/spell had any effect.
@@ -1534,7 +1649,6 @@ struct obj *obj, *otmp;
{
int res = 1; /* affected object by default */
boolean learn_it = FALSE, maybelearnit;
xchar refresh_x, refresh_y;
/* fundamental: a wand effect hitting itself doesn't do anything;
otherwise we need to guard against accessing otmp after something
@@ -1558,6 +1672,11 @@ struct obj *obj, *otmp;
* UNDEAD_TURNING - When an undead creature gets killed via
* undead turning, prevent its corpse from being
* immediately revived by the same effect.
* STONE_TO_FLESH - If a statue can't be revived, its
* contents get dropped before turning it into
* meat; prevent those contents from being hit.
* retouch_equipment() - bypass flag is used to track which
* items have been handled (bhito isn't involved).
*
* The bypass bit on all objects is reset each turn, whenever
* context.bypasses is set.
@@ -1736,102 +1855,7 @@ struct obj *obj, *otmp;
res = 0;
break;
case SPE_STONE_TO_FLESH:
refresh_x = obj->ox; refresh_y = obj->oy;
if (objects[obj->otyp].oc_material != MINERAL &&
objects[obj->otyp].oc_material != GEMSTONE) {
res = 0;
break;
}
/* add more if stone objects are added.. */
switch (objects[obj->otyp].oc_class) {
case ROCK_CLASS: /* boulders and statues */
if (obj->otyp == BOULDER) {
obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT);
goto smell;
} else if (obj->otyp == STATUE) {
xchar oox, ooy;
(void) get_obj_location(obj, &oox, &ooy, 0);
refresh_x = oox; refresh_y = ooy;
if (vegetarian(&mons[obj->corpsenm])) {
/* Don't animate monsters that aren't flesh */
obj = poly_obj(obj, MEATBALL);
goto smell;
}
if (!animate_statue(obj, oox, ooy,
ANIMATE_SPELL, (int *)0)) {
struct obj *item;
makecorpse: if (mons[obj->corpsenm].geno &
(G_NOCORPSE|G_UNIQ)) {
res = 0;
break;
}
/* Unlikely to get here since genociding
* monsters also sets the G_NOCORPSE flag.
* Drop the contents, poly_obj looses them.
*/
while ((item = obj->cobj) != 0) {
obj_extract_self(item);
place_object(item, oox, ooy);
}
obj = poly_obj(obj, CORPSE);
break;
}
} else { /* new rock class object... */
/* impossible? */
res = 0;
}
break;
case TOOL_CLASS: /* figurine */
{
struct monst *mon;
xchar oox, ooy;
if (obj->otyp != FIGURINE) {
res = 0;
break;
}
if (vegetarian(&mons[obj->corpsenm])) {
/* Don't animate monsters that aren't flesh */
obj = poly_obj(obj, MEATBALL);
goto smell;
}
(void) get_obj_location(obj, &oox, &ooy, 0);
refresh_x = oox; refresh_y = ooy;
mon = makemon(&mons[obj->corpsenm],
oox, ooy, NO_MM_FLAGS);
if (mon) {
delobj(obj);
if (cansee(mon->mx, mon->my))
pline_The("figurine animates!");
break;
}
goto makecorpse;
}
/* maybe add weird things to become? */
case RING_CLASS: /* some of the rings are stone */
obj = poly_obj(obj, MEAT_RING);
goto smell;
case WAND_CLASS: /* marble wand */
obj = poly_obj(obj, MEAT_STICK);
goto smell;
case GEM_CLASS: /* rocks & gems */
obj = poly_obj(obj, MEATBALL);
smell:
if (herbivorous(youmonst.data) &&
(!carnivorous(youmonst.data) ||
Role_if(PM_MONK) || !u.uconduct.unvegetarian))
Norep("You smell the odor of meat.");
else
Norep("You smell a delicious smell.");
break;
case WEAPON_CLASS: /* crysknife */
/* fall through */
default:
res = 0;
break;
}
newsym(refresh_x, refresh_y);
res = stone_to_flesh_obj(obj);
break;
default:
impossible("What an interesting effect (%d)", otmp->otyp);