extends Area2D # A gatherable crystal of asteroid ore lying on a planet's surface. Picked # up by flying any ship over it; the fleet doesn't need to stop. # # Deliberately drawn as a PERFECT HEXAGON rather than the irregular lumpy # polygon Asteroid.gd uses. The two are never on screen # together (asteroids are space-only, rocks surface-only), so the player # can only tell "collect this" from "this hurts" by learned shape language: # irregular and jagged reads as a natural hazard, regular and faceted reads # as something valuable and meant to be picked up. # # It also drifts toward a nearby ship (see _physics_process). Nothing # harmful moves helpfully toward you, so the pull doubles as a readability # cue on top of making gathering feel less fiddly. signal collected @export var attract_radius: float = 220.0 @export var attract_speed: float = 260.0 @export var attract_accel: float = 420.0 var shape_points: PackedVector2Array = PackedVector2Array() var facet_points: PackedVector2Array = PackedVector2Array() var size_scale: float = 1.0 var velocity: Vector2 = Vector2.ZERO func _ready() -> void: add_to_group("rocks") z_index = -1 area_entered.connect(_on_area_entered) queue_redraw() func build_shape(shape_seed: int) -> void: # Only the SIZE varies, never the silhouette: every ore node is the same # regular hexagon, which is the whole point of the cue. var rng: RandomNumberGenerator = RandomNumberGenerator.new() rng.seed = shape_seed size_scale = rng.randf_range(0.85, 1.25) var r: float = 13.0 * size_scale shape_points = PackedVector2Array() for i in range(6): var a: float = (float(i) / 6.0) * TAU - PI / 2.0 shape_points.append(Vector2(cos(a), sin(a)) * r) # One flat-to-flat line across the middle, so it reads as a cut face # rather than a plain outline at small sizes. facet_points = PackedVector2Array([ shape_points[5], shape_points[2], ]) queue_redraw() func _physics_process(delta: float) -> void: # Drift toward the closest ship inside attract_radius. No redraw here: # position is a transform property and the crystal's shape is fixed. var target: Node2D = _nearest_ship() if target == null: velocity = velocity.move_toward(Vector2.ZERO, attract_accel * delta) else: var desired: Vector2 = (target.position - position).normalized() * attract_speed velocity = velocity.move_toward(desired, attract_accel * delta) position += velocity * delta func _nearest_ship() -> Node2D: var nearest: Node2D = null var best: float = attract_radius for ship in get_tree().get_nodes_in_group("player_ships"): if not is_instance_valid(ship): continue var d: float = position.distance_to(ship.position) if d <= best: best = d nearest = ship return nearest func _on_area_entered(area: Area2D) -> void: if area.is_in_group("player_ships"): GameState.rocks += 1 collected.emit() queue_free() func _draw() -> void: if shape_points.is_empty(): return # Deep teal with a near-black core: a dark, saturated colour reads # clearly against the bright lunar regolith (sampled at ~0.75 grey), # where the original warm yellow disappeared into it. var glow: Color = Color(0.0, 0.62, 0.7) var core: Color = Color(0.0, 0.12, 0.15) NeonDraw.draw_glowing_polygon(self, shape_points, glow, 1.0, core) # One line only - the hexagon has a single flat-to-flat facet. This used # to draw two lines from a 4-point array; the hexagon rewrite left only # 2 points, so the second line indexed out of bounds and every rock # threw on every draw. if facet_points.size() >= 2: NeonDraw.draw_glowing_line(self, facet_points[0], facet_points[1], glow, 0.7, 0.3, core)