1
Fork 0
mirror of https://github.com/Steffo99/nanogolf.git synced 2024-11-24 00:54:19 +00:00

Sync more stuff

This commit is contained in:
Steffo 2024-03-18 06:08:31 +01:00
parent 2a0c74f1d2
commit f2b4788903
Signed by: steffo
GPG key ID: 5ADA3868646C3FC0
12 changed files with 321 additions and 184 deletions

View file

@ -1,12 +1,9 @@
extends Node extends Node
class_name Game class_name Game
## The name to be given to the player controlled by the local peer.
var local_player_name: String
## The color to be given to the player controlled by the local peer.
var local_player_color: Color
@export_category("References")
## The [PeerNodeDirectory] instance child of this node. ## The [PeerNodeDirectory] instance child of this node.
@export var peer_dir: PeerNodeDirectory = null @export var peer_dir: PeerNodeDirectory = null
@ -21,8 +18,21 @@ var local_player_color: Color
@export var level_manager: LevelManager = null @export var level_manager: LevelManager = null
## Emitted when the player has requested to exit from the game. ## Emitted when the player has requested to exit from the game.
signal leave_confirmed signal lost_connection
## Emitted when the golf ball for the local player has been spawned.
##
## Used for [GameHUD] events.
signal local_player_spawned(ball: GolfBall)
## The name to be given to the player controlled by the local peer.
var local_player_name: String
## The color to be given to the player controlled by the local peer.
var local_player_color: Color
## Initialize some signals needed by this node to function properly. ## Initialize some signals needed by this node to function properly.
@ -42,11 +52,11 @@ func _on_multiplayer_disconnected_from_server() -> void:
func _on_multiplayer_connection_failed() -> void: func _on_multiplayer_connection_failed() -> void:
Log.peer(self, "Connection failed...") Log.peer(self, "Connection failed...")
leave_confirmed.emit() lost_connection.emit()
func _on_multiplayer_peer_connected(peer_id: int) -> void: func _on_multiplayer_peer_connected(peer_id: int) -> void:
Log.peer(self, "Peer connected: %d" % peer_id) Log.peer(self, "Peer connected: %d" % peer_id)
if multiplayer.is_server(): if is_multiplayer_authority():
for peernode in peer_dir.get_children(): for peernode in peer_dir.get_children():
peer_dir.rpc_create_peernode.rpc_id(peer_id, peernode.get_multiplayer_authority()) peer_dir.rpc_create_peernode.rpc_id(peer_id, peernode.get_multiplayer_authority())
for playernode in player_dir.get_children(): for playernode in player_dir.get_children():
@ -55,60 +65,44 @@ func _on_multiplayer_peer_connected(peer_id: int) -> void:
playernode.rpc_query_color.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()) playernode.rpc_query_scores.rpc_id(playernode.get_multiplayer_authority())
peer_dir.rpc_create_peernode.rpc(peer_id) peer_dir.rpc_create_peernode.rpc(peer_id)
phase_tracker.rpc_set_phase.rpc_id(peer_id, phase_tracker.phase)
func _on_multiplayer_peer_disconnected(peer_id: int) -> void: func _on_multiplayer_peer_disconnected(peer_id: int) -> void:
Log.peer(self, "Peer disconnected: %d" % peer_id) Log.peer(self, "Peer disconnected: %d" % peer_id)
if multiplayer.is_server(): if is_multiplayer_authority():
for playernode in player_dir.get_children(): for playernode in player_dir.get_children():
if playernode.get_multiplayer_authority() == peer_id: if playernode.get_multiplayer_authority() == peer_id:
player_dir.rpc_possess_playernode.rpc(playernode.player_name, 1) player_dir.rpc_possess_playernode.rpc(playernode.player_name, 1)
peer_dir.rpc_destroy_peernode.rpc(peer_id) peer_dir.rpc_destroy_peernode.rpc(peer_id)
func _on_peerdir_peernode_created(peernode: PeerNode) -> void: func _on_peerdir_local_peernode_created(peernode: PeerNode) -> void:
Log.peer(self, "Peernode created: %d" % peernode.get_multiplayer_authority()) Log.peer(self, "Local peernode created: %s" % peernode)
if peernode.is_multiplayer_authority(): peernode.rpc_identify.rpc(local_player_name)
peernode.rpc_identify.rpc(local_player_name)
func _on_peerdir_peernode_destroyed(peernode: PeerNode) -> void:
Log.peer(self, "Peernode destroyed: %d" % peernode.get_multiplayer_authority())
func _on_peerdir_peernode_identified(player_name: String, peernode: PeerNode) -> void: func _on_peerdir_peernode_identified(player_name: String, peernode: PeerNode) -> void:
Log.peer(self, "Peernode identified: %d%s" % [peernode.get_multiplayer_authority(), player_name]) Log.peer(self, "Peernode identified: %s%s" % [peernode, player_name])
player_dir.rpc_possess_playernode.rpc(player_name, peernode.get_multiplayer_authority()) if is_multiplayer_authority():
var peer_id = peernode.get_multiplayer_authority()
phase_tracker.rpc_set_phase.rpc_id(peer_id, phase_tracker.phase)
level_manager.sync_level(peer_id)
player_dir.rpc_possess_playernode.rpc(player_name, peer_id)
func _on_playerdir_playernode_created(playernode: PlayerNode) -> void: func _on_playerdir_playernode_created(playernode: PlayerNode) -> void:
Log.peer(self, "Playernode `%s` created" % playernode.player_name) Log.peer(self, "Playernode created: %s" % playernode)
if is_multiplayer_authority():
func _on_playerdir_playernode_destroyed(playernode: PlayerNode) -> void: level_manager.add_player(playernode)
Log.peer(self, "Playernode `%s` destroyed" % playernode.player_name)
func _on_playerdir_playernode_name_changed(old: String, new: String, playernode: PlayerNode) -> void:
Log.peer(self, "Playernode `%s` changed name: %s (was %s)" % [playernode.player_name, new, old])
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():
playernode.rpc_set_name.rpc(local_player_name)
playernode.rpc_set_color.rpc(local_player_color)
func _on_playerdir_local_playernode_possessed(old: int, new: int, playernode: PlayerNode) -> void:
Log.peer(self, "Local playernode possessed: %d%d" % [old, new])
playernode.rpc_set_name.rpc(local_player_name)
playernode.rpc_set_color.rpc(local_player_color)
func _on_main_start_confirmed() -> void: func _on_main_start_confirmed() -> void:
if multiplayer.is_server(): if is_multiplayer_authority():
phase_tracker.rpc_set_phase.rpc(PhaseTracker.Phase.PLAYING) phase_tracker.rpc_set_phase.rpc(PhaseTracker.Phase.PLAYING)
level_manager.rpc_next_level.rpc() level_manager.next_level()
func _on_level_manager_playlist_complete(_playlist: LevelPlaylist) -> void: func _on_level_manager_playlist_complete(_playlist: LevelPlaylist) -> void:
if multiplayer.is_server(): if is_multiplayer_authority():
phase_tracker.rpc_set_phase.rpc(PhaseTracker.Phase.ENDED) phase_tracker.rpc_set_phase.rpc(PhaseTracker.Phase.ENDED)

