mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
Combat: add Sudden Death phase + fix tiebreak to use absolute HP
Players reported the prior 6-round exhaustion timeout felt arbitrary when both sides still had plenty of HP. Two fixes: - Add a "Sudden Death" 4th phase (3-4 extra rounds) so most fights resolve naturally before any timeout. Total combat length now caps at ~10 rounds. - When the timeout does fire, tiebreak by absolute HP instead of HP%. The %-based logic was unintuitive — a player at 88/123 (71%) would lose to a boss at 83/97 (86%) despite having more HP remaining. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -171,16 +171,23 @@ type MonsterAbility struct {
|
|||||||
|
|
||||||
// ── Default Phase Definitions ────────────────────────────────────────────────
|
// ── Default Phase Definitions ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Sudden Death is a fallback phase that runs only when none of the earlier
|
||||||
|
// phases produced a kill. Adds enough rounds to push total combat length
|
||||||
|
// to ~10 in the worst case, so the absolute-HP tiebreaker below is rarely
|
||||||
|
// hit. Players reported the prior 6-round insta-timeout felt arbitrary
|
||||||
|
// when both sides still had plenty of HP.
|
||||||
var defaultCombatPhases = []CombatPhase{
|
var defaultCombatPhases = []CombatPhase{
|
||||||
{"Opening", 2, 0.6, 0.8, 1.5, 0.15},
|
{"Opening", 2, 0.6, 0.8, 1.5, 0.15},
|
||||||
{"Clash", 3, 1.2, 1.0, 0.8, 0.08},
|
{"Clash", 3, 1.2, 1.0, 0.8, 0.08},
|
||||||
{"Decisive", 1, 1.0, 0.7, 1.0, 0.05},
|
{"Decisive", 1, 1.0, 0.7, 1.0, 0.05},
|
||||||
|
{"Sudden Death", 3, 1.1, 0.6, 1.0, 0.05},
|
||||||
}
|
}
|
||||||
|
|
||||||
var dungeonCombatPhases = []CombatPhase{
|
var dungeonCombatPhases = []CombatPhase{
|
||||||
{"Opening", 1, 0.8, 0.8, 1.2, 0.10},
|
{"Opening", 1, 0.8, 0.8, 1.2, 0.10},
|
||||||
{"Clash", 2, 1.0, 1.0, 0.8, 0.08},
|
{"Clash", 2, 1.0, 1.0, 0.8, 0.08},
|
||||||
{"Decisive", 1, 1.0, 0.7, 1.0, 0.05},
|
{"Decisive", 1, 1.0, 0.7, 1.0, 0.05},
|
||||||
|
{"Sudden Death", 4, 1.1, 0.6, 1.0, 0.05},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Simulation ───────────────────────────────────────────────────────────────
|
// ── Simulation ───────────────────────────────────────────────────────────────
|
||||||
@@ -305,12 +312,10 @@ func SimulateCombat(player, enemy Combatant, phases []CombatPhase) CombatResult
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we exhaust all phases without a kill, the side with more HP% wins.
|
// If we exhaust all phases without a kill, tiebreak by absolute HP.
|
||||||
playerMax := max(1, player.Stats.MaxHP)
|
// Was %-based, which produced unintuitive outcomes (e.g. 88/123 player
|
||||||
enemyMax := max(1, enemy.Stats.MaxHP)
|
// losing to 83/97 boss because the boss had a higher percentage).
|
||||||
playerPct := float64(st.playerHP) / float64(playerMax)
|
if st.playerHP < st.enemyHP {
|
||||||
enemyPct := float64(st.enemyHP) / float64(enemyMax)
|
|
||||||
if playerPct < enemyPct {
|
|
||||||
st.playerHP = 0
|
st.playerHP = 0
|
||||||
} else {
|
} else {
|
||||||
st.enemyHP = 0
|
st.enemyHP = 0
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ func phaseHeader(name string) string {
|
|||||||
return pickRand(decisiveHeaders)
|
return pickRand(decisiveHeaders)
|
||||||
case "pre_combat", "pre":
|
case "pre_combat", "pre":
|
||||||
return "⚔️ **The fight begins.**"
|
return "⚔️ **The fight begins.**"
|
||||||
|
case "Sudden Death", "sudden_death":
|
||||||
|
return "⚔️ **Sudden Death — No Quarter**"
|
||||||
case "exhaust":
|
case "exhaust":
|
||||||
return "⚔️ **Exhaustion — Time Runs Out**"
|
return "⚔️ **Exhaustion — Time Runs Out**"
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user