extends CharacterBody3D class_name BaseUnit ## Base class for all units (players, enemies, NPCs) in the game ## Provides common functionality like health management and taking damage signal health_changed(old_health: float, new_health: float) signal died(killer_id: int) signal respawned() @export var max_health: float = 100.0 @export var can_respawn: bool = true @export var respawn_delay: float = 3.0 var current_health: float = 100.0 var is_dead: bool = false var _respawn_point: Vector3 = Vector3.ZERO func _ready(): current_health = max_health _respawn_point = global_position func _enter_tree(): set_multiplayer_authority(str(name).to_int()) ## Take damage from an attacker ## Should only be called on the server for authority @rpc("any_peer", "reliable") func take_damage(amount: float, attacker_id: int = -1): # Only server can process damage if not multiplayer.is_server(): return if is_dead: return var old_health = current_health current_health = max(0, current_health - amount) # Broadcast health change to all clients rpc("sync_health", current_health) health_changed.emit(old_health, current_health) if current_health <= 0: _die(attacker_id) ## Heal the unit @rpc("any_peer", "reliable") func heal(amount: float): if not multiplayer.is_server(): return if is_dead: return var old_health = current_health current_health = min(max_health, current_health + amount) rpc("sync_health", current_health) health_changed.emit(old_health, current_health) ## Sync health across all clients @rpc("any_peer", "call_local", "reliable") func sync_health(new_health: float): var old_health = current_health current_health = new_health health_changed.emit(old_health, current_health) ## Handle death func _die(killer_id: int): if is_dead: return is_dead = true died.emit(killer_id) rpc("sync_death", killer_id) if can_respawn and multiplayer.is_server(): await get_tree().create_timer(respawn_delay).timeout _respawn() ## Sync death state to all clients @rpc("any_peer", "call_local", "reliable") func sync_death(killer_id: int): is_dead = true died.emit(killer_id) # Subclasses should override to add visual effects, disable collision, etc. ## Respawn the unit func _respawn(): if not multiplayer.is_server(): return is_dead = false current_health = max_health global_position = _respawn_point velocity = Vector3.ZERO rpc("sync_respawn", _respawn_point) respawned.emit() ## Sync respawn to all clients @rpc("any_peer", "call_local", "reliable") func sync_respawn(spawn_pos: Vector3): is_dead = false current_health = max_health global_position = spawn_pos velocity = Vector3.ZERO respawned.emit() ## Set the respawn point func set_respawn_point(point: Vector3): _respawn_point = point ## Get health percentage (0.0 to 1.0) func get_health_percent() -> float: return current_health / max_health if max_health > 0 else 0.0