Adding sound thanks to Event Adio plugin

This commit is contained in:
Yann Kervran 2024-11-28 10:46:27 +01:00
parent ba8e660588
commit 47eba63990
55 changed files with 1924 additions and 22 deletions

View file

@ -0,0 +1,55 @@
using Godot;
public partial class EventAudio : Node {
static private EventAudio _instance;
private GodotObject _eventAudio;
private Callable _play3DMethod;
private Callable _play2DMethod;
private Callable _stopMethod;
// Simple 'handle' class for managing Godot audio emitters.
public class EventAudioEmitter {
public GodotObject Emitter;
public EventAudioEmitter(GodotObject emitter) {
Emitter = emitter;
}
}
public override void _Ready() {
base._Ready();
_eventAudio = GetParent().GetNode("EventAudio");
_play3DMethod = _eventAudio.Get("play_3d").AsCallable();
_play2DMethod = _eventAudio.Get("play_2d").AsCallable();
_stopMethod = _eventAudio.Get("stop").AsCallable();
_instance = this;
}
public static EventAudio Instance {
get {
return _instance;
}
}
public EventAudioEmitter Play2D(string trigger, Node3D source) {
var result = _play2DMethod.Call(trigger, source);
var godotEmitter = result.AsGodotObject();
if (godotEmitter != null) {
return new EventAudioEmitter(godotEmitter);
}
return null;
}
public EventAudioEmitter Play3D(string trigger, Node3D source) {
var result = _play3DMethod.Call(trigger, source);
var godotEmitter = result.AsGodotObject();
if (godotEmitter != null) {
return new EventAudioEmitter(godotEmitter);
}
return null;
}
public void Stop(EventAudioEmitter emitter) {
_stopMethod.Call(emitter.Emitter);
}
}

View file

@ -0,0 +1,207 @@
extends Node
# Ideally this would be called just EventAudio, but that would class with the autoload
class_name EventAudioAPI
static var _separator := "+"
static var instance : EventAudioAPI
@export var log_lookups := false
@export var log_deaths := false
@export var log_registrations := false
var _trigger_map: Dictionary
var _rng: RandomNumberGenerator
var _audio_banks: Array[EAEventBank]
class AudioEmitter2D:
var source: Node2D
var player: AudioStreamPlayer2D
var event: EAEvent
var _active_emitters_2d = Array()
class AudioEmitter3D:
var source: Node3D
var player: AudioStreamPlayer3D
var event: EAEvent
var _active_emitters_3d = Array()
#---------------------------------------------------------
# API
#---------------------------------------------------------
static func get_instance() -> EventAudioAPI:
return instance
func play_2d(trigger: String, source: Node2D) -> AudioEmitter2D:
var event := _find_event_for_trigger(trigger)
if event == null:
return null
var stream_player = AudioStreamPlayer2D.new()
return _play_event(event, stream_player, source)
func play_3d(trigger: String, source: Node3D) -> AudioEmitter3D:
var event := _find_event_for_trigger(trigger)
if event == null:
return null
var stream_player = AudioStreamPlayer3D.new()
return _play_event(event, stream_player, source)
func stop(emitter):
if emitter.player != null:
emitter.player.stop()
func register_event_bank(bank: EAEventBank):
if log_registrations:
print("Registering bank: " + bank.resource_path)
_audio_banks.append(bank)
_invalidate_trigger_map()
func unregister_event_bank(bank: EAEventBank):
if log_registrations:
print("Unregistering bank: " + bank.resource_name)
var idx := _audio_banks.find(bank)
if idx >= 0:
_audio_banks.remove_at(idx)
_invalidate_trigger_map()
static func init_player_from_playback_settings(rng, stream_player, settings: EAEventPlaybackSettings):
var min_pitch := min(settings.min_pitch, settings.max_pitch) as float
var max_pitch := max(settings.min_pitch, settings.max_pitch) as float
var pitch = rng.randf_range(min_pitch, max_pitch)
stream_player.pitch_scale = pitch
stream_player.volume_db = settings.volume_db
if stream_player is AudioStreamPlayer3D:
stream_player.unit_size = settings.unit_size
stream_player.max_db = settings.max_db
stream_player.panning_strength = settings.panning_strength
elif stream_player is AudioStreamPlayer2D:
stream_player.max_distance = settings.max_distance
stream_player.attenuation = settings.attenuation
stream_player.panning_strength = settings.panning_strength
#---------------------------------------------------------
func _init():
_rng = RandomNumberGenerator.new()
func _process(_delta: float):
_active_emitters_2d = _process_active_audio(_active_emitters_2d)
_active_emitters_3d = _process_active_audio(_active_emitters_3d)
func _process_active_audio(active_audio):
var new_active_audio := Array()
# TODO - find a better way of modifying the list of active audio emitters
for audio in active_audio:
var alive := true
if audio.player == null:
alive = false
elif not audio.player.playing:
audio.player.queue_free()
audio.player = null
alive = false
elif audio.source == null:
if audio.event.playback_settings.stop_when_source_dies:
audio.player.stop()
alive = false
# Update the position
if not audio.event.playback_settings.stationary and alive and audio.source != null:
audio.player.global_position = audio.source.global_position
if alive:
new_active_audio.append(audio)
else:
_log_death(audio.event.trigger_tags)
return new_active_audio
func _enter_tree():
instance = self
func _exit_tree():
instance = null
#---------------------------------------------------------------------------------
# Internals
#---------------------------------------------------------------------------------
func _play_event(event: EAEvent, stream_player, source: Node):
var stream := event.get_weighted_random_stream(_rng.randf())
stream_player.name = "AudioPlayback"
add_child(stream_player)
stream_player.stream = stream
EventAudioAPI.init_player_from_playback_settings(_rng, stream_player, event.playback_settings)
if source:
stream_player.global_position = source.global_position
stream_player.play()
if stream_player is AudioStreamPlayer2D:
var emitter := AudioEmitter2D.new()
emitter.player = stream_player
emitter.source = source
emitter.event = event
_active_emitters_2d.append(emitter)
return emitter
else:
var emitter = AudioEmitter3D.new()
emitter.player = stream_player
emitter.source = source
emitter.event = event
_active_emitters_3d.append(emitter)
return emitter
func _invalidate_trigger_map():
_trigger_map = {}
func _make_trigger_map():
_trigger_map = {}
for bank: EAEventBank in _audio_banks:
for entry in bank.entries:
var key = entry.trigger_tags
_trigger_map[key] = entry
func _find_event_for_trigger(trigger: String) -> EAEvent:
if _trigger_map.size() == 0:
_make_trigger_map()
var current_trigger := trigger
while current_trigger != "":
_log_lookup(current_trigger)
var found_entry := _trigger_map.get(current_trigger) as EAEvent
if found_entry:
_log_found(found_entry.trigger_tags)
return found_entry
var tag_pos := current_trigger.rfind(_separator)
if tag_pos >= 0:
current_trigger = current_trigger.substr(0, tag_pos)
else:
current_trigger = ""
return null
func _log_lookup(msg: String):
if log_lookups:
print("Trying " + msg)
func _log_found(msg: String):
if log_lookups:
print("Found " + msg)
func _log_bank_add(msg: String):
if log_registrations:
print("Registering Bank " + msg)
func _log_bank_remove(msg: String):
if log_registrations:
print("Unregistering Bank " + msg)
func _log_death(msg: String):
if log_deaths:
print("Killing " + msg)

