mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-18 01:42:42 +00:00
adventure: T6 Inversion Stitch (Seamstress) + boss re-strengthen (P8)
Second in-combat Layer-2 mechanic, reusing Amendment's round-end seam. In the Seamstress's phase 2 (<=35% HP) the room sews inside-out in a repeating warn(1)->sting(2) cadence, telegraphed one round ahead: during a pulse player heals (self + ally) invert to damage, floored at 1 HP. State inversionActive/inversionTelegraph round-trips through CombatStatuses so a suspend/resume can't drop or double a pulse. Sim surfaced that unplace is reach-bound (only ~37% reach the boss, then cleared her ~98%), so the mechanic alone is sub-noise. Re-strengthened the Seamstress, the weakest T6 boss (over-softened by P7 for a zone lift that never came): HP 385->460, Atk 39->45, Needle Rain proc 0.40->0.45. Sim-validated at fighter zone 37.5% (in range), and a real boss fight now instead of a victory lap. Claude-Session: https://claude.ai/code/session_0156WqjgsbmSY2U8eQ3Kkb1s
This commit is contained in:
@@ -373,7 +373,10 @@ func resumeTurnEngine(sess *CombatSession, players []*Combatant, enemy *Combatan
|
||||
// Amendment (T6 Custodian) — round-3 snapshot + once-only rewind.
|
||||
enemyRewindHP: sess.Statuses.EnemyRewindHP,
|
||||
enemyRewindUsed: sess.Statuses.EnemyRewindUsed,
|
||||
rng: rng,
|
||||
// Inversion Stitch (T6 Seamstress) — phase-2 heal-inverting pulses.
|
||||
inversionActive: sess.Statuses.InversionActive,
|
||||
inversionTelegraph: sess.Statuses.InversionTelegraph,
|
||||
rng: rng,
|
||||
}
|
||||
order := turnOrder(sess, sess.Round, players, enemy)
|
||||
sess.Statuses.TurnIdx = turnIdxForPhase(order, sess.Statuses.TurnIdx, sess.Phase)
|
||||
@@ -573,10 +576,22 @@ func (te *turnEngine) stepPlayerActionEffect(eff *turnActionEffect) {
|
||||
st.enemyHP = max(0, st.enemyHP-enemyDmg)
|
||||
}
|
||||
if eff.PlayerHeal > 0 {
|
||||
// Respect any max_hp_drain monster ability — a drained player can't be
|
||||
// healed back past the lowered ceiling.
|
||||
hpCap := max(1, st.hpMax-st.maxHPDrain)
|
||||
st.playerHP = min(hpCap, st.playerHP+eff.PlayerHeal)
|
||||
if st.inversionActive > 0 {
|
||||
// Inversion Stitch (T6 Seamstress phase 2): the room is sewn inside-out,
|
||||
// so the cure lands as a wound. Floored at 1 so a player is never killed
|
||||
// by their own heal — the Seamstress's own blows do the finishing; the
|
||||
// sting just denies the sustain and softens the seat for them.
|
||||
st.playerHP = max(1, st.playerHP-eff.PlayerHeal)
|
||||
st.events = append(st.events, CombatEvent{
|
||||
Round: st.round, Phase: turnCombatPhase.Name, Actor: "enemy", Action: "heal_inverted",
|
||||
Damage: eff.PlayerHeal, PlayerHP: st.playerHP, EnemyHP: st.enemyHP,
|
||||
})
|
||||
} else {
|
||||
// Respect any max_hp_drain monster ability — a drained player can't be
|
||||
// healed back past the lowered ceiling.
|
||||
hpCap := max(1, st.hpMax-st.maxHPDrain)
|
||||
st.playerHP = min(hpCap, st.playerHP+eff.PlayerHeal)
|
||||
}
|
||||
}
|
||||
// §1 — heal somebody else. The caster's cursor stays where it is; only the
|
||||
// target's HP moves.
|
||||
@@ -587,14 +602,27 @@ func (te *turnEngine) stepPlayerActionEffect(eff *turnActionEffect) {
|
||||
// path depends on. Healing keeps people up; it does not bring them back.
|
||||
if eff.AllyHeal > 0 && eff.AllySeat >= 0 && eff.AllySeat < len(st.actors) {
|
||||
if tgt := st.actors[eff.AllySeat]; tgt.playerHP > 0 {
|
||||
cap := max(1, tgt.hpMax-tgt.maxHPDrain)
|
||||
before := tgt.playerHP
|
||||
tgt.playerHP = min(cap, tgt.playerHP+eff.AllyHeal)
|
||||
st.events = append(st.events, CombatEvent{
|
||||
Round: st.round, Phase: turnCombatPhase.Name, Actor: "player", Action: "ally_heal",
|
||||
Damage: tgt.playerHP - before, PlayerHP: tgt.playerHP, EnemyHP: st.enemyHP,
|
||||
Seat: eff.AllySeat, Desc: eff.Label,
|
||||
})
|
||||
if st.inversionActive > 0 {
|
||||
// Inversion Stitch: the ally-heal wounds the friend it was meant to
|
||||
// mend. Floored at 1 like the self-heal sting above — the sting denies
|
||||
// the sustain, it does not kill.
|
||||
before := tgt.playerHP
|
||||
tgt.playerHP = max(1, tgt.playerHP-eff.AllyHeal)
|
||||
st.events = append(st.events, CombatEvent{
|
||||
Round: st.round, Phase: turnCombatPhase.Name, Actor: "enemy", Action: "heal_inverted",
|
||||
Damage: before - tgt.playerHP, PlayerHP: tgt.playerHP, EnemyHP: st.enemyHP,
|
||||
Seat: eff.AllySeat, Desc: eff.Label,
|
||||
})
|
||||
} else {
|
||||
cap := max(1, tgt.hpMax-tgt.maxHPDrain)
|
||||
before := tgt.playerHP
|
||||
tgt.playerHP = min(cap, tgt.playerHP+eff.AllyHeal)
|
||||
st.events = append(st.events, CombatEvent{
|
||||
Round: st.round, Phase: turnCombatPhase.Name, Actor: "player", Action: "ally_heal",
|
||||
Damage: tgt.playerHP - before, PlayerHP: tgt.playerHP, EnemyHP: st.enemyHP,
|
||||
Seat: eff.AllySeat, Desc: eff.Label,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// Arm / replace the concentration aura. A new concentration cast overwrites
|
||||
@@ -952,6 +980,8 @@ func (te *turnEngine) commit() {
|
||||
s.EnemyAtkBuff = st.enemyAtkBuff
|
||||
s.EnemyRewindHP = st.enemyRewindHP
|
||||
s.EnemyRewindUsed = st.enemyRewindUsed
|
||||
s.InversionActive = st.inversionActive
|
||||
s.InversionTelegraph = st.inversionTelegraph
|
||||
|
||||
te.sess.TurnLog = append(te.sess.TurnLog, st.events...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user