View file

@ -24,21 +24,16 @@ level_manager = NodePath("LevelManager")
[node name="PhaseTracker" parent="." instance=ExtResource("4_kab4c")] [node name="PhaseTracker" parent="." instance=ExtResource("4_kab4c")]
[node name="LevelManager" parent="." node_paths=PackedStringArray("playlist", "player_dir") instance=ExtResource("5_tdqk1")] [node name="LevelManager" parent="." node_paths=PackedStringArray("playlist", "player_dir") instance=ExtResource("5_tdqk1")]
playlist = NodePath("LevelPlaylist") playlist = NodePath("../LevelPlaylist")
player_dir = NodePath("../PlayerNodeDirectory") player_dir = NodePath("../PlayerNodeDirectory")
[node name="LevelPlaylist" parent="LevelManager" instance=ExtResource("6_bk2n3")] [node name="LevelPlaylist" parent="." instance=ExtResource("6_bk2n3")]
script = ExtResource("7_akb5s") script = ExtResource("7_akb5s")
levels = Array[PackedScene]([ExtResource("8_h8dsk"), ExtResource("9_2kliu")]) levels = Array[PackedScene]([ExtResource("8_h8dsk"), ExtResource("9_2kliu")])
[connection signal="child_entered_tree" from="PeerNodeDirectory" to="." method="_on_peerdir_peernode_created"] [connection signal="local_peernode_created" from="PeerNodeDirectory" to="." method="_on_peerdir_local_peernode_created"]
[connection signal="child_exiting_tree" from="PeerNodeDirectory" to="." method="_on_peerdir_peernode_destroyed"]
[connection signal="peernode_identified" from="PeerNodeDirectory" to="." method="_on_peerdir_peernode_identified"] [connection signal="peernode_identified" from="PeerNodeDirectory" to="." method="_on_peerdir_peernode_identified"]
[connection signal="child_entered_tree" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_created"] [connection signal="child_entered_tree" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_created"]
[connection signal="child_exiting_tree" from="PlayerNodeDirectory" to="." method="_on_playerdir_playernode_destroyed"] [connection signal="local_playernode_possessed" from="PlayerNodeDirectory" to="." method="_on_playerdir_local_playernode_possessed"]
[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_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"] [connection signal="playlist_complete" from="LevelManager" to="." method="_on_level_manager_playlist_complete"]

View file

@ -19,37 +19,31 @@ func _on_level_completed(_level: GolfLevel) -> void:
scores_panel.hide() scores_panel.hide()
func _on_playernode_name_changed(old: String, new: String, playernode: PlayerNode) -> void: func _on_playernode_name_changed(old: String, new: String, playernode: PlayerNode) -> void:
var instance = get_node_or_null("PlayerScoreLabel__%s" % old) var instance = get_node_or_null(old)
if instance != null: if instance != null:
instance.from_score(playernode) instance.from_score(playernode)
instance.name = "PlayerScoreLabel__%s" % new instance.name = new
func _on_playernode_color_changed(_old: Color, _new: Color, playernode: PlayerNode) -> void: func _on_playernode_color_changed(_old: Color, _new: Color, playernode: PlayerNode) -> void:
var instance = get_node_or_null("PlayerScoreLabel__%s" % playernode.player_name) var instance = get_node_or_null(playernode.player_name)
if instance != null: if instance != null:
instance.from_score(playernode) instance.from_score(playernode)
func _on_playernode_possessed(_old: int, _new: int, playernode: PlayerNode) -> void: func _on_playernode_possessed(_old: int, _new: int, playernode: PlayerNode) -> void:
var instance = get_node_or_null("PlayerScoreLabel__%s" % playernode.player_name) var instance = get_node_or_null(playernode.player_name)
if instance != null: if instance != null:
instance.from_score(playernode) instance.from_score(playernode)
func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void: func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void:
var score_instance = score_scene.instantiate() var score_instance = score_scene.instantiate()
score_instance.from_score(strokes, playernode) score_instance.from_score(strokes, playernode)
score_instance.name = "PlayerScoreLabel__%s" % playernode.player_name score_instance.name = playernode.player_name
scores_container.add_child(score_instance) scores_container.add_child(score_instance)
scores_panel.show() scores_panel.show()
func _on_playernode_scores_changed(_old: Array, _new: Array, _playernode: PlayerNode) -> void: func _on_playernode_scores_changed(_old: Array, _new: Array, _playernode: PlayerNode) -> void:
pass pass
func _on_local_player_spawned(ball: GolfBall, _level: GolfLevel) -> void: func _on_playernode_putt_performed(ball: GolfBall, playernode: PlayerNode) -> void:
ball.putt_controller.putt.connect(_on_putt.bind(ball)) if playernode.is_multiplayer_authority():
strokes_label.text = "%s" % ball.strokes
func _on_putt(_putt_vector: Vector2, ball: GolfBall) -> void:
strokes_label.text = "%d" % ball.strokes
func _on_strokes_changed(strokes: int) -> void:
strokes_label.text = "%s" % strokes

View file

@ -131,20 +131,22 @@ func check_has_entered_hole() -> bool:
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
func rpc_sync_enter_hole(): func rpc_do_enter_hole():
enter_hole()
func enter_hole():
in_hole = true in_hole = true
visible = false hide()
putt_controller.can_putt = false putt_controller.can_putt = false
if not multiplayer.is_server(): if not multiplayer.is_server():
hole_sound.play() hole_sound.play()
entered_hole.emit(strokes) entered_hole.emit(strokes)
# FIXME: What happens on the server?
## Push this object's [field global_position] to all other clients. ## Push this object's [field global_position] to all other clients.
@rpc("authority", "call_remote", "unreliable_ordered") @rpc("authority", "call_remote", "unreliable_ordered")
func rpc_sync_global_position(nposition: Vector2): func rpc_set_global_position(nposition: Vector2):
global_position = nposition global_position = nposition
@ -156,9 +158,9 @@ func _physics_process(delta) -> void:
if not multiplayer.is_server() and is_multiplayer_authority(): if not multiplayer.is_server() and is_multiplayer_authority():
if not in_hole: if not in_hole:
do_movement(delta) do_movement(delta)
rpc_sync_global_position.rpc(global_position) rpc_set_global_position.rpc(global_position)
apply_friction(delta) apply_friction(delta)
if check_has_entered_hole(): if check_has_entered_hole():
rpc_sync_enter_hole.rpc() rpc_do_enter_hole.rpc()
if check_has_stopped(): if check_has_stopped():
putt_controller.can_putt = true putt_controller.can_putt = true

View file

@ -2,12 +2,6 @@ extends Node2D
class_name GolfLevel class_name GolfLevel
## Emitted when it's time to change to the next level.
signal level_completed
## Emitted when the [GolfBall] for the local player has been spawned.
signal local_player_spawned(ball: GolfBall)
@export_category("Level Data") @export_category("Level Data")
@ -30,10 +24,11 @@ signal local_player_spawned(ball: GolfBall)
## The [TileMap] of this level. ## The [TileMap] of this level.
@export var map: TileMap = null @export var map: TileMap = null
## If active, the [Timer] between emissions of [signal everyone_entered_hole] and [signal level_completed]. ## If active, the [Timer] between emissions of [signal everyone_entered_hole] and [signal level_completed].
var complete_timer: Timer = null var complete_timer: Timer = null
## If on server, this variable indicates the [GolfLevel] to replicate. ## If on server, this variable indicates the [GolfLevel] to replicate.
## ##
## The [GolfLevel] in question should not be added to the scene tree, or it will cause problems on the client acting as server. ## The [GolfLevel] in question should not be added to the scene tree, or it will cause problems on the client acting as server.
@ -65,20 +60,24 @@ const complete_timer_scene: PackedScene = preload("res://scenes/complete_timer.t
## Replicate the [field target] from the server to the clients via RPC. ## Replicate the [field target] from the server to the clients via RPC.
func build() -> void: func build(peer_id: int = 0) -> void:
build_tilemap() build_tilemap(peer_id)
build_tilemap_cells() build_tilemap_cells(peer_id)
build_tee() build_tee(peer_id)
build_balls() build_existing_balls(peer_id)
build_hole() build_new_balls(peer_id)
build_camera() build_hole(peer_id)
build_camera(peer_id)
## Replicate the [field map] of the [field target] to the remote [field map]. ## Replicate the [field map] of the [field target] to the remote [field map].
func build_tilemap() -> void: func build_tilemap(peer_id: int = 0) -> void:
Log.peer(self, "Replicating map...") Log.peer(self, "Replicating map...")
var tmap: TileMap = target.map var tmap: TileMap = target.map
rpc_build_tilemap.rpc(tmap.global_position, tmap.global_rotation, tmap.global_scale) if peer_id > 0:
rpc_build_tilemap.rpc_id(peer_id, tmap.global_position, tmap.global_rotation, tmap.global_scale)
else:
rpc_build_tilemap.rpc(tmap.global_position, tmap.global_rotation, tmap.global_scale)
## Create the [field map]. ## Create the [field map].
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
@ -90,12 +89,13 @@ func rpc_build_tilemap(tposition: Vector2, trotation: float, tscale: Vector2):
map.global_scale = tscale map.global_scale = tscale
add_child(map) add_child(map)
## Replicate the cells of [field target]'s [field map] to the remote [field map]. ## Replicate the cells of [field target]'s [field map] to the remote [field map].
## ##
## The [field map] must be already created. ## The [field map] must be already created.
## ##
## Only takes layer 0 into consideration. ## Only takes layer 0 into consideration.
func build_tilemap_cells() -> void: func build_tilemap_cells(peer_id: int = 0) -> void:
Log.peer(self, "Replicating map cells...") Log.peer(self, "Replicating map cells...")
var tmap: TileMap = target.map var tmap: TileMap = target.map
const layer = 0 const layer = 0
@ -103,8 +103,11 @@ func build_tilemap_cells() -> void:
var source_id: int = tmap.get_cell_source_id(0, coords) var source_id: int = tmap.get_cell_source_id(0, coords)
var atlas_coords: Vector2i = tmap.get_cell_atlas_coords(0, coords) var atlas_coords: Vector2i = tmap.get_cell_atlas_coords(0, coords)
var alternative_tile: int = tmap.get_cell_alternative_tile(0, coords) var alternative_tile: int = tmap.get_cell_alternative_tile(0, coords)
rpc_build_tilemap_cell.rpc(layer, coords, source_id, atlas_coords, alternative_tile) if peer_id > 0:
rpc_build_tilemap_cell.rpc_id(peer_id, layer, coords, source_id, atlas_coords, alternative_tile)
else:
rpc_build_tilemap_cell.rpc(layer, coords, source_id, atlas_coords, alternative_tile)
## Create a cell of [field map]. ## Create a cell of [field map].
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
func rpc_build_tilemap_cell( func rpc_build_tilemap_cell(
@ -119,10 +122,13 @@ func rpc_build_tilemap_cell(
## Replicate the [field tee] of the [field target] to the remote [field tee]. ## Replicate the [field tee] of the [field target] to the remote [field tee].
func build_tee() -> void: func build_tee(peer_id: int = 0) -> void:
Log.peer(self, "Replicating tee...") Log.peer(self, "Replicating tee...")
var ttee: GolfTee = target.tee var ttee: GolfTee = target.tee
rpc_build_tee.rpc(ttee.global_position) if peer_id > 0:
rpc_build_tee.rpc_id(peer_id, ttee.global_position)
else:
rpc_build_tee.rpc(ttee.global_position)
## Create the [GolfTee] object. ## Create the [GolfTee] object.
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
@ -135,25 +141,50 @@ func rpc_build_tee(tposition: Vector2):
## Replicate the currently connected players' [GolfBall]s to the remote [field tee]. ## Replicate the currently connected players' [GolfBall]s to the remote [field tee].
func build_balls() -> void: func build_existing_balls(peer_id: int = 0) -> void:
for playernode in player_dir.get_children(): Log.peer(self, "Replicating existing golf balls...")
rpc_build_ball.rpc(playernode.player_name) for tball in tee.get_children():
if peer_id > 0:
rpc_build_ball.rpc_id(peer_id, tball.name, tball.position, tball.strokes, tball.in_hole)
else:
rpc_build_ball.rpc(tball.name, tball.position, tball.strokes, tball.in_hole)
func build_new_balls(peer_id: int = 0) -> void:
Log.peer(self, "Replicating new golf balls...")
for playernode in player_dir.get_children():
if peer_id > 0:
rpc_build_ball.rpc_id(peer_id, playernode.player_name, Vector2.ZERO, 0, false)
else:
rpc_build_ball.rpc(playernode.player_name, Vector2.ZERO, 0, false)
## Create the specified player's [GolfBall] at the remote [field tee].
func build_new_ball(player_name: String, peer_id: int = 0) -> void:
Log.peer(self, "Replicating new golf ball for: %s" % player_name)
if peer_id > 0:
rpc_build_ball.rpc_id(peer_id, player_name, Vector2.ZERO, 0, false)
else:
rpc_build_ball.rpc(player_name, Vector2.ZERO, 0, false)
## Create and initialize a [GolfBall] for a single [PlayerNode] with the given name. ## Create and initialize a [GolfBall] for a single [PlayerNode] with the given name.
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
func rpc_build_ball(player_name: String): func rpc_build_ball(player_name: String, tposition: Vector2, tstrokes: int, thole: bool):
Log.peer(self, "Building tee ball for: %s" % player_name)
var playernode: PlayerNode = player_dir.get_playernode(player_name) var playernode: PlayerNode = player_dir.get_playernode(player_name)
var ball = tee.spawn(playernode) var ball: GolfBall = tee.get_golfball(playernode)
if playernode.is_multiplayer_authority(): if ball == null:
local_player_spawned.emit(ball) Log.peer(self, "Building golf ball for: %s" % player_name)
ball = tee.spawn(playernode, tposition, tstrokes, thole)
else:
Log.peer(self, "Not building duplicate golf ball for: %s" % player_name)
## Replicate the [field hole] of the [field target] to the remote [field hole]. ## Replicate the [field hole] of the [field target] to the remote [field hole].
func build_hole() -> void: func build_hole(peer_id: int = 0) -> void:
Log.peer(self, "Replicating hole...") Log.peer(self, "Replicating hole...")
var thole: GolfHole = target.hole var thole: GolfHole = target.hole
rpc_build_hole.rpc(thole.global_position, thole.global_scale) if peer_id > 0:
rpc_build_hole.rpc_id(peer_id, thole.global_position, thole.global_scale)
else:
rpc_build_hole.rpc(thole.global_position, thole.global_scale)
## Create the [GolfHole] object. ## Create the [GolfHole] object.
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
@ -166,10 +197,13 @@ func rpc_build_hole(tposition: Vector2, tscale: Vector2):
## Replicate the [field camera] of the [field target] to the remote [field camera]. ## Replicate the [field camera] of the [field target] to the remote [field camera].
func build_camera() -> void: func build_camera(peer_id: int = 0) -> void:
Log.peer(self, "Replicating camera...") Log.peer(self, "Replicating camera...")
var tcamera: FollowCamera = target.camera var tcamera: FollowCamera = target.camera
rpc_build_camera.rpc(tcamera.global_position, target.camera_follows_player) if peer_id > 0:
rpc_build_camera.rpc_id(peer_id, tcamera.global_position, target.camera_follows_player)
else:
rpc_build_camera.rpc(tcamera.global_position, target.camera_follows_player)
## Create the [Camera2D] object. ## Create the [Camera2D] object.
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
@ -185,37 +219,40 @@ func rpc_build_camera(tposition: Vector2, tfocus: bool):
for child in player_dir.get_children(): for child in player_dir.get_children():
var playernode = child as PlayerNode var playernode = child as PlayerNode
if playernode.is_multiplayer_authority(): if playernode.is_multiplayer_authority():
var ctarget = tee.get_node("GolfBall__%s" % child.player_name) var ctarget = tee.get_node(child.player_name)
camera.target = ctarget camera.target = ctarget
add_child(camera) add_child(camera)
func _ready() -> void: func _ready() -> void:
player_dir.child_entered_tree.connect(_on_playernode_created) player_dir.local_playernode_possessed.connect(_on_local_playernode_possessed)
if multiplayer.is_server(): if multiplayer.is_server():
set_physics_process(false) set_physics_process(false)
hide() hide()
func _on_playernode_created(node: Node) -> void:
Log.peer(self, "Spawning new player...")
var playernode: PlayerNode = node as PlayerNode
rpc_build_ball.rpc(playernode.player_name)
func _on_everyone_entered_hole(): func _on_everyone_entered_hole():
Log.peer(self, "Everyone entered hole, starting end timer...")
complete_timer = complete_timer_scene.instantiate() complete_timer = complete_timer_scene.instantiate()
complete_timer.timeout.connect(_on_complete_timer_timeout) complete_timer.timeout.connect(_on_complete_timer_timeout)
add_child(complete_timer) add_child(complete_timer)
func _on_complete_timer_timeout(): func _on_complete_timer_timeout():
complete_timer.queue_free() Log.peer(self, "End timer has timed out, emitting level_completed signal...")
level_completed.emit() level_completed.emit()
complete_timer.queue_free()
func _on_local_playernode_possessed(_old: int, new: int, playernode: PlayerNode):
if camera != null:
var ctarget = tee.get_node(playernode.player_name)
camera.target = ctarget
## Emitted when it's time to change to the next level.
signal level_completed
func _on_tree_exiting() -> void: func _on_tree_exiting() -> void:
player_dir.child_entered_tree.disconnect(_on_playernode_created)
if target: if target:
target.queue_free() target.queue_free()
target = null target = null

View file

@ -10,17 +10,25 @@ signal everyone_entered_hole
const ball_scene: PackedScene = preload("res://scenes/golf_ball.tscn") const ball_scene: PackedScene = preload("res://scenes/golf_ball.tscn")
func get_golfball(playernode: PlayerNode) -> GolfBall:
var nname = playernode.player_name
return get_node_or_null(nname)
## Add a new [GolfBall] from [field ball_scene], initialize it with details from the given [PlayerNode], and return it. ## Add a new [GolfBall] from [field ball_scene], initialize it with details from the given [PlayerNode], and return it.
func spawn(playernode: PlayerNode) -> GolfBall: func spawn(playernode: PlayerNode, tposition: Vector2 = Vector2.ZERO, tstrokes: int = 0, thole: bool = false) -> GolfBall:
# Create the [GolfBall] # Create the [GolfBall]
var obj: GolfBall = ball_scene.instantiate() var obj: GolfBall = ball_scene.instantiate()
# Setup the initial values # Setup the initial values
obj.position = Vector2.ZERO obj.position = tposition
obj.name = "GolfBall__%s" % playernode.player_name obj.name = playernode.player_name
obj.strokes = tstrokes
obj.player_name = playernode.player_name obj.player_name = playernode.player_name
obj.player_color = playernode.player_color obj.player_color = playernode.player_color
obj.set_multiplayer_authority(playernode.get_multiplayer_authority()) obj.set_multiplayer_authority(playernode.get_multiplayer_authority())
obj.putt_controller.can_putt = not multiplayer.is_server() and playernode.is_multiplayer_authority() obj.putt_controller.can_putt = not multiplayer.is_server() and playernode.is_multiplayer_authority()
if thole:
obj.enter_hole()
# Create callables to be able to cleanup signals on destruction # Create callables to be able to cleanup signals on destruction
var on_name_changed: Callable = _on_name_changed.bind(obj) var on_name_changed: Callable = _on_name_changed.bind(obj)
var on_color_changed: Callable = _on_color_changed.bind(obj) var on_color_changed: Callable = _on_color_changed.bind(obj)
@ -31,9 +39,10 @@ func spawn(playernode: PlayerNode) -> GolfBall:
playernode.color_changed.connect(on_color_changed) playernode.color_changed.connect(on_color_changed)
playernode.possessed.connect(on_possessed) playernode.possessed.connect(on_possessed)
obj.tree_exiting.connect(on_cleanup) obj.tree_exiting.connect(on_cleanup)
obj.putt_controller.putt.connect(_on_putt_performed.bind(playernode, obj))
obj.entered_hole.connect(_on_entered_hole.bind(playernode)) obj.entered_hole.connect(_on_entered_hole.bind(playernode))
# Add the golf ball as a child of the tee # Add the golf ball as a child of the tee
add_child(obj) add_child(obj, true)
# Return the created [GolfBall] # Return the created [GolfBall]
return obj return obj
@ -51,16 +60,26 @@ func is_everyone_in_hole() -> bool:
return true return true
func _on_name_changed(_old: String, new: String, obj: GolfBall) -> void: func _on_name_changed(old: String, new: String, obj: GolfBall) -> void:
obj.name = "GolfBall__%s" % new Log.peer(self, "PlayerNode changed name, updating it on GolfBall: %s%s" % [old, new])
obj.name = new
obj.player_name = new obj.player_name = new
func _on_color_changed(_old: Color, new: Color, obj: GolfBall) -> void: func _on_color_changed(old: Color, new: Color, obj: GolfBall) -> void:
Log.peer(self, "PlayerNode changed color, updating it on GolfBall: %s%s" % [old, new])
obj.player_color = new obj.player_color = new
func _on_possessed(_old: int, new: int, obj: GolfBall) -> void: func _on_possessed(old: int, new: int, obj: GolfBall) -> void:
Log.peer(self, "PlayerNode changed owner, updating it on GolfBall: %d%d" % [old, new])
obj.set_multiplayer_authority(new) obj.set_multiplayer_authority(new)
obj.putt_controller.can_putt = is_multiplayer_authority() obj.putt_controller.can_putt = is_multiplayer_authority()
if new == 1:
obj.hide()
else:
obj.show()
func _on_putt_performed(_dir: Vector2, playernode: PlayerNode, obj: GolfBall) -> void:
playernode.putt_performed.emit(obj)
func _on_cleanup(playernode: PlayerNode, on_name_changed: Callable, on_color_changed: Callable, on_possessed: Callable) -> void: func _on_cleanup(playernode: PlayerNode, on_name_changed: Callable, on_color_changed: Callable, on_possessed: Callable) -> void:
playernode.name_changed.disconnect(on_name_changed) playernode.name_changed.disconnect(on_name_changed)

View file

@ -12,23 +12,10 @@ class_name LevelManager
@export var player_dir: PlayerNodeDirectory = null @export var player_dir: PlayerNodeDirectory = null
## Emitted when the [GolfBall] for the local player was spawned in a level.
signal local_player_spawned(ball: GolfBall, level: GolfLevel)
## 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. ## Emitted when a level has been completed.
signal level_completed(level: GolfLevel) 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. ## The blank [GolfLevel] to initialize on clients.
@ -37,57 +24,139 @@ const base_scene: PackedScene = preload("res://scenes/golf_level.tscn")
## The currently instantiated [GolfLevel]. ## The currently instantiated [GolfLevel].
var level: GolfLevel = null var level: GolfLevel = null
## The next scene to use as [field target].
##
## Should be null on everything that's not the server.
var next: PackedScene = null
## Create the empty [GolfLevel] object everywhere. ## The currently instantiated target [GolfLevel].
@rpc("authority", "call_local", "reliable") ##
func rpc_next_level(): ## Should be null on everything that's not the server.
Log.peer(self, "Advancinng to the next level!") var target: GolfLevel = null
# Destroy the current level
if level != null:
Log.peer(self, "Destroying the current level: %s" % level) ## Destroy the current [field level].
level_destroying.emit(level) func destroy_level() -> void:
level.queue_free() Log.peer(self, "Emitting level_destroying signal...")
level = null level_destroying.emit(level)
# Determine the next level
Log.peer(self, "Queueing the free of the level and removing references...")
remove_child(level)
level.queue_free()
level = null
Log.peer(self, "Emitting level_destroyed signal...")
level_destroyed.emit()
## Emitted when the current level is about to be destroyed.
signal level_destroying(level: GolfLevel)
## Emitted when the current level has been destroyed.
signal level_destroyed
## Advance the [field playlist] and return the next level to play.
func determine_next_level() -> void:
Log.peer(self, "Determining the next level...") Log.peer(self, "Determining the next level...")
var next = playlist.advance() next = playlist.advance()
Log.peer(self, "Determined the next level: %s" % next) Log.peer(self, "Determined the next level: %s" % next)
level_determined.emit(next) level_determined.emit(next)
## Make sure the playlist hasn't been exausted
if next == null: if next == null:
Log.peer(self, "Playlist is complete, not doing anything.") Log.peer(self, "Playlist is complete.")
playlist_complete.emit(playlist) playlist_complete.emit(playlist)
return
# Create the new level ## Emitted when the next level has been determined by calling [LevelPlaylist.advance].
Log.peer(self, "Instantiating empty level template...") signal level_determined(scene: PackedScene)
## Emitted when all levels in the [field playlist] have been exausted.
signal playlist_complete(playlist: LevelPlaylist)
## Instantiate and setup a copy of the [field base_scene].
func initialize_level() -> void:
Log.peer(self, "Instantiating level template...")
level = base_scene.instantiate() level = base_scene.instantiate()
Log.peer(self, "Instantiated empty level template: %s" % level)
# Configure the new level
Log.peer(self, "Configuring level variables...") Log.peer(self, "Configuring level variables...")
level.player_dir = player_dir level.player_dir = player_dir
level.local_player_spawned.connect(_on_local_player_spawned)
Log.peer(self, "Connecting level signals...")
level.level_completed.connect(_on_level_completed) level.level_completed.connect(_on_level_completed)
if multiplayer.is_server():
Log.peer(self, "Instantiating the target level scene...") Log.peer(self, "Adding level to the scene tree...")
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) add_child(level, true)
level_created.emit(level)
# Start the replication Log.peer(self, "Emitting level_initialized signal...")
if multiplayer.is_server(): level_initialized.emit(level)
Log.peer(self, "Building level...")
level.build() ## Emitted when a copy of [field base_scene] has been initialized by [method initialize_level].
signal level_initialized(level: GolfLevel)
## Instantiate a copy of the given target [GolfLevel].
func init_target() -> void:
Log.peer(self, "Instantiating level targets...")
level.target = next.instantiate()
Log.peer(self, "Emitting target_initialized signal...")
target_initialized.emit(level.target)
## Emitted when a copy of the target level has been initialized by [method init_target].
signal target_initialized(level: GolfLevel)
## Replicate the [method target] onto the [method level] of all currently connected clients if no peer_id is specified, or to only that client if a peer_id is specified.
func build_level(peer_id: int = 0) -> void:
Log.peer(self, "Building level...")
level.build(peer_id)
Log.peer(self, "Emitting level_built signal...")
level_built.emit(level)
## Emitted on the server when it has finished sending out RPCs for the [method GolfLevel.build] of the [field target] level.
signal level_built(level: GolfLevel)
## Send the current level to a given client.
func sync_level(peer_id: int = 0) -> void:
Log.peer(self, "Repeating the current level to: %d" % peer_id)
if level:
if peer_id > 0:
rpc_wipe_level.rpc_id(peer_id)
else:
rpc_wipe_level.rpc()
build_level(peer_id)
## Add a new player to the current level.
func add_player(playernode: PlayerNode):
Log.peer(self, "Adding a new player to the level in progress...")
if level != null:
level.build_new_ball(playernode.player_name)
## Advance to the next level.
func next_level() -> void:
Log.peer(self, "Advancing to the next level...")
determine_next_level()
if next != null:
rpc_wipe_level.rpc()
init_target()
build_level()
## Clear the current level on all clients and prepare to build a new one.
@rpc("authority", "call_local", "reliable")
func rpc_wipe_level():
if level != null:
destroy_level()
initialize_level()
func _on_level_completed() -> void: func _on_level_completed() -> void:
Log.peer(self, "Level completed!") Log.peer(self, "Level completed!")
level_completed.emit(level) level_completed.emit(level)
if is_multiplayer_authority(): if is_multiplayer_authority():
rpc_next_level.rpc() next_level()
func _on_local_player_spawned(ball: GolfBall) -> void:
Log.peer(self, "Local player spawned: %s" % ball)
local_player_spawned.emit(ball, level)

View file

@ -44,7 +44,7 @@ func init_server_game(server_port: int) -> void:
scene_tree.set_multiplayer(smp, ^"/root/Main/Server") scene_tree.set_multiplayer(smp, ^"/root/Main/Server")
server_game_instance.init_signals() server_game_instance.init_signals()
server_game_instance.leave_confirmed.connect(_on_leave_confirmed) server_game_instance.lost_connection.connect(_on_lost_connection)
smp.set_multiplayer_peer(peer) smp.set_multiplayer_peer(peer)
func deinit_server_game() -> void: func deinit_server_game() -> void:
@ -71,7 +71,7 @@ func init_client_game(player_name: String, player_color: Color, server_address:
client_game_instance.local_player_name = player_name client_game_instance.local_player_name = player_name
client_game_instance.local_player_color = player_color client_game_instance.local_player_color = player_color
client_game_instance.phase_tracker.phase_changed.connect(_on_phase_changed) client_game_instance.phase_tracker.phase_changed.connect(_on_phase_changed)
client_game_instance.leave_confirmed.connect(_on_leave_confirmed) client_game_instance.lost_connection.connect(_on_lost_connection)
smp.set_multiplayer_peer(peer) smp.set_multiplayer_peer(peer)
func deinit_client_game() -> void: func deinit_client_game() -> void:
@ -83,11 +83,11 @@ func deinit_client_game() -> void:
func init_lobby_menu() -> void: func init_lobby_menu() -> void:
lobby_menu_instance = lobby_menu_scene.instantiate() lobby_menu_instance = lobby_menu_scene.instantiate()
lobby_menu_instance.init_refs() lobby_menu_instance.init_refs()
lobby_menu_instance.leave_confirmed.connect(_on_leave_confirmed) lobby_menu_instance.leave_confirmed.connect(_on_lost_connection)
if server_game_instance: if server_game_instance:
lobby_menu_instance.start_button.disabled = false lobby_menu_instance.start_button.disabled = false
lobby_menu_instance.start_confirmed.connect(server_game_instance._on_main_start_confirmed) lobby_menu_instance.start_confirmed.connect(server_game_instance._on_main_start_confirmed)
client_game_instance.multiplayer.server_disconnected.connect(_on_leave_confirmed) client_game_instance.multiplayer.server_disconnected.connect(_on_lost_connection)
client_game_instance.player_dir.child_entered_tree.connect(lobby_menu_instance.players_list._on_playernode_created) client_game_instance.player_dir.child_entered_tree.connect(lobby_menu_instance.players_list._on_playernode_created)
client_game_instance.player_dir.child_exiting_tree.connect(lobby_menu_instance.players_list._on_playernode_destroyed) client_game_instance.player_dir.child_exiting_tree.connect(lobby_menu_instance.players_list._on_playernode_destroyed)
client_game_instance.player_dir.playernode_name_changed.connect(lobby_menu_instance.players_list._on_playernode_name_changed) client_game_instance.player_dir.playernode_name_changed.connect(lobby_menu_instance.players_list._on_playernode_name_changed)
@ -96,10 +96,10 @@ func init_lobby_menu() -> void:
interface_instance.add_child(lobby_menu_instance) interface_instance.add_child(lobby_menu_instance)
func deinit_lobby_menu() -> void: func deinit_lobby_menu() -> void:
lobby_menu_instance.leave_confirmed.disconnect(_on_leave_confirmed) lobby_menu_instance.leave_confirmed.disconnect(_on_lost_connection)
if server_game_instance: if server_game_instance:
lobby_menu_instance.start_confirmed.disconnect(server_game_instance._on_main_start_confirmed) lobby_menu_instance.start_confirmed.disconnect(server_game_instance._on_main_start_confirmed)
client_game_instance.multiplayer.server_disconnected.disconnect(_on_leave_confirmed) client_game_instance.multiplayer.server_disconnected.disconnect(_on_lost_connection)
client_game_instance.player_dir.child_entered_tree.disconnect(lobby_menu_instance.players_list._on_playernode_created) client_game_instance.player_dir.child_entered_tree.disconnect(lobby_menu_instance.players_list._on_playernode_created)
client_game_instance.player_dir.child_exiting_tree.disconnect(lobby_menu_instance.players_list._on_playernode_destroyed) client_game_instance.player_dir.child_exiting_tree.disconnect(lobby_menu_instance.players_list._on_playernode_destroyed)
client_game_instance.player_dir.playernode_name_changed.disconnect(lobby_menu_instance.players_list._on_playernode_name_changed) client_game_instance.player_dir.playernode_name_changed.disconnect(lobby_menu_instance.players_list._on_playernode_name_changed)
@ -111,22 +111,22 @@ func deinit_lobby_menu() -> void:
func init_game_hud() -> void: func init_game_hud() -> void:
game_hud_instance = game_hud_scene.instantiate() game_hud_instance = game_hud_scene.instantiate()
client_game_instance.level_manager.level_completed.connect(game_hud_instance._on_level_completed) client_game_instance.level_manager.level_completed.connect(game_hud_instance._on_level_completed)
client_game_instance.level_manager.local_player_spawned.connect(game_hud_instance._on_local_player_spawned)
client_game_instance.player_dir.playernode_name_changed.connect(game_hud_instance._on_playernode_name_changed) 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_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_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_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) client_game_instance.player_dir.playernode_scores_changed.connect(game_hud_instance._on_playernode_scores_changed)
client_game_instance.player_dir.playernode_putt_performed.connect(game_hud_instance._on_playernode_putt_performed)
interface_instance.add_child(game_hud_instance) interface_instance.add_child(game_hud_instance)
func deinit_game_hud() -> void: func deinit_game_hud() -> void:
client_game_instance.level_manager.level_completed.disconnect(game_hud_instance._on_level_completed) client_game_instance.level_manager.level_completed.disconnect(game_hud_instance._on_level_completed)
client_game_instance.level_manager.local_player_spawned.disconnect(game_hud_instance._on_local_player_spawned)
client_game_instance.player_dir.playernode_name_changed.disconnect(game_hud_instance._on_playernode_name_changed) 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_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_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_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) client_game_instance.player_dir.playernode_scores_changed.disconnect(game_hud_instance._on_playernode_scores_changed)
client_game_instance.player_dir.playernode_putt_performed.disconnect(game_hud_instance._on_playernode_putt_performed)
game_hud_instance.queue_free() game_hud_instance.queue_free()
game_hud_instance = null game_hud_instance = null
@ -144,7 +144,7 @@ func _on_connecting_confirmed(player_name: String, player_color: Color, server_a
init_client_game(player_name, player_color, server_address, server_port) init_client_game(player_name, player_color, server_address, server_port)
init_lobby_menu() init_lobby_menu()
func _on_leave_confirmed() -> void: func _on_lost_connection() -> void:
if lobby_menu_instance != null: if lobby_menu_instance != null:
deinit_lobby_menu() deinit_lobby_menu()
if game_hud_instance != null: if game_hud_instance != null:

View file

@ -37,6 +37,14 @@ func rpc_destroy_peernode(peer_id: int):
peernode.queue_free() peernode.queue_free()
func _on_peernode_created(peernode: PeerNode) -> void:
if peernode.is_multiplayer_authority():
local_peernode_created.emit(peernode)
## Emitted on a client when the [PeerNode] for itself has been created.
signal local_peernode_created(peernode: PeerNode)
## Called on the server when a [PeerNode] calls [method rpc_identify] for itself. ## Called on the server when a [PeerNode] calls [method rpc_identify] for itself.
func _on_peernode_identified(player_name: String, peernode: PeerNode): func _on_peernode_identified(player_name: String, peernode: PeerNode):
peernode_identified.emit(player_name, peernode) peernode_identified.emit(player_name, peernode)

View file

@ -4,3 +4,5 @@
[node name="PeerNodeDirectory" type="Node"] [node name="PeerNodeDirectory" type="Node"]
script = ExtResource("1_mc47a") script = ExtResource("1_mc47a")
[connection signal="child_entered_tree" from="." to="." method="_on_peernode_created"]

View file

@ -106,3 +106,8 @@ signal score_reported(strokes: int)
## Emitted when the hole scores have changed because of [method rpc_set_scores]. ## Emitted when the hole scores have changed because of [method rpc_set_scores].
signal scores_changed(old: Array, new: Array) signal scores_changed(old: Array, new: Array)
## Emitted when a stroke has been performed on this player's [GolfBall].
##
## Currently tracked only for the local ball.
signal putt_performed(ball: GolfBall)

View file

@ -28,6 +28,7 @@ func rpc_possess_playernode(player_name: String, peer_id: int):
playernode.possessed.connect(_on_playernode_possessed.bind(playernode)) playernode.possessed.connect(_on_playernode_possessed.bind(playernode))
playernode.score_reported.connect(_on_playernode_score_reported.bind(playernode)) playernode.score_reported.connect(_on_playernode_score_reported.bind(playernode))
playernode.scores_changed.connect(_on_playernode_scores_changed.bind(playernode)) playernode.scores_changed.connect(_on_playernode_scores_changed.bind(playernode))
playernode.putt_performed.connect(_on_playernode_putt_performed.bind(playernode))
var sanitized_player_name = PlayerNode.sanitize_player_name(player_name) var sanitized_player_name = PlayerNode.sanitize_player_name(player_name)
playernode.player_name = sanitized_player_name playernode.player_name = sanitized_player_name
playernode.name = sanitized_player_name playernode.name = sanitized_player_name
@ -45,6 +46,8 @@ func _on_playernode_color_changed(old: Color, new: Color, playernode: PlayerNode
func _on_playernode_possessed(old: int, new: int, playernode: PlayerNode) -> void: func _on_playernode_possessed(old: int, new: int, playernode: PlayerNode) -> void:
playernode_possessed.emit(old, new, playernode) playernode_possessed.emit(old, new, playernode)
if playernode.is_multiplayer_authority() and not multiplayer.is_server():
local_playernode_possessed.emit(old, new, playernode)
func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void: func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void:
playernode_score_reported.emit(strokes, playernode) playernode_score_reported.emit(strokes, playernode)
@ -52,6 +55,9 @@ func _on_playernode_score_reported(strokes: int, playernode: PlayerNode) -> void
func _on_playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode) -> void: func _on_playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode) -> void:
playernode_scores_changed.emit(old, new, playernode) playernode_scores_changed.emit(old, new, playernode)
func _on_playernode_putt_performed(ball: GolfBall, playernode: PlayerNode) -> void:
playernode_putt_performed.emit(ball, playernode)
## Emitted when the name of one of the children [PlayerNode]s changes on the local scene. ## 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) signal playernode_name_changed(old: String, new: String, playernode: PlayerNode)
@ -62,8 +68,14 @@ signal playernode_color_changed(old: Color, new: Color, playernode: PlayerNode)
## Emitted everywhere when one of the children [PlayerNode]s has changed multiplayer authority. ## Emitted everywhere when one of the children [PlayerNode]s has changed multiplayer authority.
signal playernode_possessed(old: int, new: int, playernode: PlayerNode) signal playernode_possessed(old: int, new: int, playernode: PlayerNode)
## Emitted on a client when it becomes authority of a [PlayerNode].
signal local_playernode_possessed(old: int, new: int, playernode: PlayerNode)
## Emitted when a [PlayerNode] reports a score. ## Emitted when a [PlayerNode] reports a score.
signal playernode_score_reported(strokes: int, playernode: PlayerNode) signal playernode_score_reported(strokes: int, playernode: PlayerNode)
## Emitted when the scores of one of the children [PlayerNode]s change on the local scene. ## 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) signal playernode_scores_changed(old: Array, new: Array, playernode: PlayerNode)
## Emitted when a [PlayerNode] performs a putt on its controlled [GolfBall].
signal playernode_putt_performed(ball: GolfBall, playernode: PlayerNode)