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() }