153 lines
5.6 KiB
Go
153 lines
5.6 KiB
Go
package controller
|
|
|
|
import (
|
|
"github.com/google/uuid"
|
|
e "github.com/iliadenisov/galaxy/internal/error"
|
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
|
)
|
|
|
|
func (c *Controller) UpgradeGroup(raceName string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
|
ri, err := c.Cache.raceIndex(raceName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.Cache.UpgradeGroup(ri, groupIndex, techInput, limitShips, limitLevel)
|
|
}
|
|
|
|
func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
|
|
c.validateRaceIndex(ri)
|
|
sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
|
|
if !ok {
|
|
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
|
}
|
|
st := c.ShipGroupShipClass(sgi)
|
|
|
|
if s := c.ShipGroup(sgi).State(); s != game.StateInOrbit && s != game.StateUpgrade {
|
|
return e.NewShipsBusyError()
|
|
}
|
|
|
|
pl := c.MustPlanet(c.ShipGroup(sgi).Destination)
|
|
if pl.Owner != uuid.Nil && pl.Owner != c.g.Race[ri].ID {
|
|
return e.NewEntityNotOwnedError("planet #%d for upgrade group #%d", pl.Number, groupIndex)
|
|
}
|
|
|
|
upgradeValidTech := map[string]game.Tech{
|
|
game.TechDrive.String(): game.TechDrive,
|
|
game.TechWeapons.String(): game.TechWeapons,
|
|
game.TechShields.String(): game.TechShields,
|
|
game.TechCargo.String(): game.TechCargo,
|
|
game.TechAll.String(): game.TechAll,
|
|
}
|
|
|
|
techRequest, ok := upgradeValidTech[techInput]
|
|
if !ok {
|
|
return e.NewTechUnknownError(techInput)
|
|
}
|
|
|
|
var blockMasses map[game.Tech]float64 = map[game.Tech]float64{
|
|
game.TechDrive: st.DriveBlockMass(),
|
|
game.TechWeapons: st.WeaponsBlockMass(),
|
|
game.TechShields: st.ShieldsBlockMass(),
|
|
game.TechCargo: st.CargoBlockMass(),
|
|
}
|
|
|
|
switch {
|
|
case techRequest != game.TechAll && blockMasses[techRequest] == 0:
|
|
return e.NewUpgradeShipTechNotUsedError()
|
|
case techRequest == game.TechAll && limitLevel != 0:
|
|
return e.NewUpgradeParameterNotAllowedError("tech=%s max_level=%f", techRequest.String(), limitLevel)
|
|
}
|
|
|
|
targetLevel := make(map[game.Tech]float64)
|
|
var sumLevels float64
|
|
for _, tech := range []game.Tech{game.TechDrive, game.TechWeapons, game.TechShields, game.TechCargo} {
|
|
if techRequest == game.TechAll || tech == techRequest {
|
|
if c.g.Race[ri].TechLevel(tech) < limitLevel {
|
|
return e.NewUpgradeTechLevelInsufficientError("%s=%.03f < %.03f", tech.String(), c.g.Race[ri].TechLevel(tech), limitLevel)
|
|
}
|
|
targetLevel[tech] = game.FutureUpgradeLevel(c.g.Race[ri].TechLevel(tech), c.ShipGroup(sgi).TechLevel(tech), limitLevel)
|
|
} else {
|
|
targetLevel[tech] = game.CurrentUpgradingLevel(c.g.ShipGroups[sgi], tech)
|
|
}
|
|
sumLevels += targetLevel[tech]
|
|
}
|
|
|
|
productionCapacity := c.PlanetProductionCapacity(pl.Number)
|
|
if c.ShipGroup(sgi).State() == game.StateUpgrade {
|
|
// to calculate actual capacity we must substract upgrade cost of selected group, if is upgrade state
|
|
productionCapacity -= c.ShipGroup(sgi).StateUpgrade.Cost()
|
|
}
|
|
uc := game.GroupUpgradeCost(*(c.ShipGroup(sgi)), *st, targetLevel[game.TechDrive], targetLevel[game.TechWeapons], targetLevel[game.TechShields], targetLevel[game.TechCargo])
|
|
costForShip := uc.UpgradeCost(1)
|
|
if costForShip == 0 {
|
|
return e.NewUpgradeShipsAlreadyUpToDateError("%#v", targetLevel)
|
|
}
|
|
|
|
shipsToUpgrade := c.ShipGroup(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 = game.GroupUpgradeCost(*(c.ShipGroup(sgi)), *st, targetLevel[game.TechDrive], targetLevel[game.TechWeapons], targetLevel[game.TechShields], targetLevel[game.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 < c.ShipGroup(sgi).Number {
|
|
if c.ShipGroup(sgi).State() == game.StateUpgrade {
|
|
return e.NewUpgradeGroupBreakNotAllowedError("ships=%d max=%d", c.ShipGroup(sgi).Number, maxUpgradableShips)
|
|
}
|
|
nsgi, err := c.breakGroupSafe(ri, groupIndex, maxUpgradableShips)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sgi = nsgi
|
|
}
|
|
|
|
// finally, fill group upgrade prefs
|
|
for tech := range targetLevel {
|
|
if targetLevel[tech] > 0 {
|
|
c.UpgradeShipGroup(sgi, tech, targetLevel[tech])
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Cache) UpgradeShipGroup(sgi int, tech game.Tech, v float64) {
|
|
sg := *(c.ShipGroup(sgi))
|
|
st := c.ShipGroupShipClass(sgi)
|
|
c.g.ShipGroups[sgi] = game.UpgradeGroupPreference(sg, *st, tech, v)
|
|
}
|