race quit, transfer state, refactor
This commit is contained in:
@@ -43,7 +43,7 @@ func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType {
|
||||
func (c *Cache) RaceIndex(ID uuid.UUID) int {
|
||||
if c.cacheRaceIndexByID == nil {
|
||||
c.cacheRaceIndexByID = make(map[uuid.UUID]int)
|
||||
for i := range c.g.Race {
|
||||
for i := range c.listRaceIdx() {
|
||||
c.cacheRaceIndexByID[c.g.Race[i].ID] = i
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
e "github.com/iliadenisov/galaxy/internal/error"
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
)
|
||||
|
||||
func (c Controller) QuitGame(actor string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Cache.g.Race[ri].TTL = 3
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Controller) GiveVotes(actor, acceptor string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rec, err := c.Cache.validRace(acceptor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Cache.g.Race[ri].VoteFor = c.Cache.g.Race[rec].ID
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME: remove, not a command
|
||||
func (c Controller) Relation(actor, acceptor string) (game.Relation, error) {
|
||||
r1, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return game.Relation(""), err
|
||||
}
|
||||
r2, err := c.Cache.validRace(acceptor)
|
||||
if err != nil {
|
||||
return game.Relation(""), err
|
||||
}
|
||||
return c.Cache.Relation(r1, r2), nil
|
||||
}
|
||||
|
||||
func (c Controller) UpdateRelation(actor, acceptor string, rel game.Relation) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var other int
|
||||
if actor == acceptor {
|
||||
other = ri
|
||||
} else if other, err = c.Cache.validRace(acceptor); err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.UpdateRelation(ri, other, rel)
|
||||
}
|
||||
|
||||
func (c *Controller) JoinShipGroupToFleet(actor, fleetName string, group, count uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.JoinShipGroupToFleet(ri, fleetName, group, count)
|
||||
}
|
||||
|
||||
func (c *Controller) JoinFleets(actor, fleetSourceName, fleetTargetName string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.JoinFleets(ri, fleetSourceName, fleetTargetName)
|
||||
}
|
||||
|
||||
func (c *Controller) SendFleet(actor, fleetName string, planetNumber uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, ok := c.Cache.fleetIndex(ri, fleetName)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("fleet %q", fleetName)
|
||||
}
|
||||
return c.Cache.SendFleet(ri, fi, planetNumber)
|
||||
}
|
||||
|
||||
func (c *Controller) RenamePlanet(actor string, planetNumber int, typeName string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.RenamePlanet(ri, planetNumber, typeName)
|
||||
}
|
||||
|
||||
func (c *Controller) PlanetProduction(actor string, planetNumber int, prodType, subject string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var prod game.ProductionType
|
||||
switch game.ProductionType(strings.ToUpper(prodType)) {
|
||||
case game.ProductionMaterial:
|
||||
prod = game.ProductionMaterial
|
||||
case game.ProductionCapital:
|
||||
prod = game.ProductionCapital
|
||||
case game.ResearchDrive:
|
||||
prod = game.ResearchDrive
|
||||
case game.ResearchWeapons:
|
||||
prod = game.ResearchWeapons
|
||||
case game.ResearchShields:
|
||||
prod = game.ResearchShields
|
||||
case game.ResearchCargo:
|
||||
prod = game.ResearchCargo
|
||||
case game.ResearchScience:
|
||||
prod = game.ResearchScience
|
||||
case game.ProductionShip:
|
||||
prod = game.ProductionShip
|
||||
default:
|
||||
return e.NewProductionInvalidError(prodType)
|
||||
}
|
||||
return c.Cache.PlanetProduction(ri, planetNumber, prod, subject)
|
||||
}
|
||||
|
||||
func (c *Controller) SetRoute(actor, loadType string, origin, destination uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rt, ok := game.RouteTypeSet[loadType]
|
||||
if !ok {
|
||||
return e.NewCargoTypeInvalidError(loadType)
|
||||
}
|
||||
return c.Cache.SetRoute(ri, rt, origin, destination)
|
||||
}
|
||||
|
||||
func (c *Controller) RemoveRoute(actor, loadType string, origin uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rt, ok := game.RouteTypeSet[loadType]
|
||||
if !ok {
|
||||
return e.NewCargoTypeInvalidError(loadType)
|
||||
}
|
||||
return c.Cache.RemoveRoute(ri, rt, origin)
|
||||
}
|
||||
|
||||
func (c *Controller) CreateScience(actor, typeName string, drive, weapons, shields, cargo float64) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.CreateScience(ri, typeName, drive, weapons, shields, cargo)
|
||||
}
|
||||
|
||||
func (c *Controller) DeleteScience(actor, typeName string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.DeleteScience(ri, typeName)
|
||||
}
|
||||
|
||||
func (c *Controller) CreateShipType(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.CreateShipType(ri, typeName, drive, ammo, weapons, shileds, cargo)
|
||||
}
|
||||
|
||||
func (c *Controller) MergeShipType(actor, name, targetName string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.MergeShipType(ri, name, targetName)
|
||||
}
|
||||
|
||||
func (c *Controller) DeleteShipType(actor, typeName string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.DeleteShipType(ri, typeName)
|
||||
}
|
||||
|
||||
func (c *Controller) SendGroup(actor string, groupIndex, planetNumber, quantity uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.SendGroup(ri, groupIndex, planetNumber, quantity)
|
||||
}
|
||||
|
||||
func (c *Controller) UpgradeGroup(actor string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.UpgradeGroup(ri, groupIndex, techInput, limitShips, limitLevel)
|
||||
}
|
||||
|
||||
func (c *Controller) JoinEqualGroups(actor string) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Cache.JoinEqualGroups(ri)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) BreakGroup(actor string, groupIndex, quantity uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.BreakGroup(ri, groupIndex, quantity)
|
||||
}
|
||||
|
||||
func (c *Controller) DisassembleGroup(actor string, groupIndex, quantity uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.DisassembleGroup(ri, groupIndex, quantity)
|
||||
}
|
||||
|
||||
func (c *Controller) LoadCargo(actor string, groupIndex uint, cargoType string, ships uint, quantity float64) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ct, ok := game.CargoTypeSet[cargoType]
|
||||
if !ok {
|
||||
return e.NewCargoTypeInvalidError(cargoType)
|
||||
}
|
||||
return c.Cache.LoadCargo(ri, groupIndex, ct, ships, quantity)
|
||||
}
|
||||
|
||||
func (c *Controller) UnloadCargo(actor string, groupIndex uint, ships uint, quantity float64) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.UnloadCargo(ri, groupIndex, ships, quantity)
|
||||
}
|
||||
|
||||
func (c *Controller) TransferGroup(actor, acceptor string, groupIndex, quantity uint) error {
|
||||
ri, err := c.Cache.validActor(actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
riAccept, err := c.Cache.validRace(acceptor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.TransferGroup(ri, riAccept, groupIndex, quantity)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ func (c *Cache) AddRace(n string) (int, uuid.UUID) {
|
||||
Relations: make([]game.RaceRelation, len(c.g.Race)),
|
||||
}
|
||||
c.g.Race = append(c.g.Race, *r)
|
||||
for i := range c.g.Race {
|
||||
for i := range c.listRaceIdx() {
|
||||
if c.g.Race[i].ID != id {
|
||||
c.g.Race[i].Relations = append(c.g.Race[i].Relations, game.RaceRelation{RaceID: id, Relation: game.RelationPeace})
|
||||
continue
|
||||
|
||||
@@ -16,6 +16,7 @@ var (
|
||||
ID: Race_0_ID,
|
||||
VoteFor: Race_0_ID,
|
||||
Name: "Race_0",
|
||||
TTL: 10,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: 1.1,
|
||||
game.TechWeapons: 1.2,
|
||||
@@ -28,6 +29,7 @@ var (
|
||||
ID: Race_1_ID,
|
||||
VoteFor: Race_1_ID,
|
||||
Name: "Race_1",
|
||||
TTL: 10,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: 2.1,
|
||||
game.TechWeapons: 2.2,
|
||||
|
||||
@@ -100,14 +100,6 @@ func (c *Cache) FleetSpeedAndMass(fi int) (float64, float64) {
|
||||
return speed, mass
|
||||
}
|
||||
|
||||
func (c *Controller) JoinShipGroupToFleet(raceName, fleetName string, group, count uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.JoinShipGroupToFleet(ri, fleetName, group, count)
|
||||
}
|
||||
|
||||
func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quantity uint) (err error) {
|
||||
c.validateRaceIndex(ri)
|
||||
name, ok := util.ValidateTypeName(fleetName)
|
||||
@@ -119,8 +111,8 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||
return e.NewShipsBusyError()
|
||||
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
|
||||
return e.NewShipsBusyError("state: %s", state)
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).Number < quantity {
|
||||
@@ -178,14 +170,6 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) JoinFleets(raceName, fleetSourceName, fleetTargetName string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.JoinFleets(ri, fleetSourceName, fleetTargetName)
|
||||
}
|
||||
|
||||
func (c *Cache) JoinFleets(ri int, fleetSourceName, fleetTargetName string) (err error) {
|
||||
fiSource, ok := c.fleetIndex(ri, fleetSourceName)
|
||||
if !ok {
|
||||
@@ -242,19 +226,21 @@ func (c *Cache) deleteFleetSafe(ri int, name string) error {
|
||||
return e.NewEntityInUseError("fleet %s: race %s, group #%d", name, c.g.Race[ri].Name, sg.Number)
|
||||
}
|
||||
}
|
||||
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
|
||||
c.cacheFleetIndexByID = nil
|
||||
c.unsafeDeleteFleet(fi)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) unsafeDeleteFleet(fi int) {
|
||||
c.validateFleetIndex(fi)
|
||||
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
|
||||
c.invalidateFleetCache()
|
||||
}
|
||||
|
||||
// Internal funcs
|
||||
|
||||
func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
|
||||
if len(c.cacheFleetIndexByID) == 0 {
|
||||
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
|
||||
for i := range c.g.Fleets {
|
||||
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
|
||||
}
|
||||
c.cacheFleetIndex()
|
||||
}
|
||||
if v, ok := c.cacheFleetIndexByID[ID]; ok {
|
||||
return v, true
|
||||
@@ -263,6 +249,17 @@ func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) cacheFleetIndex() {
|
||||
if c.cacheFleetIndexByID != nil {
|
||||
clear(c.cacheFleetIndexByID)
|
||||
} else {
|
||||
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
|
||||
}
|
||||
for i := range c.g.Fleets {
|
||||
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) MustFleetIndex(ID uuid.UUID) int {
|
||||
if v, ok := c.FleetIndex(ID); ok {
|
||||
return v
|
||||
|
||||
@@ -6,25 +6,13 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/util"
|
||||
)
|
||||
|
||||
func (c *Controller) SendFleet(raceName, fleetName string, planetNumber uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, ok := c.Cache.fleetIndex(ri, fleetName)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("fleet %q", fleetName)
|
||||
}
|
||||
return c.Cache.SendFleet(ri, fi, planetNumber)
|
||||
}
|
||||
|
||||
func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error {
|
||||
c.validateRaceIndex(ri)
|
||||
c.validateFleetIndex(fi)
|
||||
fleetState := c.FleetState(c.g.Fleets[fi].ID)
|
||||
sourcePlanet, ok := fleetState.AtPlanet()
|
||||
if !ok || game.StateInOrbit != fleetState.State && game.StateLaunched != fleetState.State {
|
||||
return e.NewShipsBusyError()
|
||||
return e.NewShipsBusyError("state: %s", fleetState.State)
|
||||
}
|
||||
|
||||
p1, ok := c.Planet(sourcePlanet)
|
||||
|
||||
@@ -118,6 +118,10 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) {
|
||||
gameMap.Planet[i].Number, gameMap.Planet[j].Number = gameMap.Planet[j].Number, gameMap.Planet[i].Number
|
||||
})
|
||||
|
||||
for i := range gameMap.Planet {
|
||||
g.Votes = g.Votes.Add(gameMap.Planet[i].Votes())
|
||||
}
|
||||
|
||||
g.Map = *gameMap
|
||||
|
||||
return g, nil
|
||||
|
||||
@@ -13,6 +13,9 @@ func MakeTurn(c *Controller, r Repo) error {
|
||||
// Next turn
|
||||
c.Cache.g.Turn += 1
|
||||
|
||||
// 00. Вышедшие расы удаляются из списка участвующих рас перед началом просчета очередного хода
|
||||
c.Cache.TurnWipeExtinctRaces()
|
||||
|
||||
// 01. Корабли, где это возможно, объединяются в группы.
|
||||
c.Cache.TurnMergeEqualShipGroups()
|
||||
|
||||
@@ -118,6 +121,13 @@ func MakeTurn(c *Controller, r Repo) error {
|
||||
}
|
||||
}
|
||||
|
||||
for i := range c.Cache.g.Race {
|
||||
if c.Cache.g.Race[i].Extinct {
|
||||
continue
|
||||
}
|
||||
c.Cache.g.Race[i].TTL -= 1
|
||||
}
|
||||
|
||||
// [ ] monitor memory consumption at this point?
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
e "github.com/iliadenisov/galaxy/internal/error"
|
||||
@@ -12,14 +11,6 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/util"
|
||||
)
|
||||
|
||||
func (c *Controller) RenamePlanet(raceName string, planetNumber int, typeName string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.RenamePlanet(ri, planetNumber, typeName)
|
||||
}
|
||||
|
||||
func (c *Cache) RenamePlanet(ri int, number int, name string) error {
|
||||
n, ok := util.ValidateTypeName(name)
|
||||
if !ok {
|
||||
@@ -39,35 +30,6 @@ func (c *Cache) RenamePlanet(ri int, number int, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) PlanetProduction(raceName string, planetNumber int, prodType, subject string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var prod game.ProductionType
|
||||
switch game.ProductionType(strings.ToUpper(prodType)) {
|
||||
case game.ProductionMaterial:
|
||||
prod = game.ProductionMaterial
|
||||
case game.ProductionCapital:
|
||||
prod = game.ProductionCapital
|
||||
case game.ResearchDrive:
|
||||
prod = game.ResearchDrive
|
||||
case game.ResearchWeapons:
|
||||
prod = game.ResearchWeapons
|
||||
case game.ResearchShields:
|
||||
prod = game.ResearchShields
|
||||
case game.ResearchCargo:
|
||||
prod = game.ResearchCargo
|
||||
case game.ResearchScience:
|
||||
prod = game.ResearchScience
|
||||
case game.ProductionShip:
|
||||
prod = game.ProductionShip
|
||||
default:
|
||||
return e.NewProductionInvalidError(prodType)
|
||||
}
|
||||
return c.Cache.PlanetProduction(ri, planetNumber, prod, subject)
|
||||
}
|
||||
|
||||
func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, subj string) error {
|
||||
c.validateRaceIndex(ri)
|
||||
if number < 0 {
|
||||
|
||||
+76
-44
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"slices"
|
||||
|
||||
e "github.com/iliadenisov/galaxy/internal/error"
|
||||
@@ -9,40 +10,11 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
)
|
||||
|
||||
func (c Controller) Relation(from, to string) (game.Relation, error) {
|
||||
r1, err := c.Cache.raceIndex(from)
|
||||
if err != nil {
|
||||
return game.Relation(""), err
|
||||
}
|
||||
r2, err := c.Cache.raceIndex(to)
|
||||
if err != nil {
|
||||
return game.Relation(""), err
|
||||
}
|
||||
return c.Cache.Relation(r1, r2), nil
|
||||
}
|
||||
|
||||
func (c Controller) UpdateRelation(race, opponent string, rel game.Relation) error {
|
||||
ri, err := c.Cache.raceIndex(race)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var other int
|
||||
if race == opponent {
|
||||
other = ri
|
||||
} else if other, err = c.Cache.raceIndex(opponent); err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.UpdateRelation(ri, other, rel)
|
||||
}
|
||||
|
||||
func (c *Cache) Relation(r1, r2 int) game.Relation {
|
||||
if c.cacheRelation == nil {
|
||||
c.cacheRelation = make(map[int]map[int]game.Relation)
|
||||
for r1 := range c.g.Race {
|
||||
for r2 := range c.g.Race {
|
||||
for r1 := range c.listRaceActingIdx() {
|
||||
for r2 := range c.listRaceActingIdx() {
|
||||
if r1 == r2 {
|
||||
continue
|
||||
}
|
||||
@@ -78,19 +50,6 @@ func (c *Cache) updateRelationCache(r1, r2 int, rel game.Relation) {
|
||||
c.cacheRelation[r1][r2] = rel
|
||||
}
|
||||
|
||||
func (c *Cache) GiveVotes(race, recipient string) error {
|
||||
ri, err := c.raceIndex(race)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rec, err := c.raceIndex(recipient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.g.Race[ri].VoteFor = c.g.Race[rec].ID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) Voted(ri int) int {
|
||||
c.validateRaceIndex(ri)
|
||||
return c.RaceIndex(c.g.Race[ri].VoteFor)
|
||||
@@ -123,6 +82,26 @@ func (c *Cache) validateRaceIndex(i int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) validActor(name string) (int, error) {
|
||||
i, err := c.validRace(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
c.g.Race[i].TTL = 10
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (c *Cache) validRace(name string) (int, error) {
|
||||
i, err := c.raceIndex(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if c.g.Race[i].Extinct {
|
||||
return -1, e.NewRaceExinctError(name)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (c *Cache) raceIndex(name string) (int, error) {
|
||||
i := slices.IndexFunc(c.g.Race, func(r game.Race) bool { return r.Name == name })
|
||||
if i < 0 {
|
||||
@@ -135,3 +114,56 @@ func (c *Cache) raceTechLevel(ri int, t game.Tech, v float64) {
|
||||
c.validateRaceIndex(ri)
|
||||
c.g.Race[ri].Tech = c.g.Race[ri].Tech.Set(t, v)
|
||||
}
|
||||
|
||||
func (c *Cache) TurnWipeExtinctRaces() {
|
||||
for i := range c.listRaceActingIdx() {
|
||||
if c.g.Race[i].TTL == 0 {
|
||||
c.wipeRace(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) wipeRace(ri int) {
|
||||
c.validateRaceIndex(ri)
|
||||
r := &c.g.Race[ri]
|
||||
c.g.ShipGroups = slices.DeleteFunc(c.g.ShipGroups, func(v game.ShipGroup) bool { return v.OwnerID == r.ID })
|
||||
c.g.Fleets = slices.DeleteFunc(c.g.Fleets, func(v game.Fleet) bool { return v.OwnerID == r.ID })
|
||||
clear(r.ShipTypes)
|
||||
clear(r.Sciences)
|
||||
for i := range c.g.Map.Planet {
|
||||
p := &c.g.Map.Planet[i]
|
||||
if p.Owner != nil && *p.Owner != r.ID {
|
||||
continue
|
||||
}
|
||||
p.Wipe()
|
||||
}
|
||||
r.Votes = 0
|
||||
r.VoteFor = r.ID
|
||||
r.Extinct = true
|
||||
r.TTL = 0
|
||||
c.invalidateFleetCache()
|
||||
c.invalidateShipGroupCache()
|
||||
}
|
||||
|
||||
func (c *Cache) listRaceActingIdx() iter.Seq[int] {
|
||||
return func(yield func(int) bool) {
|
||||
for i := range c.listRaceIdx() {
|
||||
if c.g.Race[i].Extinct {
|
||||
continue
|
||||
}
|
||||
if !yield(i) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) listRaceIdx() iter.Seq[int] {
|
||||
return func(yield func(int) bool) {
|
||||
for i := range c.g.Race {
|
||||
if !yield(i) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@ import (
|
||||
)
|
||||
|
||||
func TestGiveVotes(t *testing.T) {
|
||||
c, _ := newCache()
|
||||
c, g := newCache()
|
||||
|
||||
assert.Equal(t, c.Voted(Race_0_idx), Race_0_idx)
|
||||
assert.Equal(t, c.Voted(Race_1_idx), Race_1_idx)
|
||||
|
||||
assert.NoError(t, c.GiveVotes(Race_0.Name, Race_1.Name))
|
||||
assert.NoError(t, g.GiveVotes(Race_0.Name, Race_1.Name))
|
||||
assert.Equal(t, Race_1_idx, c.Voted(Race_0_idx))
|
||||
assert.Equal(t, Race_1_idx, c.Voted(Race_1_idx))
|
||||
|
||||
assert.ErrorContains(t, c.GiveVotes("UnknownRace", Race_1.Name), e.GenericErrorText(e.ErrInputUnknownRace))
|
||||
assert.ErrorContains(t, c.GiveVotes(Race_0.Name, "UnknownRace"), e.GenericErrorText(e.ErrInputUnknownRace))
|
||||
assert.ErrorContains(t, g.GiveVotes("UnknownRace", Race_1.Name), e.GenericErrorText(e.ErrInputUnknownRace))
|
||||
assert.ErrorContains(t, g.GiveVotes(Race_0.Name, "UnknownRace"), e.GenericErrorText(e.ErrInputUnknownRace))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
func (c *Cache) Report(t uint, battles []*mr.BattleReport, bombings []*mr.Bombing) iter.Seq[*mr.Report] {
|
||||
report := c.InitReport(t)
|
||||
return func(yield func(*mr.Report) bool) {
|
||||
for i := range c.g.Race {
|
||||
for i := range c.listRaceActingIdx() {
|
||||
c.ReportRace(i, report, battles, bombings)
|
||||
if !yield(report) {
|
||||
break
|
||||
@@ -55,12 +55,13 @@ func (c *Cache) InitReport(t uint) *mr.Report {
|
||||
planets[ri] = planets[ri] + 1
|
||||
}
|
||||
|
||||
for ri := range c.g.Race {
|
||||
for ri := range c.listRaceIdx() {
|
||||
r := &c.g.Race[ri]
|
||||
rr := &report.Player[ri]
|
||||
|
||||
rr.ID = r.ID
|
||||
rr.Name = r.Name
|
||||
rr.Extinct = r.Extinct
|
||||
rr.Drive = mr.F(r.TechLevel(game.TechDrive))
|
||||
rr.Weapons = mr.F(r.TechLevel(game.TechWeapons))
|
||||
rr.Shields = mr.F(r.TechLevel(game.TechShields))
|
||||
|
||||
@@ -13,18 +13,6 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/util"
|
||||
)
|
||||
|
||||
func (c *Controller) SetRoute(raceName, loadType string, origin, destination uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rt, ok := game.RouteTypeSet[loadType]
|
||||
if !ok {
|
||||
return e.NewCargoTypeInvalidError(loadType)
|
||||
}
|
||||
return c.Cache.SetRoute(ri, rt, origin, destination)
|
||||
}
|
||||
|
||||
func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) error {
|
||||
c.validateRaceIndex(ri)
|
||||
p1, ok := c.Planet(origin)
|
||||
@@ -48,18 +36,6 @@ func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) RemoveRoute(raceName, loadType string, origin uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rt, ok := game.RouteTypeSet[loadType]
|
||||
if !ok {
|
||||
return e.NewCargoTypeInvalidError(loadType)
|
||||
}
|
||||
return c.Cache.RemoveRoute(ri, rt, origin)
|
||||
}
|
||||
|
||||
func (c *Cache) RemoveRoute(ri int, rt game.RouteType, origin uint) error {
|
||||
c.validateRaceIndex(ri)
|
||||
p1, ok := c.Planet(origin)
|
||||
@@ -269,23 +245,6 @@ func (c *Cache) selectColUnloadGroup(groups []int) (result iter.Seq[int]) {
|
||||
return
|
||||
}
|
||||
|
||||
func MaxOrRandomLoadId(IDtoLoad map[int]float64) int {
|
||||
if len(IDtoLoad) < 2 {
|
||||
panic("IDtoLoad must contain at least 2 keys")
|
||||
}
|
||||
IDs := slices.Collect(maps.Keys(IDtoLoad))
|
||||
slices.SortFunc(IDs, func(id1, id2 int) int { return cmp.Compare(IDtoLoad[id2], IDtoLoad[id1]) })
|
||||
|
||||
// no single winner with highest load
|
||||
if IDtoLoad[IDs[0]] == IDtoLoad[IDs[1]] {
|
||||
// remove IDs which load less than maximum
|
||||
IDs = slices.DeleteFunc(IDs, func(v int) bool { return IDtoLoad[v] < IDtoLoad[IDs[0]] })
|
||||
// IDs[0] will have random index
|
||||
rand.Shuffle(len(IDs), func(i, j int) { IDs[i], IDs[j] = IDs[j], IDs[i] })
|
||||
}
|
||||
return IDs[0]
|
||||
}
|
||||
|
||||
func (c *Cache) listRoutedUnloadShipGroupIds(pn uint, routeType game.RouteType) iter.Seq[int] {
|
||||
return func(yield func(int) bool) {
|
||||
yielded := make(map[int]bool)
|
||||
@@ -311,3 +270,20 @@ func (c *Cache) listRoutedUnloadShipGroupIds(pn uint, routeType game.RouteType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func MaxOrRandomLoadId(IDtoLoad map[int]float64) int {
|
||||
if len(IDtoLoad) < 2 {
|
||||
panic("IDtoLoad must contain at least 2 keys")
|
||||
}
|
||||
IDs := slices.Collect(maps.Keys(IDtoLoad))
|
||||
slices.SortFunc(IDs, func(id1, id2 int) int { return cmp.Compare(IDtoLoad[id2], IDtoLoad[id1]) })
|
||||
|
||||
// no single winner with highest load
|
||||
if IDtoLoad[IDs[0]] == IDtoLoad[IDs[1]] {
|
||||
// remove IDs which load less than maximum
|
||||
IDs = slices.DeleteFunc(IDs, func(v int) bool { return IDtoLoad[v] < IDtoLoad[IDs[0]] })
|
||||
// IDs[0] will have random index
|
||||
rand.Shuffle(len(IDs), func(i, j int) { IDs[i], IDs[j] = IDs[j], IDs[i] })
|
||||
}
|
||||
return IDs[0]
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestListRoutedSendGroupIds(t *testing.T) {
|
||||
|
||||
// Foreign group -> idx 1
|
||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
||||
assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 5, 0))
|
||||
assert.NoError(t, g.TransferGroup(Race_0.Name, Race_1.Name, 5, 0))
|
||||
|
||||
// 5: idx = 4 / Part of the Fleet
|
||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
||||
|
||||
@@ -10,14 +10,6 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/util"
|
||||
)
|
||||
|
||||
func (c *Controller) CreateScience(raceName, typeName string, drive, weapons, shields, cargo float64) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.CreateScience(ri, typeName, drive, weapons, shields, cargo)
|
||||
}
|
||||
|
||||
func (c *Cache) CreateScience(ri int, name string, drive, weapons, shileds, cargo float64) error {
|
||||
c.validateRaceIndex(ri)
|
||||
n, ok := util.ValidateTypeName(name)
|
||||
@@ -55,14 +47,6 @@ func (c *Cache) CreateScience(ri int, name string, drive, weapons, shileds, carg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) DeleteScience(raceName, typeName string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.DeleteScience(ri, typeName)
|
||||
}
|
||||
|
||||
func (c *Cache) DeleteScience(ri int, name string) error {
|
||||
c.validateRaceIndex(ri)
|
||||
sc := slices.IndexFunc(c.g.Race[ri].Sciences, func(s game.Science) bool { return s.Name == name })
|
||||
|
||||
@@ -11,14 +11,6 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/util"
|
||||
)
|
||||
|
||||
func (c *Controller) CreateShipType(raceName, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.CreateShipType(ri, typeName, drive, ammo, weapons, shileds, cargo)
|
||||
}
|
||||
|
||||
func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
|
||||
c.validateRaceIndex(ri)
|
||||
if err := checkShipTypeValues(drive, ammo, weapons, shileds, cargo); err != nil {
|
||||
@@ -45,14 +37,6 @@ func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) MergeShipType(race, name, targetName string) error {
|
||||
ri, err := c.Cache.raceIndex(race)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.MergeShipType(ri, name, targetName)
|
||||
}
|
||||
|
||||
func (c *Cache) MergeShipType(ri int, name, targetName string) error {
|
||||
c.validateRaceIndex(ri)
|
||||
st, sti, ok := c.ShipClass(ri, name)
|
||||
@@ -101,14 +85,6 @@ func (c *Cache) MergeShipType(ri int, name, targetName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) DeleteShipType(raceName, typeName string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.DeleteShipType(ri, typeName)
|
||||
}
|
||||
|
||||
func (c *Cache) DeleteShipType(ri int, name string) error {
|
||||
c.validateRaceIndex(ri)
|
||||
st, i, ok := c.ShipClass(ri, name)
|
||||
@@ -136,15 +112,6 @@ func (c *Cache) DeleteShipType(ri int, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShipTypes used for tests only
|
||||
func (c *Controller) ShipTypes(race string) ([]*game.ShipType, error) {
|
||||
ri, err := c.Cache.raceIndex(race)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Cache.ShipTypes(ri), nil
|
||||
}
|
||||
|
||||
// ShipTypes used for tests only
|
||||
func (c *Cache) ShipTypes(ri int) []*game.ShipType {
|
||||
c.validateRaceIndex(ri)
|
||||
|
||||
@@ -118,29 +118,39 @@ func (c *Cache) DeleteShipGroup(i int) {
|
||||
}
|
||||
|
||||
func (c *Cache) DeleteKilledShipGroups() {
|
||||
keepFleet := make(map[uuid.UUID]bool, len(c.g.Fleets))
|
||||
for i := len(c.g.ShipGroups) - 1; i >= 0; i-- {
|
||||
if c.g.ShipGroups[i].FleetID != nil {
|
||||
id := *c.g.ShipGroups[i].FleetID
|
||||
keepFleet[id] = keepFleet[id] || c.g.ShipGroups[i].Number > 0
|
||||
}
|
||||
if c.g.ShipGroups[i].Number == 0 {
|
||||
c.unsafeDeleteShipGroup(i)
|
||||
}
|
||||
}
|
||||
// TODO: delete empty fleets
|
||||
}
|
||||
|
||||
func (c *Controller) JoinEqualGroups(raceName string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
for id, keep := range keepFleet {
|
||||
if keep {
|
||||
continue
|
||||
}
|
||||
c.unsafeDeleteFleet(c.MustFleetIndex(id))
|
||||
}
|
||||
c.Cache.JoinEqualGroups(ri)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) TurnMergeEqualShipGroups() {
|
||||
for i := range c.g.Race {
|
||||
for i := range c.listRaceActingIdx() {
|
||||
c.transferPendingGroups(i)
|
||||
c.JoinEqualGroups(i)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) transferPendingGroups(ri int) {
|
||||
for sg := range c.listShipGroups(ri) {
|
||||
if sg.State() == game.StateTransfer {
|
||||
sg.StateTransfer = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) JoinEqualGroups(ri int) {
|
||||
c.validateRaceIndex(ri)
|
||||
raceGroups := make([]game.ShipGroup, 0)
|
||||
@@ -183,30 +193,14 @@ func (c *Cache) JoinEqualGroups(ri int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) BreakGroup(raceName string, groupIndex, quantity uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.BreakGroup(ri, groupIndex, quantity)
|
||||
}
|
||||
|
||||
func (c *Controller) DisassembleGroup(raceName string, groupIndex, quantity uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.DisassembleGroup(ri, groupIndex, quantity)
|
||||
}
|
||||
|
||||
func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
|
||||
sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||
return e.NewShipsBusyError()
|
||||
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
|
||||
return e.NewShipsBusyError("state: %s", state)
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).Number < quantity {
|
||||
@@ -254,18 +248,6 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) LoadCargo(raceName string, groupIndex uint, cargoType string, ships uint, quantity float64) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ct, ok := game.CargoTypeSet[cargoType]
|
||||
if !ok {
|
||||
return e.NewCargoTypeInvalidError(cargoType)
|
||||
}
|
||||
return c.Cache.LoadCargo(ri, groupIndex, ct, ships, quantity)
|
||||
}
|
||||
|
||||
// Корабль может нести только один тип груза одновременно.
|
||||
// Возможные типы груза - это колонисты, сырье и промышленность.
|
||||
// Груз может быть доставлен на борт корабля с Вашей или не занятой планеты, на которой он имеется.
|
||||
@@ -277,8 +259,8 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||
return e.NewShipsBusyError()
|
||||
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
|
||||
return e.NewShipsBusyError("state: %s", state)
|
||||
}
|
||||
p, ok := c.Planet(c.ShipGroup(sgi).Destination)
|
||||
if !ok {
|
||||
@@ -337,14 +319,6 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) UnloadCargo(raceName string, groupIndex uint, ships uint, quantity float64) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.UnloadCargo(ri, groupIndex, ships, quantity)
|
||||
}
|
||||
|
||||
// Промышленность и Сырье могут быть выгружены на любой планете.
|
||||
// Колонисты могут быть высажены только на планеты, принадлежащие Вам или на необитаемые планеты.
|
||||
func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float64) error {
|
||||
@@ -356,8 +330,8 @@ func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float6
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||
return e.NewShipsBusyError()
|
||||
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
|
||||
return e.NewShipsBusyError("state: %s", state)
|
||||
}
|
||||
st := c.ShipGroupShipClass(sgi)
|
||||
if st.Cargo < 1 {
|
||||
@@ -429,19 +403,7 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) {
|
||||
p.UnpackCapital()
|
||||
}
|
||||
|
||||
func (c *Controller) GiveawayGroup(raceName, raceAcceptor string, groupIndex, quantity uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
riAccept, err := c.Cache.raceIndex(raceAcceptor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.GiveawayGroup(ri, riAccept, groupIndex, quantity)
|
||||
}
|
||||
|
||||
func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err error) {
|
||||
func (c *Cache) TransferGroup(ri, riAccept int, groupIndex, quantity uint) (err error) {
|
||||
if ri == riAccept {
|
||||
return e.NewSameRaceError(c.g.Race[riAccept].Name)
|
||||
}
|
||||
@@ -449,8 +411,13 @@ func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
if c.ShipGroup(sgi).Number < quantity {
|
||||
return e.NewBeakGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
||||
sg := c.ShipGroup(sgi)
|
||||
state := sg.State()
|
||||
if state == game.StateTransfer {
|
||||
return e.NewShipsBusyError("state: %s", state)
|
||||
}
|
||||
if sg.Number < quantity {
|
||||
return e.NewBeakGroupNumberNotEnoughError("%d<%d", sg.Number, quantity)
|
||||
}
|
||||
|
||||
st := c.ShipGroupShipClass(sgi)
|
||||
@@ -474,18 +441,22 @@ func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err
|
||||
stAcc = len(c.g.Race[riAccept].ShipTypes) - 1
|
||||
}
|
||||
|
||||
sg := *(c.ShipGroup(sgi))
|
||||
sg.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID
|
||||
sg.Number = uint(quantity)
|
||||
sg.Tech = maps.Clone(sg.Tech)
|
||||
c.appendShipGroup(riAccept, &sg)
|
||||
newGroup := *(sg)
|
||||
newGroup.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID
|
||||
newGroup.Tech = maps.Clone(sg.Tech)
|
||||
if state == game.StateLaunched {
|
||||
newGroup.StateTransfer = true
|
||||
}
|
||||
|
||||
if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
|
||||
if quantity == 0 || quantity == sg.Number {
|
||||
c.unsafeDeleteShipGroup(sgi)
|
||||
} else {
|
||||
c.ShipGroup(sgi).Number -= quantity
|
||||
newGroup.Number = quantity
|
||||
sg.Number -= quantity
|
||||
}
|
||||
|
||||
c.appendShipGroup(riAccept, &newGroup)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -502,7 +473,7 @@ func (c *Cache) BreakGroup(ri int, groupIndex, quantity uint) error {
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
|
||||
return e.NewShipsBusyError()
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,6 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/util"
|
||||
)
|
||||
|
||||
func (c *Controller) SendGroup(raceName string, groupIndex, planetNumber, quantity uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.SendGroup(ri, groupIndex, planetNumber, quantity)
|
||||
}
|
||||
|
||||
func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error {
|
||||
c.validateRaceIndex(ri)
|
||||
|
||||
@@ -29,7 +21,7 @@ func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error
|
||||
|
||||
sourcePlanet, ok := c.ShipGroup(sgi).AtPlanet()
|
||||
if !ok {
|
||||
return e.NewShipsBusyError()
|
||||
return e.NewShipsBusyError("state: %s", c.ShipGroup(sgi).State())
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).Number < quantity {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controller_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
@@ -159,7 +160,7 @@ func TestBreakGroup(t *testing.T) {
|
||||
assert.Nil(t, c.ShipGroup(3).FleetID)
|
||||
}
|
||||
|
||||
func TestGiveawayGroup(t *testing.T) {
|
||||
func TestTransferGroup(t *testing.T) {
|
||||
c, g := newCache()
|
||||
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 11)) // group #1 (0)
|
||||
assert.NoError(t, c.CreateShips(Race_1_idx, ShipType_Cruiser, R1_Planet_1_num, 23)) // group #1 (1)
|
||||
@@ -178,25 +179,25 @@ func TestGiveawayGroup(t *testing.T) {
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
|
||||
|
||||
assert.ErrorContains(t,
|
||||
g.GiveawayGroup("UnknownRace", Race_1.Name, 2, 0),
|
||||
g.TransferGroup("UnknownRace", Race_1.Name, 2, 0),
|
||||
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||
assert.ErrorContains(t,
|
||||
g.GiveawayGroup(Race_0.Name, "UnknownRace", 2, 0),
|
||||
g.TransferGroup(Race_0.Name, "UnknownRace", 2, 0),
|
||||
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||
assert.ErrorContains(t,
|
||||
g.GiveawayGroup(Race_0.Name, Race_0.Name, 2, 0),
|
||||
g.TransferGroup(Race_0.Name, Race_0.Name, 2, 0),
|
||||
e.GenericErrorText(e.ErrInputSameRace))
|
||||
assert.ErrorContains(t,
|
||||
g.GiveawayGroup(Race_0.Name, Race_1.Name, 555, 0),
|
||||
g.TransferGroup(Race_0.Name, Race_1.Name, 555, 0),
|
||||
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||
assert.ErrorContains(t,
|
||||
g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 18),
|
||||
g.TransferGroup(Race_0.Name, Race_1.Name, 2, 18),
|
||||
e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
|
||||
assert.ErrorContains(t,
|
||||
g.GiveawayGroup(Race_0.Name, Race_1.Name, 1, 0),
|
||||
g.TransferGroup(Race_0.Name, Race_1.Name, 1, 0),
|
||||
e.GenericErrorText(e.ErrGiveawayGroupShipsTypeNotEqual))
|
||||
|
||||
assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
|
||||
assert.NoError(t, g.TransferGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2)
|
||||
|
||||
@@ -216,14 +217,31 @@ func TestGiveawayGroup(t *testing.T) {
|
||||
assert.Equal(t, c.ShipGroup(2).Destination, c.ShipGroup(3).Destination)
|
||||
assert.Equal(t, c.ShipGroup(2).StateInSpace, c.ShipGroup(3).StateInSpace)
|
||||
assert.Equal(t, c.ShipGroup(2).StateUpgrade, c.ShipGroup(3).StateUpgrade)
|
||||
assert.Equal(t, c.ShipGroup(2).StateTransfer, c.ShipGroup(3).StateTransfer)
|
||||
assert.Equal(t, c.ShipGroup(3).OwnerID, Race_1_ID)
|
||||
assert.Equal(t, c.ShipGroup(3).TypeID, c.MustShipClass(Race_1_idx, Race_0_Gunship).ID)
|
||||
assert.Equal(t, c.ShipGroup(3).Number, uint(11))
|
||||
assert.Nil(t, c.ShipGroup(3).FleetID)
|
||||
|
||||
assert.NoError(t, g.GiveawayGroup(Race_1.Name, Race_0.Name, 2, 11))
|
||||
assert.NoError(t, g.TransferGroup(Race_1.Name, Race_0.Name, 2, 11))
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
|
||||
|
||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||
assert.Equal(t, game.StateInOrbit, c.ShipGroup(4).State())
|
||||
assert.NoError(t, g.SendGroup(Race_0.Name, c.ShipGroup(4).Index, R0_Planet_2_num, 0))
|
||||
assert.Equal(t, game.StateLaunched, c.ShipGroup(4).State())
|
||||
assert.Equal(t, c.ShipGroup(4).OwnerID, Race_0_ID)
|
||||
|
||||
assert.NoError(t, g.TransferGroup(Race_0.Name, Race_1.Name, c.ShipGroup(4).Index, 0))
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2)
|
||||
assert.Equal(t, game.StateTransfer, c.ShipGroup(4).State())
|
||||
assert.Equal(t, c.ShipGroup(4).OwnerID, Race_1_ID)
|
||||
assert.ErrorContains(t,
|
||||
g.TransferGroup(Race_1.Name, Race_0.Name, c.ShipGroup(4).Index, 0),
|
||||
e.GenericErrorText(e.ErrShipsBusy))
|
||||
}
|
||||
|
||||
func TestLoadCargo(t *testing.T) {
|
||||
@@ -540,3 +558,7 @@ func TestShipGroupDestroyItem(t *testing.T) {
|
||||
assert.Equal(t, float64(c.ShipGroup(0).Number)*10, c.ShipGroup(0).Load.F())
|
||||
}
|
||||
}
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
assert.Equal(t, "In_Orbit", fmt.Sprintf("%s", game.StateInOrbit))
|
||||
}
|
||||
|
||||
@@ -9,14 +9,6 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
)
|
||||
|
||||
func (c *Controller) UpgradeGroup(raceName string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.UpgradeGroup(ri, groupIndex, techInput, limitShips, limitLevel)
|
||||
}
|
||||
|
||||
func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
||||
c.validateRaceIndex(ri)
|
||||
sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
|
||||
@@ -26,8 +18,8 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi
|
||||
st := c.ShipGroupShipClass(sgi)
|
||||
sg := c.ShipGroup(sgi)
|
||||
|
||||
if s := sg.State(); s != game.StateInOrbit { // && s != game.StateUpgrade
|
||||
return e.NewShipsBusyError()
|
||||
if state := sg.State(); state != game.StateInOrbit {
|
||||
return e.NewShipsBusyError("state: %s", state)
|
||||
}
|
||||
|
||||
p := c.MustPlanet(sg.Destination)
|
||||
|
||||
Reference in New Issue
Block a user