Files
Super-Mario-Bros.-Remastere…/Scripts/Classes/Singletons/AudioManager.gd

292 lines
11 KiB
GDScript

extends Node
const DEFAULT_SFX_LIBRARY := {
"small_jump": ("res://Assets/Audio/SFX/SmallJump.wav"),
"big_jump": ("res://Assets/Audio/SFX/BigJump.wav"),
"coin": ("res://Assets/Audio/SFX/Coin.wav"),
"bump": ("res://Assets/Audio/SFX/Bump.wav"),
"pipe": ("res://Assets/Audio/SFX/Pipe.wav"),
"damage": ("res://Assets/Audio/SFX/Damage.wav"),
"power_up": ("res://Assets/Audio/SFX/Powerup.wav"),
"item_appear": ("res://Assets/Audio/SFX/ItemAppear.wav"),
"block_break": ("res://Assets/Audio/SFX/BreakBlock.wav"),
"enemy_stomp": ("res://Assets/Audio/SFX/Stomp.wav"),
"kick": ("res://Assets/Audio/SFX/Kick.wav"),
"fireball": ("res://Assets/Audio/SFX/Fireball.wav"),
"1_up": ("res://Assets/Audio/SFX/1up.wav"),
"cannon": ("res://Assets/Audio/SFX/Cannon.wav"),
"checkpoint": ("res://Assets/Audio/SFX/Checkpoint.wav"),
"magic": ("res://Assets/Audio/SFX/Magic.wav"),
"beep": ("res://Assets/Audio/SFX/Score.wav"),
"switch": ("res://Assets/Audio/SFX/Switch.wav"),
"boo_laugh": ("res://Assets/Audio/SFX/BooLaugh.wav"),
"icicle_fall": ("res://Assets/Audio/SFX/IcicleFall.wav"),
"icicle_break": ("res://Assets/Audio/SFX/IcicleCrash.wav"),
"score": "res://Assets/Audio/SFX/ScoreLoop.wav",
"score_end": "res://Assets/Audio/SFX/Score.wav",
"pause": ("res://Assets/Audio/SFX/Pause.wav"),
"spring": ("res://Assets/Audio/SFX/Spring.wav"),
"swim": ("res://Assets/Audio/SFX/Swim.wav"),
"dry_bones_crumble": ("res://Assets/Audio/SFX/DryBonesCrumble.wav"),
"clock_get": ("res://Assets/Audio/SFX/ClockGet.wav"),
"bowser_flame": ("res://Assets/Audio/SFX/BowserFire.wav"),
"correct": ("res://Assets/Audio/SFX/Correct.wav"),
"note_block": ("res://Assets/Audio/SFX/NoteBlock.wav"),
"podoboo": ("res://Assets/Audio/SFX/Podoboo.wav"),
"hammer_throw": ("res://Assets/Audio/SFX/HammerThrow.wav"),
"firework": "res://Assets/Audio/SFX/Firework.wav",
"timer_beep": "res://Assets/Audio/SFX/TimerBeep.wav",
"hachisuke": "res://Assets/Audio/SFX/Hachisuke.wav",
"burner": "res://Assets/Audio/SFX/Burner.wav",
"rank_up_1": "res://Assets/Audio/SFX/RankUpCBA.wav",
"rank_up_2": "res://Assets/Audio/SFX/RankUpSP.wav",
"rank_down": "res://Assets/Audio/SFX/RankDown.wav",
"combo_lost": "res://Assets/Audio/SFX/ComboMeterLoss.wav",
"lakitu_throw": "res://Assets/Audio/SFX/LakituThrow.wav",
"lift_fall": "res://Assets/Audio/SFX/LiftFall.wav",
"cheep_cheep": "res://Assets/Audio/SFX/CheepCheepJump.wav",
"menu_move": "res://Assets/Audio/SFX/MenuNavigate.wav",
"timer_warning": "res://Assets/Audio/SFX/TimerRunningLow.wav",
"door_open": "res://Assets/Audio/SFX/DoorOpen.wav",
"door_close": "res://Assets/Audio/SFX/DoorClose.wav",
"key_collect": "res://Assets/Audio/SFX/KeyCollect.wav",
"lucky_star": "res://Assets/Audio/SFX/LuckyStar.wav",
"bumper": "res://Assets/Audio/SFX/Bumper.wav",
"bumper_high": "res://Assets/Audio/SFX/BumperHigh.wav",
"door_unlock": "res://Assets/Audio/SFX/DoorUnlock.wav",
"door_locked": "res://Assets/Audio/SFX/DoorLocked.wav"
}
@onready var sfx_library = DEFAULT_SFX_LIBRARY.duplicate()
@onready var music_player: AudioStreamPlayer = $Music
@onready var music_override_player: AudioStreamPlayer = $MusicOverride
var music_override_priority := -1
var active_sfxs := {}
var current_level_theme := ""
var current_clip_idx := 0
signal music_beat
var queued_sfxs := []
var current_music_override: MUSIC_OVERRIDES
enum MUSIC_OVERRIDES{NONE=-1, STAR=0, DEATH, PSWITCH, BOWSER, TIME_WARNING, LEVEL_COMPLETE, CASTLE_COMPLETE, ENDING, FLAG_POLE, HAMMER, RACE_LOSE, RACE_WIN, WING, COIN_HEAVEN_BONUS}
const OVERRIDE_STREAMS := [
("res://Assets/Audio/BGM/StarMan.json"),
("res://Assets/Audio/BGM/PlayerDie.json"),
("res://Assets/Audio/BGM/PSwitch.json"),
"res://Assets/Audio/BGM/Bowser.json",
"res://Assets/Audio/BGM/Hurry.json",
"res://Assets/Audio/BGM/LevelFinish.json",
"res://Assets/Audio/BGM/CastleFinish.json",
"res://Assets/Audio/BGM/Ending.json",
"res://Assets/Audio/SFX/FlagSlide.wav",
"res://Assets/Audio/BGM/Hammer.mp3",
("res://Assets/Audio/BGM/LoseRace.json"),
("res://Assets/Audio/BGM/WinRace.json"),
"res://Assets/Audio/BGM/Wing.json",
"res://Assets/Audio/BGM/PerfectCoinHeaven.mp3"
]
const MUSIC_BASE = preload("uid://da4vqkrpqnma0")
var character_sfx_map := {}
var audio_override_queue := []
func play_sfx(stream_name = "", position := Vector2.ZERO, pitch := 1.0) -> void:
if queued_sfxs.has(stream_name):
return
queued_sfxs.append(stream_name)
if stream_name is String:
if active_sfxs.has(stream_name):
active_sfxs[stream_name].queue_free()
var player = AudioStreamPlayer2D.new()
player.global_position = position
var stream = stream_name
var is_custom = false
if stream_name is String:
is_custom = sfx_library[stream_name].contains("user://custom_characters")
stream = import_stream(sfx_library[stream_name])
if is_custom == false:
player.stream = ResourceSetter.get_resource(stream, player)
else:
player.stream = stream
player.autoplay = true
player.pitch_scale = pitch
player.max_distance = 99999
player.bus = "SFX"
add_child(player)
active_sfxs[stream_name] = player
queued_sfxs.erase(stream_name)
await player.finished
active_sfxs.erase(stream_name)
player.queue_free()
func play_global_sfx(stream_name := "") -> void:
if get_viewport().get_camera_2d() == null:
return
play_sfx(stream_name, get_viewport().get_camera_2d().get_screen_center_position())
func _process(_delta: float) -> void:
handle_music()
func on_beat(idx := 0) -> void:
music_beat.emit(idx)
func stop_all_music() -> void:
AudioManager.music_player.stop()
if Global.current_level != null:
Global.current_level.music = null
AudioManager.audio_override_queue.clear()
AudioManager.stop_music_override(MUSIC_OVERRIDES.NONE, true)
func kill_sfx(sfx_name := "") -> void:
print(active_sfxs)
if active_sfxs.has(sfx_name):
active_sfxs[sfx_name].queue_free()
active_sfxs.erase(sfx_name)
func set_music_override(stream: MUSIC_OVERRIDES, priority := 0, stop_on_finish := true, restart := true) -> void:
if audio_override_queue.has(stream):
if current_music_override == stream and restart:
music_override_player.play()
return
if music_override_priority > priority:
audio_override_queue.push_front(stream)
return
else:
audio_override_queue.append(stream)
current_music_override = stream
print(OVERRIDE_STREAMS[stream])
music_override_player.stream = create_stream_from_json(OVERRIDE_STREAMS[stream])
music_override_player.bus = "Music" if stream != MUSIC_OVERRIDES.FLAG_POLE else "SFX"
music_override_player.play()
music_override_priority = priority
if stop_on_finish:
await music_override_player.finished
stop_music_override(stream)
func stop_music_override(stream: MUSIC_OVERRIDES, force := false) -> void:
if not force:
if stream == null:
return
elif stream != current_music_override:
audio_override_queue.erase(stream)
return
audio_override_queue.pop_back()
current_music_override = MUSIC_OVERRIDES.NONE
music_override_player.stop()
music_override_priority = -1
if audio_override_queue.is_empty():
audio_override_queue.clear()
music_override_priority = -1
current_music_override = MUSIC_OVERRIDES.NONE
music_override_player.stop()
else:
set_music_override(audio_override_queue[audio_override_queue.size() - 1])
func load_sfx_map(json := {}) -> void:
sfx_library = DEFAULT_SFX_LIBRARY.duplicate()
for i in json:
sfx_library[i] = json[i]
print(json)
func handle_music() -> void:
if Global.in_title_screen:
current_level_theme = ""
AudioServer.set_bus_effect_enabled(1, 0, Global.game_paused)
if is_instance_valid(Global.current_level):
if Global.current_level.music == null or current_music_override != MUSIC_OVERRIDES.NONE:
music_player.stop()
handle_music_override()
return
music_player.stream_paused = false
if current_level_theme != Global.current_level.music.resource_path and Global.current_level.music != null:
var stream = create_stream_from_json(Global.current_level.music.resource_path)
music_player.stream = stream
current_level_theme = Global.current_level.music.resource_path
if music_player.is_playing() == false and current_music_override == MUSIC_OVERRIDES.NONE:
music_player.stop()
current_music_override = MUSIC_OVERRIDES.NONE
music_player.play()
if music_player.stream is AudioStreamInteractive and music_player.is_playing():
if Global.time <= 100:
if music_player.get_stream_playback().get_current_clip_index() != 1:
music_player.get_stream_playback().switch_to_clip(1)
elif music_player.get_stream_playback().get_current_clip_index() != 0:
music_player.get_stream_playback().switch_to_clip(0)
func handle_music_override() -> void:
if music_override_player.stream is AudioStreamInteractive and music_override_player.is_playing():
if Global.time <= 100:
if music_override_player.get_stream_playback().get_current_clip_index() != 1:
music_override_player.get_stream_playback().switch_to_clip(1)
elif music_override_player.get_stream_playback().get_current_clip_index() != 0:
music_override_player.get_stream_playback().switch_to_clip(0)
func create_stream_from_json(json_path := "") -> AudioStream:
if json_path.contains(".json") == false:
var path = ResourceSetter.get_pure_resource_path(json_path)
if path.contains("user://"):
match json_path.get_slice(".", 1):
"wav":
return AudioStreamWAV.load_from_file(ResourceSetter.get_pure_resource_path(json_path))
"mp3":
return AudioStreamMP3.load_from_file(ResourceSetter.get_pure_resource_path(json_path))
"ogg":
return AudioStreamOggVorbis.load_from_file(ResourceSetter.get_pure_resource_path(json_path))
elif path.contains("res://"):
return load(path)
var bgm_file = $ResourceSetterNew.get_variation_json(JSON.parse_string(FileAccess.open(ResourceSetter.get_pure_resource_path(json_path), FileAccess.READ).get_as_text()).variations).source
var path = json_path.replace(json_path.get_file(), bgm_file)
path = ResourceSetter.get_pure_resource_path(path)
var stream = null
if path.get_file().contains(".bgm"):
stream = generate_interactive_stream(JSON.parse_string(FileAccess.open(path, FileAccess.READ).get_as_text()))
else:
if path.contains("res://"):
stream = load(path)
elif path.contains(".mp3"):
stream = AudioStreamMP3.load_from_file(path)
elif path.contains(".ogg"):
stream = AudioStreamOggVorbis.load_from_file(path)
return stream
func generate_interactive_stream(bgm_file := {}) -> AudioStreamInteractive:
var stream = MUSIC_BASE.duplicate()
var normal_path = ResourceSetter.get_pure_resource_path("res://Assets/Audio/BGM/" + bgm_file.Normal.source)
var hurry_path = ResourceSetter.get_pure_resource_path("res://Assets/Audio/BGM/" + bgm_file.Hurry.source)
stream.set_clip_stream(0, import_stream(normal_path, bgm_file.Normal.loop))
stream.set_clip_stream(1, import_stream(hurry_path, bgm_file.Hurry.loop))
return stream
func import_stream(file_path := "", loop_point := -1.0) -> AudioStream:
var path = file_path
var stream = null
if path.contains("res://"):
stream = load(path)
elif path.contains(".mp3"):
stream = AudioStreamMP3.load_from_file(ResourceSetter.get_pure_resource_path(file_path))
elif path.contains(".ogg"):
stream = AudioStreamOggVorbis.load_from_file(ResourceSetter.get_pure_resource_path(file_path))
elif path.contains(".wav"):
stream = AudioStreamWAV.load_from_file(path)
print([path, stream])
if path.contains(".mp3"):
stream.set_loop(loop_point >= 0)
stream.set_loop_offset(loop_point)
elif path.contains(".ogg"):
stream.set_loop(loop_point >= 0)
stream.set_loop_offset(loop_point)
return stream