Add a free-costs sandbox tool

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
This commit is contained in:
Loïc Wisniewski 2026-09-04 22:34:36 +02:00
commit 571f01dc31
5 changed files with 136 additions and 18 deletions

View file

@ -5,6 +5,7 @@ extends SceneTree
# the live fleet, not only GameState bookkeeping.
const VESSEL_DEFINITIONS = preload("res://vessels/VesselDefinitions.gd")
const BUILDING_DEFINITIONS = preload("res://buildings/BuildingDefinitions.gd")
var state: Node
var scene: Node
@ -73,6 +74,39 @@ func _process(delta: float) -> bool:
return _fail("unlimited mode did not replenish research points")
print("skill unlock and unlimited resources operate on persistent GameState")
# --- #202: every research and production price reads zero -------
# Unlimited resources goes off first: a bottomless bank and a free
# price are indistinguishable while both are on, and it is the
# price this checks.
menu.unlimited_button.pressed.emit()
if state.dev_unlimited_resources:
return _fail("the unlimited-resources toggle did not turn back off")
var charged: int = _priced_things()
if charged == 0:
return _fail("nothing in the game carries a price to make free")
menu.free_costs_button.pressed.emit()
if not state.dev_free_costs:
return _fail("the free-costs toggle did not turn on")
if _priced_things() != 0:
return _fail("%d prices survived the free-costs toggle" % _priced_things())
# The accessors are what every purchase, queue and price label
# reads, so an empty bank must now afford anything.
for resource_type in state.resource_types():
state.bank_inventory.set_amount(resource_type, 0)
state.fleet_inventory.set_amount(resource_type, 0)
state.research_points = 0
for definition in BUILDING_DEFINITIONS.all():
if not state.can_afford_resources(state.building_costs(String(definition.definition_id))):
return _fail("a broke fleet could not afford the free %s" % definition.definition_id)
print("free costs zeroed every research and production price")
menu.free_costs_button.pressed.emit()
if state.dev_free_costs:
return _fail("the free-costs toggle did not turn back off")
if _priced_things() != charged:
return _fail("turning free costs off did not restore every price")
print("prices come back exactly as they were when the toggle goes off")
var wave_before: int = state.wave_number
menu.wave_button.pressed.emit()
if menu.visible:
@ -81,7 +115,7 @@ func _process(delta: float) -> bool:
return _fail("the dev menu did not retain the immediate wave tool")
state.reset_new_game()
if state.dev_mode or state.dev_unlimited_resources:
if state.dev_mode or state.dev_unlimited_resources or state.dev_free_costs:
return _fail("dev cheats leaked into a normal new game")
print("TEST_DEV_MENU: PASS")
quit(0)
@ -89,6 +123,30 @@ func _process(delta: float) -> bool:
return false
# How many prices in the whole game are currently non-zero: skills, vessel
# unlocks and upgrades on the research side, buildings and vessel production
# on the other. Counted rather than sampled so a price added later is covered
# here without anyone remembering to add it.
func _priced_things() -> int:
var total: int = 0
for node in GameState.SKILL_TREE:
var id: String = String(node.id)
total += state.skill_costs(id).size() + state.skill_research_point_cost(id)
for definition in BUILDING_DEFINITIONS.all():
total += state.building_costs(String(definition.definition_id)).size()
for definition in VESSEL_DEFINITIONS.all():
total += state.product_costs(String(definition.definition_id)).size()
var unlock_id: String = String(definition.unlock_id)
if unlock_id != "":
total += state.research_costs(unlock_id).size() \
+ state.research_point_cost(unlock_id)
for upgrade in definition.upgrades:
var upgrade_id: String = String(upgrade.upgrade_id)
total += state.upgrade_cost(upgrade_id) \
+ state.research_point_cost(upgrade_id)
return total
func _fail(message: String) -> bool:
print("TEST_DEV_MENU: FAIL (%s)" % message)
quit(1)