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
}
