Randoloot/scripts/items/loot_reveal_effect.gd
2026-10-26 21:06:32 +01:00

87 lines
2.8 KiB
GDScript

extends Node2D
const DURATION := 0.85
static var _sounds: Dictionary = {}
static var _last_sound_ms := -1000
var tint := Color("91dbea")
var quality := 0
var age := 0.0
static func spawn(parent: Node, at: Vector2, color: Color, rarity: int = 0, screen_audio: bool = false) -> Node2D:
var effect := Node2D.new()
effect.set_script(load("res://scripts/items/loot_reveal_effect.gd"))
effect.tint = color
effect.quality = clampi(rarity,0,4)
effect.position = at
parent.add_child(effect)
# Simultaneous drops share one chime, keeping battles from becoming too loud.
var now := Time.get_ticks_msec()
if now-_last_sound_ms >= 100:
_last_sound_ms = now
var audio: Node
if screen_audio:
audio = AudioStreamPlayer.new()
else:
audio = AudioStreamPlayer2D.new()
audio.max_distance = 1200.0
audio.attenuation = 1.5
audio.stream = sound(quality_band(rarity))
audio.volume_db = -12.0
if AudioServer.get_bus_index("SFX") >= 0: audio.bus = &"SFX"
effect.add_child(audio)
audio.play()
return effect
static func quality_band(rarity: int) -> int:
return 1 if rarity >= 3 else 0
static func sound(band: int = 0) -> AudioStreamWAV:
if _sounds.has(band): return _sounds[band]
var rate := 22050
var length := 0.65
var data := PackedByteArray()
data.resize(int(rate*length)*2)
for index in int(rate*length):
var t := float(index)/rate
var value := 0.0
for note in 3:
var elapsed := t-note*0.075
if elapsed < 0.0: continue
var frequency: float = [659.25,830.61,987.77][note]*(1.25 if band > 0 else 1.0)
var envelope := minf(elapsed/0.006,1.0)*exp(-elapsed*9.0)
value += (sin(TAU*frequency*elapsed)+0.18*sin(TAU*frequency*2.0*elapsed))*envelope*0.18
data.encode_s16(index*2,clampi(roundi(value*32767),-32768,32767))
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_16_BITS
stream.mix_rate = rate
stream.data = data
_sounds[band] = stream
return stream
func _ready() -> void:
z_index = 30
func _process(delta: float) -> void:
age += delta
if age >= DURATION:
queue_free()
return
queue_redraw()
func _draw() -> void:
var progress := clampf(age/DURATION,0.0,1.0)
var fade := pow(1.0-progress,2.0)
var radius := 16.0+75.0*(1.0-pow(1.0-progress,3.0))
for layer in range(4,0,-1):
draw_circle(Vector2.ZERO,12.0+layer*8.0,Color(tint,fade*0.035))
draw_arc(Vector2.ZERO,radius,0,TAU,64,Color(tint,fade*0.8),1.4,true)
for index in 14+quality*2:
var angle := index*2.39996
var direction := Vector2.from_angle(angle)
var distance := (18.0+float(index%4)*8.0)+progress*(42.0+float(index%3)*18.0)
var point := direction*distance
draw_line(point,point-direction*(3.0+8.0*(1.0-progress)),Color(tint,fade),1.5,true)
var beam := maxf(0.0,1.0-age/0.45)
draw_line(Vector2(0,12),Vector2(0,-92-quality*12),Color(tint,beam*0.65),2.0,true)
draw_line(Vector2(-10,0),Vector2(10,0),Color.WHITE*Color(1,1,1,beam),2.0,true)