ui/phase-20: ship-group inspector actions
Eight ship-group operations land on the inspector behind a single inline-form panel: split, send, load, unload, modernize, dismantle, transfer, join fleet. Each action either appends a typed command to the local order draft or surfaces a tooltip explaining the disabled state. Partial-ship operations emit an implicit breakShipGroup command before the targeted action so the engine sees a clean (Break, Action) pair on the wire. `pkg/calc.BlockUpgradeCost` migrates from `game/internal/controller/ship_group_upgrade.go` so the calc bridge can wrap a pure pkg/calc formula; the controller now imports it. The bridge surfaces the function as `core.blockUpgradeCost`, which the inspector calls once per ship block to render the modernize cost preview. `GameReport.otherRaces` is decoded from the report's player block (non-extinct, ≠ self) and feeds the transfer-to-race picker. The planet inspector's stationed-ship rows become clickable for own groups so the actions panel is reachable from the standard click flow (the renderer continues to hide on-planet groups). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -55,3 +55,13 @@ func CargoCapacity(cargo, cargoTech float64) float64 {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -171,6 +171,31 @@ func TestCarryingMassParity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockUpgradeCostParity(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
blockMass float64
|
||||
currentTech float64
|
||||
targetTech float64
|
||||
}{
|
||||
{"zero_block_mass", 0, 1, 2},
|
||||
{"target_equal_to_current", 5, 2, 2},
|
||||
{"target_below_current", 5, 2, 1},
|
||||
{"doubling_tech_on_mass_5", 5, 1, 2},
|
||||
{"partial_step_2_to_2_5", 5, 2, 2.5},
|
||||
{"high_tech_to_higher_tech", 12, 4, 6},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
want := source.BlockUpgradeCost(c.blockMass, c.currentTech, c.targetTech)
|
||||
got := bridge.BlockUpgradeCost(c.blockMass, c.currentTech, c.targetTech)
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDesignerPreviewComposition exercises the exact composition the
|
||||
// ship-class designer performs: empty mass, full-load mass via
|
||||
// CarryingMass(CargoCapacity), max speed at empty, and range at full
|
||||
|
||||
Reference in New Issue
Block a user