Real thunder (Gitea #4): six CC0 recordings, close cracks and distant rolls, in place of the synthesised rumble; a storm's flash is followed by a roll seconds later or a crack sooner, quieter indoors, and a strike on a tree cracks from the tree
This commit is contained in:
@@ -39,6 +39,21 @@ Breviceps (https://freesound.org/people/Breviceps/sounds/465697/, License:
|
|||||||
Creative Commons 0; the preview is in `tools/audio/sources/`), cut at 0.12 s
|
Creative Commons 0; the preview is in `tools/audio/sources/`), cut at 0.12 s
|
||||||
and 2.65 s (leaving out the short sound between them), then levelled. Thank you.
|
and 2.65 s (leaving out the short sound between them), then levelled. Thank you.
|
||||||
|
|
||||||
|
## Thunder (CC0)
|
||||||
|
`sfx/thunder/` is cut from six Freesound recordings, all License: Creative
|
||||||
|
Commons 0 (the previews are in `tools/audio/sources/thunder_*`), each round
|
||||||
|
its loudest strike, faded out and levelled by
|
||||||
|
`tools/audio/make_audio.py thunder`. Close cracks: "Thunder 08" by Emulius
|
||||||
|
(https://freesound.org/people/Emulius/sounds/744723/), "thunder11" by
|
||||||
|
saha213131 (https://freesound.org/people/saha213131/sounds/696550/) and
|
||||||
|
"Thunder strike" by AyaDrevis
|
||||||
|
(https://freesound.org/people/AyaDrevis/sounds/652690/). Distant rolls:
|
||||||
|
"Short_Thunder_Mid" by SholeColtis
|
||||||
|
(https://freesound.org/people/SholeColtis/sounds/683421/), "thunder7" by
|
||||||
|
nednednerb (https://freesound.org/people/nednednerb/sounds/38250/) and
|
||||||
|
"Thunder Clap OWB KY 441x16.wav" by Dave Welsh
|
||||||
|
(https://freesound.org/people/Dave%20Welsh/sounds/194364/). Thank you.
|
||||||
|
|
||||||
## Old Tam's voice
|
## Old Tam's voice
|
||||||
`voice/` is spoken by **Kokoro** (Kokoro-82M by hexgrad, Apache-2.0,
|
`voice/` is spoken by **Kokoro** (Kokoro-82M by hexgrad, Apache-2.0,
|
||||||
https://huggingface.co/hexgrad/Kokoro-82M) with its British "George" voice,
|
https://huggingface.co/hexgrad/Kokoro-82M) with its British "George" voice,
|
||||||
|
|||||||
BIN
assets/audio/sfx/thunder/thunder_far_0.ogg
Normal file
BIN
assets/audio/sfx/thunder/thunder_far_0.ogg
Normal file
Binary file not shown.
BIN
assets/audio/sfx/thunder/thunder_far_1.ogg
Normal file
BIN
assets/audio/sfx/thunder/thunder_far_1.ogg
Normal file
Binary file not shown.
BIN
assets/audio/sfx/thunder/thunder_far_2.ogg
Normal file
BIN
assets/audio/sfx/thunder/thunder_far_2.ogg
Normal file
Binary file not shown.
BIN
assets/audio/sfx/thunder/thunder_near_0.ogg
Normal file
BIN
assets/audio/sfx/thunder/thunder_near_0.ogg
Normal file
Binary file not shown.
BIN
assets/audio/sfx/thunder/thunder_near_1.ogg
Normal file
BIN
assets/audio/sfx/thunder/thunder_near_1.ogg
Normal file
Binary file not shown.
BIN
assets/audio/sfx/thunder/thunder_near_2.ogg
Normal file
BIN
assets/audio/sfx/thunder/thunder_near_2.ogg
Normal file
Binary file not shown.
@@ -19,6 +19,7 @@ extends Node
|
|||||||
const K := "res://assets/audio/sfx/kenney/"
|
const K := "res://assets/audio/sfx/kenney/"
|
||||||
const S := "res://assets/audio/sfx/synth/"
|
const S := "res://assets/audio/sfx/synth/"
|
||||||
const C := "res://assets/audio/sfx/creatures/"
|
const C := "res://assets/audio/sfx/creatures/"
|
||||||
|
const T := "res://assets/audio/sfx/thunder/"
|
||||||
const EARSHOT := 45.0 ## a server sends a sound to players this close
|
const EARSHOT := 45.0 ## a server sends a sound to players this close
|
||||||
|
|
||||||
## id -> files (or a [prefix, count] pair), volume (dB), pitch range, reach (m),
|
## id -> files (or a [prefix, count] pair), volume (dB), pitch range, reach (m),
|
||||||
@@ -28,6 +29,10 @@ const SOUNDS := {
|
|||||||
"tree_fall": {"set": [K + "wood_heavy_%d.ogg", 5], "db": 0.0, "pitch": [0.6, 0.7], "reach": 60.0, "with": "creak"},
|
"tree_fall": {"set": [K + "wood_heavy_%d.ogg", 5], "db": 0.0, "pitch": [0.6, 0.7], "reach": 60.0, "with": "creak"},
|
||||||
"creak": {"set": [K + "creak_%d.ogg", 3], "db": -2.0, "pitch": [0.8, 0.95], "reach": 50.0},
|
"creak": {"set": [K + "creak_%d.ogg", 3], "db": -2.0, "pitch": [0.8, 0.95], "reach": 50.0},
|
||||||
"mine": {"set": [K + "mine_%d.ogg", 5], "db": -1.0, "pitch": [0.9, 1.1]},
|
"mine": {"set": [K + "mine_%d.ogg", 5], "db": -1.0, "pitch": [0.9, 1.1]},
|
||||||
|
# Thunder (0.6.2, CC0 recordings: assets/audio/CREDITS.md). The weather
|
||||||
|
# plays the storm's own (weather_fx.gd); a strike on a tree cracks from it.
|
||||||
|
"thunder_near": {"set": [T + "thunder_near_%d.ogg", 3], "db": 0.0, "pitch": [0.92, 1.05], "reach": 400.0},
|
||||||
|
"thunder_far": {"set": [T + "thunder_far_%d.ogg", 3], "db": -5.0, "pitch": [0.9, 1.05], "reach": 400.0},
|
||||||
"rock_break": {"set": [K + "mine_%d.ogg", 5], "db": 1.0, "pitch": [0.6, 0.7], "reach": 50.0},
|
"rock_break": {"set": [K + "mine_%d.ogg", 5], "db": 1.0, "pitch": [0.6, 0.7], "reach": 50.0},
|
||||||
"rustle": {"set": [K + "cloth_%d.ogg", 4], "db": -4.0, "pitch": [0.7, 0.9]},
|
"rustle": {"set": [K + "cloth_%d.ogg", 4], "db": -4.0, "pitch": [0.7, 0.9]},
|
||||||
"pickup": {"set": [K + "leather_%d.ogg", 3], "db": -6.0, "pitch": [0.95, 1.15], "reach": 15.0},
|
"pickup": {"set": [K + "leather_%d.ogg", 3], "db": -6.0, "pitch": [0.95, 1.15], "reach": 15.0},
|
||||||
@@ -196,6 +201,17 @@ func me(id: String) -> void:
|
|||||||
_log(id, null)
|
_log(id, null)
|
||||||
_play2(id)
|
_play2(id)
|
||||||
|
|
||||||
|
## A random one of sound `id`'s files, and its volume: for a player of your
|
||||||
|
## own (the weather's thunder). Not logged: call log_heard() when it plays.
|
||||||
|
func stream_for(id: String) -> AudioStream:
|
||||||
|
return _pick(id) if SOUNDS.has(id) else null
|
||||||
|
|
||||||
|
func db_for(id: String) -> float:
|
||||||
|
return float(SOUNDS.get(id, {}).get("db", 0.0))
|
||||||
|
|
||||||
|
func log_heard(id: String) -> void:
|
||||||
|
_log(id, null)
|
||||||
|
|
||||||
## Menus and the HUD.
|
## Menus and the HUD.
|
||||||
func ui(id: String) -> void:
|
func ui(id: String) -> void:
|
||||||
_log(id, null)
|
_log(id, null)
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ func pick_tree() -> Node3D:
|
|||||||
func strike(n: Node3D) -> void:
|
func strike(n: Node3D) -> void:
|
||||||
strikes += 1
|
strikes += 1
|
||||||
show_strike(n)
|
show_strike(n)
|
||||||
|
Sfx.at("thunder_near", n.global_position + Vector3(0, 6, 0)) # (a crack from the tree itself)
|
||||||
if world.server_mode:
|
if world.server_mode:
|
||||||
WorldNet.struck(n)
|
WorldNet.struck(n)
|
||||||
for p in world.players():
|
for p in world.players():
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ extends Node3D
|
|||||||
## - Ground wetness, settled snow and wind strength are global shader uniforms
|
## - Ground wetness, settled snow and wind strength are global shader uniforms
|
||||||
## (weather_wet, weather_snow, weather_wind) read by the terrain, grass,
|
## (weather_wet, weather_snow, weather_wind) read by the terrain, grass,
|
||||||
## foliage and rock shaders.
|
## foliage and rock shaders.
|
||||||
## - Sound is synthesised: filtered noise for rain and wind, a decaying
|
## - Rain and wind are synthesised (filtered noise). Thunder is recorded
|
||||||
## low rumble for thunder. No audio assets, nothing to license.
|
## (CC0, 0.6.2: assets/audio/sfx/thunder, Sfx "thunder_far"/"thunder_near"):
|
||||||
|
## after each flash in a storm, usually a distant roll a few seconds later,
|
||||||
|
## sometimes a nearer crack sooner. Quieter under a roof. (A strike on a
|
||||||
|
## tree near you cracks from the tree itself: hazards.gd.)
|
||||||
##
|
##
|
||||||
## Client-side only: in M2 the server runs weather.gd and never this.
|
## Client-side only: in M2 the server runs weather.gd and never this.
|
||||||
|
|
||||||
@@ -31,10 +34,11 @@ var _rng := RandomNumberGenerator.new()
|
|||||||
var _rain_lp := 0.0
|
var _rain_lp := 0.0
|
||||||
var _rain_hp := 0.0
|
var _rain_hp := 0.0
|
||||||
var _brown := 0.0
|
var _brown := 0.0
|
||||||
var _brown2 := 0.0
|
|
||||||
var _gust_t := 0.0
|
var _gust_t := 0.0
|
||||||
var _thunder_env := 0.0
|
|
||||||
var _thunder_delay := -1.0
|
var _thunder_delay := -1.0
|
||||||
|
var _thunder_id := ""
|
||||||
|
var _thunder: AudioStreamPlayer
|
||||||
|
const NEAR_CHANCE := 0.3 ## of a storm's flashes, the share that are close
|
||||||
var _seen_strikes := 0
|
var _seen_strikes := 0
|
||||||
var _rain_amp := 0.0
|
var _rain_amp := 0.0
|
||||||
var _wind_amp := 0.0
|
var _wind_amp := 0.0
|
||||||
@@ -83,6 +87,10 @@ func setup(w: Node, p: Node3D) -> void:
|
|||||||
add_child(_audio)
|
add_child(_audio)
|
||||||
_audio.play()
|
_audio.play()
|
||||||
_playback = _audio.get_stream_playback()
|
_playback = _audio.get_stream_playback()
|
||||||
|
_thunder = AudioStreamPlayer.new()
|
||||||
|
_thunder.name = "Thunder"
|
||||||
|
_thunder.bus = "SFX" if AudioServer.get_bus_index("SFX") >= 0 else "Master"
|
||||||
|
add_child(_thunder)
|
||||||
|
|
||||||
func _make_particles(amount: int, life: float, pm: ParticleProcessMaterial, mesh: Mesh) -> GPUParticles3D:
|
func _make_particles(amount: int, life: float, pm: ParticleProcessMaterial, mesh: Mesh) -> GPUParticles3D:
|
||||||
var p := GPUParticles3D.new()
|
var p := GPUParticles3D.new()
|
||||||
@@ -173,13 +181,32 @@ func _process(delta: float) -> void:
|
|||||||
_wind_amp = weather.wind * weather.wind * 0.22 * (0.35 if sheltered else 1.0)
|
_wind_amp = weather.wind * weather.wind * 0.22 * (0.35 if sheltered else 1.0)
|
||||||
if weather.lightning_count != _seen_strikes:
|
if weather.lightning_count != _seen_strikes:
|
||||||
_seen_strikes = weather.lightning_count
|
_seen_strikes = weather.lightning_count
|
||||||
_thunder_delay = _rng.randf_range(0.5, 2.5)
|
lightning()
|
||||||
if _thunder_delay >= 0.0:
|
if _thunder_delay >= 0.0:
|
||||||
_thunder_delay -= delta
|
_thunder_delay -= delta
|
||||||
if _thunder_delay < 0.0:
|
if _thunder_delay < 0.0:
|
||||||
_thunder_env = 1.0
|
_thunder_now()
|
||||||
_fill_audio()
|
_fill_audio()
|
||||||
|
|
||||||
|
## A flash in the sky: its thunder follows, after the time the sound takes
|
||||||
|
## to come (a near one sooner, a distant one later).
|
||||||
|
func lightning() -> void:
|
||||||
|
var near := _rng.randf() < NEAR_CHANCE
|
||||||
|
_thunder_id = "thunder_near" if near else "thunder_far"
|
||||||
|
_thunder_delay = _rng.randf_range(0.3, 0.9) if near else _rng.randf_range(1.5, 4.0)
|
||||||
|
|
||||||
|
func _thunder_now() -> void:
|
||||||
|
Sfx.log_heard(_thunder_id)
|
||||||
|
if _thunder == null:
|
||||||
|
return
|
||||||
|
var st: AudioStream = Sfx.stream_for(_thunder_id)
|
||||||
|
if st == null:
|
||||||
|
return
|
||||||
|
_thunder.stream = st
|
||||||
|
_thunder.volume_db = Sfx.db_for(_thunder_id) - 8.0 * _muffle
|
||||||
|
_thunder.pitch_scale = _rng.randf_range(0.9, 1.05)
|
||||||
|
_thunder.play()
|
||||||
|
|
||||||
func _set_amount(p: GPUParticles3D, a: float) -> void:
|
func _set_amount(p: GPUParticles3D, a: float) -> void:
|
||||||
p.amount_ratio = a
|
p.amount_ratio = a
|
||||||
p.emitting = a > 0.0
|
p.emitting = a > 0.0
|
||||||
@@ -194,7 +221,6 @@ func _fill_audio() -> void:
|
|||||||
var rain_cut := lerpf(0.55, 0.12, _muffle)
|
var rain_cut := lerpf(0.55, 0.12, _muffle)
|
||||||
var buf := PackedVector2Array()
|
var buf := PackedVector2Array()
|
||||||
buf.resize(n)
|
buf.resize(n)
|
||||||
var thunder_decay := exp(-1.0 / (MIX_RATE * 0.9))
|
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
var white := _rng.randf() * 2.0 - 1.0
|
var white := _rng.randf() * 2.0 - 1.0
|
||||||
# Rain: band-limited hiss (low-pass, minus a slower low-pass).
|
# Rain: band-limited hiss (low-pass, minus a slower low-pass).
|
||||||
@@ -206,12 +232,6 @@ func _fill_audio() -> void:
|
|||||||
_gust_t += 1.0 / MIX_RATE
|
_gust_t += 1.0 / MIX_RATE
|
||||||
var gust := 0.6 + 0.4 * sin(_gust_t * 0.7) * sin(_gust_t * 0.23 + 1.0)
|
var gust := 0.6 + 0.4 * sin(_gust_t * 0.7) * sin(_gust_t * 0.23 + 1.0)
|
||||||
var wind_s := _brown * _wind_amp * gust * 2.0
|
var wind_s := _brown * _wind_amp * gust * 2.0
|
||||||
# Thunder: a heavier brown rumble with a long decay.
|
var s := clampf(rain + wind_s, -1.0, 1.0)
|
||||||
var thunder := 0.0
|
|
||||||
if _thunder_env > 0.001:
|
|
||||||
_brown2 = clampf(_brown2 * 0.9995 + white * 0.02, -1.0, 1.0)
|
|
||||||
thunder = _brown2 * _thunder_env * 2.2
|
|
||||||
_thunder_env *= thunder_decay
|
|
||||||
var s := clampf(rain + wind_s + thunder, -1.0, 1.0)
|
|
||||||
buf[i] = Vector2(s, s)
|
buf[i] = Vector2(s, s)
|
||||||
_playback.push_buffer(buf)
|
_playback.push_buffer(buf)
|
||||||
|
|||||||
@@ -269,6 +269,31 @@ func _lightning() -> void:
|
|||||||
var tree: Node3D = hz._burning.keys()[0]
|
var tree: Node3D = hz._burning.keys()[0]
|
||||||
check(tree.global_position.distance_to(at) < hz.STRIKE_RANGE, "near you (%.0f m away)" % tree.global_position.distance_to(at))
|
check(tree.global_position.distance_to(at) < hz.STRIKE_RANGE, "near you (%.0f m away)" % tree.global_position.distance_to(at))
|
||||||
check(_toast_has("Lightning struck"), "you see it (%s)" % [_toasts])
|
check(_toast_has("Lightning struck"), "you see it (%s)" % [_toasts])
|
||||||
|
# Gitea #4: thunder is recorded now; the strike cracks from the tree.
|
||||||
|
var SX: Node = root.get_node("Sfx")
|
||||||
|
var cracks: Array = SX.heard.filter(func(h): return h[0] == "thunder_near" and h[1] is Vector3 and (h[1] as Vector3).distance_to(tree.global_position) < 8.0)
|
||||||
|
check(cracks.size() == 1, "and hear it: a thunder crack from the tree")
|
||||||
|
check(SX.files("thunder_near").size() == 3 and SX.files("thunder_far").size() == 3 and
|
||||||
|
SX.files("thunder_near").all(func(f): return ResourceLoader.exists(f)) and SX.files("thunder_far").all(func(f): return ResourceLoader.exists(f)),
|
||||||
|
"(three recorded cracks and three distant rolls)")
|
||||||
|
var fx: Node3D = load("res://scripts/world/weather_fx.gd").new()
|
||||||
|
world.add_child(fx)
|
||||||
|
fx.setup(wx, player)
|
||||||
|
var kinds := {}
|
||||||
|
var delays := {"thunder_near": [], "thunder_far": []}
|
||||||
|
for _i in range(60):
|
||||||
|
fx.lightning()
|
||||||
|
kinds[fx._thunder_id] = int(kinds.get(fx._thunder_id, 0)) + 1
|
||||||
|
delays[fx._thunder_id].append(fx._thunder_delay)
|
||||||
|
check(kinds.has("thunder_near") and kinds.has("thunder_far") and int(kinds.thunder_far) > int(kinds.thunder_near),
|
||||||
|
"a storm's flashes: mostly distant rolls, sometimes a crack close by (%s)" % [kinds])
|
||||||
|
check(delays.thunder_near.all(func(d): return d < 1.0) and delays.thunder_far.all(func(d): return d >= 1.5),
|
||||||
|
"a close one follows its flash quickly, a distant one seconds later")
|
||||||
|
var h0: int = SX.heard.size()
|
||||||
|
fx._thunder_delay = 0.01
|
||||||
|
fx._process(0.05)
|
||||||
|
check(SX.heard.size() > h0 and SX.heard.slice(h0).any(func(h): return String(h[0]).begins_with("thunder_")), "and then you hear it")
|
||||||
|
fx.queue_free()
|
||||||
check(world.heat_at(tree.global_position + Vector3(2.5, 0, 0)) > 0.3, "it burns: you can warm yourself by it")
|
check(world.heat_at(tree.global_position + Vector3(2.5, 0, 0)) > 0.3, "it burns: you can warm yourself by it")
|
||||||
check(not tree.depleted, "still standing while it burns")
|
check(not tree.depleted, "still standing while it burns")
|
||||||
for _i in range(41):
|
for _i in range(41):
|
||||||
|
|||||||
@@ -699,6 +699,44 @@ def _decode(src, sr, af=None):
|
|||||||
return [x * 0.06 / rms for x in left], [x * 0.06 / rms for x in right]
|
return [x * 0.06 / rms for x in left], [x * 0.06 / rms for x in right]
|
||||||
|
|
||||||
|
|
||||||
|
def make_thunder():
|
||||||
|
"""Thunder (0.6.2, Gitea #4): CC0 recordings (tools/audio/sources/
|
||||||
|
thunder_near_* and thunder_far_*, credited in assets/audio/CREDITS.md),
|
||||||
|
each cut round its loudest strike, faded out and evened out in level, into
|
||||||
|
assets/audio/sfx/thunder/thunder_near_N.ogg (a crack close by) and
|
||||||
|
thunder_far_N.ogg (a roll in the distance). Replaces the synthesised rumble."""
|
||||||
|
d = os.path.join(OUT, "sfx", "thunder")
|
||||||
|
os.makedirs(d, exist_ok=True)
|
||||||
|
print("thunder ->", os.path.relpath(d, ROOT))
|
||||||
|
sr = 44100
|
||||||
|
for kind, lead, most in (("near", 0.15, 7.0), ("far", 0.6, 10.0)):
|
||||||
|
for n, src in enumerate(sources("thunder_" + kind)):
|
||||||
|
raw = subprocess.run(["ffmpeg", "-v", "error", "-i", src, "-ac", "1", "-ar", str(sr), "-f", "s16le", "-"],
|
||||||
|
capture_output=True, check=True).stdout
|
||||||
|
x = [struct.unpack_from("<h", raw, i * 2)[0] / 32768.0 for i in range(len(raw) // 2)]
|
||||||
|
win = sr // 20
|
||||||
|
env = [math.sqrt(sum(v * v for v in x[i:i + win]) / win) + 1e-9 for i in range(0, len(x) - win, win)]
|
||||||
|
ip = max(range(len(env)), key=lambda i: env[i])
|
||||||
|
top = env[ip]
|
||||||
|
# Back from the loudest moment to where this strike starts (20 dB down).
|
||||||
|
j = ip
|
||||||
|
while j > 0 and env[j] > top * 0.1:
|
||||||
|
j -= 1
|
||||||
|
start = max(0, int((j * 0.05 - lead) * sr))
|
||||||
|
# On until it's died away (40 dB down), or `most` seconds.
|
||||||
|
k = ip
|
||||||
|
while k < len(env) - 1 and env[k] > top * 0.01 and (k - j) * 0.05 < most:
|
||||||
|
k += 1
|
||||||
|
end = min(len(x), int(k * 0.05 * sr) + win)
|
||||||
|
seg = x[start:end]
|
||||||
|
fade_in, fade_out = int(0.02 * sr), min(int(1.5 * sr), len(seg) // 3)
|
||||||
|
for i in range(fade_in):
|
||||||
|
seg[i] *= i / fade_in
|
||||||
|
for i in range(fade_out):
|
||||||
|
seg[-1 - i] *= i / fade_out
|
||||||
|
_encode(os.path.join(d, "thunder_%s_%d.ogg" % (kind, n)), [seg], sr, 5)
|
||||||
|
|
||||||
|
|
||||||
def make_recorded(name, srcs=None):
|
def make_recorded(name, srcs=None):
|
||||||
"""A field-recorded ambience loop (assets/audio/ambience/<name>.ogg) from
|
"""A field-recorded ambience loop (assets/audio/ambience/<name>.ogg) from
|
||||||
CC0 recordings (tools/audio/sources/<name>_*, or the files given): each
|
CC0 recordings (tools/audio/sources/<name>_*, or the files given): each
|
||||||
@@ -808,6 +846,9 @@ if __name__ == "__main__":
|
|||||||
if what in RECORDED: # one recorded loop: birds | crickets
|
if what in RECORDED: # one recorded loop: birds | crickets
|
||||||
make_recorded(what)
|
make_recorded(what)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
if what == "thunder": # the recorded thunder (not part of "all": it's from sources/)
|
||||||
|
make_thunder()
|
||||||
|
sys.exit(0)
|
||||||
if what in ("all", "sfx"):
|
if what in ("all", "sfx"):
|
||||||
make_sfx()
|
make_sfx()
|
||||||
if what in ("all", "sfx", "voices"):
|
if what in ("all", "sfx", "voices"):
|
||||||
|
|||||||
BIN
tools/audio/sources/thunder_far_davewelsh_194364.mp3
Normal file
BIN
tools/audio/sources/thunder_far_davewelsh_194364.mp3
Normal file
Binary file not shown.
BIN
tools/audio/sources/thunder_far_nednednerb_38250.mp3
Normal file
BIN
tools/audio/sources/thunder_far_nednednerb_38250.mp3
Normal file
Binary file not shown.
BIN
tools/audio/sources/thunder_far_sholecoltis_683421.mp3
Normal file
BIN
tools/audio/sources/thunder_far_sholecoltis_683421.mp3
Normal file
Binary file not shown.
BIN
tools/audio/sources/thunder_near_ayadrevis_652690.mp3
Normal file
BIN
tools/audio/sources/thunder_near_ayadrevis_652690.mp3
Normal file
Binary file not shown.
BIN
tools/audio/sources/thunder_near_emulius_744723.mp3
Normal file
BIN
tools/audio/sources/thunder_near_emulius_744723.mp3
Normal file
Binary file not shown.
BIN
tools/audio/sources/thunder_near_saha213131_696550.mp3
Normal file
BIN
tools/audio/sources/thunder_near_saha213131_696550.mp3
Normal file
Binary file not shown.
Reference in New Issue
Block a user