diff --git a/scenes/complete_timer.tscn b/scenes/complete_timer.tscn new file mode 100644 index 0000000..bb4838b --- /dev/null +++ b/scenes/complete_timer.tscn @@ -0,0 +1,6 @@ +[gd_scene format=3 uid="uid://d3xiiqurxhmfc"] + +[node name="CompleteTimer" type="Timer"] +wait_time = 3.0 +one_shot = true +autostart = true diff --git a/scenes/game.gd b/scenes/game.gd index ffc60db..3a60186 100644 --- a/scenes/game.gd +++ b/scenes/game.gd @@ -48,6 +48,7 @@ func _on_multiplayer_peer_connected(peer_id: int) -> void: player_dir.rpc_possess_playernode.rpc_id(peer_id, playernode.player_name, playernode.get_multiplayer_authority()) playernode.rpc_query_name.rpc_id(playernode.get_multiplayer_authority()) playernode.rpc_query_color.rpc_id(playernode.get_multiplayer_authority()) + playernode.rpc_query_scores.rpc_id(playernode.get_multiplayer_authority()) peer_dir.rpc_create_peernode.rpc(peer_id) phase_tracker.rpc_set_phase.rpc_id(peer_id, phase_tracker.phase) @@ -85,6 +86,12 @@ func _on_playerdir_playernode_name_changed(old: String, new: String, playernode: func _on_playerdir_playernode_color_changed(old: Color, new: Color, playernode: PlayerNode) -> void: Log.peer(self, "Playernode `%s` changed color: %s (was %s)" % [playernode.player_name, new, old]) +func _on_playerdir_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void: + Log.peer(self, "Playernode `%s` reported score: %d" % [playernode.player_name, strokes]) + +func _on_playerdir_playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode) -> void: + Log.peer(self, "Playernode `%s` changed scores: %s (was %s)" % [playernode.player_name, new, old]) + func _on_playerdir_playernode_possessed(old: int, new: int, playernode: PlayerNode) -> void: Log.peer(self, "Playernode `%s` possessed: %d (was %d)" % [playernode.player_name, new, old]) if playernode.is_multiplayer_authority() and not multiplayer.is_server(): @@ -95,4 +102,8 @@ func _on_playerdir_playernode_possessed(old: int, new: int, playernode: PlayerNo func _on_main_start_confirmed() -> void: if multiplayer.is_server(): phase_tracker.rpc_set_phase.rpc(PhaseTracker.Phase.PLAYING) - level_manager.rpc_next_level.rpc() \ No newline at end of file + level_manager.rpc_next_level.rpc() + +func _on_level_manager_playlist_complete(_playlist: LevelPlaylist) -> void: + if multiplayer.is_server(): + phase_tracker.rpc_set_phase.rpc(PhaseTracker.Phase.ENDED) diff --git a/scenes/game.tscn b/scenes/game.tscn index d74ecbc..6f1c5f7 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -39,3 +39,6 @@ levels = Array[PackedScene]([ExtResource("8_h8dsk"), ExtResource("9_2kliu")]) [connection signal="playernode_color_changed" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_color_changed"] [connection signal="playernode_name_changed" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_name_changed"] [connection signal="playernode_possessed" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_possessed"] +[connection signal="playernode_score_reported" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_score_reported"] +[connection signal="playernode_scores_changed" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_scores_changed"] +[connection signal="playlist_complete" from="LevelManager" to="." method="_on_level_manager_playlist_complete"] diff --git a/scenes/game_hud.gd b/scenes/game_hud.gd new file mode 100644 index 0000000..f0fc185 --- /dev/null +++ b/scenes/game_hud.gd @@ -0,0 +1,50 @@ +extends Control +class_name GameHUD + + +@export var strokes_label: Label + +@export var scores_panel: PanelContainer + +@export var scores_container: Control + + +const score_scene = preload("res://scenes/player_score_label.tscn") + + +func _on_level_completed(_level: GolfLevel) -> void: + strokes_label.text = "0" + for child in scores_container.get_children(): + child.queue_free() + scores_panel.hide() + +func _on_playernode_name_changed(old: String, new: String, playernode: PlayerNode) -> void: + var instance = get_node_or_null("PlayerScoreLabel__%s" % old) + if instance != null: + instance.from_score(playernode) + instance.name = "PlayerScoreLabel__%s" % new + +func _on_playernode_color_changed(old: Color, new: Color, playernode: PlayerNode) -> void: + var instance = get_node_or_null("PlayerScoreLabel__%s" % playernode.player_name) + if instance != null: + instance.from_score(playernode) + +func _on_playernode_possessed(old: int, new: int, playernode: PlayerNode) -> void: + var instance = get_node_or_null("PlayerScoreLabel__%s" % playernode.player_name) + if instance != null: + instance.from_score(playernode) + +func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void: + var score_instance = score_scene.instantiate() + score_instance.from_score(strokes, playernode) + score_instance.name = "PlayerScoreLabel__%s" % playernode.player_name + scores_container.add_child(score_instance) + scores_panel.show() + +func _on_playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode) -> void: + pass + + + +func _on_strokes_changed(strokes: int) -> void: + strokes_label.text = "%s" % strokes diff --git a/scenes/game_hud.tscn b/scenes/game_hud.tscn new file mode 100644 index 0000000..09071e9 --- /dev/null +++ b/scenes/game_hud.tscn @@ -0,0 +1,82 @@ +[gd_scene load_steps=2 format=3 uid="uid://cfma7kdxh8jn2"] + +[ext_resource type="Script" path="res://scenes/game_hud.gd" id="1_x04no"] + +[node name="GameHUD" type="Control" node_paths=PackedStringArray("strokes_label", "scores_panel", "scores_container")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_x04no") +strokes_label = NodePath("GridContainer/TopLeft/StrokesPanel/Margin/Label") +scores_panel = NodePath("GridContainer/TopRight/ScoresPanel") +scores_container = NodePath("GridContainer/TopRight/ScoresPanel/Margin/Scores") + +[node name="GridContainer" type="GridContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +columns = 2 + +[node name="TopLeft" type="Control" parent="GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="StrokesPanel" type="PanelContainer" parent="GridContainer/TopLeft"] +layout_mode = 2 +offset_right = 18.0 +offset_bottom = 31.0 +size_flags_horizontal = 0 + +[node name="Margin" type="MarginContainer" parent="GridContainer/TopLeft/StrokesPanel"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="Label" type="Label" parent="GridContainer/TopLeft/StrokesPanel/Margin"] +layout_mode = 2 +text = "0" + +[node name="TopRight" type="Control" parent="GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ScoresPanel" type="PanelContainer" parent="GridContainer/TopRight"] +visible = false +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -60.0 +offset_bottom = 25.0 +grow_horizontal = 0 +size_flags_horizontal = 8 + +[node name="Margin" type="MarginContainer" parent="GridContainer/TopRight/ScoresPanel"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="Scores" type="VBoxContainer" parent="GridContainer/TopRight/ScoresPanel/Margin"] +layout_mode = 2 + +[node name="BottomLeft" type="Control" parent="GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="BottomRight" type="Control" parent="GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 diff --git a/scenes/golf_ball.gd b/scenes/golf_ball.gd index 22a1dda..ebc13a1 100644 --- a/scenes/golf_ball.gd +++ b/scenes/golf_ball.gd @@ -136,7 +136,6 @@ func rpc_sync_enter_hole(): visible = false hole_sound.play() entered_hole.emit(strokes) - Log.peer(self, "Entered hole in: %d" % strokes) @@ -152,7 +151,7 @@ func _ready() -> void: func _physics_process(delta) -> void: - if is_multiplayer_authority(): + if not multiplayer.is_server() and is_multiplayer_authority(): if not in_hole: do_movement(delta) rpc_sync_global_position.rpc(global_position) diff --git a/scenes/golf_ball_directory.tscn b/scenes/golf_ball_directory.tscn deleted file mode 100644 index 221dc91..0000000 --- a/scenes/golf_ball_directory.tscn +++ /dev/null @@ -1,10 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://cywer226gp6vt"] - -[sub_resource type="GDScript" id="GDScript_ek5xt"] -script/source = "extends Node -class_name GolfBallDirectory - -" - -[node name="GolfBallDirectory" type="Node"] -script = SubResource("GDScript_ek5xt") diff --git a/scenes/golf_level.gd b/scenes/golf_level.gd index 798f70d..948f7f0 100644 --- a/scenes/golf_level.gd +++ b/scenes/golf_level.gd @@ -6,6 +6,7 @@ class_name GolfLevel signal level_completed + @export_category("Level Data") ## Whether the [field camera] follows the active player or not. @@ -27,6 +28,9 @@ signal level_completed ## The [TileMap] of this level. @export var map: TileMap = null +## If active, the [Timer] between emissions of [signal everyone_entered_hole] and [signal level_completed]. +var complete_timer: Timer = null + ## If on server, this variable indicates the [GolfLevel] to replicate. ## @@ -54,6 +58,9 @@ const hole_scene: PackedScene = preload("res://scenes/golf_hole.tscn") ## The [PackedScene] to initialize as [Camera2D]. const camera_scene: PackedScene = preload("res://scenes/follow_camera.tscn") +## The [PackedScene] to initialize as [Timer] between emissions of [signal everyone_entered_hole] and [signal level_completed]. +const complete_timer_scene: PackedScene = preload("res://scenes/complete_timer.tscn") + ## Replicate the [field target] from the server to the clients via RPC. func build() -> void: @@ -193,7 +200,14 @@ func _on_playernode_created(node: Node) -> void: rpc_build_ball.rpc(playernode.player_name) -func _on_everyone_entered_hole() -> void: +func _on_everyone_entered_hole(): + complete_timer = complete_timer_scene.instantiate() + complete_timer.timeout.connect(_on_complete_timer_timeout) + add_child(complete_timer) + + +func _on_complete_timer_timeout(): + complete_timer.queue_free() level_completed.emit() diff --git a/scenes/golf_level.tscn b/scenes/golf_level.tscn index e5f5227..6d18ab9 100644 --- a/scenes/golf_level.tscn +++ b/scenes/golf_level.tscn @@ -4,6 +4,5 @@ [node name="GolfLevel" type="Node2D"] script = ExtResource("1_evup0") -camera_follows_player = null [connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"] diff --git a/scenes/golf_level_1.tscn b/scenes/golf_level_1.tscn index d858bfd..4e9022d 100644 --- a/scenes/golf_level_1.tscn +++ b/scenes/golf_level_1.tscn @@ -8,7 +8,6 @@ [node name="GolfLevel" type="Node2D" node_paths=PackedStringArray("camera", "tee", "hole", "map")] script = ExtResource("1_qljh7") -camera_follows_player = null camera = NodePath("FollowCamera") tee = NodePath("GolfTee") hole = NodePath("GolfHole") diff --git a/scenes/golf_tee.gd b/scenes/golf_tee.gd index 0db80b6..d405186 100644 --- a/scenes/golf_tee.gd +++ b/scenes/golf_tee.gd @@ -1,7 +1,8 @@ extends Node2D class_name GolfTee -## Emitted when all connected [GolfBall]s have their [field GolfBall.in_hole] parameter set to true. + +## Emitted when all connected [GolfBall]s have entered the hole. signal everyone_entered_hole @@ -19,7 +20,7 @@ func spawn(playernode: PlayerNode) -> GolfBall: obj.player_name = playernode.player_name obj.player_color = playernode.player_color obj.set_multiplayer_authority(playernode.get_multiplayer_authority()) - obj.putt_controller.can_putt = is_multiplayer_authority() + obj.putt_controller.can_putt = not multiplayer.is_server() and playernode.is_multiplayer_authority() # Create callables to be able to cleanup signals on destruction var on_name_changed: Callable = _on_name_changed.bind(obj) var on_color_changed: Callable = _on_color_changed.bind(obj) @@ -30,12 +31,13 @@ func spawn(playernode: PlayerNode) -> GolfBall: playernode.color_changed.connect(on_color_changed) playernode.possessed.connect(on_possessed) obj.tree_exiting.connect(on_cleanup) - obj.entered_hole.connect(_on_entered_hole) + obj.entered_hole.connect(_on_entered_hole.bind(playernode)) # Add the golf ball as a child of the tee add_child(obj) # Return the created [GolfBall] return obj + func is_everyone_in_hole() -> bool: for child in get_children(): var ball: GolfBall = child as GolfBall @@ -65,6 +67,8 @@ func _on_cleanup(playernode: PlayerNode, on_name_changed: Callable, on_color_cha playernode.color_changed.disconnect(on_color_changed) playernode.possessed.disconnect(on_possessed) -func _on_entered_hole(_strokes: int) -> void: +func _on_entered_hole(strokes: int, playernode: PlayerNode) -> void: + if playernode.is_multiplayer_authority(): + playernode.report_score(strokes) if is_everyone_in_hole(): everyone_entered_hole.emit() diff --git a/scenes/level_manager.gd b/scenes/level_manager.gd index ac51a79..ccbc9af 100644 --- a/scenes/level_manager.gd +++ b/scenes/level_manager.gd @@ -1,8 +1,9 @@ extends Node class_name LevelManager -## The blank [GolfLevel] to initialize on clients. -const base_scene: PackedScene = preload("res://scenes/golf_level.tscn") + + +@export_category("References") ## The [LevelPlaylist] to use to load [GolfLevel]s from. @export var playlist: LevelPlaylist = null @@ -11,6 +12,25 @@ const base_scene: PackedScene = preload("res://scenes/golf_level.tscn") @export var player_dir: PlayerNodeDirectory = null +## Emitted when the current level is about to be destroyed. +signal level_destroying(level: GolfLevel) + +## Emitted when the next level has been determined by calling [LevelPlaylist.advance]. +signal level_determined(scene: PackedScene) + +## Emitted when the new level has been added to the tree, but not built. +signal level_created(level: GolfLevel) + +## Emitted when a level has been completed. +signal level_completed(level: GolfLevel) + +## Emitted when all levels in the [field playlist] have been exausted. +signal playlist_complete(playlist: LevelPlaylist) + + +## The blank [GolfLevel] to initialize on clients. +const base_scene: PackedScene = preload("res://scenes/golf_level.tscn") + ## The currently instantiated [GolfLevel]. var level: GolfLevel = null @@ -18,25 +38,47 @@ var level: GolfLevel = null ## Create the empty [GolfLevel] object everywhere. @rpc("authority", "call_local", "reliable") func rpc_next_level(): + Log.peer(self, "Advancinng to the next level!") # Destroy the current level if level != null: + Log.peer(self, "Destroying the current level: %s" % level) + level_destroying.emit(level) level.queue_free() level = null + # Determine the next level + Log.peer(self, "Determining the next level...") + var next = playlist.advance() + Log.peer(self, "Determined the next level: %s" % next) + level_determined.emit(next) + ## Make sure the playlist hasn't been exausted + if next == null: + Log.peer(self, "Playlist is complete, not doing anything.") + playlist_complete.emit(playlist) + return # Create the new level + Log.peer(self, "Instantiating empty level template...") level = base_scene.instantiate() + Log.peer(self, "Instantiated empty level template: %s" % level) + # Configure the new level + Log.peer(self, "Configuring level variables...") level.player_dir = player_dir level.level_completed.connect(_on_level_completed) - # Determine the next level if multiplayer.is_server(): - var target = playlist.advance().instantiate() - level.target = target + Log.peer(self, "Instantiating the target level scene...") + level.target = next.instantiate() + Log.peer(self, "Instantiated the target level scene: %s" % level.target) # Add the level to the tree + Log.peer(self, "Adding level to the tree...") add_child(level, true) + level_created.emit(level) # Start the replication if multiplayer.is_server(): + Log.peer(self, "Building level...") level.build() func _on_level_completed() -> void: - if multiplayer.is_server(): + Log.peer(self, "Level completed!") + level_completed.emit(level) + if is_multiplayer_authority(): rpc_next_level.rpc() diff --git a/scenes/level_playlist.gd b/scenes/level_playlist.gd index 707cf52..cc22b8b 100644 --- a/scenes/level_playlist.gd +++ b/scenes/level_playlist.gd @@ -6,6 +6,10 @@ class_name LevelPlaylist @export var levels: Array[PackedScene] = [] +## Emitted when the playlist has advanced to a new level, but before it is returned by [method advanced]. +signal advanced(level: PackedScene) + + ## The index of the current level in [field levels]. var idx: int = -1 @@ -17,4 +21,6 @@ func advance() -> PackedScene: idx += 1 if idx >= len(levels): return null - return levels[idx] + var level = levels[idx] + advanced.emit(level) + return level diff --git a/scenes/main.gd b/scenes/main.gd index 6604654..fd264c9 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -3,7 +3,8 @@ class_name Main @onready var scene_tree: SceneTree = get_tree() -@onready var interface_instance: MarginContainer = $"Interface" + +@export var interface_instance: Interface const main_menu_scene: PackedScene = preload("res://scenes/main_menu.tscn") var main_menu_instance: MainMenu = null @@ -15,6 +16,9 @@ var client_game_instance: Game = null const lobby_menu_scene: PackedScene = preload("res://scenes/lobby_menu.tscn") var lobby_menu_instance: LobbyMenu = null +const game_hud_scene: PackedScene = preload("res://scenes/game_hud.tscn") +var game_hud_instance: GameHUD = null + func init_main_menu() -> void: main_menu_instance = main_menu_scene.instantiate() main_menu_instance.hosting_confirmed.connect(_on_hosting_confirmed) @@ -23,6 +27,7 @@ func init_main_menu() -> void: func deinit_main_menu() -> void: main_menu_instance.queue_free() + main_menu_instance = null func init_server_game(server_port: int) -> void: server_game_instance = game_scene.instantiate() @@ -45,6 +50,7 @@ func deinit_server_game() -> void: server_game_instance.multiplayer.multiplayer_peer = null scene_tree.set_multiplayer(multiplayer, ^"/root/Main/Server") server_game_instance.queue_free() + server_game_instance = null func init_client_game(player_name: String, player_color: Color, server_address: String, server_port: int) -> void: client_game_instance = game_scene.instantiate() @@ -69,6 +75,7 @@ func deinit_client_game() -> void: client_game_instance.multiplayer.multiplayer_peer = null scene_tree.set_multiplayer(multiplayer, ^"/root/Main/Client") client_game_instance.queue_free() + client_game_instance = null func init_lobby_menu() -> void: lobby_menu_instance = lobby_menu_scene.instantiate() @@ -98,6 +105,27 @@ func deinit_lobby_menu() -> void: client_game_instance.player_dir.playernode_possessed.disconnect(lobby_menu_instance.players_list._on_playernode_possessed) client_game_instance.phase_tracker.phase_changed.disconnect(_on_phase_changed) lobby_menu_instance.queue_free() + lobby_menu_instance = null + +func init_game_hud() -> void: + game_hud_instance = game_hud_scene.instantiate() + client_game_instance.level_manager.level_completed.connect(game_hud_instance._on_level_completed) + client_game_instance.player_dir.playernode_name_changed.connect(game_hud_instance._on_playernode_name_changed) + client_game_instance.player_dir.playernode_color_changed.connect(game_hud_instance._on_playernode_color_changed) + client_game_instance.player_dir.playernode_possessed.connect(game_hud_instance._on_playernode_possessed) + client_game_instance.player_dir.playernode_score_reported.connect(game_hud_instance._on_playernode_score_reported) + client_game_instance.player_dir.playernode_scores_changed.connect(game_hud_instance._on_playernode_scores_changed) + interface_instance.add_child(game_hud_instance) + +func deinit_game_hud() -> void: + client_game_instance.level_manager.level_completed.disconnect(game_hud_instance._on_level_completed) + client_game_instance.player_dir.playernode_name_changed.disconnect(game_hud_instance._on_playernode_name_changed) + client_game_instance.player_dir.playernode_color_changed.disconnect(game_hud_instance._on_playernode_color_changed) + client_game_instance.player_dir.playernode_possessed.disconnect(game_hud_instance._on_playernode_possessed) + client_game_instance.player_dir.playernode_score_reported.disconnect(game_hud_instance._on_playernode_score_reported) + client_game_instance.player_dir.playernode_scores_changed.disconnect(game_hud_instance._on_playernode_scores_changed) + game_hud_instance.queue_free() + game_hud_instance = null func _ready() -> void: init_main_menu() @@ -128,7 +156,7 @@ func _on_phase_changed(old: PhaseTracker.Phase, new: PhaseTracker.Phase) -> void PhaseTracker.Phase.LOBBY: deinit_lobby_menu() PhaseTracker.Phase.PLAYING: - pass # TODO + deinit_game_hud() PhaseTracker.Phase.ENDED: pass # TODO # Then, initialize the new one @@ -136,6 +164,6 @@ func _on_phase_changed(old: PhaseTracker.Phase, new: PhaseTracker.Phase) -> void PhaseTracker.Phase.LOBBY: init_lobby_menu() PhaseTracker.Phase.PLAYING: - pass # TODO + init_game_hud() PhaseTracker.Phase.ENDED: pass # TODO diff --git a/scenes/main.tscn b/scenes/main.tscn index 629cdfd..e61e548 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -4,8 +4,11 @@ [ext_resource type="PackedScene" uid="uid://dtwi2nyjbvr82" path="res://scenes/interface.tscn" id="1_bd856"] [ext_resource type="Theme" uid="uid://mau3moiintkp" path="res://themes/nanogolf_theme.tres" id="2_oe7am"] -[node name="Main" type="Node"] +[node name="Main" type="Node" node_paths=PackedStringArray("interface_instance")] script = ExtResource("1_a2cl7") +interface_instance = NodePath("CanvasLayer/Interface") -[node name="Interface" parent="." instance=ExtResource("1_bd856")] +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="Interface" parent="CanvasLayer" instance=ExtResource("1_bd856")] theme = ExtResource("2_oe7am") diff --git a/scenes/player_score_label.gd b/scenes/player_score_label.gd new file mode 100644 index 0000000..40e0612 --- /dev/null +++ b/scenes/player_score_label.gd @@ -0,0 +1,8 @@ +extends Label +class_name PlayerScoreLabel + + +func from_score(strokes: int, playernode: PlayerNode) -> void: + text = "%s - %d" % [playernode.player_name, strokes] + modulate = playernode.player_color + modulate.a = 0.3 if playernode.get_multiplayer_authority() == 1 else 1.0 diff --git a/scenes/player_score_label.tscn b/scenes/player_score_label.tscn new file mode 100644 index 0000000..db85201 --- /dev/null +++ b/scenes/player_score_label.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=3 uid="uid://duu2uo362yfnb"] + +[ext_resource type="Script" path="res://scenes/player_score_label.gd" id="1_b8kiv"] + +[node name="PlayerScoreLabel" type="Label"] +size_flags_horizontal = 8 +theme_override_font_sizes/font_size = 12 +text = "Steffo - 0" +horizontal_alignment = 2 +script = ExtResource("1_b8kiv") diff --git a/scenes/playernode.gd b/scenes/playernode.gd index 57cde0f..234beef 100644 --- a/scenes/playernode.gd +++ b/scenes/playernode.gd @@ -13,6 +13,9 @@ var player_name: String ## The color of the player represented by this node. var player_color: Color +## The scores of the player in all holes. +var hole_scores: Array = [] + ## Change the [field player_name] everywhere. @rpc("authority", "call_local", "reliable") func rpc_set_name(value: String): @@ -39,7 +42,6 @@ func possess(value: int) -> void: set_multiplayer_authority(value) possessed.emit(old_value, value) - ## Prompt the multiplayer authority for this node to call [method rpc_set_name] again with the current value. ## ## Used to repeat [field player_name] to new peers. @@ -57,6 +59,39 @@ func rpc_query_color(): rpc_set_color.rpc(player_color) +## Add a new score entry to the [field hole_scores]. +func report_score(strokes: int) -> void: + assert(is_multiplayer_authority()) + rpc_report_score.rpc(strokes) + var tscores = hole_scores.duplicate(true) + tscores.push_back(strokes) + rpc_set_scores.rpc(tscores) + +## Emit [signal score_reported] everywhere. +@rpc("authority", "call_local", "reliable") +func rpc_report_score(strokes: int): + score_reported.emit(strokes) + + +## Change the [field hole_scores] everywhere. +@rpc("authority", "call_local", "reliable") +func rpc_set_scores(value: Array): + var old_value: Array = hole_scores + if old_value != value: # How does this behave? + hole_scores = value + scores_changed.emit(old_value, value) + + +## Prompt the multiplayer authority for this node to call [method rpc_set_scores] again with the current value. +## +## Used to repeat [field hole_scores] to new peers. +@rpc("any_peer", "call_local", "reliable") +func rpc_query_scores(): + if is_multiplayer_authority(): + rpc_set_scores.rpc(hole_scores) + + + ## Emitted when the name changes on the local scene because of [method rpc_set_name]. signal name_changed(old: String, new: String) @@ -65,3 +100,9 @@ signal color_changed(old: Color, new: Color) ## Emitted when the multiplayer authority of this [Node] is changed via [method possess]. signal possessed(old: int, new: int) + +## Emitted when a new hole score has been reported. +signal score_reported(strokes: int) + +## Emitted when the hole scores have changed because of [method rpc_set_scores]. +signal scores_changed(old: Array, new: Array) diff --git a/scenes/playernode_directory.gd b/scenes/playernode_directory.gd index c52c089..ef66c3c 100644 --- a/scenes/playernode_directory.gd +++ b/scenes/playernode_directory.gd @@ -26,6 +26,8 @@ func rpc_possess_playernode(player_name: String, peer_id: int): playernode.name_changed.connect(_on_playernode_name_changed.bind(playernode)) playernode.color_changed.connect(_on_playernode_color_changed.bind(playernode)) playernode.possessed.connect(_on_playernode_possessed.bind(playernode)) + playernode.score_reported.connect(_on_playernode_score_reported.bind(playernode)) + playernode.scores_changed.connect(_on_playernode_scores_changed.bind(playernode)) var sanitized_player_name = PlayerNode.sanitize_player_name(player_name) playernode.player_name = sanitized_player_name playernode.name = sanitized_player_name @@ -35,21 +37,33 @@ func rpc_possess_playernode(player_name: String, peer_id: int): -func _on_playernode_name_changed(old: String, new: String, playernode: PlayerNode): +func _on_playernode_name_changed(old: String, new: String, playernode: PlayerNode) -> void: playernode_name_changed.emit(old, new, playernode) -func _on_playernode_color_changed(old: Color, new: Color, playernode: PlayerNode): +func _on_playernode_color_changed(old: Color, new: Color, playernode: PlayerNode) -> void: playernode_color_changed.emit(old, new, playernode) -func _on_playernode_possessed(old: int, new: int, playernode: PlayerNode): +func _on_playernode_possessed(old: int, new: int, playernode: PlayerNode) -> void: playernode_possessed.emit(old, new, playernode) +func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void: + playernode_score_reported.emit(strokes, playernode) + +func _on_playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode) -> void: + playernode_scores_changed.emit(old, new, playernode) + ## Emitted when the name of one of the children [PlayerNode]s changes on the local scene. signal playernode_name_changed(old: String, new: String, playernode: PlayerNode) -## Emitted when the name of one of the children [PlayerNode]s changes on the local scene. +## Emitted when the color of one of the children [PlayerNode]s changes on the local scene. signal playernode_color_changed(old: Color, new: Color, playernode: PlayerNode) ## Emitted everywhere when one of the children [PlayerNode]s has changed multiplayer authority. signal playernode_possessed(old: int, new: int, playernode: PlayerNode) + +## Emitted when a [PlayerNode] reports a score. +signal playernode_score_reported(strokes: int, playernode: PlayerNode) + +## Emitted when the scores of one of the children [PlayerNode]s change on the local scene. +signal playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode)