spaceship/buildings/GasBurnerBuildingDefinition.tres

38 lines
1.3 KiB
Text
Raw Permalink Normal View History

Garrison laser vessels and gas bombers, on a shared ability seam #105 shipped with interceptors as the only garrisonable vessel, and the reason was a design fault rather than a decision: firing the laser and the gas bomb lived in the player's fleet controllers, so a stationed laser would have held its position and never shot. The shot is now a thing in the world rather than something a controller draws. LaserBeamEffect and GasBombEffect are self-contained, self-animating and self-freeing, added to the tree root - the same shape EnemyBigLaserBeam and ArtilleryShell already had on the enemy side - and they outlive the vessel that fired them. Above them sits one seam, on VesselCooldownFillComponent: ability_range(), ability_ammo_resource(), ability_ammo_cost() and fire_ability(). A beam and a bomb are now the same shape of thing to whoever pulls the trigger, so the garrison AI never names an ability - the test asserts that literally, by reading the AI's own source. A third armed vessel needs to implement the seam and nothing else. Ammunition is the CALLER's business, deliberately: the fleet spends from its hold, a garrison from its outpost's store, and neither concern belongs in the component. A garrison with an empty magazine holds fire rather than falling back on the fleet's hold. Outpost storage: 50 gas per stationed bomber and 50 energy per stationed laser, off the VESSEL definitions, so an outpost never learns which vessel needs what. It is a capacity, not a stock - hub transporters fill it exactly as they fill a gas burner's buffer, because building_logistics_deficit now takes the larger of the definition's static buffer and the garrison's magazine. Every other resource is capped at zero: an outpost is not a depot, and ore parked at one has nothing to do there and no transporter coming for it. Also restores two properties the Godot editor dropped from GasBurnerBuildingDefinition.tres when it re-saved the file: collected_outputs and capabilities. Without the first, nothing ever collects the burner's energy - test_gas_burner caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 19:23:35 +02:00
[gd_resource type="Resource" script_class="BuildingDefinition" format=3 uid="uid://cia6vbju65sbe"]
[ext_resource type="Script" uid="uid://d10s0frtxiw62" path="res://buildings/BuildingDefinition.gd" id="1"]
#84: move 215 root files into a folder hierarchy Everything lived in one flat directory, so a diff gave no clue what area it touched and finding the file you wanted meant knowing its exact name. Twelve folders now, grouped by what a thing IS rather than what type it is - a vessel's definition, its component scenes and its upgrade resources sit together, because that is the set you read when you change one of them. buildings core controllers enemies logistics planets resources shaders surface ui vessels vfx world 332 files moved and every res:// reference rewritten - 306 files touched, including the autoload and main-scene paths in project.godot. Nothing renamed: this is purely where files sit, so the diff is a pile of pure moves and no behaviour is buried in it. ui_theme.tres, icon.svg and web_shell.html stay at the root, where Godot and the export preset expect them. Verified three ways, because a suite alone would not have caught all of it: the full 117 pass, the web export builds clean, and the game renders. The export is why that matters. The FIRST export after the move failed with "Attempt to open script 'res://GameState.gd'" - the old path - while project.godot on disk plainly said res://core/GameState.gd. That is this project's documented Android FUSE mtime trap: Godot served a cached copy of a file whose content had changed. A `touch` fixed it. Nothing in the test suite would have found that, because the suite runs from a copy on a native filesystem - the one place the bug cannot happen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 19:44:14 +02:00
[ext_resource type="PackedScene" path="res://buildings/GasBurnerBuildingVisual.tscn" id="2"]
Garrison laser vessels and gas bombers, on a shared ability seam #105 shipped with interceptors as the only garrisonable vessel, and the reason was a design fault rather than a decision: firing the laser and the gas bomb lived in the player's fleet controllers, so a stationed laser would have held its position and never shot. The shot is now a thing in the world rather than something a controller draws. LaserBeamEffect and GasBombEffect are self-contained, self-animating and self-freeing, added to the tree root - the same shape EnemyBigLaserBeam and ArtilleryShell already had on the enemy side - and they outlive the vessel that fired them. Above them sits one seam, on VesselCooldownFillComponent: ability_range(), ability_ammo_resource(), ability_ammo_cost() and fire_ability(). A beam and a bomb are now the same shape of thing to whoever pulls the trigger, so the garrison AI never names an ability - the test asserts that literally, by reading the AI's own source. A third armed vessel needs to implement the seam and nothing else. Ammunition is the CALLER's business, deliberately: the fleet spends from its hold, a garrison from its outpost's store, and neither concern belongs in the component. A garrison with an empty magazine holds fire rather than falling back on the fleet's hold. Outpost storage: 50 gas per stationed bomber and 50 energy per stationed laser, off the VESSEL definitions, so an outpost never learns which vessel needs what. It is a capacity, not a stock - hub transporters fill it exactly as they fill a gas burner's buffer, because building_logistics_deficit now takes the larger of the definition's static buffer and the garrison's magazine. Every other resource is capped at zero: an outpost is not a depot, and ore parked at one has nothing to do there and no transporter coming for it. Also restores two properties the Godot editor dropped from GasBurnerBuildingDefinition.tres when it re-saved the file: collected_outputs and capabilities. Without the first, nothing ever collects the burner's energy - test_gas_burner caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 19:23:35 +02:00
[ext_resource type="Script" uid="uid://cydp18abylrgd" path="res://buildings/BuildingLogisticsProfile.gd" id="3"]
[sub_resource type="Resource" id="Resource_logistics"]
script = ExtResource("3")
input_buffers = {
"gas": 40
}
collected_outputs = PackedStringArray("energy")
conversion_input = "gas"
conversion_output = "energy"
conversion_rate = 0.2
conversion_yield = 2
Gas burner building and the energy resource (#54) A new building that consumes gas to produce energy, a new resource stored and moved exactly like ore and gas. The resource: EnergyResourceGlyph (a cell/bolt mark) + energy's own warm yellow-green ResourceDefinition, registered in ResourceDefinitions.ORDER/DEFINITIONS and GameState.RESOURCE_ENERGY. Inventory/ResourceBar/ResourceTransferList needed no changes - #49 already made them resource-agnostic, confirmed structurally in the test rather than touched. The building: GasBurnerBuildingVisual (a hexagonal chamber with intake vents in gas's colour and output ports in energy's, so the silhouette itself reads "gas in, energy out"), GasBurnerBuildingDefinition (capabilities=["gas_burner"], its own build-menu icon appended to IconButton.Icon rather than inserted, per the ticket's own warning about renumbering stored scene values), and the matching UI/BuildMenu/GasBurner* nodes in Main.tscn. Gated behind a new "Power Systems" skill-tree node, mirroring how "freight" unlocks the transport hub. Conversion: _advance_gas_burners(), simulated in GameState alongside _advance_production/extractor mining so it keeps running while the player is elsewhere. Withdraws gas and deposits energy in the building's OWN inventory only - never the bank - so it stalls with no local gas exactly like a lab waiting on materials. Logistics: a gas burner's ongoing gas deficit is folded into the existing queued-building-supply dispatch path (same "needs resource X" loop production/research orders already use, extended with gas_burner_gas_deficit()), and a new TO_BUILDING_PICKUP/ TO_MOTHERSHIP_FROM_BUILDING transporter state pair collects its energy to the bank, mirroring the extractor pickup path per the ticket. The existing f5c60f8 priority (queued deliveries outrank collection) holds: the pickup dispatch loop runs last. Tests: new tests/test_gas_burner.gd (resource genericity, stall semantics, research gate, and full delivery+pickup logistics via a real hub), run_tests transporter/resource regressions, and screenshots of the build menu (all 7 buildings) and transfer menu (all 3 resources). Full suite: 81/81. Two real bugs found and fixed along the way, neither gas-burner- specific: - _transporter_inbound_to() matched ANY non-idle transporter with a matching target_id, regardless of purpose. Once pickup states shared target_id with delivery states (both address a building, not an extractor), a transporter delivering gas made the SAME building look "busy" to the unrelated energy-pickup dispatch, starving it even with a second, genuinely free transporter sitting idle at the hub. Split into _transporter_inbound_to() (delivery states only) and a new _transporter_inbound_for_pickup() (pickup states only). - The build menu's 3-row/6-slot layout (MENU_ROW_TOPS + row content sizing) had no room for a 7th building - despite the ticket's own note that no layout changes should be needed. A naive 4th row ran off the bottom of a 720x1280 viewport; fitting one meant both adding a 4th row top AND shrinking the shared button/label/cost sizing so each row's content no longer taller than the compressed row spacing. Caught only by actually screenshotting it - a real build menu, not headless, since --headless renders nothing. 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 12:10:31 +02:00
[resource]
script = ExtResource("1")
definition_id = "gas_burner"
display_name = "Gas Burner"
description = "Burns gas into energy at its own site. Hub transporters keep it stocked with gas and collect the energy it makes."
#138: structures and doctrines cost more than rocks, and more the deeper they are Both halves of the ticket, in the shape vessels have used since gas bombers and laser ships: a definition carries an ore figure and, when it wants more than ore, a dictionary that IS the price. `building_costs()` and `skill_costs()` answer it the way `product_costs()` already did, so there is one question with one answer whatever is being bought, and every payment path now goes through `spend_resources` rather than `spend`. Seven of ten structures and thirteen of seventeen doctrines now want gas, energy or dark matter alongside their rocks, and the totals climb with tree depth - structures 16/31/58/62/96 by depth, doctrines 15/30/73/96/143/258/530. The constraint that shaped the table is REACHABILITY, and it is the reason some prices stayed ore-only. Energy exists only where a gas burner stands, and a gas burner needs Power Systems - so nothing at or before that depth may be priced in energy, or it is a wall with no way through. Dark matter is rarer still and appears once, on the deepest node in the game. The extractor stays rocks-only despite its depth allowing gas, because the quest chain reaches it before it has sent the player for any. test_progressive_costs checks those as properties rather than transcribing the numbers, so rebalancing a price costs nothing while breaking the shape fails: nothing unreachable at its own depth, prices rising with depth, the ore field agreeing with the dictionary (repairs quote the field alone, since a repair drone carries ore and nothing else), and the quest chain never asking for a structure it has not funded. Two things cleaned up on the way: - The per-building cost constants in GameState are gone. Nothing but tests used them, and they were a second copy of a number the definitions already hold - exactly what would drift once a price gained a resource. - research_for_building() returns a display NAME, and the id-keyed half of the tree wants an id. That mix-up is silent - skill_depth() of a name finds no node and answers 0, so a deep structure reads as an ungated one, which is how the first draft of the test let an unreachable price through. research_node_for_building() is the id, and the name is expressed from it. PriceTag no longer rebuilds its labels when re-told the same price; it refreshes the held figures instead. The craft menu re-states prices every frame and the build menu now does too, so that was a dozen Labels being torn down and rebuilt sixty times a second.
2026-08-28 15:30:30 +02:00
cost = 40
Guard the definition catalog against silent .tres property loss Opening the project in the Godot editor re-saves .tres files, and its writer DROPS properties it considers default-valued. An empty-looking PackedStringArray is the usual casualty, and it has now cost this project twice - both times silently, and both times the damage surfaced somewhere else entirely: GasBurnerBuildingDefinition lost `capabilities` and `collected_outputs`, which took the burner's whole conversion with it. InterceptorVesselDefinition lost `capabilities`, `hull_points`, `build_time`, `icon` and `glow_color` - so the starting vessel had no cargo hold, and a full suite came back with ten unrelated tests broken because nothing anywhere could mine. Neither raised an error. The resource still loaded; it was just quietly less than it had been. test_definitions_intact asserts the SHAPE of the catalog rather than any one value: every vessel has a name, stats, a hull and its components; a hold and a "cargo" capability imply each other; every building has a description, a price, hit points, its build-menu wiring and at least one capability; and a converter keeps the fields its conversion needs. Whatever the editor eats now fails here, next to the thing that was eaten. Verified against both real incidents by reproducing them: deleting the interceptor's capabilities line fails with "cargo_capacity 20.0 but capabilities []", and deleting the burner's collected_outputs fails with "converts but lists no collected outputs". The first draft of that last check looked for the conversion fields on the DEFINITION, where they are not - they live on its logistics sub-resource - so it answered null and never fired. It passed while the burner sat there broken, which is the same class of quiet as the bug it is guarding. Also restores what the editor took from the gas burner this time round.
2026-08-29 14:38:51 +02:00
build_resource_costs = {
"gas": 22,
"ore": 40
}
Gas burner building and the energy resource (#54) A new building that consumes gas to produce energy, a new resource stored and moved exactly like ore and gas. The resource: EnergyResourceGlyph (a cell/bolt mark) + energy's own warm yellow-green ResourceDefinition, registered in ResourceDefinitions.ORDER/DEFINITIONS and GameState.RESOURCE_ENERGY. Inventory/ResourceBar/ResourceTransferList needed no changes - #49 already made them resource-agnostic, confirmed structurally in the test rather than touched. The building: GasBurnerBuildingVisual (a hexagonal chamber with intake vents in gas's colour and output ports in energy's, so the silhouette itself reads "gas in, energy out"), GasBurnerBuildingDefinition (capabilities=["gas_burner"], its own build-menu icon appended to IconButton.Icon rather than inserted, per the ticket's own warning about renumbering stored scene values), and the matching UI/BuildMenu/GasBurner* nodes in Main.tscn. Gated behind a new "Power Systems" skill-tree node, mirroring how "freight" unlocks the transport hub. Conversion: _advance_gas_burners(), simulated in GameState alongside _advance_production/extractor mining so it keeps running while the player is elsewhere. Withdraws gas and deposits energy in the building's OWN inventory only - never the bank - so it stalls with no local gas exactly like a lab waiting on materials. Logistics: a gas burner's ongoing gas deficit is folded into the existing queued-building-supply dispatch path (same "needs resource X" loop production/research orders already use, extended with gas_burner_gas_deficit()), and a new TO_BUILDING_PICKUP/ TO_MOTHERSHIP_FROM_BUILDING transporter state pair collects its energy to the bank, mirroring the extractor pickup path per the ticket. The existing f5c60f8 priority (queued deliveries outrank collection) holds: the pickup dispatch loop runs last. Tests: new tests/test_gas_burner.gd (resource genericity, stall semantics, research gate, and full delivery+pickup logistics via a real hub), run_tests transporter/resource regressions, and screenshots of the build menu (all 7 buildings) and transfer menu (all 3 resources). Full suite: 81/81. Two real bugs found and fixed along the way, neither gas-burner- specific: - _transporter_inbound_to() matched ANY non-idle transporter with a matching target_id, regardless of purpose. Once pickup states shared target_id with delivery states (both address a building, not an extractor), a transporter delivering gas made the SAME building look "busy" to the unrelated energy-pickup dispatch, starving it even with a second, genuinely free transporter sitting idle at the hub. Split into _transporter_inbound_to() (delivery states only) and a new _transporter_inbound_for_pickup() (pickup states only). - The build menu's 3-row/6-slot layout (MENU_ROW_TOPS + row content sizing) had no room for a 7th building - despite the ticket's own note that no layout changes should be needed. A naive 4th row ran off the bottom of a 720x1280 viewport; fitting one meant both adding a 4th row top AND shrinking the shared button/label/cost sizing so each row's content no longer taller than the compressed row spacing. Caught only by actually screenshotting it - a real build menu, not headless, since --headless renders nothing. 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 12:10:31 +02:00
action_icon = 33
menu_icon = 33
visual_scene = ExtResource("2")
capabilities = PackedStringArray("gas_burner")
logistics = SubResource("Resource_logistics")
Gas burner building and the energy resource (#54) A new building that consumes gas to produce energy, a new resource stored and moved exactly like ore and gas. The resource: EnergyResourceGlyph (a cell/bolt mark) + energy's own warm yellow-green ResourceDefinition, registered in ResourceDefinitions.ORDER/DEFINITIONS and GameState.RESOURCE_ENERGY. Inventory/ResourceBar/ResourceTransferList needed no changes - #49 already made them resource-agnostic, confirmed structurally in the test rather than touched. The building: GasBurnerBuildingVisual (a hexagonal chamber with intake vents in gas's colour and output ports in energy's, so the silhouette itself reads "gas in, energy out"), GasBurnerBuildingDefinition (capabilities=["gas_burner"], its own build-menu icon appended to IconButton.Icon rather than inserted, per the ticket's own warning about renumbering stored scene values), and the matching UI/BuildMenu/GasBurner* nodes in Main.tscn. Gated behind a new "Power Systems" skill-tree node, mirroring how "freight" unlocks the transport hub. Conversion: _advance_gas_burners(), simulated in GameState alongside _advance_production/extractor mining so it keeps running while the player is elsewhere. Withdraws gas and deposits energy in the building's OWN inventory only - never the bank - so it stalls with no local gas exactly like a lab waiting on materials. Logistics: a gas burner's ongoing gas deficit is folded into the existing queued-building-supply dispatch path (same "needs resource X" loop production/research orders already use, extended with gas_burner_gas_deficit()), and a new TO_BUILDING_PICKUP/ TO_MOTHERSHIP_FROM_BUILDING transporter state pair collects its energy to the bank, mirroring the extractor pickup path per the ticket. The existing f5c60f8 priority (queued deliveries outrank collection) holds: the pickup dispatch loop runs last. Tests: new tests/test_gas_burner.gd (resource genericity, stall semantics, research gate, and full delivery+pickup logistics via a real hub), run_tests transporter/resource regressions, and screenshots of the build menu (all 7 buildings) and transfer menu (all 3 resources). Full suite: 81/81. Two real bugs found and fixed along the way, neither gas-burner- specific: - _transporter_inbound_to() matched ANY non-idle transporter with a matching target_id, regardless of purpose. Once pickup states shared target_id with delivery states (both address a building, not an extractor), a transporter delivering gas made the SAME building look "busy" to the unrelated energy-pickup dispatch, starving it even with a second, genuinely free transporter sitting idle at the hub. Split into _transporter_inbound_to() (delivery states only) and a new _transporter_inbound_for_pickup() (pickup states only). - The build menu's 3-row/6-slot layout (MENU_ROW_TOPS + row content sizing) had no room for a 7th building - despite the ticket's own note that no layout changes should be needed. A naive 4th row ran off the bottom of a 720x1280 viewport; fitting one meant both adding a 4th row top AND shrinking the shared button/label/cost sizing so each row's content no longer taller than the compressed row spacing. Caught only by actually screenshotting it - a real build menu, not headless, since --headless renders nothing. 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 12:10:31 +02:00
map_color = Color(0.95, 0.35, 0.15, 1)
map_sides = 5
menu_button_name = "GasBurnerButton"
menu_label_name = "GasBurnerLabel"
menu_cost_name = "GasBurnerCost"
max_hp = 60