diff --git a/internal/controller/bombing.go b/internal/controller/bombing.go index e8303e9..e9974a6 100644 --- a/internal/controller/bombing.go +++ b/internal/controller/bombing.go @@ -5,6 +5,35 @@ import ( "github.com/iliadenisov/galaxy/internal/model/game" ) +func (c *Cache) ProduceBombings() []*game.Bombing { + report := make([]*game.Bombing, 0) + for pn, enemies := range c.collectBombingGroups() { + p := c.MustPlanet(pn) + if !p.Owned() { + continue + } + for ri, groups := range enemies { + br := c.bombingReport(p, ri, groups) + report = append(report, br) + if br.Wiped { + break + } + } + + if p.Population == 0 { + // TODO: what aboul colonists left on planet? + p.Free() + clear(p.Route) + } else { + // Если на планете остались также и колонисты, то они превращаются в население, + // а накопленная промышленность возмещает потери производства. + p.UnpackColonists() + p.UnpackCapital() + } + } + return report +} + func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) *game.Bombing { attackPower := 0. for _, i := range groups { @@ -14,10 +43,10 @@ func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) *game.Bombin } r := &game.Bombing{ ID: uuid.New(), - PlanetOwnedID: p.Owner, + PlanetOwnedID: *p.Owner, Planet: p.Name, Number: p.Number, - Owner: c.g.Race[c.RaceIndex(p.Owner)].Name, + Owner: c.g.Race[c.RaceIndex(*p.Owner)].Name, Attacker: c.g.Race[ri].Name, Production: c.PlanetProductionDisplayName(p.Number), Industry: p.Industry, @@ -32,33 +61,6 @@ func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) *game.Bombin return r } -func (c *Cache) ProduceBombings() []*game.Bombing { - report := make([]*game.Bombing, 0) - for pn, enemies := range c.collectBombingGroups() { - p := c.MustPlanet(pn) - for ri, groups := range enemies { - br := c.bombingReport(p, ri, groups) - report = append(report, br) - if br.Wiped { - break - } - } - - if p.Population == 0 { - // TODO: what aboul colonists left on planet? - p.Owner = uuid.Nil - p.Production = game.ProductionNone.AsType(uuid.Nil) - clear(p.Route) - } else { - // Если на планете остались также и колонисты, то они превращаются в население, - // а накопленная промышленность возмещает потери производства. - p.UnpackColonists() - p.UnpackCapital() - } - } - return report -} - func bombPlanet(p *game.Planet, power float64) { // Уничтожается население и колонисты в количестве равном [суммарной] мощности бомбардировки if power > p.Population.F() { @@ -94,11 +96,11 @@ func (c *Cache) collectBombingGroups() map[uint]map[int][]int { continue } p := c.MustPlanet(sg.Destination) - if p.Owner == sg.OwnerID || p.Owner == uuid.Nil { + if p.OwnedBy(sg.OwnerID) || !p.Owned() { continue } r1 := c.RaceIndex(sg.OwnerID) - r2 := c.RaceIndex(p.Owner) + r2 := c.RaceIndex(*p.Owner) if c.Relation(r1, r2) == game.RelationPeace { continue } diff --git a/internal/controller/bombing_test.go b/internal/controller/bombing_test.go index b9e7462..1debcde 100644 --- a/internal/controller/bombing_test.go +++ b/internal/controller/bombing_test.go @@ -10,7 +10,8 @@ import ( ) func TestBombPlanet(t *testing.T) { - p := controller.NewPlanet(0, "Planet_0", uuid.New(), 1, 1, 1000, 300, 200, 10, game.ResearchDrive.AsType(uuid.Nil)) + id := uuid.New() + p := controller.NewPlanet(0, "Planet_0", &id, 1, 1, 1000, 300, 200, 10, game.ResearchDrive.AsType(uuid.Nil)) (&p).Colonists = 100. assert.Equal(t, 0., p.Material.F()) @@ -128,7 +129,7 @@ func TestProduceBombings(t *testing.T) { assert.Equal(t, Race_0.Name, b.Attacker) assert.InDelta(t, 697.857, b.AttackPower.F(), 0.0003) assert.True(t, b.Wiped) - assert.Equal(t, uuid.Nil, c.MustPlanet(pn).Owner) + assert.False(t, c.MustPlanet(pn).Owned()) assert.Empty(t, c.MustPlanet(pn).Route) assert.Equal(t, 0., c.MustPlanet(pn).Population.F()) case R0_Planet_2_num: @@ -136,7 +137,7 @@ func TestProduceBombings(t *testing.T) { assert.Equal(t, Race_1.Name, b.Attacker) assert.InDelta(t, 358.856, b.AttackPower.F(), 0.0001) assert.False(t, b.Wiped) - assert.Equal(t, Race_0_ID, c.MustPlanet(pn).Owner) + assert.True(t, c.MustPlanet(pn).OwnedBy(Race_0_ID)) assert.NotEmpty(t, c.MustPlanet(pn).Route) assert.InDelta(t, 500.-358.85596, c.MustPlanet(pn).Population.F(), 0.000001) } diff --git a/internal/controller/controller_test.go b/internal/controller/controller_test.go index b737705..ae58dd0 100644 --- a/internal/controller/controller_test.go +++ b/internal/controller/controller_test.go @@ -95,11 +95,11 @@ func newGame() *game.Game { Width: 1000, Height: 1000, Planet: []game.Planet{ - controller.NewPlanet(R0_Planet_0_num, "Planet_0", Race_0.ID, 1, 1, 100, 100, 100, 0, game.ProductionCapital.AsType(uuid.Nil)), - controller.NewPlanet(R1_Planet_1_num, "Planet_1", Race_1.ID, 2, 2, 100, 0, 0, 0, game.ProductionCapital.AsType(uuid.Nil)), - controller.NewPlanet(R0_Planet_2_num, "Planet_2", Race_0.ID, 3, 3, 100, 0, 0, 0, game.ProductionCapital.AsType(uuid.Nil)), - controller.NewPlanet(Uninhabited_Planet_3_num, "Planet_3", uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)), - controller.NewPlanet(Uninhabited_Planet_4_num, "Planet_4", uuid.Nil, 10, 10, 500, 0, 0, 10, game.ProductionNone.AsType(uuid.Nil)), + controller.NewPlanet(R0_Planet_0_num, "Planet_0", &Race_0.ID, 1, 1, 100, 100, 100, 0, game.ProductionCapital.AsType(uuid.Nil)), + controller.NewPlanet(R1_Planet_1_num, "Planet_1", &Race_1.ID, 2, 2, 100, 0, 0, 0, game.ProductionCapital.AsType(uuid.Nil)), + controller.NewPlanet(R0_Planet_2_num, "Planet_2", &Race_0.ID, 3, 3, 100, 0, 0, 0, game.ProductionCapital.AsType(uuid.Nil)), + controller.NewPlanet(Uninhabited_Planet_3_num, "Planet_3", &uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)), + controller.NewPlanet(Uninhabited_Planet_4_num, "Planet_4", nil, 10, 10, 500, 0, 0, 10, game.ProductionNone.AsType(uuid.Nil)), }, }, } diff --git a/internal/controller/generate_game.go b/internal/controller/generate_game.go index d38ee72..84a7d0e 100644 --- a/internal/controller/generate_game.go +++ b/internal/controller/generate_game.go @@ -71,7 +71,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { gameMap.Planet = append(gameMap.Planet, NewPlanet( planetCount, m.HomePlanets[i].HW.RandomName(), - raceID, + &raceID, m.HomePlanets[i].HW.Position.X, m.HomePlanets[i].HW.Position.Y, m.HomePlanets[i].HW.Size, @@ -85,7 +85,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { gameMap.Planet = append(gameMap.Planet, NewPlanet( planetCount, m.HomePlanets[i].DW[dw].RandomName(), - raceID, + &raceID, m.HomePlanets[i].DW[dw].Position.X, m.HomePlanets[i].DW[dw].Position.Y, m.HomePlanets[i].DW[dw].Size, @@ -107,7 +107,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { gameMap.Planet = append(gameMap.Planet, NewPlanet( planetCount, m.FreePlanets[i].RandomName(), - uuid.Nil, + &uuid.Nil, m.FreePlanets[i].Position.X, m.FreePlanets[i].Position.Y, m.FreePlanets[i].Size, @@ -128,23 +128,20 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { return g, nil } -func NewPlanet(num uint, name string, owner uuid.UUID, x, y, size, pop, ind, res float64, prod game.Production) game.Planet { +func NewPlanet(num uint, name string, owner *uuid.UUID, x, y, size, pop, ind, res float64, prod game.Production) game.Planet { + if owner != nil && *owner == uuid.Nil { + owner = nil + } return game.Planet{ - Owner: owner, - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - UnidentifiedPlanet: game.UnidentifiedPlanet{ - X: game.F(x), - Y: game.F(y), - Number: num, - }, - Size: game.F(size), - Name: name, - Resources: game.F(res), - }, - Population: game.F(pop), - Industry: game.F(ind), - Production: prod, - }, + Owner: owner, + X: game.F(x), + Y: game.F(y), + Number: num, + Size: game.F(size), + Name: name, + Resources: game.F(res), + Population: game.F(pop), + Industry: game.F(ind), + Production: prod, } } diff --git a/internal/controller/planet.go b/internal/controller/planet.go index 764ca8c..b9212c8 100644 --- a/internal/controller/planet.go +++ b/internal/controller/planet.go @@ -31,7 +31,7 @@ func (c *Cache) RenamePlanet(ri int, number int, name string) error { if !ok { return e.NewEntityNotExistsError("planet #%d", number) } - if p.Owner != c.g.Race[ri].ID { + if !p.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d", number) } c.g.Map.Planet[c.MustPlanetIndex(p.Number)].Name = n @@ -76,7 +76,7 @@ func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, s if !ok { return e.NewEntityNotExistsError("planet #%d", number) } - if p.Owner != c.g.Race[ri].ID { + if !p.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d", number) } var subjectID *uuid.UUID @@ -128,7 +128,10 @@ func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, s // TODO: test func (c *Cache) PlanetProductionDisplayName(pn uint) string { p := c.MustPlanet(pn) - ri := c.RaceIndex(p.Owner) + if !p.Owned() { + return "-" + } + ri := c.RaceIndex(*p.Owner) switch pt := p.Production.Type; pt { case game.ResearchDrive: return "Drive" @@ -202,14 +205,14 @@ func (c *Cache) TurnPlanetProductions() { // cancel upgrade for groups on wiped planets for sgi := range c.ShipGroupsIndex() { sg := c.ShipGroup(sgi) - if sg.State() == game.StateUpgrade && c.MustPlanet(sg.Destination).Owner == uuid.Nil { + if sg.State() == game.StateUpgrade && !c.MustPlanet(sg.Destination).Owned() { sg.StateUpgrade = nil } } for pn := range c.listProducingPlanets() { p := c.MustPlanet(pn) - ri := c.RaceIndex(p.Owner) + ri := c.RaceIndex(*p.Owner) r := &c.g.Race[ri] // upgrade groups and return to in_orbit state @@ -261,7 +264,7 @@ func (c *Cache) TurnPlanetProductions() { func (c *Cache) listProducingPlanets() iter.Seq[uint] { ordered := make([]int, 0) for i := range c.g.Map.Planet { - if c.g.Map.Planet[i].Owner == uuid.Nil || c.g.Map.Planet[i].Production.Type == game.ProductionNone { + if !c.g.Map.Planet[i].Owned() || c.g.Map.Planet[i].Production.Type == game.ProductionNone { continue } ordered = append(ordered, i) diff --git a/internal/controller/planet_test.go b/internal/controller/planet_test.go index bf90033..a407255 100644 --- a/internal/controller/planet_test.go +++ b/internal/controller/planet_test.go @@ -182,7 +182,8 @@ func TestProduceShip(t *testing.T) { Shields: 35, Cargo: 0, } - p := controller.NewPlanet(0, "Planet_0", uuid.New(), 1, 1, 1000, 1000, 1000, 10, game.ProductionShip.AsType(uuid.Nil)) + id := uuid.New() + p := controller.NewPlanet(0, "Planet_0", &id, 1, 1, 1000, 1000, 1000, 10, game.ProductionShip.AsType(uuid.Nil)) r := controller.ProduceShip(&p, p.ProductionCapacity(), Drone.EmptyMass()) assert.Equal(t, uint(99), r) diff --git a/internal/controller/report.go b/internal/controller/report.go index 2dcdba0..54ae0f0 100644 --- a/internal/controller/report.go +++ b/internal/controller/report.go @@ -46,10 +46,10 @@ func (c *Cache) InitReport(t uint) *mr.Report { planets := make(map[int]uint16) for pi := range c.g.Map.Planet { p := &c.g.Map.Planet[pi] - if p.Owner == uuid.Nil { + if !p.Owned() { continue } - ri := c.RaceIndex(p.Owner) + ri := c.RaceIndex(*p.Owner) sumPop[ri] += p.Population.F() sumInd[ri] += p.Industry.F() planets[ri] = planets[ri] + 1 @@ -206,10 +206,10 @@ func (c *Cache) ReportOtherScience(ri int, rep *mr.Report) { continue } p := c.MustPlanet(sg.Destination) - if p.Owner == uuid.Nil || p.Owner == r.ID || p.Production.Type != game.ResearchScience { + if !p.Owned() || p.OwnedBy(r.ID) || p.Production.Type != game.ResearchScience { continue } - ownerIdx := c.RaceIndex(p.Owner) + ownerIdx := c.RaceIndex(*p.Owner) owner := &c.g.Race[ownerIdx] sc := c.mustScience(ownerIdx, *p.Production.SubjectID) @@ -296,7 +296,7 @@ func (c *Cache) ReportOtherShipClass(ri int, rep *mr.Report) { // add visible ships from owned and observed planets for pn := range rep.OnPlanetGroupCache { p := c.MustPlanet(pn) - if p.Owner == r.ID || + if p.OwnedBy(r.ID) || slices.IndexFunc(rep.OnPlanetGroupCache[pn], func(sgi int) bool { return c.ShipGroup(sgi).OwnerID == r.ID }) >= 0 { for _, sgi := range rep.OnPlanetGroupCache[pn] { sg := c.ShipGroup(sgi) @@ -389,7 +389,7 @@ func (c *Cache) ReportIncomingGroup(ri int, rep *mr.Report) { } p1 := c.MustPlanet(sg.StateInSpace.Origin) p2 := c.MustPlanet(sg.Destination) - if p2.Owner != r.ID { + if !p2.OwnedBy(r.ID) { continue } @@ -420,7 +420,7 @@ func (c *Cache) ReportLocalPlanet(ri int, rep *mr.Report) { i := 0 for pi := range c.g.Map.Planet { p := &c.g.Map.Planet[pi] - if p.Owner != r.ID { + if !p.OwnedBy(r.ID) { continue } @@ -463,13 +463,13 @@ func (c *Cache) ReportOtherPlanet(ri int, rep *mr.Report) { continue } p := c.MustPlanet(sg.Destination) - if p.Owner == r.ID || p.Owner == uuid.Nil { + if !p.Owned() || p.OwnedBy(r.ID) { continue } sliceIndexValidate(&rep.OtherPlanet, i) c.localPlanet(&rep.OtherPlanet[i].LocalPlanet, p, rep) - rep.OtherPlanet[i].Owner = c.g.Race[c.RaceIndex(p.Owner)].Name + rep.OtherPlanet[i].Owner = c.g.Race[c.RaceIndex(*p.Owner)].Name i++ } } @@ -485,7 +485,7 @@ func (c *Cache) ReportUninhabitedPlanet(ri int, rep *mr.Report) { continue } p := c.MustPlanet(sg.Destination) - if p.Owner != uuid.Nil { + if p.Owned() { continue } @@ -506,7 +506,7 @@ func (c *Cache) ReportUnidentifiedPlanet(ri int, rep *mr.Report) { p := &c.g.Map.Planet[pi] // skip player's owned planets - if p.Owner == r.ID { + if p.OwnedBy(r.ID) { continue } @@ -530,7 +530,7 @@ func (c *Cache) ReportShipProduction(ri int, rep *mr.Report) { i := 0 for pi := range c.g.Map.Planet { p := &c.g.Map.Planet[pi] - if p.Owner != r.ID || p.Production.Type != game.ProductionShip { + if !p.OwnedBy(r.ID) || p.Production.Type != game.ProductionShip { continue } st := c.MustShipType(ri, *p.Production.SubjectID) @@ -557,7 +557,7 @@ func (c *Cache) ReportRoute(ri int, rep *mr.Report) { i := 0 for pi := range c.g.Map.Planet { p := &c.g.Map.Planet[pi] - if p.Owner != r.ID || len(p.Route) == 0 { + if !p.OwnedBy(r.ID) || len(p.Route) == 0 { continue } @@ -662,7 +662,7 @@ func (c *Cache) ReportOtherGroup(ri int, rep *mr.Report) { // visible groups from owned and observed planets for pn := range rep.OnPlanetGroupCache { p := c.MustPlanet(pn) - if p.Owner == r.ID || + if p.OwnedBy(r.ID) || slices.IndexFunc(rep.OnPlanetGroupCache[pn], func(sgi int) bool { return c.ShipGroup(sgi).OwnerID == r.ID }) >= 0 { for _, sgi := range rep.OnPlanetGroupCache[pn] { sg := c.ShipGroup(sgi) @@ -697,7 +697,7 @@ func (c *Cache) ReportUnidentifiedGroup(ri int, rep *mr.Report) { } for pi := range c.g.Map.Planet { p := &c.g.Map.Planet[pi] - if p.Owner != r.ID { + if !p.OwnedBy(r.ID) { continue } if v, ok := rep.InSpaceGroupRangeCache[sgi][p.Number]; !ok { diff --git a/internal/controller/route.go b/internal/controller/route.go index 9186858..fa2efc5 100644 --- a/internal/controller/route.go +++ b/internal/controller/route.go @@ -8,7 +8,6 @@ import ( "math/rand/v2" "slices" - "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" "github.com/iliadenisov/galaxy/internal/util" @@ -32,7 +31,7 @@ func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) er if !ok { return e.NewEntityNotExistsError("origin planet #%d", origin) } - if p1.Owner != c.g.Race[ri].ID { + if !p1.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d", origin) } p2, ok := c.Planet(destination) @@ -67,7 +66,7 @@ func (c *Cache) RemoveRoute(ri int, rt game.RouteType, origin uint) error { if !ok { return e.NewEntityNotExistsError("origin planet #%d", origin) } - if p1.Owner != c.g.Race[ri].ID { + if !p1.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d", origin) } @@ -177,7 +176,7 @@ func (c *Cache) listRoutedSendGroupIds(pn uint) iter.Seq[int] { for i := range c.ShipGroupsIndex() { sg := c.ShipGroup(i) st := c.ShipGroupShipClass(i) - if sg.OwnerID != p.Owner || // Planet must be owned by ships owner + if !p.OwnedBy(sg.OwnerID) || // Planet must be owned by ships owner sg.FleetID != nil || // Ships must not be part of a Fleet sg.State() != game.StateInOrbit || // Ships must be only In_Orbit state st.CargoBlockMass() == 0 || // Ship Class must have Cargo bays @@ -208,10 +207,10 @@ func (c *Cache) TurnUnloadEnroutedGroups() { func (c *Cache) RemoveUnreachableRoutes() { for i := range c.g.Map.Planet { p1 := &c.g.Map.Planet[i] - if p1.Owner == uuid.Nil { + if !p1.Owned() { continue } - ri := c.RaceIndex(p1.Owner) + ri := c.RaceIndex(*p1.Owner) for rt, destination := range p1.Route { p2 := c.MustPlanet(destination) rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X.F(), p1.Y.F(), p2.X.F(), p2.Y.F()) @@ -231,13 +230,13 @@ func (c *Cache) doUnload(groups iter.Seq[int]) { func (c *Cache) unloadRoutedColonists(pn uint, groups iter.Seq[int]) iter.Seq[int] { p := c.MustPlanet(pn) gr := slices.Collect(groups) - if p.Owner == uuid.Nil { + if !p.Owned() { return c.selectColUnloadGroup(gr) } return func(yield func(int) bool) { for _, sgi := range gr { sg := c.ShipGroup(sgi) - if p.Owner != sg.OwnerID { + if !p.OwnedBy(sg.OwnerID) { continue } if !yield(sgi) { diff --git a/internal/controller/route_test.go b/internal/controller/route_test.go index cc2e090..cf9f3c2 100644 --- a/internal/controller/route_test.go +++ b/internal/controller/route_test.go @@ -412,7 +412,7 @@ func TestTurnUnloadEnroutedGroups(t *testing.T) { assert.Equal(t, 11., c.MustPlanet(R0_Planet_0_num).Capital.F()) assert.Equal(t, 0., c.ShipGroup(2).Load.F()) assert.Equal(t, 96.8, c.MustPlanet(Uninhabited_Planet_4_num).Population.F()) - assert.Equal(t, Race_1_ID, c.MustPlanet(Uninhabited_Planet_4_num).Owner) + assert.True(t, c.MustPlanet(Uninhabited_Planet_4_num).OwnedBy(Race_1_ID)) assert.Equal(t, game.ProductionCapital, c.MustPlanet(Uninhabited_Planet_4_num).Production.Type) assert.Equal(t, 17.3, c.ShipGroup(3).Load.F()) } diff --git a/internal/controller/ship_class.go b/internal/controller/ship_class.go index bf3fa02..d7914c5 100644 --- a/internal/controller/ship_class.go +++ b/internal/controller/ship_class.go @@ -75,7 +75,7 @@ func (c *Cache) MergeShipType(ri int, name, targetName string) error { // switch planet productions to the new type for pl := range c.g.Map.Planet { - if c.g.Map.Planet[pl].Owner == c.g.Race[ri].ID && + if c.g.Map.Planet[pl].OwnedBy(c.g.Race[ri].ID) && c.g.Map.Planet[pl].Production.Type == game.ProductionShip && c.g.Map.Planet[pl].Production.SubjectID != nil && *c.g.Map.Planet[pl].Production.SubjectID == st.ID { diff --git a/internal/controller/ship_group.go b/internal/controller/ship_group.go index e712bab..3b4252a 100644 --- a/internal/controller/ship_group.go +++ b/internal/controller/ship_group.go @@ -21,7 +21,7 @@ func (c *Cache) CreateShips(ri int, shipTypeName string, planetNumber uint, quan if !ok { return e.NewEntityNotExistsError("planet #%d", planetNumber) } - if p.Owner != c.g.Race[ri].ID { + if !p.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d", planetNumber) } @@ -231,7 +231,7 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error { load := c.ShipGroup(sgi).Load.F() switch ct { case game.CargoColonist: - if p.Owner == c.g.Race[ri].ID { + if p.OwnedBy(c.g.Race[ri].ID) { p = game.UnloadColonists(p, load) } case game.CargoMaterial: @@ -281,7 +281,7 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination) } - if p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID { + if p.Owned() && !p.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d", p.Number) } st := c.ShipGroupShipClass(sgi) @@ -366,7 +366,7 @@ func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float6 ct := *c.ShipGroup(sgi).CargoType p := c.MustPlanet(c.ShipGroup(sgi).Destination) - if ct == game.CargoColonist && p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID { + if ct == game.CargoColonist && p.Owned() && !p.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d unload %v", p.Number, ct) } if ships > 0 && ships < c.ShipGroup(sgi).Number { @@ -405,8 +405,8 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) { switch ct { case game.CargoColonist: availableOnPlanet = &p.Colonists - if p.Owner == uuid.Nil { - p.Owner = c.ShipGroup(sgi).OwnerID + if !p.Owned() { + p.Own(c.ShipGroup(sgi).OwnerID) p.Production = game.ProductionCapital.AsType(uuid.Nil) } case game.CargoMaterial: diff --git a/internal/controller/ship_group_upgrade.go b/internal/controller/ship_group_upgrade.go index 472a10c..9a16ae1 100644 --- a/internal/controller/ship_group_upgrade.go +++ b/internal/controller/ship_group_upgrade.go @@ -4,7 +4,6 @@ import ( "math" "slices" - "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" ) @@ -31,7 +30,7 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi } p := c.MustPlanet(sg.Destination) - if p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID { + if p.Owned() && !p.OwnedBy(c.g.Race[ri].ID) { return e.NewEntityNotOwnedError("planet #%d for upgrade group #%d", p.Number, groupIndex) } diff --git a/internal/controller/vote.go b/internal/controller/vote.go index 2889fc3..0f61f7b 100644 --- a/internal/controller/vote.go +++ b/internal/controller/vote.go @@ -83,10 +83,10 @@ func (c *Cache) votesByRace() map[int]float64 { result := make(map[int]float64) for i := range c.g.Map.Planet { p := &c.g.Map.Planet[i] - if p.Owner == uuid.Nil { + if !p.Owned() { continue } - ri := c.RaceIndex(p.Owner) + ri := c.RaceIndex(*p.Owner) planetVotes := p.Votes() result[ri] += planetVotes } diff --git a/internal/game/cmd_planet_test.go b/internal/game/cmd_planet_test.go index 158da13..7473427 100644 --- a/internal/game/cmd_planet_test.go +++ b/internal/game/cmd_planet_test.go @@ -19,9 +19,9 @@ func TestRenamePlanet(t *testing.T) { var number int var owner uuid.UUID for pl := range cg.Map.Planet { - if cg.Map.Planet[pl].Owner != uuid.Nil { + if cg.Map.Planet[pl].Owned() { number = int(cg.Map.Planet[pl].Number) - owner = cg.Map.Planet[pl].Owner + owner = *cg.Map.Planet[pl].Owner break } } @@ -36,7 +36,7 @@ func TestRenamePlanet(t *testing.T) { err := game.RenamePlanet(p, race, number, newName) assert.NoError(t, err) cg = g() - pi := slices.IndexFunc(cg.Map.Planet, func(pl mg.Planet) bool { return pl.Owner == owner && pl.Number == uint(number) }) + pi := slices.IndexFunc(cg.Map.Planet, func(pl mg.Planet) bool { return pl.OwnedBy(owner) && pl.Number == uint(number) }) assert.GreaterOrEqual(t, pi, 0) assert.Equal(t, "Some-New-Name", cg.Map.Planet[pi].Name) diff --git a/internal/model/game/game.go b/internal/model/game/game.go index 9de28fe..e01c29f 100644 --- a/internal/model/game/game.go +++ b/internal/model/game/game.go @@ -70,7 +70,7 @@ type BattleMeta struct { func (g Game) RaceVotes(raceID uuid.UUID) float64 { var result float64 for i := range g.Map.Planet { - if g.Map.Planet[i].Owner == raceID { + if g.Map.Planet[i].OwnedBy(raceID) { result += g.Map.Planet[i].Votes() } } diff --git a/internal/model/game/planet.go b/internal/model/game/planet.go index 727adc3..5c82581 100644 --- a/internal/model/game/planet.go +++ b/internal/model/game/planet.go @@ -6,39 +6,48 @@ import ( "github.com/google/uuid" ) -type UnidentifiedPlanet struct { - X Float `json:"x"` - Y Float `json:"y"` - Number uint `json:"number"` -} - -type UninhabitedPlanet struct { - UnidentifiedPlanet - Size Float `json:"size"` - Name string `json:"name"` - Resources Float `json:"resources"` // R - Ресурсы - Capital Float `json:"capital"` // CAP $ - Запасы промышленности - Material Float `json:"material"` // MAT M - Запасы ресурсов / сырьё -} - -type PlanetReport struct { - UninhabitedPlanet - Industry Float `json:"industry"` // I - Промышленность - Population Float `json:"population"` // P - Население - Colonists Float `json:"colonists"` // COL C - Количество колонистов - Production Production `json:"production"` // TODO: internal/report format -} - -// TODO: unwrap in one struct type Planet struct { - Owner uuid.UUID `json:"owner"` // FIXME: nil value when no owner - Route map[RouteType]uint `json:"route"` - PlanetReport + X Float `json:"x"` + Y Float `json:"y"` + Number uint `json:"number"` + Size Float `json:"size"` + Name string `json:"name"` + Owner *uuid.UUID `json:"owner,omitempty"` + Resources Float `json:"resources"` // R - Ресурсы + Capital Float `json:"capital"` // CAP $ - Запасы промышленности + Material Float `json:"material"` // MAT M - Запасы ресурсов / сырьё + Industry Float `json:"industry"` // I - Промышленность + Population Float `json:"population"` // P - Население + Colonists Float `json:"colonists"` // COL C - Количество колонистов + Production Production `json:"production"` + Route map[RouteType]uint `json:"route"` } -type PlanetReportForeign struct { - RaceName string - PlanetReport +func (p *Planet) Own(v uuid.UUID) { + if v == uuid.Nil { + p.Free() + return + } + if p.Owner == nil || *p.Owner != v { + p.Production = ProductionCapital.AsType(uuid.Nil) + } + p.Owner = &v +} + +func (p *Planet) Free() { + p.Owner = nil + p.Production = ProductionNone.AsType(uuid.Nil) +} + +func (p Planet) Owned() bool { + return p.Owner != nil && *p.Owner != uuid.Nil +} + +func (p Planet) OwnedBy(v uuid.UUID) bool { + if !p.Owned() { + return false + } + return *p.Owner == v } func (p *Planet) Mat(v float64) { diff --git a/internal/model/game/planet_test.go b/internal/model/game/planet_test.go index 3a681bf..17daee2 100644 --- a/internal/model/game/planet_test.go +++ b/internal/model/game/planet_test.go @@ -16,24 +16,16 @@ func TestPlanetProduction(t *testing.T) { func TestProduceIndustry(t *testing.T) { HW := &game.Planet{ - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - Size: 1000, - Resources: 10, - }, - Population: 1000, - Industry: 1000, - }, + Size: 1000, + Resources: 10, + Population: 1000, + Industry: 1000, } DW := &game.Planet{ - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - Size: 500, - Resources: 10, - }, - Population: 500, - Industry: 500, - }, + Size: 500, + Resources: 10, + Population: 500, + Industry: 500, } HW.ProduceIndustry() assert.InDelta(t, 196.078, HW.Capital.F(), 0.0005) @@ -58,14 +50,10 @@ func TestProduceIndustry(t *testing.T) { func TestProduceMaterial(t *testing.T) { HW := &game.Planet{ - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - Size: 1000, - Resources: 10, - }, - Population: 1000, - Industry: 1000, - }, + Size: 1000, + Resources: 10, + Population: 1000, + Industry: 1000, } assert.Equal(t, 0., HW.Material.F()) @@ -84,14 +72,10 @@ func TestProduceMaterial(t *testing.T) { func TestUnpackCapital(t *testing.T) { HW := &game.Planet{ - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - Size: 1000, - Resources: 10, - }, - Population: 1000, - Industry: 1000, - }, + Size: 1000, + Resources: 10, + Population: 1000, + Industry: 1000, } assert.Equal(t, 0., HW.Capital.F()) @@ -119,14 +103,10 @@ func TestUnpackCapital(t *testing.T) { func TestUnpackColonists(t *testing.T) { HW := &game.Planet{ - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - Size: 1000, - Resources: 10, - }, - Population: 1000, - Industry: 1000, - }, + Size: 1000, + Resources: 10, + Population: 1000, + Industry: 1000, } assert.Equal(t, 0., HW.Colonists.F()) @@ -152,14 +132,10 @@ func TestUnpackColonists(t *testing.T) { func TestProducePopulation(t *testing.T) { HW := &game.Planet{ - PlanetReport: game.PlanetReport{ - UninhabitedPlanet: game.UninhabitedPlanet{ - Size: 1000, - Resources: 10, - }, - Population: 500, - Industry: 1000, - }, + Size: 1000, + Resources: 10, + Population: 500, + Industry: 1000, } assert.Equal(t, 500., HW.Population.F()) assert.Equal(t, 0., HW.Colonists.F())