135 lines
4.7 KiB
GDScript
135 lines
4.7 KiB
GDScript
extends Node
|
|
## Quiet industrial foley built from CC0 recordings; lives only inside the deck.
|
|
const ROOT := "res://assets/sounds/freighter/"
|
|
const VOICE_LIMIT := 4
|
|
var _streams: Dictionary = {}
|
|
var _beds: Array[AudioStreamPlayer] = []
|
|
var _voices: Array[AudioStreamPlayer] = []
|
|
var _panners: Array[AudioEffectPanner] = []
|
|
var _buses: Array[StringName] = []
|
|
var _rng := RandomNumberGenerator.new()
|
|
var _elapsed := 0.0
|
|
var _last_action := -10.0
|
|
var _last_service := -10.0
|
|
var _last_section := ""
|
|
var _next_tool := 9.0
|
|
var _next_console := 18.0
|
|
var _next_pressure := 32.0
|
|
var _last_tool := ""
|
|
var played_count := 0
|
|
var _maintenance_needed := false
|
|
var _alarm_due := INF
|
|
|
|
func _ready() -> void:
|
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
|
_rng.randomize()
|
|
for id in ["reactor_loop","ventilation_loop","tool_1","tool_2","tool_3","metal_1","metal_2","airlock_open","airlock_close","pressure_release","docking_clamp","service_chime","maintenance_alarm"]:
|
|
_streams[id] = load(ROOT+id+".wav")
|
|
for id in ["console_1","console_2"]: _streams[id] = load(ROOT+id+".ogg")
|
|
_start_bed("reactor_loop",-14.0)
|
|
_start_bed("ventilation_loop",-21.0)
|
|
for index in VOICE_LIMIT:
|
|
var bus := StringName("Frigate_%d_%d" % [get_instance_id(),index])
|
|
AudioServer.add_bus()
|
|
var bus_index := AudioServer.bus_count-1
|
|
AudioServer.set_bus_name(bus_index,bus)
|
|
AudioServer.set_bus_send(bus_index,&"Master")
|
|
var panner := AudioEffectPanner.new()
|
|
AudioServer.add_bus_effect(bus_index,panner)
|
|
_buses.append(bus)
|
|
_panners.append(panner)
|
|
var voice := AudioStreamPlayer.new()
|
|
voice.name = "Foley%d" % index
|
|
voice.bus = bus
|
|
add_child(voice)
|
|
_voices.append(voice)
|
|
play_effect("docking_clamp",0.65,-13.0,true)
|
|
_next_tool = _rng.randf_range(8,15)
|
|
_next_console = _rng.randf_range(18,30)
|
|
_next_pressure = _rng.randf_range(32,55)
|
|
|
|
func _start_bed(id: String, level: float) -> void:
|
|
var player := AudioStreamPlayer.new()
|
|
player.name = id.to_pascal_case()
|
|
var stream := (_streams[id] as AudioStreamWAV).duplicate() as AudioStreamWAV
|
|
stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
|
stream.loop_begin = 0
|
|
stream.loop_end = roundi(stream.get_length()*stream.mix_rate)
|
|
player.stream = stream
|
|
player.volume_db = -60
|
|
add_child(player)
|
|
player.play()
|
|
_beds.append(player)
|
|
create_tween().tween_property(player,"volume_db",level,1.2)
|
|
|
|
func play_effect(id: String, pan: float, level := -15.0, important := false) -> bool:
|
|
if not _streams.has(id): return false
|
|
var available := -1
|
|
for index in _voices.size():
|
|
if not _voices[index].playing:
|
|
available = index
|
|
break
|
|
if available < 0:
|
|
if not important: return false
|
|
available = 0
|
|
for index in _voices.size():
|
|
if float(_voices[index].get_meta("started",0.0)) < float(_voices[available].get_meta("started",0.0)): available = index
|
|
var voice := _voices[available]
|
|
voice.stop()
|
|
voice.stream = _streams[id]
|
|
voice.volume_db = level
|
|
voice.pitch_scale = 1.0 if id == "service_chime" else _rng.randf_range(0.97,1.03)
|
|
_panners[available].pan = clampf(pan,-0.75,0.75)
|
|
voice.set_meta("started",_elapsed)
|
|
voice.play()
|
|
played_count += 1
|
|
return true
|
|
|
|
func enter_section(id: String, pan: float) -> void:
|
|
if id == _last_section or _elapsed-_last_action < 0.7: return
|
|
_last_action = _elapsed
|
|
_last_section = id
|
|
play_effect("airlock_open",pan,-13.5,true)
|
|
|
|
func close_section() -> void:
|
|
if _last_section.is_empty(): return
|
|
_last_section = ""
|
|
if _elapsed-_last_action < 0.7: return
|
|
_last_action = _elapsed
|
|
play_effect("airlock_close",0.0,-17.0)
|
|
|
|
func service_completed(pan := 0.0) -> void:
|
|
if _elapsed-_last_service < 0.7: return
|
|
_last_service = _elapsed
|
|
play_effect("service_chime",pan,-20.0)
|
|
|
|
func set_maintenance_needed(value: bool) -> void:
|
|
if value and not _maintenance_needed: _alarm_due = _elapsed+4.0
|
|
if not value: _alarm_due = INF
|
|
_maintenance_needed = value
|
|
|
|
func _process(delta: float) -> void:
|
|
_elapsed += delta
|
|
if _elapsed >= _alarm_due:
|
|
play_effect("maintenance_alarm",0.6,-22.0)
|
|
_alarm_due = INF # One advisory per damaged-lander visit, no false emergency loop.
|
|
if _elapsed >= _next_tool:
|
|
var choices := ["tool_1","tool_2","tool_3","metal_1","metal_2"]
|
|
choices.erase(_last_tool)
|
|
_last_tool = choices[_rng.randi_range(0,choices.size()-1)]
|
|
play_effect(_last_tool,-0.48,_rng.randf_range(-17,-13))
|
|
_next_tool = _elapsed+_rng.randf_range(10,23)
|
|
if _elapsed >= _next_console:
|
|
play_effect("console_%d" % _rng.randi_range(1,2),-0.7,-25.0)
|
|
_next_console = _elapsed+_rng.randf_range(24,48)
|
|
if _elapsed >= _next_pressure:
|
|
play_effect("pressure_release",0.6,-23.0)
|
|
_next_pressure = _elapsed+_rng.randf_range(38,75)
|
|
|
|
func _exit_tree() -> void:
|
|
for voice in _voices: voice.stop()
|
|
for bed in _beds: bed.stop()
|
|
for bus in _buses:
|
|
var index := AudioServer.get_bus_index(bus)
|
|
if index >= 0: AudioServer.remove_bus(index)
|