Randoloot/scripts/game/asteroid_destruction_effect.gd
2026-10-17 14:28:20 +02:00

89 lines
3.2 KiB
GDScript

extends Node2D
const PICKUP := preload("res://scripts/items/mineral_pickup.gd")
var _elapsed := 0.0
var _dust: Array[Dictionary] = []
func configure(sprite: Sprite2D, count: int = 6, mineral: String = "iron") -> void:
transform = sprite.global_transform
var source_scale := scale
scale = Vector2.ONE
var dimensions := sprite.texture.get_size()
var offset := randf()
var cracks: Array[PackedVector2Array] = []
for index in count:
var endpoint := _edge_point(offset + float(index) * 4.0 / count) * dimensions
cracks.append(_fracture_edge(endpoint, count))
for index in count:
# Neighbours share the same jagged fracture, so the pieces fit together.
var start := offset + float(index) * 4.0 / count
var finish := offset + float(index + 1) * 4.0 / count
var vertices := cracks[index].duplicate()
for corner in range(int(floor(start)) + 1, int(ceil(finish))):
vertices.append(_edge_point(float(corner)) * dimensions)
var next_crack := cracks[(index + 1) % count]
for vertex_index in range(next_crack.size() - 1, 0, -1):
vertices.append(next_crack[vertex_index])
var center := Vector2.ZERO
for vertex in vertices:
center += vertex
center /= vertices.size()
var fragment_points := PackedVector2Array()
var fragment_uv := PackedVector2Array()
for vertex in vertices:
fragment_points.append((vertex - center) * source_scale)
fragment_uv.append(vertex / dimensions + Vector2(0.5, 0.5))
var fragment := Node2D.new()
fragment.set_script(PICKUP)
fragment.set("mineral", mineral)
fragment.set("texture", sprite.texture)
fragment.set("points", fragment_points)
fragment.set("uv", fragment_uv)
fragment.position = center * source_scale
fragment.set("drift", center.normalized() * randf_range(65.0, 115.0))
fragment.set("spin", randf_range(-2.5, 2.5))
add_child(fragment)
for index in 28:
_dust.append({"velocity": Vector2.from_angle(randf() * TAU) * randf_range(45.0, 180.0), "size": randf_range(1.0, 3.0)})
light_mask = 4
func _fracture_edge(endpoint: Vector2, count: int) -> PackedVector2Array:
var edge := PackedVector2Array([Vector2.ZERO])
var normal := endpoint.orthogonal().normalized()
var roughness := endpoint.length() * minf(0.14, 0.65 / count)
var side := -1.0 if randf() < 0.5 else 1.0
for index in 5:
var along := float(index + 1) / 6.0
var offset := roughness * sin(along * PI) * side * randf_range(0.6, 1.0)
edge.append(endpoint * along + normal * offset)
side *= -1.0
edge.append(endpoint)
return edge
func _edge_point(distance: float) -> Vector2:
var t := fposmod(distance, 4.0)
if t < 1.0:
return Vector2(-0.5 + t, -0.5)
if t < 2.0:
return Vector2(0.5, -0.5 + t - 1.0)
if t < 3.0:
return Vector2(0.5 - (t - 2.0), 0.5)
return Vector2(-0.5, 0.5 - (t - 3.0))
func _process(delta: float) -> void:
_elapsed += delta
if _elapsed <= 1.0:
queue_redraw()
elif not _dust.is_empty():
_dust.clear()
queue_redraw()
if _elapsed > 1.0 and get_child_count() == 0:
queue_free()
func _draw() -> void:
var fade := maxf(0.0, 1.0 - _elapsed)
for mote in _dust:
draw_circle(mote.velocity * (1.0 - exp(-2.0 * _elapsed)) * 0.5, mote.size, Color(0.7, 0.75, 0.8, fade * 0.65))
if _elapsed < 0.18:
draw_circle(Vector2.ZERO, 16.0 * (1.0 - _elapsed / 0.18), Color(0.85, 0.9, 1.0, fade * 0.5))