diff --git a/include/decl.h b/include/decl.h index 8487ae6fc..1a048b007 100644 --- a/include/decl.h +++ b/include/decl.h @@ -838,6 +838,9 @@ struct instance_globals_r { struct instance_globals_s { + /* allmain.c */ + boolean saving_grace_turn; /* saving grace was triggered this turn */ + /* artifact.c */ int spec_dbon_applies; /* coordinate effects from spec_dbon() with messages in artifact_hit() */ @@ -969,6 +972,9 @@ struct instance_globals_t { struct instance_globals_u { + /* allmain.c */ + int uhp_at_start_of_monster_turn; + /* botl.c */ boolean update_all; diff --git a/src/allmain.c b/src/allmain.c index cde778bfb..b4d7158f4 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -200,6 +200,7 @@ moveloop_core(void) encumber_msg(); svc.context.mon_moving = TRUE; + gu.uhp_at_start_of_monster_turn = u.uhp; do { monscanmove = movemon(); if (u.umovement >= NORMAL_SPEED) @@ -272,6 +273,8 @@ moveloop_core(void) if (u.ublesscnt) u.ublesscnt--; + gs.saving_grace_turn = FALSE; + /* One possible result of prayer is healing. Whether or * not you get healed depends on your current hit points. * If you are allowed to regenerate during the prayer, @@ -423,6 +426,8 @@ moveloop_core(void) else if (!u.umoved) (void) pooleffects(FALSE); + gs.saving_grace_turn = FALSE; + /* vision while buried or underwater is updated here */ if (Underwater) under_water(0); diff --git a/src/decl.c b/src/decl.c index 04f55eb99..e6fd152f0 100644 --- a/src/decl.c +++ b/src/decl.c @@ -674,6 +674,8 @@ static const struct instance_globals_r g_init_r = { }; static const struct instance_globals_s g_init_s = { + /* allmain.c */ + FALSE, /* saving_grace_turn */ /* artifact.c */ 0, /* spec_dbon_applies */ /* decl.c */ @@ -764,6 +766,8 @@ static const struct instance_globals_t g_init_t = { }; static const struct instance_globals_u g_init_u = { + /* allmain.c */ + 0, /* uhp_at_start_of_monster_turn */ /* botl.c */ FALSE, /* update_all */ /* decl.c */ diff --git a/src/hack.c b/src/hack.c index 9c0a56712..fb356ab78 100644 --- a/src/hack.c +++ b/src/hack.c @@ -4126,7 +4126,25 @@ saving_grace(int dmg) return 0; } - if (!u.usaving_grace && dmg >= u.uhp && (u.uhp * 100 / u.uhpmax) > 90) { + if (!svc.context.mon_moving) { + /* saving grace doesn't protect you from your own actions */ + return dmg; + } + + if (dmg < u.uhp || u.uhp <= 0) { + /* no need for saving grace */ + return dmg; + } + + if (gs.saving_grace_turn) { + /* saving grace already triggered and prevents HP reducing below 1 + this turn (specifically: until the next player action or turn + boundary), don't print further messages or livelog entries */ + return u.uhp - 1; + } + + if (!u.usaving_grace && + (gu.uhp_at_start_of_monster_turn * 100 / u.uhpmax) >= 90) { /* saving_grace doesn't have it's own livelog classification; we might invent one, or perhaps use LL_LIFESAVE, but surviving certain death (or preserving worn amulet of life saving) via @@ -4135,11 +4153,14 @@ saving_grace(int dmg) from #chronicle during play but show it to livelog observers */ livelog_printf(LL_CONDUCT | LL_SPOILER, "%s (%d damage, %d/%d HP)", "survived one-shot death via saving-grace", - dmg, u.uhp, u.uhpmax); + /* include damage that happened earlier this turn */ + gu.uhp_at_start_of_monster_turn - u.uhp + dmg, + gu.uhp_at_start_of_monster_turn, u.uhpmax); /* note: this could reduce dmg to 0 if u.uhpmax==1 */ dmg = u.uhp - 1; u.usaving_grace = 1; /* used up */ + gs.saving_grace_turn = TRUE; end_running(TRUE); if (u.usleep) unmul("Suddenly you wake up!");