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.
112 lines
3.0 KiB
112 lines
3.0 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) |
|
else: |
|
# Otherwise, request server to apply damage |
|
owner_character.rpc_id(1, "_server_apply_damage", hit_body.name, weapon_data.damage, attacker_id) |
|
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
|
|
|