go_games_collection/wayfarer_ship.go

244 lines
8.3 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"
// WayfarerEquipment — необязательное снаряжение корабля.
type WayfarerEquipment int
const (
EquipFuelScoop WayfarerEquipment = iota
EquipCargoExpansion
EquipShieldBooster
EquipEscapePod
EquipBetterLaser
)
// WayfarerShip — полное состояние корабля и его владельца.
type WayfarerShip struct {
Credits float64
FuelCapacity float64
Fuel float64
FuelPerLY float64 // расход топлива на 1 условный световой год
CargoCapacity int
Cargo []int // Cargo[i] — количество товара i в трюме
CargoCostBasis []float64 // CargoCostBasis[i] — средняя цена покупки за единицу текущего груза i (0, если груза нет)
MaxHull int
Hull int
MaxShield int
Shield int
LaserPower int
Equipment map[WayfarerEquipment]bool
Kills int // сколько вражеских кораблей уничтожено — для рейтинга пилота
CurrentSystem WayfarerSystemID
Visited map[WayfarerSystemID]bool
ActiveQuest *WayfarerQuestState
CompletedQuests map[string]bool
FailedQuests map[string]bool
}
// NewWayfarerShip создаёт начальный корабль игрока — скромный, но
// работоспособный торговец на старте карьеры.
func NewWayfarerShip(start WayfarerSystemID) *WayfarerShip {
s := &WayfarerShip{
Credits: 1000,
FuelCapacity: 7,
Fuel: 7,
FuelPerLY: 1.0 / 30.0, // условно: полный бак = ~7 прыжков по системе средней удалённости
CargoCapacity: 20,
MaxHull: 100,
Hull: 100,
MaxShield: 50,
Shield: 50,
LaserPower: 12,
Equipment: map[WayfarerEquipment]bool{},
CurrentSystem: start,
Visited: map[WayfarerSystemID]bool{start: true},
CompletedQuests: map[string]bool{},
FailedQuests: map[string]bool{},
}
s.Cargo = make([]int, len(WayfarerCommodities))
s.CargoCostBasis = make([]float64, len(WayfarerCommodities))
return s
}
// CargoUsed — сколько единиц трюма сейчас занято.
func (s *WayfarerShip) CargoUsed() int {
used := 0
for _, n := range s.Cargo {
used += n
}
return used
}
// CargoFree — сколько единиц трюма ещё свободно.
func (s *WayfarerShip) CargoFree() int {
return s.CargoCapacity - s.CargoUsed()
}
// MaxJumpRange — максимальное расстояние прыжка при полном баке.
func (s *WayfarerShip) MaxJumpRange() float64 {
return s.FuelCapacity / s.FuelPerLY
}
// FuelRange — максимальное расстояние прыжка при текущем запасе
// топлива.
func (s *WayfarerShip) FuelRange() float64 {
return s.Fuel / s.FuelPerLY
}
var (
errWayfarerInsufficientFunds = wayfarerErr("недостаточно кредитов")
errWayfarerCargoFull = wayfarerErr("трюм заполнен")
errWayfarerNoCargo = wayfarerErr("этого товара нет в трюме в таком количестве")
errWayfarerOutOfRange = wayfarerErr("не хватит топлива долететь так далеко")
errWayfarerSameSystem = wayfarerErr("вы уже в этой системе")
)
type wayfarerErr string
func (e wayfarerErr) Error() string { return string(e) }
// BuyCommodity покупает qty единиц товара commodityIdx по цене
// unitPrice за единицу (цена вычисляется вызывающим кодом через
// CommodityPrice — так UI и движок используют одно и то же число,
// без риска рассинхронизации от повторного вызова генератора
// случайности). Заодно обновляет среднюю цену покупки этого товара
// в трюме (взвешенное среднее старой и новой партии) — чтобы потом
// на рынке можно было показать, по какой цене товар куплен.
func (s *WayfarerShip) BuyCommodity(commodityIdx int, qty int, unitPrice float64) error {
if qty <= 0 {
return nil
}
if qty > s.CargoFree() {
return errWayfarerCargoFull
}
cost := unitPrice * float64(qty)
if cost > s.Credits {
return errWayfarerInsufficientFunds
}
s.Credits -= cost
oldQty := s.Cargo[commodityIdx]
oldTotal := s.CargoCostBasis[commodityIdx] * float64(oldQty)
s.Cargo[commodityIdx] += qty
s.CargoCostBasis[commodityIdx] = (oldTotal + cost) / float64(s.Cargo[commodityIdx])
return nil
}
// SellCommodity продаёт qty единиц товара commodityIdx по цене
// unitPrice за единицу.
func (s *WayfarerShip) SellCommodity(commodityIdx int, qty int, unitPrice float64) error {
if qty <= 0 {
return nil
}
if qty > s.Cargo[commodityIdx] {
return errWayfarerNoCargo
}
s.Cargo[commodityIdx] -= qty
s.Credits += unitPrice * float64(qty)
if s.Cargo[commodityIdx] == 0 {
s.CargoCostBasis[commodityIdx] = 0 // трюм по этому товару опустел — старая цена покупки больше не актуальна
}
return nil
}
// Refuel заправляет корабль на amount единиц топлива (не больше
// полного бака) по цене pricePerUnit за единицу.
func (s *WayfarerShip) Refuel(amount float64, pricePerUnit float64) error {
if amount <= 0 {
return nil
}
if s.Fuel+amount > s.FuelCapacity {
amount = s.FuelCapacity - s.Fuel
}
cost := amount * pricePerUnit
if cost > s.Credits {
return errWayfarerInsufficientFunds
}
s.Credits -= cost
s.Fuel += amount
return nil
}
// Repair чинит корпус на amount очков по цене pricePerPoint.
func (s *WayfarerShip) Repair(amount int, pricePerPoint float64) error {
if amount <= 0 {
return nil
}
if s.Hull+amount > s.MaxHull {
amount = s.MaxHull - s.Hull
}
cost := float64(amount) * pricePerPoint
if cost > s.Credits {
return errWayfarerInsufficientFunds
}
s.Credits -= cost
s.Hull += amount
return nil
}
// JumpTo перемещает корабль в целевую систему, если хватает
// топлива, списывая расход по фактическому расстоянию.
func (s *WayfarerShip) JumpTo(target WayfarerSystemID) error {
if target == s.CurrentSystem {
return errWayfarerSameSystem
}
from := GenerateSystem(s.CurrentSystem)
to := GenerateSystem(target)
dist := from.Distance(to)
fuelNeeded := dist * s.FuelPerLY
if fuelNeeded > s.Fuel {
return errWayfarerOutOfRange
}
s.Fuel -= fuelNeeded
s.CurrentSystem = target
s.Visited[target] = true
return nil
}
// wayfarerRatingThresholds — пороги числа побед для присвоения
// пилотского рейтинга (намёк на классическую систему рангов Elite —
// свой набор названий и порогов, не копия оригинальных чисел).
var wayfarerRatingThresholds = []struct {
Kills int
Key string
}{
{0, "rating.harmless"},
{4, "rating.mostly_harmless"},
{12, "rating.poor"},
{28, "rating.average"},
{50, "rating.above_average"},
{100, "rating.competent"},
{200, "rating.dangerous"},
{400, "rating.deadly"},
{800, "rating.wayfarer_elite"},
}
// PilotRating возвращает ключ перевода текущего пилотского ранга.
func (s *WayfarerShip) PilotRating() string {
rating := wayfarerRatingThresholds[0].Key
for _, r := range wayfarerRatingThresholds {
if s.Kills >= r.Kills {
rating = r.Key
}
}
return rating
}
// NetWorth — кредиты плюс примерная стоимость груза в трюме (по
// половине базовой цены каждого товара — грубая, но достаточная для
// отображения общего "веса" игрока оценка).
func (s *WayfarerShip) NetWorth() float64 {
worth := s.Credits
for i, n := range s.Cargo {
worth += float64(n) * WayfarerCommodities[i].BasePrice * 0.5
}
return math.Round(worth)
}