|
|
|
@ -15,6 +15,7 @@ var owner_character: Character = null |
|
|
|
var _mesh_instance: Node3D = null |
|
|
|
var _mesh_instance: Node3D = null |
|
|
|
var _attack_timer: float = 0.0 |
|
|
|
var _attack_timer: float = 0.0 |
|
|
|
var _hitbox: HitBox = null |
|
|
|
var _hitbox: HitBox = null |
|
|
|
|
|
|
|
var _is_attacking: bool = false # Prevents overlapping attacks |
|
|
|
|
|
|
|
|
|
|
|
func _ready(): |
|
|
|
func _ready(): |
|
|
|
if weapon_data and weapon_data.mesh_scene: |
|
|
|
if weapon_data and weapon_data.mesh_scene: |
|
|
|
@ -126,15 +127,23 @@ func perform_attack() -> bool: |
|
|
|
if not weapon_data or not owner_character: |
|
|
|
if not weapon_data or not owner_character: |
|
|
|
return false |
|
|
|
return false |
|
|
|
|
|
|
|
|
|
|
|
# Check cooldown |
|
|
|
# Check cooldown and if already attacking |
|
|
|
if _attack_timer > 0: |
|
|
|
if _attack_timer > 0 or _is_attacking: |
|
|
|
return false |
|
|
|
return false |
|
|
|
|
|
|
|
|
|
|
|
_attack_timer = weapon_data.attack_cooldown |
|
|
|
# Calculate total attack duration (startup + active) |
|
|
|
|
|
|
|
var startup = weapon_data.startup_time if weapon_data else 0.15 |
|
|
|
|
|
|
|
var active = weapon_data.active_time if weapon_data else 0.2 |
|
|
|
|
|
|
|
var total_duration = startup + active |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set cooldown to at least cover the full attack duration |
|
|
|
|
|
|
|
var cooldown = max(weapon_data.attack_cooldown, total_duration) |
|
|
|
|
|
|
|
_attack_timer = cooldown |
|
|
|
|
|
|
|
_is_attacking = true |
|
|
|
|
|
|
|
|
|
|
|
# Notify owner character of attack cooldown (for UI) |
|
|
|
# Notify owner character of attack cooldown (for UI) |
|
|
|
if owner_character and owner_character.is_multiplayer_authority(): |
|
|
|
if owner_character and owner_character.is_multiplayer_authority(): |
|
|
|
owner_character._attack_timer = weapon_data.attack_cooldown |
|
|
|
owner_character._attack_timer = cooldown |
|
|
|
|
|
|
|
|
|
|
|
# Play attack animation on owner (use weapon's animation) |
|
|
|
# Play attack animation on owner (use weapon's animation) |
|
|
|
if owner_character._body: |
|
|
|
if owner_character._body: |
|
|
|
@ -151,31 +160,42 @@ func perform_attack() -> bool: |
|
|
|
attack_performed.emit() |
|
|
|
attack_performed.emit() |
|
|
|
return true |
|
|
|
return true |
|
|
|
|
|
|
|
|
|
|
|
## Activate the hitbox for attack detection - active for entire animation |
|
|
|
## Activate the hitbox for attack detection - waits for startup, then activates |
|
|
|
func _activate_hitbox(): |
|
|
|
func _activate_hitbox(): |
|
|
|
if not _hitbox: |
|
|
|
if not _hitbox: |
|
|
|
|
|
|
|
_is_attacking = false |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
# Update owner reference in case it changed |
|
|
|
# Update owner reference in case it changed |
|
|
|
_hitbox.owner_entity = owner_character |
|
|
|
_hitbox.owner_entity = owner_character |
|
|
|
|
|
|
|
|
|
|
|
# Activate hitbox immediately for the duration specified in weapon data |
|
|
|
# STARTUP PHASE - Wait before activating (wind-up animation) |
|
|
|
|
|
|
|
var startup = weapon_data.startup_time if weapon_data else 0.15 |
|
|
|
|
|
|
|
if startup > 0: |
|
|
|
|
|
|
|
await get_tree().create_timer(startup).timeout |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Check if weapon/hitbox is still valid after await |
|
|
|
|
|
|
|
if not _hitbox or not is_instance_valid(_hitbox): |
|
|
|
|
|
|
|
_is_attacking = false |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ACTIVE PHASE - Hitbox on, can deal damage |
|
|
|
_hitbox.activate() |
|
|
|
_hitbox.activate() |
|
|
|
|
|
|
|
|
|
|
|
# Calculate total active duration from weapon resource (startup + active) |
|
|
|
# Wait for active duration |
|
|
|
var startup = weapon_data.startup_time if weapon_data else 0.15 |
|
|
|
|
|
|
|
var active = weapon_data.active_time if weapon_data else 0.2 |
|
|
|
var active = weapon_data.active_time if weapon_data else 0.2 |
|
|
|
var duration = startup + active |
|
|
|
await get_tree().create_timer(active).timeout |
|
|
|
|
|
|
|
|
|
|
|
await get_tree().create_timer(duration).timeout |
|
|
|
# RECOVERY PHASE - Hitbox off |
|
|
|
|
|
|
|
|
|
|
|
# Deactivate when attack is complete |
|
|
|
|
|
|
|
if _hitbox and is_instance_valid(_hitbox): |
|
|
|
if _hitbox and is_instance_valid(_hitbox): |
|
|
|
_hitbox.deactivate() |
|
|
|
_hitbox.deactivate() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Attack complete |
|
|
|
|
|
|
|
_is_attacking = false |
|
|
|
|
|
|
|
|
|
|
|
## Check if weapon can attack |
|
|
|
## Check if weapon can attack |
|
|
|
func can_attack() -> bool: |
|
|
|
func can_attack() -> bool: |
|
|
|
return _attack_timer <= 0 |
|
|
|
return _attack_timer <= 0 and not _is_attacking |
|
|
|
|
|
|
|
|
|
|
|
## Set the character who owns this weapon |
|
|
|
## Set the character who owns this weapon |
|
|
|
func set_owner_character(character: Character): |
|
|
|
func set_owner_character(character: Character): |
|
|
|
|