Files
galaxy-game/ui/core/calc/ship.go
T
Ilia Denisov 9ae7b88b89
Tests · UI / test (push) Successful in 2m14s
Tests · Go / test (push) Successful in 2m25s
feat(ui): Phase 30 ship-class calculator with goal-seek and reach circles
Fuse the standalone ship-class designer (Phases 17/18) into a sidebar calculator: live mass/speed/attack/defence/bombing results, a planet build-rate readout, single-target goal-seek, a modernization-cost mode, and auto reach circles on the map for the selected planet.

pkg/calc becomes the single source for the new math (no mirroring): extract BombingPower from the engine model and the per-turn ship-production loop from controller.ProduceShip into pkg/calc (engine now delegates), and add inverse goal-seek solvers in pkg/calc/solve.go. Thin-bridge the combat, planet-build, and solver functions through ui/core/calc + ui/wasm and rebuild core.wasm.

Remove the standalone designer view/route; the ship-classes table and the view/bottom menus open the calculator via a shared request store.

Docs: rewrite ui/PLAN.md Phase 30, adjust Phase 34 (realistic forecast + CAP/COL ownership), add ui/docs/calculator-ux.md, extend calc-bridge.md, fix navigation.md; remove ui/CALCULATOR.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:04:07 +02:00

91 lines
4.0 KiB
Go

// Package calc is the WASM-side bridge over `galaxy/calc`'s ship math.
// Each function is a one-line passthrough: signatures match the
// underlying `pkg/calc/ship.go` exactly so the bridge contains zero
// math beyond the call. Wrapping `pkg/calc` here keeps the JS/Go
// surface in one file and lets the canonical math live in a single
// upstream package shared with the engine.
package calc
import "galaxy/calc"
// DriveEffective wraps `calc.DriveEffective` (`pkg/calc/ship.go`):
// effective drive power equals the ship's drive block multiplied by
// the player's drive tech level.
func DriveEffective(drive, driveTech float64) float64 {
return calc.DriveEffective(drive, driveTech)
}
// EmptyMass wraps `calc.EmptyMass` (`pkg/calc/ship.go`): mass of the
// ship without cargo. Returns ok == false when the weapons/armament
// pair is invalid (one zero, the other non-zero).
func EmptyMass(drive, weapons float64, armament uint, shields, cargo float64) (float64, bool) {
return calc.EmptyMass(drive, weapons, armament, shields, cargo)
}
// WeaponsBlockMass wraps `calc.WeaponsBlockMass` (`pkg/calc/ship.go`):
// mass of the weapons sub-block. Returns ok == false on the same
// invalid pairing as EmptyMass.
func WeaponsBlockMass(weapons float64, armament uint) (float64, bool) {
return calc.WeaponsBlockMass(weapons, armament)
}
// FullMass wraps `calc.FullMass` (`pkg/calc/ship.go`): empty mass plus
// the mass of the carried cargo.
func FullMass(emptyMass, carryingMass float64) float64 {
return calc.FullMass(emptyMass, carryingMass)
}
// Speed wraps `calc.Speed` (`pkg/calc/ship.go`): light-years per turn,
// equal to effective drive times 20 divided by the ship's full mass.
// Zero when fullMass is non-positive.
func Speed(driveEffective, fullMass float64) float64 {
return calc.Speed(driveEffective, fullMass)
}
// CargoCapacity wraps `calc.CargoCapacity` (`pkg/calc/ship.go`):
// hold capacity of one ship in cargo units, scaled by the player's
// cargo tech.
func CargoCapacity(cargo, cargoTech float64) float64 {
return calc.CargoCapacity(cargo, cargoTech)
}
// CarryingMass wraps `calc.CarryingMass` (`pkg/calc/ship.go`): mass of
// a payload of `load` cargo units at the player's cargo tech. Used by
// the designer preview to derive full-load mass from CargoCapacity.
func CarryingMass(load, cargoTech float64) float64 {
return calc.CarryingMass(load, cargoTech)
}
// BlockUpgradeCost wraps `calc.BlockUpgradeCost` (`pkg/calc/ship.go`):
// production cost of upgrading a single ship block from currentBlockTech
// to targetBlockTech. Returns 0 when blockMass is zero or the target is
// not above the current level. Used by the ship-group inspector's
// modernize cost preview, with each of the four blocks (drive, weapons,
// shields, cargo) priced through a separate call.
func BlockUpgradeCost(blockMass, currentBlockTech, targetBlockTech float64) float64 {
return calc.BlockUpgradeCost(blockMass, currentBlockTech, targetBlockTech)
}
// EffectiveAttack wraps `calc.EffectiveAttack` (`pkg/calc/ship.go`):
// combat attack power of a ship, equal to its weapons block times the
// player's weapons tech.
func EffectiveAttack(weapons, weaponsTech float64) float64 {
return calc.EffectiveAttack(weapons, weaponsTech)
}
// EffectiveDefence wraps `calc.EffectiveDefence` (`pkg/calc/ship.go`):
// combat defence power of a ship, its shields block times the player's
// shields tech, normalised by the cube root of full mass (bigger hulls
// defend worse per shield point). Zero when defendingFullMass ≤ 0.
func EffectiveDefence(defendingShields, defendingShieldsTech, defendingFullMass float64) float64 {
return calc.EffectiveDefence(defendingShields, defendingShieldsTech, defendingFullMass)
}
// BombingPower wraps `calc.BombingPower` (`pkg/calc/ship.go`): the
// planet-bombing power of number ships with the given weapons block,
// weapons tech, and armament. The calculator passes number = 1 for a
// per-ship reading.
func BombingPower(weapons, weaponsTech, armament, number float64) float64 {
return calc.BombingPower(weapons, weaponsTech, armament, number)
}