Skip to content

Commit

Permalink
stone-to-flesh vs golem statues and figurines (trunk only)
Browse files Browse the repository at this point in the history
     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.
  • Loading branch information
nethack.rankin committed Apr 1, 2007
1 parent e91ff47 commit d8a0f73
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 101 deletions.
2 changes: 2 additions & 0 deletions doc/fixes35.0
Expand Up @@ -207,6 +207,8 @@ give better feedback when thrown shop-owned food gets used up taming a monster
effect of negative AC on damage received was calculated differently than
normal when deciding whether hero poly'd into pudding would split
unicorn horn produced by revived monster will polymorph as if non-magic
stone-to-flesh on any golem statue or golem figurine creates flesh golem
stone-to-flesh which activates shop-owned figurine entails shop charges


Platform- and/or Interface-Specific Fixes
Expand Down
8 changes: 5 additions & 3 deletions src/trap.c
@@ -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. */

Expand Down Expand Up @@ -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[] =
Expand All @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down
220 changes: 122 additions & 98 deletions src/zap.c
Expand Up @@ -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));
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit d8a0f73

Please sign in to comment.