165 lines
4.9 KiB
Go
165 lines
4.9 KiB
Go
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
|
||
}
|