102 lines
3.6 KiB
GDScript3
102 lines
3.6 KiB
GDScript3
|
|
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)
|