Limit how much the fleet can carry #56
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The fleet hold is currently unbounded -
GameState.fleet_inventoryis constructed asINVENTORY.new()with no capacity, so a lone interceptor can carry an unlimited amount of everything. Cap it, and make the cap depend on fleet composition: only basic vessels haul cargo, combat specialists do not.Inventoryalready supports this natively (total_capacity,space_for(), and clamping insidedeposit/set_amount/transfer_to), so most of this ticket is wiring rather than new mechanics.Scope
VesselDefinition.base_statsgains acargo_capacityentry per vessel:GameState.fleet_cargo_capacity() -> int: sumvessel_stat(type, "cargo_capacity")over the current fleet composition. A vessel contributes only if its definition declares thecargocapability, so the rule is data-driven rather than a list of vessel ids.fleet_inventory.total_capacitywhenever composition changes:Fleet.spawn_ships(),Fleet.add_ship(),Fleet.store_ship(),Fleet._on_ship_destroyed(), and the surface/space scene-swap restore inPlanetSurfaceController/PlanetAtmosphereController. Put the recompute in oneGameStatemethod that all of those call.GameState.collect_cargo()(rock pickup) must respect the cap: if the hold is full, the rock stays on the surface rather than being consumed for nothing. CheckRock.gd's collection path - the pickup should only free itself when something was actually accepted.12/40on the fleet column) so a full hold is legible rather than mysterious. If #49 has landed, add it there; otherwise add it to the existing fleet counters.Out of scope
The carrier vessel (#57), which raises the ceiling.
Acceptance criteria
Tests
tests/test_fleet_cargo_limit.gd(bash tests/run_tests.sh cargo), plusrun_tests.sh transferandrun_tests.sh fleetfor regressions.Repo notes (read before starting)
/storage/emulated/0/GodotProjects/SpaceshipTest.touchon it. Android FUSE does not bump mtime under~/storage/shared, and Godot's import cache will silently keep serving the old version, making a correct fix look like it did nothing.bash tests/run_tests.sh <substring>; full suite with no argument. Tests areSceneTreescripts printingTEST_NAME: PASS/TEST_NAME: FAIL (reason)and callingquit(0|1)- copy the shape oftests/test_capture_points.gd.bash tests/screenshot.sh <res://Scene.tscn> <out.png> <frames> 720x1280. Use few frames (~15); the software rasterizer is slow.--headlessrenders nothing at all, so it cannot validate visuals.NeonDraw/NeonPiece; do not introduce a different visual idiom.*Definitionresources plus capability component scenes. Do not addif type == "..."branches.FileAccess, nouser://).GameStateis an in-memory autoload. Do not invent a save system.Traps that have already cost hours on this codebase
const X = preload(...)in a test file that is itself the-sentry point. It evaluates during that file's own early compilation, which can run before autoloads are registered; any script it pulls in that referencesGameStateinside a function body then fails to compile withIdentifier not found: GameState. Usevar X: Scriptplusload()inside_initialize()instead. See the header comment intests/test_capture_points.gd.[]to a typedArray[Resource]export from another script fails at runtime - the compiler only sees the target asResourceand cannot confirm the element type. Leave the declared default alone, or build the typed array inside the owning script.Fleet.spawn_ships(0, ...)does not empty the fleet - it falls back to one default interceptor by design. Clearfleet.shipsexplicitly if a test or preview needs genuinely zero ships.ship.positionwithout also settingship.move_targetletsShip._physics_processsteer it straight back toward the stale target within a few frames.SceneTreetest's own_process()clock under device load, even with--fixed-fps 60. Gate waits on the system's own state (a component's timer or flag), not onelapsed >= duration.queue_free()s itself (projectiles, shells) must not be re-read inside a per-frame wait condition - cache the scalar you need before the wait._screenshot_tool.gd's_initialize(), so their_ready()races the game scene's own_ready(). Do setup on the first_process()tick, andget_tree().paused = trueonce everything is placed if the scene must hold still.Implemented in
f040327.The fleet hold is now finite and driven by fleet composition, instead of unbounded.
cargo_capacity+ a"cargo"capability added toInterceptorVesselDefinition.tres(20, plusVesselCargoComponent.tscnin itscomponent_scenesso the capability is backed by a real composed component, per the no-type-switch rule -test_vessel_composition.gdenforces every declared capability actually resolves to a child component, and caught me skipping that on the first pass). Shield vessel and gas bomber declare no"cargo"capability, so they contribute nothing - near-zero capacity for a combat-only fleet, verified directly. The transporter already hadcargo_capacity/"cargo"for its own per-trip load limit, but it's never fleet-eligible, so it was already structurally excluded from ever affecting the fleet hold.GameState.fleet_cargo_capacity(vessel_types)sumscargo_capacityover ships with the"cargo"capability (data-driven, no vessel-id list);GameState.sync_fleet_cargo_capacity(vessel_types)applies it tofleet_inventory.total_capacity.Fleet.gdcalls the sync in the one place composition changes:spawn_ships(),add_ship(),store_ship(),_on_ship_destroyed()- which transitively covers the space<->surface scene-swap restore too, since bothPlanetSurfaceControllerandPlanetAtmosphereControllerrebuild the fleet throughspawn_ships().Rock.gdandGasVentField.gd(the two "fly over it" pickup paths) now check whatcollect_cargo()/collect_gas_cargo()actually accepted (both now return the deposited amount) before consuming their source. A rock over a full hold stays on the surface; a gas vent keeps its undelivered fraction rather than discarding it into a full hold.ResourceReadout) now shows the live total, e.g.FLEET HOLD 27/40, reusing the sibling caption Label rather than adding a new node.Tests:
tests/test_fleet_cargo_limit.gd(data-driven capacity by composition, a full hold refusing a pickup without consuming the rock, losing a ship dropping the ceiling without deleting cargo, a clamped transfer reporting the actual amount moved, capacity surviving the space->surface swap).run_tests.sh cargo(3/3),run_tests.sh fleet(3/3),run_tests.sh transfer(2/2, after fixingtest_resource_transfer.gd- it was pushing more ore through a 2-ship fleet than 40 capacity allows; bumped it to spawn the largest fleet every unlocked doctrine permits, since that test is about the transfer UI, not the cap). Full suite: 83/83.Screenshot taken and visually confirmed: 2 interceptors + 1 shield vessel (40 capacity total), hold at 27, HUD caption reads
FLEET HOLD 27/40clearly, no overlap with the resource rows below it.