]> granicus.if.org Git - nethack/commitdiff
stone-to-flesh vs golem statues and figurines (trunk only)
authornethack.rankin <nethack.rankin>
Sun, 1 Apr 2007 03:32:53 +0000 (03:32 +0000)
committernethack.rankin <nethack.rankin>
Sun, 1 Apr 2007 03:32:53 +0000 (03:32 +0000)
     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.

doc/fixes35.0
src/trap.c
src/zap.c

index 14bb9fe1a0336782b37ac0cac5281f25bcb0e5b6..815628b62ac135db7b2052260edbd5b8624c8a61 100644 (file)
@@ -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
index a3aa0b372cada2a62158f4e0e7c1d1d7e00d6ac6..aa81fd7a8789cc2051869280898d5eba875baa5b 100644 (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) {
index 1d9684fa1a21ffe9f25765abe3d13a650074f2eb..f701cbf832e2fdf3dbe9efe87d2971021665d6cf 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -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);