package main import "math/rand" // WayfarerCombatOutcome — текущий/итоговый исход боя. type WayfarerCombatOutcome int const ( CombatOngoing WayfarerCombatOutcome = iota CombatWon // враг уничтожен CombatLost // корабль игрока уничтожен CombatFled // игрок сбежал CombatNegotiated // удалось договориться ) // Базовые вероятности успеха действий "бегство"/"переговоры" — в // сумме с некоторой случайностью боя это делает обе опции реальными, // но не гарантированными альтернативами прямому бою. const ( wayfarerFleeChance = 0.45 wayfarerNegotiateChance = 0.35 ) // WayfarerCombatState — состояние одного пошагового боя. type WayfarerCombatState struct { EnemyNameKey string EnemyLaser int EnemyHull int EnemyMaxHull int PlayerLaser int PlayerHull int PlayerMaxHull int PlayerShield int PlayerMaxShield int Negotiable bool // можно ли вообще пытаться договориться с этим противником Round int Outcome WayfarerCombatOutcome LastPlayerDamage int // урон, нанесённый игроком в последнем раунде (для лога) LastEnemyDamage int // урон, полученный игроком в последнем раунде } // NewWayfarerCombat создаёт новое состояние боя. func NewWayfarerCombat(enemyNameKey string, enemyHull, enemyLaser int, playerHull, playerMaxHull, playerShield, playerMaxShield, playerLaser int, negotiable bool) *WayfarerCombatState { return &WayfarerCombatState{ EnemyNameKey: enemyNameKey, EnemyHull: enemyHull, EnemyMaxHull: enemyHull, EnemyLaser: enemyLaser, PlayerHull: playerHull, PlayerMaxHull: playerMaxHull, PlayerShield: playerShield, PlayerMaxShield: playerMaxShield, PlayerLaser: playerLaser, Negotiable: negotiable, } } // rollDamage — базовый урон laser ± 30% случайного разброса. func rollDamage(laser int, rnd *rand.Rand) int { factor := 0.7 + rnd.Float64()*0.6 dmg := int(float64(laser) * factor) if dmg < 1 { dmg = 1 } return dmg } // applyDamage наносит урон dmg сначала по щиту, затем по корпусу // (щит поглощает урон первым, как в большинстве похожих игр). func applyDamage(shield, hull *int, dmg int) { if *shield > 0 { absorbed := dmg if absorbed > *shield { absorbed = *shield } *shield -= absorbed dmg -= absorbed } if dmg > 0 { *hull -= dmg if *hull < 0 { *hull = 0 } } } // Attack — игрок атакует; если враг не уничтожен, враг атакует в // ответ в тот же раунд. func (c *WayfarerCombatState) Attack(rnd *rand.Rand) { if c.Outcome != CombatOngoing { return } c.Round++ playerDmg := rollDamage(c.PlayerLaser, rnd) c.EnemyHull -= playerDmg if c.EnemyHull < 0 { c.EnemyHull = 0 } c.LastPlayerDamage = playerDmg if c.EnemyHull == 0 { c.Outcome = CombatWon c.LastEnemyDamage = 0 return } enemyDmg := rollDamage(c.EnemyLaser, rnd) applyDamage(&c.PlayerShield, &c.PlayerHull, enemyDmg) c.LastEnemyDamage = enemyDmg if c.PlayerHull == 0 { c.Outcome = CombatLost } } // Flee — попытка сбежать из боя. При неудаче враг наносит полный // удар (без ответа игрока в этом раунде — сосредоточились на // бегстве, не на атаке). func (c *WayfarerCombatState) Flee(rnd *rand.Rand) bool { if c.Outcome != CombatOngoing { return false } c.Round++ if rnd.Float64() < wayfarerFleeChance { c.Outcome = CombatFled c.LastEnemyDamage = 0 return true } enemyDmg := rollDamage(c.EnemyLaser, rnd) applyDamage(&c.PlayerShield, &c.PlayerHull, enemyDmg) c.LastEnemyDamage = enemyDmg if c.PlayerHull == 0 { c.Outcome = CombatLost } return false } // Negotiate — попытка договориться (доступна только если // c.Negotiable). При неудаче враг наносит полный удар, как и при // неудачном бегстве. func (c *WayfarerCombatState) Negotiate(rnd *rand.Rand) bool { if c.Outcome != CombatOngoing || !c.Negotiable { return false } c.Round++ if rnd.Float64() < wayfarerNegotiateChance { c.Outcome = CombatNegotiated c.LastEnemyDamage = 0 return true } enemyDmg := rollDamage(c.EnemyLaser, rnd) applyDamage(&c.PlayerShield, &c.PlayerHull, enemyDmg) c.LastEnemyDamage = enemyDmg if c.PlayerHull == 0 { c.Outcome = CombatLost } return false }