now both players can take damage
This commit is contained in:
@@ -233,42 +233,40 @@ func broadcast_chat_message(message: String):
|
|||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func request_lift_pot(pot_path: NodePath, peer_id: int):
|
func request_lift_pot(pot_path: NodePath, peer_id: int):
|
||||||
if multiplayer.is_server():
|
if multiplayer.is_server():
|
||||||
var pot = get_node_or_null(pot_path)
|
var entity = get_node_or_null(pot_path)
|
||||||
var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id))
|
var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id))
|
||||||
if pot and "lift" in pot and pot.liftable and player:
|
if entity and player:
|
||||||
# Clear grabbed entity if it's not the pot we're lifting
|
# Check if entity is a pot or a player
|
||||||
if player.grabbed_entity != null and player.grabbed_entity != pot and "release" in player.grabbed_entity:
|
var is_pot = "lift" in entity and "liftable" in entity and entity.liftable
|
||||||
|
var is_player_entity = "is_player" in entity and entity.is_player and not entity.is_being_lifted
|
||||||
|
|
||||||
|
if (is_pot or is_player_entity):
|
||||||
|
# Clear grabbed entity if it's not the entity we're lifting
|
||||||
|
if player.grabbed_entity != null and player.grabbed_entity != entity and "release" in player.grabbed_entity:
|
||||||
player.grabbed_entity.release()
|
player.grabbed_entity.release()
|
||||||
if player.grabbed_entity != pot:
|
if player.grabbed_entity != entity:
|
||||||
player.grabbed_entity = null
|
player.grabbed_entity = null
|
||||||
# Don't clear grabbed_entity_path if we're lifting the same pot
|
|
||||||
# This prevents the pot from being released when we're trying to lift it
|
|
||||||
player.is_grabbing = false
|
player.is_grabbing = false
|
||||||
player.is_lifting = true
|
# DON'T clear is_lifting - it should stay true while holding the pot
|
||||||
|
# is_lifting will be cleared when player releases button or throws
|
||||||
# Set held_entity_path directly on server, use RPC for clients
|
# Set held_entity_path directly on server, use RPC for clients
|
||||||
player.held_entity_path = str(pot.get_path())
|
player.held_entity_path = str(entity.get_path())
|
||||||
# Send RPC to all other players (excluding server)
|
# Send RPC to the requesting player (joiner) to sync held_entity_path
|
||||||
|
player.set_held_entity_path_rpc.rpc(str(entity.get_path()))
|
||||||
|
# Send RPC to all other players (excluding server and requesting player)
|
||||||
var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children()
|
var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children()
|
||||||
for p in all_players:
|
for p in all_players:
|
||||||
if p != player and p.has_method("set_held_entity_path_rpc"):
|
if p != player and p.has_method("set_held_entity_path_rpc"):
|
||||||
p.set_held_entity_path_rpc.rpc(str(pot.get_path()))
|
p.set_held_entity_path_rpc.rpc(str(entity.get_path()))
|
||||||
pot.lift(player)
|
|
||||||
|
# Call lift on the entity (works for both pot and player)
|
||||||
|
if "lift" in entity:
|
||||||
|
entity.lift(player)
|
||||||
|
|
||||||
# Set animation on the player who is lifting (the joiner)
|
# Set animation on the player who is lifting (the joiner)
|
||||||
player.current_animation = "LIFT"
|
player.current_animation = "LIFT"
|
||||||
# Sync animation to all clients
|
# Sync animation to all clients
|
||||||
player.sync_animation.rpc("LIFT")
|
player.sync_animation.rpc("LIFT")
|
||||||
# Pot state is auto-synced via @export variables, no need for manual sync
|
|
||||||
|
|
||||||
# Sync animation and entity to all clients
|
|
||||||
#print("MultiplayerManager syncing LIFT animation and held_entity to all clients")
|
|
||||||
# Send RPC to all players except the server (since server already has correct state)
|
|
||||||
#var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children()
|
|
||||||
#for p in all_players:
|
|
||||||
#if p.has_method("sync_animation") and p.get_multiplayer_authority() != 1:
|
|
||||||
#print("MultiplayerManager syncing to player: ", p.name)
|
|
||||||
#p.sync_animation.rpc("LIFT")
|
|
||||||
#p.sync_held_entity.rpc(str(pot.get_path()))
|
|
||||||
#p.sync_grabbed_entity.rpc("")
|
|
||||||
|
|
||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func request_put_down_pot(pot_path: NodePath, peer_id: int):
|
func request_put_down_pot(pot_path: NodePath, peer_id: int):
|
||||||
@@ -287,16 +285,22 @@ func request_put_down_pot(pot_path: NodePath, peer_id: int):
|
|||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func request_throw_pot(pot_path: NodePath, peer_id: int, direction: Vector2):
|
func request_throw_pot(pot_path: NodePath, peer_id: int, direction: Vector2):
|
||||||
if multiplayer.is_server():
|
if multiplayer.is_server():
|
||||||
var pot = get_node_or_null(pot_path)
|
var entity = get_node_or_null(pot_path)
|
||||||
var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id))
|
var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id))
|
||||||
if pot and player:
|
if entity and player:
|
||||||
# Check if the pot is being held by this player
|
# Check if the entity (pot or player) is being held by this player
|
||||||
if pot.holder_peer_id == peer_id or (pot.holder != null and pot.holder.get_multiplayer_authority() == peer_id):
|
var is_held = false
|
||||||
pot.throw(direction)
|
if "holder_peer_id" in entity:
|
||||||
|
is_held = entity.holder_peer_id == peer_id or (entity.holder != null and entity.holder.get_multiplayer_authority() == peer_id)
|
||||||
|
elif "is_being_lifted" in entity and entity.is_being_lifted:
|
||||||
|
is_held = entity.holder_peer_id == peer_id or (entity.holder != null and entity.holder.get_multiplayer_authority() == peer_id)
|
||||||
|
|
||||||
|
if is_held and "throw" in entity:
|
||||||
|
entity.throw(direction)
|
||||||
# Use RPC to clear held_entity_path on all players (including server)
|
# Use RPC to clear held_entity_path on all players (including server)
|
||||||
player.set_held_entity_path_rpc.rpc("")
|
player.set_held_entity_path_rpc.rpc("")
|
||||||
player.current_animation = "THROW"
|
player.current_animation = "THROW"
|
||||||
# Pot state is auto-synced via @export variables, no need for manual sync
|
# Entity state is auto-synced via @export variables, no need for manual sync
|
||||||
|
|
||||||
# Sync animation to all clients (entity sync is handled automatically by PlayerSynchronizer)
|
# Sync animation to all clients (entity sync is handled automatically by PlayerSynchronizer)
|
||||||
var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children()
|
var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children()
|
||||||
|
|||||||
@@ -285,7 +285,17 @@ func _physics_process(delta: float) -> void:
|
|||||||
if stats.hp > 0:
|
if stats.hp > 0:
|
||||||
#print("we are below", knockback_timer, ", delta was:", delta)
|
#print("we are below", knockback_timer, ", delta was:", delta)
|
||||||
stats.is_invulnerable = false
|
stats.is_invulnerable = false
|
||||||
|
# Sync invulnerability cleared to clients
|
||||||
|
if multiplayer.is_server():
|
||||||
|
sync_invulnerability.rpc(false)
|
||||||
move_and_collide(velocity * delta)
|
move_and_collide(velocity * delta)
|
||||||
|
else:
|
||||||
|
# Client also needs to decrement knockback_timer for visual effects
|
||||||
|
if knockback_timer > 0:
|
||||||
|
velocity = velocity.move_toward(Vector2.ZERO, 300 * delta)
|
||||||
|
knockback_timer -= delta
|
||||||
|
if knockback_timer <= 0.0:
|
||||||
|
knockback_timer = 0
|
||||||
if is_moving:
|
if is_moving:
|
||||||
# this only plays on server.... must play on client also somehow...
|
# this only plays on server.... must play on client also somehow...
|
||||||
if !$SfxWalk.playing and $SfxWalk/TimerWalk.is_stopped():
|
if !$SfxWalk.playing and $SfxWalk/TimerWalk.is_stopped():
|
||||||
@@ -769,19 +779,40 @@ func die_sound():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void:
|
func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void:
|
||||||
|
# Direct call version - used when called locally
|
||||||
|
var damager_pos = iBody.global_position if iBody != null else global_position
|
||||||
|
var damager_path = _iByWhoOrWhat.get_path() if _iByWhoOrWhat != null else ""
|
||||||
|
_take_damage_internal(damager_pos, damager_path, _iByWhoOrWhat)
|
||||||
|
|
||||||
|
@rpc("any_peer", "reliable")
|
||||||
|
func take_damage_rpc(damager_position: Vector2, damager_path: String, damager_peer_id: int):
|
||||||
|
# RPC version - used when server calls on joiner's player node
|
||||||
|
# Get the actual damager node if we can
|
||||||
|
var _iByWhoOrWhat = null
|
||||||
|
if damager_path != "":
|
||||||
|
_iByWhoOrWhat = get_node_or_null(damager_path)
|
||||||
|
_take_damage_internal(damager_position, damager_path, _iByWhoOrWhat)
|
||||||
|
|
||||||
|
func _take_damage_internal(damager_position: Vector2, damager_path: String, _iByWhoOrWhat: Node2D):
|
||||||
|
# Internal function that handles the actual damage logic
|
||||||
|
# This allows both direct calls and RPC calls to work
|
||||||
if !stats.is_invulnerable:
|
if !stats.is_invulnerable:
|
||||||
take_dmg_sound.rpc()
|
# Apply damage (works on both server and client since each player has authority over themselves)
|
||||||
stats.take_damage(13.0)
|
stats.take_damage(13.0)
|
||||||
stats.is_invulnerable = true
|
stats.is_invulnerable = true
|
||||||
knockback_timer = knockback_duration
|
knockback_timer = knockback_duration
|
||||||
|
|
||||||
|
# Sync invulnerability state to all clients (so other players know this player is invulnerable)
|
||||||
|
sync_invulnerability.rpc(true)
|
||||||
|
|
||||||
if current_animation != "DAMAGE":
|
if current_animation != "DAMAGE":
|
||||||
time_since_last_frame = 0
|
time_since_last_frame = 0
|
||||||
current_frame = 0
|
current_frame = 0
|
||||||
|
|
||||||
current_animation = "DAMAGE"
|
current_animation = "DAMAGE"
|
||||||
|
|
||||||
# Calculate knockback direction from the damaging body
|
# Calculate knockback direction from the damaging body position
|
||||||
knockback_direction = (global_position - iBody.global_position).normalized()
|
knockback_direction = (global_position - damager_position).normalized()
|
||||||
velocity = knockback_direction * knockback_strength
|
velocity = knockback_direction * knockback_strength
|
||||||
_updateHp()
|
_updateHp()
|
||||||
if held_entity != null:
|
if held_entity != null:
|
||||||
@@ -832,6 +863,46 @@ func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void:
|
|||||||
call_deferred("_on_died")
|
call_deferred("_on_died")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@rpc("any_peer", "reliable")
|
||||||
|
func take_damage_effects(damager_position: Vector2, damager_path: String):
|
||||||
|
# Client receives damage effects - play visual/audio but don't modify stats
|
||||||
|
# Note: is_invulnerable is already set to true by sync_invulnerability RPC
|
||||||
|
# We just need to set knockback_timer for visual effects
|
||||||
|
take_dmg_sound.rpc()
|
||||||
|
knockback_timer = knockback_duration
|
||||||
|
|
||||||
|
if current_animation != "DAMAGE":
|
||||||
|
time_since_last_frame = 0
|
||||||
|
current_frame = 0
|
||||||
|
|
||||||
|
current_animation = "DAMAGE"
|
||||||
|
|
||||||
|
# Calculate knockback direction from the damaging body position
|
||||||
|
knockback_direction = (global_position - damager_position).normalized()
|
||||||
|
velocity = knockback_direction * knockback_strength
|
||||||
|
|
||||||
|
if held_entity != null:
|
||||||
|
if multiplayer.is_server():
|
||||||
|
held_entity.throw(knockback_direction)
|
||||||
|
else:
|
||||||
|
MultiplayerManager.request_throw_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id(), knockback_direction)
|
||||||
|
held_entity = null
|
||||||
|
held_entity_path = ""
|
||||||
|
is_lifting = false
|
||||||
|
|
||||||
|
if grabbed_entity != null and "release" in grabbed_entity:
|
||||||
|
grabbed_entity.release()
|
||||||
|
grabbed_entity = null
|
||||||
|
is_grabbing = false
|
||||||
|
|
||||||
|
# Create damage number
|
||||||
|
if damage_number_scene:
|
||||||
|
var damage_number = damage_number_scene.instantiate()
|
||||||
|
get_tree().current_scene.call_deferred("add_child", damage_number)
|
||||||
|
damage_number.global_position = global_position + Vector2(0, -16)
|
||||||
|
damage_number.direction = Vector2(0, -1)
|
||||||
|
damage_number.label = "1"
|
||||||
|
|
||||||
@rpc("call_local", "reliable")
|
@rpc("call_local", "reliable")
|
||||||
func sync_player_kills(iKills: int):
|
func sync_player_kills(iKills: int):
|
||||||
stats.kills = iKills
|
stats.kills = iKills
|
||||||
@@ -854,6 +925,16 @@ func sync_animation(animation_name: String):
|
|||||||
current_animation = animation_name
|
current_animation = animation_name
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@rpc("any_peer", "reliable")
|
||||||
|
func sync_invulnerability(is_invul: bool):
|
||||||
|
# Sync invulnerability state from server to clients
|
||||||
|
# This ensures both server and client have the same invulnerability state
|
||||||
|
stats.is_invulnerable = is_invul
|
||||||
|
# If invulnerability is cleared, also clear knockback_timer on client
|
||||||
|
if not is_invul:
|
||||||
|
knockback_timer = 0
|
||||||
|
pass
|
||||||
|
|
||||||
# RPC functions removed - entity synchronization now handled by setter functions
|
# RPC functions removed - entity synchronization now handled by setter functions
|
||||||
|
|
||||||
@rpc("reliable")
|
@rpc("reliable")
|
||||||
|
|||||||
@@ -817,6 +817,25 @@ func _on_area_2d_collision_body_entered(body: Node2D) -> void:
|
|||||||
if abs(velocity.x) > 0.05 or abs(velocity.y) > 0.05:
|
if abs(velocity.x) > 0.05 or abs(velocity.y) > 0.05:
|
||||||
if "take_damage" in body or body is TileMapLayer or collider is TileMapLayer:
|
if "take_damage" in body or body is TileMapLayer or collider is TileMapLayer:
|
||||||
if "take_damage" in body:
|
if "take_damage" in body:
|
||||||
|
# Check if body is a player - if so, check if it's a joiner (needs RPC) or server (direct call)
|
||||||
|
# Otherwise, call directly (for enemies, etc.)
|
||||||
|
if "is_player" in body and body.is_player:
|
||||||
|
# Player - check if it's a joiner (needs RPC) or server (direct call)
|
||||||
|
# If the player's authority matches the server's unique ID, it's the server's own player
|
||||||
|
var player_authority = body.get_multiplayer_authority()
|
||||||
|
var is_server_player = (player_authority == 1) # Server's peer ID is 1
|
||||||
|
|
||||||
|
if is_server_player:
|
||||||
|
# Server's own player - call directly (server has authority)
|
||||||
|
body.take_damage(self, thrown_by)
|
||||||
|
else:
|
||||||
|
# Joiner player - use RPC with serializable parameters (joiner has authority)
|
||||||
|
var damager_pos = self.global_position
|
||||||
|
var damager_path = thrown_by.get_path() if thrown_by != null else ""
|
||||||
|
var damager_peer_id = thrown_by.get_multiplayer_authority() if thrown_by != null else 0
|
||||||
|
body.take_damage_rpc.rpc(damager_pos, damager_path, damager_peer_id)
|
||||||
|
else:
|
||||||
|
# Non-player (enemy, etc.) - call directly
|
||||||
body.take_damage(self, thrown_by)
|
body.take_damage(self, thrown_by)
|
||||||
elif collider != self and "breakPot" in collider:
|
elif collider != self and "breakPot" in collider:
|
||||||
collider.take_damage(self, thrown_by)
|
collider.take_damage(self, thrown_by)
|
||||||
|
|||||||
Reference in New Issue
Block a user