Player Damage and Respawn Issue in Multiplayer Battle Royale Game

Godot Version

v.4.3.stable

Question

I’m working on a multiplayer battle royale-style game, where Player A can deal damage to Player B. When this happens, Player B should get killed and then respawn at a random location after 10 seconds. However, my code isn’t behaving as expected.

The issue is that when Player A inflicts damage on Player B, Player A (the attacker) gets killed and respawns instead. Additionally, the 10-second respawn timer only works the first time. After that, players don’t respawn at all.

I’ve been stuck on this problem for a while, and no matter what changes I make, the result is always the same. I need help identifying what’s going wrong and how to fix it. Here’s my current player code:

extends CharacterBody2D

@export var speed: int
@onready var camera = $Camera2D as Camera2D
var mafia: bool = false
var respawning: bool
var dead: bool

Texture identifier mapping

var texture_map = {
“green”: “res://characters/green_character.png”,
“purple”: “res://characters/purple_character.png”,
“red”: “res://characters/red_character.png”,
“yellow”: “res://characters/yellow_character.png”
}

var item = [“axe”, “arrow”, “dagger”, “pole”]

Synchronization variables

var syncPos: Vector2
var syncRot = 0
var syncWeaponRot = 0
var syncTextureID: String # Texture identifier (instead of full texture path)
var syncWeaponID: String # Weapon Identifier
var syncVisibility: bool
var syncDisableCollision: bool
var syncName: String
var syncArrowPos: Vector2
var syncArrowState: bool
var syncDaggerRot = 0
var arrow_scene: PackedScene
var arrow_projectile

@onready var arrow = $Arrow
@onready var axe = $Axe
@onready var pole = $Pole
@onready var sword = $Sword
@onready var dagger = $Dagger

Randomly select skin and weapon

var skin = [“green”, “purple”, “red”, “yellow”][randi_range(0, 3)]
var weapon = ‘dagger’ #item[randi_range(0, item.size() - 1)]

var loaded_bow
var empty_bow
var loaded: bool
var can_shoot: bool

func _ready():

Initializes the player with a randomly selected texture and weapon.

arrow.visible = false
axe.visible = false
pole.visible = false
sword.visible = false
dagger.visible = false
respawning = false
GlobalScript.dead = false
$CanvasLayer.visible = false

Assign the multiplayer authority

$MultiplayerSynchronizer.set_multiplayer_authority(str(name).to_int())

for i in GameManager.Players:
    if i == multiplayer.get_unique_id():
    syncName = GameManager.Players[i].name

if weapon in item:
   syncWeaponID = weapon

Assign texture and syncTextureID

if skin in texture_map:
    syncTextureID = skin  # Store texture identifier (e.g., "green")
    $Body.texture = load(texture_map[skin])  # Load texture using the identifier

Initialize weapon visibility and textures

if weapon == "arrow":
   arrow.visible = true
   loaded = true
   can_shoot = true

   arrow_scene = load("res://arrow.tscn")
   loaded_bow = load("res://items/weapon_bow_arrow.png")
   empty_bow = load("res://items/weapon_bow.png")

  $Arrow/Weapon.texture = loaded_bow
  $"Arrow/Left Hand".texture = load("res://characters/"+skin+"_hand.png")
  $"Arrow/Right Hand".texture = load("res://characters/"+skin+"_hand.png")

elif weapon == "pole":
   pole.visible = true
   $"Pole/Left Hand".texture = load("res://characters/"+skin+"_hand.png")
   $"Pole/Right Hand".texture = load("res://characters/"+skin+"_hand.png")

elif weapon == "dagger":
   dagger.visible = true
   $"Dagger/Left Hand".texture = load("res://characters/"+skin+"_hand.png")
   $"Dagger/Right Hand".texture = load("res://characters/"+skin+"_hand.png")

elif weapon == "axe":
   axe.visible = true
   $"Axe/Left Hand".texture = load("res://characters/"+skin+"_hand.png")
   $"Axe/Right Hand".texture = load("res://characters/"+skin+"_hand.png")

