diff --git a/MainWindow.gd b/MainWindow.gd index e063342..37f53a5 100644 --- a/MainWindow.gd +++ b/MainWindow.gd @@ -91,7 +91,7 @@ var music_list_scroll: ScrollContainer = $MusicListPanel/ScrollContainer var music_list: VBoxContainer = $MusicListPanel/ScrollContainer/MusicList @onready -var players: VBoxContainer = $Players/VBoxContainer +var player_container: VBoxContainer = $Players/VBoxContainer @onready var character_select: Control = $CharacterSelect @@ -120,6 +120,9 @@ var winner_popup: PopupPanel = $WinnerPopupPanel @onready var winner_label: Label = $WinnerPopupPanel/WinnerLabel +@onready +var winner_picture: TextureRect = $WinnerPopupPanel/WinnerPicture + @onready var auto_repeat_song_button: CheckButton = $RepeatSongCheckButton @@ -127,27 +130,26 @@ var auto_repeat_song_button: CheckButton = $RepeatSongCheckButton var music_player_container: PanelContainer = $MusicPlayer var player := preload("res://Player.tscn") +var song_list_object := preload("res://song_list_object.tscn") var songs: Array= [] var games: Array = [] -var song_list: Array = [] -var next_label: Label var current_player: Node # Called when the node enters the scene tree for the first time. func _ready() -> void: var is_debug: bool = OS.has_feature("debug") - - #var is_mac := OS.has_feature("macos") + var is_mac: bool = OS.has_feature("macos") if is_debug: print("is_debug") ##Settings.default_path = "http://localhost:8080" ##Settings.selected_server = 3 - Settings.default_path = "http://localhost:8080" - Settings.selected_server = 3 - #if is_mac: - # print("is_mac") - next_button.pressed.connect(next_track) + Settings.default_path = "http://localhost:8080" + Settings.selected_server = 3 + Settings.winning_score = 5 + if is_mac: + print("is_mac") + next_button.pressed.connect(fetch_song.bind(false)) show_answer_button.pressed.connect(show_answer_pressed) search_button.pressed.connect(show_search) sync_button.pressed.connect(sync_games) @@ -160,8 +162,8 @@ func _ready() -> void: character_select.connect("character_selected", _on_character_selected) new_player_name_field.connect("enter_key_pressed", add_player) - music_player_container.connect("add_to_queue", _on_add_to_queue) - music_player_container.connect("show_answer", show_answer_pressed) + music_player_container.connect("play_next_song", fetch_song.bind(false)) + music_player_container.connect("update_song_list", update_song_list) add_players_button.pressed.connect(add_players) add_player_button.pressed.connect(add_player) @@ -169,7 +171,8 @@ func _ready() -> void: auto_repeat_song_button.pressed.connect(repeat_song) get_suggestion_list() - fetch_full_music_list_at_start() + fetch_song_list_at_start() + fetch_song(true) func _input(event: InputEvent) -> void: if event is InputEventKey and event.pressed: @@ -199,21 +202,127 @@ func _input(event: InputEvent) -> void: func repeat_song() -> void: Settings.auto_repeat_song = !Settings.auto_repeat_song -func next_track() -> void: - next_button.disabled = true - music_player_container.pause() - music_player_container.fetch_next_song() +func show_answer_pressed() -> void: + print("show_answer_pressed") + print("show_answer_pressed song_object: ", Settings.song_object_array[Settings.latest_played_song]._to_string()) + Settings.song_object_array[Settings.latest_played_song].is_answered = true + update_song_list() + +func fetch_song_list_at_start() -> void: + print("fetch_song_list_at_start") + var fetch_song_list_at_start_done: Callable = func fetch_songs_at_start_done(data) -> void: + Settings.song_object_array = [] + if data == null: + Settings.latest_played_song = Settings.song_object_array.size() + update_song_list() + return + if typeof(data) == TYPE_ARRAY: + for d in data: + var new_song_object: SongObject = SongObject.new() + new_song_object.song_number = d.SongNo + new_song_object.game_title = d.Game + new_song_object.song_title = d.Song + new_song_object.is_answered = true + new_song_object.has_played = true + Settings.song_object_array.append(new_song_object) + else: + print("Unexpected data") + Settings.latest_played_song = Settings.song_object_array.size() + update_song_list() + Settings.make_request2("/music/list", fetch_song_list_at_start_done, true) + +#not first "/music/addPlayed" +#"/music/rand" +#"/music/addQue" +#"/music/info" +func fetch_song(first_time: bool) -> void: + print("fetch_song") + if !first_time: + if Settings.song_object_array[Settings.latest_played_song].has_played && !Settings.song_object_array[Settings.latest_played_song].is_answered: + Settings.song_object_array[Settings.latest_played_song].is_answered = true + Settings.latest_played_song = Settings.song_object_array.size() - 1 + music_player_container.play_song_object(Settings.latest_played_song) + if Settings.add_to_stats : + print("add to stats") + Settings.make_request3("/music/addPlayed") + + next_button.disabled = true + + var url: String = "" + if Settings.use_low_played_mode: + url = "/music/rand/low" + else: + url = "/music/rand" + var new_song_object: SongObject = SongObject.new() + var fetch_song_done: Callable = func fetch_song_done(body: PackedByteArray) -> void: + print("fetch_song_done") + var sound: AudioStream = AudioStreamMP3.new() + sound.data = body + new_song_object.song = sound + + var added_to_que_done: Callable = func added_to_que_done() -> void: + print("added_to_que_done") + var fetch_song_info_done: Callable = func fetch_song_info_done(data_received: Dictionary) -> void: + print("fetch_song_info_done") + new_song_object.song_number = data_received.SongNo + new_song_object.game_title = data_received.Game + new_song_object.song_title = data_received.Song + + print("new_song_object: ", new_song_object.to_string()) + Settings.song_object_array.append(new_song_object) + next_button.disabled = false + Settings.make_request2("/music/info", fetch_song_info_done, true) + + Settings.make_request2("/music/addQue", added_to_que_done, false) + + Settings.make_request2(url, fetch_song_done, true) + +func update_song_list() -> void: + print("update_song_list") + if Settings.currently_playing_song >= 0: + game_label.text = Settings.song_object_array[Settings.currently_playing_song].get_game_title() + song_label.text = Settings.song_object_array[Settings.currently_playing_song].get_song_title() + Settings.delete_children(music_list) + for s: SongObject in Settings.song_object_array: + if !s.has_played: + return + var new_song_list_object: SongListObject = song_list_object.instantiate() + new_song_list_object.song_object = s + new_song_list_object.gui_input.connect(song_object_clicked.bind(s.song_number)) + music_list.add_child(new_song_list_object) + +func song_object_clicked(event: InputEvent, song_no: int) -> void: + if (event is InputEventMouseButton && event.pressed && event.button_index == MOUSE_BUTTON_LEFT): + print("Clicked Song Object: ", Settings.song_object_array[song_no].to_string()) + if Settings.song_object_array[song_no].song == null: + print("has no song, need to download") + var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void: + var sound: AudioStream = AudioStreamMP3.new() + sound.data = body + Settings.song_object_array[song_no].song = sound + music_player_container.play_song_object(song_no) + + Settings.make_request2("/music?song=" + str(song_no), song_downloaded, true) + else: + print("song already downloaded") + music_player_container.play_song_object(song_no) func reset_playlist() -> void: print("reset_playlist") - Settings.make_request2("/music/reset", fetch_full_music_list_at_start, false) + var playlist_reset_done: Callable = func playlist_reset_done() -> void: + Settings.currently_playing_song = -1 + Settings.latest_played_song = 0 + Settings.song_object_array.clear() + fetch_song_list_at_start() + fetch_song(true) + + Settings.make_request2("/music/reset", playlist_reset_done, false) func reset_points() -> void: print("reset_points") - var players_to_reset: Array = players.get_children() - for player_to_reset: Node in players_to_reset: - if player_to_reset.has_method("reset_points"): - player_to_reset.reset_points() + for player_objects: PlayerObject in Settings.player_array: + player_objects.reset_points() + load_players() func show_about() -> void: print("show_about") @@ -232,13 +341,6 @@ func get_statistics() -> void: statistic_popup.visible = true statistic_label.text = "Total amount of games in the playlist: " + str(games.size()) -func _on_add_to_queue() -> void: - print("_on_add_to_queue") - next_button.disabled = false - Settings.make_request2("/music/addQue", fetched, false) - game_label.text = "????????" - song_label.text = "??????" - func sync_games() -> void: print("sync_games") sync_button.disabled = true @@ -249,14 +351,24 @@ func sync_games() -> void: reset_playlist() get_suggestion_list() search_view.get_list_of_games() + + var new_games_synced: Callable = func new_games_synced(synced) -> void: + sync_button.disabled = false + sync_popup.visible = true + print("games_synced") + print("synced: ", synced) + reset_playlist() + get_suggestion_list() + search_view.get_list_of_games() + if Settings.quick_sync == true: - Settings.make_request2("/sync/quick", games_synced, false) + Settings.make_request2("/sync/new", new_games_synced, true) else: - Settings.make_request2("/sync", games_synced, false) + Settings.make_request2("/sync/quick", games_synced, false) func get_suggestion_list() -> void: print("get_suggestion_list") - var populate_list: Callable = func(array) -> void: + var populate_list: Callable = func populate_list(array) -> void: if typeof(array) == TYPE_ARRAY: games = [] games.append_array(array) @@ -274,162 +386,73 @@ func get_suggestion_list() -> void: func add_players() -> void: print("add_players") + Settings.edit_players = !Settings.edit_players add_player_container.visible = !add_player_container.visible new_player_name_field.grab_focus() func add_player() -> void: print("add_player") - var new_player: Node = player.instantiate() - new_player.player_name = new_player_name_field.text - new_player.player_score = new_player_name_field.text + ": 0" - new_player.id = Settings.get_next_player_id() + var new_player_object: PlayerObject = PlayerObject.new(new_player_name_field.text, Settings.player_array.size()) new_player_name_field.text = "" - players.add_child(new_player) - new_player.connect("change_character_clicked", _on_player_change_character_clicked.bind(new_player)) - new_player.connect("first_point_triggerd", music_player_container._on_point_triggered.bind("first")) - new_player.connect("match_point_triggerd", music_player_container._on_point_triggered.bind("match")) - new_player.connect("winner_triggerd", _on_player_won.bind(new_player.player_name)) - new_player.connect("player_removed", _on_player_removed.bind(new_player)) + Settings.player_array.append(new_player_object) + new_player_object.connect("first_point_triggerd", music_player_container._on_point_triggered.bind("first")) + new_player_object.connect("match_point_triggerd", music_player_container._on_point_triggered.bind("match")) + new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object.id)) + new_player_object.connect("point_given_sound", _on_make_point_given_sound) + new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object.id)) + new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object.id)) + load_players() -func _on_player_won(player_name: String) -> void: +func load_players() -> void: + print("load_players") + Settings.delete_player_children(player_container) + var counter: int = 0 + for player_object: PlayerObject in Settings.player_array: + print(player_object.player_name) + + var new_player: Player = player.instantiate() + new_player.id = counter + + new_player.connect("change_character_clicked", _on_player_change_character_clicked.bind(new_player)) + new_player.connect("player_removed", _on_player_removed.bind(counter)) + + player_container.add_child(new_player) + counter += 1 + +func _on_point_given(player_given_point: int) -> void: + print("_on_point_given") + if Settings.currently_playing_song >= 0: + Settings.song_object_array[Settings.currently_playing_song].add_point(player_given_point) + update_song_list() + +func _on_make_point_given_sound() -> void: + music_player_container.play_sound_effect(preload("res://sounds/itemopen.wav")) + +func _on_point_taken(player_taken_point: int) -> void: + print("_on_point_taken") + music_player_container.play_sound_effect(preload("res://sounds/itemequip.wav")) + if Settings.currently_playing_song >= 0: + Settings.song_object_array[Settings.currently_playing_song].remove_point(player_taken_point) + update_song_list() + +func _on_player_won(winning_player_id: int) -> void: print("_on_player_won") winner_popup.visible = true - winner_label.text = player_name + " won!!" + winner_label.text = Settings.player_array[winning_player_id].player_name + " won!!" + winner_picture.custom_minimum_size = Vector2(80, 40) + winner_picture.ignore_texture_size = true + winner_picture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT + winner_picture.texture = Settings.player_array[winning_player_id].character music_player_container.play_sound(preload("res://sounds/winning.mp3")) -func show_search() -> void: - print("show_search") - if search_view.visible == false: - search_view.visible = true - else: - search_view.visible = false - -func show_answer_pressed() -> void: - print("show_answer_pressed") - var show_fetched: Callable = func(data_received: Dictionary) -> void: - print("show_fetched data_received: ", data_received) - game_label.text = data_received.Game - song_label.text = data_received.Song - var format_string: String = "%d. %s - %s" - var actual_string: String = format_string % [(data_received.SongNo+1), data_received.Game, data_received.Song] - if next_label == null: - next_label = Label.new() - next_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) - next_label.add_theme_font_size_override("font_size", 20) - next_label.text = actual_string - Settings.make_request2("/music/info", show_fetched, true) - -func fetch_full_music_list_at_start() -> void: - print("fetch_full_music_list_at_start") - var show_music_list_at_start: Callable = func(data) -> void: - if data == null: - song_list = [] - Settings.delete_children(music_list) - return - if typeof(data) == TYPE_ARRAY: - song_list = [] - song_list.append_array(data) - print("song_list", song_list) - for song: Dictionary in song_list: - var music_list_label: Label = Label.new() - music_list_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) - music_list_label.add_theme_font_size_override("font_size", 20) - music_list_label.autowrap_mode = TextServer.AUTOWRAP_WORD - var format_string: String = "%d. %s - %s" - var actual_string: String = format_string % [(song.SongNo+1), song.Game, song.Song] - music_list_label.text = actual_string - music_list_label.mouse_filter = Control.MOUSE_FILTER_PASS - music_list_label.gui_input.connect(song_clicked.bind(music_list_label, song.SongNo)) - music_list.add_child(music_list_label) - else: - print("Unexpected data") - Settings.make_request2("/music/list", show_music_list_at_start, true) +func _on_player_removed(player_to_remove: int) -> void: + print("_on_player_removed ", player_to_remove) + Settings.player_array.remove_at(player_to_remove) + load_players() -func fetch_full_music_list(event: InputEvent, song_no: int) -> void: - print("fetch_full_music_list") - if (event is InputEventMouseButton && event.pressed && event.button_index == MOUSE_BUTTON_LEFT): - var show_music_list: Callable = func(data_received) -> void: - if typeof(data_received) == TYPE_ARRAY: - song_list = [] - song_list.append_array(data_received) - Settings.delete_children(music_list) - for song: Dictionary in song_list: - var music_label: Label= Label.new() - music_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) - music_label.add_theme_font_size_override("font_size", 20) - music_label.autowrap_mode = TextServer.AUTOWRAP_WORD - game_label.text = song.Game - song_label.text = song.Song - var format_string: String = "%d. %s - %s" - var actual_string: String = format_string % [(song.SongNo+1), song.Game, song.Song] - music_label.text = actual_string - music_label.mouse_filter = Control.MOUSE_FILTER_PASS - music_label.gui_input.connect(song_clicked.bind(music_label, song.SongNo)) - print(str(music_list.get_child_count()) + " | " + str(song_list.size() - 1)) - if music_list.get_child_count() == song_list.size() - 1: - music_label.add_theme_color_override("font_color", Color(1, 0.5, 0)) - music_list.add_child(music_label) - else: - print("Unexpected data") - Settings.make_request2("/music/list", show_music_list, true) - Settings.make_request2("/music?song=" + str(song_no), music_player_container.play_song, true) - Settings.make_request2("/music/info", show_answer, true) - -func show_answer(answer: Dictionary) -> void: - print("show_answer1: ", answer) - game_label.text = answer.Game - song_label.text = answer.Song - -func fetched() -> void: - print("fetched") - var show_fetched_list: Callable = func(data_received) -> void: - if typeof(data_received) == TYPE_ARRAY: - song_list = [] - song_list.append_array(data_received) - Settings.delete_children(music_list) - song_list.remove_at(song_list.size() - 1) - for song: Dictionary in song_list: - var fetched_song_label: Label = Label.new() - fetched_song_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) - fetched_song_label.add_theme_font_size_override("font_size", 20) - fetched_song_label.autowrap_mode = TextServer.AUTOWRAP_WORD - var format_string: String = "%d. %s - %s" - var actual_string: String = format_string % [(song.SongNo+1), song.Game, song.Song] - fetched_song_label.text = actual_string - fetched_song_label.mouse_filter = Control.MOUSE_FILTER_PASS - fetched_song_label.gui_input.connect(song_clicked.bind(fetched_song_label, song.SongNo)) - music_list.add_child(fetched_song_label) - - songs = music_list.get_children() - for song: Label in songs: - song.remove_theme_color_override("font_color") - next_label = Label.new() - next_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) - next_label.add_theme_font_size_override("font_size", 20) - next_label.add_theme_color_override("font_color", Color(1, 0.5, 0)) - next_label.text = "??? - ???" - next_label.mouse_filter = Control.MOUSE_FILTER_PASS - next_label.gui_input.connect(fetch_full_music_list.bind(songs.size())) - music_list.add_child(next_label) - else: - print("Unexpected data") - Settings.make_request2("/music/list", show_fetched_list, true) - -func song_clicked(event: InputEvent, clicked_song_label: Label, song_no: int) -> void: - print("song_clicked") - if (event is InputEventMouseButton && event.pressed && event.button_index == MOUSE_BUTTON_LEFT): - print("Song Clicked: " + str(song_no)) - songs = music_list.get_children() - for song: Label in songs: - song.remove_theme_color_override("font_color") - clicked_song_label.add_theme_color_override("font_color", Color(1, 0.5, 0)) - Settings.make_request2("/music?song=" + str(song_no), music_player_container.play_song, true) - Settings.make_request2("/music/info", show_answer, true) - -func _on_player_removed(new_player: Node) -> void: - print("_on_player_removed") - Settings.player_removed() - players.remove_child(new_player) + for song_list_object: SongObject in Settings.song_object_array: + Settings.song_object_array[song_list_object.song_number].players_given_point.remove_at(Settings.song_object_array[song_list_object.song_number].players_given_point.find(player_to_remove)) + update_song_list() func _on_player_change_character_clicked(new_player: Node) -> void: print("_on_player_change_character_clicked") @@ -441,6 +464,14 @@ func _on_character_selected(file_name: String) -> void: character_select.visible = false current_player._on_control_character_selected_clicked(file_name) +func show_search() -> void: + print("show_search") + if search_view.visible == false: + search_view.visible = true + else: + search_view.visible = false + + ###Local var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3' diff --git a/MainWindow.tscn b/MainWindow.tscn index bca8396..182a06d 100644 --- a/MainWindow.tscn +++ b/MainWindow.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=25 format=3 uid="uid://xwq863o6uvsu"] +[gd_scene load_steps=26 format=3 uid="uid://xwq863o6uvsu"] [ext_resource type="Script" path="res://MainWindow.gd" id="1_eu0t5"] [ext_resource type="PackedScene" uid="uid://b16on0oscg1bv" path="res://CharacterSelect.tscn" id="2_76kf4"] @@ -9,6 +9,7 @@ [ext_resource type="Script" path="res://PlayerNameField.gd" id="7_qsdfy"] [ext_resource type="Theme" uid="uid://rxexo3ur85as" path="res://LightGrayTheme.tres" id="7_wxbv6"] [ext_resource type="PackedScene" uid="uid://bxydgil1yifps" path="res://SearchWindow.tscn" id="9_5ijvr"] +[ext_resource type="Texture2D" uid="uid://r4as0nmtoa7p" path="res://noCharacter.png" id="11_1qef0"] [ext_resource type="PackedScene" uid="uid://dldpeo5y3l5hq" path="res://SettingsWindow.tscn" id="11_k62u5"] [sub_resource type="LabelSettings" id="LabelSettings_ychxr"] @@ -103,6 +104,26 @@ file_mode = 2 access = 2 show_hidden_files = true +[node name="MusicListPanel" type="PanelContainer" parent="."] +layout_mode = 0 +offset_left = 64.0 +offset_top = 384.0 +offset_right = 1152.0 +offset_bottom = 896.0 +theme = ExtResource("7_wxbv6") + +[node name="ScrollContainer" type="ScrollContainer" parent="MusicListPanel"] +layout_mode = 2 +follow_focus = true +script = ExtResource("7_dj026") + +[node name="MusicList" type="VBoxContainer" parent="MusicListPanel/ScrollContainer"] +texture_filter = 1 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 10 + [node name="ScrollContainer" type="ScrollContainer" parent="."] texture_filter = 1 layout_mode = 0 @@ -297,25 +318,6 @@ action_mode = 0 shortcut = SubResource("Shortcut_d6fml") text = "Randomize new track" -[node name="MusicListPanel" type="PanelContainer" parent="."] -layout_mode = 0 -offset_left = 64.0 -offset_top = 384.0 -offset_right = 1152.0 -offset_bottom = 896.0 -theme = ExtResource("7_wxbv6") - -[node name="ScrollContainer" type="ScrollContainer" parent="MusicListPanel"] -layout_mode = 2 -follow_focus = true -script = ExtResource("7_dj026") - -[node name="MusicList" type="VBoxContainer" parent="MusicListPanel/ScrollContainer"] -texture_filter = 1 -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 - [node name="SearchButton" type="Button" parent="."] texture_filter = 1 layout_mode = 0 @@ -408,7 +410,7 @@ vertical_alignment = 1 [node name="AboutPopupPanel" type="PopupPanel" parent="."] initial_position = 2 -size = Vector2i(848, 272) +size = Vector2i(848, 710) [node name="VBoxContainer" type="VBoxContainer" parent="AboutPopupPanel"] offset_left = 4.0 @@ -479,17 +481,27 @@ offset_bottom = 354.0 [node name="WinnerPopupPanel" type="PopupPanel" parent="."] initial_position = 2 -size = Vector2i(350, 100) +size = Vector2i(600, 350) + +[node name="WinnerPicture" type="TextureRect" parent="WinnerPopupPanel"] +offset_left = 4.0 +offset_top = 4.0 +offset_right = 596.0 +offset_bottom = 346.0 +texture = ExtResource("11_1qef0") +expand_mode = 2 +stretch_mode = 4 [node name="WinnerLabel" type="Label" parent="WinnerPopupPanel"] texture_filter = 1 offset_left = 4.0 offset_top = 4.0 -offset_right = 346.0 -offset_bottom = 96.0 +offset_right = 596.0 +offset_bottom = 346.0 text = "Sansan won!!" label_settings = SubResource("LabelSettings_hr75l") horizontal_alignment = 1 +vertical_alignment = 2 [node name="RepeatSongCheckButton" type="CheckButton" parent="."] layout_mode = 0 diff --git a/MusicPlayer.gd b/MusicPlayer.gd index cbdd39d..f2ac383 100644 --- a/MusicPlayer.gd +++ b/MusicPlayer.gd @@ -13,6 +13,12 @@ var restart_button: TextureButton = $MusicPlayerContainer/RestartTextureButton @onready var audio_player: AudioStreamPlayer = $AudioStreamPlayer +@onready +var sound_player: AudioStreamPlayer = $AudioSoundPlayer + +@onready +var sound_effect_player: AudioStreamPlayer = $AudioSoundEffectPlayer + @onready var progress_slider: HSlider = $MusicPlayerContainer/MusicPlayerSlider @@ -35,10 +41,8 @@ var playback_position: float var stream: AudioStream var song_finished: bool = false -signal fetched -signal winner(player_name: String) -signal add_to_queue -signal show_answer +signal update_song_list +signal play_next_song # Called when the node enters the scene tree for the first time. func _ready() -> void: @@ -98,7 +102,7 @@ func _on_finished() -> void: play_button.texture_normal = play_icon song_finished = true if !Settings.stop_after_current: - fetch_next_song() + play_next_song.emit() if Settings.auto_repeat_song: restart() @@ -133,59 +137,53 @@ func _on_point_triggered(point: String) -> void: elif point == "match": play_sound(preload("res://sounds/sound0.mp3")) +func play_sound_effect(sound_effect_name: AudioStream) -> void: + sound_effect_player.stream = sound_effect_name + sound_effect_player.play() + func play_sound(sound_name: AudioStream) -> void: - audio_player.stream = sound_name - audio_player.play() - song_finished = false - play_button.texture_normal = pause_icon - stream = audio_player.stream - progress_slider.max_value = round(stream.get_length()) - progress_slider.tick_count = round(stream.get_length() / 60) + sound_player.stream = sound_name + audio_player.stop() + sound_player.play() + song_finished = true + play_button.texture_normal = play_icon + progress_slider.value = 0 + if stream != null: + music_time_label.text = format_text(progress_slider.value, stream.get_length()) func play_song(body: PackedByteArray) -> void: var sound: AudioStream = AudioStreamMP3.new() sound.data = body audio_player.stream = sound audio_player.play() + sound_player.stop() song_finished = false play_button.texture_normal = pause_icon stream = audio_player.stream progress_slider.max_value = round(stream.get_length()) - progress_slider.tick_count = round(stream.get_length() / 60) + progress_slider.tick_count = round(stream.get_length() / 60) -func song_fetched(body: PackedByteArray) -> void: +func play_song_object(song_object_no: int) -> void: + print("play_song_object") if audio_player.is_playing(): audio_player.stop() - await get_tree().create_timer(1.0).timeout - print("song_fetched") - var sound: AudioStream = AudioStreamMP3.new() - sound.data = body - print("play given song") - audio_player.stream = sound + await get_tree().create_timer(0.5).timeout + audio_player.stream = Settings.song_object_array[song_object_no].song + sound_player.stop() audio_player.play() song_finished = false play_button.texture_normal = pause_icon stream = audio_player.stream progress_slider.max_value = round(stream.get_length()) progress_slider.tick_count = round(stream.get_length() / 60) - add_to_queue.emit() - if Settings.add_to_stats: - print("add to stats") - Settings.make_request3("/music/addPlayed") + Settings.currently_playing_song = song_object_no + Settings.unset_is_playing() if !Settings.hide_next_track: - show_answer.emit() - -func fetch_next_song() -> void: - #if audio_player.is_playing(): - # audio_player.stop() - # await get_tree().create_timer(1.0).timeout - print("fetch_next_song") - var url: String = "" - if Settings.use_low_played_mode: - url = "/music/rand/low" - else: - url = "/music/rand" - Settings.make_request2(url, song_fetched, true) + print("Show answer now!!") + Settings.song_object_array[song_object_no].is_answered = true + Settings.song_object_array[song_object_no].has_played = true + Settings.song_object_array[song_object_no].is_playing = true + update_song_list.emit() func get_sound_test_song() -> void: Settings.make_request2("/music/soundTest", play_song, true) diff --git a/MusicPlayer.tscn b/MusicPlayer.tscn index 1e389d8..938dd47 100644 --- a/MusicPlayer.tscn +++ b/MusicPlayer.tscn @@ -45,6 +45,14 @@ script = ExtResource("1_t24ra") stream = ExtResource("2_xti80") bus = &"music" +[node name="AudioSoundPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("2_xti80") +bus = &"music" + +[node name="AudioSoundEffectPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("2_xti80") +bus = &"music" + [node name="MusicPlayerContainer" type="HBoxContainer" parent="."] layout_mode = 2 size_flags_vertical = 4 diff --git a/Player.gd b/Player.gd index f61a729..2d20b37 100644 --- a/Player.gd +++ b/Player.gd @@ -5,10 +5,10 @@ extends Control var player_name_field := $HBoxContainer/Name @onready -var add := $HBoxContainer/AddPoint +var add_button := $HBoxContainer/AddPoint @onready -var minus := $HBoxContainer/RemovePoint +var minus_button := $HBoxContainer/RemovePoint @onready var character := $HBoxContainer/Character @@ -17,119 +17,55 @@ var character := $HBoxContainer/Character var remove_player := $HBoxContainer/RemovePlayer signal change_character_clicked -signal first_point_triggerd -signal match_point_triggerd -signal winner_triggerd signal player_removed -@export -var player_name: String - -@export -var player_score: String - @export var id: int -var is_first_point: bool = true - # Called when the node enters the scene tree for the first time. func _ready() -> void: - add.pressed.connect(add_point) - minus.pressed.connect(minus_point) + add_button.pressed.connect(add_point) + add_button.set_shortcut(create_button_shortcut(id, false)) + minus_button.pressed.connect(minus_point) + minus_button.set_shortcut(create_button_shortcut(id, true)) character.pressed.connect(change_character) - remove_player.pressed.connect(func(): player_removed.emit()) - player_name_field.text = player_score + remove_player.pressed.connect(func() -> void: player_removed.emit()) + update_score() + set_player_character() -func _input(event: InputEvent) -> void: - if event is InputEventKey and event.pressed: - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_1: - check_player(1, false) - elif event.alt_pressed && event.keycode == KEY_1: - check_player(1, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_2: - check_player(2, false) - elif event.alt_pressed && event.keycode == KEY_2: - check_player(2, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_3: - check_player(3, false) - elif event.alt_pressed && event.keycode == KEY_3: - check_player(3, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_4: - check_player(4, false) - elif event.alt_pressed && event.keycode == KEY_4: - check_player(4, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_5: - check_player(5, false) - elif event.alt_pressed && event.keycode == KEY_5: - check_player(5, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_6: - check_player(6, false) - elif event.alt_pressed && event.keycode == KEY_6: - check_player(6, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_7: - check_player(7, false) - elif event.alt_pressed && event.keycode == KEY_7: - check_player(7, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_8: - check_player(8, false) - elif event.alt_pressed && event.keycode == KEY_8: - check_player(8, true) - if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_9: - check_player(9, false) - elif event.alt_pressed && event.keycode == KEY_9: - check_player(9, true) +func _process(delta: float) -> void: + remove_player.visible = Settings.edit_players -func check_player(pressed: int, add: bool): - if (id == pressed): - print("Player " + str(id) + " pressed") - if add: - add_point() - else: - minus_point() +func create_button_shortcut(scancode : int, ctrl_pressed: bool) -> Shortcut: + var button_shortcut: Shortcut = Shortcut.new() + var button_event : InputEventKey = InputEventKey.new() + button_event.keycode = scancode + 49 + button_event.alt_pressed = true + button_event.ctrl_pressed = ctrl_pressed + button_shortcut.events.append(button_event) + return button_shortcut -func add_point(): - if is_first_point: - is_first_point = false - first_point_triggerd.emit() +func add_point() -> void: + Settings.player_array[id].add_point() + update_score() - var new_value := get_point() + 1 - set_point(new_value) - - if new_value == Settings.winning_score - 1: - match_point_triggerd.emit() - - if new_value == Settings.winning_score: - winner_triggerd.emit() - -func minus_point(): - var new_value := get_point() - 1 - set_point(new_value) - - if new_value == 0: - is_first_point = true - -func reset_points(): - var new_value := 0 - set_point(new_value) - is_first_point = true - -func change_character(): +func minus_point() -> void: + Settings.player_array[id].minus_point() + update_score() + +func change_character() -> void: change_character_clicked.emit() -func _on_control_character_selected_clicked(file_name: String): +func _on_control_character_selected_clicked(file_name: String) -> void: print("Back in player list with: " + file_name) - - var texture = load("res://characters/" + file_name) + Settings.player_array[id].character = load("res://characters/" + file_name) + set_player_character() + +func set_player_character() -> void: character.custom_minimum_size = Vector2(80, 40) character.ignore_texture_size = true character.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT - character.texture_normal = texture + character.texture_normal = Settings.player_array[id].character -func get_point() -> int: - var arr = player_name_field.text.split(":") - return arr[1].to_int() - -func set_point(new_point: int): - var arr = player_name_field.text.split(":") - player_name_field.text = arr[0] + ": " + str(new_point) +func update_score() -> void: + player_name_field.text = str(Settings.player_array[id].id) + " " + Settings.player_array[id].player_name + ": " + str(Settings.player_array[id].player_score) diff --git a/PlayerObject.gd b/PlayerObject.gd index 403ab15..1996a0d 100644 --- a/PlayerObject.gd +++ b/PlayerObject.gd @@ -1,5 +1,12 @@ extends Node +class_name PlayerObject +signal first_point_triggerd +signal match_point_triggerd +signal winner_triggerd +signal point_given_sound +signal player_point_given +signal player_point_taken @export var id: int @@ -8,6 +15,38 @@ var id: int var player_name: String @export -var player_score: String +var player_score: int -var character: Texture +var is_first_point: bool = true + +var character: Texture = load("res://noCharacter.png") + +func _init(new_name: String, new_id: int): + player_name = new_name + id = new_id + player_score = 0 + +func add_point() -> void: + if player_score < Settings.winning_score: + player_score += 1 + player_point_given.emit() + if player_score == 1 && is_first_point: + is_first_point = false + first_point_triggerd.emit() + elif player_score == Settings.winning_score - 1: + match_point_triggerd.emit() + elif player_score == Settings.winning_score: + winner_triggerd.emit() + else: + point_given_sound.emit() + +func minus_point() -> void: + if player_score > 0: + player_score -= 1 + player_point_taken.emit() + if player_score == 0: + is_first_point = true + +func reset_points() -> void: + player_score = 0 + is_first_point = true diff --git a/Settings.gd b/Settings.gd index 1f79b85..d84d344 100644 --- a/Settings.gd +++ b/Settings.gd @@ -17,11 +17,16 @@ var fullscreen: bool = false var quick_sync: bool = true var play_local: bool = false -var number_of_players: int = 0 +var player_array: Array[PlayerObject] +var song_object_array: Array[SongObject] = [] +var latest_played_song: int = 0 +var currently_playing_song: int = -1 +var edit_players: bool = false -var version: String = "0.9.0-Beta" +var version: String = "1.5.0" var whats_new: String = "Changelog: +1.5.0: Made big changes to players and the song list and how the local song list works 0.9.0-Beta: Fixed settings and updated the player view 0.8.0-Beta: Fixed reset buttons and some other small things 0.7.8-Beta: Added shortcuts. Added dialog for winner. Started cleaning code. @@ -41,26 +46,20 @@ Alt + Z = Reset Alt + X = Play/Pause Alt + C = Next Song Alt + V = Show Answer -Alt + Enter = Fullscreen" - -#play = X -#nästa = c -#visa svar = v -#lägga till poäng? 1, 2, 3, 4, 5, 6 - -func get_next_player_id() -> int: - number_of_players += 1 - return number_of_players - -func player_removed() -> void: - number_of_players -= 1 +Alt + Enter = Fullscreen +Alt + UP = Volume up +Alt + DOWN = Volume down +Alt + LEFT = Jump back in song +Alt + Ctrl = Restart song +Alt + RIGHT = Jump forward in song +Alt + 1, 2, 3, 4, 5, 6 = Give player point +Alt + Ctrl + 1, 2, 3, 4, 5, 6 = Take point from player" func make_request2(address: String, func_name: Callable, expect_data: bool) -> void: - print("func_name: ", func_name.get_method()) - print("get_object: ", func_name.get_object()) var error_handling: Callable = func(_result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void: print("request done to address: ", default_path + address) if response_code == 200: + print("func_name: ", func_name.get_method()) if !expect_data: func_name.call() else: @@ -68,14 +67,14 @@ func make_request2(address: String, func_name: Callable, expect_data: bool) -> v var error: int = json.parse(body.get_string_from_utf8()) if error == OK: var data_received = json.get_data() - #print("data_received: ", data_received) - print("data_received type: ", typeof(data_received)) + print("data_received type: ", type_string(typeof(data_received))) if typeof(data_received) == TYPE_ARRAY: func_name.call(data_received) elif func_name != null: func_name.call(data_received) else: print("song_received") + print("data_received type: ", type_string(typeof(body))) func_name.call(body) var http_request: HTTPRequest = HTTPRequest.new() @@ -90,6 +89,7 @@ func make_request2(address: String, func_name: Callable, expect_data: bool) -> v func make_request3(address: String) -> void: var error_handling: Callable = func(_result: int, response_code: int, _headers: PackedStringArray, _body: PackedByteArray) -> void: + print("request done to address: ", default_path + address) if response_code != 200: print("Error: " + str(response_code)) @@ -105,3 +105,19 @@ func delete_children(node: Node) -> void: for n: Node in node.get_children(): node.remove_child(n) n.queue_free() + +func delete_player_children(node: Node) -> void: + for n: Node in node.get_children(): + print(n) + print(n.name) + if n.name == "HBoxContainer": + pass + elif n.name == "AddPlayerContainer": + pass + else: + node.remove_child(n) + n.queue_free() + +func unset_is_playing() -> void: + for song_object: SongObject in song_object_array: + song_object.is_playing = false diff --git a/SettingsWindow.gd b/SettingsWindow.gd index ceda87e..145122c 100644 --- a/SettingsWindow.gd +++ b/SettingsWindow.gd @@ -50,6 +50,9 @@ func _ready(): hide_next_track_button.button_pressed = Settings.hide_next_track add_to_database_button.button_pressed = Settings.add_to_stats low_played_button.button_pressed = Settings.use_low_played_mode + score_label.text = str(Settings.winning_score) + fullscreen_button.button_pressed = Settings.fullscreen + quick_sync_button.button_pressed = Settings.quick_sync select_server_button.select(Settings.selected_server) select_server_button.item_selected.connect(select_server) diff --git a/SongObject.gd b/SongObject.gd index 4d5d480..a4a756c 100644 --- a/SongObject.gd +++ b/SongObject.gd @@ -1,12 +1,14 @@ extends Node +class_name SongObject + var song_number: int var song_title: String var game_title: String -var players_given_point: Array[String] +var players_given_point: Array[int] = [] var is_answered: bool = false @@ -15,3 +17,38 @@ var has_played: bool = false var is_playing: bool = false var song: AudioStream + +func get_game_title() -> String: + if is_answered: + return game_title + else: + return "????????" + +func get_song_title() -> String: + if is_answered: + return song_title + else: + return "??????" + +func get_song_info() -> String: + if is_answered: + var format_string: String = "%d. %s - %s" + return format_string % [(song_number + 1), game_title, song_title] + else: + return "??? - ???" + +func add_point(id: int) -> void: + print("add_point") + if !players_given_point.has(id): + players_given_point.append(id) + players_given_point.sort() + +func remove_point(id: int) -> void: + print("remove_point") + if players_given_point.has(id): + players_given_point.remove_at(players_given_point.find(id)) + players_given_point.sort() + +func _to_string() -> String: + var format_string: String = "song_number: %d | game_title: %s | song_title: %s | is_answered: %s | has_played: %s | is_playing: %s" + return format_string % [(song_number), game_title, song_title, is_answered, has_played, is_playing] diff --git a/default_theme.tres b/default_theme.tres new file mode 100644 index 0000000..8f931a5 --- /dev/null +++ b/default_theme.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://8bllart6xn1f"] + +[ext_resource type="StyleBox" uid="uid://bsv6yb0yir04l" path="res://style_box_flat.tres" id="1_uierc"] + +[resource] +PanelContainer/styles/panel = ExtResource("1_uierc") diff --git a/export_presets.cfg b/export_presets.cfg index 7d8516c..00eacf2 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -9,7 +9,7 @@ custom_features="" export_filter="all_resources" include_filter="" exclude_filter="" -export_path="../../ResilioSync/Sorterat/MusicPlayer_1.3.0.exe" +export_path="../../ResilioSync/Sorterat/MusicPlayer_1.5.0.exe" encryption_include_filters="" encryption_exclude_filters="" encrypt_pck=false diff --git a/song_list_object.gd b/song_list_object.gd new file mode 100644 index 0000000..1a6690d --- /dev/null +++ b/song_list_object.gd @@ -0,0 +1,50 @@ +class_name SongListObject +extends PanelContainer + +@onready +var song_list_label: Label = $HBoxContainer/SongListLabel + +@onready +var song_list_players: HBoxContainer = $HBoxContainer/SongListPlayers + +var song_object: SongObject + +#Color(0.18, 0.18, 0.18) +#Color(0.302, 0.302, 0.302) +#Color(0.259, 0.259, 0.259) + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + #if song_object != null && song_object.song_number % 2 == 0: + # var new_stylebox_normal = self.get_theme_stylebox("panel").duplicate() + # new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18) + # self.add_theme_stylebox_override("panel", new_stylebox_normal) + # song_list_label.add_theme_stylebox_override("normal", new_stylebox_normal) + #else: + # var new_stylebox_normal = self.get_theme_stylebox("panel").duplicate() + # new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18) + # self.add_theme_stylebox_override("panel", new_stylebox_normal) + # song_list_label.add_theme_stylebox_override("normal", new_stylebox_normal) + set_label() + update_players() + +func set_label() -> void: + if song_object != null: + var size_to_change: int = song_object.players_given_point.size() * 50 + song_list_label.custom_minimum_size = Vector2(1000 - size_to_change, 28) + if song_object.is_playing: + song_list_label.add_theme_color_override("font_color", Color(1, 0.5, 0)) + song_list_label.text = song_object.get_song_info() + +func update_players() -> void: + Settings.delete_children(song_list_players) + if song_object != null: + for player_id: int in song_object.players_given_point: + for player: PlayerObject in Settings.player_array: + if player.id == player_id: + var texture: TextureRect = TextureRect.new() + texture.texture = player.character + texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + texture.custom_minimum_size = Vector2(50, 25) + song_list_players.add_child(texture) diff --git a/song_list_object.tscn b/song_list_object.tscn new file mode 100644 index 0000000..074970f --- /dev/null +++ b/song_list_object.tscn @@ -0,0 +1,65 @@ +[gd_scene load_steps=5 format=3 uid="uid://1jbtmhbqolqe"] + +[ext_resource type="Texture2D" uid="uid://c8r7ykphss4c2" path="res://characters/Link.png" id="1_ll28s"] +[ext_resource type="Script" path="res://song_list_object.gd" id="1_lspki"] +[ext_resource type="Theme" uid="uid://8bllart6xn1f" path="res://default_theme.tres" id="1_x7fq8"] +[ext_resource type="StyleBox" uid="uid://bsv6yb0yir04l" path="res://style_box_flat.tres" id="3_5h84f"] + +[node name="SongListObject" type="PanelContainer"] +custom_minimum_size = Vector2(1000, 0) +offset_right = 1088.0 +offset_bottom = 59.0 +theme = ExtResource("1_x7fq8") +script = ExtResource("1_lspki") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 0 + +[node name="SongListLabel" type="Label" parent="HBoxContainer"] +texture_filter = 2 +custom_minimum_size = Vector2(865, 28) +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 1 +theme = ExtResource("1_x7fq8") +theme_override_font_sizes/font_size = 20 +theme_override_styles/normal = ExtResource("3_5h84f") +text = "Final Fantasy Dissidia 012 Duodecim (Sista Fantasin Meningsskiljaktigheter 012 Tolv) - 'Crystal Cave -arrange-'' from FINAL FANTASY III" +autowrap_mode = 2 + +[node name="Panel" type="Panel" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 0 + +[node name="SongListPlayers" type="HBoxContainer" parent="HBoxContainer"] +layout_mode = 2 +size_flags_vertical = 0 + +[node name="TextureRect" type="TextureRect" parent="HBoxContainer/SongListPlayers"] +custom_minimum_size = Vector2(50, 25) +layout_mode = 2 +texture = ExtResource("1_ll28s") +expand_mode = 1 +stretch_mode = 5 + +[node name="TextureRect2" type="TextureRect" parent="HBoxContainer/SongListPlayers"] +custom_minimum_size = Vector2(50, 25) +layout_mode = 2 +texture = ExtResource("1_ll28s") +expand_mode = 1 +stretch_mode = 6 + +[node name="TextureRect3" type="TextureRect" parent="HBoxContainer/SongListPlayers"] +custom_minimum_size = Vector2(50, 25) +layout_mode = 2 +texture = ExtResource("1_ll28s") +expand_mode = 1 + +[node name="TextureRect4" type="TextureRect" parent="HBoxContainer/SongListPlayers"] +custom_minimum_size = Vector2(50, 25) +layout_mode = 2 +texture = ExtResource("1_ll28s") +expand_mode = 1 +stretch_mode = 6 diff --git a/sounds/itemequip.wav b/sounds/itemequip.wav new file mode 100644 index 0000000..c404be2 Binary files /dev/null and b/sounds/itemequip.wav differ diff --git a/sounds/itemequip.wav.import b/sounds/itemequip.wav.import new file mode 100644 index 0000000..60332c4 --- /dev/null +++ b/sounds/itemequip.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bxec8tc8fbni" +path="res://.godot/imported/itemequip.wav-7374990029ccea3349119e7625a59c28.sample" + +[deps] + +source_file="res://sounds/itemequip.wav" +dest_files=["res://.godot/imported/itemequip.wav-7374990029ccea3349119e7625a59c28.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 diff --git a/sounds/itemopen.wav b/sounds/itemopen.wav new file mode 100644 index 0000000..ef69fb7 Binary files /dev/null and b/sounds/itemopen.wav differ diff --git a/sounds/itemopen.wav.import b/sounds/itemopen.wav.import new file mode 100644 index 0000000..9fa3bf8 --- /dev/null +++ b/sounds/itemopen.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://csg6u2h41dm4t" +path="res://.godot/imported/itemopen.wav-f6c0fafd05bb02980988f5de7142987f.sample" + +[deps] + +source_file="res://sounds/itemopen.wav" +dest_files=["res://.godot/imported/itemopen.wav-f6c0fafd05bb02980988f5de7142987f.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 diff --git a/style_box_flat.tres b/style_box_flat.tres new file mode 100644 index 0000000..7aecf29 --- /dev/null +++ b/style_box_flat.tres @@ -0,0 +1,4 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://bsv6yb0yir04l"] + +[resource] +bg_color = Color(0.180392, 0.180392, 0.180392, 1)