mirror of
https://github.com/Steffo99/nanogolf.git
synced 2024-11-21 07:34:20 +00:00
Everything works!
This commit is contained in:
parent
c252fe13fc
commit
ff9336eecd
15 changed files with 239 additions and 73 deletions
|
@ -23,6 +23,12 @@ window/stretch/mode="canvas_items"
|
|||
window/stretch/aspect="expand"
|
||||
window/handheld/orientation=1
|
||||
|
||||
[layer_names]
|
||||
|
||||
2d_physics/layer_1="Default"
|
||||
2d_physics/layer_2="Players"
|
||||
2d_physics/layer_3="Walls"
|
||||
|
||||
[physics]
|
||||
|
||||
common/physics_ticks_per_second=300
|
||||
|
|
|
@ -7,4 +7,4 @@ class_name FollowCamera
|
|||
|
||||
func _physics_process(_delta):
|
||||
if target != null:
|
||||
position = target.position
|
||||
position = target.global_position
|
||||
|
|
|
@ -2,31 +2,58 @@ extends CharacterBody2D
|
|||
class_name GolfBall
|
||||
|
||||
|
||||
|
||||
@export_category("References")
|
||||
|
||||
## The [PuttController] that this node should poll.
|
||||
@export var putt_controller: PuttController
|
||||
|
||||
## The [HoleController] that this node should poll.
|
||||
@export var hole_controller: HoleController
|
||||
|
||||
## The [AudioStreamPlayer2D] that this node should play when entering the hole.
|
||||
@export var hole_sound: AudioStreamPlayer2D
|
||||
|
||||
## The [Label] where the name of this player should be displayed.
|
||||
@export var player_label: Label
|
||||
|
||||
|
||||
|
||||
@export_category("Physics")
|
||||
|
||||
## Dynamic friction subtracted from the body's velocity on each physics step.
|
||||
@export var physics_friction: float
|
||||
|
||||
## The maximum number of bounces that the collision algorithm will consider in a single physics step.
|
||||
@export var physics_max_bounces: float
|
||||
|
||||
## A multiplier applied to the body's velocity every time it collides with something.
|
||||
@export var physics_bounce_coefficient: float
|
||||
|
||||
## The scene to instantiate to play the collision sound
|
||||
@export var collision_sound: PackedScene
|
||||
|
||||
|
||||
|
||||
@export_category("Sounds")
|
||||
|
||||
## Curve mapping relative putt power to putt sound volume.
|
||||
@export var collision_volume_db: Curve
|
||||
|
||||
## The velocity at which the maximum volume of [member collision_volume_db] is played.
|
||||
@export var collision_volume_max_velocity: float
|
||||
|
||||
|
||||
## Emitted when the ball enters the hole.
|
||||
signal entered_hole(strokes: int)
|
||||
|
||||
|
||||
## How many strokes have been performed so far.
|
||||
var strokes: int = 0
|
||||
|
||||
## Whether the ball has entered the hole.
|
||||
var in_hole: bool = false
|
||||
|
||||
@onready var putt_controller: PuttController = $"PuttController"
|
||||
@onready var hole_controller: HoleController = $"HoleController"
|
||||
@onready var hole_sound: AudioStreamPlayer2D = $"HoleSound"
|
||||
@onready var player_label: Label = $"PlayerLabel"
|
||||
|
||||
|
||||
## The name of the player represented by this scene.
|
||||
var player_name: String = "Player":
|
||||
|
@ -88,25 +115,36 @@ func do_movement(delta: float) -> void:
|
|||
velocity *= physics_bounce_coefficient
|
||||
|
||||
|
||||
func apply_friction(delta) -> void:
|
||||
## Reduce [field velocity] by [field physics_friction], taking the [param delta] into account.
|
||||
func apply_friction(delta: float) -> void:
|
||||
var new_velocity_length = max(0.0, velocity.length() - physics_friction * delta)
|
||||
velocity = velocity.normalized() * new_velocity_length
|
||||
|
||||
|
||||
## Return whether this object can be considered stopped or not.
|
||||
func check_has_stopped() -> bool:
|
||||
return velocity.length() == 0.0
|
||||
|
||||
|
||||
## Return whether this object will enter the hole on this frame or not.
|
||||
func check_has_entered_hole() -> bool:
|
||||
return check_has_stopped() and hole_controller.over_hole
|
||||
|
||||
|
||||
func enter_hole() -> void:
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_sync_enter_hole():
|
||||
in_hole = true
|
||||
visible = false
|
||||
hole_sound.play()
|
||||
entered_hole.emit(strokes)
|
||||
print("[GolfBall] Entered hole in: ", strokes)
|
||||
Log.peer(self, "Entered hole in: %d" % strokes)
|
||||
|
||||
|
||||
|
||||
# FIXME: What happens on the server?
|
||||
## Push this object's [field global_position] to all other clients.
|
||||
@rpc("authority", "call_remote", "unreliable_ordered")
|
||||
func rpc_sync_global_position(nposition: Vector2):
|
||||
global_position = nposition
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
@ -114,10 +152,12 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func _physics_process(delta) -> void:
|
||||
if not in_hole:
|
||||
do_movement(delta)
|
||||
apply_friction(delta)
|
||||
if check_has_entered_hole():
|
||||
enter_hole()
|
||||
if check_has_stopped():
|
||||
putt_controller.can_putt = true
|
||||
if is_multiplayer_authority():
|
||||
if not in_hole:
|
||||
do_movement(delta)
|
||||
rpc_sync_global_position.rpc(global_position)
|
||||
apply_friction(delta)
|
||||
if check_has_entered_hole():
|
||||
rpc_sync_enter_hole.rpc()
|
||||
if check_has_stopped():
|
||||
putt_controller.can_putt = true
|
||||
|
|
|
@ -20,10 +20,16 @@ radius = 4.0
|
|||
[sub_resource type="CircleShape2D" id="CircleShape2D_aigrf"]
|
||||
radius = 1.0
|
||||
|
||||
[node name="GolfBall" type="CharacterBody2D"]
|
||||
[node name="GolfBall" type="CharacterBody2D" node_paths=PackedStringArray("putt_controller", "hole_controller", "hole_sound", "player_label")]
|
||||
collision_layer = 2
|
||||
collision_mask = 4
|
||||
motion_mode = 1
|
||||
wall_min_slide_angle = 0.0
|
||||
script = ExtResource("1_1uswk")
|
||||
putt_controller = NodePath("PuttController")
|
||||
hole_controller = NodePath("HoleController")
|
||||
hole_sound = NodePath("HoleSound")
|
||||
player_label = NodePath("PlayerLabel")
|
||||
physics_friction = 60.0
|
||||
physics_max_bounces = 4.0
|
||||
physics_bounce_coefficient = 0.65
|
||||
|
|
10
scenes/golf_ball_directory.tscn
Normal file
10
scenes/golf_ball_directory.tscn
Normal file
|
@ -0,0 +1,10 @@
|
|||
[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")
|
|
@ -1,6 +1,11 @@
|
|||
extends Node2D
|
||||
class_name GolfLevel
|
||||
|
||||
|
||||
## Emitted when it's time to change to the next level.
|
||||
signal level_completed
|
||||
|
||||
|
||||
@export_category("Level Data")
|
||||
|
||||
## Whether the [field camera] follows the active player or not.
|
||||
|
@ -23,7 +28,7 @@ class_name GolfLevel
|
|||
@export var map: TileMap = null
|
||||
|
||||
|
||||
## If on server, this variable indicates the [GolfLevel] to replicate on clients.
|
||||
## 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.
|
||||
##
|
||||
|
@ -31,22 +36,22 @@ class_name GolfLevel
|
|||
var target: GolfLevel = null
|
||||
|
||||
|
||||
## The [PlayerNodeDirectory] to use to determine which players to spawn on clients.
|
||||
## The [PlayerNodeDirectory] to use to determine which players to spawn.
|
||||
##
|
||||
## It should be set on instantiation of a new [GolfLevel].
|
||||
var player_dir: PlayerNodeDirectory = null
|
||||
|
||||
|
||||
## The [PackedScene] to initialize on clients as [TileMap].
|
||||
## The [PackedScene] to initialize as [TileMap].
|
||||
const tilemap_scene: PackedScene = preload("res://scenes/golf_tilemap.tscn")
|
||||
|
||||
## The [PackedScene] to initialize on clients as [GolfTee].
|
||||
## The [PackedScene] to initialize as [GolfTee].
|
||||
const tee_scene: PackedScene = preload("res://scenes/golf_tee.tscn")
|
||||
|
||||
## The [PackedScene] to initialize on clients as [GolfHole].
|
||||
## The [PackedScene] to initialize as [GolfHole].
|
||||
const hole_scene: PackedScene = preload("res://scenes/golf_hole.tscn")
|
||||
|
||||
## The [PackedScene] to initialize on clients as [Camera2D].
|
||||
## The [PackedScene] to initialize as [Camera2D].
|
||||
const camera_scene: PackedScene = preload("res://scenes/follow_camera.tscn")
|
||||
|
||||
|
||||
|
@ -55,6 +60,7 @@ func build() -> void:
|
|||
build_tilemap()
|
||||
build_tilemap_cells()
|
||||
build_tee()
|
||||
build_balls()
|
||||
build_hole()
|
||||
build_camera()
|
||||
|
||||
|
@ -65,8 +71,8 @@ func build_tilemap() -> void:
|
|||
var tmap: TileMap = target.map
|
||||
rpc_build_tilemap.rpc(tmap.global_position, tmap.global_rotation, tmap.global_scale)
|
||||
|
||||
## Create the [field map] on clients.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
## Create the [field map].
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_build_tilemap(tposition: Vector2, trotation: float, tscale: Vector2):
|
||||
Log.peer(self, "Building map...")
|
||||
map = tilemap_scene.instantiate()
|
||||
|
@ -77,7 +83,7 @@ func rpc_build_tilemap(tposition: Vector2, trotation: float, tscale: Vector2):
|
|||
|
||||
## Replicate the cells of [field target]'s [field map] to the remote [field map].
|
||||
##
|
||||
## The [field map] must be already created on clients.
|
||||
## The [field map] must be already created.
|
||||
##
|
||||
## Only takes layer 0 into consideration.
|
||||
func build_tilemap_cells() -> void:
|
||||
|
@ -90,8 +96,8 @@ func build_tilemap_cells() -> void:
|
|||
var alternative_tile: int = tmap.get_cell_alternative_tile(0, coords)
|
||||
rpc_build_tilemap_cell.rpc(layer, coords, source_id, atlas_coords, alternative_tile)
|
||||
|
||||
## Create a cell of [field map] on clients.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
## Create a cell of [field map].
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_build_tilemap_cell(
|
||||
layer: int,
|
||||
coords: Vector2i,
|
||||
|
@ -108,31 +114,29 @@ func build_tee() -> void:
|
|||
Log.peer(self, "Replicating tee...")
|
||||
var ttee: GolfTee = target.tee
|
||||
rpc_build_tee.rpc(ttee.global_position)
|
||||
rpc_build_tee_balls.rpc()
|
||||
|
||||
## Create the [GolfTee] object on clients.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
## Create the [GolfTee] object.
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_build_tee(tposition: Vector2):
|
||||
Log.peer(self, "Building tee...")
|
||||
tee = tee_scene.instantiate()
|
||||
tee.global_position = tposition
|
||||
tee.everyone_entered_hole.connect(_on_everyone_entered_hole)
|
||||
add_child(tee)
|
||||
|
||||
## Create and initialize the [GolfBall] object on clients, at the [GolfTee]'s position.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
func rpc_build_tee_balls():
|
||||
Log.peer(self, "Building tee balls...")
|
||||
|
||||
## Replicate the currently connected players' [GolfBall]s to the remote [field tee].
|
||||
func build_balls() -> void:
|
||||
for playernode in player_dir.get_children():
|
||||
var ball: GolfBall = tee.create(playernode)
|
||||
add_child(ball)
|
||||
rpc_build_ball.rpc(playernode.player_name)
|
||||
|
||||
|
||||
## Create and initialize a [GolfBall] for a single [PlayerNode] with the given name.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
func rpc_build_tee_ball(player_name: String):
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_build_ball(player_name: String):
|
||||
Log.peer(self, "Building tee ball for: %s" % player_name)
|
||||
var playernode: PlayerNode = player_dir.get_playernode(player_name)
|
||||
var ball: GolfBall = tee.create(playernode)
|
||||
add_child(ball)
|
||||
tee.spawn(playernode)
|
||||
|
||||
|
||||
## Replicate the [field hole] of the [field target] to the remote [field hole].
|
||||
|
@ -141,8 +145,8 @@ func build_hole() -> void:
|
|||
var thole: GolfHole = target.hole
|
||||
rpc_build_hole.rpc(thole.global_position, thole.global_scale)
|
||||
|
||||
## Create the [GolfHole] object on clients.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
## Create the [GolfHole] object.
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_build_hole(tposition: Vector2, tscale: Vector2):
|
||||
Log.peer(self, "Building hole...")
|
||||
hole = hole_scene.instantiate()
|
||||
|
@ -157,18 +161,44 @@ func build_camera() -> void:
|
|||
var tcamera: FollowCamera = target.camera
|
||||
rpc_build_camera.rpc(tcamera.global_position, target.camera_follows_player)
|
||||
|
||||
## Create the [Camera2D] object on clients.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
## Create the [Camera2D] object.
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func rpc_build_camera(tposition: Vector2, tfocus: bool):
|
||||
if multiplayer.is_server():
|
||||
Log.peer(self, "Not building camera on the server.")
|
||||
return
|
||||
Log.peer(self, "Building camera...")
|
||||
camera = camera_scene.instantiate()
|
||||
camera.global_position = tposition
|
||||
if tfocus:
|
||||
camera.target = null # TODO: Find local player
|
||||
# This supports only a single player per peer, for now.
|
||||
for child in player_dir.get_children():
|
||||
var playernode = child as PlayerNode
|
||||
if playernode.is_multiplayer_authority():
|
||||
var ctarget = tee.get_node("GolfBall__%s" % child.player_name)
|
||||
camera.target = ctarget
|
||||
add_child(camera)
|
||||
|
||||
|
||||
func _on_playernode_created(node: Node):
|
||||
func _ready() -> void:
|
||||
player_dir.child_entered_tree.connect(_on_playernode_created)
|
||||
if multiplayer.is_server():
|
||||
set_physics_process(false)
|
||||
hide()
|
||||
|
||||
|
||||
func _on_playernode_created(node: Node) -> void:
|
||||
Log.peer(self, "Spawning new player...")
|
||||
var playernode: PlayerNode = node as PlayerNode
|
||||
rpc_build_tee_ball.rpc(playernode.player_name)
|
||||
rpc_build_ball.rpc(playernode.player_name)
|
||||
|
||||
|
||||
func _on_everyone_entered_hole() -> void:
|
||||
level_completed.emit()
|
||||
|
||||
|
||||
func _on_tree_exiting() -> void:
|
||||
player_dir.child_entered_tree.disconnect(_on_playernode_created)
|
||||
if target:
|
||||
target.queue_free()
|
||||
target = null
|
||||
|
|
|
@ -4,3 +4,6 @@
|
|||
|
||||
[node name="GolfLevel" type="Node2D"]
|
||||
script = ExtResource("1_evup0")
|
||||
camera_follows_player = null
|
||||
|
||||
[connection signal="tree_exiting" from="." to="." method="_on_tree_exiting"]
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
[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")
|
||||
|
|
|
@ -14,11 +14,10 @@ tee = NodePath("GolfTee")
|
|||
hole = NodePath("GolfHole")
|
||||
map = NodePath("TileMap")
|
||||
|
||||
[node name="FollowCamera" parent="." node_paths=PackedStringArray("target") instance=ExtResource("1_3a7ly")]
|
||||
[node name="FollowCamera" parent="." instance=ExtResource("1_3a7ly")]
|
||||
process_callback = 0
|
||||
position_smoothing_enabled = true
|
||||
drag_vertical_enabled = true
|
||||
target = NodePath("")
|
||||
|
||||
[node name="GolfTee" parent="." instance=ExtResource("3_6q2yk")]
|
||||
position = Vector2(0, -112)
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
extends Node2D
|
||||
class_name GolfTee
|
||||
|
||||
## Emitted when all connected [GolfBall]s have their [field GolfBall.in_hole] parameter set to true.
|
||||
signal everyone_entered_hole
|
||||
|
||||
|
||||
## The [GolfBall] [PackedScene] to [method spawn].
|
||||
const ball_scene: PackedScene = preload("res://scenes/golf_ball.tscn")
|
||||
|
||||
|
||||
## Create a new [GolfBall] from [field ball_scene], initialize it with details from the given [PlayerNode], and return it.
|
||||
##
|
||||
## Note that this does not add the [GolfBall] to the scene tree, as it is the [GolfLevel]'s responsibility to do so.
|
||||
func create(playernode: PlayerNode) -> GolfBall:
|
||||
## Add a new [GolfBall] from [field ball_scene], initialize it with details from the given [PlayerNode], and return it.
|
||||
func spawn(playernode: PlayerNode) -> GolfBall:
|
||||
# Create the [GolfBall]
|
||||
var obj: GolfBall = ball_scene.instantiate()
|
||||
# Setup the initial values
|
||||
obj.global_position = global_position
|
||||
obj.position = Vector2.ZERO
|
||||
obj.name = "GolfBall__%s" % playernode.player_name
|
||||
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()
|
||||
# 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)
|
||||
|
@ -27,11 +30,27 @@ func create(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)
|
||||
# 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
|
||||
if ball == null:
|
||||
# Ignore collision sounds
|
||||
continue
|
||||
if ball.get_multiplayer_authority() == 1:
|
||||
continue
|
||||
if not ball.in_hole:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _on_name_changed(_old: String, new: String, obj: GolfBall) -> void:
|
||||
obj.name = "GolfBall__%s" % new
|
||||
obj.player_name = new
|
||||
|
||||
func _on_color_changed(_old: Color, new: Color, obj: GolfBall) -> void:
|
||||
|
@ -39,8 +58,13 @@ func _on_color_changed(_old: Color, new: Color, obj: GolfBall) -> void:
|
|||
|
||||
func _on_possessed(_old: int, new: int, obj: GolfBall) -> void:
|
||||
obj.set_multiplayer_authority(new)
|
||||
obj.putt_controller.can_putt = is_multiplayer_authority()
|
||||
|
||||
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.color_changed.disconnect(on_color_changed)
|
||||
playernode.possessed.disconnect(on_possessed)
|
||||
|
||||
func _on_entered_hole(_strokes: int) -> void:
|
||||
if is_everyone_in_hole():
|
||||
everyone_entered_hole.emit()
|
||||
|
|
|
@ -20,20 +20,23 @@ var level: GolfLevel = null
|
|||
func rpc_next_level():
|
||||
# Destroy the current level
|
||||
if level != null:
|
||||
level.target.queue_free()
|
||||
player_dir.child_entered_tree.disconnect(level._on_playernode_created)
|
||||
level.queue_free()
|
||||
level = null
|
||||
# Create the new level
|
||||
level = base_scene.instantiate()
|
||||
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
|
||||
player_dir.child_entered_tree.connect(level._on_playernode_created)
|
||||
# Add the level to the tree
|
||||
add_child(level)
|
||||
add_child(level, true)
|
||||
# Start the replication
|
||||
if multiplayer.is_server():
|
||||
level.build()
|
||||
|
||||
|
||||
func _on_level_completed() -> void:
|
||||
if multiplayer.is_server():
|
||||
rpc_next_level.rpc()
|
||||
|
|
|
@ -87,7 +87,16 @@ func init_lobby_menu() -> void:
|
|||
interface_instance.add_child(lobby_menu_instance)
|
||||
|
||||
func deinit_lobby_menu() -> void:
|
||||
# TODO: Disconnect all signals above
|
||||
lobby_menu_instance.leave_confirmed.disconnect(_on_leave_confirmed)
|
||||
if server_game_instance:
|
||||
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.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.playernode_name_changed.disconnect(lobby_menu_instance.players_list._on_playernode_name_changed)
|
||||
client_game_instance.player_dir.playernode_color_changed.disconnect(lobby_menu_instance.players_list._on_playernode_color_changed)
|
||||
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()
|
||||
|
||||
func _ready() -> void:
|
||||
|
@ -105,8 +114,10 @@ func _on_connecting_confirmed(player_name: String, player_color: Color, server_a
|
|||
init_lobby_menu()
|
||||
|
||||
func _on_leave_confirmed() -> void:
|
||||
deinit_lobby_menu()
|
||||
deinit_client_game()
|
||||
if lobby_menu_scene:
|
||||
deinit_lobby_menu()
|
||||
if client_game_instance:
|
||||
deinit_client_game()
|
||||
if server_game_instance != null:
|
||||
deinit_server_game()
|
||||
init_main_menu()
|
||||
|
|
|
@ -1,24 +1,54 @@
|
|||
extends Node2D
|
||||
class_name PuttController
|
||||
|
||||
|
||||
|
||||
@export_category("References")
|
||||
|
||||
## The [Sprite2D] used to calculate [field sprite_texture_width] from.
|
||||
@export var sprite: Sprite2D
|
||||
|
||||
## The [AudioStreamPlayer2D] to play when a putt happens.
|
||||
@export var sound: AudioStreamPlayer2D
|
||||
|
||||
|
||||
|
||||
@export_category("Physics")
|
||||
|
||||
## The maximum impulse that a putt can have.
|
||||
@export var putt_max_impulse: float
|
||||
|
||||
|
||||
|
||||
@export_category("Scale")
|
||||
|
||||
## How many game units a pixel of screen mouse movement corresponds to.
|
||||
@export var putt_drag_scale: float
|
||||
|
||||
## Length multiplier of the putt ghost
|
||||
@export var putt_ghost_scale: float
|
||||
|
||||
## Curve mapping relative putt impulse to putt sound volume.
|
||||
@export var putt_volume: Curve
|
||||
|
||||
|
||||
|
||||
## Emitted when a putt has happened.
|
||||
signal putt(putt_vector: Vector2)
|
||||
|
||||
@onready var sprite: Sprite2D = $Sprite
|
||||
@onready var sound: AudioStreamPlayer2D = $Sound
|
||||
|
||||
## The width in pixels that the putt ghost should have.
|
||||
@onready var sprite_texture_width: float = sprite.texture.get_width()
|
||||
|
||||
## Whether a putt is currently in progress of not.
|
||||
##
|
||||
## If this is true, then [field drag_start_point] should contain a value.
|
||||
var is_putting: bool = false
|
||||
var putt_start_point: Vector2
|
||||
|
||||
## The position on the screen where the putt has started.
|
||||
var drag_start_point: Vector2
|
||||
|
||||
## Whether a putt can currently be performed or not.
|
||||
var can_putt: bool = false:
|
||||
get:
|
||||
return can_putt
|
||||
|
@ -30,7 +60,7 @@ var can_putt: bool = false:
|
|||
|
||||
func _input(event: InputEvent):
|
||||
if can_putt:
|
||||
if event is InputEventMouseButton:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
if not is_putting:
|
||||
start_putt(event.position)
|
||||
|
@ -42,10 +72,10 @@ func _input(event: InputEvent):
|
|||
else:
|
||||
push_warning("Attempted to end putt while none was in progress.")
|
||||
if is_putting:
|
||||
update_putt_ghost(compute_putt(event.position, putt_start_point))
|
||||
update_putt_ghost(compute_putt(event.position, drag_start_point))
|
||||
if event is InputEventMouseMotion:
|
||||
if is_putting:
|
||||
update_putt_ghost(compute_putt(event.position, putt_start_point))
|
||||
update_putt_ghost(compute_putt(event.position, drag_start_point))
|
||||
|
||||
|
||||
func update_putt_ghost(putt_vector: Vector2):
|
||||
|
@ -65,14 +95,14 @@ func compute_putt(start_position: Vector2, end_position: Vector2) -> Vector2:
|
|||
func start_putt(start_position: Vector2):
|
||||
visible = true
|
||||
is_putting = true
|
||||
putt_start_point = start_position
|
||||
drag_start_point = start_position
|
||||
|
||||
|
||||
func end_putt(end_position: Vector2):
|
||||
visible = false
|
||||
is_putting = false
|
||||
can_putt = false
|
||||
var putt_vector = compute_putt(putt_start_point, end_position)
|
||||
var putt_vector = compute_putt(drag_start_point, end_position)
|
||||
putt.emit(putt_vector)
|
||||
play_putt_sound(putt_vector)
|
||||
|
||||
|
|
|
@ -10,10 +10,12 @@ max_value = 0.0
|
|||
_data = [Vector2(0, -16), 0.0, 16.0, 0, 1, Vector2(1, 0), 16.0, 0.0, 1, 0]
|
||||
point_count = 2
|
||||
|
||||
[node name="PuttController" type="Node2D"]
|
||||
[node name="PuttController" type="Node2D" node_paths=PackedStringArray("sprite", "sound")]
|
||||
visible = false
|
||||
modulate = Color(1, 1, 1, 0.25098)
|
||||
script = ExtResource("1_rb6v1")
|
||||
sprite = NodePath("Sprite")
|
||||
sound = NodePath("Sound")
|
||||
putt_max_impulse = 333.0
|
||||
putt_drag_scale = 1.0
|
||||
putt_ghost_scale = 0.3
|
||||
|
|
|
@ -31,7 +31,8 @@ use_texture_padding = false
|
|||
|
||||
[resource]
|
||||
tile_size = Vector2i(128, 128)
|
||||
physics_layer_0/collision_layer = 1
|
||||
physics_layer_0/collision_layer = 4
|
||||
physics_layer_0/collision_mask = 0
|
||||
sources/1 = SubResource("TileSetAtlasSource_174yq")
|
||||
sources/2 = SubResource("TileSetAtlasSource_2md10")
|
||||
sources/3 = SubResource("TileSetAtlasSource_mbu12")
|
||||
|
|
Loading…
Reference in a new issue