func launch_arrow():
if !loaded:
$Arrow/Weapon.texture = empty_bow
can_shoot = false
arrow_projectile = arrow_scene.instantiate()
$Arrow.add_child(arrow_projectile)
arrow_projectile.global_position = $Arrow/Marker2D.global_position
syncArrowPos = arrow_projectile.global_position
$WeaponTimer.start(0.25)

func is_dead():
GlobalScript.Player_target = ‘’
GlobalScript.respawn_counter = 10
GlobalScript.dead = true

func respawn():
respawning = true

if GlobalScript.dead and GlobalScript.respawn_counter > 0:
   $RespawnTimer.start()
else:
   $RespawnTimer.stop()
   var spawn_nodes_count = get_tree().get_node_count_in_group("PlayerSpawn")
   for pos in get_tree().get_nodes_in_group("PlayerSpawn"):
       var picker = randi_range(0, (spawn_nodes_count - 1))
       if pos.name == str(picker):
           global_position = pos.global_position
           GlobalScript.dead = false
       break

func _physics_process(delta):
print(GlobalScript.dead)

Handles movement, rotation, and synchronization.

if $MultiplayerSynchronizer.get_multiplayer_authority() == multiplayer.get_unique_id():
   camera.make_current()
   $MarginContainer/Name.text = 'You'

   if GlobalScript.dead:
       syncVisibility = false
       $CollisionShape2D.disabled = true
       syncDisableCollision = true
       $CanvasLayer.visible = true
       if !respawning:
          respawn()

   elif !GlobalScript.dead:
       $CanvasLayer.visible = false
       syncVisibility = true
       $CollisionShape2D.disabled = false
       syncDisableCollision = false
       syncArrowState = loaded

   if loaded:
       $Arrow/Weapon.texture = loaded_bow

   var direction = Input.get_vector("left", "right", "up", "down")
   velocity = direction * speed

   if velocity.length() > 0:
       velocity = velocity.normalized() * speed

   position += velocity * delta
   self.look_at(Vector2(get_global_mouse_position()))
   syncRot = rotation_degrees

   move_and_slide()
   if Input.is_action_just_pressed("attack"):
       if weapon == "arrow":
          if can_shoot:
              loaded = false
              launch_arrow()

else:
   self.visible = syncVisibility
   $CollisionShape2D.disabled = syncDisableCollision

   if syncName != '' and syncName != ' ':
       $MarginContainer/Name.text = syncName
   else:
       $MarginContainer/Name.text = "Dick Head"

   rotation_degrees = lerpf(rotation_degrees, syncRot, 1)

   arrow.visible = false
   axe.visible = false
   pole.visible = false
   sword.visible = false
   dagger.visible = false

   if syncTextureID in texture_map:
       $Body.texture = load(texture_map[syncTextureID])

   if syncWeaponID in item:
       weapon = syncWeaponID

   if weapon == "arrow":
       arrow.visible = true
       loaded_bow = load("res://items/weapon_bow_arrow.png")
       empty_bow = load("res://items/weapon_bow.png")
       loaded = syncArrowState
       if loaded:
          $Arrow/Weapon.texture = loaded_bow
       else:
          $Arrow/Weapon.texture = empty_bow
          $"Arrow/Left Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")
          $"Arrow/Right Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")

   elif weapon == "pole":
       pole.visible = true
       $"Pole/Left Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")
       $"Pole/Right Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")

   elif weapon == "dagger":
       dagger.visible = true
       $"Dagger/Left Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")
       $"Dagger/Right Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")
   $Dagger/dagger.rotation_degrees = lerpf(rotation_degrees, syncDaggerRot, .8)

   elif weapon == "axe":
       axe.visible = true
       $"Axe/Left Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")
       $"Axe/Right Hand".texture = load("res://characters/"+syncTextureID+"_hand.png")

func _on_weapon_timer_timeout() → void:
if !dead and weapon == “arrow”:
can_shoot = true
loaded = true

func _on_respawn_timer_timeout():
if GlobalScript.respawn_counter != 0:
$CanvasLayer/RespawnTimerNotification.text = 'Time to respawn: ’ + str(GlobalScript.respawn_counter) + ‘s’
GlobalScript.respawn_counter -= 1
else:
respawn()