Skip to content

Commit

Permalink
github issue #475 revisited - Trollsbane
Browse files Browse the repository at this point in the history
Change Trollsbane versus troll corpse revival:  instead of revival
failing if Trollsbane is wielded at time of revival attempt, mark
the corpse no-revive if killed by Trollsbane (whether by the hero
or a monster).

If a no-revive corpse is within view when time to revive occurs,
give "the troll corpse twitches feebly" even when the hero isn't
responsible.  That used to only apply if the hero zapped the
corpse with undead turning, which would have become inoperative
because now being zapped by undead turning clears the no-revive
flag and revives as normal.  In other words, undead turning magic
overrides killed-by-Trollsbane or non-ice troll having been in an
ice box.
  • Loading branch information
PatR committed Apr 2, 2021
1 parent 770afba commit 328dc5b
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 58 deletions.
3 changes: 3 additions & 0 deletions include/decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion include/extern.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ### */

Expand Down
4 changes: 4 additions & 0 deletions include/monst.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
21 changes: 0 additions & 21 deletions src/artifact.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*/
3 changes: 3 additions & 0 deletions src/decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
3 changes: 2 additions & 1 deletion src/dig.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions src/do.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -1950,22 +1950,22 @@ 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))
free_omonst(body);

body->corpsenm = zmon;
revive_mon(arg, timeout);
} else {
rot_corpse(arg, timeout);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/mhitm.c
Original file line number Diff line number Diff line change
Expand Up @@ -906,13 +906,16 @@ 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
|| mattk->aatyp == AT_BITE)
&& 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)
Expand Down
38 changes: 19 additions & 19 deletions src/mkobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand Down
36 changes: 28 additions & 8 deletions src/uhitm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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)) {
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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.");
Expand All @@ -4075,6 +4091,7 @@ damageum(
} else if (mhm.damage) {
killed(mdef); /* regular "you kill <mdef>" message */
}
g.mkcorpstat_norevive = FALSE;
return MM_DEF_DIED;
}
return MM_HIT;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
"<u.ustuck> is no longer in your clutches" */
Expand Down
17 changes: 13 additions & 4 deletions src/zap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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 */
Expand All @@ -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)
Expand Down

0 comments on commit 328dc5b

Please sign in to comment.