166 lines
4.5 KiB
GDScript
166 lines
4.5 KiB
GDScript
# Player_Attack_Component.gd
|
|
extends Node2D
|
|
|
|
# 节点引用
|
|
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
|
@onready var ray_cast_2d_1: RayCast2D = $RayCast2D_1
|
|
@onready var ray_cast_2d_2: RayCast2D = $RayCast2D_2
|
|
@onready var ray_cast_2d_3: RayCast2D = $RayCast2D_3
|
|
@onready var hit_queue_processor = owner.get_node("Hit/Hit_Queue_Processor")
|
|
|
|
# 导出变量
|
|
var attack_cooldown: float = 0.1
|
|
|
|
# 成员变量
|
|
var attack_timer: float = 0.0
|
|
var direction: Vector2
|
|
var hit_force: float = 630
|
|
var damage: float = 1
|
|
var checked_objects = []
|
|
var is_attack_active := false
|
|
var raycasts: Array[RayCast2D] = []
|
|
|
|
#region 生命周期方法
|
|
func _ready() -> void:
|
|
_setup_connections()
|
|
_initialize_raycasts()
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
if attack_timer > 0:
|
|
attack_timer = max(0, attack_timer - delta)
|
|
if animation_player.is_playing():
|
|
validate_and_hit_target()
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if event.is_action_pressed("Attack") and owner.can_attack and attack_timer <= 0:
|
|
var mouse_pos = get_global_mouse_position()
|
|
direction = (mouse_pos - self.global_position).normalized()
|
|
execute_knife_attack()
|
|
owner.add_attack_up_velocity = false
|
|
|
|
func _exit_tree() -> void:
|
|
is_attack_active = false
|
|
manage_raycasts_state(false)
|
|
hit_queue_processor.clear_queue()
|
|
#endregion
|
|
|
|
#region 核心攻击逻辑
|
|
func execute_knife_attack(force: bool = false) -> void:
|
|
if force:
|
|
force_reset_attack_state()
|
|
|
|
is_attack_active = true
|
|
owner.can_attack = false
|
|
|
|
_apply_attack_movement()
|
|
_update_attack_visuals()
|
|
|
|
# 在动画开始前启用射线
|
|
manage_raycasts_state(true)
|
|
animation_player.play("Swing_Knife")
|
|
owner.get_node("Player_State_Machine").change_state("attack")
|
|
|
|
func _apply_attack_movement() -> void:
|
|
if owner.is_on_rope() and direction.y > 0:
|
|
owner.set_one_way_mask_value("rope",false)
|
|
#print("set false")
|
|
|
|
var current_state_name = owner.get_node("Player_State_Machine").current_state.name.to_lower()
|
|
|
|
if current_state_name not in ["roll","run_to_idle"] and sign(owner.velocity.x) == sign(direction.x) and owner.velocity.x != 0:
|
|
owner.velocity.x += direction.x * 50
|
|
else:
|
|
owner.velocity.x = direction.x * 400
|
|
|
|
owner.velocity.y = direction.y * 500 if owner.add_attack_up_velocity else direction.y * 80
|
|
|
|
func _update_attack_visuals() -> void:
|
|
var angle = direction.angle()
|
|
self.rotation = angle
|
|
self.scale.y = -1 if direction.x < 0 else 1
|
|
#endregion
|
|
|
|
#region 射线检测管理
|
|
func _initialize_raycasts() -> void:
|
|
raycasts = [ray_cast_2d_1, ray_cast_2d_2, ray_cast_2d_3]
|
|
store_raycast_initial_states()
|
|
manage_raycasts_state(false)
|
|
|
|
# 统一管理射线状态
|
|
func manage_raycasts_state(enabled: bool) -> void:
|
|
if not is_attack_active and enabled:
|
|
return
|
|
|
|
for ray in raycasts:
|
|
ray.enabled = enabled
|
|
|
|
func store_raycast_initial_states():
|
|
var rays = [ray_cast_2d_1, ray_cast_2d_2, ray_cast_2d_3]
|
|
for ray in rays:
|
|
ray.set_meta("default_values", {
|
|
"target_position": ray.target_position,
|
|
"rotation": ray.rotation,
|
|
"position": ray.position,
|
|
"enabled": ray.enabled
|
|
})
|
|
#endregion
|
|
|
|
#region 状态管理
|
|
func force_reset_attack_state() -> void:
|
|
if animation_player.is_playing():
|
|
animation_player.stop()
|
|
|
|
is_attack_active = false
|
|
manage_raycasts_state(false)
|
|
owner.can_attack = true
|
|
attack_timer = 0
|
|
checked_objects.clear()
|
|
|
|
#endregion
|
|
|
|
#region 信号连接设置
|
|
func _setup_connections() -> void:
|
|
animation_player.animation_finished.connect(_on_attack_animation_finished)
|
|
#endregion
|
|
|
|
#region 信号回调
|
|
func _on_attack_animation_finished(anim_name: String) -> void:
|
|
if anim_name == "Swing_Knife":
|
|
is_attack_active = false
|
|
manage_raycasts_state(false)
|
|
attack_timer = attack_cooldown
|
|
owner.can_attack = true
|
|
#endregion
|
|
|
|
#region 命中检测
|
|
func validate_and_hit_target() -> void:
|
|
if not is_attack_active:
|
|
return
|
|
|
|
var temp_exceptions = []
|
|
|
|
for ray in raycasts:
|
|
if ray.is_colliding():
|
|
var collider = ray.get_collider()
|
|
if collider.owner in checked_objects:
|
|
continue
|
|
|
|
if collider.owner.is_in_group("Enemy"):
|
|
checked_objects.append(collider.owner)
|
|
var hit_info = [collider.owner, damage, direction, hit_force + randf_range(0, 150), 'attack']
|
|
hit_queue_processor.add_target_to_queue(hit_info)
|
|
|
|
# 记录临时例外
|
|
temp_exceptions.append(collider)
|
|
for r in raycasts:
|
|
r.add_exception(collider)
|
|
r.force_raycast_update()
|
|
|
|
# 清理所有临时例外
|
|
for exception in temp_exceptions:
|
|
for ray in raycasts:
|
|
ray.remove_exception(exception)
|
|
ray.force_raycast_update()
|
|
|
|
checked_objects.clear()
|