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")
|
||||
func request_lift_pot(pot_path: NodePath, peer_id: int):
|
||||
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))
|
||||
if pot and "lift" in pot and pot.liftable and player:
|
||||
# Clear grabbed entity if it's not the pot we're lifting
|
||||
if player.grabbed_entity != null and player.grabbed_entity != pot and "release" in player.grabbed_entity:
|
||||
player.grabbed_entity.release()
|
||||
if player.grabbed_entity != pot:
|
||||
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_lifting = true
|
||||
# Set held_entity_path directly on server, use RPC for clients
|
||||
player.held_entity_path = str(pot.get_path())
|
||||
# Send RPC to all other players (excluding server)
|
||||
var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children()
|
||||
for p in all_players:
|
||||
if p != player and p.has_method("set_held_entity_path_rpc"):
|
||||
p.set_held_entity_path_rpc.rpc(str(pot.get_path()))
|
||||
pot.lift(player)
|
||||
# Set animation on the player who is lifting (the joiner)
|
||||
player.current_animation = "LIFT"
|
||||
# Sync animation to all clients
|
||||
player.sync_animation.rpc("LIFT")
|
||||
# Pot state is auto-synced via @export variables, no need for manual sync
|
||||
if entity and player:
|
||||
# Check if entity is a pot or a player
|
||||
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
|
||||
|
||||
# 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("")
|
||||
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()
|
||||
if player.grabbed_entity != entity:
|
||||
player.grabbed_entity = null
|
||||
player.is_grabbing = false
|
||||
# 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
|
||||
player.held_entity_path = str(entity.get_path())
|
||||
# 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()
|
||||
for p in all_players:
|
||||
if p != player and p.has_method("set_held_entity_path_rpc"):
|
||||
p.set_held_entity_path_rpc.rpc(str(entity.get_path()))
|
||||
|
||||
# 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)
|
||||
player.current_animation = "LIFT"
|
||||
# Sync animation to all clients
|
||||
player.sync_animation.rpc("LIFT")
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
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")
|
||||
func request_throw_pot(pot_path: NodePath, peer_id: int, direction: Vector2):
|
||||
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))
|
||||
if pot and player:
|
||||
# Check if the pot 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):
|
||||
pot.throw(direction)
|
||||
if entity and player:
|
||||
# Check if the entity (pot or player) is being held by this player
|
||||
var is_held = false
|
||||
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)
|
||||
player.set_held_entity_path_rpc.rpc("")
|
||||
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)
|
||||
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:
|
||||
#print("we are below", knockback_timer, ", delta was:", delta)
|
||||
stats.is_invulnerable = false
|
||||
# Sync invulnerability cleared to clients
|
||||
if multiplayer.is_server():
|
||||
sync_invulnerability.rpc(false)
|
||||
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:
|
||||
# this only plays on server.... must play on client also somehow...
|
||||
if !$SfxWalk.playing and $SfxWalk/TimerWalk.is_stopped():
|
||||
@@ -769,19 +779,40 @@ func die_sound():
|
||||
pass
|
||||
|
||||
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:
|
||||
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.is_invulnerable = true
|
||||
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":
|
||||
time_since_last_frame = 0
|
||||
current_frame = 0
|
||||
|
||||
current_animation = "DAMAGE"
|
||||
|
||||
# Calculate knockback direction from the damaging body
|
||||
knockback_direction = (global_position - iBody.global_position).normalized()
|
||||
# Calculate knockback direction from the damaging body position
|
||||
knockback_direction = (global_position - damager_position).normalized()
|
||||
velocity = knockback_direction * knockback_strength
|
||||
_updateHp()
|
||||
if held_entity != null:
|
||||
@@ -819,7 +850,7 @@ func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void:
|
||||
pass
|
||||
else:
|
||||
_iByWhoOrWhat.stats.forceUpdate()
|
||||
'
|
||||
'
|
||||
|
||||
# give score to other player...
|
||||
if current_animation != "DIE":
|
||||
@@ -831,6 +862,46 @@ func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void:
|
||||
#_updateScore.rpc()
|
||||
call_deferred("_on_died")
|
||||
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")
|
||||
func sync_player_kills(iKills: int):
|
||||
@@ -854,6 +925,16 @@ func sync_animation(animation_name: String):
|
||||
current_animation = animation_name
|
||||
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("reliable")
|
||||
|
||||
@@ -817,7 +817,26 @@ func _on_area_2d_collision_body_entered(body: Node2D) -> void:
|
||||
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:
|
||||
body.take_damage(self, thrown_by)
|
||||
# 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)
|
||||
elif collider != self and "breakPot" in collider:
|
||||
collider.take_damage(self, thrown_by)
|
||||
# create particles from pot:
|
||||
|
||||
Reference in New Issue
Block a user