diff --git a/include/decl.h b/include/decl.h index 85f2d38fb..e432cdeff 100644 --- a/include/decl.h +++ b/include/decl.h @@ -961,6 +961,9 @@ struct instance_globals { int xmin, ymin, xmax, ymax; /* level boundaries */ boolean ransacked; + /* mkobj.c */ + boolean mkcorpstat_norevive; /* for trolls */ + /* mon.c */ boolean vamp_rise_msg; boolean disintegested; diff --git a/include/extern.h b/include/extern.h index 25cb5dc06..a42769579 100644 --- a/include/extern.h +++ b/include/extern.h @@ -98,7 +98,6 @@ extern void retouch_equipment(int); extern void mkot_trap_warn(void); extern boolean is_magic_key(struct monst *, struct obj *); extern struct obj *has_magic_key(struct monst *); -extern boolean Trollsbane_wielded(void); /* ### attrib.c ### */ diff --git a/include/monst.h b/include/monst.h index 66f51f469..9b99a6ca6 100644 --- a/include/monst.h +++ b/include/monst.h @@ -210,6 +210,10 @@ struct monst { #define is_obj_mappear(mon,otyp) (M_AP_TYPE(mon) == M_AP_OBJECT \ && (mon)->mappearance == (otyp)) +/* is mon m (presumably just killed) a troll and obj o Trollsbane? */ +#define troll_baned(m,o) \ + ((m)->data->mlet == S_TROLL && (o) && (o)->oartifact == ART_TROLLSBANE) + /* Get the maximum difficulty monsters that can currently be generated, given the current level difficulty and the hero's level. */ #define monmax_difficulty(levdif) (((levdif) + u.ulevel) / 2) diff --git a/src/artifact.c b/src/artifact.c index 47b3ca03d..1202369e1 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -2176,25 +2176,4 @@ has_magic_key(struct monst *mon) /* if null, hero assumed */ return (struct obj *) 0; } -/* True if anyone on the level is wielding Trollsbane, False otherwise; - used to prevent troll resurrection (FIXME: really ought to be inhibited - when killed by Trollsbane rather than whether anyone wields that) */ -boolean -Trollsbane_wielded(void) -{ - struct monst *mtmp; - struct obj *mw_tmp; - - if (uwep && uwep->oartifact == ART_TROLLSBANE) - return TRUE; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp)) - continue; - if ((mw_tmp = MON_WEP(mtmp)) != 0 - && mw_tmp->oartifact == ART_TROLLSBANE) - return TRUE; - } - return FALSE; -} - /*artifact.c*/ diff --git a/src/decl.c b/src/decl.c index da8402cd9..ddeb071e9 100644 --- a/src/decl.c +++ b/src/decl.c @@ -486,6 +486,9 @@ const struct instance_globals g_init = { UNDEFINED_VALUE, /* ymax */ 0, /* ransacked */ + /* mkobj.c */ + FALSE, /* mkcorpstat_norevive */ + /* mon.c */ FALSE, /* vamp_rise_msg */ FALSE, /* disintegested */ diff --git a/src/dig.c b/src/dig.c index b62a30d67..16948cd77 100644 --- a/src/dig.c +++ b/src/dig.c @@ -2022,7 +2022,8 @@ rot_corpse(anything *arg, long timeout) if (mtmp && !OBJ_AT(x, y) && mtmp->mundetected && hides_under(mtmp->data)) { mtmp->mundetected = 0; - } else if (x == u.ux && y == u.uy && u.uundetected && hides_under(g.youmonst.data)) + } else if (x == u.ux && y == u.uy + && u.uundetected && hides_under(g.youmonst.data)) (void) hideunder(&g.youmonst); newsym(x, y); } else if (in_invent) diff --git a/src/do.c b/src/do.c index fadc37ff8..647225519 100644 --- a/src/do.c +++ b/src/do.c @@ -1941,7 +1941,7 @@ revive_mon(anything *arg, long timeout UNUSED) } else { /* rot this corpse away */ You_feel("%sless hassled.", is_rider(mptr) ? "much " : ""); action = ROT_CORPSE; - when = 250L - (g.monstermoves - body->age); + when = (long) d(5, 50) - (g.monstermoves - body->age); if (when < 1L) when = 1L; } @@ -1950,15 +1950,13 @@ revive_mon(anything *arg, long timeout UNUSED) } /* Timeout callback. Revive the corpse as a zombie. */ -/*ARGSUSED*/ void -zombify_mon(anything *arg, long timeout UNUSED) +zombify_mon(anything *arg, long timeout) { struct obj *body = arg->a_obj; int zmon = zombie_form(&mons[body->corpsenm]); if (zmon != NON_PM) { - if (has_omid(body)) free_omid(body); if (has_omonst(body)) @@ -1966,6 +1964,8 @@ zombify_mon(anything *arg, long timeout UNUSED) body->corpsenm = zmon; revive_mon(arg, timeout); + } else { + rot_corpse(arg, timeout); } } diff --git a/src/mhitm.c b/src/mhitm.c index 2c3f2388e..e6f990593 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -906,6 +906,8 @@ mdamagem(struct monst *magr, struct monst *mdef, place_monster(mdef, mdef->mx, mdef->my); mdef->mhp = 0; } + if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW) + g.mkcorpstat_norevive = troll_baned(mdef, mwep) ? TRUE : FALSE; g.zombify = (!mwep && zombie_maker(magr) && (mattk->aatyp == AT_TUCH || mattk->aatyp == AT_CLAW @@ -913,6 +915,7 @@ mdamagem(struct monst *magr, struct monst *mdef, && zombie_form(mdef->data) != NON_PM); monkilled(mdef, "", (int) mattk->adtyp); g.zombify = FALSE; /* reset */ + g.mkcorpstat_norevive = FALSE; if (!DEADMONSTER(mdef)) return mhm.hitflags; /* mdef lifesaved */ else if (mhm.hitflags == MM_AGR_DIED) diff --git a/src/mkobj.c b/src/mkobj.c index 50aa4c3ea..71295af3d 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1136,44 +1136,43 @@ void start_corpse_timeout(struct obj* body) { long when; /* rot away when this old */ - long corpse_age; /* age of corpse */ + long age; /* age of corpse */ int rot_adjust; short action; - boolean no_revival; - /* if a troll corpse was frozen, it won't get a revive timer */ - no_revival = (body->norevive != 0); - body->norevive = 0; /* always clear corpse's 'frozen' flag */ + /* + * Note: + * if body->norevive is set, the corpse will rot away instead + * of revive when its REVIVE_MON timer finishes. + */ /* lizards and lichen don't rot or revive */ if (body->corpsenm == PM_LIZARD || body->corpsenm == PM_LICHEN) return; - action = ROT_CORPSE; /* default action: rot away */ + action = ROT_CORPSE; /* default action: rot away */ rot_adjust = g.in_mklev ? 25 : 10; /* give some variation */ - corpse_age = g.monstermoves - body->age; - if (corpse_age > ROT_AGE) + age = g.monstermoves - body->age; + if (age > ROT_AGE) when = rot_adjust; else - when = ROT_AGE - corpse_age; + when = ROT_AGE - age; when += (long) (rnz(rot_adjust) - rot_adjust); if (is_rider(&mons[body->corpsenm])) { action = REVIVE_MON; when = rider_revival_time(body, FALSE); - } else if (mons[body->corpsenm].mlet == S_TROLL && !no_revival) { - long age; - + } else if (mons[body->corpsenm].mlet == S_TROLL) { for (age = 2; age <= TAINT_AGE; age++) if (!rn2(TROLL_REVIVE_CHANCE)) { /* troll revives */ action = REVIVE_MON; when = age; break; } - } else if (!no_revival && g.zombify - && zombie_form(&mons[body->corpsenm]) != NON_PM) { + } else if (g.zombify && zombie_form(&mons[body->corpsenm]) != NON_PM + && !body->norevive) { action = ZOMBIFY_MON; - when = 5 + rn2(15); + when = rn1(15, 5); /* 5..19 */ } (void) start_timer(when, TIMER_OBJECT, action, obj_to_any(body)); @@ -1463,10 +1462,10 @@ mkgold(long amount, int x, int y) */ struct obj * mkcorpstat( - int objtype, /* CORPSE or STATUE */ - struct monst *mtmp, - struct permonst *ptr, - int x, int y, + int objtype, /* CORPSE or STATUE */ + struct monst *mtmp, /* dead monster, might be Null */ + struct permonst *ptr, /* if non-Null, overrides mtmp->mndx */ + int x, int y, /* where to place corpse; <0,0> => random */ unsigned corpstatflags) { struct obj *otmp; @@ -1480,6 +1479,7 @@ mkcorpstat( } else { otmp = mksobj_at(objtype, x, y, init, FALSE); } + otmp->norevive = g.mkcorpstat_norevive; /* when 'mtmp' is non-null save the monster's details with the corpse or statue; it will also force the 'ptr' override below */ diff --git a/src/uhitm.c b/src/uhitm.c index ac55059b0..d16b41acc 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1361,6 +1361,7 @@ hmon_hitmon(struct monst *mon, /* [note: thrown obj might go away during killed()/xkilled() call (via 'thrownobj'; if swallowed, it gets added to engulfer's minvent and might merge with a stack that's already there)] */ + /* already_killed and poiskilled won't apply for Trollsbane */ if (needpoismsg) pline_The("poison doesn't seem to affect %s.", mon_nam(mon)); @@ -1370,8 +1371,12 @@ hmon_hitmon(struct monst *mon, xkilled(mon, XKILL_NOMSG); destroyed = TRUE; /* return FALSE; */ } else if (destroyed) { - if (!already_killed) + if (!already_killed) { + if (troll_baned(mon, obj)) + g.mkcorpstat_norevive = TRUE; killed(mon); /* takes care of most messages */ + g.mkcorpstat_norevive = FALSE; + } } else if (u.umconf && hand_to_hand) { nohandglow(mon); if (!mon->mconf && !resist(mon, SPBOOK_CLASS, 0, NOTELL)) { @@ -3306,7 +3311,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef, || mattk->aatyp == AT_TUCH || mattk->aatyp == AT_HUGS) { if (thick_skinned(pd)) - mhm->damage = (mattk->aatyp == AT_KICK) ? 0 : (mhm->damage + 1) / 2; + mhm->damage = (mattk->aatyp == AT_KICK) ? 0 + : (mhm->damage + 1) / 2; /* add ring(s) of increase damage */ if (u.udaminc > 0) { /* applies even if damage was 0 */ @@ -3362,7 +3368,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef, if (mhm->damage <= 0) mhm->damage = 1; if (!(otmp->oartifact && artifact_hit(magr, mdef, otmp, - &mhm->damage, g.mhitu_dieroll))) + &mhm->damage, + g.mhitu_dieroll))) hitmsg(magr, mattk); if (!mhm->damage) return; @@ -3434,7 +3441,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef, now we'll know and might need to deliver skipped message (note: if there's no message there'll be no auxilliary damage so the message here isn't coming too late) */ - if (!artifact_hit(magr, mdef, mwep, &mhm->damage, mhm->dieroll)) { + if (!artifact_hit(magr, mdef, mwep, &mhm->damage, + mhm->dieroll)) { if (g.vis) pline("%s hits %s.", Monnam(magr), mon_nam_too(mdef, magr)); @@ -3443,7 +3451,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef, damage; however, it might cause carried items to be destroyed and they might do so */ if (DEADMONSTER(mdef)) { - mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED)); + mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 + : MM_AGR_DIED)); mhm->done = TRUE; return; } @@ -4044,6 +4053,7 @@ damageum( int specialdmg) /* blessed and/or silver bonus against various things */ { struct mhitm_data mhm; + mhm.damage = d((int) mattk->damn, (int) mattk->damd); mhm.hitflags = MM_MISS; mhm.permdmg = 0; @@ -4063,6 +4073,12 @@ damageum( mdef->mstrategy &= ~STRAT_WAITFORU; /* in case player is very fast */ mdef->mhp -= mhm.damage; if (DEADMONSTER(mdef)) { + /* troll killed by Trollsbane won't auto-revive; FIXME? same when + Trollsbane is wielded as primary and two-weaponing kills with + secondary, which matches monster vs monster behavior but is + different from the non-poly'd hero vs monster behavior */ + if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW) + g.mkcorpstat_norevive = troll_baned(mdef, uwep) ? TRUE : FALSE; /* (DEADMONSTER(mdef) and !mhm.damage => already killed) */ if (mdef->mtame && !cansee(mdef->mx, mdef->my)) { You_feel("embarrassed for a moment."); @@ -4075,6 +4091,7 @@ damageum( } else if (mhm.damage) { killed(mdef); /* regular "you kill " message */ } + g.mkcorpstat_norevive = FALSE; return MM_DEF_DIED; } return MM_HIT; @@ -4393,7 +4410,8 @@ hmonas(struct monst *mon) struct attack *mattk, alt_attk; struct obj *weapon, **originalweapon; boolean altwep = FALSE, weapon_used = FALSE, odd_claw = TRUE; - int i, tmp, armorpenalty, sum[NATTK], nsum = MM_MISS, dhit = 0, attknum = 0; + int i, tmp, armorpenalty, sum[NATTK], nsum = MM_MISS, + dhit = 0, attknum = 0; int dieroll, multi_claw = 0; /* with just one touch/claw/weapon attack, both rings matter; @@ -4425,7 +4443,8 @@ hmonas(struct monst *mon) get to make another weapon attack (note: monsters who use weapons do not have this restriction, but they also never have the opportunity to use two weapons) */ - if (weapon_used && (sum[i - 1] > MM_MISS) && uwep && bimanual(uwep)) + if (weapon_used && (sum[i - 1] > MM_MISS) + && uwep && bimanual(uwep)) continue; /* Certain monsters don't use weapons when encountered as enemies, * but players who polymorph into them have hands or claws and @@ -4674,7 +4693,8 @@ hmonas(struct monst *mon) if (silverhit && flags.verbose) silver_sears(&g.youmonst, mon, silverhit); sum[i] = damageum(mon, mattk, specialdmg); - } else if (i >= 2 && (sum[i - 1] > MM_MISS) && (sum[i - 2] > MM_MISS)) { + } else if (i >= 2 && (sum[i - 1] > MM_MISS) + && (sum[i - 2] > MM_MISS)) { /* in case we're hugging a new target while already holding something else; yields feedback " is no longer in your clutches" */ diff --git a/src/zap.c b/src/zap.c index a2de95a5d..ea78ea23d 100644 --- a/src/zap.c +++ b/src/zap.c @@ -783,9 +783,9 @@ revive(struct obj *corpse, boolean by_hero) x = xy.x, y = xy.y; } - if ((mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ)) - || (mons[montype].mlet == S_TROLL && Trollsbane_wielded())) { - if (by_hero && cansee(x, y)) + if (corpse->norevive + || (mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ))) { + if (cansee(x, y)) pline("%s twitches feebly.", upstart(corpse_xname(corpse, (const char *) 0, CXN_PFX_THE))); return (struct monst *) 0; @@ -961,6 +961,7 @@ unturn_dead(struct monst *mon) struct obj *otmp, *otmp2; struct monst *mtmp2; char owner[BUFSZ], corpse[BUFSZ]; + unsigned save_norevive; boolean youseeit, different_type, is_u = (mon == &g.youmonst); int corpsenm, res = 0; @@ -987,6 +988,10 @@ unturn_dead(struct monst *mon) /* for a stack, only one is revived; if is_u, revive() calls useup() which calls update_inventory() but not encumber_msg() */ corpsenm = otmp->corpsenm; + /* norevive applies to revive timer, not to explicit unturn_dead() */ + save_norevive = otmp->norevive; + otmp->norevive = 0; + if ((mtmp2 = revive(otmp, !g.context.mon_moving)) != 0) { ++res; /* might get revived as a zombie rather than corpse's monster */ @@ -1005,9 +1010,13 @@ unturn_dead(struct monst *mon) pline("%s%s suddenly %s%s%s!", owner, corpse, nonliving(mtmp2->data) ? "reanimates" : "comes alive", different_type ? " as " : "", - different_type ? an(pmname(mtmp2->data, Mgender(mtmp2))) : ""); + different_type ? an(pmname(mtmp2->data, Mgender(mtmp2))) + : ""); else if (canseemon(mtmp2)) pline("%s suddenly appears!", Amonnam(mtmp2)); + } else { + /* revival failed; corpse 'otmp' is intact */ + otmp->norevive = save_norevive ? 1 : 0; } } if (is_u && res)