D3/Char/Player/Player_Components/player_attack_component.gd
2025-05-10 23:19:52 +08:00

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()