View file

@ -0,0 +1,59 @@
using Godot;
public partial class ExampleEmitter3D : Node3D {
[Export]
float Speed = 1.0f;
[Export]
Node3D OrbitNode;
EventAudio.EventAudioEmitter _loopEmitter;
float _orbitRadius = 1.0f;
public override void _Ready() {
_orbitRadius = (GlobalPosition - OrbitNode.GlobalPosition).Length();
}
public override void _Process(double _delta) {
float orbitAngle = (Time.GetTicksMsec() / 1000.0f % Speed) * 2.0f * 3.14159f;
float offset_x = _orbitRadius * Mathf.Cos(orbitAngle);
float offset_y = _orbitRadius * Mathf.Sin(orbitAngle);
var new_position = OrbitNode.GlobalPosition;
new_position.X += offset_x;
new_position.Z += offset_y;
GlobalPosition = new_position;
}
public override void _Input(InputEvent ev_) {
if (!(ev_ is InputEventKey) || !ev_.IsPressed()) {
return;
}
var ev = ev_ as InputEventKey;
if (ev.Keycode == Key.Key1) {
EventAudio.Instance.Play3D("hit", this);
}
if (ev.Keycode == Key.Key2) {
EventAudio.Instance.Play3D("hit+large", this);
}
if (ev.Keycode == Key.Key3) {
EventAudio.Instance.Play3D("hit+nonexistent", this);
}
if (ev.Keycode == Key.Key4) {
EventAudio.Instance.Play3D("random_shoot", this);
}
if (ev.Keycode == Key.Key5) {
if (_loopEmitter != null) {
EventAudio.Instance.Stop(_loopEmitter);
_loopEmitter = null;
} else {
_loopEmitter = EventAudio.Instance.Play3D("loop", this);
}
}
}
}

View file

@ -0,0 +1,31 @@
[gd_scene load_steps=6 format=3 uid="uid://fk1nqp2seuw6"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_bank_mounter.gd" id="1_0y6i8"]
[ext_resource type="Resource" uid="uid://cfxmacq8po281" path="res://addons/event_audio/example/example_audio_bank.tres" id="2_qo1e5"]
[ext_resource type="Script" path="res://addons/event_audio/example/ExampleEmitter3D.cs" id="3_w1shi"]
[ext_resource type="PackedScene" uid="uid://kjsaalyhmkc5" path="res://addons/event_audio/example/example_ui.tscn" id="4_e42vy"]
[sub_resource type="SphereMesh" id="SphereMesh_3bdsu"]
[node name="Example3d" type="Node3D"]
[node name="Camera3D" type="Camera3D" parent="."]
[node name="Node3D" type="Node3D" parent="." node_paths=PackedStringArray("OrbitNode")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -4.70263)
script = ExtResource("3_w1shi")
OrbitNode = NodePath("../Camera3D")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Node3D"]
mesh = SubResource("SphereMesh_3bdsu")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.176947, 0.98422, 0, -0.98422, 0.176947, 0, 1.9958, 0)
[node name="ExampleAudioBank" type="Node" parent="."]
script = ExtResource("1_0y6i8")
_audio_bank_resource = ExtResource("2_qo1e5")
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="ExampleUi" parent="CanvasLayer" instance=ExtResource("4_e42vy")]

View file

@ -0,0 +1,33 @@
[gd_scene load_steps=6 format=3 uid="uid://konnaekcsk2e"]
[ext_resource type="Script" path="res://addons/event_audio/example/example_emitter_2d.gd" id="1_7u4pm"]
[ext_resource type="Resource" uid="uid://cfxmacq8po281" path="res://addons/event_audio/example/example_audio_bank.tres" id="3_kb4ey"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_bank_mounter.gd" id="3_q1afy"]
[ext_resource type="PackedScene" uid="uid://kjsaalyhmkc5" path="res://addons/event_audio/example/example_ui.tscn" id="4_kisj8"]
[sub_resource type="SphereMesh" id="SphereMesh_7scev"]
radius = 10.0
height = 20.0
[node name="Example2d" type="Node2D"]
[node name="Emitter" type="MeshInstance2D" parent="."]
position = Vector2(2.08165e-12, 175.95)
mesh = SubResource("SphereMesh_7scev")
script = ExtResource("1_7u4pm")
[node name="Camera2D" type="Camera2D" parent="."]
position = Vector2(578, 324)
[node name="AudioListener2D" type="AudioListener2D" parent="Camera2D"]
current = true
[node name="TestAudioBank" type="Node" parent="."]
script = ExtResource("3_q1afy")
_audio_bank_resource = ExtResource("3_kb4ey")
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="ExampleUi" parent="CanvasLayer" instance=ExtResource("4_kisj8")]

View file

@ -0,0 +1,31 @@
[gd_scene load_steps=6 format=3 uid="uid://b12tpedbx38u7"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_bank_mounter.gd" id="1_ncilt"]
[ext_resource type="Resource" uid="uid://cfxmacq8po281" path="res://addons/event_audio/example/example_audio_bank.tres" id="2_nj06b"]
[ext_resource type="Script" path="res://addons/event_audio/example/example_emitter_3d.gd" id="3_wpepl"]
[ext_resource type="PackedScene" uid="uid://kjsaalyhmkc5" path="res://addons/event_audio/example/example_ui.tscn" id="4_sw6y2"]
[sub_resource type="SphereMesh" id="SphereMesh_3bdsu"]
[node name="Example3d" type="Node3D"]
[node name="Camera3D" type="Camera3D" parent="."]
[node name="Node3D" type="Node3D" parent="." node_paths=PackedStringArray("OrbitNode")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -4.70263)
script = ExtResource("3_wpepl")
OrbitNode = NodePath("../Camera3D")
[node name="MeshInstance3D" type="MeshInstance3D" parent="Node3D"]
mesh = SubResource("SphereMesh_3bdsu")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.176947, 0.98422, 0, -0.98422, 0.176947, 0, 1.9958, 0)
[node name="TestAudioBank" type="Node" parent="."]
script = ExtResource("1_ncilt")
_audio_bank_resource = ExtResource("2_nj06b")
[node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="ExampleUi" parent="CanvasLayer" instance=ExtResource("4_sw6y2")]

View file

