mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Death source tracking + combat threat/jitter fixes
- Adventure characters now record DeathSource ("adventure"|"arena") and
DeathLocation when killed. Threaded through Kill() and transitionDeath.
Daily report uses the recorded death location instead of misattributing
arena deaths to the day's adventure log: when DeathSource != "adventure",
the adventure block shows the alive icon and a separate "Later fell in
{location}." line. Standout-loss flavor uses DeathLocation. Empty
DeathSource (legacy rows) falls back to current behavior.
- calcDamage applies a ±15% per-hit jitter, so successive hits in a phase
no longer produce identical numbers. Previously every hit in a phase was
flat (e.g. 17, 17, 17, 17) and mirror-symmetric between player/enemy,
reading as scripted.
- assessThreat rewritten to use the engine's actual penetration formula:
damage-per-round each direction, then rounds-to-kill ratio. The old
additive HP+Attack*3 model ignored Defense, so even fights could be
rated trivial — players died after consumables were skipped with the
"threat assessed as manageable" message.
- SelectConsumables never skips on trivial when arenaRound > 0. Arena
losses cost real money and equipment, so the cost of a wrong assessment
is too high to gamble on; adventure-side fights still skip trivial
threats to avoid wasting items on chump enemies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,17 +86,31 @@ const (
|
||||
threatDangerous // > 1.0
|
||||
)
|
||||
|
||||
// assessThreat estimates rounds-to-kill in both directions using the same
|
||||
// penetration model as the combat engine, then bands the threat by the ratio
|
||||
// of player-RTK to enemy-RTK. Old additive HP+Attack model ignored Defense and
|
||||
// rated even fights as "trivial" — players died after consumables were skipped.
|
||||
func assessThreat(player, enemy CombatStats) threatLevel {
|
||||
playerPower := float64(player.MaxHP + player.Attack*3)
|
||||
enemyPower := float64(enemy.MaxHP + enemy.Attack*3)
|
||||
if playerPower == 0 {
|
||||
const K = 40.0
|
||||
dprPlayer := float64(player.Attack) * (K / (K + float64(enemy.Defense)))
|
||||
dprEnemy := float64(enemy.Attack) * (K / (K + float64(player.Defense)))
|
||||
if dprPlayer < 1 {
|
||||
dprPlayer = 1
|
||||
}
|
||||
if dprEnemy < 1 {
|
||||
dprEnemy = 1
|
||||
}
|
||||
rtkEnemy := float64(enemy.MaxHP) / dprPlayer // rounds for player to kill enemy
|
||||
rtkPlayer := float64(player.MaxHP) / dprEnemy // rounds for enemy to kill player
|
||||
if rtkPlayer <= 0 {
|
||||
return threatDangerous
|
||||
}
|
||||
ratio := enemyPower / playerPower
|
||||
// ratio < 1: player wins the race (lower = more dominant). >1: enemy wins.
|
||||
ratio := rtkEnemy / rtkPlayer
|
||||
switch {
|
||||
case ratio < 0.4:
|
||||
case ratio < 0.35:
|
||||
return threatTrivial
|
||||
case ratio < 0.7:
|
||||
case ratio < 0.65:
|
||||
return threatEasy
|
||||
case ratio < 1.0:
|
||||
return threatCompetitive
|
||||
@@ -111,7 +125,10 @@ func assessThreat(player, enemy CombatStats) threatLevel {
|
||||
// contentTier caps consumable tier to the content being fought (0 = no cap).
|
||||
func SelectConsumables(inventory []ConsumableItem, playerStats, enemyStats CombatStats, arenaRound int, contentTier int) []ConsumableItem {
|
||||
threat := assessThreat(playerStats, enemyStats)
|
||||
if threat == threatTrivial {
|
||||
// Arena losses cost real money + equipment durability — never skip there,
|
||||
// even if the threat looks trivial. Adventure-side fights still skip
|
||||
// trivial threats to avoid wasting items on chump enemies.
|
||||
if threat == threatTrivial && arenaRound == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user