feat(audio): add built-in sound effects (bell, coin, chime, etc.), inspector sound presets, and preview player
checkpoint / checkpoint (push) Failing after 6m3s
checkpoint / checkpoint (push) Failing after 6m3s
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dvln67rbfmcnt"
|
||||||
|
path="res://.godot/imported/bell.wav-c9e8b15c72938ff0cc4247d900f73a2f.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/bell.wav"
|
||||||
|
dest_files=["res://.godot/imported/bell.wav-c9e8b15c72938ff0cc4247d900f73a2f.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=2
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://uvtdcjsrs4oc"
|
||||||
|
path="res://.godot/imported/chime.wav-cbf50c3ed007f7bda834f64e66ec1ffa.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/chime.wav"
|
||||||
|
dest_files=["res://.godot/imported/chime.wav-cbf50c3ed007f7bda834f64e66ec1ffa.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=2
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://cxiiaesukt1ci"
|
||||||
|
path="res://.godot/imported/click.wav-726094e36c7052fadb6392aaf7409595.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/click.wav"
|
||||||
|
dest_files=["res://.godot/imported/click.wav-726094e36c7052fadb6392aaf7409595.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=2
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://cssl2kgkc53is"
|
||||||
|
path="res://.godot/imported/coin.wav-e1bec9e9502a5df388e6fd33d4bb5689.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/coin.wav"
|
||||||
|
dest_files=["res://.godot/imported/coin.wav-e1bec9e9502a5df388e6fd33d4bb5689.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=2
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://c10qifwxnhin6"
|
||||||
|
path="res://.godot/imported/door_open.wav-d530c3547ac0143568ab23d68285c71c.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/door_open.wav"
|
||||||
|
dest_files=["res://.godot/imported/door_open.wav-d530c3547ac0143568ab23d68285c71c.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=2
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://bl6jimdmb1d3u"
|
||||||
|
path="res://.godot/imported/fanfare.wav-e0524ad6f859b26eee9ccfabfec4f302.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/fanfare.wav"
|
||||||
|
dest_files=["res://.godot/imported/fanfare.wav-e0524ad6f859b26eee9ccfabfec4f302.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=2
|
||||||
+2
-2
@@ -7,7 +7,7 @@ advanced_options=false
|
|||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="*.json, *.glb"
|
include_filter="*.json, *.glb, *.wav"
|
||||||
exclude_filter=""
|
exclude_filter=""
|
||||||
export_path="export/linux/modbison.x86_64"
|
export_path="export/linux/modbison.x86_64"
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
@@ -38,7 +38,7 @@ advanced_options=false
|
|||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter="*.json, *.glb"
|
include_filter="*.json, *.glb, *.wav"
|
||||||
exclude_filter=""
|
exclude_filter=""
|
||||||
export_path="export/windows/modbison.exe"
|
export_path="export/windows/modbison.exe"
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
|
|||||||
@@ -32,5 +32,8 @@ margin = 0.2
|
|||||||
[node name="Camera3D" type="Camera3D" parent="CameraPivot/SpringArm3D"]
|
[node name="Camera3D" type="Camera3D" parent="CameraPivot/SpringArm3D"]
|
||||||
current = true
|
current = true
|
||||||
|
|
||||||
|
[node name="AudioListener3D" type="AudioListener3D" parent="CameraPivot/SpringArm3D/Camera3D"]
|
||||||
|
current = true
|
||||||
|
|
||||||
[node name="Inventory" type="Node" parent="."]
|
[node name="Inventory" type="Node" parent="."]
|
||||||
script = ExtResource("2_inventory")
|
script = ExtResource("2_inventory")
|
||||||
|
|||||||
@@ -359,6 +359,47 @@ func _setup_rule_form() -> void:
|
|||||||
# 4. play_sound
|
# 4. play_sound
|
||||||
_action_sound_box = VBoxContainer.new()
|
_action_sound_box = VBoxContainer.new()
|
||||||
_action_sound_box.visible = false
|
_action_sound_box.visible = false
|
||||||
|
|
||||||
|
var snd_preset_row := HBoxContainer.new()
|
||||||
|
var snd_preset_lbl := Label.new()
|
||||||
|
snd_preset_lbl.text = "Preset:"
|
||||||
|
snd_preset_lbl.custom_minimum_size = Vector2(70, 0)
|
||||||
|
snd_preset_row.add_child(snd_preset_lbl)
|
||||||
|
|
||||||
|
var snd_option := OptionButton.new()
|
||||||
|
snd_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
snd_option.add_item("🔔 Bell Chime (bell)", 0)
|
||||||
|
snd_option.set_item_metadata(0, "bell")
|
||||||
|
snd_option.add_item("🪙 Coin Pickup (coin)", 1)
|
||||||
|
snd_option.set_item_metadata(1, "coin")
|
||||||
|
snd_option.add_item("🔘 Button Click (click)", 2)
|
||||||
|
snd_option.set_item_metadata(2, "click")
|
||||||
|
snd_option.add_item("✨ Magic Chime (chime)", 3)
|
||||||
|
snd_option.set_item_metadata(3, "chime")
|
||||||
|
snd_option.add_item("🎺 Victory Fanfare (fanfare)", 4)
|
||||||
|
snd_option.set_item_metadata(4, "fanfare")
|
||||||
|
snd_option.add_item("🚪 Door Open (door_open)", 5)
|
||||||
|
snd_option.set_item_metadata(5, "door_open")
|
||||||
|
snd_preset_row.add_child(snd_option)
|
||||||
|
|
||||||
|
var preview_btn := Button.new()
|
||||||
|
preview_btn.text = "▶ Preview"
|
||||||
|
preview_btn.tooltip_text = "Listen to this sound effect"
|
||||||
|
preview_btn.pressed.connect(func():
|
||||||
|
var snd_id := _sound_id_edit.text.strip_edges()
|
||||||
|
var p := "res://assets/audio/%s.wav" % snd_id
|
||||||
|
if ResourceLoader.exists(p):
|
||||||
|
var st = load(p)
|
||||||
|
if st is AudioStream:
|
||||||
|
var asp := AudioStreamPlayer.new()
|
||||||
|
asp.stream = st
|
||||||
|
add_child(asp)
|
||||||
|
asp.finished.connect(asp.queue_free)
|
||||||
|
asp.play()
|
||||||
|
)
|
||||||
|
snd_preset_row.add_child(preview_btn)
|
||||||
|
_action_sound_box.add_child(snd_preset_row)
|
||||||
|
|
||||||
var snd_row := HBoxContainer.new()
|
var snd_row := HBoxContainer.new()
|
||||||
var snd_lbl := Label.new()
|
var snd_lbl := Label.new()
|
||||||
snd_lbl.text = "Sound ID:"
|
snd_lbl.text = "Sound ID:"
|
||||||
@@ -369,6 +410,13 @@ func _setup_rule_form() -> void:
|
|||||||
_sound_id_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
_sound_id_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
snd_row.add_child(_sound_id_edit)
|
snd_row.add_child(_sound_id_edit)
|
||||||
_action_sound_box.add_child(snd_row)
|
_action_sound_box.add_child(snd_row)
|
||||||
|
|
||||||
|
snd_option.item_selected.connect(func(idx: int):
|
||||||
|
var meta = snd_option.get_item_metadata(idx)
|
||||||
|
if meta != null:
|
||||||
|
_sound_id_edit.text = str(meta)
|
||||||
|
)
|
||||||
|
|
||||||
form_box.add_child(_action_sound_box)
|
form_box.add_child(_action_sound_box)
|
||||||
|
|
||||||
# 5. spawn_from_catalog
|
# 5. spawn_from_catalog
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate crisp, pleasant retro/modern sound effects as 16-bit 44.1kHz mono WAVs."""
|
||||||
|
import math
|
||||||
|
import struct
|
||||||
|
import wave
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SAMPLE_RATE = 44100
|
||||||
|
OUT_DIR = Path(__file__).resolve().parent.parent / "assets" / "audio"
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def write_wav(filename: str, samples: list[float]):
|
||||||
|
path = OUT_DIR / filename
|
||||||
|
with wave.open(str(path), "w") as wav:
|
||||||
|
wav.setnchannels(1)
|
||||||
|
wav.setsampwidth(2)
|
||||||
|
wav.setframerate(SAMPLE_RATE)
|
||||||
|
raw = bytearray()
|
||||||
|
for s in samples:
|
||||||
|
val = max(-1.0, min(1.0, s))
|
||||||
|
ival = int(val * 32767)
|
||||||
|
raw.extend(struct.pack("<h", ival))
|
||||||
|
wav.writeframes(raw)
|
||||||
|
print(f"Generated {path} ({len(samples)} samples, {len(samples)/SAMPLE_RATE:.2f}s)")
|
||||||
|
|
||||||
|
|
||||||
|
def gen_bell():
|
||||||
|
"""Resonant brass bell chime with multiple harmonics and long decay."""
|
||||||
|
duration = 1.6
|
||||||
|
n = int(SAMPLE_RATE * duration)
|
||||||
|
samples = []
|
||||||
|
f0 = 880.0 # A5
|
||||||
|
for i in range(n):
|
||||||
|
t = i / SAMPLE_RATE
|
||||||
|
env = math.exp(-3.5 * t)
|
||||||
|
# Bell acoustics: partials at 1.0, 2.0, 3.0, 4.2 with differing decay rates
|
||||||
|
s = (
|
||||||
|
math.sin(2 * math.pi * f0 * t) * 0.6
|
||||||
|
+ math.sin(2 * math.pi * f0 * 2.0 * t) * 0.3 * math.exp(-2.0 * t)
|
||||||
|
+ math.sin(2 * math.pi * f0 * 3.0 * t) * 0.15 * math.exp(-4.0 * t)
|
||||||
|
+ math.sin(2 * math.pi * f0 * 4.2 * t) * 0.1 * math.exp(-6.0 * t)
|
||||||
|
) * env
|
||||||
|
samples.append(s * 0.9)
|
||||||
|
write_wav("bell.wav", samples)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_coin():
|
||||||
|
"""Bright two-tone pickup chime (B5 -> E6)."""
|
||||||
|
duration = 0.35
|
||||||
|
n = int(SAMPLE_RATE * duration)
|
||||||
|
samples = []
|
||||||
|
t_split = 0.08
|
||||||
|
for i in range(n):
|
||||||
|
t = i / SAMPLE_RATE
|
||||||
|
if t < t_split:
|
||||||
|
freq = 987.77 # B5
|
||||||
|
env = min(1.0, t / 0.005)
|
||||||
|
else:
|
||||||
|
freq = 1318.51 # E6
|
||||||
|
t_sub = t - t_split
|
||||||
|
env = math.exp(-9.0 * t_sub)
|
||||||
|
# Add a light sparkle harmonic
|
||||||
|
s = (math.sin(2 * math.pi * freq * t) + 0.25 * math.sin(4 * math.pi * freq * t)) * env
|
||||||
|
samples.append(s * 0.8)
|
||||||
|
write_wav("coin.wav", samples)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_click():
|
||||||
|
"""Short crisp mechanical button click."""
|
||||||
|
duration = 0.05
|
||||||
|
n = int(SAMPLE_RATE * duration)
|
||||||
|
samples = []
|
||||||
|
for i in range(n):
|
||||||
|
t = i / SAMPLE_RATE
|
||||||
|
env = math.exp(-80.0 * t)
|
||||||
|
freq = 1200.0 - 800.0 * (t / duration)
|
||||||
|
s = math.sin(2 * math.pi * freq * t) * env
|
||||||
|
samples.append(s * 0.7)
|
||||||
|
write_wav("click.wav", samples)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_chime():
|
||||||
|
"""Pleasant 3-note ascending arpeggio (C6, E6, G6)."""
|
||||||
|
duration = 0.8
|
||||||
|
n = int(SAMPLE_RATE * duration)
|
||||||
|
notes = [1046.5, 1318.5, 1567.98] # C6, E6, G6
|
||||||
|
step = 0.12
|
||||||
|
samples = []
|
||||||
|
for i in range(n):
|
||||||
|
t = i / SAMPLE_RATE
|
||||||
|
s_total = 0.0
|
||||||
|
for idx, freq in enumerate(notes):
|
||||||
|
t_start = idx * step
|
||||||
|
if t >= t_start:
|
||||||
|
t_note = t - t_start
|
||||||
|
env = math.exp(-6.0 * t_note)
|
||||||
|
s_total += math.sin(2 * math.pi * freq * t_note) * env * 0.35
|
||||||
|
samples.append(s_total)
|
||||||
|
write_wav("chime.wav", samples)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_fanfare():
|
||||||
|
"""Triumphant short fanfare jingle (C5, G5, C6)."""
|
||||||
|
duration = 1.0
|
||||||
|
n = int(SAMPLE_RATE * duration)
|
||||||
|
notes = [(0.0, 0.15, 523.25), (0.15, 0.30, 783.99), (0.30, 0.95, 1046.5)]
|
||||||
|
samples = []
|
||||||
|
for i in range(n):
|
||||||
|
t = i / SAMPLE_RATE
|
||||||
|
s_total = 0.0
|
||||||
|
for t_start, t_end, freq in notes:
|
||||||
|
if t >= t_start:
|
||||||
|
t_rel = t - t_start
|
||||||
|
env = math.exp(-4.0 * t_rel) if t >= t_end - 0.05 else min(1.0, t_rel / 0.01)
|
||||||
|
s_total += (math.sin(2 * math.pi * freq * t_rel) + 0.2 * math.sin(4 * math.pi * freq * t_rel)) * env * 0.4
|
||||||
|
samples.append(s_total)
|
||||||
|
write_wav("fanfare.wav", samples)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_door_open():
|
||||||
|
"""Low wooden mechanical slide/creak."""
|
||||||
|
duration = 0.6
|
||||||
|
n = int(SAMPLE_RATE * duration)
|
||||||
|
samples = []
|
||||||
|
for i in range(n):
|
||||||
|
t = i / SAMPLE_RATE
|
||||||
|
env = math.sin(math.pi * (t / duration)) ** 0.5
|
||||||
|
freq = 160.0 + 80.0 * math.sin(30.0 * t)
|
||||||
|
s = math.sin(2 * math.pi * freq * t) * env * 0.6
|
||||||
|
samples.append(s)
|
||||||
|
write_wav("door_open.wav", samples)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
gen_bell()
|
||||||
|
gen_coin()
|
||||||
|
gen_click()
|
||||||
|
gen_chime()
|
||||||
|
gen_fanfare()
|
||||||
|
gen_door_open()
|
||||||
@@ -396,6 +396,7 @@ func _act_give_item(action: Dictionary, player: Node) -> void:
|
|||||||
_show_give_feedback(item_id, count)
|
_show_give_feedback(item_id, count)
|
||||||
if item_id.to_lower() in ["buffalo_chips", "chips", "credits"]:
|
if item_id.to_lower() in ["buffalo_chips", "chips", "credits"]:
|
||||||
_deposit_to_wallet(count)
|
_deposit_to_wallet(count)
|
||||||
|
_act_play_sound({"sound_id": "coin"}, player)
|
||||||
|
|
||||||
|
|
||||||
func _show_give_feedback(item_id: String, count: int) -> void:
|
func _show_give_feedback(item_id: String, count: int) -> void:
|
||||||
@@ -441,19 +442,26 @@ func _deposit_to_wallet(count: int) -> void:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Trusted, code-defined mapping from sound_id -> resource path. Rule data
|
||||||
|
## supplies only the key; this table (not the data) decides what actually
|
||||||
|
## plays.
|
||||||
|
const _TRUSTED_SOUND_TABLE: Dictionary = {
|
||||||
|
"bell": "res://assets/audio/bell.wav",
|
||||||
|
"coin": "res://assets/audio/coin.wav",
|
||||||
|
"click": "res://assets/audio/click.wav",
|
||||||
|
"chime": "res://assets/audio/chime.wav",
|
||||||
|
"fanfare": "res://assets/audio/fanfare.wav",
|
||||||
|
"door_open": "res://assets/audio/door_open.wav",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func _act_play_sound(action: Dictionary, _player: Node) -> void:
|
func _act_play_sound(action: Dictionary, _player: Node) -> void:
|
||||||
# No real audio asset catalog exists in this repo yet. The verb still
|
var sound_id: String = str(action.get("sound_id", "")).to_lower().strip_edges().trim_suffix(".wav")
|
||||||
# runs through a real, trusted playback path -- attempting to resolve
|
|
||||||
# and play a stream when one is registered -- so it's not a no-op
|
|
||||||
# pretending to be a verb; it's just that today's trusted sound table
|
|
||||||
# is empty. `sound_id` is only ever used as a key into that trusted,
|
|
||||||
# code-defined table below, never as a path handed to load() directly.
|
|
||||||
var sound_id: String = action.get("sound_id", "")
|
|
||||||
var stream_path: String = _TRUSTED_SOUND_TABLE.get(sound_id, "")
|
var stream_path: String = _TRUSTED_SOUND_TABLE.get(sound_id, "")
|
||||||
if not stream_path.is_empty() and ResourceLoader.exists(stream_path):
|
if not stream_path.is_empty() and ResourceLoader.exists(stream_path):
|
||||||
var stream: Resource = load(stream_path)
|
var stream: Resource = load(stream_path)
|
||||||
if stream is AudioStream:
|
if stream is AudioStream:
|
||||||
var player_node := AudioStreamPlayer3D.new()
|
var player_node := AudioStreamPlayer.new()
|
||||||
player_node.stream = stream
|
player_node.stream = stream
|
||||||
add_child(player_node)
|
add_child(player_node)
|
||||||
player_node.finished.connect(player_node.queue_free)
|
player_node.finished.connect(player_node.queue_free)
|
||||||
@@ -461,12 +469,6 @@ func _act_play_sound(action: Dictionary, _player: Node) -> void:
|
|||||||
verb_executed.emit("play_sound", action)
|
verb_executed.emit("play_sound", action)
|
||||||
|
|
||||||
|
|
||||||
## Trusted, code-defined mapping from sound_id -> resource path. Rule data
|
|
||||||
## supplies only the key; this table (not the data) decides what actually
|
|
||||||
## plays. Empty for now -- no sound assets exist in this repo yet.
|
|
||||||
const _TRUSTED_SOUND_TABLE: Dictionary = {}
|
|
||||||
|
|
||||||
|
|
||||||
func _act_move(action: Dictionary, player: Node) -> void:
|
func _act_move(action: Dictionary, player: Node) -> void:
|
||||||
var target_node := _resolve_target(action.get("target", "self"), player)
|
var target_node := _resolve_target(action.get("target", "self"), player)
|
||||||
if target_node == null:
|
if target_node == null:
|
||||||
|
|||||||
Reference in New Issue
Block a user