From 02643c76c437eb8ae4a1ccbf8e02ff8060e4ec6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Wisniewski?= Date: Fri, 14 Aug 2026 16:13:24 +0200 Subject: [PATCH] Drive planets from definitions --- GameState.gd | 3 +- GasGiantDefinition.tres | 14 ++++++ GasGiantVisual.gd | 31 ++++++++++++++ GasGiantVisual.gd.uid | 1 + GasGiantVisual.tscn | 6 +++ Planet.gd | 80 +++++++++++++---------------------- Planet.tscn | 2 + PlanetAtmosphereController.gd | 3 +- PlanetDefinition.gd | 10 +++++ PlanetDefinition.gd.uid | 1 + PlanetDefinitions.gd | 14 ++++++ PlanetDefinitions.gd.uid | 1 + PlanetField.gd | 4 +- PlanetSurfaceController.gd | 4 +- RockyPlanetDefinition.tres | 14 ++++++ RockyPlanetVisual.gd | 26 ++++++++++++ RockyPlanetVisual.gd.uid | 1 + RockyPlanetVisual.tscn | 6 +++ WorldGen.gd | 19 +++------ WorldMapContent.gd | 5 ++- tests/_gas_surface_preview.gd | 3 +- tests/test_gas_system.gd | 17 ++++---- 22 files changed, 183 insertions(+), 82 deletions(-) create mode 100644 GasGiantDefinition.tres create mode 100644 GasGiantVisual.gd create mode 100644 GasGiantVisual.gd.uid create mode 100644 GasGiantVisual.tscn create mode 100644 PlanetDefinition.gd create mode 100644 PlanetDefinition.gd.uid create mode 100644 PlanetDefinitions.gd create mode 100644 PlanetDefinitions.gd.uid create mode 100644 RockyPlanetDefinition.tres create mode 100644 RockyPlanetVisual.gd create mode 100644 RockyPlanetVisual.gd.uid create mode 100644 RockyPlanetVisual.tscn diff --git a/GameState.gd b/GameState.gd index 6b19551..11a4b7a 100644 --- a/GameState.gd +++ b/GameState.gd @@ -13,8 +13,7 @@ var return_world_position: Vector2 = Vector2.ZERO var return_camera_zoom: float = 1.0 var planet_name: String = "" -var planet_type: String = "" -var surface_profile_id: String = "rocky" +var planet_definition_id: String = "rocky" # Explored-chunk history for the world map's fog-of-war reveal. Lives # here (not on ExplorationTracker, which gets destroyed and recreated on diff --git a/GasGiantDefinition.tres b/GasGiantDefinition.tres new file mode 100644 index 0000000..0a5109c --- /dev/null +++ b/GasGiantDefinition.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="PlanetDefinition" load_steps=3 format=3] + +[ext_resource type="Script" path="res://PlanetDefinition.gd" id="1"] +[ext_resource type="PackedScene" path="res://GasGiantVisual.tscn" id="2"] + +[resource] +script = ExtResource("1") +definition_id = "gas_giant" +display_name = "Gas giant" +names = PackedStringArray("Aurelia", "Boreas", "Cerulean Deep", "Helios Major", "Vesper") +visual_scene = ExtResource("2") +surface_profile_id = "gas" +resource_id = "gas" +map_color = Color(0.65, 0.42, 0.9, 1) diff --git a/GasGiantVisual.gd b/GasGiantVisual.gd new file mode 100644 index 0000000..e9172ca --- /dev/null +++ b/GasGiantVisual.gd @@ -0,0 +1,31 @@ +extends Node2D + +var gas_phase: float = 0.0 + + +func configure(_shape_seed: int) -> void: + queue_redraw() + + +func extractor_anchor() -> Vector2: + return Vector2(0.0, -118.0) + + +func _process(delta: float) -> void: + gas_phase += delta + queue_redraw() + + +func _draw() -> void: + var rim := PackedVector2Array() + for i in range(32): + var angle := TAU * float(i) / 32.0 + rim.append(Vector2(cos(angle) * 118.0, sin(angle) * 118.0)) + NeonDraw.draw_glowing_polygon(self, rim, Color(0.4, 0.78, 1.0), 0.9) + for row in [-62.0, -28.0, 8.0, 43.0, 73.0]: + var band := PackedVector2Array() + var half_width: float = sqrt(maxf(0.0, 118.0 * 118.0 - row * row)) + for i in range(15): + var x := lerpf(-half_width, half_width, float(i) / 14.0) + band.append(Vector2(x, row + sin(x * 0.045 + gas_phase * 1.6 + row) * 7.0)) + NeonDraw.draw_glowing_path(self, band, Color(0.85, 0.52, 1.0), 0.75, 0.65) diff --git a/GasGiantVisual.gd.uid b/GasGiantVisual.gd.uid new file mode 100644 index 0000000..af718cf --- /dev/null +++ b/GasGiantVisual.gd.uid @@ -0,0 +1 @@ +uid://bxfw4ja2hw4pf diff --git a/GasGiantVisual.tscn b/GasGiantVisual.tscn new file mode 100644 index 0000000..513b87e --- /dev/null +++ b/GasGiantVisual.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://GasGiantVisual.gd" id="1"] + +[node name="Visual" type="Node2D"] +script = ExtResource("1") diff --git a/Planet.gd b/Planet.gd index 71b1e9f..4d17d84 100644 --- a/Planet.gd +++ b/Planet.gd @@ -1,5 +1,7 @@ extends Node2D +const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd") + # A landable world-object - unlike regular Asteroid.gd, planets are # stationary ("Big Stable Asteroid" - stable means it doesn't drift or @@ -12,16 +14,33 @@ extends Node2D # while the fleet as a whole was clearly sitting on the planet. @export var planet_name: String = "" -@export var planet_type: String = "rocky" -@export var resource_type: String = "ore" -@export var surface_profile_id: String = "rocky" +@export var definition_id: String = PLANET_DEFINITIONS.DEFAULT_DEFINITION_ID @export var landing_radius: float = 140.0 # Set by whoever spawns this planet (PlanetField, seeded from the chunk's # own coordinates) so the outline shape is stable across respawns of the # same chunk, instead of reading as a "different" planet every time. @export var shape_seed: int = 0 -var shape_points: PackedVector2Array = PackedVector2Array() +var definition: Resource +var visual: Node2D + +var resource_type: String: + get: return String(definition.resource_id) if definition != null else "ore" + +var surface_profile_id: String: + get: return String(definition.surface_profile_id) if definition != null else "rocky" + +var shape_points: PackedVector2Array: + get: + if visual != null and "shape_points" in visual: + return visual.shape_points + return PackedVector2Array() + +var gas_phase: float: + get: + if visual != null and "gas_phase" in visual: + return float(visual.gas_phase) + return 0.0 # An extractor is built down on this planet's SURFACE, in a different scene # the player is no longer looking at, so from space nothing distinguished a @@ -39,31 +58,20 @@ const DERRICK_HALF_WIDTH: float = 16.0 # only at _ready() left the planet looking untouched until it happened to # despawn and respawn. var shows_extractor: bool = false -var gas_phase: float = 0.0 func _ready() -> void: add_to_group("planets") z_index = -1 - - var rng: RandomNumberGenerator = RandomNumberGenerator.new() - rng.seed = shape_seed - var point_count: int = 10 - for i in range(point_count): - var angle: float = (float(i) / point_count) * TAU - var radius: float = rng.randf_range(70.0, 100.0) - shape_points.append(Vector2(cos(angle), sin(angle)) * radius) + definition = PLANET_DEFINITIONS.get_definition(definition_id) + visual = definition.visual_scene.instantiate() + visual.configure(shape_seed) + get_node("VisualHost").add_child(visual) shows_extractor = GameState.has_extractor_at(planet_name) GameState.extractor_added.connect(_on_extractor_added) queue_redraw() -func _process(delta: float) -> void: - if planet_type == "gas_giant": - gas_phase += delta - queue_redraw() - - func _on_extractor_added(built_on: String) -> void: # Only the planet it was actually built on, and only a redraw when the # answer really changed - every other planet in range gets this too. @@ -74,14 +82,9 @@ func _on_extractor_added(built_on: String) -> void: func extractor_derrick_anchor() -> Vector2: - # The rim vertex the derrick stands on: the shape's own topmost point, - # rather than a fixed radius, so it sits ON the outline whatever lumpy - # shape the seed drew instead of floating off it or sinking inside it. - var base: Vector2 = Vector2(0.0, -100.0) - for p in shape_points: - if p.y < base.y: - base = p - return base + if visual != null and visual.has_method("extractor_anchor"): + return visual.extractor_anchor() + return Vector2(0.0, -100.0) func extractor_derrick_tip() -> Vector2: @@ -113,28 +116,5 @@ func extractor_derrick_points() -> PackedVector2Array: func _draw() -> void: - if planet_type == "gas_giant": - _draw_gas_giant() - if shows_extractor: - NeonDraw.draw_glowing_polygon(self, extractor_derrick_points(), EXTRACTOR_MARKER_COLOR, 1.0) - return - NeonDraw.draw_glowing_polygon(self, shape_points, Color(0.6, 0.55, 0.5), 1.0) if shows_extractor: NeonDraw.draw_glowing_polygon(self, extractor_derrick_points(), EXTRACTOR_MARKER_COLOR, 1.0) - - -func _draw_gas_giant() -> void: - # A round, animated giant with drifting latitudinal bands. It is visibly - # unlike the rocky outline and keeps moving even while the fleet is idle. - var rim := PackedVector2Array() - for i in range(32): - var a := TAU * float(i) / 32.0 - rim.append(Vector2(cos(a) * 118.0, sin(a) * 118.0)) - NeonDraw.draw_glowing_polygon(self, rim, Color(0.4, 0.78, 1.0), 0.9) - for row in [-62.0, -28.0, 8.0, 43.0, 73.0]: - var band := PackedVector2Array() - var half_width: float = sqrt(maxf(0.0, 118.0 * 118.0 - row * row)) - for i in range(15): - var x := lerpf(-half_width, half_width, float(i) / 14.0) - band.append(Vector2(x, row + sin(x * 0.045 + gas_phase * 1.6 + row) * 7.0)) - NeonDraw.draw_glowing_path(self, band, Color(0.85, 0.52, 1.0), 0.75, 0.65) diff --git a/Planet.tscn b/Planet.tscn index 844672b..0e7fe1f 100644 --- a/Planet.tscn +++ b/Planet.tscn @@ -4,3 +4,5 @@ [node name="Planet" type="Node2D"] script = ExtResource("1") + +[node name="VisualHost" type="Node2D" parent="."] diff --git a/PlanetAtmosphereController.gd b/PlanetAtmosphereController.gd index b13e69f..3a66ea4 100644 --- a/PlanetAtmosphereController.gd +++ b/PlanetAtmosphereController.gd @@ -113,8 +113,7 @@ func _on_enter_pressed() -> void: GameState.return_world_position = current_planet.position GameState.return_camera_zoom = camera.zoom.x GameState.planet_name = current_planet.planet_name - GameState.planet_type = current_planet.planet_type - GameState.surface_profile_id = current_planet.surface_profile_id + GameState.planet_definition_id = current_planet.definition_id var tween: Tween = create_tween() tween.tween_property(fade_rect, "color:a", 1.0, fade_duration) diff --git a/PlanetDefinition.gd b/PlanetDefinition.gd new file mode 100644 index 0000000..e184241 --- /dev/null +++ b/PlanetDefinition.gd @@ -0,0 +1,10 @@ +class_name PlanetDefinition +extends Resource + +@export var definition_id: String = "rocky" +@export var display_name: String = "Rocky planet" +@export var names: PackedStringArray = [] +@export var visual_scene: PackedScene +@export var surface_profile_id: String = "rocky" +@export var resource_id: String = "ore" +@export var map_color: Color = Color(0.7, 0.65, 0.55) diff --git a/PlanetDefinition.gd.uid b/PlanetDefinition.gd.uid new file mode 100644 index 0000000..ee09e6d --- /dev/null +++ b/PlanetDefinition.gd.uid @@ -0,0 +1 @@ +uid://dt6tsdmmlnfaf diff --git a/PlanetDefinitions.gd b/PlanetDefinitions.gd new file mode 100644 index 0000000..fa9dd2d --- /dev/null +++ b/PlanetDefinitions.gd @@ -0,0 +1,14 @@ +class_name PlanetDefinitions +extends RefCounted + +const ROCKY_ID: String = "rocky" +const GAS_GIANT_ID: String = "gas_giant" +const DEFAULT_DEFINITION_ID: String = ROCKY_ID +const DEFINITIONS: Dictionary = { + ROCKY_ID: preload("res://RockyPlanetDefinition.tres"), + GAS_GIANT_ID: preload("res://GasGiantDefinition.tres"), +} + + +static func get_definition(definition_id: String) -> Resource: + return DEFINITIONS.get(definition_id, DEFINITIONS[DEFAULT_DEFINITION_ID]) diff --git a/PlanetDefinitions.gd.uid b/PlanetDefinitions.gd.uid new file mode 100644 index 0000000..188741c --- /dev/null +++ b/PlanetDefinitions.gd.uid @@ -0,0 +1 @@ +uid://ipsld2ex2t8e diff --git a/PlanetField.gd b/PlanetField.gd index 53b6ddf..6a8c3c6 100644 --- a/PlanetField.gd +++ b/PlanetField.gd @@ -84,9 +84,7 @@ func _spawn_chunk_planet(chunk: Vector2i) -> void: planet.position = origin + data.local_pos planet.planet_name = data.name planet.shape_seed = data.shape_seed - planet.planet_type = String(data.get("planet_type", "rocky")) - planet.resource_type = String(data.get("resource_type", "ore")) - planet.surface_profile_id = String(data.get("surface_profile_id", "rocky")) + planet.definition_id = String(data.get("definition_id", "rocky")) # Deferred: update_chunks() can run synchronously from inside a # parent's own _ready() chain (forced once right after the fleet is # positioned, to avoid a pop-in on scene load) - Godot refuses a diff --git a/PlanetSurfaceController.gd b/PlanetSurfaceController.gd index 9f632b4..7c683d4 100644 --- a/PlanetSurfaceController.gd +++ b/PlanetSurfaceController.gd @@ -1,6 +1,7 @@ extends Node2D const SURFACE_PROFILES = preload("res://PlanetSurfaceProfiles.gd") +const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd") # Bootstraps the surviving fleet on this scene's own startup (from # GameState, since this is a fresh scene tree - nothing here carries @@ -22,7 +23,8 @@ var transitioning: bool = false func _ready() -> void: - _compose_surface(SURFACE_PROFILES.get_profile(GameState.surface_profile_id)) + var planet_definition: Resource = PLANET_DEFINITIONS.get_definition(GameState.planet_definition_id) + _compose_surface(SURFACE_PROFILES.get_profile(planet_definition.surface_profile_id)) fleet.spawn_ships(GameState.surviving_ship_count, Vector2.ZERO) camera.position = Vector2.ZERO camera.zoom = Vector2(GameState.return_camera_zoom, GameState.return_camera_zoom) diff --git a/RockyPlanetDefinition.tres b/RockyPlanetDefinition.tres new file mode 100644 index 0000000..b0a3e9f --- /dev/null +++ b/RockyPlanetDefinition.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="PlanetDefinition" load_steps=3 format=3] + +[ext_resource type="Script" path="res://PlanetDefinition.gd" id="1"] +[ext_resource type="PackedScene" path="res://RockyPlanetVisual.tscn" id="2"] + +[resource] +script = ExtResource("1") +definition_id = "rocky" +display_name = "Rocky planet" +names = PackedStringArray("Kepler's Rest", "Ceres Minor", "Vesta's Shadow", "Ironhold", "Drift Anchor", "Stonefall", "Grey Watch", "Silent Quarry", "Baros Rock", "Umbra Point") +visual_scene = ExtResource("2") +surface_profile_id = "rocky" +resource_id = "ore" +map_color = Color(0.7, 0.65, 0.55, 1) diff --git a/RockyPlanetVisual.gd b/RockyPlanetVisual.gd new file mode 100644 index 0000000..dc2a889 --- /dev/null +++ b/RockyPlanetVisual.gd @@ -0,0 +1,26 @@ +extends Node2D + +var shape_points: PackedVector2Array = PackedVector2Array() + + +func configure(shape_seed: int) -> void: + shape_points.clear() + var rng := RandomNumberGenerator.new() + rng.seed = shape_seed + for i in range(10): + var angle: float = (float(i) / 10.0) * TAU + var radius: float = rng.randf_range(70.0, 100.0) + shape_points.append(Vector2(cos(angle), sin(angle)) * radius) + queue_redraw() + + +func extractor_anchor() -> Vector2: + var anchor := Vector2(0.0, -100.0) + for point in shape_points: + if point.y < anchor.y: + anchor = point + return anchor + + +func _draw() -> void: + NeonDraw.draw_glowing_polygon(self, shape_points, Color(0.6, 0.55, 0.5), 1.0) diff --git a/RockyPlanetVisual.gd.uid b/RockyPlanetVisual.gd.uid new file mode 100644 index 0000000..b6ecadb --- /dev/null +++ b/RockyPlanetVisual.gd.uid @@ -0,0 +1 @@ +uid://b82xh8nosedlm diff --git a/RockyPlanetVisual.tscn b/RockyPlanetVisual.tscn new file mode 100644 index 0000000..ee6a302 --- /dev/null +++ b/RockyPlanetVisual.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://RockyPlanetVisual.gd" id="1"] + +[node name="Visual" type="Node2D"] +script = ExtResource("1") diff --git a/WorldGen.gd b/WorldGen.gd index feea7f6..5e73ee5 100644 --- a/WorldGen.gd +++ b/WorldGen.gd @@ -1,6 +1,8 @@ class_name WorldGen extends RefCounted +const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd") + # Deterministic procedural world generation: the world is divided into # square chunks, and each chunk's biome is derived from a seeded # low-frequency noise field sampled at the chunk's own coordinates - so @@ -22,13 +24,6 @@ const GAS_GIANT_THRESHOLD: float = 0.55 const NEARBY_GAS_GIANT_CHUNK: Vector2i = Vector2i.ZERO const NEARBY_GAS_GIANT_LOCAL_POS: Vector2 = Vector2(900.0, 600.0) -const PLANET_NAMES: Array = [ - "Kepler's Rest", "Ceres Minor", "Vesta's Shadow", "Ironhold", - "Drift Anchor", "Stonefall", "Grey Watch", "Silent Quarry", - "Baros Rock", "Umbra Point", -] -const GAS_GIANT_NAMES: Array = ["Aurelia", "Boreas", "Cerulean Deep", "Helios Major", "Vesper"] - static var _noise: FastNoiseLite static var _planet_noise: FastNoiseLite @@ -88,16 +83,16 @@ static func planet_data_at_chunk(chunk: Vector2i) -> Dictionary: var guaranteed_nearby_giant: bool = chunk == NEARBY_GAS_GIANT_CHUNK var local_pos: Vector2 = NEARBY_GAS_GIANT_LOCAL_POS if guaranteed_nearby_giant \ else Vector2(rng.randf() * CHUNK_SIZE, rng.randf() * CHUNK_SIZE) - var planet_name: String = PLANET_NAMES[rng.randi() % PLANET_NAMES.size()] var shape_seed: int = rng.randi() # A stable per-chunk roll makes gas giants persistent landmarks rather # than a different resource whenever the scene reloads. var gas_giant: bool = guaranteed_nearby_giant or biome_at_chunk(chunk) == Biome.SPACE - if gas_giant: - planet_name = GAS_GIANT_NAMES[rng.randi() % GAS_GIANT_NAMES.size()] + var definition_id: String = PLANET_DEFINITIONS.GAS_GIANT_ID if gas_giant else PLANET_DEFINITIONS.ROCKY_ID + var definition: Resource = PLANET_DEFINITIONS.get_definition(definition_id) + var names: PackedStringArray = definition.names + var planet_name: String = names[rng.randi() % names.size()] return {"local_pos": local_pos, "name": planet_name, "shape_seed": shape_seed, - "planet_type": "gas_giant" if gas_giant else "rocky", "resource_type": "gas" if gas_giant else "ore", - "surface_profile_id": "gas" if gas_giant else "rocky"} + "definition_id": definition_id} static func biome_color(biome: int) -> Color: diff --git a/WorldMapContent.gd b/WorldMapContent.gd index e8ba8df..33212a0 100644 --- a/WorldMapContent.gd +++ b/WorldMapContent.gd @@ -1,5 +1,7 @@ extends Node2D +const PLANET_DEFINITIONS = preload("res://PlanetDefinitions.gd") + const WORLD_TO_LOCAL: float = 0.04 # Mirrors Building.gd's own MOTHERSHIP_COLOR/CRAFTER_COLOR/COMMAND_COLOR so a @@ -105,7 +107,8 @@ func _draw() -> void: # spawn_radius_chunks of the fleet). var data: Dictionary = WorldGen.planet_data_at_chunk(chunk) var planet_local: Vector2 = origin_local + data.local_pos * WORLD_TO_LOCAL - draw_circle(planet_local, 5.0, Color(0.7, 0.65, 0.55)) + var definition: Resource = PLANET_DEFINITIONS.get_definition(String(data.definition_id)) + draw_circle(planet_local, 5.0, definition.map_color) draw_arc(planet_local, 5.0, 0.0, TAU, 16, Color(1.0, 1.0, 1.0, 0.9), 1.0, true) if chunk_has_extractor_badge(chunk): diff --git a/tests/_gas_surface_preview.gd b/tests/_gas_surface_preview.gd index 41cde0a..5dafbe5 100644 --- a/tests/_gas_surface_preview.gd +++ b/tests/_gas_surface_preview.gd @@ -3,8 +3,7 @@ extends Node2D func _ready() -> void: GameState.planet_name = "Violet Tempest" - GameState.planet_type = "gas_giant" - GameState.surface_profile_id = "gas" + GameState.planet_definition_id = "gas_giant" GameState.surviving_ship_count = 3 GameState.rocks = 128 GameState.gas = 46 diff --git a/tests/test_gas_system.gd b/tests/test_gas_system.gd index f0797d1..94edc1d 100644 --- a/tests/test_gas_system.gd +++ b/tests/test_gas_system.gd @@ -27,8 +27,7 @@ func _process(delta: float) -> bool: return _fail("nearby gas giant chunk does not contain a planet") if WorldGen.biome_at_chunk(nearby_chunk) != WorldGen.Biome.SPACE: return _fail("nearby gas giant is not in the normal space biome") - if String(nearby_data.get("planet_type", "")) != "gas_giant" \ - or String(nearby_data.get("resource_type", "")) != "gas": + if String(nearby_data.get("definition_id", "")) != "gas_giant": return _fail("nearby guaranteed planet is not a gas source") if nearby_position.length() > WorldGen.CHUNK_SIZE * 2.0: return _fail("nearby gas giant spawned too far away: %s" % nearby_position) @@ -36,8 +35,7 @@ func _process(delta: float) -> bool: # Gas surfaces have vents only: no deferred ore pickups, and the vent # emission advances continuously so the source visibly looks active. game_state.planet_name = "Gas Test" - game_state.planet_type = "gas_giant" - game_state.surface_profile_id = "gas" + game_state.planet_definition_id = "gas_giant" var surface: Node = load("res://PlanetSurface.tscn").instantiate() root.add_child(surface) var gas_field: Node = surface.get_node("SurfaceFeatures/ResourceProvider") @@ -53,7 +51,7 @@ func _process(delta: float) -> bool: return _fail("gas-source emission animation did not advance") surface.free() - game_state.surface_profile_id = "rocky" + game_state.planet_definition_id = "rocky" var rocky_surface: Node = load("res://PlanetSurface.tscn").instantiate() root.add_child(rocky_surface) var rocky_features: Node = rocky_surface.get_node("SurfaceFeatures/ResourceProvider") @@ -69,16 +67,17 @@ func _process(delta: float) -> bool: for x in range(-12, 13): for y in range(-12, 13): var data: Dictionary = WorldGen.planet_data_at_chunk(Vector2i(x, y)) - if String(data.get("planet_type", "")) == "gas_giant": - found_giant = String(data.get("resource_type", "")) == "gas" + if String(data.get("definition_id", "")) == "gas_giant": + found_giant = true break if found_giant: break if not found_giant: return _fail("world generation produced no deterministic gas giant") var giant: Node2D = load("res://Planet.tscn").instantiate() - giant.planet_type = "gas_giant" - giant._process(0.5) + giant.definition_id = "gas_giant" + root.add_child(giant) + giant.visual._process(0.5) if giant.gas_phase <= 0.0: return _fail("gas giant animation did not advance") giant.free()