refactor: game funcs moved to controller
This commit is contained in:
@@ -1,10 +1,6 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import "github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
// "github.com/iliadenisov/galaxy/internal/controller"
|
|
||||||
// "github.com/iliadenisov/galaxy/internal/game/battle"
|
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TransformBattle(c *Cache, b *Battle) *game.BattleReport {
|
func TransformBattle(c *Cache, b *Battle) *game.BattleReport {
|
||||||
r := &game.BattleReport{
|
r := &game.BattleReport{
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ type Cache struct {
|
|||||||
g *game.Game
|
g *game.Game
|
||||||
cacheRaceIndexByID map[uuid.UUID]int
|
cacheRaceIndexByID map[uuid.UUID]int
|
||||||
cacheFleetIndexByID map[uuid.UUID]int
|
cacheFleetIndexByID map[uuid.UUID]int
|
||||||
raceIndexByShipGroupIndex map[int]int
|
cacheRaceIndexByShipGroupIndex map[int]int
|
||||||
shipClassByShipGroupIndex map[int]*game.ShipType
|
cacheShipClassByShipGroupIndex map[int]*game.ShipType
|
||||||
planetByPlanetNumber map[uint]*game.Planet
|
cachePlanetByPlanetNumber map[uint]*game.Planet
|
||||||
cacheRelation map[int]map[int]game.Relation
|
cacheRelation map[int]map[int]game.Relation
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,11 +29,11 @@ func NewCache(g *game.Game) *Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType {
|
func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType {
|
||||||
if c.shipClassByShipGroupIndex == nil || len(c.shipClassByShipGroupIndex) == 0 {
|
if len(c.cacheShipClassByShipGroupIndex) == 0 {
|
||||||
c.cacheShipsAndGroups()
|
c.cacheShipsAndGroups()
|
||||||
}
|
}
|
||||||
c.validateShipGroupIndex(groupIndex)
|
c.validateShipGroupIndex(groupIndex)
|
||||||
if v, ok := c.shipClassByShipGroupIndex[groupIndex]; ok {
|
if v, ok := c.cacheShipClassByShipGroupIndex[groupIndex]; ok {
|
||||||
return v
|
return v
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("ShipClassByShipGroupIndex: group not found by index=%v", groupIndex))
|
panic(fmt.Sprintf("ShipClassByShipGroupIndex: group not found by index=%v", groupIndex))
|
||||||
@@ -55,43 +55,34 @@ func (c *Cache) RaceIndex(ID uuid.UUID) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) cacheShipsAndGroups() {
|
func (c *Cache) cacheShipsAndGroups() {
|
||||||
if c.raceIndexByShipGroupIndex != nil {
|
if c.cacheRaceIndexByShipGroupIndex != nil {
|
||||||
clear(c.raceIndexByShipGroupIndex)
|
clear(c.cacheRaceIndexByShipGroupIndex)
|
||||||
} else {
|
} else {
|
||||||
c.raceIndexByShipGroupIndex = make(map[int]int)
|
c.cacheRaceIndexByShipGroupIndex = make(map[int]int)
|
||||||
}
|
}
|
||||||
if c.shipClassByShipGroupIndex != nil {
|
if c.cacheShipClassByShipGroupIndex != nil {
|
||||||
clear(c.shipClassByShipGroupIndex)
|
clear(c.cacheShipClassByShipGroupIndex)
|
||||||
} else {
|
} else {
|
||||||
c.shipClassByShipGroupIndex = make(map[int]*game.ShipType)
|
c.cacheShipClassByShipGroupIndex = make(map[int]*game.ShipType)
|
||||||
}
|
}
|
||||||
for groupIndex := range c.g.ShipGroups {
|
for groupIndex := range c.g.ShipGroups {
|
||||||
ri := c.RaceIndex(c.g.ShipGroups[groupIndex].OwnerID)
|
ri := c.RaceIndex(c.g.ShipGroups[groupIndex].OwnerID)
|
||||||
c.raceIndexByShipGroupIndex[groupIndex] = ri
|
c.cacheRaceIndexByShipGroupIndex[groupIndex] = ri
|
||||||
sti, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[groupIndex].TypeID)
|
sti, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[groupIndex].TypeID)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(fmt.Sprintf("CollectPlanetGroups: ship class not found for race=%q group=%v", c.g.Race[ri].Name, c.g.ShipGroups[groupIndex].Index))
|
panic(fmt.Sprintf("CollectPlanetGroups: ship class not found for race=%q group=%v", c.g.Race[ri].Name, c.g.ShipGroups[groupIndex].Index))
|
||||||
}
|
}
|
||||||
c.shipClassByShipGroupIndex[groupIndex] = &c.g.Race[ri].ShipTypes[sti]
|
c.cacheShipClassByShipGroupIndex[groupIndex] = &c.g.Race[ri].ShipTypes[sti]
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) cacheShipGroup(groupIndex, ri int, class *game.ShipType) {
|
|
||||||
if c.raceIndexByShipGroupIndex != nil {
|
|
||||||
c.raceIndexByShipGroupIndex[groupIndex] = ri
|
|
||||||
}
|
|
||||||
if c.shipClassByShipGroupIndex != nil {
|
|
||||||
c.shipClassByShipGroupIndex[groupIndex] = class
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) invalidateShipGroupCache() {
|
func (c *Cache) invalidateShipGroupCache() {
|
||||||
if c.raceIndexByShipGroupIndex != nil {
|
clear(c.cacheRaceIndexByShipGroupIndex)
|
||||||
clear(c.raceIndexByShipGroupIndex)
|
clear(c.cacheShipClassByShipGroupIndex)
|
||||||
}
|
|
||||||
if c.shipClassByShipGroupIndex != nil {
|
|
||||||
clear(c.shipClassByShipGroupIndex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) invalidateFleetCache() {
|
||||||
|
clear(c.cacheFleetIndexByID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|||||||
@@ -13,10 +13,14 @@ func (c *Cache) Race(i int) game.Race {
|
|||||||
return c.g.Race[i]
|
return c.g.Race[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ListShipGroups(ri int) iter.Seq[*game.ShipGroup] {
|
func (c *Cache) RaceShipGroups(ri int) iter.Seq[*game.ShipGroup] {
|
||||||
return c.listShipGroups(ri)
|
return c.listShipGroups(ri)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) RaceScience(ri int) []game.Science {
|
||||||
|
return c.raceScience(ri)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) ListFleets(ri int) iter.Seq[*game.Fleet] {
|
func (c *Cache) ListFleets(ri int) iter.Seq[*game.Fleet] {
|
||||||
return c.listFleets(ri)
|
return c.listFleets(ri)
|
||||||
}
|
}
|
||||||
@@ -42,7 +46,7 @@ func (c *Cache) MustShipGroup(ri int, index uint) *game.ShipGroup {
|
|||||||
func (c *Cache) MustShipClass(ri int, name string) *game.ShipType {
|
func (c *Cache) MustShipClass(ri int, name string) *game.ShipType {
|
||||||
st, _, ok := c.ShipClass(ri, name)
|
st, _, ok := c.ShipClass(ri, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("ship class not foind")
|
panic("ship class not found")
|
||||||
}
|
}
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
@@ -59,6 +63,6 @@ func (c *Cache) PutMaterial(pn uint, v float64) {
|
|||||||
c.putMaterial(pn, v)
|
c.putMaterial(pn, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) RacetTechLevel(ri int, t game.Tech, v float64) {
|
func (c *Cache) RaceTechLevel(ri int, t game.Tech, v float64) {
|
||||||
c.racetTechLevel(ri, t, v)
|
c.raceTechLevel(ri, t, v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ func (c *Cache) FleetSpeed(fl game.Fleet) float64 {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
st := c.ShipGroupShipClass(sg)
|
st := c.ShipGroupShipClass(sg)
|
||||||
// st := g.mustShipType(g.ShipGroups[sg].TypeID)
|
|
||||||
typeSpeed := c.ShipGroup(sg).Speed(st)
|
typeSpeed := c.ShipGroup(sg).Speed(st)
|
||||||
if typeSpeed < result {
|
if typeSpeed < result {
|
||||||
result = typeSpeed
|
result = typeSpeed
|
||||||
@@ -85,20 +84,6 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
|
|||||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sgi := -1
|
|
||||||
// var maxIndex uint
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||||
return e.NewShipsBusyError()
|
return e.NewShipsBusyError()
|
||||||
}
|
}
|
||||||
@@ -107,6 +92,12 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
|
|||||||
return e.NewJoinFleetGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
return e.NewJoinFleetGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var oldFleetID *uuid.UUID
|
||||||
|
if c.ShipGroup(sgi).FleetID != nil {
|
||||||
|
fID := *c.ShipGroup(sgi).FleetID
|
||||||
|
oldFleetID = &fID
|
||||||
|
}
|
||||||
|
|
||||||
fi, ok := c.fleetIndex(ri, name)
|
fi, ok := c.fleetIndex(ri, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
fi, err = c.createFleet(ri, name)
|
fi, err = c.createFleet(ri, name)
|
||||||
@@ -120,22 +111,35 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: if g.ShipGroups[sgi].FleetID != nil { // delete old fleet if empty, ALSO mind breaking group }
|
|
||||||
if quantity > 0 && quantity < c.ShipGroup(sgi).Number {
|
if quantity > 0 && quantity < c.ShipGroup(sgi).Number {
|
||||||
nsgi, err := c.breakGroupSafe(ri, groupIndex, quantity)
|
nsgi, err := c.breakGroupSafe(ri, groupIndex, quantity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sgi = nsgi
|
sgi = nsgi
|
||||||
// newGroup := g.ShipGroups[sgi]
|
|
||||||
// newGroup.Number -= quantity
|
|
||||||
// g.ShipGroups[sgi].Number = quantity
|
|
||||||
// newGroup.Index = maxIndex + 1
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, newGroup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// g.ShipGroups[sgi].FleetID = &g.Fleets[fi].ID
|
c.ShipGroupJoinFleet(sgi, &c.g.Fleets[fi].ID)
|
||||||
c.ShipGroupFleet(sgi, &c.g.Fleets[fi].ID)
|
|
||||||
|
if oldFleetID != nil {
|
||||||
|
keepOldFleet := false
|
||||||
|
for sg := range c.listShipGroups(ri) {
|
||||||
|
if sg.FleetID != nil && *sg.FleetID == *oldFleetID {
|
||||||
|
keepOldFleet = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !keepOldFleet {
|
||||||
|
oldFleetIndex, ok := c.FleetIndex(*oldFleetID)
|
||||||
|
if !ok {
|
||||||
|
return e.NewGameStateError("old fleet index not found by ID=%v", *oldFleetID)
|
||||||
|
}
|
||||||
|
if err := c.deleteFleetSafe(ri, c.g.Fleets[oldFleetIndex].Name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +180,7 @@ func (c *Cache) createFleet(ri int, name string) (int, error) {
|
|||||||
return 0, e.NewEntityTypeNameValidationError("%q", n)
|
return 0, e.NewEntityTypeNameValidationError("%q", n)
|
||||||
}
|
}
|
||||||
if _, ok := c.fleetIndex(ri, n); ok {
|
if _, ok := c.fleetIndex(ri, n); ok {
|
||||||
return 0, e.NewEntityTypeNameDuplicateError("fleet %w", n)
|
return 0, e.NewEntityTypeNameDuplicateError("fleet %q", n)
|
||||||
}
|
}
|
||||||
fleets := slices.Clone(c.g.Fleets)
|
fleets := slices.Clone(c.g.Fleets)
|
||||||
fleets = append(fleets, game.Fleet{
|
fleets = append(fleets, game.Fleet{
|
||||||
@@ -202,17 +206,15 @@ 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)
|
return e.NewEntityInUseError("fleet %s: race %s, group #%d", name, c.g.Race[ri].Name, sg.Number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.cacheFleetIndexByID != nil {
|
|
||||||
delete(c.cacheFleetIndexByID, c.g.Fleets[fi].ID)
|
|
||||||
}
|
|
||||||
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
|
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
|
||||||
|
c.cacheFleetIndexByID = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal funcs
|
// Internal funcs
|
||||||
|
|
||||||
func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
|
func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
|
||||||
if c.cacheFleetIndexByID == nil {
|
if len(c.cacheFleetIndexByID) == 0 {
|
||||||
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
|
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
|
||||||
for i := range c.g.Fleets {
|
for i := range c.g.Fleets {
|
||||||
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
|
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
|
||||||
@@ -270,16 +272,8 @@ func (c *Cache) fleetIndex(ri int, name string) (int, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) mustFleetIndex(ri int, name string) int {
|
|
||||||
if v, ok := c.fleetIndex(ri, name); ok {
|
|
||||||
return v
|
|
||||||
} else {
|
|
||||||
panic(fmt.Sprintf("fleet not found: race_idx=%d name=%q", ri, name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) validateFleetIndex(i int) {
|
func (c *Cache) validateFleetIndex(i int) {
|
||||||
if i >= len(c.g.Fleets) {
|
if i >= len(c.g.Fleets) {
|
||||||
panic(fmt.Sprintf("race index out of range: %d >= %d", i, len(c.g.Fleets)))
|
panic(fmt.Sprintf("fleet index out of range: %d >= %d", i, len(c.g.Fleets)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,10 @@ func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p1, ok := c.Planet(*sourcePlanet)
|
p1, ok := c.Planet(*sourcePlanet)
|
||||||
// p1, ok := PlanetByNum(g, *sourcePlanet)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewGameStateError("source planet #%d does not exists", sourcePlanet)
|
return e.NewGameStateError("source planet #%d does not exists", sourcePlanet)
|
||||||
}
|
}
|
||||||
p2, ok := c.Planet(planetNumber)
|
p2, ok := c.Planet(planetNumber)
|
||||||
// p2, ok := PlanetByNum(g, planetNumber)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewEntityNotExistsError("destination planet #%d", planetNumber)
|
return e.NewEntityNotExistsError("destination planet #%d", planetNumber)
|
||||||
}
|
}
|
||||||
@@ -42,11 +40,7 @@ func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for sg := range c.FleetGroups(ri, fi) {
|
for sg := range c.FleetGroups(ri, fi) {
|
||||||
st := c.ShipType(ri, sg.TypeID)
|
st := c.MustShipType(ri, sg.TypeID)
|
||||||
// st, ok := ShipClass(g, ri, sg.TypeID)
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", sg.TypeID)
|
|
||||||
// }
|
|
||||||
if st.DriveBlockMass() == 0 {
|
if st.DriveBlockMass() == 0 {
|
||||||
return e.NewSendShipHasNoDrivesError("Class=%s", st.Name)
|
return e.NewSendShipHasNoDrivesError("Class=%s", st.Name)
|
||||||
}
|
}
|
||||||
@@ -66,12 +60,7 @@ func (c *Cache) LaunchFleet(ri, fi int, destination uint) {
|
|||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
c.validateFleetIndex(fi)
|
c.validateFleetIndex(fi)
|
||||||
for sg := range c.FleetGroups(ri, fi) {
|
for sg := range c.FleetGroups(ri, fi) {
|
||||||
c.LaunchShips(*sg, destination)
|
c.LaunchShips(sg, destination)
|
||||||
// sgi := slices.IndexFunc(g.ShipGroups, func(s ShipGroup) bool { return sg.Index == s.Index })
|
|
||||||
// if sgi < 0 {
|
|
||||||
// panic(fmt.Sprintf("LauncgFleet: cannot find ship group index=%d", sg.Index))
|
|
||||||
// }
|
|
||||||
// g.ShipGroups[sgi] = c.LaunchShips(sg, destination)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,11 +68,6 @@ func (c *Cache) UnsendFleet(ri, fi int) {
|
|||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
c.validateFleetIndex(fi)
|
c.validateFleetIndex(fi)
|
||||||
for sg := range c.FleetGroups(ri, fi) {
|
for sg := range c.FleetGroups(ri, fi) {
|
||||||
c.UnsendShips(*sg)
|
c.UnsendShips(sg)
|
||||||
// sgi := slices.IndexFunc(g.ShipGroups, func(s ShipGroup) bool { return sg.Index == s.Index })
|
|
||||||
// if sgi < 0 {
|
|
||||||
// panic(fmt.Sprintf("UnsendFleet: cannot find ship group index=%d", sg.Index))
|
|
||||||
// }
|
|
||||||
// g.ShipGroups[sgi] = c.UnsendShips(sg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,28 @@ func TestSendFleet(t *testing.T) {
|
|||||||
fleetSending := "R0_Fleet_one"
|
fleetSending := "R0_Fleet_one"
|
||||||
fleetInSpace := "R0_Fleet_inSpace"
|
fleetInSpace := "R0_Fleet_inSpace"
|
||||||
fleetUnmovable := "R0_Fleet_unmovable"
|
fleetUnmovable := "R0_Fleet_unmovable"
|
||||||
|
fleetUnmovable2 := "R0_Fleet_unmovable2"
|
||||||
|
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSending, 1, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSending, 1, 0))
|
||||||
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1)
|
||||||
|
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSending, 3, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSending, 3, 0))
|
||||||
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1)
|
||||||
|
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetInSpace, 2, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetInSpace, 2, 0))
|
||||||
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 2)
|
||||||
|
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetUnmovable, 3, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetUnmovable, 3, 0))
|
||||||
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 3)
|
||||||
|
|
||||||
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetUnmovable2, 4, 0))
|
||||||
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 4)
|
||||||
|
|
||||||
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetUnmovable, 4, 0))
|
||||||
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 3)
|
||||||
|
|
||||||
// group #2 - in_space
|
// group #2 - in_space
|
||||||
c.ShipGroup(1).StateInSpace = &game.InSpace{Origin: 2, Range: 1.23}
|
c.ShipGroup(1).StateInSpace = &game.InSpace{Origin: 2, Range: 1.23}
|
||||||
// g.ShipGroups[1].StateInSpace = &game.InSpace{Origin: 2, Range: 1.23}
|
|
||||||
|
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.SendFleet("UnknownRace", fleetSending, 2),
|
g.SendFleet("UnknownRace", fleetSending, 2),
|
||||||
@@ -57,7 +70,6 @@ func TestSendFleet(t *testing.T) {
|
|||||||
e.GenericErrorText(e.ErrSendShipHasNoDrives))
|
e.GenericErrorText(e.ErrSendShipHasNoDrives))
|
||||||
|
|
||||||
assert.NoError(t, g.SendFleet(Race_0.Name, fleetSending, 2))
|
assert.NoError(t, g.SendFleet(Race_0.Name, fleetSending, 2))
|
||||||
// fi := slices.IndexFunc(slices.Collect(c.ListFleets(Race_0_idx)), func(f *game.Fleet) bool { return f.Name == fleetSending })
|
|
||||||
state, _, _ := c.FleetState(c.MustFleetID(Race_0_idx, fleetSending))
|
state, _, _ := c.FleetState(c.MustFleetID(Race_0_idx, fleetSending))
|
||||||
assert.Equal(t, game.StateLaunched, state)
|
assert.Equal(t, game.StateLaunched, state)
|
||||||
for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) {
|
for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) {
|
||||||
@@ -65,7 +77,6 @@ func TestSendFleet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(t, g.SendFleet(Race_0.Name, fleetSending, 0))
|
assert.NoError(t, g.SendFleet(Race_0.Name, fleetSending, 0))
|
||||||
// fi = slices.IndexFunc(slices.Collect(c.ListFleets(Race_0_idx)), func(f *game.Fleet) bool { return f.Name == fleetSending })
|
|
||||||
state, _, _ = c.FleetState(c.MustFleetID(Race_0_idx, fleetSending))
|
state, _, _ = c.FleetState(c.MustFleetID(Race_0_idx, fleetSending))
|
||||||
assert.Equal(t, game.StateInOrbit, state)
|
assert.Equal(t, game.StateInOrbit, state)
|
||||||
for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) {
|
for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func TestJoinShipGroupToFleet(t *testing.T) {
|
|||||||
|
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOne, groupIndex, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOne, groupIndex, 0))
|
||||||
fleets := slices.Collect(c.ListFleets(Race_0_idx))
|
fleets := slices.Collect(c.ListFleets(Race_0_idx))
|
||||||
groups := slices.Collect(c.ListShipGroups(Race_0_idx))
|
groups := slices.Collect(c.RaceShipGroups(Race_0_idx))
|
||||||
assert.Len(t, groups, 1)
|
assert.Len(t, groups, 1)
|
||||||
gi := 0
|
gi := 0
|
||||||
assert.Len(t, fleets, 1)
|
assert.Len(t, fleets, 1)
|
||||||
@@ -52,7 +52,7 @@ func TestJoinShipGroupToFleet(t *testing.T) {
|
|||||||
groupIndex = 2
|
groupIndex = 2
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetTwo, groupIndex, 2))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetTwo, groupIndex, 2))
|
||||||
fleets = slices.Collect(c.ListFleets(Race_0_idx))
|
fleets = slices.Collect(c.ListFleets(Race_0_idx))
|
||||||
groups = slices.Collect(c.ListShipGroups(Race_0_idx))
|
groups = slices.Collect(c.RaceShipGroups(Race_0_idx))
|
||||||
assert.Len(t, groups, 3)
|
assert.Len(t, groups, 3)
|
||||||
assert.Len(t, fleets, 2)
|
assert.Len(t, fleets, 2)
|
||||||
assert.Equal(t, fleets[1].Name, fleetTwo)
|
assert.Equal(t, fleets[1].Name, fleetTwo)
|
||||||
@@ -75,7 +75,7 @@ func TestJoinShipGroupToFleet(t *testing.T) {
|
|||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOne, groupIndex, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOne, groupIndex, 0))
|
||||||
fleets = slices.Collect(c.ListFleets(Race_0_idx))
|
fleets = slices.Collect(c.ListFleets(Race_0_idx))
|
||||||
assert.Len(t, fleets, 2)
|
assert.Len(t, fleets, 2)
|
||||||
groups = slices.Collect(c.ListShipGroups(Race_0_idx))
|
groups = slices.Collect(c.RaceShipGroups(Race_0_idx))
|
||||||
assert.NotNil(t, groups[gi].FleetID)
|
assert.NotNil(t, groups[gi].FleetID)
|
||||||
assert.Equal(t, fleets[0].ID, *groups[gi].FleetID)
|
assert.Equal(t, fleets[0].ID, *groups[gi].FleetID)
|
||||||
state, _, _ = c.FleetState(fleets[0].ID)
|
state, _, _ = c.FleetState(fleets[0].ID)
|
||||||
@@ -94,11 +94,8 @@ func TestJoinShipGroupToFleet(t *testing.T) {
|
|||||||
c.ShipGroup(gi).StateInSpace = nil
|
c.ShipGroup(gi).StateInSpace = nil
|
||||||
|
|
||||||
// existing fleet not on the same planet or in_orbit
|
// existing fleet not on the same planet or in_orbit
|
||||||
c.ShipGroup(0).StateInSpace = &game.InSpace{
|
c.ShipGroup(0).StateInSpace = &game.InSpace{Origin: 2, Range: 1}
|
||||||
Origin: 2,
|
c.ShipGroup(1).StateInSpace = c.ShipGroup(0).StateInSpace
|
||||||
Range: 1,
|
|
||||||
}
|
|
||||||
c.ShipGroup(2).StateInSpace = c.ShipGroup(0).StateInSpace
|
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.JoinShipGroupToFleet(Race_0.Name, fleetOne, c.ShipGroup(gi).Index, 0),
|
g.JoinShipGroupToFleet(Race_0.Name, fleetOne, c.ShipGroup(gi).Index, 0),
|
||||||
e.GenericErrorText(e.ErrShipsNotOnSamePlanet))
|
e.GenericErrorText(e.ErrShipsNotOnSamePlanet))
|
||||||
@@ -116,22 +113,22 @@ func TestJoinFleets(t *testing.T) {
|
|||||||
// ensure race has no Fleets
|
// ensure race has no Fleets
|
||||||
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 0)
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 0)
|
||||||
|
|
||||||
fleetPlanet2 := "R0_Fleet_On_Planet_2"
|
fleetOnPlanet2 := "R0_Fleet_On_Planet_2"
|
||||||
fleetSource := "R0_Fleet_one"
|
fleetSourceOne := "R0_Fleet_one"
|
||||||
fleetTarget := "R0_Fleet_two"
|
fleetTargetTwo := "R0_Fleet_two"
|
||||||
|
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.JoinFleets(Race_0.Name, fleetSource, fleetTarget),
|
g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo),
|
||||||
e.GenericErrorText(e.ErrInputEntityNotExists))
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSource, 1, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetSourceOne, 1, 0))
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.JoinFleets(Race_0.Name, fleetSource, fleetTarget),
|
g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo),
|
||||||
e.GenericErrorText(e.ErrInputEntityNotExists))
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetTarget, 3, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetTargetTwo, 3, 0))
|
||||||
assert.NoError(t, g.JoinFleets(Race_0.Name, fleetSource, fleetTarget))
|
assert.NoError(t, g.JoinFleets(Race_0.Name, fleetSourceOne, fleetTargetTwo))
|
||||||
|
|
||||||
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetPlanet2, 2, 0))
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleetOnPlanet2, 2, 0))
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.JoinFleets(Race_0.Name, fleetPlanet2, fleetTarget),
|
g.JoinFleets(Race_0.Name, fleetOnPlanet2, fleetTargetTwo),
|
||||||
e.GenericErrorText(e.ErrShipsNotOnSamePlanet))
|
e.GenericErrorText(e.ErrShipsNotOnSamePlanet))
|
||||||
}
|
}
|
||||||
|
|||||||
+140
-22
@@ -2,29 +2,160 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 := validateTypeName(name)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityTypeNameValidationError("%q", n)
|
||||||
|
}
|
||||||
|
if number < 0 {
|
||||||
|
return e.NewPlanetNumberError(number)
|
||||||
|
}
|
||||||
|
p, ok := c.Planet(uint(number))
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("planet #%d", number)
|
||||||
|
}
|
||||||
|
if p.Owner != c.g.Race[ri].ID {
|
||||||
|
return e.NewEntityNotOwnedError("planet #%d", number)
|
||||||
|
}
|
||||||
|
c.g.Map.Planet[c.MustPlanetIndex(p.Number)].Name = n
|
||||||
|
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 {
|
||||||
|
return e.NewPlanetNumberError(number)
|
||||||
|
}
|
||||||
|
p, ok := c.Planet(uint(number))
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("planet #%d", number)
|
||||||
|
}
|
||||||
|
if p.Owner != c.g.Race[ri].ID {
|
||||||
|
return e.NewEntityNotOwnedError("planet #%d", number)
|
||||||
|
}
|
||||||
|
i := c.MustPlanetIndex(p.Number)
|
||||||
|
var subjectID *uuid.UUID
|
||||||
|
if (prod == game.ResearchScience || prod == game.ProductionShip) && subj == "" {
|
||||||
|
return e.NewEntityTypeNameValidationError("%s=%q", prod, subj)
|
||||||
|
}
|
||||||
|
if prod == game.ResearchScience {
|
||||||
|
i := slices.IndexFunc(c.g.Race[ri].Sciences, func(s game.Science) bool { return s.Name == subj })
|
||||||
|
if i < 0 {
|
||||||
|
return e.NewEntityNotExistsError("science %q", subj)
|
||||||
|
}
|
||||||
|
subjectID = &c.g.Race[ri].Sciences[i].ID
|
||||||
|
}
|
||||||
|
if prod == game.ProductionShip {
|
||||||
|
st, _, ok := c.ShipClass(ri, subj)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("ship type %q", subj)
|
||||||
|
}
|
||||||
|
if p.Production.Type == game.ProductionShip &&
|
||||||
|
p.Production.SubjectID != nil &&
|
||||||
|
*p.Production.SubjectID == st.ID {
|
||||||
|
// Planet already produces this ship type, keeping progress intact
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
subjectID = &st.ID
|
||||||
|
var progress float64 = 0.
|
||||||
|
c.g.Map.Planet[i].Production.Progress = &progress
|
||||||
|
} else {
|
||||||
|
c.g.Map.Planet[i].Production.Progress = nil
|
||||||
|
}
|
||||||
|
if p.Production.Type == game.ProductionShip && prod != game.ProductionShip {
|
||||||
|
if p.Production.SubjectID == nil {
|
||||||
|
return e.NewGameStateError("planet #%d produces ship but SubjectID is empty", p.Number)
|
||||||
|
}
|
||||||
|
s := *p.Production.SubjectID
|
||||||
|
if p.Production.Progress == nil {
|
||||||
|
return e.NewGameStateError("planet #%d produces ship but Progress is empty", p.Number)
|
||||||
|
}
|
||||||
|
progress := *p.Production.Progress
|
||||||
|
st, ok := c.ShipType(ri, s)
|
||||||
|
if !ok {
|
||||||
|
return e.NewGameStateError("planet #%d produces ship but ShipType was not found for race %s", p.Number, c.g.Race[ri].Name)
|
||||||
|
}
|
||||||
|
mat, _ := st.ProductionCost()
|
||||||
|
extra := mat * progress
|
||||||
|
c.g.Map.Planet[i].Material += extra
|
||||||
|
}
|
||||||
|
c.g.Map.Planet[i].Production.Type = prod
|
||||||
|
c.g.Map.Planet[i].Production.SubjectID = subjectID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) Planet(planetNumber uint) (*game.Planet, bool) {
|
func (c *Cache) Planet(planetNumber uint) (*game.Planet, bool) {
|
||||||
if c.planetByPlanetNumber == nil {
|
if c.cachePlanetByPlanetNumber == nil {
|
||||||
c.planetByPlanetNumber = make(map[uint]*game.Planet)
|
c.cachePlanetByPlanetNumber = make(map[uint]*game.Planet)
|
||||||
for p := range c.g.Map.Planet {
|
for p := range c.g.Map.Planet {
|
||||||
c.planetByPlanetNumber[c.g.Map.Planet[p].Number] = &c.g.Map.Planet[p]
|
c.cachePlanetByPlanetNumber[c.g.Map.Planet[p].Number] = &c.g.Map.Planet[p]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := c.planetByPlanetNumber[planetNumber]; ok {
|
if v, ok := c.cachePlanetByPlanetNumber[planetNumber]; ok {
|
||||||
return v, true
|
return v, true
|
||||||
} else {
|
} else {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) MustPlanet(planetNumber uint) *game.Planet {
|
func (c *Cache) MustPlanet(pn uint) *game.Planet {
|
||||||
if v, ok := c.Planet(planetNumber); ok {
|
if v, ok := c.Planet(pn); ok {
|
||||||
return v
|
return v
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("Planet: not found by number=%d", planetNumber))
|
panic(fmt.Sprintf("planet not found by number=%d", pn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) MustPlanetIndex(pn uint) int {
|
||||||
|
if idx := slices.IndexFunc(c.g.Map.Planet, func(p game.Planet) bool { return p.Number == pn }); idx < 0 {
|
||||||
|
panic(fmt.Sprintf("planet not found by number=%d", pn))
|
||||||
|
} else {
|
||||||
|
return idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,18 +164,14 @@ func (c *Cache) MustPlanet(planetNumber uint) *game.Planet {
|
|||||||
// за вычетом затрат, расходуемых в течение хода на модернизацию кораблей
|
// за вычетом затрат, расходуемых в течение хода на модернизацию кораблей
|
||||||
func (c *Cache) PlanetProductionCapacity(planetNumber uint) float64 {
|
func (c *Cache) PlanetProductionCapacity(planetNumber uint) float64 {
|
||||||
p := c.MustPlanet(planetNumber)
|
p := c.MustPlanet(planetNumber)
|
||||||
// p, err := g.PlanetByNumber(planetNumber)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
var busyResources float64
|
var busyResources float64
|
||||||
for sg := range c.g.ShipsInUpgrade(p.Number) {
|
for sg := range c.shipGroupsInUpgrade(p.Number) {
|
||||||
busyResources += sg.StateUpgrade.Cost()
|
busyResources += sg.StateUpgrade.Cost()
|
||||||
}
|
}
|
||||||
return game.PlanetProduction(p.Industry, p.Population) - busyResources
|
return game.PlanetProduction(p.Industry, p.Population) - busyResources
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal fincs
|
// Internal funcs
|
||||||
|
|
||||||
func (c *Cache) putPopulation(pn uint, v float64) {
|
func (c *Cache) putPopulation(pn uint, v float64) {
|
||||||
c.MustPlanet(pn).Population = v
|
c.MustPlanet(pn).Population = v
|
||||||
@@ -57,12 +184,3 @@ func (c *Cache) putColonists(pn uint, v float64) {
|
|||||||
func (c *Cache) putMaterial(pn uint, v float64) {
|
func (c *Cache) putMaterial(pn uint, v float64) {
|
||||||
c.MustPlanet(pn).Material = v
|
c.MustPlanet(pn).Material = v
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnloadColonists(p game.Planet, v float64) game.Planet {
|
|
||||||
p.Population += v * 8
|
|
||||||
if p.Population > p.Size {
|
|
||||||
p.Colonists += (p.Population - p.Size) / 8
|
|
||||||
p.Population = p.Size
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package controller_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenamePlanet(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
|
||||||
|
assert.Equal(t, "Planet_0", c.MustPlanet(R0_Planet_0_num).Name)
|
||||||
|
assert.NoError(t, g.RenamePlanet(Race_0.Name, int(R0_Planet_0_num), "Home_World"))
|
||||||
|
assert.Equal(t, "Home_World", c.MustPlanet(R0_Planet_0_num).Name)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RenamePlanet("UnknownRace", int(R0_Planet_0_num), "Home_World"),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RenamePlanet(Race_0.Name, -1, "Home_World"),
|
||||||
|
e.GenericErrorText(e.ErrInputPlanetNumber))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RenamePlanet(Race_0.Name, 500, "Home_World"),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RenamePlanet(Race_0.Name, int(R1_Planet_1_num), "Home_World"),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlanetProduction(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
|
||||||
|
first := "Drive_Shields"
|
||||||
|
assert.NoError(t, g.CreateScience(Race_0.Name, first, 0.4, 0, 0.6, 0))
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 1)
|
||||||
|
scID := c.RaceScience(Race_0_idx)[0].ID
|
||||||
|
|
||||||
|
pn := int(R0_Planet_0_num)
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "MAT", ""))
|
||||||
|
assert.Equal(t, game.ProductionMaterial, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "CAP", ""))
|
||||||
|
assert.Equal(t, game.ProductionCapital, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "Weapons", "500"))
|
||||||
|
assert.Equal(t, game.ResearchWeapons, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "cargo", ""))
|
||||||
|
assert.Equal(t, game.ResearchCargo, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "SHIELDS", first))
|
||||||
|
assert.Equal(t, game.ResearchShields, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "DrivE", ""))
|
||||||
|
assert.Equal(t, game.ResearchDrive, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "Science", first))
|
||||||
|
assert.Equal(t, game.ResearchScience, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.Nil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
assert.NotNil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
assert.Equal(t, scID, *c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
|
||||||
|
assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "SHIP", Race_0_Gunship))
|
||||||
|
assert.Equal(t, game.ProductionShip, c.MustPlanet(R0_Planet_0_num).Production.Type)
|
||||||
|
assert.NotNil(t, c.MustPlanet(R0_Planet_0_num).Production.Progress)
|
||||||
|
assert.NotNil(t, c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
stID := c.MustShipClass(Race_0_idx, Race_0_Gunship).ID
|
||||||
|
assert.Equal(t, stID, *c.MustPlanet(R0_Planet_0_num).Production.SubjectID)
|
||||||
|
|
||||||
|
pn = int(R0_Planet_2_num)
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction("UnknownRace", pn, "DRIVE", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, pn, "Hyperdrive", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputProductionInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, -1, "DRIVE", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputPlanetNumber))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, 500, "DRIVE", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, int(R1_Planet_1_num), "DRIVE", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, pn, "Science", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, pn, "SHIP", ""),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, pn, "Science", "Winning"),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.PlanetProduction(Race_0.Name, pn, "SHIP", "Drone"),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlanetProductionCapacity(t *testing.T) {
|
||||||
|
c, _ := newCache()
|
||||||
|
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1))
|
||||||
|
assert.Equal(t, 100., c.PlanetProductionCapacity(R0_Planet_0_num))
|
||||||
|
c.UpgradeShipGroup(0, game.TechDrive, 1.6)
|
||||||
|
assert.Equal(t, 53.125, c.PlanetProductionCapacity(R0_Planet_0_num))
|
||||||
|
}
|
||||||
@@ -115,17 +115,6 @@ func (c *Cache) UpdateRelation(ri, other int, rel game.Relation) (err error) {
|
|||||||
err = e.NewGameStateError("UpdateRelation: opponent not found")
|
err = e.NewGameStateError("UpdateRelation: opponent not found")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
// for o := range c.g.Race[r1].Relations {
|
|
||||||
// if c.g.Race[r1].Relations[o].RaceID == c.g.Race[r2].ID {
|
|
||||||
// c.g.Race[r1].Relations[o].Relation = rel
|
|
||||||
// if c.cacheRelation != nil {
|
|
||||||
// c.updateRelationCache(r1, r2, rel)
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return e.NewGameStateError("UpdateRelation: opponent not found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) validateRaceIndex(i int) {
|
func (c *Cache) validateRaceIndex(i int) {
|
||||||
@@ -142,7 +131,7 @@ func (c *Cache) raceIndex(name string) (int, error) {
|
|||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) racetTechLevel(ri int, t game.Tech, v float64) {
|
func (c *Cache) raceTechLevel(ri int, t game.Tech, v float64) {
|
||||||
c.validateFleetIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
c.g.Race[ri].Tech = c.g.Race[ri].Tech.Set(t, v)
|
c.g.Race[ri].Tech = c.g.Race[ri].Tech.Set(t, v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"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)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("origin planet #%d", origin)
|
||||||
|
}
|
||||||
|
if p1.Owner != c.g.Race[ri].ID {
|
||||||
|
return e.NewEntityNotOwnedError("planet #%d", origin)
|
||||||
|
}
|
||||||
|
p2, ok := c.Planet(destination)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("destination planet #%d", destination)
|
||||||
|
}
|
||||||
|
rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
||||||
|
if rangeToDestination > c.g.Race[ri].FlightDistance() {
|
||||||
|
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetPlanetRoute(rt, origin, destination)
|
||||||
|
|
||||||
|
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)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("origin planet #%d", origin)
|
||||||
|
}
|
||||||
|
if p1.Owner != c.g.Race[ri].ID {
|
||||||
|
return e.NewEntityNotOwnedError("planet #%d", origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.RemovePlanetRoute(rt, origin)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) SetPlanetRoute(rt game.RouteType, origin, destination uint) {
|
||||||
|
pi := c.MustPlanetIndex(origin)
|
||||||
|
if c.g.Map.Planet[pi].Route == nil {
|
||||||
|
c.g.Map.Planet[pi].Route = make(map[game.RouteType]uint)
|
||||||
|
}
|
||||||
|
c.g.Map.Planet[pi].Route[rt] = destination
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) RemovePlanetRoute(rt game.RouteType, origin uint) {
|
||||||
|
pi := c.MustPlanetIndex(origin)
|
||||||
|
if c.g.Map.Planet[pi].Route != nil {
|
||||||
|
delete(c.g.Map.Planet[pi].Route, rt)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package controller_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetRoute(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteMaterial)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2))
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteMaterial)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", 0, 2))
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteMaterial)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2))
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteMaterial)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 0, 2))
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteMaterial)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute("UnknownRace", "COL", 0, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "IND", 0, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "COL", 500, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "COL", 1, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "COL", 0, 3),
|
||||||
|
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRoute(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2))
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2))
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 2, 0))
|
||||||
|
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, c.MustPlanet(2).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.RemoveRoute(Race_0.Name, "COL", 0))
|
||||||
|
assert.NotContains(t, c.MustPlanet(0).Route, game.RouteColonist)
|
||||||
|
assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital)
|
||||||
|
|
||||||
|
assert.NoError(t, g.RemoveRoute(Race_0.Name, "EMP", 2))
|
||||||
|
assert.NotContains(t, c.MustPlanet(2).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute("UnknownRace", "COL", 0),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute(Race_0.Name, "IND", 0),
|
||||||
|
e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute(Race_0.Name, "COL", 500),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute(Race_0.Name, "COL", 1),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 := validateTypeName(name)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityTypeNameValidationError("%q", n)
|
||||||
|
}
|
||||||
|
if sc := slices.IndexFunc(c.g.Race[ri].Sciences, func(s game.Science) bool { return s.Name == n }); sc >= 0 {
|
||||||
|
return e.NewEntityTypeNameDuplicateError("science %q", c.g.Race[ri].Sciences[sc].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if drive < 0 {
|
||||||
|
return e.NewDriveValueError(drive)
|
||||||
|
}
|
||||||
|
if weapons < 0 {
|
||||||
|
return e.NewWeaponsValueError(weapons)
|
||||||
|
}
|
||||||
|
if shileds < 0 {
|
||||||
|
return e.NewShieldsValueError(shileds)
|
||||||
|
}
|
||||||
|
if cargo < 0 {
|
||||||
|
return e.NewCargoValueError(cargo)
|
||||||
|
}
|
||||||
|
sum := drive + weapons + shileds + cargo
|
||||||
|
if sum != 1 {
|
||||||
|
return e.NewScienceSumValuesError("D=%f W=%f S=%f C=%f sum=%f", drive, weapons, shileds, cargo, sum)
|
||||||
|
}
|
||||||
|
c.g.Race[ri].Sciences = append(c.g.Race[ri].Sciences, game.Science{
|
||||||
|
ID: uuid.New(),
|
||||||
|
ScienceReport: game.ScienceReport{
|
||||||
|
Name: n,
|
||||||
|
Drive: drive,
|
||||||
|
Weapons: weapons,
|
||||||
|
Shields: shileds,
|
||||||
|
Cargo: cargo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
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 })
|
||||||
|
if sc < 0 {
|
||||||
|
return e.NewEntityNotExistsError("science %q", name)
|
||||||
|
}
|
||||||
|
if pl := slices.IndexFunc(c.g.Map.Planet, func(p game.Planet) bool {
|
||||||
|
return p.Production.Type == game.ResearchScience &&
|
||||||
|
p.Production.SubjectID != nil &&
|
||||||
|
*p.Production.SubjectID == c.g.Race[ri].Sciences[sc].ID
|
||||||
|
}); pl >= 0 {
|
||||||
|
return e.NewDeleteSciencePlanetProductionError(c.g.Map.Planet[pl].Name)
|
||||||
|
}
|
||||||
|
c.g.Race[ri].Sciences = append(c.g.Race[ri].Sciences[:sc], c.g.Race[ri].Sciences[sc+1:]...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal func
|
||||||
|
|
||||||
|
func (c *Cache) raceScience(ri int) []game.Science {
|
||||||
|
c.validateRaceIndex(ri)
|
||||||
|
return c.g.Race[ri].Sciences
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package controller_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateScience(t *testing.T) {
|
||||||
|
// TODO: test on dead race
|
||||||
|
c, g := newCache()
|
||||||
|
first := "Drive_Shields"
|
||||||
|
second := "Hyperdrive"
|
||||||
|
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 0)
|
||||||
|
assert.NoError(t, g.CreateScience(Race_0.Name, first, 0.4, 0, 0.6, 0))
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 1)
|
||||||
|
sc := c.RaceScience(Race_0_idx)[0]
|
||||||
|
assert.NoError(t, uuid.Validate(sc.ID.String()))
|
||||||
|
assert.Equal(t, first, sc.Name)
|
||||||
|
assert.Equal(t, 0.4, sc.Drive)
|
||||||
|
assert.Equal(t, 0., sc.Weapons)
|
||||||
|
assert.Equal(t, 0.6, sc.Shields)
|
||||||
|
assert.Equal(t, 0., sc.Cargo)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience("UnknownRace", second, 0.4, 0, 0.6, 0),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, " ", 0.4, 0, 0.6, 0),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityTypeNameInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, first, 0.4, 0, 0.6, 0),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityTypeNameDuplicate))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, -0.1, 0, 1.1, 0),
|
||||||
|
e.GenericErrorText(e.ErrInputDriveValue))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 1.5, -0.5, 0, 0),
|
||||||
|
e.GenericErrorText(e.ErrInputWeaponsValue))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 1.3, 0, -0.3, 0),
|
||||||
|
e.GenericErrorText(e.ErrInputShieldsValue))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 0, 1.07, 0, -0.07),
|
||||||
|
e.GenericErrorText(e.ErrInputCargoValue))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 0.26, 0.25, 0.25, 0.25),
|
||||||
|
e.GenericErrorText(e.ErrInputScienceSumValues))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 0.25, 0.26, 0.25, 0.25),
|
||||||
|
e.GenericErrorText(e.ErrInputScienceSumValues))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 0.25, 0.25, 0.26, 0.25),
|
||||||
|
e.GenericErrorText(e.ErrInputScienceSumValues))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.CreateScience(Race_0.Name, second, 0.25, 0.25, 0.25, 0.26),
|
||||||
|
e.GenericErrorText(e.ErrInputScienceSumValues))
|
||||||
|
|
||||||
|
assert.NoError(t, g.CreateScience(Race_0.Name, second, 0.25, 0.25, 0.25, 0.25))
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 2)
|
||||||
|
sc = c.RaceScience(Race_0_idx)[1]
|
||||||
|
assert.NoError(t, uuid.Validate(sc.ID.String()))
|
||||||
|
assert.Equal(t, second, sc.Name)
|
||||||
|
assert.Equal(t, 0.25, sc.Drive)
|
||||||
|
assert.Equal(t, 0.25, sc.Weapons)
|
||||||
|
assert.Equal(t, 0.25, sc.Shields)
|
||||||
|
assert.Equal(t, 0.25, sc.Cargo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteScience(t *testing.T) {
|
||||||
|
// TODO: test on dead race
|
||||||
|
// TODO: test with existing ship group
|
||||||
|
// TODO: test with planet production busy with science
|
||||||
|
c, g := newCache()
|
||||||
|
first := "Drive_Shields"
|
||||||
|
second := "Hyperdrive"
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 0)
|
||||||
|
assert.NoError(t, g.CreateScience(Race_0.Name, first, 0.4, 0, 0.6, 0))
|
||||||
|
assert.NoError(t, g.CreateScience(Race_0.Name, second, 0.25, 0.25, 0.25, 0.25))
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 2)
|
||||||
|
|
||||||
|
assert.NoError(t, g.DeleteScience(Race_0.Name, first))
|
||||||
|
assert.Len(t, c.RaceScience(Race_0_idx), 1)
|
||||||
|
|
||||||
|
g.PlanetProduction(Race_0.Name, int(R0_Planet_0_num), "SCIENCE", second)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.DeleteScience("UnknownRace", second),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.DeleteScience(Race_0.Name, first),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.DeleteScience(Race_0.Name, second),
|
||||||
|
e.GenericErrorText(e.ErrDeleteSciencePlanetProduction))
|
||||||
|
}
|
||||||
@@ -17,14 +17,6 @@ func (c *Controller) CreateShipType(raceName, typeName string, drive float64, am
|
|||||||
return c.Cache.CreateShipType(ri, typeName, drive, ammo, weapons, shileds, cargo)
|
return c.Cache.CreateShipType(ri, typeName, drive, ammo, weapons, shileds, cargo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ShipClass(ri int, name string) (*game.ShipType, int, bool) {
|
|
||||||
i := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == name })
|
|
||||||
if i < 0 {
|
|
||||||
return nil, -1, false
|
|
||||||
}
|
|
||||||
return &c.g.Race[ri].ShipTypes[i], i, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
|
func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
|
||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
if err := checkShipTypeValues(drive, ammo, weapons, shileds, cargo); err != nil {
|
if err := checkShipTypeValues(drive, ammo, weapons, shileds, cargo); err != nil {
|
||||||
@@ -35,19 +27,21 @@ func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int,
|
|||||||
return e.NewEntityTypeNameValidationError("%q", n)
|
return e.NewEntityTypeNameValidationError("%q", n)
|
||||||
}
|
}
|
||||||
if st := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == typeName }); st >= 0 {
|
if st := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == typeName }); st >= 0 {
|
||||||
return e.NewEntityTypeNameDuplicateError("ship type %w", c.g.Race[ri].ShipTypes[st].Name)
|
return e.NewEntityTypeNameDuplicateError("ship type %q", c.g.Race[ri].ShipTypes[st].Name)
|
||||||
}
|
}
|
||||||
c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes, game.ShipType{
|
c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes, game.ShipType{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
ShipTypeReport: game.ShipTypeReport{
|
ShipTypeReport: game.ShipTypeReport{
|
||||||
Name: n,
|
Name: n,
|
||||||
Drive: drive,
|
Drive: drive,
|
||||||
|
Armament: uint(ammo),
|
||||||
Weapons: weapons,
|
Weapons: weapons,
|
||||||
Shields: shileds,
|
Shields: shileds,
|
||||||
Cargo: cargo,
|
Cargo: cargo,
|
||||||
Armament: uint(ammo),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
c.invalidateShipGroupCache()
|
||||||
|
c.invalidateFleetCache()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,18 +57,19 @@ func (c *Cache) MergeShipType(ri int, name, targetName string) error {
|
|||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
st, sti, ok := c.ShipClass(ri, name)
|
st, sti, ok := c.ShipClass(ri, name)
|
||||||
|
|
||||||
// st := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == name })
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewEntityNotExistsError("source ship type %w", name)
|
return e.NewEntityNotExistsError("source ship type %q", name)
|
||||||
}
|
}
|
||||||
if name == targetName {
|
tt, _, ok := c.ShipClass(ri, targetName)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("target ship type %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if st.Name == tt.Name {
|
||||||
return e.NewEntityTypeNameEqualityError("ship type %q", targetName)
|
return e.NewEntityTypeNameEqualityError("ship type %q", targetName)
|
||||||
}
|
}
|
||||||
tt, _, ok := c.ShipClass(ri, name)
|
|
||||||
// tt := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == targetName })
|
|
||||||
if !ok {
|
|
||||||
return e.NewEntityNotExistsError("target ship type %w", name)
|
|
||||||
}
|
|
||||||
if !st.Equal(*tt) {
|
if !st.Equal(*tt) {
|
||||||
return e.NewMergeShipTypeNotEqualError()
|
return e.NewMergeShipTypeNotEqualError()
|
||||||
}
|
}
|
||||||
@@ -100,6 +95,9 @@ func (c *Cache) MergeShipType(ri int, name, targetName string) error {
|
|||||||
// remove the source type
|
// remove the source type
|
||||||
c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes[:sti], c.g.Race[ri].ShipTypes[sti+1:]...)
|
c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes[:sti], c.g.Race[ri].ShipTypes[sti+1:]...)
|
||||||
|
|
||||||
|
c.invalidateShipGroupCache()
|
||||||
|
c.invalidateFleetCache()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,12 +113,8 @@ func (c *Cache) DeleteShipType(ri int, name string) error {
|
|||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
st, i, ok := c.ShipClass(ri, name)
|
st, i, ok := c.ShipClass(ri, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewEntityNotExistsError("ship type %w", name)
|
return e.NewEntityNotExistsError("ship type %q", name)
|
||||||
}
|
}
|
||||||
// st := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == name })
|
|
||||||
// if st < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("ship type %w", name)
|
|
||||||
// }
|
|
||||||
if pl := slices.IndexFunc(c.g.Map.Planet, func(p game.Planet) bool {
|
if pl := slices.IndexFunc(c.g.Map.Planet, func(p game.Planet) bool {
|
||||||
return p.Production.Type == game.ProductionShip &&
|
return p.Production.Type == game.ProductionShip &&
|
||||||
p.Production.SubjectID != nil &&
|
p.Production.SubjectID != nil &&
|
||||||
@@ -135,7 +129,10 @@ func (c *Cache) DeleteShipType(ri int, name string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes[:i], c.g.Race[ri].ShipTypes[i+1:]...)
|
c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes[:i], c.g.Race[ri].ShipTypes[i+1:]...)
|
||||||
// FIXME: update cache ON ALL FUNCs IN THIS FILE
|
|
||||||
|
c.invalidateShipGroupCache()
|
||||||
|
c.invalidateFleetCache()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,13 +155,28 @@ func (c *Cache) ShipTypes(ri int) []*game.ShipType {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ShipType(ri int, ID uuid.UUID) *game.ShipType {
|
func (c *Cache) ShipClass(ri int, name string) (*game.ShipType, int, bool) {
|
||||||
|
i := slices.IndexFunc(c.g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.Name == name })
|
||||||
|
if i < 0 {
|
||||||
|
return nil, -1, false
|
||||||
|
}
|
||||||
|
return &c.g.Race[ri].ShipTypes[i], i, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) ShipType(ri int, ID uuid.UUID) (*game.ShipType, bool) {
|
||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
for i := range c.g.Race[ri].ShipTypes {
|
for i := range c.g.Race[ri].ShipTypes {
|
||||||
if c.g.Race[ri].ShipTypes[i].ID == ID {
|
if c.g.Race[ri].ShipTypes[i].ID == ID {
|
||||||
return &c.g.Race[ri].ShipTypes[i]
|
return &c.g.Race[ri].ShipTypes[i], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) MustShipType(ri int, ID uuid.UUID) *game.ShipType {
|
||||||
|
if v, ok := c.ShipType(ri, ID); ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
panic(fmt.Sprintf("ship_class not found: race_id=%d id=%v", ri, ID))
|
panic(fmt.Sprintf("ship_class not found: race_id=%d id=%v", ri, ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import (
|
|||||||
func (c *Cache) CreateShips(ri int, shipTypeName string, planetNumber uint, quantity int) error {
|
func (c *Cache) CreateShips(ri int, shipTypeName string, planetNumber uint, quantity int) error {
|
||||||
class, _, ok := c.ShipClass(ri, shipTypeName)
|
class, _, ok := c.ShipClass(ri, shipTypeName)
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewEntityNotExistsError("ship class %w", shipTypeName)
|
return e.NewEntityNotExistsError("ship class q", shipTypeName)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p, ok := c.Planet(planetNumber)
|
p, ok := c.Planet(planetNumber)
|
||||||
@@ -26,10 +25,7 @@ func (c *Cache) CreateShips(ri int, shipTypeName string, planetNumber uint, quan
|
|||||||
return e.NewEntityNotOwnedError("planet #%d", planetNumber)
|
return e.NewEntityNotOwnedError("planet #%d", planetNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: move maxindex to appendShipGroup
|
c.appendShipGroup(ri, &game.ShipGroup{
|
||||||
nextIndex := c.ShipGroupMaxIndex(ri) + 1
|
|
||||||
c.appendShipGroup(ri, class, &game.ShipGroup{
|
|
||||||
Index: nextIndex,
|
|
||||||
OwnerID: c.g.Race[ri].ID,
|
OwnerID: c.g.Race[ri].ID,
|
||||||
TypeID: class.ID,
|
TypeID: class.ID,
|
||||||
Destination: p.Number,
|
Destination: p.Number,
|
||||||
@@ -50,9 +46,10 @@ func (c *Cache) ShipGroup(groupIndex int) *game.ShipGroup {
|
|||||||
return &c.g.ShipGroups[groupIndex]
|
return &c.g.ShipGroups[groupIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ShipGroupFleet(groupIndex int, fID *uuid.UUID) {
|
func (c *Cache) ShipGroupJoinFleet(groupIndex int, fID *uuid.UUID) {
|
||||||
c.validateShipGroupIndex(groupIndex)
|
c.validateShipGroupIndex(groupIndex)
|
||||||
c.g.ShipGroups[groupIndex].FleetID = fID
|
c.g.ShipGroups[groupIndex].FleetID = fID
|
||||||
|
c.invalidateFleetCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ShipGroupShipsNumber(groupIndex int, number uint) {
|
func (c *Cache) ShipGroupShipsNumber(groupIndex int, number uint) {
|
||||||
@@ -85,10 +82,10 @@ func (c *Cache) ShipGroupMaxIndex(ri int) uint {
|
|||||||
|
|
||||||
func (c *Cache) ShipGroupOwnerRaceIndex(groupIndex int) int {
|
func (c *Cache) ShipGroupOwnerRaceIndex(groupIndex int) int {
|
||||||
c.validateShipGroupIndex(groupIndex)
|
c.validateShipGroupIndex(groupIndex)
|
||||||
if len(c.raceIndexByShipGroupIndex) == 0 {
|
if len(c.cacheRaceIndexByShipGroupIndex) == 0 {
|
||||||
c.cacheShipsAndGroups()
|
c.cacheShipsAndGroups()
|
||||||
}
|
}
|
||||||
if v, ok := c.raceIndexByShipGroupIndex[groupIndex]; ok {
|
if v, ok := c.cacheRaceIndexByShipGroupIndex[groupIndex]; ok {
|
||||||
return v
|
return v
|
||||||
} else {
|
} else {
|
||||||
panic(fmt.Sprintf("ShipGroupRace: group not found by index=%v", groupIndex))
|
panic(fmt.Sprintf("ShipGroupRace: group not found by index=%v", groupIndex))
|
||||||
@@ -134,21 +131,25 @@ func (c *Cache) CmdJoinEqualGroups() {
|
|||||||
|
|
||||||
func (c *Cache) JoinEqualGroups(ri int) {
|
func (c *Cache) JoinEqualGroups(ri int) {
|
||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
shipGroups := slices.Collect(c.listShipGroups(ri))
|
raceGroups := make([]game.ShipGroup, 0)
|
||||||
origin := len(shipGroups)
|
for sg := range c.listShipGroups(ri) {
|
||||||
|
raceGroups = append(raceGroups, *sg)
|
||||||
|
}
|
||||||
|
|
||||||
|
origin := len(raceGroups)
|
||||||
if origin < 2 {
|
if origin < 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i := 0; i < len(shipGroups)-1; i++ {
|
for i := 0; i < len(raceGroups)-1; i++ {
|
||||||
for j := len(shipGroups) - 1; j > i; j-- {
|
for j := len(raceGroups) - 1; j > i; j-- {
|
||||||
if shipGroups[i].Equal(*shipGroups[j]) {
|
if raceGroups[i].Equal(raceGroups[j]) {
|
||||||
shipGroups[i].Index = maxUint(shipGroups[i].Index, shipGroups[j].Index)
|
raceGroups[i].Index = maxUint(raceGroups[i].Index, raceGroups[j].Index)
|
||||||
shipGroups[i].Number += shipGroups[j].Number
|
raceGroups[i].Number += raceGroups[j].Number
|
||||||
shipGroups = append(shipGroups[:j], shipGroups[j+1:]...)
|
raceGroups = append(raceGroups[:j], raceGroups[j+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(shipGroups) == origin {
|
if len(raceGroups) == origin {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,14 +160,16 @@ func (c *Cache) JoinEqualGroups(ri int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// c.g.ShipGroups = slices.DeleteFunc(c.g.ShipGroups, func(v game.ShipGroup) bool { return v.OwnerID == c.g.Race[ri].ID })
|
slices.Sort(toDelete)
|
||||||
|
slices.Reverse(toDelete)
|
||||||
for _, idx := range toDelete {
|
for _, idx := range toDelete {
|
||||||
c.unsafeDeleteShipGroup(idx)
|
c.unsafeDeleteShipGroup(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// c.g.ShipGroups = append(c.g.ShipGroups, shipGroups...)
|
c.invalidateShipGroupCache()
|
||||||
for i := range shipGroups {
|
|
||||||
c.g.ShipGroups = append(c.g.ShipGroups, *shipGroups[i])
|
for i := range raceGroups {
|
||||||
|
c.appendShipGroup(ri, &raceGroups[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
c.invalidateShipGroupCache()
|
c.invalidateShipGroupCache()
|
||||||
@@ -202,22 +205,13 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
|
|||||||
return e.NewBeakGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
return e.NewBeakGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
p, ok := c.Planet(c.ShipGroup(sgi).Destination)
|
pl, ok := c.Planet(c.ShipGroup(sgi).Destination)
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
||||||
}
|
}
|
||||||
|
p := *pl
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p game.Planet) bool { return p.Number == c.ShipGroup(sgi).Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
|
||||||
// }
|
|
||||||
|
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.ID == c.ShipGroup(sgi).TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", c.ShipGroup(sgi).TypeID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if quantity > 0 && quantity < c.ShipGroup(sgi).Number {
|
if quantity > 0 && quantity < c.ShipGroup(sgi).Number {
|
||||||
// make new group for disassembly
|
// make new group for disassembly
|
||||||
@@ -234,8 +228,7 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
|
|||||||
switch ct {
|
switch ct {
|
||||||
case game.CargoColonist:
|
case game.CargoColonist:
|
||||||
if p.Owner == c.g.Race[ri].ID {
|
if p.Owner == c.g.Race[ri].ID {
|
||||||
pn := UnloadColonists(*p, load)
|
p = game.UnloadColonists(p, load)
|
||||||
p = &pn
|
|
||||||
}
|
}
|
||||||
case game.CargoMaterial:
|
case game.CargoMaterial:
|
||||||
p.Material += load
|
p.Material += load
|
||||||
@@ -246,9 +239,10 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
|
|||||||
|
|
||||||
p.Material += c.ShipGroup(sgi).EmptyMass(st)
|
p.Material += c.ShipGroup(sgi).EmptyMass(st)
|
||||||
|
|
||||||
// g.ShipGroups = append(g.ShipGroups[:sgi], g.ShipGroups[sgi+1:]...)
|
|
||||||
c.unsafeDeleteShipGroup(sgi)
|
c.unsafeDeleteShipGroup(sgi)
|
||||||
|
|
||||||
|
c.g.Map.Planet[c.MustPlanetIndex(p.Number)] = p
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,19 +277,11 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint
|
|||||||
return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p game.Planet) bool { return p.Number == c.ShipGroup(sgi).Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
|
||||||
// }
|
|
||||||
if p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID {
|
if p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID {
|
||||||
return e.NewEntityNotOwnedError("planet #%d", p.Number)
|
return e.NewEntityNotOwnedError("planet #%d", p.Number)
|
||||||
}
|
}
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.ID == c.ShipGroup(sgi).TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", c.ShipGroup(sgi).TypeID)
|
|
||||||
// }
|
|
||||||
if st.Cargo < 1 {
|
if st.Cargo < 1 {
|
||||||
return e.NewNoCargoBayError("ship_type %q", st.Name)
|
return e.NewNoCargoBayError("ship_type %q", st.Name)
|
||||||
}
|
}
|
||||||
@@ -366,11 +352,6 @@ func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float6
|
|||||||
return e.NewShipsBusyError()
|
return e.NewShipsBusyError()
|
||||||
}
|
}
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.ID == c.ShipGroup(sgi).TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", c.ShipGroup(sgi).TypeID)
|
|
||||||
// }
|
|
||||||
if st.Cargo < 1 {
|
if st.Cargo < 1 {
|
||||||
return e.NewNoCargoBayError("ship_type %q", st.Name)
|
return e.NewNoCargoBayError("ship_type %q", st.Name)
|
||||||
}
|
}
|
||||||
@@ -382,10 +363,6 @@ func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float6
|
|||||||
if !ok {
|
if !ok {
|
||||||
return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
||||||
}
|
}
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p game.Planet) bool { return p.Number == c.ShipGroup(sgi).Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
|
||||||
// }
|
|
||||||
if ct == game.CargoColonist {
|
if ct == game.CargoColonist {
|
||||||
if p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID {
|
if p.Owner != uuid.Nil && p.Owner != c.g.Race[ri].ID {
|
||||||
return e.NewEntityNotOwnedError("planet #%d unload %v", p.Number, ct)
|
return e.NewEntityNotOwnedError("planet #%d unload %v", p.Number, ct)
|
||||||
@@ -451,16 +428,11 @@ func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == c.ShipGroup(sgi).TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", c.ShipGroup(sgi).TypeID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
var stAcc int
|
var stAcc int
|
||||||
if stAcc = slices.IndexFunc(c.g.Race[riAccept].ShipTypes, func(v game.ShipType) bool { return v.Name == st.Name }); stAcc >= 0 &&
|
if stAcc = slices.IndexFunc(c.g.Race[riAccept].ShipTypes, func(v game.ShipType) bool { return v.Name == st.Name }); stAcc >= 0 &&
|
||||||
!st.Equal(c.g.Race[riAccept].ShipTypes[stAcc]) {
|
!st.Equal(c.g.Race[riAccept].ShipTypes[stAcc]) {
|
||||||
return e.NewGiveawayGroupShipsTypeNotEqualError("race %w, ship type %w", c.g.Race[riAccept].Name, c.g.Race[riAccept].ShipTypes[stAcc].Name)
|
return e.NewGiveawayGroupShipsTypeNotEqualError("race %q, ship type %q", c.g.Race[riAccept].Name, c.g.Race[riAccept].ShipTypes[stAcc].Name)
|
||||||
}
|
}
|
||||||
if stAcc < 0 {
|
if stAcc < 0 {
|
||||||
err = c.CreateShipType(riAccept,
|
err = c.CreateShipType(riAccept,
|
||||||
@@ -470,55 +442,20 @@ func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err
|
|||||||
st.Weapons,
|
st.Weapons,
|
||||||
st.Shields,
|
st.Shields,
|
||||||
st.Cargo)
|
st.Cargo)
|
||||||
stAcc = len(c.g.Race[ri].ShipTypes) - 1
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
stAcc = len(c.g.Race[riAccept].ShipTypes) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// maxIndex := c.ShipGroupMaxIndex(riAccept)
|
sg := *(c.ShipGroup(sgi))
|
||||||
// var maxIndex uint
|
sg.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID
|
||||||
// for sg := range g.listShipGroups(riAccept) {
|
sg.Number = uint(quantity)
|
||||||
// if sg.Index > maxIndex {
|
sg.Tech = maps.Clone(sg.Tech)
|
||||||
// maxIndex = sg.Index
|
c.appendShipGroup(riAccept, &sg)
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
c.appendShipGroup(ri, st, &game.ShipGroup{
|
|
||||||
// Index: maxIndex + 1,
|
|
||||||
OwnerID: c.g.Race[riAccept].ID,
|
|
||||||
TypeID: c.g.Race[riAccept].ShipTypes[stAcc].ID,
|
|
||||||
Number: uint(quantity),
|
|
||||||
|
|
||||||
CargoType: c.ShipGroup(sgi).CargoType,
|
|
||||||
Load: c.ShipGroup(sgi).Load,
|
|
||||||
|
|
||||||
Tech: maps.Clone(c.ShipGroup(sgi).Tech),
|
|
||||||
|
|
||||||
Destination: c.ShipGroup(sgi).Destination,
|
|
||||||
StateInSpace: c.ShipGroup(sgi).StateInSpace,
|
|
||||||
StateUpgrade: c.ShipGroup(sgi).StateUpgrade,
|
|
||||||
})
|
|
||||||
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, game.ShipGroup{
|
|
||||||
// Index: maxIndex + 1,
|
|
||||||
// OwnerID: g.Race[riAccept].ID,
|
|
||||||
// TypeID: g.Race[riAccept].ShipTypes[stAcc].ID,
|
|
||||||
// Number: uint(quantity),
|
|
||||||
|
|
||||||
// CargoType: c.ShipGroup(sgi).CargoType,
|
|
||||||
// Load: c.ShipGroup(sgi).Load,
|
|
||||||
|
|
||||||
// Tech: maps.Clone(c.ShipGroup(sgi).Tech),
|
|
||||||
|
|
||||||
// Destination: c.ShipGroup(sgi).Destination,
|
|
||||||
// StateInSpace: c.ShipGroup(sgi).StateInSpace,
|
|
||||||
// StateUpgrade: c.ShipGroup(sgi).StateUpgrade,
|
|
||||||
// })
|
|
||||||
|
|
||||||
if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
|
if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
|
||||||
c.unsafeDeleteShipGroup(sgi)
|
c.unsafeDeleteShipGroup(sgi)
|
||||||
// g.ShipGroups = append(g.ShipGroups[:sgi], g.ShipGroups[sgi+1:]...)
|
|
||||||
} else {
|
} else {
|
||||||
c.ShipGroup(sgi).Number -= quantity
|
c.ShipGroup(sgi).Number -= quantity
|
||||||
}
|
}
|
||||||
@@ -548,7 +485,7 @@ func (c *Cache) BreakGroup(ri int, groupIndex, quantity uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
|
if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
|
||||||
c.ShipGroupFleet(sgi, nil)
|
c.ShipGroupJoinFleet(sgi, nil)
|
||||||
} else {
|
} else {
|
||||||
if _, err := c.breakGroupSafe(ri, groupIndex, quantity); err != nil {
|
if _, err := c.breakGroupSafe(ri, groupIndex, quantity); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -564,33 +501,29 @@ func (c *Cache) breakGroupSafe(ri int, groupIndex uint, newGroupShips uint) (int
|
|||||||
if !ok {
|
if !ok {
|
||||||
return -1, e.NewEntityNotExistsError("group #%d", groupIndex)
|
return -1, e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||||
}
|
}
|
||||||
// maxIndex := c.ShipGroupMaxIndex(ri)
|
|
||||||
if c.ShipGroup(sgi).Number < newGroupShips {
|
if c.ShipGroup(sgi).Number < newGroupShips {
|
||||||
return -1, e.NewBreakGroupIllegalNumberError("group #%d ships: %d -> %d", c.ShipGroup(sgi).Index, c.ShipGroup(sgi).Number, newGroupShips)
|
return -1, e.NewBreakGroupIllegalNumberError("group #%d ships: %d -> %d", c.ShipGroup(sgi).Index, c.ShipGroup(sgi).Number, newGroupShips)
|
||||||
}
|
}
|
||||||
newGroup := *c.ShipGroup(sgi)
|
newGroup := *c.ShipGroup(sgi)
|
||||||
if c.ShipGroup(sgi).CargoType != nil {
|
if c.ShipGroup(sgi).CargoType != nil {
|
||||||
newGroup.Load = c.ShipGroup(sgi).Load / float64(c.ShipGroup(sgi).Number) * float64(newGroupShips)
|
newGroup.Load = c.ShipGroup(sgi).Load / float64(c.ShipGroup(sgi).Number) * float64(newGroupShips)
|
||||||
// c.ShipGroup(sgi).Load -= newGroup.Load
|
|
||||||
}
|
}
|
||||||
newGroup.Number = newGroupShips
|
newGroup.Number = newGroupShips
|
||||||
c.ShipGroupShipsNumber(sgi, c.ShipGroup(sgi).Number-newGroup.Number)
|
c.ShipGroupShipsNumber(sgi, c.ShipGroup(sgi).Number-newGroup.Number)
|
||||||
// c.ShipGroup(sgi).Number -= newGroup.Number
|
|
||||||
// newGroup.Index = maxIndex + 1
|
|
||||||
newGroup.FleetID = nil
|
newGroup.FleetID = nil
|
||||||
st := c.ShipGroupShipClass(sgi)
|
return c.appendShipGroup(ri, &newGroup), nil
|
||||||
// c.g.ShipGroups = append(c.g.ShipGroups, newGroup)
|
|
||||||
return c.appendShipGroup(ri, st, &newGroup), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal funcs
|
// Internal funcs
|
||||||
|
|
||||||
func (c *Cache) appendShipGroup(ri int, class *game.ShipType, sg *game.ShipGroup) int {
|
func (c *Cache) appendShipGroup(ri int, sg *game.ShipGroup) int {
|
||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
sg.Index = c.ShipGroupMaxIndex(ri) + 1
|
sg.Index = c.ShipGroupMaxIndex(ri) + 1
|
||||||
|
sg.OwnerID = c.g.Race[ri].ID
|
||||||
|
sg.FleetID = nil
|
||||||
c.g.ShipGroups = append(c.g.ShipGroups, *sg)
|
c.g.ShipGroups = append(c.g.ShipGroups, *sg)
|
||||||
i := len(c.g.ShipGroups) - 1
|
i := len(c.g.ShipGroups) - 1
|
||||||
c.cacheShipGroup(i, ri, class)
|
c.invalidateShipGroupCache()
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,9 +563,9 @@ func (c *Cache) shipGroupsInUpgrade(planetNumber uint) iter.Seq[*game.ShipGroup]
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) unsafeDeleteShipGroup(i int) {
|
func (c *Cache) unsafeDeleteShipGroup(i int) {
|
||||||
|
c.validateShipGroupIndex(i)
|
||||||
c.g.ShipGroups = append(c.g.ShipGroups[:i], c.g.ShipGroups[i+1:]...)
|
c.g.ShipGroups = append(c.g.ShipGroups[:i], c.g.ShipGroups[i+1:]...)
|
||||||
delete(c.raceIndexByShipGroupIndex, i)
|
c.invalidateShipGroupCache()
|
||||||
delete(c.shipClassByShipGroupIndex, i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) validateShipGroupIndex(i int) {
|
func (c *Cache) validateShipGroupIndex(i int) {
|
||||||
|
|||||||
@@ -23,23 +23,6 @@ func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error
|
|||||||
}
|
}
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
// st := g.Race[ri].ShipTypes[sti]
|
|
||||||
|
|
||||||
if st.DriveBlockMass() == 0 {
|
if st.DriveBlockMass() == 0 {
|
||||||
return e.NewSendShipHasNoDrivesError()
|
return e.NewSendShipHasNoDrivesError()
|
||||||
}
|
}
|
||||||
@@ -53,16 +36,14 @@ func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error
|
|||||||
return e.NewBeakGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
return e.NewBeakGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
p1 := c.MustPlanet(sourcePlanet)
|
p1, ok := c.Planet(sourcePlanet)
|
||||||
// p1, ok := PlanetByNum(g, sourcePlanet)
|
if !ok {
|
||||||
// if !ok {
|
return e.NewGameStateError("source planet #%d does not exists", sourcePlanet)
|
||||||
// return e.NewGameStateError("source planet #%d does not exists", sourcePlanet)
|
}
|
||||||
// }
|
p2, ok := c.Planet(planetNumber)
|
||||||
p2 := c.MustPlanet(planetNumber)
|
if !ok {
|
||||||
// p2, ok := PlanetByNum(g, planetNumber)
|
return e.NewEntityNotExistsError("destination planet #%d", planetNumber)
|
||||||
// if !ok {
|
}
|
||||||
// return e.NewEntityNotExistsError("destination planet #%d", planetNumber)
|
|
||||||
// }
|
|
||||||
rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
||||||
if rangeToDestination > c.g.Race[ri].FlightDistance() {
|
if rangeToDestination > c.g.Race[ri].FlightDistance() {
|
||||||
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
||||||
@@ -76,35 +57,39 @@ func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error
|
|||||||
sgi = nsgi
|
sgi = nsgi
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourcePlanet == planetNumber {
|
if p1.Number == p2.Number {
|
||||||
c.UnsendShips(*c.ShipGroup(sgi))
|
c.UnsendShips(c.ShipGroup(sgi))
|
||||||
c.JoinEqualGroups(ri)
|
c.JoinEqualGroups(ri)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c.LaunchShips(*c.ShipGroup(sgi), planetNumber)
|
c.LaunchShips(c.ShipGroup(sgi), planetNumber)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) LaunchShips(sg game.ShipGroup, destination uint) *game.ShipGroup {
|
func (c *Cache) LaunchShips(sg *game.ShipGroup, destination uint) *game.ShipGroup {
|
||||||
for i := range c.ShipGroupsIndex() {
|
for i := range c.ShipGroupsIndex() {
|
||||||
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index {
|
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index {
|
||||||
state := c.g.ShipGroups[i].State()
|
state := c.ShipGroup(i).State()
|
||||||
if state != game.StateInOrbit && state != game.StateLaunched {
|
if state != game.StateInOrbit && state != game.StateLaunched {
|
||||||
panic("state invalid")
|
panic("state invalid")
|
||||||
}
|
}
|
||||||
c.g.ShipGroups[i] = LaunchShips(sg, destination)
|
c.g.ShipGroups[i] = LaunchShips(*sg, destination)
|
||||||
return &c.g.ShipGroups[i]
|
return &c.g.ShipGroups[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("ship group not found")
|
panic("ship group not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) UnsendShips(sg game.ShipGroup) *game.ShipGroup {
|
func (c *Cache) UnsendShips(sg *game.ShipGroup) *game.ShipGroup {
|
||||||
for i := range c.ShipGroupsIndex() {
|
for i := range c.ShipGroupsIndex() {
|
||||||
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index {
|
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index {
|
||||||
c.g.ShipGroups[i] = UnsendShips(sg)
|
state := c.ShipGroup(i).State()
|
||||||
|
if state != game.StateLaunched {
|
||||||
|
panic("state invalid")
|
||||||
|
}
|
||||||
|
c.g.ShipGroups[i] = UnsendShips(*sg)
|
||||||
return &c.g.ShipGroups[i]
|
return &c.g.ShipGroups[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,26 +45,26 @@ func TestSendGroup(t *testing.T) {
|
|||||||
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
||||||
|
|
||||||
assert.NoError(t, g.SendGroup(Race_0.Name, 1, 2, 3)) // send 3 of 10
|
assert.NoError(t, g.SendGroup(Race_0.Name, 1, 2, 3)) // send 3 of 10
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(7), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(7), c.ShipGroup(0).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
|
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
|
||||||
assert.Equal(t, uint(3), c.ShipGroup(3).Number)
|
assert.Equal(t, uint(3), c.ShipGroup(3).Number)
|
||||||
assert.Equal(t, game.StateLaunched, c.ShipGroup(3).State())
|
assert.Equal(t, game.StateLaunched, c.ShipGroup(3).State())
|
||||||
|
|
||||||
assert.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 2)) // un-send 2 of 3
|
assert.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 2)) // un-send 2 of 3
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(9), c.MustShipGroup(Race_0_idx, 5).Number)
|
assert.Equal(t, uint(9), c.MustShipGroup(Race_0_idx, 1).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 5).State())
|
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State())
|
||||||
assert.Equal(t, uint(1), c.MustShipGroup(Race_0_idx, 4).Number)
|
assert.Equal(t, uint(1), c.MustShipGroup(Race_0_idx, 4).Number)
|
||||||
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 4).State())
|
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 4).State())
|
||||||
|
|
||||||
assert.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 0)) // un-send the rest 1
|
assert.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 0)) // un-send the rest 1
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 5).Number)
|
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 5).State())
|
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State())
|
||||||
|
|
||||||
assert.NoError(t, g.SendGroup(Race_0.Name, 5, 2, 0))
|
assert.NoError(t, g.SendGroup(Race_0.Name, 1, 2, 0))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 5).Number)
|
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number)
|
||||||
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 5).State())
|
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 1).State())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ func TestCreateShips(t *testing.T) {
|
|||||||
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
|
||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1))
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 1)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 1)
|
||||||
|
|
||||||
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 1))
|
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 1))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(1)), 1)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(1)), 1)
|
||||||
|
|
||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 6))
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 6))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 2)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
|
||||||
|
|
||||||
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
|
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(1)), 2)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(1)), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJoinEqualGroups(t *testing.T) {
|
func TestJoinEqualGroups(t *testing.T) {
|
||||||
@@ -43,48 +43,47 @@ func TestJoinEqualGroups(t *testing.T) {
|
|||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 2)) // (3)
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 2)) // (3)
|
||||||
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
|
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
|
||||||
|
|
||||||
// g.Race[Race_0_idx].SetTechLevel(game.TechDrive, 1.5)
|
c.RaceTechLevel(Race_0_idx, game.TechDrive, 1.5)
|
||||||
c.RacetTechLevel(Race_0_idx, game.TechDrive, 1.5)
|
|
||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 9)) // 4 -> 6
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 9)) // 4 -> 6
|
||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) // 5 -> 7
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) // 5 -> 7
|
||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 4)) // (6)
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 4)) // (6)
|
||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 4)) // (7)
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 4)) // (7)
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
|
|
||||||
// g.Race[Race_1_idx].SetTechLevel(game.TechShields, 2.0)
|
c.RaceTechLevel(Race_1_idx, game.TechShields, 2.0)
|
||||||
c.RacetTechLevel(Race_1_idx, game.TechShields, 2.0)
|
assert.Equal(t, 2.0, c.Race(Race_1_idx).Tech[game.TechShields])
|
||||||
assert.NoError(t, c.CreateShips(1, Race_1_Freighter, R1_Planet_1_num, 1))
|
assert.NoError(t, c.CreateShips(1, Race_1_Freighter, R1_Planet_1_num, 1))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_1_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 3)
|
||||||
|
|
||||||
c.JoinEqualGroups(Race_0_idx)
|
c.JoinEqualGroups(Race_0_idx)
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_1_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 3)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
|
|
||||||
shipTypeID := func(ri int, name string) uuid.UUID {
|
shipTypeID := func(ri int, name string) uuid.UUID {
|
||||||
class, _, ok := c.ShipClass(ri, name)
|
class, _, ok := c.ShipClass(ri, name)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("ShipType not found: %s", name)
|
t.Fatalf("ship_class not found: %s", name)
|
||||||
return uuid.Nil
|
return uuid.Nil
|
||||||
}
|
}
|
||||||
return class.ID
|
return class.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
for sg := range c.ListShipGroups(Race_0_idx) {
|
for sg := range c.RaceShipGroups(Race_0_idx) {
|
||||||
switch {
|
switch {
|
||||||
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.1:
|
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.1:
|
||||||
assert.Equal(t, uint(7), sg.Number)
|
assert.Equal(t, uint(7), sg.Number)
|
||||||
assert.Equal(t, uint(2), sg.Index)
|
assert.Equal(t, uint(1), sg.Index)
|
||||||
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.5:
|
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.5:
|
||||||
assert.Equal(t, uint(11), sg.Number)
|
assert.Equal(t, uint(11), sg.Number)
|
||||||
assert.Equal(t, uint(7), sg.Index)
|
assert.Equal(t, uint(4), sg.Index)
|
||||||
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.1:
|
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.1:
|
||||||
assert.Equal(t, uint(2), sg.Number)
|
assert.Equal(t, uint(2), sg.Number)
|
||||||
assert.Equal(t, uint(3), sg.Index)
|
assert.Equal(t, uint(2), sg.Index)
|
||||||
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.5:
|
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.5:
|
||||||
assert.Equal(t, uint(13), sg.Number)
|
assert.Equal(t, uint(13), sg.Number)
|
||||||
assert.Equal(t, uint(6), sg.Index)
|
assert.Equal(t, uint(3), sg.Index)
|
||||||
default:
|
default:
|
||||||
t.Error("not all ship groups covered")
|
t.Error("not all ship groups covered")
|
||||||
}
|
}
|
||||||
@@ -116,12 +115,12 @@ func TestBreakGroup(t *testing.T) {
|
|||||||
g.BreakGroup(Race_0.Name, 2, 0),
|
g.BreakGroup(Race_0.Name, 2, 0),
|
||||||
e.GenericErrorText(e.ErrShipsBusy))
|
e.GenericErrorText(e.ErrShipsBusy))
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 2)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
|
||||||
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1)
|
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1)
|
||||||
|
|
||||||
// group #1 -> group #3 (5 new, 8 left)
|
// group #1 -> group #3 (5 new, 8 left)
|
||||||
assert.NoError(t, c.BreakGroup(Race_0_idx, 1, 5)) // group #3 (2)
|
assert.NoError(t, c.BreakGroup(Race_0_idx, 1, 5)) // group #3 (2)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, uint(8), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(8), c.ShipGroup(0).Number)
|
||||||
assert.NotNil(t, c.ShipGroup(0).FleetID)
|
assert.NotNil(t, c.ShipGroup(0).FleetID)
|
||||||
assert.Equal(t, uint(5), c.ShipGroup(2).Number)
|
assert.Equal(t, uint(5), c.ShipGroup(2).Number)
|
||||||
@@ -133,7 +132,7 @@ func TestBreakGroup(t *testing.T) {
|
|||||||
c.ShipGroup(0).CargoType = game.CargoColonist.Ref()
|
c.ShipGroup(0).CargoType = game.CargoColonist.Ref()
|
||||||
c.ShipGroup(0).Load = 32.8 // 8 ships
|
c.ShipGroup(0).Load = 32.8 // 8 ships
|
||||||
assert.NoError(t, c.BreakGroup(Race_0_idx, 1, 2)) // group #4 (3)
|
assert.NoError(t, c.BreakGroup(Race_0_idx, 1, 2)) // group #4 (3)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(6), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(6), c.ShipGroup(0).Number)
|
||||||
assert.NotNil(t, c.ShipGroup(0).FleetID)
|
assert.NotNil(t, c.ShipGroup(0).FleetID)
|
||||||
assert.Equal(t, uint(2), c.ShipGroup(3).Number)
|
assert.Equal(t, uint(2), c.ShipGroup(3).Number)
|
||||||
@@ -149,13 +148,13 @@ func TestBreakGroup(t *testing.T) {
|
|||||||
|
|
||||||
// group #1 -> MAX 6 off the fleet
|
// group #1 -> MAX 6 off the fleet
|
||||||
assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 6)) // group #1 (0)
|
assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 6)) // group #1 (0)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(6), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(6), c.ShipGroup(0).Number)
|
||||||
assert.Nil(t, c.ShipGroup(0).FleetID)
|
assert.Nil(t, c.ShipGroup(0).FleetID)
|
||||||
|
|
||||||
// group #4 -> ALL off the fleet
|
// group #4 -> ALL off the fleet
|
||||||
assert.NoError(t, g.BreakGroup(Race_0.Name, 4, 0)) // group #1 (0)
|
assert.NoError(t, g.BreakGroup(Race_0.Name, 4, 0)) // group #1 (0)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(2), c.ShipGroup(3).Number)
|
assert.Equal(t, uint(2), c.ShipGroup(3).Number)
|
||||||
assert.Nil(t, c.ShipGroup(3).FleetID)
|
assert.Nil(t, c.ShipGroup(3).FleetID)
|
||||||
}
|
}
|
||||||
@@ -175,8 +174,8 @@ func TestGiveawayGroup(t *testing.T) {
|
|||||||
c.ShipGroup(2).CargoType = game.CargoMaterial.Ref()
|
c.ShipGroup(2).CargoType = game.CargoMaterial.Ref()
|
||||||
c.ShipGroup(2).Load = 1.234
|
c.ShipGroup(2).Load = 1.234
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 2)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_1_idx)), 1)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
|
||||||
|
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.GiveawayGroup("UnknownRace", Race_1.Name, 2, 0),
|
g.GiveawayGroup("UnknownRace", Race_1.Name, 2, 0),
|
||||||
@@ -198,12 +197,9 @@ func TestGiveawayGroup(t *testing.T) {
|
|||||||
e.GenericErrorText(e.ErrGiveawayGroupShipsTypeNotEqual))
|
e.GenericErrorText(e.ErrGiveawayGroupShipsTypeNotEqual))
|
||||||
|
|
||||||
assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
|
assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 2)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_1_idx)), 2)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2)
|
||||||
// sto := slices.IndexFunc(g.Race[Race_0_idx].ShipTypes, func(st game.ShipType) bool { return st.Name == Race_0_Gunship })
|
|
||||||
// sti := slices.IndexFunc(g.Race[Race_1_idx].ShipTypes, func(st game.ShipType) bool { return st.Name == Race_0_Gunship })
|
|
||||||
|
|
||||||
c.MustShipClass(Race_0_idx, Race_0_Gunship)
|
|
||||||
assert.Equal(t, c.MustShipClass(Race_1_idx, Race_0_Gunship).Name, c.MustShipClass(Race_0_idx, Race_0_Gunship).Name)
|
assert.Equal(t, c.MustShipClass(Race_1_idx, Race_0_Gunship).Name, c.MustShipClass(Race_0_idx, Race_0_Gunship).Name)
|
||||||
assert.Equal(t, c.MustShipClass(Race_1_idx, Race_0_Gunship).Drive, c.MustShipClass(Race_0_idx, Race_0_Gunship).Drive)
|
assert.Equal(t, c.MustShipClass(Race_1_idx, Race_0_Gunship).Drive, c.MustShipClass(Race_0_idx, Race_0_Gunship).Drive)
|
||||||
assert.Equal(t, c.MustShipClass(Race_1_idx, Race_0_Gunship).Weapons, c.MustShipClass(Race_0_idx, Race_0_Gunship).Weapons)
|
assert.Equal(t, c.MustShipClass(Race_1_idx, Race_0_Gunship).Weapons, c.MustShipClass(Race_0_idx, Race_0_Gunship).Weapons)
|
||||||
@@ -226,8 +222,8 @@ func TestGiveawayGroup(t *testing.T) {
|
|||||||
assert.Nil(t, c.ShipGroup(3).FleetID)
|
assert.Nil(t, c.ShipGroup(3).FleetID)
|
||||||
|
|
||||||
assert.NoError(t, g.GiveawayGroup(Race_1.Name, Race_0.Name, 2, 11))
|
assert.NoError(t, g.GiveawayGroup(Race_1.Name, Race_0.Name, 2, 11))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_1_idx)), 1)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadCargo(t *testing.T) {
|
func TestLoadCargo(t *testing.T) {
|
||||||
@@ -255,7 +251,7 @@ func TestLoadCargo(t *testing.T) {
|
|||||||
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
||||||
c.ShipGroup(4).Destination = R1_Planet_1_num
|
c.ShipGroup(4).Destination = R1_Planet_1_num
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 5)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5)
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
@@ -285,7 +281,6 @@ func TestLoadCargo(t *testing.T) {
|
|||||||
g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0),
|
g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0),
|
||||||
e.GenericErrorText(e.ErrInputCargoLoadNotEnough))
|
e.GenericErrorText(e.ErrInputCargoLoadNotEnough))
|
||||||
// add cargo to planet
|
// add cargo to planet
|
||||||
// g.Map.Planet[0].Material = 100
|
|
||||||
c.PutMaterial(R0_Planet_0_num, 100)
|
c.PutMaterial(R0_Planet_0_num, 100)
|
||||||
// not enough on the planet
|
// not enough on the planet
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
@@ -296,12 +291,12 @@ func TestLoadCargo(t *testing.T) {
|
|||||||
g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 1),
|
g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 1),
|
||||||
e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
|
e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 5)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5)
|
||||||
|
|
||||||
// break group and load maximum
|
// break group and load maximum
|
||||||
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 0))
|
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 0))
|
||||||
assert.Equal(t, 58.0, c.MustPlanet(R0_Planet_0_num).Material)
|
assert.Equal(t, 58.0, c.MustPlanet(R0_Planet_0_num).Material)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 6)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6)
|
||||||
assert.Nil(t, c.ShipGroup(0).CargoType)
|
assert.Nil(t, c.ShipGroup(0).CargoType)
|
||||||
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(5).CargoType)
|
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(5).CargoType)
|
||||||
assert.Equal(t, uint(9), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(9), c.ShipGroup(0).Number)
|
||||||
@@ -312,7 +307,7 @@ func TestLoadCargo(t *testing.T) {
|
|||||||
// break group and load limited
|
// break group and load limited
|
||||||
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 18))
|
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 18))
|
||||||
assert.Equal(t, 40.0, c.MustPlanet(R0_Planet_0_num).Material)
|
assert.Equal(t, 40.0, c.MustPlanet(R0_Planet_0_num).Material)
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
assert.Nil(t, c.ShipGroup(0).CargoType)
|
assert.Nil(t, c.ShipGroup(0).CargoType)
|
||||||
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(6).CargoType)
|
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(6).CargoType)
|
||||||
assert.Equal(t, uint(7), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(7), c.ShipGroup(0).Number)
|
||||||
@@ -321,27 +316,25 @@ func TestLoadCargo(t *testing.T) {
|
|||||||
assert.Equal(t, 18.0, c.ShipGroup(6).Load)
|
assert.Equal(t, 18.0, c.ShipGroup(6).Load)
|
||||||
|
|
||||||
// add cargo to planet
|
// add cargo to planet
|
||||||
// g.Map.Planet[0].Material = 100
|
|
||||||
c.PutMaterial(R0_Planet_0_num, 100)
|
c.PutMaterial(R0_Planet_0_num, 100)
|
||||||
// loading all available cargo
|
// loading all available cargo
|
||||||
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0))
|
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material)
|
assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material)
|
||||||
assert.Equal(t, 100.0, c.ShipGroup(0).Load) // free: 131.0
|
assert.Equal(t, 100.0, c.ShipGroup(0).Load) // free: 131.0
|
||||||
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
|
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
|
||||||
|
|
||||||
// add cargo to planet
|
// add cargo to planet
|
||||||
// g.Map.Planet[0].Material = 200
|
|
||||||
c.PutMaterial(R0_Planet_0_num, 200)
|
c.PutMaterial(R0_Planet_0_num, 200)
|
||||||
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 31))
|
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 31))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
assert.Equal(t, 169.0, c.MustPlanet(R0_Planet_0_num).Material)
|
assert.Equal(t, 169.0, c.MustPlanet(R0_Planet_0_num).Material)
|
||||||
assert.Equal(t, 131.0, c.ShipGroup(0).Load) // free: 100.0
|
assert.Equal(t, 131.0, c.ShipGroup(0).Load) // free: 100.0
|
||||||
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
|
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
|
||||||
|
|
||||||
// load to maximum cargo space left
|
// load to maximum cargo space left
|
||||||
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 0))
|
assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 0))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
assert.Equal(t, 153.0, c.MustPlanet(R0_Planet_0_num).Material)
|
assert.Equal(t, 153.0, c.MustPlanet(R0_Planet_0_num).Material)
|
||||||
assert.Equal(t, 147.0, c.ShipGroup(0).Load) // free: 0.0
|
assert.Equal(t, 147.0, c.ShipGroup(0).Load) // free: 0.0
|
||||||
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
|
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
|
||||||
@@ -350,7 +343,7 @@ func TestLoadCargo(t *testing.T) {
|
|||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0),
|
g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0),
|
||||||
e.GenericErrorText(e.ErrInputCargoLoadNoSpaceLeft))
|
e.GenericErrorText(e.ErrInputCargoLoadNoSpaceLeft))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnloadCargo(t *testing.T) {
|
func TestUnloadCargo(t *testing.T) {
|
||||||
@@ -386,7 +379,7 @@ func TestUnloadCargo(t *testing.T) {
|
|||||||
c.ShipGroup(5).CargoType = game.CargoMaterial.Ref()
|
c.ShipGroup(5).CargoType = game.CargoMaterial.Ref()
|
||||||
c.ShipGroup(5).Load = 100.0
|
c.ShipGroup(5).Load = 100.0
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 6)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6)
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
@@ -416,12 +409,12 @@ func TestUnloadCargo(t *testing.T) {
|
|||||||
g.UnloadCargo(Race_0.Name, 1, 0, 1),
|
g.UnloadCargo(Race_0.Name, 1, 0, 1),
|
||||||
e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
|
e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 6)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6)
|
||||||
|
|
||||||
// unload MAT on foreign planet / break group
|
// unload MAT on foreign planet / break group
|
||||||
assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 0))
|
assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 0))
|
||||||
assert.Equal(t, 27.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material))
|
assert.Equal(t, 27.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 7)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
|
||||||
assert.Equal(t, uint(3), c.ShipGroup(6).Number)
|
assert.Equal(t, uint(3), c.ShipGroup(6).Number)
|
||||||
assert.Nil(t, c.ShipGroup(6).CargoType)
|
assert.Nil(t, c.ShipGroup(6).CargoType)
|
||||||
assert.Equal(t, 0.0, c.ShipGroup(6).Load)
|
assert.Equal(t, 0.0, c.ShipGroup(6).Load)
|
||||||
@@ -432,7 +425,7 @@ func TestUnloadCargo(t *testing.T) {
|
|||||||
// unload MAT on foreign planet / break group / limited MAT
|
// unload MAT on foreign planet / break group / limited MAT
|
||||||
assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 20.0))
|
assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 20.0))
|
||||||
assert.Equal(t, 47.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material))
|
assert.Equal(t, 47.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 8)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8)
|
||||||
assert.Equal(t, uint(3), c.ShipGroup(7).Number)
|
assert.Equal(t, uint(3), c.ShipGroup(7).Number)
|
||||||
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(7).CargoType)
|
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(7).CargoType)
|
||||||
assert.Equal(t, 7.273, number.Fixed3(c.ShipGroup(7).Load))
|
assert.Equal(t, 7.273, number.Fixed3(c.ShipGroup(7).Load))
|
||||||
@@ -443,7 +436,7 @@ func TestUnloadCargo(t *testing.T) {
|
|||||||
// unload ALL
|
// unload ALL
|
||||||
assert.NoError(t, g.UnloadCargo(Race_0.Name, 1, 0, 0))
|
assert.NoError(t, g.UnloadCargo(Race_0.Name, 1, 0, 0))
|
||||||
assert.Equal(t, 100.0, number.Fixed3(c.MustPlanet(R0_Planet_0_num).Colonists))
|
assert.Equal(t, 100.0, number.Fixed3(c.MustPlanet(R0_Planet_0_num).Colonists))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 8)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8)
|
||||||
assert.Equal(t, uint(10), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(10), c.ShipGroup(0).Number)
|
||||||
assert.Nil(t, c.ShipGroup(0).CargoType)
|
assert.Nil(t, c.ShipGroup(0).CargoType)
|
||||||
assert.Equal(t, 0.0, number.Fixed3(c.ShipGroup(0).Load))
|
assert.Equal(t, 0.0, number.Fixed3(c.ShipGroup(0).Load))
|
||||||
@@ -479,7 +472,7 @@ func TestDisassembleGroup(t *testing.T) {
|
|||||||
c.ShipGroup(4).CargoType = game.CargoColonist.Ref()
|
c.ShipGroup(4).CargoType = game.CargoColonist.Ref()
|
||||||
c.ShipGroup(4).Load = 2.345
|
c.ShipGroup(4).Load = 2.345
|
||||||
|
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 5)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5)
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
@@ -496,41 +489,37 @@ func TestDisassembleGroup(t *testing.T) {
|
|||||||
e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
|
e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
|
||||||
|
|
||||||
groupEmptyMass := c.ShipGroup(4).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
|
groupEmptyMass := c.ShipGroup(4).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
|
||||||
// groupEmptyMass := c.ShipGroup(4).EmptyMass(&g.Race[Race_0_idx].ShipTypes[Race_0_Freighter_idx])
|
|
||||||
// groupLoadCOL := c.ShipGroup(4).Load
|
|
||||||
planetMAT := c.MustPlanet(R1_Planet_1_num).Material
|
planetMAT := c.MustPlanet(R1_Planet_1_num).Material
|
||||||
planetCOL := c.MustPlanet(R1_Planet_1_num).Colonists
|
planetCOL := c.MustPlanet(R1_Planet_1_num).Colonists
|
||||||
|
|
||||||
assert.NoError(t, g.DisassembleGroup(Race_0.Name, 5, 0))
|
assert.NoError(t, g.DisassembleGroup(Race_0.Name, 5, 0))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R1_Planet_1_num).Material)
|
assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R1_Planet_1_num).Material)
|
||||||
assert.Equal(t, planetCOL, c.MustPlanet(R1_Planet_1_num).Colonists)
|
assert.Equal(t, planetCOL, c.MustPlanet(R1_Planet_1_num).Colonists)
|
||||||
|
|
||||||
groupEmptyMass = c.ShipGroup(3).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
|
groupEmptyMass = c.ShipGroup(3).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
|
||||||
// groupEmptyMass = c.ShipGroup(3).EmptyMass(&g.Race[Race_0_idx].ShipTypes[Race_0_Freighter_idx])
|
|
||||||
groupLoadMAT := c.ShipGroup(3).Load
|
groupLoadMAT := c.ShipGroup(3).Load
|
||||||
planetMAT = c.MustPlanet(R1_Planet_1_num).Material
|
planetMAT = c.MustPlanet(R1_Planet_1_num).Material
|
||||||
assert.NoError(t, g.DisassembleGroup(Race_0.Name, 4, 0))
|
assert.NoError(t, g.DisassembleGroup(Race_0.Name, 4, 0))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, c.MustPlanet(R1_Planet_1_num).Material)
|
assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, c.MustPlanet(R1_Planet_1_num).Material)
|
||||||
|
|
||||||
groupEmptyMass = c.ShipGroup(2).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
|
groupEmptyMass = c.ShipGroup(2).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
|
||||||
// groupEmptyMass = c.ShipGroup(2).EmptyMass(&g.Race[Race_0_idx].ShipTypes[Race_0_Freighter_idx])
|
|
||||||
planetMAT = c.MustPlanet(R0_Planet_0_num).Material
|
planetMAT = c.MustPlanet(R0_Planet_0_num).Material
|
||||||
planetCOL = c.MustPlanet(R0_Planet_0_num).Colonists
|
planetCOL = c.MustPlanet(R0_Planet_0_num).Colonists
|
||||||
planetPOP := 90.0
|
planetPOP := 90.0
|
||||||
|
|
||||||
// g.Map.Planet[0].Population = planetPOP
|
|
||||||
c.PutPopulation(R0_Planet_0_num, planetPOP)
|
c.PutPopulation(R0_Planet_0_num, planetPOP)
|
||||||
var shipsDisassembling uint = 3
|
assert.Equal(t, planetPOP, c.MustPlanet(R0_Planet_0_num).Population)
|
||||||
groupEmptyMass = groupEmptyMass / float64(c.ShipGroup(2).Number) * float64(shipsDisassembling)
|
var quantity uint = 3
|
||||||
newGroupUnloadedCOL := c.ShipGroup(2).Load / float64(c.ShipGroup(2).Number) * float64(shipsDisassembling)
|
groupEmptyMass = groupEmptyMass / float64(c.ShipGroup(2).Number) * float64(quantity)
|
||||||
|
newGroupUnloadedCOL := c.ShipGroup(2).Load / float64(c.ShipGroup(2).Number) * float64(quantity)
|
||||||
expectPOPIncrease := newGroupUnloadedCOL * 8
|
expectPOPIncrease := newGroupUnloadedCOL * 8
|
||||||
freePOPLeft := c.MustPlanet(R0_Planet_0_num).Size - c.MustPlanet(R0_Planet_0_num).Population
|
freePOPLeft := c.MustPlanet(R0_Planet_0_num).Size - c.MustPlanet(R0_Planet_0_num).Population
|
||||||
expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8
|
expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8
|
||||||
expectAddedPOP := freePOPLeft
|
expectAddedPOP := freePOPLeft
|
||||||
assert.NoError(t, g.DisassembleGroup(Race_0.Name, 3, shipsDisassembling))
|
assert.NoError(t, g.DisassembleGroup(Race_0.Name, 3, quantity))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, planetCOL+expectAddedCOL, c.MustPlanet(R0_Planet_0_num).Colonists)
|
assert.Equal(t, planetCOL+expectAddedCOL, c.MustPlanet(R0_Planet_0_num).Colonists)
|
||||||
assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population)
|
assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population)
|
||||||
assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R0_Planet_0_num).Material)
|
assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R0_Planet_0_num).Material)
|
||||||
|
|||||||
@@ -22,33 +22,11 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi
|
|||||||
}
|
}
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == c.ShipGroup(sgi).TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", c.ShipGroup(sgi).TypeID)
|
|
||||||
// }
|
|
||||||
// st := g.Race[ri].ShipTypes[sti]
|
|
||||||
|
|
||||||
if s := c.ShipGroup(sgi).State(); s != game.StateInOrbit && s != game.StateUpgrade {
|
if s := c.ShipGroup(sgi).State(); s != game.StateInOrbit && s != game.StateUpgrade {
|
||||||
return e.NewShipsBusyError()
|
return e.NewShipsBusyError()
|
||||||
}
|
}
|
||||||
|
|
||||||
pl := c.MustPlanet(c.ShipGroup(sgi).Destination)
|
pl := c.MustPlanet(c.ShipGroup(sgi).Destination)
|
||||||
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == c.ShipGroup(sgi).Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", c.ShipGroup(sgi).Destination)
|
|
||||||
// }
|
|
||||||
if pl.Owner != uuid.Nil && pl.Owner != c.g.Race[ri].ID {
|
if pl.Owner != uuid.Nil && pl.Owner != c.g.Race[ri].ID {
|
||||||
return e.NewEntityNotOwnedError("planet #%d for upgrade group #%d", pl.Number, groupIndex)
|
return e.NewEntityNotOwnedError("planet #%d for upgrade group #%d", pl.Number, groupIndex)
|
||||||
}
|
}
|
||||||
@@ -160,56 +138,15 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi
|
|||||||
// finally, fill group upgrade prefs
|
// finally, fill group upgrade prefs
|
||||||
for tech := range targetLevel {
|
for tech := range targetLevel {
|
||||||
if targetLevel[tech] > 0 {
|
if targetLevel[tech] > 0 {
|
||||||
c.upgradeShipGroup(sgi, tech, targetLevel[tech])
|
c.UpgradeShipGroup(sgi, tech, targetLevel[tech])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// func CurrentUpgradingLevel(sg game.ShipGroup, tech game.Tech) float64 {
|
func (c *Cache) UpgradeShipGroup(sgi int, tech game.Tech, v float64) {
|
||||||
// if sg.StateUpgrade == nil {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// ti := slices.IndexFunc(sg.StateUpgrade.UpgradeTech, func(pref game.UpgradePreference) bool { return pref.Tech == tech })
|
|
||||||
// if ti >= 0 {
|
|
||||||
// return sg.StateUpgrade.UpgradeTech[ti].Level
|
|
||||||
// }
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func FutureUpgradeLevel(raceLevel, groupLevel, limit float64) float64 {
|
|
||||||
// target := limit
|
|
||||||
// if target == 0 || target > raceLevel {
|
|
||||||
// target = raceLevel
|
|
||||||
// }
|
|
||||||
// if groupLevel == target {
|
|
||||||
// return 0
|
|
||||||
// }
|
|
||||||
// return target
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (c *Cache) upgradeShipGroup(sgi int, tech game.Tech, v float64) {
|
|
||||||
sg := *(c.ShipGroup(sgi))
|
sg := *(c.ShipGroup(sgi))
|
||||||
st := c.ShipGroupShipClass(sgi)
|
st := c.ShipGroupShipClass(sgi)
|
||||||
// if v <= 0 || st.BlockMass(tech) == 0 || sg.TechLevel(tech) >= v {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// var su game.InUpgrade
|
|
||||||
// if sg.StateUpgrade != nil {
|
|
||||||
// su = *sg.StateUpgrade
|
|
||||||
// } else {
|
|
||||||
// su = game.InUpgrade{UpgradeTech: []game.UpgradePreference{}}
|
|
||||||
// }
|
|
||||||
// ti := slices.IndexFunc(su.UpgradeTech, func(pref game.UpgradePreference) bool { return pref.Tech == tech })
|
|
||||||
// if ti < 0 {
|
|
||||||
// su.UpgradeTech = append(su.UpgradeTech, game.UpgradePreference{Tech: tech})
|
|
||||||
// ti = len(su.UpgradeTech) - 1
|
|
||||||
// }
|
|
||||||
// su.UpgradeTech[ti].Level = v
|
|
||||||
// su.UpgradeTech[ti].Cost = game.BlockUpgradeCost(st.BlockMass(tech), sg.TechLevel(tech), v) * float64(sg.Number)
|
|
||||||
|
|
||||||
// sg.StateUpgrade = &su
|
|
||||||
|
|
||||||
c.g.ShipGroups[sgi] = game.UpgradeGroupPreference(sg, *st, tech, v)
|
c.g.ShipGroups[sgi] = game.UpgradeGroupPreference(sg, *st, tech, v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,108 +9,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBlockUpgradeCost(t *testing.T) {
|
|
||||||
assert.Equal(t, 00.0, game.BlockUpgradeCost(1, 1.0, 1.0))
|
|
||||||
assert.Equal(t, 25.0, game.BlockUpgradeCost(5, 1.0, 2.0))
|
|
||||||
assert.Equal(t, 50.0, game.BlockUpgradeCost(10, 1.0, 2.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGroupUpgradeCost(t *testing.T) {
|
|
||||||
sg := game.ShipGroup{
|
|
||||||
Tech: map[game.Tech]float64{
|
|
||||||
game.TechDrive: 1.0,
|
|
||||||
game.TechWeapons: 1.0,
|
|
||||||
game.TechShields: 1.0,
|
|
||||||
game.TechCargo: 1.0,
|
|
||||||
},
|
|
||||||
Number: 1,
|
|
||||||
}
|
|
||||||
assert.Equal(t, 225.0, game.GroupUpgradeCost(sg, Cruiser, 2.0, 2.0, 2.0, 2.0).UpgradeCost(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpgradeMaxShips(t *testing.T) {
|
|
||||||
sg := game.ShipGroup{
|
|
||||||
Tech: map[game.Tech]float64{
|
|
||||||
game.TechDrive: 1.0,
|
|
||||||
game.TechWeapons: 1.0,
|
|
||||||
game.TechShields: 1.0,
|
|
||||||
game.TechCargo: 1.0,
|
|
||||||
},
|
|
||||||
Number: 10,
|
|
||||||
}
|
|
||||||
uc := game.GroupUpgradeCost(sg, Cruiser, 2.0, 2.0, 2.0, 2.0)
|
|
||||||
assert.Equal(t, uint(4), uc.UpgradeMaxShips(1000))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCurrentUpgradingLevel(t *testing.T) {
|
|
||||||
sg := &game.ShipGroup{
|
|
||||||
StateUpgrade: nil,
|
|
||||||
}
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechDrive))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechWeapons))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechShields))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechCargo))
|
|
||||||
|
|
||||||
sg.StateUpgrade = &game.InUpgrade{
|
|
||||||
UpgradeTech: []game.UpgradePreference{
|
|
||||||
{Tech: game.TechDrive, Level: 1.5, Cost: 100.1},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
assert.Equal(t, 1.5, game.CurrentUpgradingLevel(*sg, game.TechDrive))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechWeapons))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechShields))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechCargo))
|
|
||||||
|
|
||||||
sg.StateUpgrade.UpgradeTech = append(sg.StateUpgrade.UpgradeTech, game.UpgradePreference{Tech: game.TechCargo, Level: 2.2, Cost: 200.2})
|
|
||||||
assert.Equal(t, 1.5, game.CurrentUpgradingLevel(*sg, game.TechDrive))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechWeapons))
|
|
||||||
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechShields))
|
|
||||||
assert.Equal(t, 2.2, game.CurrentUpgradingLevel(*sg, game.TechCargo))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFutureUpgradeLevel(t *testing.T) {
|
|
||||||
assert.Equal(t, 0.0, game.FutureUpgradeLevel(2.0, 2.0, 2.0))
|
|
||||||
assert.Equal(t, 0.0, game.FutureUpgradeLevel(2.0, 2.0, 3.0))
|
|
||||||
assert.Equal(t, 1.5, game.FutureUpgradeLevel(1.5, 2.0, 3.0))
|
|
||||||
assert.Equal(t, 2.0, game.FutureUpgradeLevel(2.5, 1.0, 2.0))
|
|
||||||
assert.Equal(t, 2.5, game.FutureUpgradeLevel(2.5, 1.0, 0.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// func TestUpgradeGroupPreference(t *testing.T) {
|
|
||||||
// sg := game.ShipGroup{
|
|
||||||
// Number: 4,
|
|
||||||
// Tech: game.TechSet{
|
|
||||||
// game.TechDrive: 1.0,
|
|
||||||
// game.TechWeapons: 1.0,
|
|
||||||
// game.TechShields: 1.0,
|
|
||||||
// game.TechCargo: 1.0,
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// assert.Nil(t, sg.StateUpgrade)
|
|
||||||
// sg = game.UpgradeGroupPreference(sg, Cruiser, game.TechDrive, 0)
|
|
||||||
// assert.Nil(t, sg.StateUpgrade)
|
|
||||||
|
|
||||||
// sg = game.UpgradeGroupPreference(sg, Cruiser, game.TechDrive, 2.0)
|
|
||||||
// assert.NotNil(t, sg.StateUpgrade)
|
|
||||||
// assert.Equal(t, 300., sg.StateUpgrade.TechCost(game.TechDrive))
|
|
||||||
// assert.Equal(t, 300., sg.StateUpgrade.Cost())
|
|
||||||
|
|
||||||
// sg = game.UpgradeGroupPreference(sg, Cruiser, game.TechWeapons, 2.0)
|
|
||||||
// assert.NotNil(t, sg.StateUpgrade)
|
|
||||||
// assert.Equal(t, 300., sg.StateUpgrade.TechCost(game.TechWeapons))
|
|
||||||
// assert.Equal(t, 600., sg.StateUpgrade.Cost())
|
|
||||||
|
|
||||||
// sg = game.UpgradeGroupPreference(sg, Cruiser, game.TechShields, 2.0)
|
|
||||||
// assert.NotNil(t, sg.StateUpgrade)
|
|
||||||
// assert.Equal(t, 300., sg.StateUpgrade.TechCost(game.TechShields))
|
|
||||||
// assert.Equal(t, 900., sg.StateUpgrade.Cost())
|
|
||||||
|
|
||||||
// sg = game.UpgradeGroupPreference(sg, Cruiser, game.TechCargo, 2.0)
|
|
||||||
// assert.NotNil(t, sg.StateUpgrade)
|
|
||||||
// assert.Equal(t, 0., sg.StateUpgrade.TechCost(game.TechCargo))
|
|
||||||
// assert.Equal(t, 900., sg.StateUpgrade.Cost())
|
|
||||||
// }
|
|
||||||
|
|
||||||
func TestUpgradeGroup(t *testing.T) {
|
func TestUpgradeGroup(t *testing.T) {
|
||||||
c, g := newCache()
|
c, g := newCache()
|
||||||
// group #1 - in_orbit, free to upgrade
|
// group #1 - in_orbit, free to upgrade
|
||||||
@@ -150,15 +48,14 @@ func TestUpgradeGroup(t *testing.T) {
|
|||||||
g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 1.1),
|
g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 1.1),
|
||||||
e.GenericErrorText(e.ErrInputUpgradeShipsAlreadyUpToDate))
|
e.GenericErrorText(e.ErrInputUpgradeShipsAlreadyUpToDate))
|
||||||
|
|
||||||
// g.Race[Race_0_idx].SetTechLevel(game.TechDrive, 10.0)
|
c.RaceTechLevel(Race_0_idx, game.TechDrive, 10.0)
|
||||||
c.RacetTechLevel(Race_0_idx, game.TechDrive, 10.0)
|
|
||||||
assert.Equal(t, 10.0, c.Race(Race_0_idx).TechLevel(game.TechDrive))
|
assert.Equal(t, 10.0, c.Race(Race_0_idx).TechLevel(game.TechDrive))
|
||||||
assert.ErrorContains(t,
|
assert.ErrorContains(t,
|
||||||
g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 10.0),
|
g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 10.0),
|
||||||
e.GenericErrorText(e.ErrUpgradeInsufficientResources))
|
e.GenericErrorText(e.ErrUpgradeInsufficientResources))
|
||||||
|
|
||||||
assert.NoError(t, g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 2, 1.2))
|
assert.NoError(t, g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 2, 1.2))
|
||||||
assert.Len(t, slices.Collect(c.ListShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(8), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(8), c.ShipGroup(0).Number)
|
||||||
assert.Equal(t, uint(2), c.ShipGroup(3).Number)
|
assert.Equal(t, uint(2), c.ShipGroup(3).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
|
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import (
|
|||||||
func RenamePlanet(configure func(*controller.Param), race string, number int, name string) (err error) {
|
func RenamePlanet(configure func(*controller.Param), race string, number int, name string) (err error) {
|
||||||
control(configure, func(c *controller.Controller) {
|
control(configure, func(c *controller.Controller) {
|
||||||
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
||||||
err = renamePlanet(r, g, race, number, name)
|
err = renamePlanet(c, r, g, race, number, name)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func renamePlanet(r controller.Repo, g *game.Game, race string, number int, name string) error {
|
func renamePlanet(c *controller.Controller, r controller.Repo, g *game.Game, race string, number int, name string) error {
|
||||||
if err := g.RenamePlanet(race, number, name); err != nil {
|
if err := c.RenamePlanet(race, number, name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.SaveState(g)
|
return r.SaveState(g)
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import (
|
|||||||
func PlanetProduction(configure func(*controller.Param), race string, planetNumber int, prodType, subject string) (err error) {
|
func PlanetProduction(configure func(*controller.Param), race string, planetNumber int, prodType, subject string) (err error) {
|
||||||
control(configure, func(c *controller.Controller) {
|
control(configure, func(c *controller.Controller) {
|
||||||
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
||||||
err = planetProduction(r, g, race, planetNumber, prodType, subject)
|
err = planetProduction(c, r, g, race, planetNumber, prodType, subject)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func planetProduction(r controller.Repo, g *game.Game, race string, planetNumber int, prodType, subject string) error {
|
func planetProduction(c *controller.Controller, r controller.Repo, g *game.Game, race string, planetNumber int, prodType, subject string) error {
|
||||||
if err := g.PlanetProduction(race, planetNumber, prodType, subject); err != nil {
|
if err := c.PlanetProduction(race, planetNumber, prodType, subject); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.SaveState(g)
|
return r.SaveState(g)
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import (
|
|||||||
func CreateScience(configure func(*controller.Param), race, typeName string, drive, weapons, shields, cargo float64) (err error) {
|
func CreateScience(configure func(*controller.Param), race, typeName string, drive, weapons, shields, cargo float64) (err error) {
|
||||||
control(configure, func(c *controller.Controller) {
|
control(configure, func(c *controller.Controller) {
|
||||||
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
||||||
err = createScience(r, g, race, typeName, drive, weapons, shields, cargo)
|
err = createScience(c, r, g, race, typeName, drive, weapons, shields, cargo)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func createScience(r controller.Repo, g *game.Game, race, typeName string, d, w, s, c float64) error {
|
func createScience(c *controller.Controller, r controller.Repo, g *game.Game, race, typeName string, drive, weapons, shields, cargo float64) error {
|
||||||
if err := g.CreateScience(race, typeName, d, w, s, c); err != nil {
|
if err := c.CreateScience(race, typeName, drive, weapons, shields, cargo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.SaveState(g)
|
return r.SaveState(g)
|
||||||
@@ -24,14 +24,14 @@ func createScience(r controller.Repo, g *game.Game, race, typeName string, d, w,
|
|||||||
func DeleteScience(configure func(*controller.Param), race, typeName string) (err error) {
|
func DeleteScience(configure func(*controller.Param), race, typeName string) (err error) {
|
||||||
control(configure, func(c *controller.Controller) {
|
control(configure, func(c *controller.Controller) {
|
||||||
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
c.ExecuteGame(func(r controller.Repo, g *game.Game) {
|
||||||
err = deleteScience(r, g, race, typeName)
|
err = deleteScience(c, r, g, race, typeName)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteScience(r controller.Repo, g *game.Game, race, typeName string) error {
|
func deleteScience(c *controller.Controller, r controller.Repo, g *game.Game, race, typeName string) error {
|
||||||
if err := g.DeleteScience(race, typeName); err != nil {
|
if err := c.DeleteScience(race, typeName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.SaveState(g)
|
return r.SaveState(g)
|
||||||
|
|||||||
@@ -5,40 +5,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/iliadenisov/galaxy/internal/controller"
|
"github.com/iliadenisov/galaxy/internal/controller"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/game"
|
||||||
|
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
"github.com/iliadenisov/galaxy/internal/game"
|
|
||||||
mg "github.com/iliadenisov/galaxy/internal/model/game"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateScience(t *testing.T) {
|
func TestCreateScience(t *testing.T) {
|
||||||
race := "race_01"
|
|
||||||
typeName := "First Step"
|
typeName := "First Step"
|
||||||
g(t, func(p func(*controller.Param), g func() *mg.Game) {
|
c(t, func(p func(*controller.Param), g func() *controller.Controller) {
|
||||||
err := game.DeleteScience(p, race, typeName)
|
err := g().CreateScience(unknownRaceName, " "+typeName+" ", 1, 0, 0, 0)
|
||||||
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
err = game.CreateScience(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0) // TODO: test on dead race
|
|
||||||
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
|
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
err = game.CreateScience(p, race, " "+typeName+" ", 1, 0, 0, 0)
|
err = g().DeleteScience(unknownRaceName, typeName)
|
||||||
assert.NoError(t, err)
|
|
||||||
sc, err := g().Sciences(race)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, sc, 1)
|
|
||||||
assert.Equal(t, sc[0].Name, typeName)
|
|
||||||
assert.Equal(t, sc[0].Drive, 1.)
|
|
||||||
assert.Equal(t, sc[0].Weapons, 0.)
|
|
||||||
assert.Equal(t, sc[0].Shields, 0.)
|
|
||||||
assert.Equal(t, sc[0].Cargo, 0.)
|
|
||||||
// TODO: test delete with existing ship group
|
|
||||||
// TODO: test delete with planet production busy with science
|
|
||||||
err = game.DeleteScience(p, unknownRaceName, typeName) // TODO: test with actial rip race
|
|
||||||
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
|
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
err = game.DeleteScience(p, race, typeName)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
sc, err = g().Sciences(race)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Len(t, sc, 0)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +53,14 @@ func TestCreateScienceValidation(t *testing.T) {
|
|||||||
{typeName, 0, 0, 0, -1, e.GenericErrorText(e.ErrInputCargoValue)},
|
{typeName, 0, 0, 0, -1, e.GenericErrorText(e.ErrInputCargoValue)},
|
||||||
{typeName, 0, 1, 1, -1, e.GenericErrorText(e.ErrInputCargoValue)},
|
{typeName, 0, 1, 1, -1, e.GenericErrorText(e.ErrInputCargoValue)},
|
||||||
}
|
}
|
||||||
g(t, func(p func(*controller.Param), g func() *mg.Game) {
|
c(t, func(p func(*controller.Param), g func() *controller.Controller) {
|
||||||
for i, tc := range table {
|
for i, tc := range table {
|
||||||
if tc.err == "" {
|
if tc.err == "" {
|
||||||
err := game.CreateScience(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c)
|
n := tc.name + strconv.Itoa(i)
|
||||||
assert.NoError(t, err)
|
err := game.CreateScience(p, race, n, tc.d, tc.w, tc.s, tc.c)
|
||||||
err = game.CreateScience(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c)
|
assert.NoError(t, err, "for name=%q", n)
|
||||||
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameDuplicate))
|
err = game.CreateScience(p, race, n, tc.d, tc.w, tc.s, tc.c)
|
||||||
|
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameDuplicate), "for name=%q", n)
|
||||||
} else {
|
} else {
|
||||||
err := game.CreateScience(p, race, tc.name, tc.d, tc.w, tc.s, tc.c)
|
err := game.CreateScience(p, race, tc.name, tc.d, tc.w, tc.s, tc.c)
|
||||||
assert.ErrorContains(t, err, tc.err)
|
assert.ErrorContains(t, err, tc.err)
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func TestCreateShipTypeValidation(t *testing.T) {
|
|||||||
|
|
||||||
func TestMergeShipType(t *testing.T) {
|
func TestMergeShipType(t *testing.T) {
|
||||||
race := "race_01"
|
race := "race_01"
|
||||||
c(t, func(p func(*controller.Param), ctrl func() *controller.Controller) {
|
c(t, func(p func(*controller.Param), ctl func() *controller.Controller) {
|
||||||
err := game.CreateShipType(p, race, "Drone", 1, 0, 0, 0, 0)
|
err := game.CreateShipType(p, race, "Drone", 1, 0, 0, 0, 0)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = game.CreateShipType(p, race, "Spy", 1, 0, 0, 0, 0)
|
err = game.CreateShipType(p, race, "Spy", 1, 0, 0, 0, 0)
|
||||||
@@ -109,7 +109,7 @@ func TestMergeShipType(t *testing.T) {
|
|||||||
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
|
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
err = game.MergeShipType(p, race, "Spy", "Drone")
|
err = game.MergeShipType(p, race, "Spy", "Drone")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
st, err := ctrl().ShipTypes(race)
|
st, err := ctl().ShipTypes(race)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, st, 2)
|
assert.Len(t, st, 2)
|
||||||
err = game.MergeShipType(p, race, "Drone", "Cruiser")
|
err = game.MergeShipType(p, race, "Drone", "Cruiser")
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func c(t *testing.T, f func(p func(*controller.Param), g func() *controller.Cont
|
|||||||
assert.FailNow(t, "c: GenerateGame", err)
|
assert.FailNow(t, "c: GenerateGame", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g := func() *controller.Controller {
|
ctl := func() *controller.Controller {
|
||||||
c, err := controller.NewController(p)
|
c, err := controller.NewController(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.FailNow(t, "c: NewController", err)
|
assert.FailNow(t, "c: NewController", err)
|
||||||
@@ -80,5 +80,5 @@ func c(t *testing.T, f func(p func(*controller.Param), g func() *controller.Cont
|
|||||||
c.Cache = controller.NewCache(g)
|
c.Cache = controller.NewCache(g)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
f(p, g)
|
f(p, ctl)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,247 +2,8 @@ package game
|
|||||||
|
|
||||||
import "github.com/google/uuid"
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
// import (
|
|
||||||
// "fmt"
|
|
||||||
// "iter"
|
|
||||||
// "math"
|
|
||||||
// "slices"
|
|
||||||
|
|
||||||
// "github.com/google/uuid"
|
|
||||||
// e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
// )
|
|
||||||
|
|
||||||
type Fleet struct {
|
type Fleet struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
OwnerID uuid.UUID `json:"ownerId"`
|
OwnerID uuid.UUID `json:"ownerId"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// func FleetState(g *Game, fleetID uuid.UUID) (ShipGroupState, *uint, *InSpace) {
|
|
||||||
// fi := slices.IndexFunc(g.Fleets, func(f Fleet) bool { return f.ID == fleetID })
|
|
||||||
// if fi < 0 {
|
|
||||||
// panic("FleetState: fleet id not found: " + fleetID.String())
|
|
||||||
// }
|
|
||||||
// ri := slices.IndexFunc(g.Race, func(r Race) bool { return r.ID == g.Fleets[fi].OwnerID })
|
|
||||||
// if ri < 0 {
|
|
||||||
// panic("FleetState: race id not found: " + g.Fleets[fi].OwnerID.String())
|
|
||||||
// }
|
|
||||||
// var state *ShipGroupState
|
|
||||||
// var onPlanet *uint
|
|
||||||
// var is *InSpace
|
|
||||||
// for sg := range FleetGroups(g, ri, fi) {
|
|
||||||
// if state == nil {
|
|
||||||
// s := sg.State()
|
|
||||||
// state = &s
|
|
||||||
// if planet, ok := sg.OnPlanet(); ok {
|
|
||||||
// onPlanet = &planet
|
|
||||||
// }
|
|
||||||
// is = sg.StateInSpace
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// if *state != sg.State() {
|
|
||||||
// panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q has different states", g.Race[ri].Name, g.Fleets[fi].Name))
|
|
||||||
// }
|
|
||||||
// if planet, ok := sg.OnPlanet(); ok && onPlanet != nil && *onPlanet != planet {
|
|
||||||
// for sg := range FleetGroups(g, ri, fi) {
|
|
||||||
// fmt.Println("group", sg.Index, "fleet", sg.FleetID, g.Fleets[fi].Name, "state", sg.State(), "on", sg.Destination)
|
|
||||||
// }
|
|
||||||
// panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q are on different planets: %d <> %d", g.Race[ri].Name, g.Fleets[fi].Name, *onPlanet, planet))
|
|
||||||
// }
|
|
||||||
// if (is == nil && sg.StateInSpace != nil) || (is != nil && sg.StateInSpace == nil) {
|
|
||||||
// panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q on_planet and in_space at the same time", g.Race[ri].Name, g.Fleets[fi].Name))
|
|
||||||
// }
|
|
||||||
// if is != nil && sg.StateInSpace != nil && !is.Equal(*sg.StateInSpace) {
|
|
||||||
// panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q has different is_space states", g.Race[ri].Name, g.Fleets[fi].Name))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if state == nil {
|
|
||||||
// panic(fmt.Sprintf("FleetState: race's %q fleet %q has no ships", g.Race[ri].Name, g.Fleets[fi].Name))
|
|
||||||
// }
|
|
||||||
// return *state, onPlanet, is
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO: Hello! Wanna know fleet's speed? Good. Implement & test this func first.
|
|
||||||
// func (g Game) FleetSpeed(fl Fleet) float64 {
|
|
||||||
// result := math.MaxFloat64
|
|
||||||
// for sg := range g.ShipGroups {
|
|
||||||
// if g.ShipGroups[sg].FleetID == nil || *g.ShipGroups[sg].FleetID != fl.ID {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// st := g.mustShipType(g.ShipGroups[sg].TypeID)
|
|
||||||
// typeSpeed := g.ShipGroups[sg].Speed(st)
|
|
||||||
// if typeSpeed < result {
|
|
||||||
// result = typeSpeed
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return result
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) JoinShipGroupToFleet(raceName, fleetName string, group, count uint) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.joinShipGroupToFleetInternal(ri, fleetName, group, count)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) JoinFleets(raceName, fleetSourceName, fleetTargetName string) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.joinFleetsInternal(ri, fleetSourceName, fleetTargetName)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) joinShipGroupToFleetInternal(ri int, fleetName string, groupIndex, quantity uint) (err error) {
|
|
||||||
// name, ok := validateTypeName(fleetName)
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewEntityTypeNameValidationError("%q", name)
|
|
||||||
// }
|
|
||||||
// sgi := -1
|
|
||||||
// var maxIndex uint
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].State() != StateInOrbit {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].Number < quantity {
|
|
||||||
// return e.NewJoinFleetGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fi := g.fleetIndex(ri, name)
|
|
||||||
// if fi < 0 {
|
|
||||||
// fi, err = g.createFleet(ri, name)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// state, onPlanet, _ := FleetState(g, g.Fleets[fi].ID)
|
|
||||||
// if state != StateInOrbit || *onPlanet != g.ShipGroups[sgi].Destination {
|
|
||||||
// return e.NewShipsNotOnSamePlanetError("fleet: %s", fleetName)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // FIXME: if g.ShipGroups[sgi].FleetID != nil { // delete old fleet if empty, ALSO mind breaking group }
|
|
||||||
// if quantity > 0 && quantity < g.ShipGroups[sgi].Number {
|
|
||||||
// // nsgi, err := g.breakGroupSafe(ri, groupIndex, quantity)
|
|
||||||
// // if err != nil {
|
|
||||||
// // return err
|
|
||||||
// // }
|
|
||||||
// // sgi = nsgi
|
|
||||||
// newGroup := g.ShipGroups[sgi]
|
|
||||||
// newGroup.Number -= quantity
|
|
||||||
// g.ShipGroups[sgi].Number = quantity
|
|
||||||
// newGroup.Index = maxIndex + 1
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, newGroup)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// g.ShipGroups[sgi].FleetID = &g.Fleets[fi].ID
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) createFleet(ri int, name string) (int, error) {
|
|
||||||
// n, ok := validateTypeName(name)
|
|
||||||
// if !ok {
|
|
||||||
// return 0, e.NewEntityTypeNameValidationError("%q", n)
|
|
||||||
// }
|
|
||||||
// if fl := g.fleetIndex(ri, n); fl >= 0 {
|
|
||||||
// return 0, e.NewEntityTypeNameDuplicateError("fleet %w", g.Fleets[fl].Name)
|
|
||||||
// }
|
|
||||||
// fleets := slices.Clone(g.Fleets)
|
|
||||||
// fleets = append(fleets, Fleet{
|
|
||||||
// ID: uuid.New(),
|
|
||||||
// OwnerID: g.Race[ri].ID,
|
|
||||||
// Name: n,
|
|
||||||
// })
|
|
||||||
// g.Fleets = fleets
|
|
||||||
// return len(g.Fleets) - 1, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) joinFleetsInternal(ri int, fleetSourceName, fleetTargetName string) (err error) {
|
|
||||||
// fiSource := g.fleetIndex(ri, fleetSourceName)
|
|
||||||
// if fiSource < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("source fleet %s", fleetSourceName)
|
|
||||||
// }
|
|
||||||
// fiTarget := g.fleetIndex(ri, fleetTargetName)
|
|
||||||
// if fiTarget < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("target fleet %s", fleetTargetName)
|
|
||||||
// }
|
|
||||||
// srcState, planet1, _ := FleetState(g, g.Fleets[fiSource].ID)
|
|
||||||
// tgtState, planet2, _ := FleetState(g, g.Fleets[fiTarget].ID)
|
|
||||||
// if srcState != StateInOrbit || srcState != tgtState || *planet1 != *planet2 {
|
|
||||||
// return e.NewShipsNotOnSamePlanetError()
|
|
||||||
// }
|
|
||||||
// for sgi, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sg.FleetID != nil && *sg.FleetID == g.Fleets[fiSource].ID {
|
|
||||||
// g.ShipGroups[sgi].FleetID = &g.Fleets[fiTarget].ID
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return g.deleteFleetSafe(ri, fleetSourceName)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) deleteFleetSafe(ri int, name string) error {
|
|
||||||
// fi := g.fleetIndex(ri, name)
|
|
||||||
// if fi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("fleet %s", name)
|
|
||||||
// }
|
|
||||||
// for sgi := range g.ShipGroups {
|
|
||||||
// if g.ShipGroups[sgi].FleetID != nil && *g.ShipGroups[sgi].FleetID == g.Fleets[fi].ID {
|
|
||||||
// return e.NewEntityInUseError("fleet %s: race %s, group #%d", name, g.Race[ri].Name, g.ShipGroups[sgi].Number)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// g.Fleets = append(g.Fleets[:fi], g.Fleets[fi+1:]...)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) fleetIndex(ri int, name string) int {
|
|
||||||
// return slices.IndexFunc(g.Fleets, func(f Fleet) bool { return f.OwnerID == g.Race[ri].ID && f.Name == name })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) listFleets(ri int) iter.Seq[Fleet] {
|
|
||||||
// return func(yield func(Fleet) bool) {
|
|
||||||
// for _, fl := range g.listIndexFleets(ri) {
|
|
||||||
// if !yield(fl) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) listIndexFleets(ri int) iter.Seq2[int, Fleet] {
|
|
||||||
// return func(yield func(int, Fleet) bool) {
|
|
||||||
// for i := range g.Fleets {
|
|
||||||
// if g.Fleets[i].OwnerID == g.Race[ri].ID {
|
|
||||||
// if !yield(i, g.Fleets[i]) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func FleetGroups(g *Game, ri, fi int) iter.Seq[ShipGroup] {
|
|
||||||
// if len(g.Fleets) < fi+1 {
|
|
||||||
// panic(fmt.Sprintf("FleetGroups: game fleets index %d invalid: len=%d", fi, len(g.Fleets)))
|
|
||||||
// }
|
|
||||||
// return func(yield func(ShipGroup) bool) {
|
|
||||||
// for sg := range g.listShipGroups(ri) {
|
|
||||||
// if sg.FleetID != nil && *sg.FleetID == g.Fleets[fi].ID {
|
|
||||||
// if !yield(sg) {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -3,13 +3,9 @@ package game
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"iter"
|
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TechSet map[Tech]float64
|
type TechSet map[Tech]float64
|
||||||
@@ -48,46 +44,6 @@ func (g Game) Votes(raceID uuid.UUID) float64 {
|
|||||||
return pop / 1000.
|
return pop / 1000.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) PlanetByNumber(number uint) (Planet, error) {
|
|
||||||
pi := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == number })
|
|
||||||
if pi < 0 {
|
|
||||||
return Planet{}, e.NewGameStateError("PlanetByNumber: planet with number=%d not found", number)
|
|
||||||
}
|
|
||||||
return g.Map.Planet[pi], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) ShipsInUpgrade(planetNumber uint) iter.Seq[ShipGroup] {
|
|
||||||
return func(yield func(ShipGroup) bool) {
|
|
||||||
for sg := range g.ShipGroups {
|
|
||||||
if g.ShipGroups[sg].Destination == planetNumber && g.ShipGroups[sg].State() == StateUpgrade {
|
|
||||||
if !yield(g.ShipGroups[sg]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) raceIndex(name string) (int, error) {
|
|
||||||
i := slices.IndexFunc(g.Race, func(r Race) bool { return r.Name == name })
|
|
||||||
if i < 0 {
|
|
||||||
return i, e.NewRaceUnknownError(name)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// validateTypeName always return v without leading and trailing spaces
|
|
||||||
func validateTypeName(v string) (string, bool) {
|
|
||||||
s := strings.TrimSpace(v)
|
|
||||||
if len(s) > 0 {
|
|
||||||
return s, true
|
|
||||||
}
|
|
||||||
// TODO: special symbols AND include error check in all user-input test
|
|
||||||
return s, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) MarshalBinary() (data []byte, err error) {
|
func (g Game) MarshalBinary() (data []byte, err error) {
|
||||||
return json.Marshal(&g)
|
return json.Marshal(&g)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import "iter"
|
// import "iter"
|
||||||
|
|
||||||
func (g *Game) CreateShips(ri int, shipTypeName string, planetNumber uint, quantity int) error {
|
// func (g *Game) CreateShips(ri int, shipTypeName string, planetNumber uint, quantity int) error {
|
||||||
return nil // g.createShips(ri, shipTypeName, int(planetNumber), quantity)
|
// return nil // g.createShips(ri, shipTypeName, int(planetNumber), quantity)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (g Game) ListShipGroups(ri int) iter.Seq[ShipGroup] {
|
// func (g Game) ListShipGroups(ri int) iter.Seq[ShipGroup] {
|
||||||
return func(yield func(ShipGroup) bool) {}
|
// return func(yield func(ShipGroup) bool) {}
|
||||||
// return g.listShipGroups(ri)
|
// // return g.listShipGroups(ri)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (g Game) ListFleets(ri int) iter.Seq[Fleet] {
|
// func (g Game) ListFleets(ri int) iter.Seq[Fleet] {
|
||||||
return func(yield func(Fleet) bool) {}
|
// return func(yield func(Fleet) bool) {}
|
||||||
// return g.listFleets(ri)
|
// // return g.listFleets(ri)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (g Game) MustPlanetByNumber(num uint) Planet {
|
// func (g Game) MustPlanetByNumber(num uint) Planet {
|
||||||
p, err := g.PlanetByNumber(num)
|
// p, err := g.PlanetByNumber(num)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
panic(err)
|
// panic(err)
|
||||||
}
|
// }
|
||||||
return p
|
// return p
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -1,101 +1,54 @@
|
|||||||
package game_test
|
package game_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/iliadenisov/galaxy/internal/controller"
|
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func TestTechSet(t *testing.T) {
|
||||||
Race_0 = game.Race{
|
s := ts.Set(game.TechDrive, 10.5)
|
||||||
ID: Race_0_ID,
|
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||||
Vote: Race_0_ID,
|
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||||
Name: "Race_0",
|
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||||
Tech: map[game.Tech]float64{
|
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||||
game.TechDrive: 1.1,
|
|
||||||
game.TechWeapons: 1.2,
|
|
||||||
game.TechShields: 1.3,
|
|
||||||
game.TechCargo: 1.4,
|
|
||||||
},
|
|
||||||
Relations: []game.RaceRelation{{RaceID: Race_1_ID, Relation: game.RelationWar}},
|
|
||||||
}
|
|
||||||
Race_1 = game.Race{
|
|
||||||
ID: Race_1_ID,
|
|
||||||
Vote: Race_1_ID,
|
|
||||||
Name: "Race_1",
|
|
||||||
Tech: map[game.Tech]float64{
|
|
||||||
game.TechDrive: 2.1,
|
|
||||||
game.TechWeapons: 2.2,
|
|
||||||
game.TechShields: 2.3,
|
|
||||||
game.TechCargo: 2.4,
|
|
||||||
},
|
|
||||||
Relations: []game.RaceRelation{{RaceID: Race_0_ID, Relation: game.RelationPeace}},
|
|
||||||
}
|
|
||||||
|
|
||||||
Race_0_ID = uuid.New()
|
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||||
Race_0_idx = 0
|
assert.Equal(t, 1.2, s.Value(game.TechWeapons))
|
||||||
Race_0_Gunship = "R0_Gunship"
|
assert.Equal(t, 1.3, s.Value(game.TechShields))
|
||||||
Race_0_Freighter = "R0_Freighter"
|
assert.Equal(t, 1.4, s.Value(game.TechCargo))
|
||||||
R0_Planet_0_num uint = 0
|
|
||||||
R0_Planet_2_num uint = 2
|
|
||||||
Race_0_Gunship_idx = 0
|
|
||||||
Race_0_Freighter_idx = 1
|
|
||||||
Race_0_Cruiser_idx = 2
|
|
||||||
|
|
||||||
Race_1_ID = uuid.New()
|
s = s.Set(game.TechWeapons, 5.7)
|
||||||
Race_1_idx = 1
|
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||||
Race_1_Gunship = "R1_Gunship"
|
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||||
Race_1_Freighter = "R1_Freighter"
|
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||||
R1_Planet_1_num uint = 1
|
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||||
Race_1_Gunship_idx = 0
|
|
||||||
Race_1_Freighter_idx = 1
|
|
||||||
Race_1_Cruiser_idx = 2
|
|
||||||
|
|
||||||
ShipType_Cruiser = "Cruiser"
|
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||||
|
assert.Equal(t, 5.7, s.Value(game.TechWeapons))
|
||||||
|
assert.Equal(t, 1.3, s.Value(game.TechShields))
|
||||||
|
assert.Equal(t, 1.4, s.Value(game.TechCargo))
|
||||||
|
|
||||||
Cruiser = game.ShipType{
|
s = s.Set(game.TechShields, 2.13)
|
||||||
ShipTypeReport: game.ShipTypeReport{
|
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||||
Name: "Cruiser",
|
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||||
Drive: 15,
|
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||||
Armament: 1,
|
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||||
Weapons: 15,
|
|
||||||
Shields: 15,
|
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||||
Cargo: 0,
|
assert.Equal(t, 5.7, s.Value(game.TechWeapons))
|
||||||
},
|
assert.Equal(t, 2.13, s.Value(game.TechShields))
|
||||||
}
|
assert.Equal(t, 1.4, s.Value(game.TechCargo))
|
||||||
)
|
|
||||||
|
s = s.Set(game.TechCargo, 3.1415926)
|
||||||
func assertNoError(err error) {
|
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||||
if err != nil {
|
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||||
panic(fmt.Sprintf("init assertion failed: %v", err))
|
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||||
}
|
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||||
}
|
|
||||||
|
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||||
func newGame() *game.Game {
|
assert.Equal(t, 5.7, s.Value(game.TechWeapons))
|
||||||
g := &game.Game{
|
assert.Equal(t, 2.13, s.Value(game.TechShields))
|
||||||
Race: []game.Race{
|
assert.Equal(t, 3.1415926, s.Value(game.TechCargo))
|
||||||
Race_0,
|
|
||||||
Race_1,
|
|
||||||
},
|
|
||||||
Map: game.Map{
|
|
||||||
Width: 1000,
|
|
||||||
Height: 1000,
|
|
||||||
Planet: []game.Planet{
|
|
||||||
controller.NewPlanet(R0_Planet_0_num, "Planet_0", Race_0.ID, 0, 0, 100, 100, 100, 0, game.ProductionNone.AsType(uuid.Nil)),
|
|
||||||
controller.NewPlanet(R1_Planet_1_num, "Planet_1", Race_1.ID, 1, 1, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
|
||||||
controller.NewPlanet(R0_Planet_2_num, "Planet_2", Race_0.ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
|
||||||
controller.NewPlanet(3, "Planet_3", uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// assertNoError(g.CreateShipType(Race_0.Name, Race_0_Gunship, 60, 30, 100, 0, 3))
|
|
||||||
// assertNoError(g.CreateShipType(Race_0.Name, Race_0_Freighter, 8, 0, 2, 10, 0))
|
|
||||||
// assertNoError(g.CreateShipType(Race_0.Name, ShipType_Cruiser, Cruiser.Drive, Cruiser.Weapons, Cruiser.Shields, Cruiser.Cargo, int(Cruiser.Armament)))
|
|
||||||
|
|
||||||
// assertNoError(g.CreateShipType(Race_1.Name, Race_1_Gunship, 60, 30, 100, 0, 3))
|
|
||||||
// assertNoError(g.CreateShipType(Race_1.Name, Race_1_Freighter, 8, 0, 2, 10, 0))
|
|
||||||
// assertNoError(g.CreateShipType(Race_1.Name, ShipType_Cruiser, 15, 15, 15, 0, 2)) // same name - different type (why.)
|
|
||||||
return g
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,517 +229,3 @@ func (sg ShipGroup) BombingPower(st *ShipType) float64 {
|
|||||||
float64(sg.Number)
|
float64(sg.Number)
|
||||||
return number.Fixed3(result)
|
return number.Fixed3(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinEqualGroups iterates over all races and joins their respective equal ship groups.
|
|
||||||
// Used in turn production.
|
|
||||||
// func JoinEqualGroups(g *Game) {
|
|
||||||
// // for i := range g.Race {
|
|
||||||
// // g.joinEqualGroupsInternal(i)
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) JoinEqualGroups(raceName string) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// g.joinEqualGroupsInternal(ri)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) joinEqualGroupsInternal(ri int) {
|
|
||||||
// shipGroups := slices.Collect(maps.Values(maps.Collect(g.listIndexShipGroups(ri))))
|
|
||||||
// origin := len(shipGroups)
|
|
||||||
// if origin < 2 {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// for i := 0; i < len(shipGroups)-1; i++ {
|
|
||||||
// for j := len(shipGroups) - 1; j > i; j-- {
|
|
||||||
// if shipGroups[i].Equal(shipGroups[j]) {
|
|
||||||
// shipGroups[i].Index = maxUint(shipGroups[i].Index, shipGroups[j].Index)
|
|
||||||
// shipGroups[i].Number += shipGroups[j].Number
|
|
||||||
// shipGroups = append(shipGroups[:j], shipGroups[j+1:]...)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if len(shipGroups) == origin {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// g.ShipGroups = slices.DeleteFunc(g.ShipGroups, func(v ShipGroup) bool { return v.OwnerID == g.Race[ri].ID })
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, shipGroups...)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) BreakGroup(raceName string, groupIndex, quantity uint) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.breakGroupInternal(ri, groupIndex, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) DisassembleGroup(raceName string, groupIndex, quantity uint) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.disassembleGroupInternal(ri, groupIndex, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) disassembleGroupInternal(ri int, groupIndex, quantity uint) error {
|
|
||||||
// sgi := -1
|
|
||||||
// var maxIndex uint
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].State() != StateInOrbit {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].Number < quantity {
|
|
||||||
// return e.NewBeakGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == g.ShipGroups[sgi].Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", g.ShipGroups[sgi].Destination)
|
|
||||||
// }
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if quantity > 0 && quantity < g.ShipGroups[sgi].Number {
|
|
||||||
// // make new group for disassembly
|
|
||||||
// nsgi, err := g.breakGroupSafe(ri, groupIndex, quantity)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// sgi = nsgi
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].CargoType != nil {
|
|
||||||
// ct := *g.ShipGroups[sgi].CargoType
|
|
||||||
// load := g.ShipGroups[sgi].Load
|
|
||||||
// switch ct {
|
|
||||||
// case CargoColonist:
|
|
||||||
// if g.Map.Planet[pl].Owner == g.Race[ri].ID {
|
|
||||||
// g.Map.Planet[pl] = UnloadColonists(g.Map.Planet[pl], load)
|
|
||||||
// }
|
|
||||||
// case CargoMaterial:
|
|
||||||
// g.Map.Planet[pl].Material += load
|
|
||||||
// case CargoCapital:
|
|
||||||
// g.Map.Planet[pl].Capital += load
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// g.Map.Planet[pl].Material += g.ShipGroups[sgi].EmptyMass(&g.Race[ri].ShipTypes[sti])
|
|
||||||
|
|
||||||
// g.ShipGroups = append(g.ShipGroups[:sgi], g.ShipGroups[sgi+1:]...)
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) LoadCargo(raceName string, groupIndex uint, cargoType string, ships uint, quantity float64) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// ct, ok := CargoTypeSet[cargoType]
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewCargoTypeInvalidError(cargoType)
|
|
||||||
// }
|
|
||||||
// return g.loadCargoInternal(ri, groupIndex, ct, ships, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) UnloadCargo(raceName string, groupIndex uint, ships uint, quantity float64) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.unloadCargoInternal(ri, groupIndex, ships, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Промышленность и Сырье могут быть выгружены на любой планете.
|
|
||||||
// // Колонисты могут быть высажены только на планеты, принадлежащие Вам или на необитаемые планеты.
|
|
||||||
// func (g *Game) unloadCargoInternal(ri int, groupIndex uint, ships uint, quantity float64) error {
|
|
||||||
// if ships == 0 && quantity > 0 {
|
|
||||||
// return e.NewCargoQuantityWithoutGroupBreakError()
|
|
||||||
// }
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
// if g.ShipGroups[sgi].State() != StateInOrbit {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
// if g.Race[ri].ShipTypes[sti].Cargo < 1 {
|
|
||||||
// return e.NewNoCargoBayError("ship_type %q", g.Race[ri].ShipTypes[sti].Name)
|
|
||||||
// }
|
|
||||||
// if g.ShipGroups[sgi].CargoType == nil || g.ShipGroups[sgi].Load == 0 {
|
|
||||||
// return e.NewCargoUnloadEmptyError()
|
|
||||||
// }
|
|
||||||
// ct := *g.ShipGroups[sgi].CargoType
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == g.ShipGroups[sgi].Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", g.ShipGroups[sgi].Destination)
|
|
||||||
// }
|
|
||||||
// if ct == CargoColonist {
|
|
||||||
// if g.Map.Planet[pl].Owner != uuid.Nil && g.Map.Planet[pl].Owner != g.Race[ri].ID {
|
|
||||||
// return e.NewEntityNotOwnedError("planet #%d unload %v", g.Map.Planet[pl].Number, ct)
|
|
||||||
// }
|
|
||||||
// if g.Map.Planet[pl].Owner == uuid.Nil {
|
|
||||||
// g.Map.Planet[pl].Owner = g.Race[ri].ID
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// var availableOnPlanet *float64
|
|
||||||
// switch ct {
|
|
||||||
// case CargoMaterial:
|
|
||||||
// availableOnPlanet = &g.Map.Planet[pl].Material
|
|
||||||
// case CargoCapital:
|
|
||||||
// availableOnPlanet = &g.Map.Planet[pl].Capital
|
|
||||||
// case CargoColonist:
|
|
||||||
// availableOnPlanet = &g.Map.Planet[pl].Colonists
|
|
||||||
// default:
|
|
||||||
// return e.NewGameStateError("CargoType not accepted: %v", ct)
|
|
||||||
// }
|
|
||||||
// if ships > 0 && ships < g.ShipGroups[sgi].Number {
|
|
||||||
// nsgi, err := g.breakGroupSafe(ri, groupIndex, ships)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// sgi = nsgi
|
|
||||||
// }
|
|
||||||
// toBeUnloaded := quantity
|
|
||||||
// if quantity == 0 {
|
|
||||||
// toBeUnloaded = g.ShipGroups[sgi].Load
|
|
||||||
// }
|
|
||||||
// if toBeUnloaded > g.ShipGroups[sgi].Load {
|
|
||||||
// return e.NewCargoUnoadNotEnoughError("load: %.03f", g.ShipGroups[sgi].Load)
|
|
||||||
// }
|
|
||||||
// *availableOnPlanet += toBeUnloaded
|
|
||||||
// g.ShipGroups[sgi].Load -= toBeUnloaded
|
|
||||||
// if g.ShipGroups[sgi].Load == 0 {
|
|
||||||
// g.ShipGroups[sgi].CargoType = nil
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Корабль может нести только один тип груза одновременно.
|
|
||||||
// // Возможные типы груза - это колонисты, сырье и промышленность.
|
|
||||||
// // Груз может быть доставлен на борт корабля с Вашей или не занятой планеты, на которой он имеется.
|
|
||||||
// func (g *Game) loadCargoInternal(ri int, groupIndex uint, ct CargoType, ships uint, quantity float64) error {
|
|
||||||
// if ships == 0 && quantity > 0 {
|
|
||||||
// return e.NewCargoQuantityWithoutGroupBreakError()
|
|
||||||
// }
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
// if g.ShipGroups[sgi].State() != StateInOrbit {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == g.ShipGroups[sgi].Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", g.ShipGroups[sgi].Destination)
|
|
||||||
// }
|
|
||||||
// if g.Map.Planet[pl].Owner != uuid.Nil && g.Map.Planet[pl].Owner != g.Race[ri].ID {
|
|
||||||
// return e.NewEntityNotOwnedError("planet #%d", g.Map.Planet[pl].Number)
|
|
||||||
// }
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
// if g.Race[ri].ShipTypes[sti].Cargo < 1 {
|
|
||||||
// return e.NewNoCargoBayError("ship_type %q", g.Race[ri].ShipTypes[sti].Name)
|
|
||||||
// }
|
|
||||||
// if g.ShipGroups[sgi].CargoType != nil && *g.ShipGroups[sgi].CargoType != ct {
|
|
||||||
// return e.NewCargoLoadNotEqualError("cargo: %v", *g.ShipGroups[sgi].CargoType)
|
|
||||||
// }
|
|
||||||
// if ships > 0 && ships < g.ShipGroups[sgi].Number {
|
|
||||||
// nsgi, err := g.breakGroupSafe(ri, groupIndex, ships)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// sgi = nsgi
|
|
||||||
// }
|
|
||||||
// capacity := g.ShipGroups[sgi].CargoCapacity(&g.Race[ri].ShipTypes[sti])
|
|
||||||
// freeShipGroupCargoLoad := capacity - g.ShipGroups[sgi].Load
|
|
||||||
// if freeShipGroupCargoLoad == 0 {
|
|
||||||
// return e.NewCargoLoadNoSpaceLeftError()
|
|
||||||
// }
|
|
||||||
// var availableOnPlanet *float64
|
|
||||||
// switch ct {
|
|
||||||
// case CargoMaterial:
|
|
||||||
// availableOnPlanet = &g.Map.Planet[pl].Material
|
|
||||||
// case CargoCapital:
|
|
||||||
// availableOnPlanet = &g.Map.Planet[pl].Capital
|
|
||||||
// case CargoColonist:
|
|
||||||
// availableOnPlanet = &g.Map.Planet[pl].Colonists
|
|
||||||
// default:
|
|
||||||
// return e.NewGameStateError("CargoType not accepted: %v", ct)
|
|
||||||
// }
|
|
||||||
// if quantity > *availableOnPlanet || *availableOnPlanet == 0 {
|
|
||||||
// return e.NewCargoLoadNotEnoughError("planet: #%d, %s=%.03f", g.Map.Planet[pl].Number, ct, *availableOnPlanet)
|
|
||||||
// }
|
|
||||||
// toBeLoaded := quantity
|
|
||||||
// if quantity == 0 {
|
|
||||||
// toBeLoaded = *availableOnPlanet
|
|
||||||
// }
|
|
||||||
// if toBeLoaded > freeShipGroupCargoLoad {
|
|
||||||
// toBeLoaded = freeShipGroupCargoLoad
|
|
||||||
// }
|
|
||||||
// *availableOnPlanet = *availableOnPlanet - toBeLoaded
|
|
||||||
// g.ShipGroups[sgi].Load += toBeLoaded
|
|
||||||
// if g.ShipGroups[sgi].Load > 0 {
|
|
||||||
// g.ShipGroups[sgi].CargoType = &ct
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) GiveawayGroup(raceName, raceAcceptor string, groupIndex, quantity uint) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// riAccept, err := g.raceIndex(raceAcceptor)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.giveawayGroupInternal(ri, riAccept, groupIndex, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) giveawayGroupInternal(ri, riAccept int, groupIndex, quantity uint) (err error) {
|
|
||||||
// if ri == riAccept {
|
|
||||||
// return e.NewSameRaceError(g.Race[riAccept].Name)
|
|
||||||
// }
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
// if g.ShipGroups[sgi].Number < quantity {
|
|
||||||
// return e.NewBeakGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var stAcc int
|
|
||||||
// if stAcc = slices.IndexFunc(g.Race[riAccept].ShipTypes, func(st ShipType) bool { return st.Name == g.Race[ri].ShipTypes[sti].Name }); stAcc >= 0 &&
|
|
||||||
// !g.Race[ri].ShipTypes[sti].Equal(g.Race[riAccept].ShipTypes[stAcc]) {
|
|
||||||
// return e.NewGiveawayGroupShipsTypeNotEqualError("race %w, ship type %w", g.Race[riAccept].Name, g.Race[riAccept].ShipTypes[stAcc].Name)
|
|
||||||
// }
|
|
||||||
// if stAcc < 0 {
|
|
||||||
// stAcc, err = g.createShipTypeInternal(riAccept,
|
|
||||||
// g.Race[ri].ShipTypes[sti].Name,
|
|
||||||
// g.Race[ri].ShipTypes[sti].Drive,
|
|
||||||
// g.Race[ri].ShipTypes[sti].Weapons,
|
|
||||||
// g.Race[ri].ShipTypes[sti].Shields,
|
|
||||||
// g.Race[ri].ShipTypes[sti].Cargo,
|
|
||||||
// int(g.Race[ri].ShipTypes[sti].Armament))
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var maxIndex uint
|
|
||||||
// for sg := range g.listShipGroups(riAccept) {
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, ShipGroup{
|
|
||||||
// Index: maxIndex + 1,
|
|
||||||
// OwnerID: g.Race[riAccept].ID,
|
|
||||||
// TypeID: g.Race[riAccept].ShipTypes[stAcc].ID,
|
|
||||||
// Number: uint(quantity),
|
|
||||||
|
|
||||||
// CargoType: g.ShipGroups[sgi].CargoType,
|
|
||||||
// Load: g.ShipGroups[sgi].Load,
|
|
||||||
|
|
||||||
// Tech: maps.Clone(g.ShipGroups[sgi].Tech),
|
|
||||||
|
|
||||||
// Destination: g.ShipGroups[sgi].Destination,
|
|
||||||
// StateInSpace: g.ShipGroups[sgi].StateInSpace,
|
|
||||||
// StateUpgrade: g.ShipGroups[sgi].StateUpgrade,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// if quantity == 0 || quantity == g.ShipGroups[sgi].Number {
|
|
||||||
// g.ShipGroups = append(g.ShipGroups[:sgi], g.ShipGroups[sgi+1:]...)
|
|
||||||
// } else {
|
|
||||||
// g.ShipGroups[sgi].Number -= quantity
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) breakGroupInternal(ri int, groupIndex, quantity uint) error {
|
|
||||||
// sgi := -1
|
|
||||||
// var maxIndex uint
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].State() != StateInOrbit {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].Number < quantity {
|
|
||||||
// return e.NewBeakGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if quantity == 0 || quantity == g.ShipGroups[sgi].Number {
|
|
||||||
// g.ShipGroups[sgi].FleetID = nil
|
|
||||||
// } else {
|
|
||||||
// if _, err := g.breakGroupSafe(ri, groupIndex, quantity); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) breakGroupSafe(ri int, groupIndex uint, newGroupShips uint) (int, error) {
|
|
||||||
// sgi := -1
|
|
||||||
// var maxIndex uint
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return -1, e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
// if g.ShipGroups[sgi].Number < newGroupShips {
|
|
||||||
// return -1, e.NewBreakGroupIllegalNumberError("group #%d ships: %d -> %d", g.ShipGroups[sgi].Index, g.ShipGroups[sgi].Number, newGroupShips)
|
|
||||||
// }
|
|
||||||
// newGroup := g.ShipGroups[sgi]
|
|
||||||
// if g.ShipGroups[sgi].CargoType != nil {
|
|
||||||
// newGroup.Load = g.ShipGroups[sgi].Load / float64(g.ShipGroups[sgi].Number) * float64(newGroupShips)
|
|
||||||
// g.ShipGroups[sgi].Load -= newGroup.Load
|
|
||||||
// }
|
|
||||||
// newGroup.Number = newGroupShips
|
|
||||||
// g.ShipGroups[sgi].Number -= newGroup.Number
|
|
||||||
// newGroup.Index = maxIndex + 1
|
|
||||||
// newGroup.FleetID = nil
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, newGroup)
|
|
||||||
// return len(g.ShipGroups) - 1, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) createShips(ri int, shipTypeName string, planetNumber int, quantity int) error {
|
|
||||||
// st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.Name == shipTypeName })
|
|
||||||
// if st < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("ship type %w", shipTypeName)
|
|
||||||
// }
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == uint(planetNumber) })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("planet #%d", planetNumber)
|
|
||||||
// }
|
|
||||||
// if g.Map.Planet[pl].Owner != g.Race[ri].ID {
|
|
||||||
// return e.NewEntityNotOwnedError("planet #%d", planetNumber)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var maxIndex uint
|
|
||||||
// for _, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sg.Index > maxIndex {
|
|
||||||
// maxIndex = sg.Index
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// g.ShipGroups = append(g.ShipGroups, ShipGroup{
|
|
||||||
// Index: maxIndex + 1,
|
|
||||||
// OwnerID: g.Race[ri].ID,
|
|
||||||
// TypeID: g.Race[ri].ShipTypes[st].ID,
|
|
||||||
// Destination: g.Map.Planet[pl].Number,
|
|
||||||
// Number: uint(quantity),
|
|
||||||
// Tech: map[Tech]float64{
|
|
||||||
// TechDrive: g.Race[ri].TechLevel(TechDrive),
|
|
||||||
// TechWeapons: g.Race[ri].TechLevel(TechWeapons),
|
|
||||||
// TechShields: g.Race[ri].TechLevel(TechShields),
|
|
||||||
// TechCargo: g.Race[ri].TechLevel(TechCargo),
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) listShipGroups(ri int) iter.Seq[ShipGroup] {
|
|
||||||
// return func(yield func(ShipGroup) bool) {
|
|
||||||
// for _, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if !yield(sg) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) listIndexShipGroups(ri int) iter.Seq2[int, ShipGroup] {
|
|
||||||
// return func(yield func(int, ShipGroup) bool) {
|
|
||||||
// for i := range g.ShipGroups {
|
|
||||||
// if g.ShipGroups[i].OwnerID == g.Race[ri].ID {
|
|
||||||
// if !yield(i, g.ShipGroups[i]) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func MustShipGroup(g *Game, ri int, index uint) ShipGroup {
|
|
||||||
// for sg := range g.listShipGroups(ri) {
|
|
||||||
// if sg.Index == index {
|
|
||||||
// return sg
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// panic(fmt.Sprintf("race i=%d have no group i=%d", ri, index))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func maxUint(a, b uint) uint {
|
|
||||||
// if b > a {
|
|
||||||
// return b
|
|
||||||
// }
|
|
||||||
// return a
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
package game
|
|
||||||
|
|
||||||
// import (
|
|
||||||
// "slices"
|
|
||||||
|
|
||||||
// e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
// "github.com/iliadenisov/galaxy/internal/util"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// func (g *Game) SendGroup(raceName string, groupIndex, planetNumber, quantity uint) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.sendGroupInternal(ri, groupIndex, planetNumber, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) sendGroupInternal(ri int, groupIndex, planetNumber, quantity uint) error {
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
// st := g.Race[ri].ShipTypes[sti]
|
|
||||||
|
|
||||||
// if st.DriveBlockMass() == 0 {
|
|
||||||
// return e.NewSendShipHasNoDrivesError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// sourcePlanet, ok := g.ShipGroups[sgi].OnPlanet()
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].Number < quantity {
|
|
||||||
// return e.NewBeakGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, quantity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// p1, ok := PlanetByNum(g, sourcePlanet)
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewGameStateError("source planet #%d does not exists", sourcePlanet)
|
|
||||||
// }
|
|
||||||
// p2, ok := PlanetByNum(g, planetNumber)
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewEntityNotExistsError("destination planet #%d", planetNumber)
|
|
||||||
// }
|
|
||||||
// rangeToDestination := util.ShortDistance(g.Map.Width, g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
|
||||||
// if rangeToDestination > g.Race[ri].FlightDistance() {
|
|
||||||
// return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if quantity > 0 && quantity < g.ShipGroups[sgi].Number {
|
|
||||||
// nsgi, err := g.breakGroupSafe(ri, groupIndex, quantity)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// sgi = nsgi
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if sourcePlanet == planetNumber {
|
|
||||||
// g.ShipGroups[sgi] = UnsendShips(g.ShipGroups[sgi])
|
|
||||||
// g.joinEqualGroupsInternal(ri)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// g.ShipGroups[sgi] = LaunchShips(g.ShipGroups[sgi], planetNumber)
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func LaunchShips(sg ShipGroup, destination uint) ShipGroup {
|
|
||||||
// sg.StateInSpace = &InSpace{
|
|
||||||
// Origin: sg.Destination,
|
|
||||||
// }
|
|
||||||
// sg.Destination = destination
|
|
||||||
// return sg
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func UnsendShips(sg ShipGroup) ShipGroup {
|
|
||||||
// sg.Destination = sg.StateInSpace.Origin
|
|
||||||
// sg.StateInSpace = nil
|
|
||||||
// return sg
|
|
||||||
// }
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package game_test
|
|
||||||
|
|
||||||
// import (
|
|
||||||
// "slices"
|
|
||||||
// "testing"
|
|
||||||
|
|
||||||
// e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
|
|
||||||
// "github.com/iliadenisov/galaxy/internal/model/game"
|
|
||||||
// "github.com/stretchr/testify/assert"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// func TestSendGroup(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
// // group #1 - in_orbit, free to upgrade
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 10))
|
|
||||||
// // group #2 - in_space
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1))
|
|
||||||
// g.ShipGroups[1].StateInSpace = &game.InSpace{Origin: 2, Range: 1.23}
|
|
||||||
// // group #3 - in_orbit, unmovable
|
|
||||||
// g.CreateShipType(Race_0.Name, "Fortress", 0, 50, 30, 100, 0)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, "Fortress", R0_Planet_0_num, 1))
|
|
||||||
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup("UnknownRace", 1, 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup(Race_0.Name, 555, 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup(Race_0.Name, 1, 222, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup(Race_0.Name, 2, 1, 0),
|
|
||||||
// e.GenericErrorText(e.ErrShipsBusy))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup(Race_0.Name, 3, 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrSendShipHasNoDrives))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup(Race_0.Name, 1, 2, 100),
|
|
||||||
// e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.SendGroup(Race_0.Name, 1, 3, 0),
|
|
||||||
// e.GenericErrorText(e.ErrSendUnreachableDestination))
|
|
||||||
|
|
||||||
// assert.NoError(t, g.SendGroup(Race_0.Name, 1, 2, 3)) // send 3 of 10
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, uint(7), g.ShipGroups[0].Number)
|
|
||||||
// assert.Equal(t, game.StateInOrbit, g.ShipGroups[0].State())
|
|
||||||
// assert.Equal(t, uint(3), g.ShipGroups[3].Number)
|
|
||||||
// assert.Equal(t, game.StateLaunched, g.ShipGroups[3].State())
|
|
||||||
|
|
||||||
// assert.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 2)) // un-send 2 of 3
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, uint(9), game.MustShipGroup(g, Race_0_idx, 5).Number)
|
|
||||||
// assert.Equal(t, game.StateInOrbit, game.MustShipGroup(g, Race_0_idx, 5).State())
|
|
||||||
// assert.Equal(t, uint(1), game.MustShipGroup(g, Race_0_idx, 4).Number)
|
|
||||||
// assert.Equal(t, game.StateLaunched, game.MustShipGroup(g, Race_0_idx, 4).State())
|
|
||||||
|
|
||||||
// assert.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 0)) // un-send the rest 1
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3)
|
|
||||||
// assert.Equal(t, uint(10), game.MustShipGroup(g, Race_0_idx, 5).Number)
|
|
||||||
// assert.Equal(t, game.StateInOrbit, game.MustShipGroup(g, Race_0_idx, 5).State())
|
|
||||||
|
|
||||||
// assert.NoError(t, g.SendGroup(Race_0.Name, 5, 2, 0))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3)
|
|
||||||
// assert.Equal(t, uint(10), game.MustShipGroup(g, Race_0_idx, 5).Number)
|
|
||||||
// assert.Equal(t, game.StateLaunched, game.MustShipGroup(g, Race_0_idx, 5).State())
|
|
||||||
// }
|
|
||||||
@@ -255,518 +255,3 @@ func TestShipGroupEqual(t *testing.T) {
|
|||||||
left.Load = right.Load / float64(right.Number) * float64(left.Number)
|
left.Load = right.Load / float64(right.Number) * float64(left.Number)
|
||||||
assert.True(t, left.Equal(right))
|
assert.True(t, left.Equal(right))
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestCreateShips(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.CreateShips(Race_0_idx, "Unknown_Ship_Type", R0_Planet_0_num, 2),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.CreateShips(Race_0_idx, Race_0_Gunship, R1_Planet_1_num, 2),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotOwned))
|
|
||||||
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 1)
|
|
||||||
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 1))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(1)), 1)
|
|
||||||
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 6))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 2)
|
|
||||||
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(1)), 2)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestJoinEqualGroups(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1)) // 1 -> 2
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 1))
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 6)) // (2)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 2)) // (3)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
|
|
||||||
|
|
||||||
// g.Race[Race_0_idx].SetTechLevel(game.TechDrive, 1.5)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 9)) // 4 -> 6
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) // 5 -> 7
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 4)) // (6)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 4)) // (7)
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
|
|
||||||
// g.Race[Race_1_idx].SetTechLevel(game.TechShields, 2.0)
|
|
||||||
// assert.NoError(t, g.CreateShips(1, Race_1_Freighter, R1_Planet_1_num, 1))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_1_idx)), 3)
|
|
||||||
|
|
||||||
// assert.NoError(t, g.JoinEqualGroups(Race_0.Name))
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_1_idx)), 3)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
|
|
||||||
// shipTypeID := func(ri int, name string) uuid.UUID {
|
|
||||||
// st := slices.IndexFunc(g.Race[ri].ShipTypes, func(v game.ShipType) bool { return v.Name == name })
|
|
||||||
// if st < 0 {
|
|
||||||
// t.Fatalf("ShipType not found: %s", name)
|
|
||||||
// return uuid.Nil
|
|
||||||
// }
|
|
||||||
// return g.Race[ri].ShipTypes[st].ID
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for sg := range g.ListShipGroups(Race_0_idx) {
|
|
||||||
// switch {
|
|
||||||
// case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.1:
|
|
||||||
// assert.Equal(t, uint(7), sg.Number)
|
|
||||||
// assert.Equal(t, uint(2), sg.Index)
|
|
||||||
// case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.5:
|
|
||||||
// assert.Equal(t, uint(11), sg.Number)
|
|
||||||
// assert.Equal(t, uint(7), sg.Index)
|
|
||||||
// case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.1:
|
|
||||||
// assert.Equal(t, uint(2), sg.Number)
|
|
||||||
// assert.Equal(t, uint(3), sg.Index)
|
|
||||||
// case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.5:
|
|
||||||
// assert.Equal(t, uint(13), sg.Number)
|
|
||||||
// assert.Equal(t, uint(6), sg.Index)
|
|
||||||
// default:
|
|
||||||
// t.Error("not all ship groups covered")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestBreakGroup(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 13)) // group #1 (0)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 7)) // group #2 (1) - In_Space
|
|
||||||
// g.ShipGroups[1].StateInSpace = &game.InSpace{
|
|
||||||
// Origin: 1,
|
|
||||||
// Range: 1,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fleet := "R0_Fleet"
|
|
||||||
// assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleet, 1, 0))
|
|
||||||
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.BreakGroup("UnknownRace", 1, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.BreakGroup(Race_0.Name, 555, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.BreakGroup(Race_0.Name, 1, 17),
|
|
||||||
// e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.BreakGroup(Race_0.Name, 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrShipsBusy))
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 2)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListFleets(Race_0_idx)), 1)
|
|
||||||
|
|
||||||
// // group #1 -> group #3 (5 new, 8 left)
|
|
||||||
// assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 5)) // group #3 (2)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3)
|
|
||||||
// assert.Equal(t, uint(8), g.ShipGroups[0].Number)
|
|
||||||
// assert.NotNil(t, g.ShipGroups[0].FleetID)
|
|
||||||
// assert.Equal(t, uint(5), g.ShipGroups[2].Number)
|
|
||||||
// assert.Equal(t, uint(3), g.ShipGroups[2].Index)
|
|
||||||
// assert.Nil(t, g.ShipGroups[2].FleetID)
|
|
||||||
// assert.Nil(t, g.ShipGroups[2].CargoType)
|
|
||||||
|
|
||||||
// // group #1 -> group #4 (2 new, 6 left)
|
|
||||||
// g.ShipGroups[0].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[0].Load = 32.8 // 8 ships
|
|
||||||
// assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 2)) // group #4 (3)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, uint(6), g.ShipGroups[0].Number)
|
|
||||||
// assert.NotNil(t, g.ShipGroups[0].FleetID)
|
|
||||||
// assert.Equal(t, uint(2), g.ShipGroups[3].Number)
|
|
||||||
// assert.Equal(t, uint(4), g.ShipGroups[3].Index)
|
|
||||||
// assert.Nil(t, g.ShipGroups[3].FleetID)
|
|
||||||
// assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleet, 4, 0))
|
|
||||||
// assert.NotNil(t, g.ShipGroups[3].FleetID)
|
|
||||||
|
|
||||||
// assert.Equal(t, game.CargoColonist.Ref(), g.ShipGroups[0].CargoType)
|
|
||||||
// assert.Equal(t, 24.6, number.Fixed3(g.ShipGroups[0].Load))
|
|
||||||
// assert.Equal(t, game.CargoColonist.Ref(), g.ShipGroups[3].CargoType)
|
|
||||||
// assert.Equal(t, 8.2, number.Fixed3(g.ShipGroups[3].Load))
|
|
||||||
|
|
||||||
// // group #1 -> MAX 6 off the fleet
|
|
||||||
// assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 6)) // group #1 (0)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, uint(6), g.ShipGroups[0].Number)
|
|
||||||
// assert.Nil(t, g.ShipGroups[0].FleetID)
|
|
||||||
|
|
||||||
// // group #4 -> ALL off the fleet
|
|
||||||
// assert.NoError(t, g.BreakGroup(Race_0.Name, 4, 0)) // group #1 (0)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, uint(2), g.ShipGroups[3].Number)
|
|
||||||
// assert.Nil(t, g.ShipGroups[3].FleetID)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestGiveawayGroup(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 11)) // group #1 (0)
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_1_idx, ShipType_Cruiser, R1_Planet_1_num, 23)) // group #1 (1)
|
|
||||||
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 17)) // group #2 (2) - In_Space
|
|
||||||
// assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, "R0_Fleet", 2, 0))
|
|
||||||
// assert.NotNil(t, g.ShipGroups[2].FleetID)
|
|
||||||
// g.ShipGroups[2].StateInSpace = &game.InSpace{
|
|
||||||
// Origin: 2,
|
|
||||||
// Range: 31.337,
|
|
||||||
// }
|
|
||||||
// g.ShipGroups[2].CargoType = game.CargoMaterial.Ref()
|
|
||||||
// g.ShipGroups[2].Load = 1.234
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 2)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_1_idx)), 1)
|
|
||||||
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.GiveawayGroup("UnknownRace", Race_1.Name, 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.GiveawayGroup(Race_0.Name, "UnknownRace", 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.GiveawayGroup(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),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.GiveawayGroup(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),
|
|
||||||
// e.GenericErrorText(e.ErrGiveawayGroupShipsTypeNotEqual))
|
|
||||||
|
|
||||||
// assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 2)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_1_idx)), 2)
|
|
||||||
// sto := slices.IndexFunc(g.Race[Race_0_idx].ShipTypes, func(st game.ShipType) bool { return st.Name == Race_0_Gunship })
|
|
||||||
// sti := slices.IndexFunc(g.Race[Race_1_idx].ShipTypes, func(st game.ShipType) bool { return st.Name == Race_0_Gunship })
|
|
||||||
// assert.Equal(t, g.Race[Race_1_idx].ShipTypes[sti].Name, g.Race[Race_0_idx].ShipTypes[sto].Name)
|
|
||||||
// assert.Equal(t, g.Race[Race_1_idx].ShipTypes[sti].Drive, g.Race[Race_0_idx].ShipTypes[sto].Drive)
|
|
||||||
// assert.Equal(t, g.Race[Race_1_idx].ShipTypes[sti].Weapons, g.Race[Race_0_idx].ShipTypes[sto].Weapons)
|
|
||||||
// assert.Equal(t, g.Race[Race_1_idx].ShipTypes[sti].Shields, g.Race[Race_0_idx].ShipTypes[sto].Shields)
|
|
||||||
// assert.Equal(t, g.Race[Race_1_idx].ShipTypes[sti].Cargo, g.Race[Race_0_idx].ShipTypes[sto].Cargo)
|
|
||||||
// assert.Equal(t, g.Race[Race_1_idx].ShipTypes[sti].Armament, g.Race[Race_0_idx].ShipTypes[sto].Armament)
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].State(), g.ShipGroups[3].State())
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].CargoType, g.ShipGroups[3].CargoType)
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].Load, g.ShipGroups[3].Load)
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].TechLevel(game.TechDrive), g.ShipGroups[3].TechLevel(game.TechDrive))
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].TechLevel(game.TechWeapons), g.ShipGroups[3].TechLevel(game.TechWeapons))
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].TechLevel(game.TechShields), g.ShipGroups[3].TechLevel(game.TechShields))
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].TechLevel(game.TechCargo), g.ShipGroups[3].TechLevel(game.TechCargo))
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].Destination, g.ShipGroups[3].Destination)
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].StateInSpace, g.ShipGroups[3].StateInSpace)
|
|
||||||
// assert.Equal(t, g.ShipGroups[2].StateUpgrade, g.ShipGroups[3].StateUpgrade)
|
|
||||||
// assert.Equal(t, g.ShipGroups[3].OwnerID, g.Race[Race_1_idx].ID)
|
|
||||||
// assert.Equal(t, g.ShipGroups[3].TypeID, g.Race[Race_1_idx].ShipTypes[sti].ID)
|
|
||||||
// assert.Equal(t, g.ShipGroups[3].Number, uint(11))
|
|
||||||
// assert.Nil(t, g.ShipGroups[3].FleetID)
|
|
||||||
|
|
||||||
// assert.NoError(t, g.GiveawayGroup(Race_1.Name, Race_0.Name, 2, 11))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_1_idx)), 1)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestLoadCargo(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
|
|
||||||
// // 1: idx = 0 / Ready to load
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
|
|
||||||
// // 2: idx = 1 / Has no cargo bay
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
|
|
||||||
|
|
||||||
// // 3: idx = 2 / In_Space
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7))
|
|
||||||
// g.ShipGroups[2].StateInSpace = &game.InSpace{
|
|
||||||
// Origin: 2,
|
|
||||||
// Range: 31.337,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 4: idx = 3 / loaded with COL
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[3].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[3].Load = 1.234
|
|
||||||
|
|
||||||
// // 5: idx = 4 / on foreign planet
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[4].Destination = R1_Planet_1_num
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 5)
|
|
||||||
|
|
||||||
// // tests
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo("UnknownRace", 1, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 1, "GOLD", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 555, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 3, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrShipsBusy))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 5, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotOwned))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 2, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputNoCargoBay))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 4, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoLoadNotEqual))
|
|
||||||
|
|
||||||
// // initial planet is empty
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoLoadNotEnough))
|
|
||||||
// // add cargo to planet
|
|
||||||
// g.Map.Planet[0].Material = 100
|
|
||||||
// // not enough on the planet
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 101),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoLoadNotEnough))
|
|
||||||
// // quantity > ships
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 1),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 5)
|
|
||||||
|
|
||||||
// // break group and load maximum
|
|
||||||
// assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 0))
|
|
||||||
// assert.Equal(t, 58.0, g.Map.Planet[0].Material)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 6)
|
|
||||||
// assert.Nil(t, g.ShipGroups[0].CargoType)
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[5].CargoType)
|
|
||||||
// assert.Equal(t, uint(9), g.ShipGroups[0].Number)
|
|
||||||
// assert.Equal(t, 0.0, g.ShipGroups[0].Load)
|
|
||||||
// assert.Equal(t, uint(2), g.ShipGroups[5].Number)
|
|
||||||
// assert.Equal(t, 42.0, g.ShipGroups[5].Load)
|
|
||||||
|
|
||||||
// // break group and load limited
|
|
||||||
// assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 18))
|
|
||||||
// assert.Equal(t, 40.0, g.Map.Planet[0].Material)
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
// assert.Nil(t, g.ShipGroups[0].CargoType)
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[6].CargoType)
|
|
||||||
// assert.Equal(t, uint(7), g.ShipGroups[0].Number)
|
|
||||||
// assert.Equal(t, 0.0, g.ShipGroups[0].Load)
|
|
||||||
// assert.Equal(t, uint(2), g.ShipGroups[6].Number)
|
|
||||||
// assert.Equal(t, 18.0, g.ShipGroups[6].Load)
|
|
||||||
|
|
||||||
// // add cargo to planet
|
|
||||||
// g.Map.Planet[0].Material = 100
|
|
||||||
// // loading all available cargo
|
|
||||||
// assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
// assert.Equal(t, 0.0, g.Map.Planet[0].Material)
|
|
||||||
// assert.Equal(t, 100.0, g.ShipGroups[0].Load) // free: 131.0
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[0].CargoType)
|
|
||||||
|
|
||||||
// // add cargo to planet
|
|
||||||
// g.Map.Planet[0].Material = 200
|
|
||||||
// assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 31))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
// assert.Equal(t, 169.0, g.Map.Planet[0].Material)
|
|
||||||
// assert.Equal(t, 131.0, g.ShipGroups[0].Load) // free: 100.0
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[0].CargoType)
|
|
||||||
|
|
||||||
// // load to maximum cargo space left
|
|
||||||
// assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 0))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
// assert.Equal(t, 153.0, g.Map.Planet[0].Material)
|
|
||||||
// assert.Equal(t, 147.0, g.ShipGroups[0].Load) // free: 0.0
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[0].CargoType)
|
|
||||||
|
|
||||||
// // ship group is full
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoLoadNoSpaceLeft))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestUnloadCargo(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
|
|
||||||
// // 1: idx = 0 / empty
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
|
||||||
|
|
||||||
// // 2: idx = 1 / Has no cargo bay
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
|
|
||||||
|
|
||||||
// // 3: idx = 2 / In_Space
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7))
|
|
||||||
// g.ShipGroups[2].StateInSpace = &game.InSpace{
|
|
||||||
// Origin: 2,
|
|
||||||
// Range: 31.337,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 4: idx = 3 / loaded with COL
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[3].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[3].Load = 1.234
|
|
||||||
|
|
||||||
// // 5: idx = 4 / on foreign planet / loaded with COL
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[4].Destination = R1_Planet_1_num
|
|
||||||
// g.ShipGroups[4].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[4].Load = 1.234
|
|
||||||
|
|
||||||
// // 6: idx = 5 / on foreign planet / loaded with MAT
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[5].Destination = R1_Planet_1_num
|
|
||||||
// g.ShipGroups[5].CargoType = game.CargoMaterial.Ref()
|
|
||||||
// g.ShipGroups[5].Load = 100.0
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 6)
|
|
||||||
|
|
||||||
// // tests
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo("UnknownRace", 1, 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 555, 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 3, 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrShipsBusy))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 2, 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputNoCargoBay))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 1, 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoUnloadEmpty))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 5, 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotOwned))
|
|
||||||
// g.ShipGroups[0].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[0].Load = 100
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 1, 11, 101),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoUnoadNotEnough))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UnloadCargo(Race_0.Name, 1, 0, 1),
|
|
||||||
// e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 6)
|
|
||||||
|
|
||||||
// // unload MAT on foreign planet / break group
|
|
||||||
// assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 0))
|
|
||||||
// assert.Equal(t, 27.273, number.Fixed3(g.Map.Planet[1].Material))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 7)
|
|
||||||
// assert.Equal(t, uint(3), g.ShipGroups[6].Number)
|
|
||||||
// assert.Nil(t, g.ShipGroups[6].CargoType)
|
|
||||||
// assert.Equal(t, 0.0, g.ShipGroups[6].Load)
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[5].CargoType)
|
|
||||||
// assert.Equal(t, uint(8), g.ShipGroups[5].Number)
|
|
||||||
// assert.Equal(t, 72.727, number.Fixed3(g.ShipGroups[5].Load))
|
|
||||||
|
|
||||||
// // unload MAT on foreign planet / break group / limited MAT
|
|
||||||
// assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 20.0))
|
|
||||||
// assert.Equal(t, 47.273, number.Fixed3(g.Map.Planet[1].Material))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 8)
|
|
||||||
// assert.Equal(t, uint(3), g.ShipGroups[7].Number)
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[7].CargoType)
|
|
||||||
// assert.Equal(t, 7.273, number.Fixed3(g.ShipGroups[7].Load))
|
|
||||||
// assert.Equal(t, game.CargoMaterial.Ref(), g.ShipGroups[5].CargoType)
|
|
||||||
// assert.Equal(t, uint(5), g.ShipGroups[5].Number)
|
|
||||||
// assert.Equal(t, 45.455, number.Fixed3(g.ShipGroups[5].Load))
|
|
||||||
|
|
||||||
// // unload ALL
|
|
||||||
// assert.NoError(t, g.UnloadCargo(Race_0.Name, 1, 0, 0))
|
|
||||||
// assert.Equal(t, 100.0, number.Fixed3(g.Map.Planet[0].Colonists))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 8)
|
|
||||||
// assert.Equal(t, uint(10), g.ShipGroups[0].Number)
|
|
||||||
// assert.Nil(t, g.ShipGroups[0].CargoType)
|
|
||||||
// assert.Equal(t, 0.0, number.Fixed3(g.ShipGroups[0].Load))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestDisassembleGroup(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
|
|
||||||
// // 1: idx = 0 / empty
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
|
||||||
|
|
||||||
// // 2: idx = 1 / In_Space
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7))
|
|
||||||
// g.ShipGroups[1].StateInSpace = &game.InSpace{
|
|
||||||
// Origin: 2,
|
|
||||||
// Range: 31.337,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 3: idx = 2 / loaded with COL
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
|
||||||
// g.ShipGroups[2].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[2].Load = 80.0
|
|
||||||
|
|
||||||
// // 4: idx = 3 / on foreign planet / loaded with MAT
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[3].Destination = R1_Planet_1_num
|
|
||||||
// g.ShipGroups[3].CargoType = game.CargoMaterial.Ref()
|
|
||||||
// g.ShipGroups[3].Load = 100.0
|
|
||||||
|
|
||||||
// // 5: idx = 4 / on foreign planet / loaded with COL
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11))
|
|
||||||
// g.ShipGroups[4].Destination = R1_Planet_1_num
|
|
||||||
// g.ShipGroups[4].CargoType = game.CargoColonist.Ref()
|
|
||||||
// g.ShipGroups[4].Load = 2.345
|
|
||||||
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 5)
|
|
||||||
|
|
||||||
// // tests
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.DisassembleGroup("UnknownRace", 1, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.DisassembleGroup(Race_0.Name, 555, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.DisassembleGroup(Race_0.Name, 2, 0),
|
|
||||||
// e.GenericErrorText(e.ErrShipsBusy))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.DisassembleGroup(Race_0.Name, 3, 12),
|
|
||||||
// e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
|
|
||||||
|
|
||||||
// groupEmptyMass := g.ShipGroups[4].EmptyMass(&g.Race[Race_0_idx].ShipTypes[Race_0_Freighter_idx])
|
|
||||||
// // groupLoadCOL := g.ShipGroups[4].Load
|
|
||||||
// planetMAT := g.Map.Planet[1].Material
|
|
||||||
// planetCOL := g.Map.Planet[1].Colonists
|
|
||||||
|
|
||||||
// assert.NoError(t, g.DisassembleGroup(Race_0.Name, 5, 0))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, planetMAT+groupEmptyMass, g.Map.Planet[1].Material)
|
|
||||||
// assert.Equal(t, planetCOL, g.Map.Planet[1].Colonists)
|
|
||||||
|
|
||||||
// groupEmptyMass = g.ShipGroups[3].EmptyMass(&g.Race[Race_0_idx].ShipTypes[Race_0_Freighter_idx])
|
|
||||||
// groupLoadMAT := g.ShipGroups[3].Load
|
|
||||||
// planetMAT = g.Map.Planet[1].Material
|
|
||||||
// assert.NoError(t, g.DisassembleGroup(Race_0.Name, 4, 0))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3)
|
|
||||||
// assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, g.Map.Planet[1].Material)
|
|
||||||
|
|
||||||
// groupEmptyMass = g.ShipGroups[2].EmptyMass(&g.Race[Race_0_idx].ShipTypes[Race_0_Freighter_idx])
|
|
||||||
// planetMAT = g.Map.Planet[0].Material
|
|
||||||
// planetCOL = g.Map.Planet[0].Colonists
|
|
||||||
// planetPOP := 90.0
|
|
||||||
// g.Map.Planet[0].Population = planetPOP
|
|
||||||
// var shipsDisassembling uint = 3
|
|
||||||
// groupEmptyMass = groupEmptyMass / float64(g.ShipGroups[2].Number) * float64(shipsDisassembling)
|
|
||||||
// newGroupUnloadedCOL := g.ShipGroups[2].Load / float64(g.ShipGroups[2].Number) * float64(shipsDisassembling)
|
|
||||||
// expectPOPIncrease := newGroupUnloadedCOL * 8
|
|
||||||
// freePOPLeft := g.Map.Planet[0].Size - g.Map.Planet[0].Population
|
|
||||||
// expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8
|
|
||||||
// expectAddedPOP := freePOPLeft
|
|
||||||
// assert.NoError(t, g.DisassembleGroup(Race_0.Name, 3, shipsDisassembling))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3)
|
|
||||||
// assert.Equal(t, planetCOL+expectAddedCOL, g.Map.Planet[0].Colonists)
|
|
||||||
// assert.Equal(t, planetPOP+expectAddedPOP, g.Map.Planet[0].Population)
|
|
||||||
// assert.Equal(t, planetMAT+groupEmptyMass, g.Map.Planet[0].Material)
|
|
||||||
// assert.Equal(t, uint(7), g.ShipGroups[2].Number)
|
|
||||||
// assert.Equal(t, 56.0, g.ShipGroups[2].Load)
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -46,158 +46,6 @@ func GroupUpgradeCost(sg ShipGroup, st ShipType, drive, weapons, shields, cargo
|
|||||||
return *uc
|
return *uc
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (g *Game) UpgradeGroup(raceName string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.upgradeGroupInternal(ri, groupIndex, techInput, limitShips, limitLevel)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) upgradeGroupInternal(ri int, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
|
||||||
// sgi := -1
|
|
||||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
|
||||||
// if sgi < 0 && sg.Index == groupIndex {
|
|
||||||
// sgi = i
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if sgi < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var sti int
|
|
||||||
// if sti = slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == g.ShipGroups[sgi].TypeID }); sti < 0 {
|
|
||||||
// // hard to test, need manual game data invalidation
|
|
||||||
// return e.NewGameStateError("not found: ShipType ID=%v", g.ShipGroups[sgi].TypeID)
|
|
||||||
// }
|
|
||||||
// st := g.Race[ri].ShipTypes[sti]
|
|
||||||
|
|
||||||
// pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == g.ShipGroups[sgi].Destination })
|
|
||||||
// if pl < 0 {
|
|
||||||
// return e.NewGameStateError("planet #%d", g.ShipGroups[sgi].Destination)
|
|
||||||
// }
|
|
||||||
// if g.Map.Planet[pl].Owner != uuid.Nil && g.Map.Planet[pl].Owner != g.Race[ri].ID {
|
|
||||||
// return e.NewEntityNotOwnedError("planet #%d for upgrade group #%d", g.Map.Planet[pl].Number, groupIndex)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if g.ShipGroups[sgi].State() != StateInOrbit && g.ShipGroups[sgi].State() != StateUpgrade {
|
|
||||||
// return e.NewShipsBusyError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// upgradeValidTech := map[string]Tech{
|
|
||||||
// TechDrive.String(): TechDrive,
|
|
||||||
// TechWeapons.String(): TechWeapons,
|
|
||||||
// TechShields.String(): TechShields,
|
|
||||||
// TechCargo.String(): TechCargo,
|
|
||||||
// TechAll.String(): TechAll,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// techRequest, ok := upgradeValidTech[techInput]
|
|
||||||
// if !ok {
|
|
||||||
// return e.NewTechUnknownError(techInput)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var blockMasses map[Tech]float64 = map[Tech]float64{
|
|
||||||
// TechDrive: st.DriveBlockMass(),
|
|
||||||
// TechWeapons: st.WeaponsBlockMass(),
|
|
||||||
// TechShields: st.ShieldsBlockMass(),
|
|
||||||
// TechCargo: st.CargoBlockMass(),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// switch {
|
|
||||||
// case techRequest != TechAll && blockMasses[techRequest] == 0:
|
|
||||||
// return e.NewUpgradeShipTechNotUsedError()
|
|
||||||
// case techRequest == TechAll && limitLevel != 0:
|
|
||||||
// return e.NewUpgradeParameterNotAllowedError("tech=%s max_level=%f", techRequest.String(), limitLevel)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// targetLevel := make(map[Tech]float64)
|
|
||||||
// var sumLevels float64
|
|
||||||
// for _, tech := range []Tech{TechDrive, TechWeapons, TechShields, TechCargo} {
|
|
||||||
// if techRequest == TechAll || tech == techRequest {
|
|
||||||
// if g.Race[ri].TechLevel(tech) < limitLevel {
|
|
||||||
// return e.NewUpgradeTechLevelInsufficientError("%s=%.03f < %.03f", tech.String(), g.Race[ri].TechLevel(tech), limitLevel)
|
|
||||||
// }
|
|
||||||
// targetLevel[tech] = FutureUpgradeLevel(g.Race[ri].TechLevel(tech), g.ShipGroups[sgi].TechLevel(tech), limitLevel)
|
|
||||||
// } else {
|
|
||||||
// targetLevel[tech] = CurrentUpgradingLevel(g.ShipGroups[sgi], tech)
|
|
||||||
// }
|
|
||||||
// sumLevels += targetLevel[tech]
|
|
||||||
// }
|
|
||||||
|
|
||||||
// productionCapacity := PlanetProductionCapacity(g, g.Map.Planet[pl].Number)
|
|
||||||
// if g.ShipGroups[sgi].State() == StateUpgrade {
|
|
||||||
// // to calculate actual capacity we must substract upgrade cost of selected group, if is upgrade state
|
|
||||||
// productionCapacity -= g.ShipGroups[sgi].StateUpgrade.Cost()
|
|
||||||
// }
|
|
||||||
// uc := GroupUpgradeCost(g.ShipGroups[sgi], st, targetLevel[TechDrive], targetLevel[TechWeapons], targetLevel[TechShields], targetLevel[TechCargo])
|
|
||||||
// costForShip := uc.UpgradeCost(1)
|
|
||||||
// if costForShip == 0 {
|
|
||||||
// return e.NewUpgradeShipsAlreadyUpToDateError("%#v", targetLevel)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// shipsToUpgrade := g.ShipGroups[sgi].Number
|
|
||||||
// // НЕ БОЛЕЕ УКАЗАННОГО
|
|
||||||
// if limitShips > 0 && shipsToUpgrade > limitShips {
|
|
||||||
// shipsToUpgrade = limitShips
|
|
||||||
// }
|
|
||||||
|
|
||||||
// maxUpgradableShips := uc.UpgradeMaxShips(productionCapacity)
|
|
||||||
|
|
||||||
// /*
|
|
||||||
// 1. считаем стоимость модернизации одного корабля
|
|
||||||
// 2. считаем сколько кораблей можно модернизировать
|
|
||||||
// 3. если не хватает даже на 1 корабль, ограничиваемся одним кораблём и пересчитываем коэффициент пропорционально массе блоков
|
|
||||||
// 4. иначе, считаем истинное количество кораблей с учётом ограничения maxShips
|
|
||||||
// */
|
|
||||||
// blockMassSum := st.EmptyMass()
|
|
||||||
|
|
||||||
// coef := productionCapacity / costForShip
|
|
||||||
// if maxUpgradableShips == 0 {
|
|
||||||
// if limitLevel > 0 {
|
|
||||||
// return e.NewUpgradeInsufficientResourcesError("ship cost=%.03f L=%.03f", costForShip, productionCapacity)
|
|
||||||
// }
|
|
||||||
// sumLevels = sumLevels * coef
|
|
||||||
// for tech := range targetLevel {
|
|
||||||
// if blockMasses[tech] > 0 {
|
|
||||||
// proportional := sumLevels * (blockMasses[tech] / blockMassSum)
|
|
||||||
// targetLevel[tech] = proportional
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// maxUpgradableShips = 1
|
|
||||||
// } else if maxUpgradableShips > shipsToUpgrade {
|
|
||||||
// maxUpgradableShips = shipsToUpgrade
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // sanity check
|
|
||||||
// uc = GroupUpgradeCost(g.ShipGroups[sgi], st, targetLevel[TechDrive], targetLevel[TechWeapons], targetLevel[TechShields], targetLevel[TechCargo])
|
|
||||||
// costForGroup := uc.UpgradeCost(maxUpgradableShips)
|
|
||||||
// if costForGroup > productionCapacity {
|
|
||||||
// e.NewGameStateError("cost recalculation: coef=%f cost(%d)=%f L=%f", coef, maxUpgradableShips, costForGroup, productionCapacity)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // break group if needed
|
|
||||||
// if maxUpgradableShips < g.ShipGroups[sgi].Number {
|
|
||||||
// if g.ShipGroups[sgi].State() == StateUpgrade {
|
|
||||||
// return e.NewUpgradeGroupBreakNotAllowedError("ships=%d max=%d", g.ShipGroups[sgi].Number, maxUpgradableShips)
|
|
||||||
// }
|
|
||||||
// nsgi, err := g.breakGroupSafe(ri, groupIndex, maxUpgradableShips)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// sgi = nsgi
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // finally, fill group upgrade prefs
|
|
||||||
// for tech := range targetLevel {
|
|
||||||
// if targetLevel[tech] > 0 {
|
|
||||||
// g.ShipGroups[sgi] = UpgradeGroupPreference(g.ShipGroups[sgi], st, tech, targetLevel[tech])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
func CurrentUpgradingLevel(sg ShipGroup, tech Tech) float64 {
|
func CurrentUpgradingLevel(sg ShipGroup, tech Tech) float64 {
|
||||||
if sg.StateUpgrade == nil {
|
if sg.StateUpgrade == nil {
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -7,6 +7,19 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Cruiser = game.ShipType{
|
||||||
|
ShipTypeReport: game.ShipTypeReport{
|
||||||
|
Name: "Cruiser",
|
||||||
|
Drive: 15,
|
||||||
|
Armament: 1,
|
||||||
|
Weapons: 15,
|
||||||
|
Shields: 15,
|
||||||
|
Cargo: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func TestBlockUpgradeCost(t *testing.T) {
|
func TestBlockUpgradeCost(t *testing.T) {
|
||||||
assert.Equal(t, 00.0, game.BlockUpgradeCost(1, 1.0, 1.0))
|
assert.Equal(t, 00.0, game.BlockUpgradeCost(1, 1.0, 1.0))
|
||||||
assert.Equal(t, 25.0, game.BlockUpgradeCost(5, 1.0, 2.0))
|
assert.Equal(t, 25.0, game.BlockUpgradeCost(5, 1.0, 2.0))
|
||||||
@@ -108,60 +121,3 @@ func TestUpgradeGroupPreference(t *testing.T) {
|
|||||||
assert.Equal(t, 0., sg.StateUpgrade.TechCost(game.TechCargo))
|
assert.Equal(t, 0., sg.StateUpgrade.TechCost(game.TechCargo))
|
||||||
assert.Equal(t, 900., sg.StateUpgrade.Cost())
|
assert.Equal(t, 900., sg.StateUpgrade.Cost())
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestUpgradeGroup(t *testing.T) {
|
|
||||||
// g := newGame()
|
|
||||||
// // group #1 - in_orbit, free to upgrade
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 10))
|
|
||||||
// // group #2 - in_space
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1))
|
|
||||||
// g.ShipGroups[1].StateInSpace = &game.InSpace{Origin: 2, Range: 1.23}
|
|
||||||
// // group #3 - in_orbit, foreign planet
|
|
||||||
// assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1))
|
|
||||||
// g.ShipGroups[2].Destination = R1_Planet_1_num
|
|
||||||
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup("UnknownRace", 1, "DRIVE", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 555, "DRIVE", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 2, "DRIVE", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrShipsBusy))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 3, "DRIVE", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputEntityNotOwned))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 1, "GUN", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputTechUnknown))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 1, "CARGO", 0, 0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUpgradeShipTechNotUsed))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 1, "ALL", 0, 2.0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUpgradeParameterNotAllowed))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 2.0),
|
|
||||||
// e.GenericErrorText(e.ErrInputUpgradeTechLevelInsufficient))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 1.1),
|
|
||||||
// e.GenericErrorText(e.ErrInputUpgradeShipsAlreadyUpToDate))
|
|
||||||
|
|
||||||
// g.Race[Race_0_idx].SetTechLevel(game.TechDrive, 10.0)
|
|
||||||
// assert.Equal(t, 10.0, g.Race[Race_0_idx].TechLevel(game.TechDrive))
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 0, 10.0),
|
|
||||||
// e.GenericErrorText(e.ErrUpgradeInsufficientResources))
|
|
||||||
|
|
||||||
// assert.NoError(t, g.UpgradeGroup(Race_0.Name, 1, "DRIVE", 2, 1.2))
|
|
||||||
// assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4)
|
|
||||||
// assert.Equal(t, uint(8), g.ShipGroups[0].Number)
|
|
||||||
// assert.Equal(t, uint(2), g.ShipGroups[3].Number)
|
|
||||||
// assert.Equal(t, game.StateInOrbit, g.ShipGroups[0].State())
|
|
||||||
// assert.Equal(t, game.StateUpgrade, g.ShipGroups[3].State())
|
|
||||||
|
|
||||||
// assert.ErrorContains(t,
|
|
||||||
// g.UpgradeGroup(Race_0.Name, 4, "DRIVE", 1, 1.3),
|
|
||||||
// e.GenericErrorText(e.ErrInputUpgradeGroupBreakNotAllowed))
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -5,7 +5,3 @@ type Map struct {
|
|||||||
Height uint32 `json:"height"`
|
Height uint32 `json:"height"`
|
||||||
Planet []Planet `json:"planets"`
|
Planet []Planet `json:"planets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Destination(x1, y1, x2, y2 float64) float64 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type UnidentifiedPlanet struct {
|
type UnidentifiedPlanet struct {
|
||||||
@@ -48,21 +46,6 @@ func (p Planet) ProductionCapacity() float64 {
|
|||||||
return p.Industry*0.75 + p.Population*0.25
|
return p.Industry*0.75 + p.Population*0.25
|
||||||
}
|
}
|
||||||
|
|
||||||
// Свободный производственный потенциал (L)
|
|
||||||
// промышленность * 0.75 + население * 0.25
|
|
||||||
// за вычетом затрат, расходуемых в течение хода на модернизацию кораблей
|
|
||||||
func PlanetProductionCapacity(g *Game, planetNumber uint) float64 {
|
|
||||||
p, err := g.PlanetByNumber(planetNumber)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var busyResources float64
|
|
||||||
for sg := range g.ShipsInUpgrade(p.Number) {
|
|
||||||
busyResources += sg.StateUpgrade.Cost()
|
|
||||||
}
|
|
||||||
return PlanetProduction(p.Industry, p.Population) - busyResources
|
|
||||||
}
|
|
||||||
|
|
||||||
func PlanetProduction(industry, population float64) float64 {
|
func PlanetProduction(industry, population float64) float64 {
|
||||||
return industry*0.75 + population*0.25
|
return industry*0.75 + population*0.25
|
||||||
}
|
}
|
||||||
@@ -102,38 +85,3 @@ func UnloadColonists(p Planet, v float64) Planet {
|
|||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) RenamePlanet(raceName string, planetNumber int, typeName string) error {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return g.renamePlanetInternal(ri, planetNumber, typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) renamePlanetInternal(ri int, number int, name string) error {
|
|
||||||
n, ok := validateTypeName(name)
|
|
||||||
if !ok {
|
|
||||||
return e.NewEntityTypeNameValidationError("%q", n)
|
|
||||||
}
|
|
||||||
if number < 0 {
|
|
||||||
return e.NewPlanetNumberError(number)
|
|
||||||
}
|
|
||||||
pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == uint(number) })
|
|
||||||
if pl < 0 {
|
|
||||||
return e.NewEntityNotExistsError("planet #%d", number)
|
|
||||||
}
|
|
||||||
if g.Map.Planet[pl].Owner != g.Race[ri].ID {
|
|
||||||
return e.NewEntityNotOwnedError("planet #%d", number)
|
|
||||||
}
|
|
||||||
g.Map.Planet[pl].Name = n
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func PlanetByNum(g *Game, number uint) (Planet, bool) {
|
|
||||||
pi := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == number })
|
|
||||||
if pi < 0 {
|
|
||||||
return Planet{}, false
|
|
||||||
}
|
|
||||||
return g.Map.Planet[pi], true
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,11 +12,3 @@ func TestPlanetProduction(t *testing.T) {
|
|||||||
assert.Equal(t, 750., game.PlanetProduction(1000., 0.))
|
assert.Equal(t, 750., game.PlanetProduction(1000., 0.))
|
||||||
assert.Equal(t, 250., game.PlanetProduction(0., 1000.))
|
assert.Equal(t, 250., game.PlanetProduction(0., 1000.))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPlanetProductionCapacity(t *testing.T) {
|
|
||||||
g := newGame()
|
|
||||||
assert.NoError(t, g.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1))
|
|
||||||
assert.Equal(t, 100., game.PlanetProductionCapacity(g, R0_Planet_0_num))
|
|
||||||
g.ShipGroups[0] = game.UpgradeGroupPreference(g.ShipGroups[0], Cruiser, game.TechDrive, 1.6)
|
|
||||||
assert.Equal(t, 53.125, game.PlanetProductionCapacity(g, R0_Planet_0_num))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProductionType string
|
type ProductionType string
|
||||||
@@ -37,92 +34,3 @@ func (p ProductionType) AsType(subject uuid.UUID) Production {
|
|||||||
return Production{Type: p, SubjectID: nil}
|
return Production{Type: p, SubjectID: nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) PlanetProduction(raceName string, planetNumber int, prodType, subject string) error {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var prod ProductionType
|
|
||||||
switch ProductionType(prodType) {
|
|
||||||
case ProductionMaterial:
|
|
||||||
prod = ProductionMaterial
|
|
||||||
case ProductionCapital:
|
|
||||||
prod = ProductionCapital
|
|
||||||
case ResearchDrive:
|
|
||||||
prod = ResearchDrive
|
|
||||||
case ResearchWeapons:
|
|
||||||
prod = ResearchWeapons
|
|
||||||
case ResearchShields:
|
|
||||||
prod = ResearchShields
|
|
||||||
case ResearchCargo:
|
|
||||||
prod = ResearchCargo
|
|
||||||
case ResearchScience:
|
|
||||||
prod = ResearchScience
|
|
||||||
case ProductionShip:
|
|
||||||
prod = ProductionShip
|
|
||||||
default:
|
|
||||||
return e.NewProductionInvalidError(prodType)
|
|
||||||
}
|
|
||||||
return g.planetProductionInternal(ri, planetNumber, prod, subject)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) planetProductionInternal(ri int, number int, prod ProductionType, subj string) error {
|
|
||||||
if number < 0 {
|
|
||||||
return e.NewPlanetNumberError(number)
|
|
||||||
}
|
|
||||||
i := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == uint(number) })
|
|
||||||
if i < 0 {
|
|
||||||
return e.NewEntityNotExistsError("planet #%d", number)
|
|
||||||
}
|
|
||||||
if g.Map.Planet[i].Owner != g.Race[ri].ID {
|
|
||||||
return e.NewEntityNotOwnedError("planet #%d", number)
|
|
||||||
}
|
|
||||||
g.Map.Planet[i].Production.Progress = nil
|
|
||||||
var subjectID *uuid.UUID
|
|
||||||
if (prod == ResearchScience || prod == ProductionShip) && subj == "" {
|
|
||||||
return e.NewEntityTypeNameValidationError("%s=%q", prod, subj)
|
|
||||||
}
|
|
||||||
if prod == ResearchScience {
|
|
||||||
i := slices.IndexFunc(g.Race[ri].Sciences, func(s Science) bool { return s.Name == subj })
|
|
||||||
if i < 0 {
|
|
||||||
return e.NewEntityNotExistsError("science %w", subj)
|
|
||||||
}
|
|
||||||
subjectID = &g.Race[ri].Sciences[i].ID
|
|
||||||
}
|
|
||||||
if prod == ProductionShip {
|
|
||||||
i := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.Name == subj })
|
|
||||||
if i < 0 {
|
|
||||||
return e.NewEntityNotExistsError("ship type %w", subj)
|
|
||||||
}
|
|
||||||
if g.Map.Planet[i].Production.Type == ProductionShip &&
|
|
||||||
g.Map.Planet[i].Production.SubjectID != nil &&
|
|
||||||
*g.Map.Planet[i].Production.SubjectID == g.Race[ri].ShipTypes[i].ID {
|
|
||||||
// Planet already produces this ship type, keeping progress intact
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
subjectID = &g.Race[ri].ShipTypes[i].ID
|
|
||||||
var progress float64 = 0.
|
|
||||||
g.Map.Planet[i].Production.Progress = &progress
|
|
||||||
}
|
|
||||||
if g.Map.Planet[i].Production.Type == ProductionShip {
|
|
||||||
if g.Map.Planet[i].Production.SubjectID == nil {
|
|
||||||
return e.NewGameStateError("planet #%d produces ship but SubjectID is empty", g.Map.Planet[i].Number)
|
|
||||||
}
|
|
||||||
s := *g.Map.Planet[i].Production.SubjectID
|
|
||||||
if g.Map.Planet[i].Production.Progress == nil {
|
|
||||||
return e.NewGameStateError("planet #%d produces ship but Progress is empty", g.Map.Planet[i].Number)
|
|
||||||
}
|
|
||||||
progress := *g.Map.Planet[i].Production.Progress
|
|
||||||
i := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == s })
|
|
||||||
if i < 0 {
|
|
||||||
return e.NewGameStateError("planet #%d produces ship but ShipType was not found for race %s", g.Map.Planet[i].Number, g.Race[ri].Name)
|
|
||||||
}
|
|
||||||
mat, _ := g.Race[ri].ShipTypes[i].ProductionCost()
|
|
||||||
extra := mat * progress
|
|
||||||
g.Map.Planet[i].Material += extra
|
|
||||||
}
|
|
||||||
g.Map.Planet[i].Production.Type = prod
|
|
||||||
g.Map.Planet[i].Production.SubjectID = subjectID
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import "github.com/google/uuid"
|
||||||
// "slices"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
// e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Race struct {
|
type Race struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
@@ -38,11 +33,6 @@ func (r Race) TechLevel(t Tech) float64 {
|
|||||||
return r.Tech.Value(t)
|
return r.Tech.Value(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove func, move to Cache
|
|
||||||
func (r *Race) SetTechLevel(t Tech, v float64) {
|
|
||||||
r.Tech = r.Tech.Set(t, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r Race) FlightDistance() float64 {
|
func (r Race) FlightDistance() float64 {
|
||||||
return r.TechLevel(TechDrive) * 40
|
return r.TechLevel(TechDrive) * 40
|
||||||
}
|
}
|
||||||
@@ -50,75 +40,3 @@ func (r Race) FlightDistance() float64 {
|
|||||||
func (r Race) VisibilityDistance() float64 {
|
func (r Race) VisibilityDistance() float64 {
|
||||||
return r.TechLevel(TechDrive) * 30
|
return r.TechLevel(TechDrive) * 30
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (g Game) Relation(hostRace, opponentRace string) (RaceRelation, error) {
|
|
||||||
// ri, err := g.raceIndex(hostRace)
|
|
||||||
// if err != nil {
|
|
||||||
// return RaceRelation{}, err
|
|
||||||
// }
|
|
||||||
// other, err := g.raceIndex(opponentRace)
|
|
||||||
// if err != nil {
|
|
||||||
// return RaceRelation{}, err
|
|
||||||
// }
|
|
||||||
// return g.relationInternal(ri, other)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) UpdateRelation(race, opponent string, rel Relation) error {
|
|
||||||
// ri, err := g.raceIndex(race)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// var other int
|
|
||||||
// if race == opponent {
|
|
||||||
// other = ri
|
|
||||||
// } else if other, err = g.raceIndex(opponent); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.updateRelationInternal(ri, other, rel)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g *Game) GiveVotes(race, recipient string) error {
|
|
||||||
// ri, err := g.raceIndex(race)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// rec, err := g.raceIndex(recipient)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// g.Race[ri].Vote = g.Race[rec].ID
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) relationInternal(ri, other int) (RaceRelation, error) {
|
|
||||||
// if ri == other {
|
|
||||||
// return RaceRelation{
|
|
||||||
// RaceID: g.Race[ri].ID,
|
|
||||||
// Relation: RelationPeace,
|
|
||||||
// }, nil
|
|
||||||
// }
|
|
||||||
// rel := slices.IndexFunc(g.Race[ri].Relations, func(r RaceRelation) bool { return r.RaceID == g.Race[other].ID })
|
|
||||||
// if rel < 0 {
|
|
||||||
// return RaceRelation{}, e.NewGameStateError("Relation: opponent not found")
|
|
||||||
// }
|
|
||||||
// return g.Race[ri].Relations[rel], nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) updateRelationInternal(ri, other int, rel Relation) error {
|
|
||||||
// for o := range g.Race[ri].Relations {
|
|
||||||
// switch {
|
|
||||||
// case ri == other:
|
|
||||||
// g.Race[ri].Relations[o].Relation = rel
|
|
||||||
// case g.Race[ri].Relations[o].RaceID == g.Race[other].ID:
|
|
||||||
// g.Race[ri].Relations[o].Relation = rel
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if ri != other {
|
|
||||||
// return e.NewGameStateError("UpdateRelation: opponent not found")
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1 +1,35 @@
|
|||||||
package game_test
|
package game_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ts = game.TechSet{
|
||||||
|
game.TechDrive: 1.1,
|
||||||
|
game.TechWeapons: 1.2,
|
||||||
|
game.TechShields: 1.3,
|
||||||
|
game.TechCargo: 1.4,
|
||||||
|
}
|
||||||
|
r = game.Race{
|
||||||
|
Tech: ts,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTechLevel(t *testing.T) {
|
||||||
|
assert.Equal(t, 1.1, r.TechLevel(game.TechDrive))
|
||||||
|
assert.Equal(t, 1.2, r.TechLevel(game.TechWeapons))
|
||||||
|
assert.Equal(t, 1.3, r.TechLevel(game.TechShields))
|
||||||
|
assert.Equal(t, 1.4, r.TechLevel(game.TechCargo))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlightDistance(t *testing.T) {
|
||||||
|
assert.Equal(t, 44., r.FlightDistance())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVisibilityDistance(t *testing.T) {
|
||||||
|
assert.Equal(t, 33., r.VisibilityDistance())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
"github.com/iliadenisov/galaxy/internal/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RouteType string
|
type RouteType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -18,7 +10,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
routeTypeSet map[string]RouteType = map[string]RouteType{
|
RouteTypeSet map[string]RouteType = map[string]RouteType{
|
||||||
RouteMaterial.String(): RouteMaterial,
|
RouteMaterial.String(): RouteMaterial,
|
||||||
RouteCapital.String(): RouteCapital,
|
RouteCapital.String(): RouteCapital,
|
||||||
RouteColonist.String(): RouteColonist,
|
RouteColonist.String(): RouteColonist,
|
||||||
@@ -33,84 +25,3 @@ func (rt RouteType) Ref() *RouteType {
|
|||||||
func (rt RouteType) String() string {
|
func (rt RouteType) String() string {
|
||||||
return string(rt)
|
return string(rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) SetRoute(raceName, loadType string, origin, destination uint) error {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rt, ok := routeTypeSet[loadType]
|
|
||||||
if !ok {
|
|
||||||
return e.NewCargoTypeInvalidError(loadType)
|
|
||||||
}
|
|
||||||
return g.setRouteInternal(ri, rt, origin, destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Game) RemoveRoute(raceName, loadType string, origin uint) error {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rt, ok := routeTypeSet[loadType]
|
|
||||||
if !ok {
|
|
||||||
return e.NewCargoTypeInvalidError(loadType)
|
|
||||||
}
|
|
||||||
return g.removeRouteInternal(ri, rt, origin)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Game) setRouteInternal(ri int, rt RouteType, origin, destination uint) error {
|
|
||||||
p1, ok := PlanetByNum(g, origin)
|
|
||||||
if !ok {
|
|
||||||
return e.NewEntityNotExistsError("origin planet #%d", origin)
|
|
||||||
}
|
|
||||||
if p1.Owner != g.Race[ri].ID {
|
|
||||||
return e.NewEntityNotOwnedError("planet #%d", origin)
|
|
||||||
}
|
|
||||||
p2, ok := PlanetByNum(g, destination)
|
|
||||||
if !ok {
|
|
||||||
return e.NewEntityNotExistsError("destination planet #%d", destination)
|
|
||||||
}
|
|
||||||
rangeToDestination := util.ShortDistance(g.Map.Width, g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
|
||||||
if rangeToDestination > g.Race[ri].FlightDistance() {
|
|
||||||
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
|
||||||
}
|
|
||||||
|
|
||||||
SetPlanetRoute(g, rt, origin, destination)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Game) removeRouteInternal(ri int, rt RouteType, origin uint) error {
|
|
||||||
p1, ok := PlanetByNum(g, origin)
|
|
||||||
if !ok {
|
|
||||||
return e.NewEntityNotExistsError("origin planet #%d", origin)
|
|
||||||
}
|
|
||||||
if p1.Owner != g.Race[ri].ID {
|
|
||||||
return e.NewEntityNotOwnedError("planet #%d", origin)
|
|
||||||
}
|
|
||||||
|
|
||||||
RemovePlanetRoute(g, rt, origin)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetPlanetRoute(g *Game, rt RouteType, origin, destination uint) {
|
|
||||||
pi := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == origin })
|
|
||||||
if pi < 0 {
|
|
||||||
panic(fmt.Sprintf("SetPlanetRoute: origin planet #%d not found", origin))
|
|
||||||
}
|
|
||||||
if g.Map.Planet[pi].Route == nil {
|
|
||||||
g.Map.Planet[pi].Route = make(map[RouteType]uint)
|
|
||||||
}
|
|
||||||
g.Map.Planet[pi].Route[rt] = destination
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemovePlanetRoute(g *Game, rt RouteType, origin uint) {
|
|
||||||
pi := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == origin })
|
|
||||||
if pi < 0 {
|
|
||||||
panic(fmt.Sprintf("RemovePlanetRoute: origin planet #%d not found", origin))
|
|
||||||
}
|
|
||||||
if g.Map.Planet[pi].Route != nil {
|
|
||||||
delete(g.Map.Planet[pi].Route, rt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
package game_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSetRoute(t *testing.T) {
|
|
||||||
g := newGame()
|
|
||||||
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2))
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", 0, 2))
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2))
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 0, 2))
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.SetRoute("UnknownRace", "COL", 0, 2),
|
|
||||||
e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.SetRoute(Race_0.Name, "IND", 0, 2),
|
|
||||||
e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.SetRoute(Race_0.Name, "COL", 500, 2),
|
|
||||||
e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.SetRoute(Race_0.Name, "COL", 1, 2),
|
|
||||||
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.SetRoute(Race_0.Name, "COL", 0, 3),
|
|
||||||
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveRoute(t *testing.T) {
|
|
||||||
g := newGame()
|
|
||||||
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2))
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2))
|
|
||||||
assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 2, 0))
|
|
||||||
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(2).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.NoError(t, g.RemoveRoute(Race_0.Name, "COL", 0))
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
|
||||||
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
|
||||||
|
|
||||||
assert.NoError(t, g.RemoveRoute(Race_0.Name, "EMP", 2))
|
|
||||||
assert.NotContains(t, g.MustPlanetByNumber(2).Route, game.RouteEmpty)
|
|
||||||
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.RemoveRoute("UnknownRace", "COL", 0),
|
|
||||||
e.GenericErrorText(e.ErrInputUnknownRace))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.RemoveRoute(Race_0.Name, "IND", 0),
|
|
||||||
e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.RemoveRoute(Race_0.Name, "COL", 500),
|
|
||||||
e.GenericErrorText(e.ErrInputEntityNotExists))
|
|
||||||
assert.ErrorContains(t,
|
|
||||||
g.RemoveRoute(Race_0.Name, "COL", 1),
|
|
||||||
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Science struct {
|
type Science struct {
|
||||||
@@ -24,84 +21,3 @@ type ScienceReport struct {
|
|||||||
Shields float64 `json:"shields"`
|
Shields float64 `json:"shields"`
|
||||||
Cargo float64 `json:"cargo"`
|
Cargo float64 `json:"cargo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) Sciences(raceName string) ([]Science, error) {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return g.sciencesInternal(ri), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) sciencesInternal(ri int) []Science {
|
|
||||||
return g.Race[ri].Sciences
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) DeleteScience(raceName, typeName string) error {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return g.deleteScienceInternal(ri, typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) deleteScienceInternal(ri int, name string) error {
|
|
||||||
sc := slices.IndexFunc(g.Race[ri].Sciences, func(s Science) bool { return s.Name == name })
|
|
||||||
if sc < 0 {
|
|
||||||
return e.NewEntityNotExistsError("science %w", name)
|
|
||||||
}
|
|
||||||
if pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool {
|
|
||||||
return p.Production.Type == ResearchScience &&
|
|
||||||
p.Production.SubjectID != nil &&
|
|
||||||
*p.Production.SubjectID == g.Race[ri].Sciences[sc].ID
|
|
||||||
}); pl >= 0 {
|
|
||||||
return e.NewDeleteSciencePlanetProductionError(g.Map.Planet[pl].Name)
|
|
||||||
}
|
|
||||||
g.Race[ri].Sciences = append(g.Race[ri].Sciences[:sc], g.Race[ri].Sciences[sc+1:]...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) CreateScience(raceName, typeName string, d, w, s, c float64) error {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return g.createScienceInternal(ri, typeName, d, w, s, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) createScienceInternal(ri int, name string, d, w, s, c float64) error {
|
|
||||||
n, ok := validateTypeName(name)
|
|
||||||
if !ok {
|
|
||||||
return e.NewEntityTypeNameValidationError("%q", n)
|
|
||||||
}
|
|
||||||
if sc := slices.IndexFunc(g.Race[ri].Sciences, func(s Science) bool { return s.Name == n }); sc >= 0 {
|
|
||||||
return e.NewEntityTypeNameDuplicateError("science %w", g.Race[ri].Sciences[sc].Name)
|
|
||||||
}
|
|
||||||
if d < 0 {
|
|
||||||
return e.NewDriveValueError(d)
|
|
||||||
}
|
|
||||||
if w < 0 {
|
|
||||||
return e.NewWeaponsValueError(w)
|
|
||||||
}
|
|
||||||
if s < 0 {
|
|
||||||
return e.NewShieldsValueError(s)
|
|
||||||
}
|
|
||||||
if c < 0 {
|
|
||||||
return e.NewCargoValueError(c)
|
|
||||||
}
|
|
||||||
sum := d + w + s + c
|
|
||||||
if sum != 1 {
|
|
||||||
return e.NewScienceSumValuesError("D=%f W=%f S=%f C=%f sum=%f", d, w, s, c, sum)
|
|
||||||
}
|
|
||||||
g.Race[ri].Sciences = append(g.Race[ri].Sciences, Science{
|
|
||||||
ID: uuid.New(),
|
|
||||||
ScienceReport: ScienceReport{
|
|
||||||
Name: n,
|
|
||||||
Drive: d,
|
|
||||||
Weapons: w,
|
|
||||||
Shields: s,
|
|
||||||
Cargo: c,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ShipTypeReport struct {
|
type ShipTypeReport struct {
|
||||||
@@ -80,183 +76,3 @@ func (st ShipType) ProductionCost() (mat float64, pop float64) {
|
|||||||
pop = mat * 10
|
pop = mat * 10
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Game) mustShipType(id uuid.UUID) *ShipType {
|
|
||||||
for ri := range g.Race {
|
|
||||||
if st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == id }); st >= 0 {
|
|
||||||
return &g.Race[ri].ShipTypes[st]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic(fmt.Sprintf("mustShipType: ShipType not found: %v", id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) ShipTypes(raceName string) ([]ShipType, error) {
|
|
||||||
ri, err := g.raceIndex(raceName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return g.shipTypesInternal(ri), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g Game) shipTypesInternal(ri int) []ShipType {
|
|
||||||
return g.Race[ri].ShipTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (g Game) DeleteShipType(raceName, typeName string) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.deleteShipTypeInternal(ri, typeName)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) deleteShipTypeInternal(ri int, name string) error {
|
|
||||||
// st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.Name == name })
|
|
||||||
// if st < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("ship type %w", name)
|
|
||||||
// }
|
|
||||||
// if pl := slices.IndexFunc(g.Map.Planet, func(p Planet) bool {
|
|
||||||
// return p.Production.Type == ProductionShip &&
|
|
||||||
// p.Production.SubjectID != nil &&
|
|
||||||
// g.Race[ri].ShipTypes[st].ID == *p.Production.SubjectID
|
|
||||||
// }); pl >= 0 {
|
|
||||||
// return e.NewDeleteShipTypePlanetProductionError(g.Map.Planet[pl].Name)
|
|
||||||
// }
|
|
||||||
// for sg := range g.listShipGroups(ri) {
|
|
||||||
// if sg.TypeID == g.Race[ri].ShipTypes[st].ID {
|
|
||||||
// return e.NewDeleteShipTypeExistingGroupError("group: %v", sg.Index)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// g.Race[ri].ShipTypes = append(g.Race[ri].ShipTypes[:st], g.Race[ri].ShipTypes[st+1:]...)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: D A W S C
|
|
||||||
// func (g Game) CreateShipType(raceName, typeName string, d, w, s, c float64, a int) error {
|
|
||||||
// ri, err := g.raceIndex(raceName)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// _, err = g.createShipTypeInternal(ri, typeName, d, w, s, c, a)
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) createShipTypeInternal(ri int, name string, d, w, s, c float64, a int) (int, error) {
|
|
||||||
// if err := checkShipTypeValues(d, w, s, c, a); err != nil {
|
|
||||||
// return -1, err
|
|
||||||
// }
|
|
||||||
// n, ok := validateTypeName(name)
|
|
||||||
// if !ok {
|
|
||||||
// return -1, e.NewEntityTypeNameValidationError("%q", n)
|
|
||||||
// }
|
|
||||||
// if st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.Name == name }); st >= 0 {
|
|
||||||
// return -1, e.NewEntityTypeNameDuplicateError("ship type %w", g.Race[ri].ShipTypes[st].Name)
|
|
||||||
// }
|
|
||||||
// g.Race[ri].ShipTypes = append(g.Race[ri].ShipTypes, ShipType{
|
|
||||||
// ID: uuid.New(),
|
|
||||||
// ShipTypeReport: ShipTypeReport{
|
|
||||||
// Name: n,
|
|
||||||
// Drive: d,
|
|
||||||
// Weapons: w,
|
|
||||||
// Shields: s,
|
|
||||||
// Cargo: c,
|
|
||||||
// Armament: uint(a),
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// return len(g.Race[ri].ShipTypes) - 1, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) MergeShipType(race, name, targetName string) error {
|
|
||||||
// ri, err := g.raceIndex(race)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// return g.mergeShipTypeInternal(ri, name, targetName)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (g Game) mergeShipTypeInternal(ri int, name, targetName string) error {
|
|
||||||
// st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.Name == name })
|
|
||||||
// if st < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("source ship type %w", name)
|
|
||||||
// }
|
|
||||||
// if name == targetName {
|
|
||||||
// return e.NewEntityTypeNameEqualityError("ship type %q", targetName)
|
|
||||||
// }
|
|
||||||
// tt := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.Name == targetName })
|
|
||||||
// if tt < 0 {
|
|
||||||
// return e.NewEntityNotExistsError("target ship type %w", name)
|
|
||||||
// }
|
|
||||||
// if !g.Race[ri].ShipTypes[st].Equal(g.Race[ri].ShipTypes[tt]) {
|
|
||||||
// return e.NewMergeShipTypeNotEqualError()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // switch planet productions to the new type
|
|
||||||
// for pl := range g.Map.Planet {
|
|
||||||
// if g.Map.Planet[pl].Owner == g.Race[ri].ID &&
|
|
||||||
// g.Map.Planet[pl].Production.Type == ProductionShip &&
|
|
||||||
// g.Map.Planet[pl].Production.SubjectID != nil &&
|
|
||||||
// *g.Map.Planet[pl].Production.SubjectID == g.Race[ri].ShipTypes[st].ID {
|
|
||||||
// g.Map.Planet[pl].Production.SubjectID = &g.Race[ri].ShipTypes[tt].ID
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // switch ship groups to the new type
|
|
||||||
// for sg := range g.ShipGroups {
|
|
||||||
// if g.ShipGroups[sg].OwnerID == g.Race[ri].ID && g.ShipGroups[sg].TypeID == g.Race[ri].ShipTypes[st].ID {
|
|
||||||
// g.ShipGroups[sg].TypeID = g.Race[ri].ShipTypes[tt].ID
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // remove the source type
|
|
||||||
// g.Race[ri].ShipTypes = append(g.Race[ri].ShipTypes[:st], g.Race[ri].ShipTypes[st+1:]...)
|
|
||||||
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
func checkShipTypeValues(d, w, s, c float64, a int) error {
|
|
||||||
if !checkShipTypeValueDWSC(d) {
|
|
||||||
return e.NewDriveValueError(d)
|
|
||||||
}
|
|
||||||
if !checkShipTypeValueDWSC(w) {
|
|
||||||
return e.NewWeaponsValueError(w)
|
|
||||||
}
|
|
||||||
if !checkShipTypeValueDWSC(s) {
|
|
||||||
return e.NewShieldsValueError(s)
|
|
||||||
}
|
|
||||||
if !checkShipTypeValueDWSC(c) {
|
|
||||||
return e.NewCargoValueError(s)
|
|
||||||
}
|
|
||||||
if a < 0 {
|
|
||||||
return e.NewShipTypeArmamentValueError(a)
|
|
||||||
}
|
|
||||||
if (w == 0 && a > 0) || (a == 0 && w > 0) {
|
|
||||||
return e.NewShipTypeArmamentAndWeaponsValueError("A=%d W=%.0f", a, w)
|
|
||||||
}
|
|
||||||
if d == 0 && w == 0 && s == 0 && c == 0 && a == 0 {
|
|
||||||
return e.NewShipTypeShipTypeZeroValuesError()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkShipTypeValueDWSC(v float64) bool {
|
|
||||||
return v == 0 || v >= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func ShipClass(g *Game, ri int, classID uuid.UUID) (ShipType, bool) {
|
|
||||||
if len(g.Race) < ri+1 {
|
|
||||||
panic(fmt.Sprintf("ShipClass: game race index %d invalid: len=%d", ri, len(g.Race)))
|
|
||||||
}
|
|
||||||
sti := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == classID })
|
|
||||||
if sti < 0 {
|
|
||||||
return ShipType{}, false
|
|
||||||
}
|
|
||||||
return g.Race[ri].ShipTypes[sti], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func ShipClassIndex(g *Game, ri int, classID uuid.UUID) (int, bool) {
|
|
||||||
if len(g.Race) < ri+1 {
|
|
||||||
panic(fmt.Sprintf("ShipClass: game race index %d invalid: len=%d", ri, len(g.Race)))
|
|
||||||
}
|
|
||||||
sti := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == classID })
|
|
||||||
return sti, sti >= 0
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user