MultiplayerFighter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
3.1 KiB

extends Node3D
class_name BaseWeapon
## Base class for equipped weapons
## Attached to player's hand via BoneAttachment3D
## Provides common weapon functionality and stats
signal attack_performed()
@export var weapon_data: WeaponData
# Runtime references
var owner_character: Character = null
var _mesh_instance: Node3D = null
var _attack_timer: float = 0.0
func _ready():
if weapon_data and weapon_data.mesh_scene:
_spawn_mesh()
func _process(delta):
if _attack_timer > 0:
_attack_timer -= delta
## Spawn the visual mesh for this weapon
func _spawn_mesh():
# Remove old mesh if exists
if _mesh_instance:
_mesh_instance.queue_free()
# Instantiate new mesh
_mesh_instance = weapon_data.mesh_scene.instantiate()
add_child(_mesh_instance)
## Perform an attack with this weapon
## Called by the character who owns this weapon
func perform_attack() -> bool:
if not weapon_data or not owner_character:
return false
# Check cooldown
if _attack_timer > 0:
return false
_attack_timer = weapon_data.attack_cooldown
# Play attack animation on owner
if owner_character._body:
owner_character._body.play_attack()
# Find targets in range
_find_and_damage_targets()
attack_performed.emit()
return true
## Find targets in range and apply damage
func _find_and_damage_targets():
if not owner_character:
return
# Check if the owner character has authority (not this node)
if not owner_character.is_multiplayer_authority():
return
var space_state = get_world_3d().direct_space_state
var query = PhysicsShapeQueryParameters3D.new()
var sphere = SphereShape3D.new()
sphere.radius = weapon_data.attack_range
query.shape = sphere
query.transform = global_transform
query.collision_mask = 1 # Player layer
var results = space_state.intersect_shape(query)
for result in results:
var hit_body = result["collider"]
if hit_body != owner_character and hit_body is BaseUnit:
var attacker_id = multiplayer.get_unique_id()
# If we're the server, apply damage directly
if multiplayer.is_server():
owner_character._server_apply_damage(
hit_body.name,
weapon_data.damage,
attacker_id,
weapon_data.knockback_force,
owner_character.global_position
)
else:
# Otherwise, request server to apply damage
owner_character.rpc_id(1, "_server_apply_damage",
hit_body.name,
weapon_data.damage,
attacker_id,
weapon_data.knockback_force,
owner_character.global_position
)
break # Only hit one target per attack
## Check if weapon can attack
func can_attack() -> bool:
return _attack_timer <= 0
## Set the character who owns this weapon
func set_owner_character(character: Character):
owner_character = character
## Get weapon stats
func get_damage() -> float:
return weapon_data.damage if weapon_data else 0.0
func get_range() -> float:
return weapon_data.attack_range if weapon_data else 0.0
func get_cooldown() -> float:
return weapon_data.attack_cooldown if weapon_data else 0.0
## Blocking functionality
func can_block() -> bool:
return weapon_data.can_block if weapon_data else false
func get_block_reduction() -> float:
return weapon_data.block_reduction if weapon_data else 0.0