Files
galaxy-game/internal/model/game/group.go
T
2026-02-10 20:54:43 +03:00

230 lines
6.5 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"
)
func (sgs ShipGroupState) String() string {
return string(sgs)
}
type InSpace struct {
// X, Y are nil for Launched state
Origin uint `json:"origin"`
X *Float `json:"x,omitempty"`
Y *Float `json:"y,omitempty"`
}
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 {
if (is.X == nil && is.Y != nil) || (is.X != nil && is.Y == nil) {
panic("group in space state invalid: one of coordinate is not set")
}
return is.X == nil && is.Y == is.X
}
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.F()
}
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.F()
}
}
return 0.
}
type UpgradePreference struct {
Tech Tech `json:"tech"`
Level Float `json:"level"`
Cost Float `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 {
ID uuid.UUID `json:"id"`
OwnerID uuid.UUID `json:"ownerId"` // Race reference
TypeID uuid.UUID `json:"typeId"` // ShipType reference
FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet reference
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"` //
Destination uint `json:"destination"` //
StateInSpace *InSpace `json:"inSpace,omitempty"` //
StateUpgrade *InUpgrade `json:"upgrade,omitempty"` //
StateTransfer bool `json:"transfer,omitempty"` //
}
func (sg ShipGroup) TechLevel(t Tech) Float {
return F(sg.Tech.Value(t))
}
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.Launched() {
return StateInSpace
}
if sg.StateTransfer {
return StateTransfer
}
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) AtPlanet() (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.F(), sg.StateInSpace.Y.F(), 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))).F() == (other.Load/F(float64(other.Number))).F() &&
sg.State() == other.State()
}
// Грузоподъёмность
func (sg ShipGroup) CargoCapacity(st *ShipType) float64 {
return sg.TechLevel(TechCargo).F() * (st.Cargo.F() + (st.Cargo.F()*st.Cargo.F())/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.F() * sg.TechLevel(TechDrive).F()
}
// Корабли перемещаются за один ход на количество световых лет, равное
// эффективности двигателя, умноженной на 20 и деленной на "Полную массу" корабля
func (sg ShipGroup) Speed(st *ShipType) float64 {
return sg.DriveEffective(st) * 20 / sg.FullMass(st)
}
// Мощность бомбардировки
func (sg ShipGroup) BombingPower(st *ShipType) float64 {
return (math.Sqrt(st.Weapons.F()*sg.TechLevel(TechWeapons).F())/10. + 1.) *
st.Weapons.F() *
sg.TechLevel(TechWeapons).F() *
float64(st.Armament) *
float64(sg.Number)
}
func (sg ShipGroup) CargoString() string {
if sg.CargoType == nil {
return "-"
}
return sg.CargoType.String()
}