fix: a lot of network sync fixes
This commit is contained in:
@@ -9,18 +9,18 @@ func build_ragdoll(color: Color) -> void:
|
|||||||
mat.roughness = 0.8
|
mat.roughness = 0.8
|
||||||
|
|
||||||
# Create bodies
|
# Create bodies
|
||||||
torso_body = _create_body(Vector3(0.4, 0.7, 0.25), mat, Vector3(0, 1.15, 0), 20.0)
|
torso_body = _create_body(Vector3(0.4, 0.7, 0.25), mat, Vector3(0, 1.15, 0), 20.0, "Torso")
|
||||||
var head_body = _create_body(Vector3(0.25, 0.25, 0.25), mat, Vector3(0, 1.65, 0), 5.0)
|
var head_body = _create_body(Vector3(0.25, 0.25, 0.25), mat, Vector3(0, 1.65, 0), 5.0, "Head")
|
||||||
|
|
||||||
var upper_arm_l_body = _create_body(Vector3(0.12, 0.35, 0.12), mat, Vector3(-0.28, 1.275, 0), 3.0)
|
var upper_arm_l_body = _create_body(Vector3(0.12, 0.35, 0.12), mat, Vector3(-0.28, 1.275, 0), 3.0, "UpperArmL")
|
||||||
var lower_arm_l_body = _create_body(Vector3(0.1, 0.35, 0.1), mat, Vector3(-0.28, 0.925, 0), 2.0)
|
var lower_arm_l_body = _create_body(Vector3(0.1, 0.35, 0.1), mat, Vector3(-0.28, 0.925, 0), 2.0, "LowerArmL")
|
||||||
var upper_arm_r_body = _create_body(Vector3(0.12, 0.35, 0.12), mat, Vector3(0.28, 1.275, 0), 3.0)
|
var upper_arm_r_body = _create_body(Vector3(0.12, 0.35, 0.12), mat, Vector3(0.28, 1.275, 0), 3.0, "UpperArmR")
|
||||||
var lower_arm_r_body = _create_body(Vector3(0.1, 0.35, 0.1), mat, Vector3(0.28, 0.925, 0), 2.0)
|
var lower_arm_r_body = _create_body(Vector3(0.1, 0.35, 0.1), mat, Vector3(0.28, 0.925, 0), 2.0, "LowerArmR")
|
||||||
|
|
||||||
var thigh_l_body = _create_body(Vector3(0.15, 0.45, 0.15), mat, Vector3(-0.12, 0.625, 0), 5.0)
|
var thigh_l_body = _create_body(Vector3(0.15, 0.45, 0.15), mat, Vector3(-0.12, 0.625, 0), 5.0, "ThighL")
|
||||||
var calf_l_body = _create_body(Vector3(0.13, 0.45, 0.13), mat, Vector3(-0.12, 0.175, 0), 3.0)
|
var calf_l_body = _create_body(Vector3(0.13, 0.45, 0.13), mat, Vector3(-0.12, 0.175, 0), 3.0, "CalfL")
|
||||||
var thigh_r_body = _create_body(Vector3(0.15, 0.45, 0.15), mat, Vector3(0.12, 0.625, 0), 5.0)
|
var thigh_r_body = _create_body(Vector3(0.15, 0.45, 0.15), mat, Vector3(0.12, 0.625, 0), 5.0, "ThighR")
|
||||||
var calf_r_body = _create_body(Vector3(0.13, 0.45, 0.13), mat, Vector3(0.12, 0.175, 0), 3.0)
|
var calf_r_body = _create_body(Vector3(0.13, 0.45, 0.13), mat, Vector3(0.12, 0.175, 0), 3.0, "CalfR")
|
||||||
|
|
||||||
add_child(torso_body)
|
add_child(torso_body)
|
||||||
add_child(head_body)
|
add_child(head_body)
|
||||||
@@ -33,6 +33,21 @@ func build_ragdoll(color: Color) -> void:
|
|||||||
add_child(thigh_r_body)
|
add_child(thigh_r_body)
|
||||||
add_child(calf_r_body)
|
add_child(calf_r_body)
|
||||||
|
|
||||||
|
var is_server = not multiplayer.has_multiplayer_peer() or multiplayer.is_server()
|
||||||
|
var rep = SceneReplicationConfig.new()
|
||||||
|
for child in get_children():
|
||||||
|
if child is RigidBody3D:
|
||||||
|
rep.add_property(NodePath(str(child.name) + ":position"))
|
||||||
|
rep.add_property(NodePath(str(child.name) + ":rotation"))
|
||||||
|
if not is_server:
|
||||||
|
child.freeze = true
|
||||||
|
child.freeze_mode = RigidBody3D.FREEZE_MODE_KINEMATIC
|
||||||
|
|
||||||
|
var sync = MultiplayerSynchronizer.new()
|
||||||
|
sync.name = "RagdollSync"
|
||||||
|
sync.replication_config = rep
|
||||||
|
add_child(sync)
|
||||||
|
|
||||||
# Need to wait a frame for paths to be valid before creating joints
|
# Need to wait a frame for paths to be valid before creating joints
|
||||||
call_deferred("_setup_joints", head_body,
|
call_deferred("_setup_joints", head_body,
|
||||||
upper_arm_l_body, lower_arm_l_body,
|
upper_arm_l_body, lower_arm_l_body,
|
||||||
@@ -76,8 +91,9 @@ func apply_initial_velocities(linear_vel: Vector3, impulse: Vector3) -> void:
|
|||||||
if is_instance_valid(torso_body):
|
if is_instance_valid(torso_body):
|
||||||
torso_body.apply_central_impulse(impulse * 2.0)
|
torso_body.apply_central_impulse(impulse * 2.0)
|
||||||
|
|
||||||
func _create_body(size: Vector3, mat: Material, pos: Vector3, mass: float) -> RigidBody3D:
|
func _create_body(size: Vector3, mat: Material, pos: Vector3, mass: float, bname: String) -> RigidBody3D:
|
||||||
var body = RigidBody3D.new()
|
var body = RigidBody3D.new()
|
||||||
|
body.name = bname
|
||||||
body.position = pos
|
body.position = pos
|
||||||
body.mass = mass
|
body.mass = mass
|
||||||
|
|
||||||
|
|||||||
+85
-47
@@ -1,13 +1,14 @@
|
|||||||
extends Area3D
|
extends Area3D
|
||||||
class_name CombatArea
|
class_name CombatArea
|
||||||
|
|
||||||
var time_left: float = 5.0
|
var out_of_bounds_players: Dictionary = {} # body -> { time_left: 5.0 }
|
||||||
var time_safe: float = 0.0
|
var recovery_players: Dictionary = {} # body -> { time_safe: 0.0, time_left: float }
|
||||||
|
|
||||||
var _player_out_of_bounds: Node3D = null
|
|
||||||
var _ui_layer: CanvasLayer = null
|
var _ui_layer: CanvasLayer = null
|
||||||
var _ui_label: Label = null
|
var _ui_label: Label = null
|
||||||
var _ui_overlay: ColorRect = null
|
var _ui_overlay: ColorRect = null
|
||||||
|
var _is_local_player_out: bool = false
|
||||||
|
var _local_player_body: Node3D = null
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
collision_layer = 0
|
collision_layer = 0
|
||||||
@@ -17,50 +18,85 @@ func _ready() -> void:
|
|||||||
set_process(false)
|
set_process(false)
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if _player_out_of_bounds:
|
# Process players who are actively out of bounds
|
||||||
# If the player dies while out of bounds, stop the timer and remove the UI
|
var to_kill: Array = []
|
||||||
if "is_dead" in _player_out_of_bounds and _player_out_of_bounds.is_dead:
|
for body in out_of_bounds_players.keys():
|
||||||
_player_out_of_bounds = null
|
if "is_dead" in body and body.is_dead:
|
||||||
_remove_ui()
|
out_of_bounds_players.erase(body)
|
||||||
return
|
if body == _local_player_body:
|
||||||
|
_remove_ui()
|
||||||
|
continue
|
||||||
|
|
||||||
# Player is outside the combat area!
|
var data = out_of_bounds_players[body]
|
||||||
time_left -= delta
|
data.time_left -= delta
|
||||||
if time_left <= 0.0:
|
if data.time_left <= 0.0:
|
||||||
time_left = 0.0
|
data.time_left = 0.0
|
||||||
_kill_player()
|
to_kill.append(body)
|
||||||
|
|
||||||
# Update UI
|
if body == _local_player_body and _is_local_player_out and _ui_label and _ui_overlay:
|
||||||
if _ui_label and _ui_overlay:
|
_ui_label.text = "WARNING: RETURN TO COMBAT AREA\n%.1f" % data.time_left
|
||||||
_ui_label.text = "WARNING: RETURN TO COMBAT AREA\n%.1f" % time_left
|
_ui_overlay.color = Color(1.0, 0.0, 0.0, 0.8 * (1.0 - (data.time_left / 5.0)))
|
||||||
_ui_overlay.color = Color(1.0, 0.0, 0.0, 0.8 * (1.0 - (time_left / 5.0)))
|
|
||||||
else:
|
for body in to_kill:
|
||||||
# Player is inside the combat area, but might have a damaged timer
|
_kill_player(body)
|
||||||
if time_left < 5.0:
|
|
||||||
time_safe += delta
|
# Process players who returned but are recovering
|
||||||
if time_safe >= 10.0:
|
var recovered: Array = []
|
||||||
time_left = 5.0
|
for body in recovery_players.keys():
|
||||||
time_safe = 0.0
|
if "is_dead" in body and body.is_dead:
|
||||||
set_process(false) # Fully recovered, sleep
|
recovered.append(body)
|
||||||
|
continue
|
||||||
|
|
||||||
|
var data = recovery_players[body]
|
||||||
|
data.time_safe += delta
|
||||||
|
if data.time_safe >= 10.0:
|
||||||
|
recovered.append(body)
|
||||||
|
|
||||||
|
for body in recovered:
|
||||||
|
recovery_players.erase(body)
|
||||||
|
|
||||||
|
if out_of_bounds_players.is_empty() and recovery_players.is_empty():
|
||||||
|
set_process(false)
|
||||||
|
|
||||||
func _on_body_exited(body: Node3D) -> void:
|
func _on_body_exited(body: Node3D) -> void:
|
||||||
if body.name == "Player":
|
if body is CharacterBody3D and body.has_method("take_damage") and str(body.name).is_valid_int():
|
||||||
# Don't trigger if the player is already dead or the scene is tearing down
|
|
||||||
if "is_dead" in body and body.is_dead:
|
if "is_dead" in body and body.is_dead:
|
||||||
return
|
return
|
||||||
if body.is_queued_for_deletion() or not is_inside_tree():
|
if body.is_queued_for_deletion() or not is_inside_tree():
|
||||||
return
|
return
|
||||||
|
|
||||||
_player_out_of_bounds = body
|
var start_time = 5.0
|
||||||
time_safe = 0.0 # Reset recovery timer
|
if recovery_players.has(body):
|
||||||
_create_ui()
|
start_time = recovery_players[body].time_left
|
||||||
|
recovery_players.erase(body)
|
||||||
|
|
||||||
|
out_of_bounds_players[body] = { "time_left": start_time }
|
||||||
|
|
||||||
|
var is_local = false
|
||||||
|
if multiplayer.has_multiplayer_peer():
|
||||||
|
if body.get_multiplayer_authority() == multiplayer.get_unique_id():
|
||||||
|
is_local = true
|
||||||
|
|
||||||
|
if is_local:
|
||||||
|
_is_local_player_out = true
|
||||||
|
_local_player_body = body
|
||||||
|
_create_ui()
|
||||||
|
|
||||||
set_process(true)
|
set_process(true)
|
||||||
|
|
||||||
func _on_body_entered(body: Node3D) -> void:
|
func _on_body_entered(body: Node3D) -> void:
|
||||||
if body == _player_out_of_bounds:
|
if out_of_bounds_players.has(body):
|
||||||
_player_out_of_bounds = null
|
var time_left_val = out_of_bounds_players[body].time_left
|
||||||
_remove_ui()
|
out_of_bounds_players.erase(body)
|
||||||
# set_process(true) stays on to tick the time_safe recovery
|
|
||||||
|
if time_left_val < 5.0:
|
||||||
|
recovery_players[body] = { "time_left": time_left_val, "time_safe": 0.0 }
|
||||||
|
set_process(true)
|
||||||
|
|
||||||
|
if body == _local_player_body and _is_local_player_out:
|
||||||
|
_remove_ui()
|
||||||
|
_is_local_player_out = false
|
||||||
|
_local_player_body = null
|
||||||
|
|
||||||
func _create_ui() -> void:
|
func _create_ui() -> void:
|
||||||
if _ui_layer: return
|
if _ui_layer: return
|
||||||
@@ -83,7 +119,10 @@ func _create_ui() -> void:
|
|||||||
_ui_label.add_theme_color_override("font_color", Color(1, 0.2, 0.2))
|
_ui_label.add_theme_color_override("font_color", Color(1, 0.2, 0.2))
|
||||||
_ui_label.add_theme_color_override("font_outline_color", Color(0, 0, 0))
|
_ui_label.add_theme_color_override("font_outline_color", Color(0, 0, 0))
|
||||||
_ui_label.add_theme_constant_override("outline_size", 8)
|
_ui_label.add_theme_constant_override("outline_size", 8)
|
||||||
_ui_label.text = "WARNING: RETURN TO COMBAT AREA\n%.1f" % time_left
|
var display_time = 5.0
|
||||||
|
if out_of_bounds_players.has(_local_player_body):
|
||||||
|
display_time = out_of_bounds_players[_local_player_body].time_left
|
||||||
|
_ui_label.text = "WARNING: RETURN TO COMBAT AREA\n%.1f" % display_time
|
||||||
|
|
||||||
_ui_layer.add_child(_ui_label)
|
_ui_layer.add_child(_ui_label)
|
||||||
|
|
||||||
@@ -97,15 +136,14 @@ func _remove_ui() -> void:
|
|||||||
_ui_label = null
|
_ui_label = null
|
||||||
_ui_overlay = null
|
_ui_overlay = null
|
||||||
|
|
||||||
func _kill_player() -> void:
|
func _kill_player(body: Node3D) -> void:
|
||||||
if _player_out_of_bounds:
|
if body:
|
||||||
# PlayerMovementController's take_damage requires (amount, source)
|
if multiplayer.is_server():
|
||||||
if _player_out_of_bounds.has_method("take_damage"):
|
if body.has_method("take_damage"):
|
||||||
_player_out_of_bounds.take_damage(9999, Vector3.ZERO)
|
body.take_damage(9999, Vector3.ZERO, null, Vector3.ZERO)
|
||||||
elif "health" in _player_out_of_bounds:
|
|
||||||
_player_out_of_bounds.health = 0
|
|
||||||
if "is_dead" in _player_out_of_bounds:
|
|
||||||
_player_out_of_bounds.is_dead = true
|
|
||||||
|
|
||||||
_player_out_of_bounds = null
|
out_of_bounds_players.erase(body)
|
||||||
_remove_ui()
|
if body == _local_player_body and _is_local_player_out:
|
||||||
|
_remove_ui()
|
||||||
|
_is_local_player_out = false
|
||||||
|
_local_player_body = null
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func _ready() -> void:
|
|||||||
|
|
||||||
_update_label()
|
_update_label()
|
||||||
|
|
||||||
func take_damage(amount: float, hit_position: Vector3, source: Node = null, impulse: Vector3 = Vector3.ZERO) -> void:
|
func take_damage(amount: float, hit_position: Vector3, source: Node = null, _impulse: Vector3 = Vector3.ZERO) -> void:
|
||||||
if is_dead:
|
if is_dead:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -141,14 +141,13 @@ func _die(_hit_position: Vector3, source: Node, damage_amount: float) -> void:
|
|||||||
linear_vel = hit_dir * (damage_amount * 0.02)
|
linear_vel = hit_dir * (damage_amount * 0.02)
|
||||||
impulse = hit_dir * (damage_amount * 0.5)
|
impulse = hit_dir * (damage_amount * 0.5)
|
||||||
|
|
||||||
get_tree().create_timer(0.01).timeout.connect(func():
|
get_tree().create_timer(0.01).timeout.connect(_apply_ragdoll_velocity.bind(ragdoll_instance, linear_vel, impulse))
|
||||||
if is_instance_valid(ragdoll_instance):
|
|
||||||
ragdoll_instance.apply_initial_velocities(linear_vel, impulse)
|
|
||||||
)
|
|
||||||
|
|
||||||
get_tree().create_timer(3.0).timeout.connect(func():
|
get_tree().create_timer(3.0).timeout.connect(_respawn)
|
||||||
_respawn()
|
|
||||||
)
|
func _apply_ragdoll_velocity(ragdoll: Node3D, l_vel: Vector3, imp: Vector3) -> void:
|
||||||
|
if is_instance_valid(ragdoll):
|
||||||
|
ragdoll.apply_initial_velocities(l_vel, imp)
|
||||||
|
|
||||||
func _respawn() -> void:
|
func _respawn() -> void:
|
||||||
if is_instance_valid(ragdoll_instance):
|
if is_instance_valid(ragdoll_instance):
|
||||||
|
|||||||
@@ -185,15 +185,13 @@ func _die(_hit_position: Vector3, source: Node, damage_amount: float) -> void:
|
|||||||
linear_vel = hit_dir * (damage_amount * 0.02)
|
linear_vel = hit_dir * (damage_amount * 0.02)
|
||||||
impulse = hit_dir * (damage_amount * 0.5)
|
impulse = hit_dir * (damage_amount * 0.5)
|
||||||
|
|
||||||
get_tree().create_timer(0.01).timeout.connect(func():
|
get_tree().create_timer(0.01).timeout.connect(_apply_ragdoll_velocity.bind(ragdoll_instance, velocity + linear_vel, impulse))
|
||||||
if is_instance_valid(ragdoll_instance):
|
|
||||||
# The ragdoll also inherits the current walking velocity natively
|
|
||||||
ragdoll_instance.apply_initial_velocities(velocity + linear_vel, impulse)
|
|
||||||
)
|
|
||||||
|
|
||||||
get_tree().create_timer(3.0).timeout.connect(func():
|
get_tree().create_timer(3.0).timeout.connect(_respawn)
|
||||||
_respawn()
|
|
||||||
)
|
func _apply_ragdoll_velocity(ragdoll: Node3D, l_vel: Vector3, imp: Vector3) -> void:
|
||||||
|
if is_instance_valid(ragdoll):
|
||||||
|
ragdoll.apply_initial_velocities(l_vel, imp)
|
||||||
|
|
||||||
func _respawn() -> void:
|
func _respawn() -> void:
|
||||||
if is_instance_valid(ragdoll_instance):
|
if is_instance_valid(ragdoll_instance):
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ var health: float = 100.0
|
|||||||
var max_shield: float = 100.0
|
var max_shield: float = 100.0
|
||||||
var shield: float = 100.0
|
var shield: float = 100.0
|
||||||
var time_since_last_damage: float = 0.0
|
var time_since_last_damage: float = 0.0
|
||||||
|
var death_count: int = 0
|
||||||
var is_dead: bool = false
|
var is_dead: bool = false
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
@@ -325,13 +326,16 @@ func _spawn_remote_projectile(origin: Vector3, fire_dir: Vector3, weapon_name: S
|
|||||||
if camera:
|
if camera:
|
||||||
var wman = camera.get_node_or_null("WeaponManager")
|
var wman = camera.get_node_or_null("WeaponManager")
|
||||||
if wman:
|
if wman:
|
||||||
|
var target_weapon = null
|
||||||
for w in wman.weapons.values():
|
for w in wman.weapons.values():
|
||||||
if "weapon_name" in w and w.weapon_name == weapon_name:
|
if "weapon_name" in w and w.weapon_name == weapon_name:
|
||||||
if w.has_method("_spawn_custom_projectile"):
|
target_weapon = w
|
||||||
w._spawn_custom_projectile(origin, fire_dir)
|
break
|
||||||
return
|
if target_weapon and target_weapon.has_method("_spawn_custom_projectile"):
|
||||||
|
target_weapon._spawn_custom_projectile(origin, fire_dir)
|
||||||
|
return
|
||||||
|
|
||||||
# Fallback if weapon not found (e.g. joined late and weapons didn't build yet)
|
# Generic fallback if weapon not found (e.g. joined late and weapons didn't build yet)
|
||||||
var proj = Node3D.new()
|
var proj = Node3D.new()
|
||||||
proj.name = "RemoteProjectile"
|
proj.name = "RemoteProjectile"
|
||||||
|
|
||||||
@@ -825,17 +829,16 @@ func die(impulse: Vector3 = Vector3.ZERO) -> void:
|
|||||||
col.set_deferred("disabled", true)
|
col.set_deferred("disabled", true)
|
||||||
|
|
||||||
# Spawn true physics ragdoll
|
# Spawn true physics ragdoll
|
||||||
|
death_count += 1
|
||||||
var ragdoll = load("res://characters/procedural_ragdoll.gd").new()
|
var ragdoll = load("res://characters/procedural_ragdoll.gd").new()
|
||||||
|
ragdoll.name = "Ragdoll_%s_%d" % [self.name, death_count]
|
||||||
ragdoll_instance = ragdoll
|
ragdoll_instance = ragdoll
|
||||||
get_tree().current_scene.add_child(ragdoll)
|
get_tree().current_scene.add_child(ragdoll)
|
||||||
ragdoll.global_transform = global_transform
|
ragdoll.global_transform = global_transform
|
||||||
ragdoll.build_ragdoll(Color(0.2, 0.4, 0.8)) # Blueish player color
|
ragdoll.build_ragdoll(Color(0.2, 0.4, 0.8)) # Blueish player color
|
||||||
|
|
||||||
# Wait a frame for physics to initialize then apply velocity
|
# Wait a frame for physics to initialize then apply velocity
|
||||||
get_tree().create_timer(0.01).timeout.connect(func():
|
get_tree().create_timer(0.01).timeout.connect(_apply_ragdoll_velocity.bind(ragdoll, velocity, impulse))
|
||||||
if is_instance_valid(ragdoll):
|
|
||||||
ragdoll.apply_initial_velocities(velocity, impulse)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Move camera to 3rd person view using SpringArm3D
|
# Move camera to 3rd person view using SpringArm3D
|
||||||
if is_instance_valid(camera):
|
if is_instance_valid(camera):
|
||||||
@@ -900,6 +903,10 @@ func die(impulse: Vector3 = Vector3.ZERO) -> void:
|
|||||||
death_screen.visible = true
|
death_screen.visible = true
|
||||||
set_process_input(true)
|
set_process_input(true)
|
||||||
|
|
||||||
|
func _apply_ragdoll_velocity(ragdoll: Node3D, l_vel: Vector3, imp: Vector3) -> void:
|
||||||
|
if is_instance_valid(ragdoll):
|
||||||
|
ragdoll.apply_initial_velocities(l_vel, imp)
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
if not is_multiplayer_authority(): return
|
if not is_multiplayer_authority(): return
|
||||||
if is_dead and death_screen and death_screen.visible:
|
if is_dead and death_screen and death_screen.visible:
|
||||||
@@ -908,11 +915,26 @@ func _input(event: InputEvent) -> void:
|
|||||||
_on_respawn_pressed()
|
_on_respawn_pressed()
|
||||||
|
|
||||||
func _on_respawn_pressed() -> void:
|
func _on_respawn_pressed() -> void:
|
||||||
var spawn_pos = Vector3(randf_range(-5, 5), 5, randf_range(-5, 5))
|
if multiplayer.is_server():
|
||||||
if not multiplayer.has_multiplayer_peer() or multiplayer.multiplayer_peer is OfflineMultiplayerPeer:
|
var spawn_pos = Vector3(randf_range(-5, 5), 5, randf_range(-5, 5))
|
||||||
rpc_respawn(spawn_pos)
|
|
||||||
else:
|
|
||||||
rpc_respawn.rpc(spawn_pos)
|
rpc_respawn.rpc(spawn_pos)
|
||||||
|
else:
|
||||||
|
rpc_request_respawn.rpc_id(1)
|
||||||
|
|
||||||
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
|
func rpc_request_respawn() -> void:
|
||||||
|
if not multiplayer.is_server():
|
||||||
|
return
|
||||||
|
var sender_id = multiplayer.get_remote_sender_id()
|
||||||
|
if sender_id != 1 and sender_id != str(name).to_int():
|
||||||
|
return
|
||||||
|
|
||||||
|
if is_dead:
|
||||||
|
var spawn_pos = Vector3(randf_range(-5, 5), 5, randf_range(-5, 5))
|
||||||
|
rpc_respawn.rpc(spawn_pos)
|
||||||
|
else:
|
||||||
|
# Deal 9999 damage to trigger the full death sequence locally and over the network
|
||||||
|
take_damage(9999, global_position, null, Vector3.ZERO)
|
||||||
|
|
||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func rpc_respawn(spawn_pos: Vector3) -> void:
|
func rpc_respawn(spawn_pos: Vector3) -> void:
|
||||||
|
|||||||
+2
-2
@@ -104,8 +104,8 @@ func _respawn() -> void:
|
|||||||
if spawner:
|
if spawner:
|
||||||
local_player = spawner.get_node_or_null(str(pid))
|
local_player = spawner.get_node_or_null(str(pid))
|
||||||
|
|
||||||
if local_player and local_player.has_method("take_damage"):
|
if local_player and local_player.has_method("rpc_request_respawn"):
|
||||||
local_player.take_damage(9999, Vector3.ZERO)
|
local_player.rpc_request_respawn.rpc_id(1)
|
||||||
|
|
||||||
func _build_ui() -> void:
|
func _build_ui() -> void:
|
||||||
bg = ColorRect.new()
|
bg = ColorRect.new()
|
||||||
|
|||||||
+10
-1
@@ -188,7 +188,16 @@ func _spawn_custom_projectile(origin: Vector3, fire_dir: Vector3) -> void:
|
|||||||
if player:
|
if player:
|
||||||
proj.velocity += fire_dir * max(0.0, player.velocity.dot(fire_dir))
|
proj.velocity += fire_dir * max(0.0, player.velocity.dot(fire_dir))
|
||||||
|
|
||||||
proj.initial_aim_dir = _current_aim_dir
|
var aim_dir = _current_aim_dir
|
||||||
|
# If remote player, use the synced head rotation to determine forward direction
|
||||||
|
if player and not player.is_multiplayer_authority():
|
||||||
|
var head = player.get_node_or_null("HeadPivot")
|
||||||
|
if head:
|
||||||
|
aim_dir = -head.global_transform.basis.z.normalized()
|
||||||
|
else:
|
||||||
|
aim_dir = fire_dir # Fallback
|
||||||
|
|
||||||
|
proj.initial_aim_dir = aim_dir
|
||||||
proj.cruise_speed = 65.0
|
proj.cruise_speed = 65.0
|
||||||
proj.ignition_timer = randf_range(0.3, 0.6) # Staggered ignition
|
proj.ignition_timer = randf_range(0.3, 0.6) # Staggered ignition
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ func _build_loadout() -> void:
|
|||||||
else:
|
else:
|
||||||
is_auth = is_multiplayer_authority()
|
is_auth = is_multiplayer_authority()
|
||||||
|
|
||||||
|
print("DEBUG [", multiplayer.get_unique_id(), "]: _build_loadout is_auth=", is_auth, " player=", str(player.name) if player else "null")
|
||||||
|
|
||||||
if not is_auth:
|
if not is_auth:
|
||||||
return # Let the player_movement_controller's remote _process call _build_remote_loadout
|
return # Let the player_movement_controller's remote _process call _build_remote_loadout
|
||||||
|
|
||||||
@@ -77,6 +79,7 @@ func _build_loadout() -> void:
|
|||||||
weapons.clear()
|
weapons.clear()
|
||||||
|
|
||||||
var l = LoadoutManager.get_active_loadout()
|
var l = LoadoutManager.get_active_loadout()
|
||||||
|
print("DEBUG [", multiplayer.get_unique_id(), "]: Loadout primary1=", l["primary_1"])
|
||||||
|
|
||||||
if player:
|
if player:
|
||||||
player.synced_loadout_p1 = l["primary_1"]
|
player.synced_loadout_p1 = l["primary_1"]
|
||||||
@@ -112,15 +115,18 @@ func _build_remote_loadout(p1: String, p2: String, sp: String, melee: String) ->
|
|||||||
|
|
||||||
func _spawn_weapon(slot: int, weapon_id: String) -> void:
|
func _spawn_weapon(slot: int, weapon_id: String) -> void:
|
||||||
if weapon_id == "" or weapon_id == "none" or not LoadoutManager.weapon_db.has(weapon_id):
|
if weapon_id == "" or weapon_id == "none" or not LoadoutManager.weapon_db.has(weapon_id):
|
||||||
|
print("DEBUG [", multiplayer.get_unique_id(), "]: _spawn_weapon failed check for ", weapon_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
var w_data = LoadoutManager.weapon_db[weapon_id]
|
var w_data = LoadoutManager.weapon_db[weapon_id]
|
||||||
var script_path = w_data["script"]
|
var script_path = w_data["script"]
|
||||||
if script_path == "":
|
if script_path == "":
|
||||||
|
print("DEBUG [", multiplayer.get_unique_id(), "]: _spawn_weapon failed script path for ", weapon_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
var script = load(script_path)
|
var script = load(script_path)
|
||||||
if script:
|
if script:
|
||||||
|
print("DEBUG [", multiplayer.get_unique_id(), "]: Successfully loaded script, adding weapon ", weapon_id)
|
||||||
var w = script.new()
|
var w = script.new()
|
||||||
w.name = "Weapon_" + str(slot) + "_" + weapon_id
|
w.name = "Weapon_" + str(slot) + "_" + weapon_id
|
||||||
# Assuming all weapons have player and camera vars
|
# Assuming all weapons have player and camera vars
|
||||||
|
|||||||
Reference in New Issue
Block a user