@ -0,0 +1,94 @@
[gd_resource type="Resource" script_class="EAEventBank" load_steps=17 format=3 uid="uid://cfxmacq8po281"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event.gd" id="1_fy8lm"]
[ext_resource type="AudioStream" uid="uid://7ncmg0ox4nxr" path="res://addons/event_audio/example/sounds/loop.wav" id="2_fyotq"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_bank.gd" id="2_rbntb"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_playback_settings.gd" id="3_fy5gc"]
[ext_resource type="AudioStream" uid="uid://byg26ypxea5qh" path="res://addons/event_audio/example/sounds/laser1.wav" id="3_nyuuq"]
[ext_resource type="AudioStream" uid="uid://ftyi4ikfmbii" path="res://addons/event_audio/example/sounds/laser2.wav" id="4_ahbca"]
[ext_resource type="AudioStream" uid="uid://cqo6bi2ygpwbu" path="res://addons/event_audio/example/sounds/shot1.wav" id="4_fpv34"]
[ext_resource type="AudioStream" uid="uid://cultflwybpfo1" path="res://addons/event_audio/example/sounds/shot2.wav" id="5_1yf0v"]
[sub_resource type="Resource" id="Resource_dd35s"]
script = ExtResource("3_fy5gc")
stop_when_source_dies = false
stationary = true
volume_db = 0.0
min_pitch = 0.8
max_pitch = 1.2
panning_strength = 1.0
attenuation = 1.0
max_distance = 2000
unit_size = 10.0
max_db = 3.0
[sub_resource type="Resource" id="Resource_lfo5e"]
script = ExtResource("1_fy8lm")
audio_streams = Array[AudioStream]([ExtResource("4_fpv34")])
probability_weights = Array[float]([1.0])
trigger_tags = "hit"
playback_settings = SubResource("Resource_dd35s")
[sub_resource type="Resource" id="Resource_j54ca"]
script = ExtResource("3_fy5gc")
stop_when_source_dies = false
stationary = true
volume_db = 0.0
min_pitch = 0.9
max_pitch = 1.2
panning_strength = 1.0
attenuation = 1.0
max_distance = 2000
unit_size = 10.0
max_db = 3.0
[sub_resource type="Resource" id="Resource_d2op2"]
script = ExtResource("1_fy8lm")
audio_streams = Array[AudioStream]([ExtResource("5_1yf0v")])
probability_weights = Array[float]([1.0])
trigger_tags = "hit+large"
playback_settings = SubResource("Resource_j54ca")
[sub_resource type="Resource" id="Resource_ljdcd"]
script = ExtResource("3_fy5gc")
stop_when_source_dies = false
stationary = false
volume_db = 0.0
min_pitch = 0.8
max_pitch = 1.2
panning_strength = 2.0
attenuation = 1.0
max_distance = 2000
unit_size = 10.0
max_db = 3.0
[sub_resource type="Resource" id="Resource_ps0cx"]
script = ExtResource("1_fy8lm")
audio_streams = Array[AudioStream]([ExtResource("2_fyotq")])
probability_weights = Array[float]([1.0])
trigger_tags = "loop"
playback_settings = SubResource("Resource_ljdcd")
[sub_resource type="Resource" id="Resource_1xrvu"]
script = ExtResource("3_fy5gc")
stop_when_source_dies = false
stationary = true
volume_db = 0.0
min_pitch = 0.8
max_pitch = 1.2
panning_strength = 1.0
attenuation = 1.0
max_distance = 2000
unit_size = 10.0
max_db = 3.0
[sub_resource type="Resource" id="Resource_xjlcv"]
script = ExtResource("1_fy8lm")
audio_streams = Array[AudioStream]([ExtResource("3_nyuuq"), ExtResource("4_ahbca")])
probability_weights = Array[float]([1.0, 1.0])
trigger_tags = "random_shoot"
playback_settings = SubResource("Resource_1xrvu")
[resource]
script = ExtResource("2_rbntb")
entries = Array[ExtResource("1_fy8lm")]([SubResource("Resource_lfo5e"), SubResource("Resource_d2op2"), SubResource("Resource_ps0cx"), SubResource("Resource_xjlcv")])

View file

@ -0,0 +1,48 @@
extends Node2D
class_name ExampleEmitter2D
@export var Speed := 1.0
var _loop_emitter : EventAudioAPI.AudioEmitter2D
func _init():
EventAudio.log_lookups = true
EventAudio.log_registrations = true
EventAudio.log_deaths = true
func _process(delta: float):
var screen_width := get_viewport_rect().size.x
var step := Speed * screen_width * delta
var new_position := global_position
new_position.x += step
if new_position.x < 0:
new_position.x = 0
Speed = -Speed
elif new_position.x >= screen_width:
new_position.x = screen_width
Speed = -Speed
global_position = new_position
func _input(event: InputEvent):
if not event is InputEventKey or not event.is_pressed():
return
if event.keycode == KEY_1:
EventAudio.play_2d("hit", self)
if event.keycode == KEY_2:
EventAudio.play_2d("hit+large", self)
if event.keycode == KEY_3:
EventAudio.play_2d("hit+nonexistent", self)
if event.keycode == KEY_4:
EventAudio.play_2d("random_shoot", self)
if event.keycode == KEY_5:
if _loop_emitter:
EventAudio.stop(_loop_emitter)
_loop_emitter = null
else:
_loop_emitter = EventAudio.play_2d("loop", self)

View file

@ -0,0 +1,50 @@
extends Node3D
class_name ExampleEmitter3D
@export var Speed := 1.0
@export var OrbitNode : Node3D
var _loop_emitter : EventAudioAPI.AudioEmitter3D
var _orbit_radius := 1.0
func _init():
EventAudio.log_lookups = true
EventAudio.log_registrations = true
EventAudio.log_deaths = true
func _ready():
_orbit_radius = (global_position - OrbitNode.global_position).length()
func _process(_delta: float):
var orbit_angle = fmod(Time.get_ticks_msec() / 1000.0, Speed) * 2 * PI
var offset_x = _orbit_radius * cos(orbit_angle)
var offset_y = _orbit_radius * sin(orbit_angle)
var new_position := OrbitNode.global_position
new_position.x += offset_x
new_position.z += offset_y
global_position = new_position
func _input(event: InputEvent):
if not event is InputEventKey or not event.is_pressed():
return
if event.keycode == KEY_1:
EventAudio.play_3d("hit", self)
if event.keycode == KEY_2:
EventAudio.play_3d("hit+large", self)
if event.keycode == KEY_3:
EventAudio.play_3d("hit+nonexistent", self)
if event.keycode == KEY_4:
EventAudio.play_3d("random_shoot", self)
if event.keycode == KEY_5:
if _loop_emitter:
EventAudio.stop(_loop_emitter)
_loop_emitter = null
else:
_loop_emitter = EventAudio.play_3d("loop", self)

View file

@ -0,0 +1,24 @@
[gd_scene format=3 uid="uid://kjsaalyhmkc5"]
[node name="ExampleUi" type="Control"]
layout_mode = 3
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
grow_vertical = 0
[node name="Label" type="Label" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -127.0
offset_right = 196.0
grow_vertical = 0
text = "1 - Play \"hit\"
2 - Play \"hit+large\"
3 - Play \"hit+nonexistent\"
4 - Play \"random_shoot\"
5 - Toggle \"loop\""
vertical_alignment = 2
justification_flags = 161

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://byg26ypxea5qh"
path="res://.godot/imported/laser1.wav-7c7bf6a86d0428120783ce6ae72e8afb.sample"
[deps]
source_file="res://addons/event_audio/example/sounds/laser1.wav"
dest_files=["res://.godot/imported/laser1.wav-7c7bf6a86d0428120783ce6ae72e8afb.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://ftyi4ikfmbii"
path="res://.godot/imported/laser2.wav-396964f333f7fcbda54278f155b07608.sample"
[deps]
source_file="res://addons/event_audio/example/sounds/laser2.wav"
dest_files=["res://.godot/imported/laser2.wav-396964f333f7fcbda54278f155b07608.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://7ncmg0ox4nxr"
path="res://.godot/imported/loop.wav-fa3b1ef3ac7c882255ced1130766deef.sample"
[deps]
source_file="res://addons/event_audio/example/sounds/loop.wav"
dest_files=["res://.godot/imported/loop.wav-fa3b1ef3ac7c882255ced1130766deef.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=2
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cqo6bi2ygpwbu"
path="res://.godot/imported/shot1.wav-d93bbca9f7efde511f9d9572265cbb25.sample"
[deps]
source_file="res://addons/event_audio/example/sounds/shot1.wav"
dest_files=["res://.godot/imported/shot1.wav-d93bbca9f7efde511f9d9572265cbb25.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cultflwybpfo1"
path="res://.godot/imported/shot2.wav-ac04745e5c74f182ec001473606b2120.sample"
[deps]
source_file="res://addons/event_audio/example/sounds/shot2.wav"
dest_files=["res://.godot/imported/shot2.wav-ac04745e5c74f182ec001473606b2120.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

BIN
addons/event_audio/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d3rrns80mmgqo"
path="res://.godot/imported/icon.png-5ff47921d459c4c7814e206e0a2e0cc0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icon.png"
dest_files=["res://.godot/imported/icon.png-5ff47921d459c4c7814e206e0a2e0cc0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 149 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dqg7mfau2g7ck"
path="res://.godot/imported/Add.svg-219bdd038475c83ffbebee883f646de0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icons/Add.svg"
dest_files=["res://.godot/imported/Add.svg-219bdd038475c83ffbebee883f646de0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 3 10 10M3 13 13 3" fill="none" stroke="#e0e0e0" stroke-width="2"/></svg>

After

Width:  |  Height:  |  Size: 168 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://pad8hlsq2shw"
path="res://.godot/imported/Close.svg-326dec14804498c2ab2273a4a572c2e2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icons/Close.svg"
dest_files=["res://.godot/imported/Close.svg-326dec14804498c2ab2273a4a572c2e2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 12a1 1 0 0 0 1.555.832l6-4a1 1 0 0 0 0-1.664l-6-4A1 1 0 0 0 4 4z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 184 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://byclw0utbwbam"
path="res://.godot/imported/Play.svg-33c5bae738cb342d56c12b8caaf4909d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icons/Play.svg"
dest_files=["res://.godot/imported/Play.svg-33c5bae738cb342d56c12b8caaf4909d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1C1 1 1 1 1 8s0 7 7 7 7 0 7-7 0-7-7-7zM4.25 2.25a1 1 0 0 1 0 4 1 1 0 0 1 0-4zM8 6a1 1 0 0 1 0 4 1 1 0 0 1 0-4zm3.75 3.75a1 1 0 0 1 0 4 1 1 0 0 1 0-4z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 266 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dcbbwdp16tdns"
path="res://.godot/imported/RandomNumberGenerator.svg-307a8ce8bcb247fd591f4766aea337ae.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icons/RandomNumberGenerator.svg"
dest_files=["res://.godot/imported/RandomNumberGenerator.svg-307a8ce8bcb247fd591f4766aea337ae.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1v1h-4v2h14v-2h-4v-1zm-3 4v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-8zm1 2h2v6h-2zm4 0h2v6h-2zm4 0h2v6h-2z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 218 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://djgktanb5swk6"
path="res://.godot/imported/Remove.svg-dad4f6b2d936a8b9536e5322302cb088.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icons/Remove.svg"
dest_files=["res://.godot/imported/Remove.svg-dad4f6b2d936a8b9536e5322302cb088.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 1 3 3l1 2v4H2v3.5a2.5 2.5 0 0 0 5 0V9H5V5l1-2-1-2zm6 .174a3 3 0 0 0 0 5.652V14a1 1 0 0 0 2 0V6.824a3 3 0 0 0 0-5.648V4a1 1 0 0 1-2 0z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 253 B

View file

@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://i4qa40akvjaa"
path="res://.godot/imported/Tools.svg-4c4437be5a640503622d98eb51e3bd18.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/event_audio/icons/Tools.svg"
dest_files=["res://.godot/imported/Tools.svg-4c4437be5a640503622d98eb51e3bd18.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,6 @@
[plugin]
name="EventAudio"
description=""
author="Simon Carter"
version="1.0.1"
script="plugin.gd"

View file

@ -0,0 +1,33 @@
@tool
extends EditorPlugin
var _editor_plugin = preload("res://addons/event_audio/src/ea_event_bank_inspector.gd").new()
var _runtime_scene = "res://addons/event_audio/scenes/event_audio_autoload.tscn"
func _enter_tree():
var mono_supported = ClassDB.class_exists("CSharpScript")
if mono_supported:
print ("Mono supported")
else:
print ("Mono unsupported")
add_inspector_plugin(_editor_plugin)
if not ProjectSettings.has_setting("autoload/EventAudio"):
add_autoload_singleton("EventAudio", _runtime_scene)
if mono_supported:
if not ProjectSettings.has_setting("autoload/EventAudioCSharp"):
var _cs_script = "res://addons/event_audio/EventAudio.cs"
add_autoload_singleton("EventAudioCSharp", _cs_script)
func _exit_tree():
if _editor_plugin != null:
remove_inspector_plugin(_editor_plugin)
if ProjectSettings.has_setting("autoload/EventAudio"):
remove_autoload_singleton("EventAudio")
if ProjectSettings.has_setting("autoload/EventAudioCSharp"):
remove_autoload_singleton("EventAudioCSharp")

View file

@ -0,0 +1,68 @@
[gd_scene load_steps=6 format=3 uid="uid://co2eglljfwoxo"]
[ext_resource type="Texture2D" uid="uid://pad8hlsq2shw" path="res://addons/event_audio/icons/Close.svg" id="1_2kmlt"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_edit_control.gd" id="1_melcq"]
[ext_resource type="Texture2D" uid="uid://i4qa40akvjaa" path="res://addons/event_audio/icons/Tools.svg" id="2_31ihp"]
[ext_resource type="Texture2D" uid="uid://dcbbwdp16tdns" path="res://addons/event_audio/icons/RandomNumberGenerator.svg" id="3_ikqib"]
[ext_resource type="PackedScene" uid="uid://b84km51g3vs0n" path="res://addons/event_audio/scenes/bank_resource_line.tscn" id="5_nou6o"]
[node name="TriggerLine" type="HBoxContainer" node_paths=PackedStringArray("delete_trigger_button", "play_random_button", "trigger_name_edit", "settings_button", "stream_settings_list")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -38.0
offset_bottom = -655.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
script = ExtResource("1_melcq")
delete_trigger_button = NodePath("HBoxContainer/DeleteButton")
play_random_button = NodePath("HBoxContainer/RandomButton")
trigger_name_edit = NodePath("HBoxContainer/TriggerEdit")
settings_button = NodePath("HBoxContainer/SettingsButton")
stream_settings_list = NodePath("BoxContainer/ResourceList")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
[node name="DeleteButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
tooltip_text = "Delete this event."
icon = ExtResource("1_2kmlt")
[node name="TriggerEdit" type="LineEdit" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "The string that will trigger this audio event.
Trigger strings are chopped right-to-left at the seperator character until a match is found."
placeholder_text = "trigger string"
[node name="SettingsButton" type="Button" parent="HBoxContainer"]
custom_minimum_size = Vector2(16, 16)
layout_mode = 2
tooltip_text = "Playback settings."
icon = ExtResource("2_31ihp")
icon_alignment = 1
[node name="RandomButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
tooltip_text = "Trigger this audio. Plays back a random audio variant, taking into account the variant weights."
icon = ExtResource("3_ikqib")
[node name="BoxContainer" type="BoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
[node name="ResourceList" type="VBoxContainer" parent="BoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ResourceLine" parent="BoxContainer/ResourceList" instance=ExtResource("5_nou6o")]
visible = false
layout_mode = 2
[node name="ResourceLine2" parent="BoxContainer/ResourceList" instance=ExtResource("5_nou6o")]
visible = false
layout_mode = 2

View file

@ -0,0 +1,76 @@
[gd_scene load_steps=5 format=3 uid="uid://b84km51g3vs0n"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_stream_edit_control.gd" id="1_fwtai"]
[ext_resource type="Texture2D" uid="uid://byclw0utbwbam" path="res://addons/event_audio/icons/Play.svg" id="2_jis6r"]
[ext_resource type="Texture2D" uid="uid://dqg7mfau2g7ck" path="res://addons/event_audio/icons/Add.svg" id="3_aknch"]
[ext_resource type="Texture2D" uid="uid://djgktanb5swk6" path="res://addons/event_audio/icons/Remove.svg" id="4_il77p"]
[node name="ResourceLine" type="HBoxContainer" node_paths=PackedStringArray("delete_event_button", "add_stream_button", "play_button", "audio_label")]
offset_right = 328.0
offset_bottom = 40.0
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_fwtai")
delete_event_button = NodePath("DeleteButton")
add_stream_button = NodePath("AddButton")
play_button = NodePath("PlayButton")
audio_label = NodePath("ResourcePicker/ResourceLabel")
[node name="PlayButton" type="Button" parent="."]
layout_mode = 2
tooltip_text = "Play this audio variant."
icon = ExtResource("2_jis6r")
[node name="WeightSliderContainer" type="FlowContainer" parent="."]
custom_minimum_size = Vector2(64, 0)
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.5
tooltip_text = "The weighted probabily this variant will play.
Only used when there is more than one variant."
alignment = 1
[node name="ResourcePicker" type="Control" parent="."]
clip_contents = true
custom_minimum_size = Vector2(128, 0)
layout_mode = 2
size_flags_horizontal = 3
[node name="ResourcePickerContainer" type="BoxContainer" parent="ResourcePicker"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
alignment = 1
vertical = true
[node name="ResourceLabel" type="Label" parent="ResourcePicker"]
visible = false
layout_mode = 1
anchors_preset = -1
anchor_left = 0.234
anchor_right = 0.703
anchor_bottom = 1.0
offset_left = 0.0479984
offset_right = 0.0159988
grow_horizontal = 2
grow_vertical = 2
horizontal_alignment = 1
vertical_alignment = 1
clip_text = true
text_overrun_behavior = 1
[node name="AddButton" type="Button" parent="."]
layout_mode = 2
size_flags_vertical = 3
tooltip_text = "Add an audio variant to the trigger."
icon = ExtResource("3_aknch")
[node name="DeleteButton" type="Button" parent="."]
layout_mode = 2
size_flags_vertical = 3
tooltip_text = "Delete this variant."
icon = ExtResource("4_il77p")

View file

@ -0,0 +1,8 @@
[gd_scene load_steps=2 format=3 uid="uid://be741v81fj3xc"]
[ext_resource type="Script" path="res://addons/event_audio/event_audio.gd" id="1_75kvd"]
[node name="EventAudio" type="Node"]
script = ExtResource("1_75kvd")
[node name="EventAudioAutoload" type="Node" parent="."]

View file

@ -0,0 +1,201 @@
class_name EAEditorTools
static var _editor_stream_player : AudioStreamPlayer
static var _global_rng : RandomNumberGenerator
static func get_global_rng() -> RandomNumberGenerator:
if _global_rng == null:
_global_rng = RandomNumberGenerator.new()
return _global_rng
static func play_sound(entry: EAEvent, stream: AudioStream):
var main_screen = EditorInterface.get_editor_main_screen()
var audio = _editor_stream_player
if audio == null:
audio = AudioStreamPlayer.new()
audio.name = "_EditorAudio"
main_screen.add_child(audio)
_editor_stream_player = audio
EventAudioAPI.init_player_from_playback_settings(get_global_rng(), audio, entry.playback_settings)
audio.stop()
audio.stream = stream
audio.play()
static func stop_sound():
var audio := _editor_stream_player
if audio != null:
print("Stopping audio")
audio.stop()
audio.stream = null
static func make_property_panel(obj: Object, title: String, excludes : Dictionary, change_callback : Callable) -> Container:
var panel := PanelContainer.new()
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
panel.modulate = Color(0.8, 0.8, 1)
var property_container := VBoxContainer.new()
property_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
panel.add_child(property_container)
if title != "":
var title_label := Label.new()
title_label.text = title
title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
property_container.add_child(title_label)
for prop in obj.get_property_list():
if not prop.name in excludes:
var val = obj.get(prop.name)
var property_control := _make_property_control(prop, val, change_callback.bind(obj, prop.name))
if property_control != null:
property_container.add_child(property_control)
return panel
static func _make_property_control(prop, initial_value, update_callback: Callable) -> Control:
# print(prop)
var control : Control
if prop.usage & PROPERTY_USAGE_STORAGE:
control = HBoxContainer.new()
control.size_flags_horizontal = Control.SIZE_EXPAND_FILL
var text_label = Label.new()
text_label.text = _property_name_to_display_name(prop.name)
text_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
control.add_child(text_label)
var prop_type: Variant.Type = prop["type"]
var prop_range : Dictionary = _parse_range(prop)
match prop_type:
TYPE_BOOL:
var prop_editor = CheckBox.new()
prop_editor.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if initial_value != null:
prop_editor.button_pressed = initial_value
prop_editor.toggled.connect(update_callback)
control.add_child(prop_editor)
TYPE_INT, TYPE_FLOAT:
var prop_editor := EditorSpinSlider.new()
var default_step = null
if prop_range.has("step"):
default_step = prop_range["step"]
# For floats, show a normal input box if the step has been set to 1.
# For ints, show a normal input box if the step has been set to 1, or left out.
# Otherwise show a slider
if prop_type == TYPE_FLOAT:
if default_step == null:
prop_editor.hide_slider = false
prop_editor.step = 0.0001
elif default_step == 1:
prop_editor.step = 1
else:
prop_editor.step = default_step
prop_editor.hide_slider = false
else:
if default_step == null or default_step == 1:
prop_editor.step = 1
else:
prop_editor.step = default_step
prop_editor.hide_slider = false
prop_editor.allow_lesser = prop_range.has("or_lesser")
prop_editor.allow_greater = prop_range.has("or_greater")
if prop_range.has("min"):
prop_editor.min_value = prop_range["min"]
if prop_range.has("max"):
prop_editor.max_value = prop_range["max"]
prop_editor.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if initial_value != null:
prop_editor.value = initial_value
prop_editor.value_changed.connect(update_callback)
control.add_child(prop_editor)
TYPE_STRING, TYPE_STRING_NAME, TYPE_NODE_PATH:
var prop_editor = LineEdit.new()
prop_editor.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if initial_value != null:
prop_editor.text = initial_value
prop_editor.text_changed.connect(update_callback)
control.add_child(prop_editor)
_:
pass
elif prop.usage & PROPERTY_USAGE_GROUP:
var group_label := Label.new()
group_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
group_label.text = prop.name
control = group_label
return control
static func _parse_range(prop) -> Dictionary:
if prop["hint"] != PROPERTY_HINT_RANGE:
return {}
var prop_range := {}
var parts := prop["hint_string"].split(",") as PackedStringArray
var is_float : bool = prop["type"] == TYPE_FLOAT
if parts.size() > 0:
if is_float:
prop_range["min"] = parts[0].to_float()
else:
prop_range["min"] = parts[0].to_int()
if parts.size() > 1:
if is_float:
prop_range["max"] = parts[1].to_float()
else:
prop_range["max"] = parts[1].to_int()
if parts.size() > 2:
if is_float:
prop_range["step"] = parts[2].to_float()
else:
prop_range["step"] = parts[2].to_int()
if parts.size() > 3:
match parts[3]:
"or_lesser":
prop_range["or_lesser"] = true
"or_greater":
prop_range["or_greater"] = true
if parts.size() > 4:
match parts[4]:
"or_lesser":
prop_range["or_lesser"] = true
"or_greater":
prop_range["or_greater"] = true
_:
pass
return prop_range
static func _property_name_to_display_name(name: String):
var name_snake := name.to_snake_case()
var parts := name_snake.split("_")
var display_name := ""
for c1 : int in parts.size():
var part := parts[c1]
display_name += part.to_pascal_case()
if c1 < parts.size() - 1:
display_name += " "
return display_name

View file

@ -0,0 +1,40 @@
@tool
extends Resource
class_name EAEvent
@export var audio_streams : Array[AudioStream] = []
@export var probability_weights : Array[float] = []
@export var trigger_tags: String = ""
@export var playback_settings: EAEventPlaybackSettings
func _init():
if audio_streams.size() == 0:
audio_streams.push_front(null)
probability_weights.push_front(1.0)
playback_settings = EAEventPlaybackSettings.new()
func add_stream(index: int):
audio_streams.insert(index+1, null)
probability_weights.insert(index+1, 1.0)
func remove_stream(index: int):
audio_streams.remove_at(index)
probability_weights.remove_at(index)
func get_weighted_random_stream(random: float) -> AudioStream:
var total_weight := 0.0
for w : float in probability_weights:
total_weight = total_weight + w
var r := random * total_weight
var num_entries := probability_weights.size()
var weight_count := 0.0
for i in num_entries:
if i + 1 == num_entries:
return audio_streams[i]
elif r <= weight_count + probability_weights[i]:
return audio_streams[i]
weight_count += probability_weights[i]
return null

View file

@ -0,0 +1,27 @@
@tool
extends Resource
class_name EAEventBank
@export var entries: Array[EAEvent]
func add_entry():
entries.insert(0, EAEvent.new())
func delete_entry(entry: EAEvent):
var entry_idx := entries.find(entry)
if entry_idx >= 0:
entries.remove_at(entry_idx)
func find_entry_with_trigger(trigger: String) -> EAEvent:
for entry: EAEvent in entries:
if entry.trigger_tags == trigger:
return entry
return null
func _sort_func(a: EAEvent, b: EAEvent) -> bool:
return a.trigger_tags < b.trigger_tags
func sort_by_trigger():
entries.sort_custom(_sort_func)

View file

@ -0,0 +1,11 @@
@tool
extends EditorInspectorPlugin
func _can_handle(object):
return object is EAEventBank
func _parse_property(_object, _type, name, _hint_type, _hint_string, _usage_flags, _wide):
if name == "entries":
add_property_editor(name, EAEventBankProperty.new())
return true
return false

View file

@ -0,0 +1,13 @@
extends Node
class_name EAEventBankMounter
@export var _audio_bank_resource : EAEventBank
# @export var audioBankResources: Array[EAEventBank]
func _enter_tree():
var player := EventAudio.instance
player.register_event_bank(_audio_bank_resource)
func _exit_tree():
var player := EventAudio.instance
player.unregister_event_bank(_audio_bank_resource)

View file

@ -0,0 +1,109 @@
@tool
extends EditorProperty
class_name EAEventBankProperty
var _audio_bank_line_scene := preload("res://addons/event_audio/scenes/bank_line.tscn")
var _audio_bank_resource_line_scene := preload("res://addons/event_audio/scenes/bank_resource_line.tscn")
var _resource: EAEventBank
var _property_name: StringName
var _entries: Array[EAEvent]
var _root_container := VBoxContainer.new()
var _focus_on_trigger : String = ""
#----------------------------------------------
# Godot call to update rendering
func _update_property():
# print("updating property")
var open_settings := {}
for control : Node in _root_container.get_children():
if control is EAEventEditControl and control.is_settings_open():
open_settings[control.get_event()] = true
_root_container.remove_child(control)
control.queue_free()
_make_lines(open_settings)
# Search for the trigger we were looking at previously.
if _focus_on_trigger == "":
return
await get_tree().process_frame
# Find the trigger we were editing previously and select it.
for control : Control in _root_container.get_children():
if control is not EAEventEditControl:
continue
var line = control as EAEventEditControl
if line.trigger_name_edit.text == _focus_on_trigger:
EditorInterface.get_inspector().ensure_control_visible(line.trigger_name_edit)
break
_focus_on_trigger = ""
func _enter_tree():
_property_name = get_edited_property()
_resource = get_edited_object() as EAEventBank
_entries = _resource.entries
_root_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_make_lines()
add_child(_root_container)
set_bottom_editor(_root_container)
func _exit_tree() -> void:
EAEditorTools.stop_sound()
func sort_bank():
_resource.sort_by_trigger()
func signal_entry_changed(update_ui: bool):
emit_changed(_property_name, _entries, "", not update_ui)
func set_focus_on_trigger(trigger: String):
_focus_on_trigger = trigger
func delete_event(event: EAEvent):
_resource.delete_entry(event)
signal_entry_changed(true)
#--------------------------------------
func _make_lines(setting_to_restore = {}):
var add_button := Button.new()
add_button.text = "Add Entry"
add_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
add_button.pressed.connect(_on_add_entry_button_clicked)
_root_container.add_child(add_button)
var stop_button := Button.new()
stop_button.text = "Stop playback"
stop_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
stop_button.pressed.connect(_on_stop_button_clicked)
_root_container.add_child(stop_button)
for entry : EAEvent in _resource.entries:
var settings_open := setting_to_restore.has(entry)
_makeEntryLine(entry, settings_open)
func _makeEntryLine(entry: EAEvent, settings_open: bool):
var line := _audio_bank_line_scene.instantiate() as EAEventEditControl
_root_container.add_child(line)
line.create(self, entry, settings_open)
for c1: int in entry.audio_streams.size():
var resource_line := _audio_bank_resource_line_scene.instantiate() as EAStreamEditControl
line.stream_settings_list.add_child(resource_line)
resource_line.create(self, entry, c1, entry.audio_streams.size() == 1)
func _on_add_entry_button_clicked():
_resource.add_entry()
signal_entry_changed(true)
func _on_stop_button_clicked():
EAEditorTools.stop_sound()

View file

@ -0,0 +1,91 @@
@tool
extends Container
class_name EAEventEditControl
@export var delete_trigger_button : BaseButton
@export var play_random_button : BaseButton
@export var trigger_name_edit : LineEdit
@export var settings_button : BaseButton
@export var stream_settings_list : Container
var _exclude_props = {"resource_local_to_scene": true, "Resource": true, "resource_name": true, "script": true}
var _settings_panel : Control
var _event : EAEvent
var _bank_inspector : EAEventBankProperty
func create(bank_inspector: EAEventBankProperty, event: EAEvent, open_settings: bool) -> void:
_event = event
_bank_inspector = bank_inspector
delete_trigger_button.pressed.connect(_on_delete_entry_button_clicked)
trigger_name_edit.set_text(_event.trigger_tags)
trigger_name_edit.text_submitted.connect(_on_trigger_submitted)
trigger_name_edit.text_changed.connect(_on_trigger_changed)
settings_button.pressed.connect(_on_settings_button_clicked)
play_random_button.pressed.connect(_on_play_random_button_clicked)
if open_settings:
var panel := _make_settings_panel()
self.add_sibling(panel)
_settings_panel = panel
func is_settings_open():
return _settings_panel != null
func get_event():
return _event
func _on_settings_button_clicked():
_toggle_settings_panel()
func _on_settings_entry_changed(val, settings: EAEventPlaybackSettings, member: StringName):
settings.set(member, val)
_bank_inspector.signal_entry_changed(false)
func _on_delete_entry_button_clicked():
_bank_inspector.delete_event(_event)
func _check_trigger_name(entry: EAEvent, trigger: String) -> bool:
var bank_entry := _bank_inspector._resource.find_entry_with_trigger(trigger)
if bank_entry and bank_entry != entry:
return false
return true
func _on_trigger_changed(trigger: String):
# If the trigger isn't valid, show an error color
if _check_trigger_name(_event, trigger):
trigger_name_edit.modulate = Color.WHITE
else:
trigger_name_edit.modulate = Color.RED
func _on_trigger_submitted(trigger: String):
if _check_trigger_name(_event, trigger):
_event.trigger_tags = trigger
trigger_name_edit.release_focus()
# Re-sort the bank - this may trigger a UI update, so make sure we focus on the right control
_bank_inspector.set_focus_on_trigger(trigger)
_bank_inspector.sort_bank()
_bank_inspector.signal_entry_changed(true)
func _on_play_random_button_clicked():
var roll := EAEditorTools.get_global_rng().randf_range(0, 1.0)
var stream := _event.get_weighted_random_stream(roll)
if stream:
EAEditorTools.play_sound(_event, stream)
func _toggle_settings_panel():
if _settings_panel:
_settings_panel.get_parent().remove_child(_settings_panel)
_settings_panel.queue_free()
_settings_panel = null
else:
var panel := _make_settings_panel()
_settings_panel = panel
self.add_sibling(panel)
func _make_settings_panel() -> Container:
return EAEditorTools.make_property_panel(
_event.playback_settings, "Playback Settings", _exclude_props, _on_settings_entry_changed)

View file

@ -0,0 +1,22 @@
extends Resource
class_name EAEventPlaybackSettings
## The factor for the attenuation effect. Higher values make the sound audible over a larger distance.
@export_group("Behaviour")
@export var stop_when_source_dies := false
@export var stationary := false
@export_group("Shared Playback")
@export_range(-10.0, 10.0, 0.1) var volume_db := 0.0
@export_range(0.1, 2.0, 0.1) var min_pitch := 1.0
@export_range(0.1, 2.0, 0.1) var max_pitch := 1.0
@export_range(0.0, 3.0, 0.1) var panning_strength := 1.0
@export_group("2D Playback")
@export_range(0.0, 3.0, 0.1) var attenuation := 1.0
@export_range(0, 9999999) var max_distance := 2000
@export_group("3D Playback")
@export_range(0, 1000) var unit_size := 10.0
@export_range(-10.0, 10.0, 0.1) var max_db := 3.0

View file

@ -0,0 +1,131 @@
@tool
extends Container
class_name EAStreamEditControl
@export var delete_event_button : Button
@export var add_stream_button : Button
@export var play_button : Button
@export var audio_label : Label
var weight_editor : EditorSpinSlider
var audio_selector : EditorResourcePicker
var _stream_id : int
var _event : EAEvent
var _bank_inspector : EAEventBankProperty
func create(bank_inspector: EAEventBankProperty, event: EAEvent, stream_id: int, primary: bool) -> void:
# When this is the primary resource, we don't want to delete it.
# add_stream_button.visible = true
_stream_id = stream_id
_event = event
_bank_inspector = bank_inspector
if primary:
delete_event_button.disabled = true
else:
delete_event_button.disabled = false
play_button.pressed.connect(_on_play_button_clicked)
audio_selector.resource_changed.connect(_on_stream_changed)
audio_selector.edited_resource = _event.audio_streams[_stream_id]
add_stream_button.pressed.connect(_on_add_resource_button_clicked)
delete_event_button.pressed.connect(_on_delete_resource_button_clicked)
weight_editor.value_changed.connect(_on_stream_weight_changed)
weight_editor.value = _event.probability_weights[_stream_id]
_make_audio_picker_pretty()
func _ready() -> void:
get_node("WeightSliderContainer").add_child(weight_editor)
get_node("ResourcePicker/ResourcePickerContainer").add_child(audio_selector)
# if _event:
# audio_selector.edited_resource = _event.audio_streams[_stream_id]
_make_audio_picker_pretty()
func _init() -> void:
weight_editor = EditorSpinSlider.new()
weight_editor.label = "weight"
weight_editor.step = 0.05
weight_editor.hide_slider = false
weight_editor.allow_lesser = false
weight_editor.allow_greater = false
weight_editor.min_value = 0
weight_editor.max_value = 1.0
weight_editor.size_flags_horizontal = Control.SIZE_EXPAND_FILL
audio_selector = EditorResourcePicker.new()
audio_selector.size_flags_horizontal = Control.SIZE_EXPAND_FILL
audio_selector.base_type = "AudioStream"
# audio_selector.edited_resource = _event.audio_streams[_stream_id]
audio_selector.resource_changed.connect(_on_resource_changed)
audio_selector.resource_selected.connect(_on_resource_clicked)
func _on_resource_clicked(res: Resource, _inspect: bool):
# When the resource picker is clicked, visit the resource.
# Do it the next frame, however, in case the controls are being updated.
if res != null:
await get_tree().process_frame
EditorInterface.edit_resource(res)
func _on_resource_changed(_res: Resource):
# When the resource is changed, update the prettification of the resource picker.
_make_audio_picker_pretty()
# The default resource picker has several drawbacks when used in a custom inspector.
# - It draws double-height, to show an audio preview; even when there is no preview.
# - When audio is assigned it stops showing the path name.
# This function 'fixes' that, somewhat hackily.
func _make_audio_picker_pretty():
# In certain circumstances, show a custom label with the resources file path.
# Otherwise, hide it and rely on the default control.
var res := audio_selector.edited_resource
if res == null:
# If no resource, show the default button text
audio_label.visible = false
else:
# Otherwise, try to show the file name
if FileAccess.file_exists(res.resource_path):
audio_label.text = res.resource_path.get_file()
audio_label.visible = true
else:
audio_label.text = res.get_class()
# TODO - can't tell when the button is rendering at the moment, so this shows on top, sometimes.
# Until we can, make the label invisible
audio_label.visible = false
# This is the only way we found to get the ResourcePicker smaller.
# It's not ideal, as it depends on the internals of the resource picker.
# Search for the texture rect that shows the preview and detach it.
var children := audio_selector.get_children()
while not children.is_empty():
var child := children.pop_back() as Node
children.append_array(child.get_children())
if child is TextureRect:
child.get_parent().remove_child(child)
func _on_play_button_clicked():
var stream := _event.audio_streams[_stream_id]
if stream:
EAEditorTools.play_sound(_event, stream)
func _on_stream_weight_changed(val):
_event.probability_weights[_stream_id] = val
_bank_inspector.signal_entry_changed(false)
func _on_add_resource_button_clicked():
_event.add_stream(_stream_id)
_bank_inspector.signal_entry_changed(true)
func _on_delete_resource_button_clicked():
_event.remove_stream(_stream_id)
_bank_inspector.signal_entry_changed(true)
func _on_stream_changed(res: Resource):
_event.audio_streams[_stream_id] = res as AudioStream
_bank_inspector.signal_entry_changed(false)

10
main.gd
View file

@ -7,6 +7,7 @@ extends Control
@onready var trueview: int = 0
# variables for nodes
@onready var sound: Node2D = $Sounds
@onready var roll_dice: TextureButton = $Roll/Roll_dice
@onready var psyline_other: Control = $Psy_line_other
@onready var psyline_self: Control = $Psy_line_self
@ -15,10 +16,11 @@ extends Control
@onready var keep_psy: CheckBox = $Roll/Keep_psy
@onready var keep_traits: CheckBox = $Roll/Keep_traits
@onready var language_choice: MenuButton = $Language
@onready var soundplayer: EAEventBankMounter = $SoundPlayer
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
lets_roll(seed)
lets_roll(seed, 0)
roll_dice.pressed.connect(_on_roll_dice_pressed)
language_choice.get_popup().connect("id_pressed", _on_menu_language_selected)
@ -35,13 +37,17 @@ func _process(delta: float) -> void:
func _on_roll_dice_pressed() -> void:
lets_roll(seed)
func lets_roll(chosen_seed: int) -> void:
func lets_roll(chosen_seed: int, noise: bool = 1) -> void:
# Random psy profiles
var possible_psy: int = $Psy_line_other.resources.size()
var random = RandomNumberGenerator.new()
random.seed = chosen_seed
# Play sound if wanted
if noise:
EventAudio.play_2d("DICEROLL", sound)
# Randomize psychology
if not keep_psy.button_pressed:
random.randomize()

View file

@ -1,10 +1,12 @@
[gd_scene load_steps=6 format=3 uid="uid://d14wihi8i46og"]
[gd_scene load_steps=8 format=3 uid="uid://d14wihi8i46og"]
[ext_resource type="PackedScene" uid="uid://bff864odl7vlj" path="res://psyline.tscn" id="1_03r8p"]
[ext_resource type="Script" path="res://main.gd" id="1_gqole"]
[ext_resource type="Theme" uid="uid://qsom67gcgupx" path="res://themes/base.tres" id="2_vc4v2"]
[ext_resource type="PackedScene" uid="uid://cl5wswke7jxpi" path="res://traits.tscn" id="3_t3gol"]
[ext_resource type="Texture2D" uid="uid://b7fg7g42642p" path="res://textures/dices.png" id="5_rsth0"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_bank_mounter.gd" id="6_im5e0"]
[ext_resource type="Resource" uid="uid://c51i5hutfhrsx" path="res://sounds/eventbanks/UI_sounds.tres" id="7_6knbw"]
[node name="Main" type="Control"]
layout_mode = 3
@ -239,3 +241,9 @@ item_count = 2
popup/item_0/text = "FRENCH"
popup/item_1/text = "ENGLISH"
popup/item_1/id = 1
[node name="SoundPlayer" type="Node" parent="."]
script = ExtResource("6_im5e0")
_audio_bank_resource = ExtResource("7_6knbw")
[node name="Sounds" type="Node2D" parent="."]

View file

@ -15,6 +15,14 @@ run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.3", "Forward Plus")
config/icon="res://textures/icon.png"
[autoload]
EventAudio="*res://addons/event_audio/scenes/event_audio_autoload.tscn"
[editor_plugins]
enabled=PackedStringArray("res://addons/event_audio/plugin.cfg")
[input]
ui_accept={

View file

@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bbsb62p7ci00d"
path="res://.godot/imported/dieShuffle1.ogg-dcb2d2e1f13163ad0879571ecc9e036c.oggvorbisstr"
[deps]
source_file="res://sounds/assets/dieShuffle1.ogg"
dest_files=["res://.godot/imported/dieShuffle1.ogg-dcb2d2e1f13163ad0879571ecc9e036c.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View file

@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bbsb62p7ci00d"
path="res://.godot/imported/dieShuffle1.ogg-839321e52515a7254293b5f786bc71b8.oggvorbisstr"
[deps]
source_file="res://sounds/dieShuffle1.ogg"
dest_files=["res://.godot/imported/dieShuffle1.ogg-839321e52515a7254293b5f786bc71b8.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View file

@ -0,0 +1,30 @@
[gd_resource type="Resource" script_class="EAEventBank" load_steps=7 format=3 uid="uid://c51i5hutfhrsx"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event.gd" id="1_kbnqw"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_bank.gd" id="2_jlkrc"]
[ext_resource type="AudioStream" uid="uid://bbsb62p7ci00d" path="res://sounds/assets/dieShuffle1.ogg" id="2_uyd46"]
[ext_resource type="Script" path="res://addons/event_audio/src/ea_event_playback_settings.gd" id="3_dcij5"]
[sub_resource type="Resource" id="Resource_odclp"]
script = ExtResource("3_dcij5")
stop_when_source_dies = false
stationary = false
volume_db = 0.0
min_pitch = 1.0
max_pitch = 1.0
panning_strength = 1.0
attenuation = 1.0
max_distance = 2000
unit_size = 10.0
max_db = 3.0
[sub_resource type="Resource" id="Resource_5ogac"]
script = ExtResource("1_kbnqw")
audio_streams = Array[AudioStream]([ExtResource("2_uyd46")])
probability_weights = Array[float]([1.0])
trigger_tags = "DICEROLL"
playback_settings = SubResource("Resource_odclp")
[resource]
script = ExtResource("2_jlkrc")
entries = Array[ExtResource("1_kbnqw")]([SubResource("Resource_5ogac")])