aboutsummaryrefslogtreecommitdiff
path: root/game/src/Game/MusicConductor
diff options
context:
space:
mode:
Diffstat (limited to 'game/src/Game/MusicConductor')
-rw-r--r--game/src/Game/MusicConductor/MusicConductor.gd77
-rw-r--r--game/src/Game/MusicConductor/MusicConductor.tscn13
-rw-r--r--game/src/Game/MusicConductor/MusicPlayer.gd73
-rw-r--r--game/src/Game/MusicConductor/MusicPlayer.tscn63
-rw-r--r--game/src/Game/MusicConductor/SongInfo.gd11
5 files changed, 237 insertions, 0 deletions
diff --git a/game/src/Game/MusicConductor/MusicConductor.gd b/game/src/Game/MusicConductor/MusicConductor.gd
new file mode 100644
index 0000000..98dd0eb
--- /dev/null
+++ b/game/src/Game/MusicConductor/MusicConductor.gd
@@ -0,0 +1,77 @@
+extends Node
+
+# REQUIREMENTS
+# * SS-67
+@export_dir var music_directory : String
+@export var first_song_name : String
+
+var _selected_track = 0
+var _available_songs : Array[SongInfo] = []
+var _auto_play_next_song : bool = true
+
+## True if music player should be visible.
+## Used to keep keep consistency between scene changes
+var is_music_player_visible : bool = true
+
+func get_all_song_names() -> Array[String]:
+ var songNames : Array[String] = []
+ for si in _available_songs:
+ songNames.append(si.song_name)
+ return songNames
+
+func get_current_song_index() -> int:
+ return _selected_track
+
+func get_current_song_name() -> String:
+ return _available_songs[_selected_track].song_name
+
+func scrub_song_by_percentage(percentage: float) -> void:
+ var percentInSeconds : float = (percentage / 100.0) * $AudioStreamPlayer.stream.get_length()
+ $AudioStreamPlayer.play(percentInSeconds)
+
+func get_current_song_progress_percentage() -> float:
+ return 100 * ($AudioStreamPlayer.get_playback_position() / $AudioStreamPlayer.stream.get_length())
+
+func is_paused() -> bool:
+ return $AudioStreamPlayer.stream_paused
+
+func toggle_play_pause() -> void:
+ $AudioStreamPlayer.stream_paused = !$AudioStreamPlayer.stream_paused
+
+func start_current_song() -> void:
+ $AudioStreamPlayer.stream = _available_songs[_selected_track].song_stream
+ $AudioStreamPlayer.play()
+
+# REQUIREMENTS
+# * SS-70
+func start_song_by_index(id: int) -> void:
+ _selected_track = id
+ start_current_song()
+
+# REQUIREMENTS
+# * SS-69
+func select_next_song() -> void:
+ _selected_track = (_selected_track + 1) % len(_available_songs)
+ start_current_song()
+
+func select_previous_song() -> void:
+ _selected_track = (len(_available_songs) - 1) if (_selected_track == 0) else (_selected_track - 1)
+ start_current_song()
+
+# REQUIREMENTS
+# * SND-2
+func _ready():
+ var dir = DirAccess.open(music_directory)
+ for fname in dir.get_files():
+ if fname.ends_with(".import"):
+ fname = fname.get_basename()
+ if fname.get_basename() == first_song_name:
+ _selected_track = _available_songs.size()
+ _available_songs.append(SongInfo.new(music_directory, fname))
+ start_current_song()
+
+
+func _on_audio_stream_player_finished():
+ if _auto_play_next_song:
+ select_next_song()
+ start_current_song()
diff --git a/game/src/Game/MusicConductor/MusicConductor.tscn b/game/src/Game/MusicConductor/MusicConductor.tscn
new file mode 100644
index 0000000..3c288be
--- /dev/null
+++ b/game/src/Game/MusicConductor/MusicConductor.tscn
@@ -0,0 +1,13 @@
+[gd_scene load_steps=2 format=3 uid="uid://b1h31mnn8n2nu"]
+
+[ext_resource type="Script" path="res://src/Game/MusicConductor/MusicConductor.gd" id="1_56t1b"]
+
+[node name="MusicConductor" type="Node"]
+script = ExtResource("1_56t1b")
+music_directory = "res://audio/music"
+first_song_name = "The_Crown"
+
+[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
+bus = &"MUSIC_BUS"
+
+[connection signal="finished" from="AudioStreamPlayer" to="." method="_on_audio_stream_player_finished"]
diff --git a/game/src/Game/MusicConductor/MusicPlayer.gd b/game/src/Game/MusicConductor/MusicPlayer.gd
new file mode 100644
index 0000000..47be158
--- /dev/null
+++ b/game/src/Game/MusicConductor/MusicPlayer.gd
@@ -0,0 +1,73 @@
+extends Control
+
+@export var _song_selector_button : OptionButton
+@export var _progress_slider : HSlider
+@export var _previous_song_button : Button
+@export var _play_pause_button : Button
+@export var _next_song_button : Button
+@export var _visbility_button : Button
+
+var _is_user_dragging_progress_slider : bool = false
+
+func _ready():
+ for songName in MusicConductor.get_all_song_names():
+ _song_selector_button.add_item(songName, _song_selector_button.item_count)
+ _update_song_name_visual()
+ _update_play_pause_button()
+ _set_music_player_visible(MusicConductor.is_music_player_visible)
+
+
+func _process(_delta):
+ if !_is_user_dragging_progress_slider:
+ _progress_slider.value = MusicConductor.get_current_song_progress_percentage()
+
+func _update_song_name_visual():
+ _song_selector_button.selected = MusicConductor.get_current_song_index()
+
+func _update_play_pause_button():
+ _play_pause_button.text = "◼" if MusicConductor.is_paused() else "▶"
+
+func _on_play_pause_button_pressed():
+ MusicConductor.toggle_play_pause()
+ _update_play_pause_button()
+
+func _on_next_song_button_pressed():
+ MusicConductor.select_next_song()
+ _update_song_name_visual()
+ _update_play_pause_button()
+
+func _on_previous_song_button_pressed():
+ MusicConductor.select_previous_song()
+ _update_song_name_visual()
+ _update_play_pause_button()
+
+# REQUIREMENTS
+# * UIFUN-92
+func _on_option_button_item_selected(index):
+ MusicConductor.start_song_by_index(index)
+ _update_song_name_visual()
+ _update_play_pause_button()
+
+
+func _on_progress_slider_drag_started():
+ _is_user_dragging_progress_slider = true
+
+
+func _on_progress_slider_drag_ended(_value_changed):
+ MusicConductor.scrub_song_by_percentage(_progress_slider.value)
+ _is_user_dragging_progress_slider = false
+ _update_play_pause_button()
+
+func _set_music_player_visible(is_player_visible : bool) -> void:
+ MusicConductor.is_music_player_visible = is_player_visible
+ _visbility_button.text = "⬆️" if is_player_visible else "⬇"
+ _song_selector_button.visible = is_player_visible
+ _progress_slider.visible = is_player_visible
+ _previous_song_button.visible = is_player_visible
+ _play_pause_button.visible = is_player_visible
+ _next_song_button.visible = is_player_visible
+
+# REQUIREMENTS
+# * UIFUN-91
+func _on_music_ui_visibility_button_pressed():
+ _set_music_player_visible(not MusicConductor.is_music_player_visible)
diff --git a/game/src/Game/MusicConductor/MusicPlayer.tscn b/game/src/Game/MusicConductor/MusicPlayer.tscn
new file mode 100644
index 0000000..8d47ba0
--- /dev/null
+++ b/game/src/Game/MusicConductor/MusicPlayer.tscn
@@ -0,0 +1,63 @@
+[gd_scene load_steps=2 format=3 uid="uid://cvl76duuym1wq"]
+
+[ext_resource type="Script" path="res://src/Game/MusicConductor/MusicPlayer.gd" id="1_gcm4m"]
+
+[node name="MusicPlayer" type="BoxContainer" node_paths=PackedStringArray("_song_selector_button", "_progress_slider", "_previous_song_button", "_play_pause_button", "_next_song_button", "_visbility_button")]
+editor_description = "UI-104"
+offset_right = 150.0
+offset_bottom = 110.0
+mouse_filter = 2
+vertical = true
+script = ExtResource("1_gcm4m")
+_song_selector_button = NodePath("SongSelectorButton")
+_progress_slider = NodePath("ProgressSlider")
+_previous_song_button = NodePath("ButtonList/PreviousSongButton")
+_play_pause_button = NodePath("ButtonList/PlayPauseButton")
+_next_song_button = NodePath("ButtonList/NextSongButton")
+_visbility_button = NodePath("MusicUIVisibilityButton")
+
+[node name="SongSelectorButton" type="OptionButton" parent="."]
+editor_description = "UI-107"
+custom_minimum_size = Vector2(150, 0)
+layout_mode = 2
+alignment = 1
+text_overrun_behavior = 3
+fit_to_longest_item = false
+
+[node name="ProgressSlider" type="HSlider" parent="."]
+custom_minimum_size = Vector2(150, 0)
+layout_mode = 2
+size_flags_vertical = 1
+
+[node name="ButtonList" type="HBoxContainer" parent="."]
+layout_mode = 2
+size_flags_horizontal = 4
+mouse_filter = 2
+
+[node name="PreviousSongButton" type="Button" parent="ButtonList"]
+layout_mode = 2
+text = "<"
+
+[node name="PlayPauseButton" type="Button" parent="ButtonList"]
+custom_minimum_size = Vector2(30, 0)
+layout_mode = 2
+text = "▶"
+
+[node name="NextSongButton" type="Button" parent="ButtonList"]
+layout_mode = 2
+text = ">"
+
+[node name="MusicUIVisibilityButton" type="Button" parent="."]
+editor_description = "UI-106"
+layout_mode = 2
+size_flags_horizontal = 4
+toggle_mode = true
+text = "⬆"
+
+[connection signal="item_selected" from="SongSelectorButton" to="." method="_on_option_button_item_selected"]
+[connection signal="drag_ended" from="ProgressSlider" to="." method="_on_progress_slider_drag_ended"]
+[connection signal="drag_started" from="ProgressSlider" to="." method="_on_progress_slider_drag_started"]
+[connection signal="pressed" from="ButtonList/PreviousSongButton" to="." method="_on_previous_song_button_pressed"]
+[connection signal="pressed" from="ButtonList/PlayPauseButton" to="." method="_on_play_pause_button_pressed"]
+[connection signal="pressed" from="ButtonList/NextSongButton" to="." method="_on_next_song_button_pressed"]
+[connection signal="pressed" from="MusicUIVisibilityButton" to="." method="_on_music_ui_visibility_button_pressed"]
diff --git a/game/src/Game/MusicConductor/SongInfo.gd b/game/src/Game/MusicConductor/SongInfo.gd
new file mode 100644
index 0000000..1ee9adc
--- /dev/null
+++ b/game/src/Game/MusicConductor/SongInfo.gd
@@ -0,0 +1,11 @@
+extends Resource
+class_name SongInfo
+
+var song_path : String = ""
+var song_name : String = ""
+var song_stream : Resource
+
+func _init(dirname:String, fname:String):
+ song_path = dirname.path_join(fname)
+ song_name = fname.get_basename().replace("_", " ")
+ song_stream = load(song_path)