package controller import ( "fmt" "slices" "strings" "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "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) } 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 } if p.Production.Type == game.ProductionShip && (prod != game.ProductionShip || *subjectID != *p.Production.SubjectID) { mat, _ := c.MustShipType(ri, *p.Production.SubjectID).ProductionCost() p.Material += mat * (*p.Production.Progress) *p.Production.Progress = 0. } else if prod == game.ProductionShip { // new ship class to produce; otherwise we must have been returned from the func earlier var progress float64 = 0. p.Production.Progress = &progress } if prod != game.ProductionShip { p.Production.Progress = nil } p.Production.Type = prod p.Production.SubjectID = subjectID return nil } // TODO: test func (c *Cache) PlanetProductionDisplayName(pn uint) string { p := c.MustPlanet(pn) ri := c.RaceIndex(p.Owner) switch pt := p.Production.Type; pt { case game.ResearchDrive: return "Drive" case game.ResearchWeapons: return "Weapons" case game.ResearchShields: return "Shields" case game.ResearchCargo: return "Cargo" case game.ProductionMaterial: return "Material" case game.ProductionCapital: return "Capital" case game.ProductionShip: return c.MustShipType(ri, *p.Production.SubjectID).Name case game.ResearchScience: i := slices.IndexFunc(c.g.Race[ri].Sciences, func(sc game.Science) bool { return sc.ID == *p.Production.SubjectID }) if i < 0 { panic("researching science not found") } return c.g.Race[ri].Sciences[i].Name default: return string(pt) } } func (c *Cache) Planet(planetNumber uint) (*game.Planet, bool) { if c.cachePlanetByPlanetNumber == nil { c.cachePlanetByPlanetNumber = make(map[uint]*game.Planet) for p := range c.g.Map.Planet { c.cachePlanetByPlanetNumber[c.g.Map.Planet[p].Number] = &c.g.Map.Planet[p] } } if v, ok := c.cachePlanetByPlanetNumber[planetNumber]; ok { return v, true } else { return nil, false } } func (c *Cache) MustPlanet(pn uint) *game.Planet { if v, ok := c.Planet(pn); ok { return v } else { 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 } } // Свободный "Производственный Потенциал" (L) // промышленность * 0.75 + население * 0.25 // за вычетом затрат, расходуемых в течение хода на модернизацию кораблей func (c *Cache) PlanetProductionCapacity(planetNumber uint) float64 { p := c.MustPlanet(planetNumber) var busyResources float64 for sg := range c.shipGroupsInUpgrade(p.Number) { busyResources += sg.StateUpgrade.Cost() } return p.ProductionCapacity() - busyResources } func (c *Cache) ProduceShips() { for pl := range c.g.Map.Planet { p := c.MustPlanet(c.g.Map.Planet[pl].Number) if p.Owner == uuid.Nil || p.Production.Type != game.ProductionShip { continue } ri := c.RaceIndex(p.Owner) st := c.MustShipType(ri, *p.Production.SubjectID) ships := ProduceShip(p, st.EmptyMass()) if ships > 0 { if err := c.CreateShips(ri, st.Name, p.Number, ships); err != nil { panic(fmt.Sprintf("ProduceShips: CreateShip: %s", err)) } } } } // Internal funcs func (c *Cache) putPopulation(pn uint, v float64) { c.MustPlanet(pn).Population = v } func (c *Cache) putColonists(pn uint, v float64) { c.MustPlanet(pn).Colonists = v } func (c *Cache) putMaterial(pn uint, v float64) { c.MustPlanet(pn).Material = v } func ProduceShip(p *game.Planet, shipMass float64) int { productionAvailable := p.ProductionCapacity() if productionAvailable <= 0 { return 0 } CAP_perShip := shipMass / p.Resources productionForMass := shipMass * 10. ships := 0 flZero := 0. p.Production.Progress = &flZero for productionAvailable > 0 { var productionExtraCAP float64 if CAP_deficit := p.Capital - CAP_perShip; CAP_deficit < 0 { productionExtraCAP = -CAP_deficit } productionCost := productionExtraCAP + productionForMass if productionAvailable >= productionCost { productionAvailable -= productionCost p.Capital = p.Capital - (CAP_perShip - productionExtraCAP) ships++ } else { progress := productionAvailable / productionCost productionAvailable -= productionCost * progress p.Production.Progress = &progress break } } return ships }