From bbd76562b01b9e24b29a66ca1804a9535f695f8f Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 8 Apr 2023 16:40:21 -0700 Subject: [PATCH] redo fix for issue #1003 - energy drain The fix in commit 14d003c4ba678140b0bb4445461921d69ceaf3f0 prevented current energy from ending up 1 point above maximum energy but it didn't preserve the intent of splitting the drain with up to half coming out of maximum and the remainder out of current. This restores that intent but now only does so when maximum is more than the full drain amount rather than when it is more than the up-to-half portion, becoming less harsh when hero's max energy is very low. If current is also very low then max energy will be reduced anyway, but by less. Some unrelated formatting of invent.c has gotten mixed in. Revises #1003 --- include/extern.h | 2 +- src/invent.c | 30 ++++++++++++++++-------------- src/mhitu.c | 2 +- src/trap.c | 48 +++++++++++++++++++++++++++++++++++++++--------- src/uhitm.c | 2 +- 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/include/extern.h b/include/extern.h index 506726e99..deda4d8ec 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2907,7 +2907,7 @@ extern int water_damage(struct obj *, const char *, boolean); extern void water_damage_chain(struct obj *, boolean); extern boolean rnd_nextto_goodpos(coordxy *, coordxy *, struct monst *); extern boolean drown(void); -extern void drain_en(int); +extern void drain_en(int, boolean); extern int dountrap(void); extern int could_untrap(boolean, boolean); extern void cnv_trap_obj(int, int, struct trap *, boolean); diff --git a/src/invent.c b/src/invent.c index 38f44a078..39b716743 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1575,7 +1575,8 @@ getobj( cntgiven = TRUE; goto need_more_cq; /* now, get CMDQ_KEY */ } else { - cmdq_clear(CQ_CANNED); /* this should maybe clear the CQ_REPEAT too? */ + cmdq_clear(CQ_CANNED); + /* should maybe clear the CQ_REPEAT too? */ return NULL; } } @@ -4345,21 +4346,18 @@ look_here( There("are %s%s objects here.", (obj_cnt == 2) ? "two" : (obj_cnt < 5) ? "a few" - : (obj_cnt < 10) ? "several" - : "many", + : (obj_cnt < 10) ? "several" + : "many", picked_some ? " more" : ""); for (; otmp; otmp = otmp->nexthere) if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) { pline("%s %s%s.", - (obj_cnt > 1) - ? "Including" - : (otmp->quan > 1L) - ? "They're" - : "It's", + (obj_cnt > 1) ? "Including" + : (otmp->quan > 1L) ? "They're" + : "It's", corpse_xname(otmp, (const char *) 0, CXN_ARTICLE), - poly_when_stoned(gy.youmonst.data) - ? "" - : ", unfortunately"); + poly_when_stoned(gy.youmonst.data) ? "" + : ", unfortunately"); feel_cockatrice(otmp, FALSE); break; } @@ -5354,7 +5352,10 @@ worn_wield_only(struct obj *obj) * MINV_ALL - display all inventory */ struct obj * -display_minventory(struct monst *mon, int dflags, char *title) +display_minventory( + struct monst *mon, /* monster whose minvent we're showing */ + int dflags, /* control over what to display */ + char *title) /* menu title */ { struct obj *ret; char tmp[QBUFSZ]; @@ -5362,7 +5363,8 @@ display_minventory(struct monst *mon, int dflags, char *title) menu_item *selected = 0; int do_all = (dflags & MINV_ALL) != 0, incl_hero = (do_all && engulfing_u(mon)), - have_inv = (mon->minvent != 0), have_any = (have_inv || incl_hero), + have_inv = (mon->minvent != 0), + have_any = (have_inv || incl_hero), pickings = (dflags & MINV_PICKMASK); Sprintf(tmp, "%s %s:", s_suffix(noit_Monnam(mon)), @@ -5383,7 +5385,7 @@ display_minventory(struct monst *mon, int dflags, char *title) iflags.suppress_price--; /* was 'set_uasmon();' but that potentially has side-effects */ - gy.youmonst.data = &mons[u.umonnum]; /* most basic part of set_uasmon */ + gy.youmonst.data = &mons[u.umonnum]; /* basic part of set_uasmon() */ } else { invdisp_nothing(title ? title : tmp, "(none)"); n = 0; diff --git a/src/mhitu.c b/src/mhitu.c index 8f4b5c7e6..08928a9fc 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1406,7 +1406,7 @@ gulpmu(struct monst *mtmp, struct attack *mattk) case AD_DREN: /* AC magic cancellation doesn't help when engulfed */ if (!mtmp->mcan && rn2(4)) /* 75% chance */ - drain_en(tmp); + drain_en(tmp, FALSE); tmp = 0; break; default: diff --git a/src/trap.c b/src/trap.c index af2daf329..b4dbdb076 100644 --- a/src/trap.c +++ b/src/trap.c @@ -2115,8 +2115,8 @@ trapeffect_anti_magic( unsigned int trflags UNUSED) { if (mtmp == &gy.youmonst) { - int drain = d(2, 6); /* 2d6 => 2..12 */ - int halfd = rnd((drain + 1) / 2); /* 1..drain/2 (rounded up) */ + int drain, halfd; + boolean exclaim_it = FALSE; seetrap(trap); if (Antimagic) { @@ -2149,11 +2149,24 @@ trapeffect_anti_magic( losehp(dmgval2, "anti-magic implosion", KILLED_BY_AN); } - if (u.uenmax > halfd) { + /* if the drain amount is more than hero's maximum energy then up + to half of the amount comes directly out of maximum, the rest + comes out of current energy; drain_en() lowers the current + amount and when doing so it will take even more from maximum + if the new current value would drop below zero */ + drain = d(2, 6); /* 2d6 => 2..12 */ + halfd = rnd(drain / 2); /* 1..drain/2 (round down) */ + if (u.uenmax > drain) { /* [was u.uenmax > halfd] */ + /* note: since 'halfd' is no more than half, 'drain -= halfd' + is at least as big, so drain_en() is never asked to remove + less from current than what we're removing from maximum; + however, it might do that anyway (via its throttle check) so + it needs to make sure uen doesn't end up exceeding uenmax */ u.uenmax -= halfd; /* drain_en() will set context.botl */ - drain = halfd; + drain -= halfd; + exclaim_it = TRUE; } - drain_en(drain); + drain_en(drain, exclaim_it); } else { boolean trapkilled = FALSE; boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed); @@ -4598,31 +4611,48 @@ drown(void) } void -drain_en(int n) +drain_en(int n, boolean max_already_drained) { + const char *mesg; + char punct = max_already_drained ? '!' : '.'; + /* * FIXME? * u.uenmax should probably have a higher mininum than 0; * perhaps u.ulevel or (u.ulevel + 1) / 2 */ - if (!u.uenmax) { + if (u.uenmax < 1) { /* energy is completely gone */ - You_feel("momentarily lethargic."); + if (u.uen || u.uenmax) { /* paranoia */ + u.uen = u.uenmax = 0; + gc.context.botl = TRUE; + } + mesg = "momentarily lethargic"; } else { /* throttle further loss a bit when there's not much left to lose */ if (n > (u.uen + u.uenmax) / 3) n = rnd(n); - You_feel("your magical energy drain away%c", (n > u.uen) ? '!' : '.'); + mesg = "your magical energy drain away"; + if (n > u.uen) + punct = '!'; + u.uen -= n; if (u.uen < 0) { u.uenmax -= rnd(-u.uen); if (u.uenmax < 0) u.uenmax = 0; u.uen = 0; + } else if (u.uen > u.uenmax) { + /* uen might be greater than uenmax if caller reduced uenmax + and then we throttled the loss being applied to current */ + u.uen = u.uenmax; } gc.context.botl = TRUE; } + /* after manipulating u.uen,uenmax and setting context.botl, so + that You_feel() -> pline() will update status before the message */ + You_feel("%s%c", mesg, punct); } /* the #untrap command - disarm a trap */ diff --git a/src/uhitm.c b/src/uhitm.c index 66b22af0b..f10accf3d 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -2224,7 +2224,7 @@ mhitm_ad_dren( /* mhitu */ hitmsg(magr, mattk); if (!negated && !rn2(4)) /* 25% chance */ - drain_en(mhm->damage); + drain_en(mhm->damage, FALSE); mhm->damage = 0; } else { /* mhitm */