243 lines
6.9 KiB
Go
243 lines
6.9 KiB
Go
package game
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type CargoType string
|
|
|
|
const (
|
|
CargoColonist CargoType = "COL" // Колонисты
|
|
CargoMaterial CargoType = "MAT" // Сырьё
|
|
CargoCapital CargoType = "CAP" // Промышленность
|
|
)
|
|
|
|
var (
|
|
CargoTypeSet map[string]CargoType = map[string]CargoType{
|
|
CargoColonist.String(): CargoColonist,
|
|
CargoMaterial.String(): CargoMaterial,
|
|
CargoCapital.String(): CargoCapital,
|
|
}
|
|
)
|
|
|
|
func (ct CargoType) Ref() *CargoType {
|
|
return &ct
|
|
}
|
|
|
|
func (ct CargoType) String() string {
|
|
return string(ct)
|
|
}
|
|
|
|
type ShipGroupState string
|
|
|
|
const (
|
|
StateInOrbit ShipGroupState = "In_Orbit"
|
|
StateLaunched ShipGroupState = "Launched"
|
|
StateInSpace ShipGroupState = "In_Space"
|
|
StateUpgrade ShipGroupState = "Upgrade"
|
|
StateTransfer ShipGroupState = "Transfer_Status"
|
|
)
|
|
|
|
type InSpace struct {
|
|
Origin uint `json:"origin"`
|
|
X float64 `json:"x"`
|
|
Y float64 `json:"y"`
|
|
// zero is for Launched status
|
|
// TODO: calculate range dynamically -BUT- if affects ShipGroup.State()
|
|
Range float64 `json:"range"`
|
|
}
|
|
|
|
func (is InSpace) Equal(other InSpace) bool {
|
|
return is.Origin == other.Origin && is.X == other.X && is.Y == other.Y
|
|
}
|
|
|
|
func (is InSpace) Launched() bool {
|
|
return is.Range == 0
|
|
}
|
|
|
|
type InUpgrade struct {
|
|
UpgradeTech []UpgradePreference `json:"preference"`
|
|
}
|
|
|
|
func (iu InUpgrade) Cost() float64 {
|
|
var sum float64
|
|
for i := range iu.UpgradeTech {
|
|
sum += iu.UpgradeTech[i].Cost
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func (iu InUpgrade) TechCost(t Tech) float64 {
|
|
for i := range iu.UpgradeTech {
|
|
if iu.UpgradeTech[i].Tech == t {
|
|
return iu.UpgradeTech[i].Cost
|
|
}
|
|
}
|
|
return 0.
|
|
}
|
|
|
|
type UpgradePreference struct {
|
|
Tech Tech `json:"tech"`
|
|
Level float64 `json:"level"`
|
|
Cost float64 `json:"cost"`
|
|
}
|
|
|
|
type Tech string
|
|
|
|
const (
|
|
TechAll Tech = "ALL"
|
|
TechDrive Tech = "DRIVE"
|
|
TechWeapons Tech = "WEAPONS"
|
|
TechShields Tech = "SHIELDS"
|
|
TechCargo Tech = "CARGO"
|
|
)
|
|
|
|
func (t Tech) String() string {
|
|
return string(t)
|
|
}
|
|
|
|
type ShipGroup struct {
|
|
Index uint `json:"index"` // FIXME: use UUID for Group Index (ordered)
|
|
OwnerID uuid.UUID `json:"ownerId"` // Race link
|
|
TypeID uuid.UUID `json:"typeId"` // ShipType link
|
|
FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet link
|
|
Number uint `json:"number"` // Number (quantity) ships of specific ShipType
|
|
|
|
CargoType *CargoType `json:"loadType,omitempty"`
|
|
Load Float `json:"load"` // Cargo loaded - "Масса груза"
|
|
|
|
Tech TechSet `json:"tech"`
|
|
|
|
// TODO: TEST: Destination, Origin, Range
|
|
Destination uint `json:"destination"`
|
|
StateInSpace *InSpace `json:"stateInSpace,omitempty"`
|
|
StateUpgrade *InUpgrade `json:"stateUpgrade,omitempty"`
|
|
}
|
|
|
|
func (sg ShipGroup) TechLevel(t Tech) Float {
|
|
return F(sg.Tech.Value(t))
|
|
}
|
|
|
|
// TODO: refactor to separate method with *ShipGroup as parameter
|
|
func (sg *ShipGroup) SetTechLevel(t Tech, v float64) {
|
|
sg.Tech = sg.Tech.Set(t, v)
|
|
}
|
|
|
|
func (sg ShipGroup) State() ShipGroupState {
|
|
switch {
|
|
case sg.StateInSpace == nil && sg.StateUpgrade == nil:
|
|
return StateInOrbit
|
|
case sg.StateInSpace != nil && sg.StateUpgrade == nil:
|
|
if sg.StateInSpace.Range > 0 {
|
|
return StateInSpace
|
|
}
|
|
return StateLaunched
|
|
case sg.StateUpgrade != nil && sg.StateInSpace == nil:
|
|
return StateUpgrade
|
|
default:
|
|
panic(fmt.Sprintf("ambigous group state: in_space=%#v upgrage=%#v", sg.StateInSpace, sg.StateUpgrade))
|
|
}
|
|
}
|
|
|
|
func (sg ShipGroup) OnPlanet() (uint, bool) {
|
|
switch sg.State() {
|
|
case StateInOrbit:
|
|
return sg.Destination, true
|
|
case StateLaunched:
|
|
return sg.StateInSpace.Origin, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func (sg ShipGroup) Coord() (float64, float64, bool) {
|
|
state := sg.State()
|
|
if state == StateInSpace || state == StateLaunched {
|
|
return sg.StateInSpace.X, sg.StateInSpace.Y, true
|
|
}
|
|
return 0, 0, false
|
|
}
|
|
|
|
func (sg ShipGroup) Equal(other ShipGroup) bool {
|
|
return sg.OwnerID == other.OwnerID &&
|
|
sg.TypeID == other.TypeID &&
|
|
sg.FleetID == other.FleetID &&
|
|
sg.TechLevel(TechDrive) == other.TechLevel(TechDrive) &&
|
|
sg.TechLevel(TechWeapons) == other.TechLevel(TechWeapons) &&
|
|
sg.TechLevel(TechShields) == other.TechLevel(TechShields) &&
|
|
sg.TechLevel(TechCargo) == other.TechLevel(TechCargo) &&
|
|
sg.CargoType == other.CargoType &&
|
|
sg.Load.F()/float64(sg.Number) == other.Load.F()/float64(other.Number) &&
|
|
sg.State() == other.State()
|
|
}
|
|
|
|
// Грузоподъёмность
|
|
func (sg ShipGroup) CargoCapacity(st *ShipType) float64 {
|
|
return sg.TechLevel(TechCargo).F() * (st.Cargo + (st.Cargo*st.Cargo)/20) * float64(sg.Number)
|
|
}
|
|
|
|
// Масса перевозимого груза -
|
|
// общее количество единиц груза, деленное на технологический уровень Грузоперевозок
|
|
func (sg ShipGroup) CarryingMass() float64 {
|
|
return sg.Load.F() / sg.TechLevel(TechCargo).F()
|
|
}
|
|
|
|
// Масса группы без учёта груза
|
|
func (sg ShipGroup) EmptyMass(st *ShipType) float64 {
|
|
return st.EmptyMass() * float64(sg.Number)
|
|
}
|
|
|
|
// Полная масса -
|
|
// массу корабля самого по себе плюс масса перевозимого груза
|
|
func (sg ShipGroup) FullMass(st *ShipType) float64 {
|
|
return sg.EmptyMass(st) + sg.CarryingMass()
|
|
}
|
|
|
|
// Эффективность двигателя -
|
|
// равна мощности Двигателей, умноженной на технологический уровень блока Двигателей
|
|
func (sg ShipGroup) DriveEffective(st *ShipType) float64 {
|
|
return st.Drive * sg.TechLevel(TechDrive).F()
|
|
}
|
|
|
|
// Корабли перемещаются за один ход на количество световых лет, равное
|
|
// эффективности двигателя, умноженной на 20 и деленной на "Полную массу" корабля
|
|
func (sg ShipGroup) Speed(st *ShipType) float64 {
|
|
return sg.DriveEffective(st) * 20 / sg.FullMass(st)
|
|
}
|
|
|
|
func (sg ShipGroup) UpgradeDriveCost(st *ShipType, drive float64) float64 {
|
|
return (1 - sg.TechLevel(TechDrive).F()/drive) * 10 * st.Drive
|
|
}
|
|
|
|
// TODO: test on other values
|
|
func (sg ShipGroup) UpgradeWeaponsCost(st *ShipType, weapons float64) float64 {
|
|
return (1 - sg.TechLevel(TechWeapons).F()/weapons) * 10 * st.WeaponsBlockMass()
|
|
}
|
|
|
|
func (sg ShipGroup) UpgradeShieldsCost(st *ShipType, shields float64) float64 {
|
|
return (1 - sg.TechLevel(TechShields).F()/shields) * 10 * st.Shields
|
|
}
|
|
|
|
func (sg ShipGroup) UpgradeCargoCost(st *ShipType, cargo float64) float64 {
|
|
return (1 - sg.TechLevel(TechCargo).F()/cargo) * 10 * st.Cargo
|
|
}
|
|
|
|
// Мощность бомбардировки
|
|
func (sg ShipGroup) BombingPower(st *ShipType) float64 {
|
|
return (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons).F())/10. + 1.) *
|
|
st.Weapons *
|
|
sg.TechLevel(TechWeapons).F() *
|
|
float64(st.Armament) *
|
|
float64(sg.Number)
|
|
}
|
|
|
|
func (sg ShipGroup) CargoString() string {
|
|
if sg.CargoType == nil {
|
|
return "-"
|
|
}
|
|
return sg.CargoType.String()
|
|
}
|