Hitbox Timing
This commit is contained in:
@@ -11,7 +11,7 @@ damage = 20.0
|
|||||||
attack_range = 3.5
|
attack_range = 3.5
|
||||||
attack_cooldown = 0.6
|
attack_cooldown = 0.6
|
||||||
knockback_force = 12.0
|
knockback_force = 12.0
|
||||||
startup_time = 0.5
|
startup_time = 0.1
|
||||||
active_time = 1.0
|
active_time = 1.0
|
||||||
mesh_scene = ExtResource("1_gdc1w")
|
mesh_scene = ExtResource("1_gdc1w")
|
||||||
weight = 2.0
|
weight = 2.0
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
+12
-2
@@ -48,6 +48,7 @@ var is_blocking: bool = false
|
|||||||
@export var unarmed_active: float = 0.15 # Hit window duration
|
@export var unarmed_active: float = 0.15 # Hit window duration
|
||||||
var _attack_timer: float = 0.0
|
var _attack_timer: float = 0.0
|
||||||
var _unarmed_hitbox: HitBox = null
|
var _unarmed_hitbox: HitBox = null
|
||||||
|
var _is_unarmed_attacking: bool = false # Prevents overlapping unarmed attacks
|
||||||
|
|
||||||
# Dash system
|
# Dash system
|
||||||
@export var dash_speed_multiplier: float = 2.0
|
@export var dash_speed_multiplier: float = 2.0
|
||||||
@@ -430,10 +431,14 @@ func _perform_attack():
|
|||||||
if _body and _body.animation_player and _body.animation_player.current_animation.begins_with("Attack"):
|
if _body and _body.animation_player and _body.animation_player.current_animation.begins_with("Attack"):
|
||||||
return
|
return
|
||||||
|
|
||||||
if _attack_timer > 0:
|
if _attack_timer > 0 or _is_unarmed_attacking:
|
||||||
return
|
return
|
||||||
|
|
||||||
_attack_timer = attack_cooldown
|
# Calculate total attack duration and ensure cooldown covers it
|
||||||
|
var total_duration = unarmed_startup + unarmed_active
|
||||||
|
var cooldown = max(attack_cooldown, total_duration)
|
||||||
|
_attack_timer = cooldown
|
||||||
|
_is_unarmed_attacking = true
|
||||||
|
|
||||||
# Play attack animation once
|
# Play attack animation once
|
||||||
if _body:
|
if _body:
|
||||||
@@ -650,6 +655,7 @@ func _on_unarmed_hit(target: Node, damage_amount: float, knockback_amount: float
|
|||||||
|
|
||||||
func _activate_unarmed_hitbox():
|
func _activate_unarmed_hitbox():
|
||||||
if not _unarmed_hitbox:
|
if not _unarmed_hitbox:
|
||||||
|
_is_unarmed_attacking = false
|
||||||
return
|
return
|
||||||
|
|
||||||
# STARTUP PHASE - Wait before activating (wind-up animation)
|
# STARTUP PHASE - Wait before activating (wind-up animation)
|
||||||
@@ -657,6 +663,7 @@ func _activate_unarmed_hitbox():
|
|||||||
await get_tree().create_timer(unarmed_startup).timeout
|
await get_tree().create_timer(unarmed_startup).timeout
|
||||||
|
|
||||||
if not _unarmed_hitbox or not is_instance_valid(_unarmed_hitbox):
|
if not _unarmed_hitbox or not is_instance_valid(_unarmed_hitbox):
|
||||||
|
_is_unarmed_attacking = false
|
||||||
return
|
return
|
||||||
|
|
||||||
# ACTIVE PHASE - Hitbox on, can deal damage
|
# ACTIVE PHASE - Hitbox on, can deal damage
|
||||||
@@ -668,6 +675,9 @@ func _activate_unarmed_hitbox():
|
|||||||
if _unarmed_hitbox and is_instance_valid(_unarmed_hitbox):
|
if _unarmed_hitbox and is_instance_valid(_unarmed_hitbox):
|
||||||
_unarmed_hitbox.deactivate()
|
_unarmed_hitbox.deactivate()
|
||||||
|
|
||||||
|
# Attack complete
|
||||||
|
_is_unarmed_attacking = false
|
||||||
|
|
||||||
func _on_weapon_area_entered(area: Area3D):
|
func _on_weapon_area_entered(area: Area3D):
|
||||||
# Check if the area belongs to a WorldWeapon
|
# Check if the area belongs to a WorldWeapon
|
||||||
var weapon = area.get_parent()
|
var weapon = area.get_parent()
|
||||||
|
|||||||
Reference in New Issue
Block a user