mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Review follow-ups: harden the extraction guard, fix Misty/concentration ordering
Applying /code-review high findings on the review-follow-up stack: - expeditionCmdStart: the resumable-extraction guard swallowed a partySize error and fell open, starting a new expedition on top of a still-seated party — the exact orphaning it exists to prevent. Now checks extractionLapsed first (no DB call on the reap path) and treats a roster-read error as "assume occupied → refuse". - Lapsed reap on !expedition start silently unseated members. Extracted a shared reapLapsedExtraction helper (reap + notify the roster) and routed both the hourly sweeper and the start-path reap through it. - stepRoundEnd: moved Misty's crowd/heal seat-loop after the concentration tick so a caster whose lingering aura would kill the enemy that round wins before the end-of-round crowd swing, honoring the concentration block's "a lethal pulse settles the fight" intent. - Promoted the misty_heal event-log scan to a shared hasAction helper. - Renamed the new sweep test off the dnd_ prefix. Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
This commit is contained in:
@@ -753,27 +753,12 @@ func (te *turnEngine) stepRoundEnd() {
|
||||
}
|
||||
te.stampSeat(mark, i)
|
||||
}
|
||||
// Misty's crowd, then Misty's heal — per seat, after the round's other
|
||||
// damage has landed so the heal can answer it, which is the order
|
||||
// endOfRoundForSeat uses. Both no-op (and draw no RNG) for a character with
|
||||
// no Misty history, which is every simulated one.
|
||||
for i := range st.actors {
|
||||
st.seat(i)
|
||||
if st.playerHP <= 0 {
|
||||
continue
|
||||
}
|
||||
mark := len(st.events)
|
||||
over := seatEndOfRound(st, st.c, CombatPhaseRoundEnd, te.result)
|
||||
te.stampSeat(mark, i)
|
||||
if over {
|
||||
te.finish(CombatStatusLost)
|
||||
return
|
||||
}
|
||||
}
|
||||
// Concentration aura (Spirit Guardians et al.): the lingering spell bites
|
||||
// the enemy each round it stays up. Concentration is per-caster, so every
|
||||
// seat holding one pulses. Ticks before enemy regen so a lethal pulse
|
||||
// settles the fight before the enemy knits its wounds back.
|
||||
// settles the fight before the enemy knits its wounds back — and before
|
||||
// Misty's crowd swings, so a caster whose aura would end the round is not
|
||||
// robbed of the win by an end-of-round debuff.
|
||||
for i := range st.actors {
|
||||
st.seat(i)
|
||||
if st.concentrationDmg <= 0 || st.enemyHP <= 0 || st.playerHP <= 0 {
|
||||
@@ -789,6 +774,24 @@ func (te *turnEngine) stepRoundEnd() {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Misty's crowd, then Misty's heal — per seat, after the round's other
|
||||
// damage (including the concentration pulse above) has landed so the heal
|
||||
// can answer it, which is the order endOfRoundForSeat uses. Both no-op (and
|
||||
// draw no RNG) for a character with no Misty history, which is every
|
||||
// simulated one.
|
||||
for i := range st.actors {
|
||||
st.seat(i)
|
||||
if st.playerHP <= 0 {
|
||||
continue
|
||||
}
|
||||
mark := len(st.events)
|
||||
over := seatEndOfRound(st, st.c, CombatPhaseRoundEnd, te.result)
|
||||
te.stampSeat(mark, i)
|
||||
if over {
|
||||
te.finish(CombatStatusLost)
|
||||
return
|
||||
}
|
||||
}
|
||||
st.seat(0)
|
||||
// Regenerate (monster ability): the enemy knits its wounds at round end.
|
||||
if st.enemyRegen > 0 && st.enemyHP > 0 && st.enemyHP < te.enemy.Stats.MaxHP {
|
||||
|
||||
Reference in New Issue
Block a user