extends Node2D # Scatters gatherable rocks across a planet's surface around the fleet, and # tops them back up as they are collected, so there is always something to # mine wherever the player flies. # # Unlike AsteroidField/PlanetField this is deliberately NOT deterministic # per chunk: rocks are consumable, and a chunk-seeded layout would respawn # the very same rock in the very same spot the moment the player flew back, # making them infinite and free. Tracking every collected rock forever would # grow without bound, so they are simply scattered at random within a ring # around the fleet and despawned once left far behind. @export var target_count: int = 14 @export var spawn_radius: float = 1400.0 @export var min_spawn_distance: float = 320.0 @export var despawn_radius: float = 2600.0 @export var check_interval: float = 0.5 @onready var fleet: Node2D = get_node("../Fleet") var rock_scene: PackedScene = preload("res://Rock.tscn") var rocks: Array = [] var timer: float = 0.0 var rng: RandomNumberGenerator = RandomNumberGenerator.new() func _ready() -> void: rng.randomize() # Populate immediately rather than waiting out the first interval, so # the surface isn't visibly empty for half a second on arrival. update_rocks() func _process(delta: float) -> void: timer += delta if timer < check_interval: return timer = 0.0 update_rocks() func update_rocks() -> void: var centre: Vector2 = fleet.get_centroid() var surviving: Array = [] for rock in rocks: if not is_instance_valid(rock): continue if rock.position.distance_to(centre) > despawn_radius: rock.queue_free() continue surviving.append(rock) rocks = surviving while rocks.size() < target_count: _spawn_rock(centre) func _spawn_rock(centre: Vector2) -> void: # Ring, not disc: spawning right on top of the fleet would hand the # player free rocks without flying anywhere. var angle: float = rng.randf() * TAU var distance: float = rng.randf_range(min_spawn_distance, spawn_radius) var rock: Area2D = rock_scene.instantiate() rock.position = centre + Vector2(cos(angle), sin(angle)) * distance rock.build_shape(rng.randi()) # Deferred: update_rocks() runs from _ready(), and Godot refuses a # direct add_child() while the tree is still setting up children. get_parent().add_child.call_deferred(rock) rocks.append(rock)