Cap fleet cargo capacity by composition (#56)
The fleet hold was unbounded - GameState.fleet_inventory had no total_capacity, so a lone ship could carry unlimited resources. Capacity is now data-driven: only vessels declaring the "cargo" capability contribute their cargo_capacity stat (interceptor: 20; shield vessel and gas bomber: 0, unarmed transporters are never in the fleet at all). Fleet.gd recomputes it in the one place composition can change (spawn_ships/add_ship/store_ship/_on_ship_destroyed), which also covers the space<->surface scene-swap restore since that goes through spawn_ships(). Rock.gd and GasVentField.gd (the two "fly over it to collect" pickup paths) now check what collect_cargo()/collect_gas_cargo() actually accepted before consuming their source, instead of assuming an unlimited hold. The HUD's FLEET HOLD caption shows the live total/cap. Also fixes test_resource_transfer.gd, which pushed more ore through a 2-ship fleet than the new cap allows - it now spawns the largest fleet every unlocked doctrine permits, since that test is about the transfer UI rather than the cap itself (test_fleet_cargo_limit.gd owns that).
This commit is contained in:
parent
cbe07bbb55
commit
f040327523
11 changed files with 250 additions and 12 deletions
4
Fleet.gd
4
Fleet.gd
|
|
@ -80,6 +80,7 @@ func spawn_ships(count: int, center: Vector2 = Vector2.ZERO,
|
||||||
ship.destroyed.connect(_on_ship_destroyed.bind(ship))
|
ship.destroyed.connect(_on_ship_destroyed.bind(ship))
|
||||||
|
|
||||||
fleet_target = center
|
fleet_target = center
|
||||||
|
GameState.sync_fleet_cargo_capacity(vessel_types())
|
||||||
|
|
||||||
|
|
||||||
func vessel_types() -> PackedStringArray:
|
func vessel_types() -> PackedStringArray:
|
||||||
|
|
@ -138,6 +139,7 @@ func add_ship(vessel_type: String = GameState.VESSEL_INTERCEPTOR) -> bool:
|
||||||
if ship.has_signal("destroyed"):
|
if ship.has_signal("destroyed"):
|
||||||
ship.destroyed.connect(_on_ship_destroyed.bind(ship))
|
ship.destroyed.connect(_on_ship_destroyed.bind(ship))
|
||||||
ship.set_move_target(fleet_target + offset)
|
ship.set_move_target(fleet_target + offset)
|
||||||
|
GameState.sync_fleet_cargo_capacity(vessel_types())
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -155,6 +157,7 @@ func store_ship(index: int) -> String:
|
||||||
formation_offsets.remove_at(index)
|
formation_offsets.remove_at(index)
|
||||||
ship.queue_free()
|
ship.queue_free()
|
||||||
apply_formation()
|
apply_formation()
|
||||||
|
GameState.sync_fleet_cargo_capacity(vessel_types())
|
||||||
return vessel_type
|
return vessel_type
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -273,6 +276,7 @@ func _on_ship_destroyed(ship: Node) -> void:
|
||||||
if idx != -1:
|
if idx != -1:
|
||||||
ships.remove_at(idx)
|
ships.remove_at(idx)
|
||||||
formation_offsets.remove_at(idx)
|
formation_offsets.remove_at(idx)
|
||||||
|
GameState.sync_fleet_cargo_capacity(vessel_types())
|
||||||
|
|
||||||
|
|
||||||
func get_centroid() -> Vector2:
|
func get_centroid() -> Vector2:
|
||||||
|
|
|
||||||
40
GameState.gd
40
GameState.gd
|
|
@ -57,11 +57,14 @@ var fleet_gas_cargo: int:
|
||||||
get: return fleet_inventory.amount(RESOURCE_GAS)
|
get: return fleet_inventory.amount(RESOURCE_GAS)
|
||||||
set(value): fleet_inventory.set_amount(RESOURCE_GAS, value)
|
set(value): fleet_inventory.set_amount(RESOURCE_GAS, value)
|
||||||
|
|
||||||
func collect_cargo(amount: int = 1) -> void:
|
# Both return the amount actually accepted (#56: the fleet hold now has a
|
||||||
fleet_inventory.deposit(RESOURCE_ORE, amount)
|
# finite total_capacity), so a collector can tell a full hold apart from a
|
||||||
|
# successful pickup instead of assuming everything offered was taken.
|
||||||
|
func collect_cargo(amount: int = 1) -> int:
|
||||||
|
return fleet_inventory.deposit(RESOURCE_ORE, amount)
|
||||||
|
|
||||||
func collect_gas_cargo(amount: int = 1) -> void:
|
func collect_gas_cargo(amount: int = 1) -> int:
|
||||||
fleet_inventory.deposit(RESOURCE_GAS, amount)
|
return fleet_inventory.deposit(RESOURCE_GAS, amount)
|
||||||
|
|
||||||
|
|
||||||
func resource_types() -> PackedStringArray:
|
func resource_types() -> PackedStringArray:
|
||||||
|
|
@ -90,6 +93,16 @@ func fleet_resource(resource_type: String) -> int:
|
||||||
return fleet_inventory.amount(resource_type)
|
return fleet_inventory.amount(resource_type)
|
||||||
|
|
||||||
|
|
||||||
|
# #56: total across every resource type in the hold, and the combined cap
|
||||||
|
# that limits it - a HUD readout wants both, not any one resource's amount.
|
||||||
|
func fleet_cargo_used() -> int:
|
||||||
|
return fleet_inventory.total()
|
||||||
|
|
||||||
|
|
||||||
|
func fleet_cargo_total_capacity() -> int:
|
||||||
|
return fleet_inventory.total_capacity
|
||||||
|
|
||||||
|
|
||||||
func _inventory_for_record(record: Dictionary) -> RefCounted:
|
func _inventory_for_record(record: Dictionary) -> RefCounted:
|
||||||
var inventory: RefCounted = record.get("inventory") as RefCounted
|
var inventory: RefCounted = record.get("inventory") as RefCounted
|
||||||
if inventory == null:
|
if inventory == null:
|
||||||
|
|
@ -633,6 +646,25 @@ func shield_recharge_time() -> float:
|
||||||
var definition: Resource = VESSEL_DEFINITIONS.with_capability("shield")
|
var definition: Resource = VESSEL_DEFINITIONS.with_capability("shield")
|
||||||
return vessel_stat(definition.definition_id, "shield_recharge")
|
return vessel_stat(definition.definition_id, "shield_recharge")
|
||||||
|
|
||||||
|
# #56: sums cargo_capacity over ships that actually declare the "cargo"
|
||||||
|
# capability, so combat specialists (no capability, whatever their stat
|
||||||
|
# dict says) contribute nothing - data-driven rather than a vessel-id list.
|
||||||
|
# Takes the composition as an argument rather than reading it off a live
|
||||||
|
# Fleet node: GameState has no fixed reference to "the" Fleet (space and a
|
||||||
|
# planet surface each own a separate one), so the caller passes its own
|
||||||
|
# Fleet.vessel_types().
|
||||||
|
func fleet_cargo_capacity(vessel_types: PackedStringArray) -> int:
|
||||||
|
var total: int = 0
|
||||||
|
for vessel_type in vessel_types:
|
||||||
|
if vessel_has_capability(vessel_type, "cargo"):
|
||||||
|
total += int(vessel_stat(vessel_type, "cargo_capacity"))
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
func sync_fleet_cargo_capacity(vessel_types: PackedStringArray) -> void:
|
||||||
|
fleet_inventory.total_capacity = fleet_cargo_capacity(vessel_types)
|
||||||
|
|
||||||
|
|
||||||
func transporter_load_limit() -> int:
|
func transporter_load_limit() -> int:
|
||||||
var definition: Resource = VESSEL_DEFINITIONS.with_capability("transport")
|
var definition: Resource = VESSEL_DEFINITIONS.with_capability("transport")
|
||||||
return int(vessel_stat(definition.definition_id, "cargo_capacity"))
|
return int(vessel_stat(definition.definition_id, "cargo_capacity"))
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,11 @@ func _process(delta: float) -> void:
|
||||||
if fleet.get_centroid().distance_to(vent) < 90.0:
|
if fleet.get_centroid().distance_to(vent) < 90.0:
|
||||||
progress += delta
|
progress += delta
|
||||||
if progress >= 1.0:
|
if progress >= 1.0:
|
||||||
GameState.collect_gas_cargo(int(progress))
|
# #56: a full fleet hold rejects the deposit - keep the
|
||||||
progress = fmod(progress, 1.0)
|
# undelivered whole units in progress rather than discarding
|
||||||
|
# them, so gas is only ever lost by not gathering it.
|
||||||
|
var whole: int = int(progress)
|
||||||
|
progress -= float(GameState.collect_gas_cargo(whole))
|
||||||
return
|
return
|
||||||
progress = 0.0
|
progress = 0.0
|
||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
[gd_resource type="Resource" script_class="VesselDefinition" load_steps=7 format=3]
|
[gd_resource type="Resource" script_class="VesselDefinition" load_steps=8 format=3]
|
||||||
[ext_resource type="Script" path="res://VesselDefinition.gd" id="1"]
|
[ext_resource type="Script" path="res://VesselDefinition.gd" id="1"]
|
||||||
[ext_resource type="PackedScene" path="res://VesselWeaponComponent.tscn" id="2"]
|
[ext_resource type="PackedScene" path="res://VesselWeaponComponent.tscn" id="2"]
|
||||||
[ext_resource type="Resource" path="res://InterceptorDamageUpgrade.tres" id="3"]
|
[ext_resource type="Resource" path="res://InterceptorDamageUpgrade.tres" id="3"]
|
||||||
[ext_resource type="Resource" path="res://InterceptorRateUpgrade.tres" id="4"]
|
[ext_resource type="Resource" path="res://InterceptorRateUpgrade.tres" id="4"]
|
||||||
[ext_resource type="Resource" path="res://InterceptorRangeUpgrade.tres" id="5"]
|
[ext_resource type="Resource" path="res://InterceptorRangeUpgrade.tres" id="5"]
|
||||||
[ext_resource type="Resource" path="res://InterceptorSpeedUpgrade.tres" id="6"]
|
[ext_resource type="Resource" path="res://InterceptorSpeedUpgrade.tres" id="6"]
|
||||||
|
[ext_resource type="PackedScene" path="res://VesselCargoComponent.tscn" id="7"]
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
definition_id = "interceptor"
|
definition_id = "interceptor"
|
||||||
|
|
@ -15,7 +16,7 @@ build_time = 10.0
|
||||||
icon = 2
|
icon = 2
|
||||||
glow_color = Color(0.25, 0.85, 1, 1)
|
glow_color = Color(0.25, 0.85, 1, 1)
|
||||||
fleet_eligible = true
|
fleet_eligible = true
|
||||||
capabilities = PackedStringArray("weapon")
|
capabilities = PackedStringArray("weapon", "cargo")
|
||||||
component_scenes = Array[PackedScene]([ExtResource("2")])
|
component_scenes = Array[PackedScene]([ExtResource("2"), ExtResource("7")])
|
||||||
base_stats = {"damage": 1.0, "fire_cooldown": 0.5, "fire_range": 320.0, "max_speed": 460.0, "acceleration": 450.0, "delivery_speed": 180.0}
|
base_stats = {"damage": 1.0, "fire_cooldown": 0.5, "fire_range": 320.0, "max_speed": 460.0, "acceleration": 450.0, "delivery_speed": 180.0, "cargo_capacity": 20.0}
|
||||||
upgrades = Array[Resource]([ExtResource("3"), ExtResource("4"), ExtResource("5"), ExtResource("6")])
|
upgrades = Array[Resource]([ExtResource("3"), ExtResource("4"), ExtResource("5"), ExtResource("6")])
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,19 @@ const GLYPH_GAP: float = 8.0
|
||||||
var _banked_numbers: Dictionary = {}
|
var _banked_numbers: Dictionary = {}
|
||||||
var _fleet_numbers: Dictionary = {}
|
var _fleet_numbers: Dictionary = {}
|
||||||
|
|
||||||
|
# #56: the fleet hold now has a finite total capacity. Rather than a new
|
||||||
|
# node, this reuses the existing "FLEET HOLD" caption Label that both Main
|
||||||
|
# and PlanetSurface already place as a sibling - get_node_or_null so a
|
||||||
|
# scene that happens to embed ResourceReadout without that sibling (none
|
||||||
|
# does today) degrades to just not showing a limit, instead of erroring.
|
||||||
|
@onready var _fleet_caption: Label = get_node_or_null("../FleetCaption")
|
||||||
|
var _fleet_caption_base_text: String = ""
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
if _fleet_caption != null:
|
||||||
|
_fleet_caption_base_text = _fleet_caption.text
|
||||||
var y: float = 0.0
|
var y: float = 0.0
|
||||||
for resource_type in GameState.resource_types():
|
for resource_type in GameState.resource_types():
|
||||||
var banked_color: Color = GameState.resource_color(resource_type)
|
var banked_color: Color = GameState.resource_color(resource_type)
|
||||||
|
|
@ -81,6 +91,11 @@ func fleet_number(resource_type: String) -> NeonNumber:
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
|
if _fleet_caption != null:
|
||||||
|
var capacity: int = GameState.fleet_cargo_total_capacity()
|
||||||
|
_fleet_caption.text = "%s %d/%d" % [_fleet_caption_base_text, GameState.fleet_cargo_used(), capacity] \
|
||||||
|
if capacity >= 0 else _fleet_caption_base_text
|
||||||
|
|
||||||
for resource_type in GameState.resource_types():
|
for resource_type in GameState.resource_types():
|
||||||
var banked: NeonNumber = _banked_numbers.get(resource_type)
|
var banked: NeonNumber = _banked_numbers.get(resource_type)
|
||||||
if banked != null:
|
if banked != null:
|
||||||
|
|
|
||||||
1
ResourceReadout.gd.uid
Normal file
1
ResourceReadout.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://ctbtddn7ewmvv
|
||||||
5
Rock.gd
5
Rock.gd
|
|
@ -82,7 +82,10 @@ func _on_area_entered(area: Area2D) -> void:
|
||||||
if area.is_in_group("player_ships"):
|
if area.is_in_group("player_ships"):
|
||||||
# Into the fleet's hold, not the bank (#14). It only counts as
|
# Into the fleet's hold, not the bank (#14). It only counts as
|
||||||
# spendable once the fleet has flown it back to the mothership.
|
# spendable once the fleet has flown it back to the mothership.
|
||||||
GameState.collect_cargo(1)
|
# #56: the hold has a finite total_capacity now - a full fleet must
|
||||||
|
# leave the rock behind rather than despawning it for nothing.
|
||||||
|
if GameState.collect_cargo(1) <= 0:
|
||||||
|
return
|
||||||
collected.emit()
|
collected.emit()
|
||||||
queue_free()
|
queue_free()
|
||||||
|
|
||||||
|
|
|
||||||
19
tests/_fleet_cargo_limit_preview.gd
Normal file
19
tests/_fleet_cargo_limit_preview.gd
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
extends Node2D
|
||||||
|
|
||||||
|
# Preview, not a test - the fleet-hold cap shown on the FLEET HOLD caption.
|
||||||
|
#
|
||||||
|
# bash tests/screenshot.sh res://tests/_fleet_cargo_limit_preview.tscn /tmp/cargo_limit.png 15 720x1280
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
GameState.buildings.clear()
|
||||||
|
GameState.extractors.clear()
|
||||||
|
GameState.rocks = 200
|
||||||
|
GameState.gas = 30
|
||||||
|
GameState.fleet_cargo = 0
|
||||||
|
var scene: Node = load("res://Main.tscn").instantiate()
|
||||||
|
add_child(scene)
|
||||||
|
var fleet: Node = scene.get_node("Fleet")
|
||||||
|
fleet.spawn_ships(3, Vector2.ZERO, PackedStringArray(["interceptor", "interceptor", "shield"]))
|
||||||
|
# Two cargo-capable interceptors (40) + one shield vessel (0) = 40 total.
|
||||||
|
GameState.fleet_cargo = 27
|
||||||
6
tests/_fleet_cargo_limit_preview.tscn
Normal file
6
tests/_fleet_cargo_limit_preview.tscn
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tests/_fleet_cargo_limit_preview.gd" id="1"]
|
||||||
|
|
||||||
|
[node name="FleetCargoLimitPreview" type="Node2D"]
|
||||||
|
script = ExtResource("1")
|
||||||
149
tests/test_fleet_cargo_limit.gd
Normal file
149
tests/test_fleet_cargo_limit.gd
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
# Headless gameplay test: run with
|
||||||
|
# godot --headless -s res://tests/test_fleet_cargo_limit.gd
|
||||||
|
# Forgejo #56: the fleet hold has a finite, composition-driven capacity
|
||||||
|
# instead of being unbounded.
|
||||||
|
#
|
||||||
|
# Covers:
|
||||||
|
# A. capacity is data-driven: only vessels with the "cargo" capability
|
||||||
|
# contribute their cargo_capacity stat - combat specialists (shield,
|
||||||
|
# gas bomber) contribute zero
|
||||||
|
# B. a lone interceptor cannot carry more than its capacity - a further
|
||||||
|
# Rock pickup is refused and the rock is not consumed
|
||||||
|
# C. losing a cargo-capable ship lowers the ceiling immediately without
|
||||||
|
# deleting cargo already held (clamps on the next deposit instead)
|
||||||
|
# D. a transfer into a full hold clamps to the remaining space and
|
||||||
|
# reports the amount actually moved
|
||||||
|
# E. capacity survives the space -> surface scene swap
|
||||||
|
|
||||||
|
var game_state: Node
|
||||||
|
var scene: Node
|
||||||
|
var fleet: Node
|
||||||
|
var stage: int = 0
|
||||||
|
var elapsed: float = 0.0
|
||||||
|
var mothership_id: int = 0
|
||||||
|
var rock: Node
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
game_state = root.get_node("GameState")
|
||||||
|
game_state.buildings.clear()
|
||||||
|
game_state.extractors.clear()
|
||||||
|
game_state.rocks = 500
|
||||||
|
game_state.fleet_cargo = 0
|
||||||
|
mothership_id = game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
||||||
|
scene = load("res://Main.tscn").instantiate()
|
||||||
|
root.add_child(scene)
|
||||||
|
current_scene = scene
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> bool:
|
||||||
|
elapsed += delta
|
||||||
|
match stage:
|
||||||
|
0:
|
||||||
|
if elapsed < 0.3:
|
||||||
|
return false
|
||||||
|
scene.get_node("AsteroidField").set_process(false)
|
||||||
|
scene.get_node("EnemySpawner").set_process(false)
|
||||||
|
fleet = scene.get_node("Fleet")
|
||||||
|
|
||||||
|
# --- A. capacity is data-driven ---------------------------------
|
||||||
|
fleet.spawn_ships(1, Vector2.ZERO, PackedStringArray(["interceptor"]))
|
||||||
|
if game_state.fleet_cargo_total_capacity() != 20:
|
||||||
|
return _fail("one interceptor: capacity %d, expected 20" % game_state.fleet_cargo_total_capacity())
|
||||||
|
|
||||||
|
fleet.spawn_ships(2, Vector2.ZERO, PackedStringArray(["shield", "gas_bomber"]))
|
||||||
|
if game_state.fleet_cargo_total_capacity() != 0:
|
||||||
|
return _fail("shield+gas_bomber: capacity %d, expected 0" % game_state.fleet_cargo_total_capacity())
|
||||||
|
print("capacity is data-driven: interceptor=20, shield+gas_bomber=0")
|
||||||
|
|
||||||
|
# --- B. a full hold refuses a pickup, the rock stays ------------
|
||||||
|
fleet.spawn_ships(1, Vector2.ZERO, PackedStringArray(["interceptor"]))
|
||||||
|
game_state.fleet_cargo = 20
|
||||||
|
rock = load("res://Rock.tscn").instantiate()
|
||||||
|
rock.build_shape(1)
|
||||||
|
rock.global_position = fleet.ships[0].global_position
|
||||||
|
scene.add_child(rock)
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 1
|
||||||
|
return false
|
||||||
|
1:
|
||||||
|
if elapsed < 0.3:
|
||||||
|
return false
|
||||||
|
if game_state.fleet_cargo != 20:
|
||||||
|
return _fail("a full hold still absorbed a pickup: hold=%d" % game_state.fleet_cargo)
|
||||||
|
if not is_instance_valid(rock):
|
||||||
|
return _fail("the rock was consumed even though the hold was full")
|
||||||
|
print("full hold (20/20) refused a pickup, the rock remains")
|
||||||
|
|
||||||
|
# --- C. losing a cargo ship drops the ceiling, keeps the cargo --
|
||||||
|
# Mirrors Ship.gd's own death sequence (destroyed.emit() then
|
||||||
|
# queue_free()) rather than just calling the handler, so no
|
||||||
|
# untracked ghost ship is left sitting in the scene.
|
||||||
|
var dying_ship: Node = fleet.ships[0]
|
||||||
|
fleet._on_ship_destroyed(dying_ship)
|
||||||
|
dying_ship.queue_free()
|
||||||
|
if game_state.fleet_cargo_total_capacity() != 0:
|
||||||
|
return _fail("empty fleet: capacity %d, expected 0" % game_state.fleet_cargo_total_capacity())
|
||||||
|
if game_state.fleet_cargo != 20:
|
||||||
|
return _fail("existing cargo was deleted when capacity dropped below it: %d" % game_state.fleet_cargo)
|
||||||
|
print("losing the ship: capacity=0, existing cargo untouched at %d" % game_state.fleet_cargo)
|
||||||
|
|
||||||
|
if game_state.collect_cargo(5) != 0:
|
||||||
|
return _fail("an over-capacity hold accepted a further deposit")
|
||||||
|
print("over-capacity hold clamps further deposits to zero, as documented")
|
||||||
|
|
||||||
|
# --- D. a transfer into a hold with partial room clamps ---------
|
||||||
|
fleet.spawn_ships(2, Vector2.ZERO, PackedStringArray(["interceptor", "interceptor"]))
|
||||||
|
if game_state.fleet_cargo_total_capacity() != 40:
|
||||||
|
return _fail("two interceptors: capacity %d, expected 40" % game_state.fleet_cargo_total_capacity())
|
||||||
|
var bank_before: int = game_state.rocks
|
||||||
|
var moved: int = game_state.transfer_resource_to_fleet(mothership_id, GameState.RESOURCE_ORE, 100)
|
||||||
|
if moved != 20:
|
||||||
|
return _fail("transfer into 20/40 room reported moved=%d, expected 20" % moved)
|
||||||
|
if game_state.fleet_cargo != 40:
|
||||||
|
return _fail("hold after the clamped transfer is %d, expected 40 (full)" % game_state.fleet_cargo)
|
||||||
|
if game_state.rocks != bank_before - 20:
|
||||||
|
return _fail("bank dropped by %d, expected exactly the 20 that moved" % (bank_before - game_state.rocks))
|
||||||
|
print("transfer into 20/40 room clamped to 20, hold now full, bank debited exactly 20")
|
||||||
|
|
||||||
|
# --- E. capacity survives the space -> surface scene swap -------
|
||||||
|
game_state.surviving_ship_count = 2
|
||||||
|
game_state.surviving_vessel_types = PackedStringArray(["interceptor", "interceptor"])
|
||||||
|
game_state.planet_id = "test_fleet_cargo_limit"
|
||||||
|
game_state.planet_name = "Cargo Limit Test"
|
||||||
|
scene.queue_free()
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 2
|
||||||
|
return false
|
||||||
|
2:
|
||||||
|
# A tick for queue_free() to actually leave the tree before adding
|
||||||
|
# the surface scene, matching the shape of every other scene-swap
|
||||||
|
# test in this suite.
|
||||||
|
if elapsed < 0.3:
|
||||||
|
return false
|
||||||
|
scene = load("res://PlanetSurface.tscn").instantiate()
|
||||||
|
root.add_child(scene)
|
||||||
|
current_scene = scene
|
||||||
|
elapsed = 0.0
|
||||||
|
stage = 3
|
||||||
|
return false
|
||||||
|
3:
|
||||||
|
if elapsed < 0.3:
|
||||||
|
return false
|
||||||
|
if game_state.fleet_cargo_total_capacity() != 40:
|
||||||
|
return _fail("capacity after the surface restore is %d, expected 40" % game_state.fleet_cargo_total_capacity())
|
||||||
|
if game_state.fleet_cargo != 40:
|
||||||
|
return _fail("cargo did not survive the scene swap: %d, expected 40" % game_state.fleet_cargo)
|
||||||
|
print("capacity (40) and cargo (40) both survived the space -> surface swap")
|
||||||
|
print("TEST_FLEET_CARGO_LIMIT: PASS")
|
||||||
|
quit(0)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
func _fail(message: String) -> bool:
|
||||||
|
print("TEST_FLEET_CARGO_LIMIT: FAIL (%s)" % message)
|
||||||
|
quit(1)
|
||||||
|
return true
|
||||||
|
|
@ -88,7 +88,12 @@ func _process(delta: float) -> bool:
|
||||||
scene.get_node("AsteroidField").set_process(false)
|
scene.get_node("AsteroidField").set_process(false)
|
||||||
scene.get_node("EnemySpawner").set_process(false)
|
scene.get_node("EnemySpawner").set_process(false)
|
||||||
_clear_hazards()
|
_clear_hazards()
|
||||||
fleet.spawn_ships(2, MOTHERSHIP_AT)
|
# #56 gave the hold a finite, composition-driven capacity. This
|
||||||
|
# test is about the transfer UI, not that cap - test_fleet_cargo_
|
||||||
|
# limit.gd owns the cap itself - so it spawns the largest fleet
|
||||||
|
# every unlocked doctrine allows (8 interceptors x 20 = 160),
|
||||||
|
# comfortably above every hand-picked amount below.
|
||||||
|
fleet.spawn_ships(game_state.max_fleet_size(), MOTHERSHIP_AT)
|
||||||
var field: Node = scene.get_node("BuildingField")
|
var field: Node = scene.get_node("BuildingField")
|
||||||
field.place(GameState.BUILDING_MOTHERSHIP, MOTHERSHIP_AT)
|
field.place(GameState.BUILDING_MOTHERSHIP, MOTHERSHIP_AT)
|
||||||
mothership_id = int(game_state.buildings[-1].id)
|
mothership_id = int(game_state.buildings[-1].id)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue