Files
2026-09-16 12:09:33 +02:00

115 lines
3.2 KiB
GDScript

class_name Sfx
extends Node
## Primitive Klänge, im Code erzeugt: kurze Töne mit Hüllkurve.
## Keine Audiodateien, passend zu den primitiven Meshes.
const RATE := 22050
const HIT_COOLDOWN := 0.06
var _streams := {}
var _players: Array = []
var _next := 0
var _hit_gate := 0.0
func _ready() -> void:
for i in 8:
var pl := AudioStreamPlayer.new()
pl.volume_db = -8.0
add_child(pl)
_players.append(pl)
_streams["kauf"] = _tone([[660.0, 0.07]], 0.5)
_streams["verkauf"] = _tone([[520.0, 0.05], [330.0, 0.07]], 0.45)
_streams["wuerfeln"] = _noise(0.04, 0.35)
_streams["setzen"] = _tone([[180.0, 0.06]], 0.6, true)
_streams["aufwerten"] = _tone([[523.0, 0.08], [659.0, 0.08], [784.0, 0.12]], 0.55)
_streams["level"] = _tone([[440.0, 0.08], [880.0, 0.14]], 0.5)
_streams["treffer"] = _noise(0.03, 0.22)
_streams["krit"] = _tone([[220.0, 0.05]], 0.55, true)
_streams["heilung"] = _tone([[988.0, 0.06]], 0.3)
_streams["zauber"] = _tone([[880.0, 0.05], [1320.0, 0.09]], 0.45)
_streams["sieg"] = _tone([[523.0, 0.1], [659.0, 0.1], [784.0, 0.1], [1046.0, 0.2]], 0.6)
_streams["niederlage"] = _tone([[392.0, 0.12], [311.0, 0.12], [261.0, 0.24]], 0.6)
_streams["runde"] = _tone([[440.0, 0.06], [440.0, 0.06]], 0.4)
_streams["fehler"] = _tone([[200.0, 0.1]], 0.5, true)
## Lautstärke 0..1, 0 schaltet stumm.
func set_volume(linear: float) -> void:
for pl in _players:
pl.volume_db = linear_to_db(maxf(0.0001, linear)) - 8.0
pl.stream_paused = false
_muted = linear <= 0.001
var _muted := false
func play(name: String) -> void:
if _muted:
return
if not _streams.has(name):
return
if name == "treffer":
var now := Time.get_ticks_msec() / 1000.0
if now < _hit_gate:
return
_hit_gate = now + HIT_COOLDOWN
var pl: AudioStreamPlayer = _players[_next]
_next = (_next + 1) % _players.size()
pl.stream = _streams[name]
pl.play()
## Tonfolge [[frequenz, dauer], ...] mit Ein- und Ausblenden.
func _tone(notes: Array, gain: float, square: bool = false) -> AudioStreamWAV:
var total := 0.0
for n in notes:
total += n[1]
var frames := int(total * RATE)
var data := PackedByteArray()
data.resize(frames * 2)
var idx := 0
for n in notes:
var freq: float = n[0]
var len_frames := int(n[1] * RATE)
for i in len_frames:
var t := float(i) / RATE
var env := minf(1.0, float(i) / (0.005 * RATE)) * minf(1.0, float(len_frames - i) / (0.02 * RATE))
var v := sin(TAU * freq * t)
if square:
v = 1.0 if v >= 0.0 else -1.0
_put(data, idx, v * env * gain)
idx += 1
if idx >= frames:
break
return _wav(data)
## Kurzes Rauschen für Treffer und Würfeln.
func _noise(dur: float, gain: float) -> AudioStreamWAV:
var frames := int(dur * RATE)
var data := PackedByteArray()
data.resize(frames * 2)
var rng := RandomNumberGenerator.new()
rng.seed = 1234
for i in frames:
var env := 1.0 - float(i) / float(frames)
_put(data, i, rng.randf_range(-1.0, 1.0) * env * env * gain)
return _wav(data)
func _put(data: PackedByteArray, index: int, v: float) -> void:
var s := int(clampf(v, -1.0, 1.0) * 32767.0)
data.encode_s16(index * 2, s)
func _wav(data: PackedByteArray) -> AudioStreamWAV:
var w := AudioStreamWAV.new()
w.format = AudioStreamWAV.FORMAT_16_BITS
w.mix_rate = RATE
w.stereo = false
w.data = data
return w