go_games_collection/wayfarer_combat.go

165 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}