mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Combat: fix Orc Rage missing fire when enemy two-shots across rounds
The rage threshold check sat only at the top of resolvePlayerAttack, so a player who took a threshold-crossing hit and was then killed by the very next enemy swing (before getting back to their own attack) would never see "rage" emitted — even though HP visibly crossed 50% while alive. Extracted maybeTriggerOrcRage shared by both sites: - Top of resolvePlayerAttack (unchanged UX: rage applies same-round when the player swings after the enemy hit). - End of runRound as a backstop (catches cross-round two-shots). st.raged guards against double-emit. Fixes the flaky TestOrcRageFiresOnLowHP; 10/10 repeats green.
This commit is contained in:
@@ -652,9 +652,36 @@ func simulateRound(st *combatState, player, enemy *Combatant, phase *CombatPhase
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// End-of-round Orc Rage backstop. The primary trigger sits at the
|
||||||
|
// top of resolvePlayerAttack so rage fires same-round when the player
|
||||||
|
// swings after taking the threshold-crossing hit. But if the enemy
|
||||||
|
// goes first this round AND next, the player can be two-shot without
|
||||||
|
// ever getting back to that check. Re-checking here ensures the rage
|
||||||
|
// event always fires while HP > 0 and below 50%, even if the buff
|
||||||
|
// goes unused.
|
||||||
|
maybeTriggerOrcRage(st, player, phaseName)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maybeTriggerOrcRage emits the "rage" event and arms pendingRageAttack
|
||||||
|
// the first time the player's HP crosses below 50% while alive. Idempotent
|
||||||
|
// via st.raged. Shared by the player-attack and end-of-round trigger sites.
|
||||||
|
func maybeTriggerOrcRage(st *combatState, player *Combatant, phaseName string) {
|
||||||
|
if !player.Mods.RageReady || st.raged || st.playerHP <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if st.playerHP*2 >= player.Stats.MaxHP {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st.raged = true
|
||||||
|
st.pendingRageAttack = true
|
||||||
|
st.events = append(st.events, CombatEvent{
|
||||||
|
Round: st.round, Phase: phaseName, Actor: "player", Action: "rage",
|
||||||
|
PlayerHP: st.playerHP, EnemyHP: st.enemyHP, Desc: "Orc Rage",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ── Attack Resolution ────────────────────────────────────────────────────────
|
// ── Attack Resolution ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// resolvePlayerAttack — d20 + AttackBonus vs enemy AC.
|
// resolvePlayerAttack — d20 + AttackBonus vs enemy AC.
|
||||||
@@ -685,17 +712,12 @@ func resolvePlayerAttack(st *combatState, player, enemy *Combatant, phase *Comba
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Orc Rage: trigger on the first attack after dropping below 50% HP.
|
// Orc Rage: trigger on the first attack after dropping below 50% HP.
|
||||||
// Use HP*2 < MaxHP rather than HP < MaxHP/2 so the threshold is exact
|
// Threshold logic lives in maybeTriggerOrcRage; this site keeps the
|
||||||
// regardless of MaxHP parity (avoids per-character drift on odd MaxHP).
|
// fire-on-next-swing UX so rage applies to the *same round* the player
|
||||||
if player.Mods.RageReady && !st.raged && st.playerHP > 0 &&
|
// reacts to a threshold-crossing hit (when they swing after the enemy).
|
||||||
st.playerHP*2 < player.Stats.MaxHP {
|
// An end-of-round backstop in runRound covers the "enemy two-shotted
|
||||||
st.raged = true
|
// me across rounds" case so the event never silently vanishes.
|
||||||
st.pendingRageAttack = true
|
maybeTriggerOrcRage(st, player, phaseName)
|
||||||
st.events = append(st.events, CombatEvent{
|
|
||||||
Round: st.round, Phase: phaseName, Actor: "player", Action: "rage",
|
|
||||||
PlayerHP: st.playerHP, EnemyHP: st.enemyHP, Desc: "Orc Rage",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
roll := 1 + st.roll(20)
|
roll := 1 + st.roll(20)
|
||||||
// Reveal action (monster ability): the enemy read this swing coming — it's
|
// Reveal action (monster ability): the enemy read this swing coming — it's
|
||||||
|
|||||||
Reference in New Issue
Block a user