Base Weapons
Place in world, pick up and drop working on multiplayer
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
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
|
||||
Reference in New Issue
Block a user