class_name PlayerAttack extends Area3D signal attacked enum Side { RIGHT, LEFT } @export_group("Collision") @export var _collision_debug_material: Material @export var _attack_max_angle: float = 2 * PI / 3 @export var _attack_radius: float = 2 @export_group("Timers") @export var _cooldown_time: float = 0.3 @export var _hit_window_time: float = 0.15 @export var _swoop_effect_time: float = 0.25 @export_group("Hits") @export var _hit_projectile_speed: float = 35 @export var _direction_angles: Dictionary = {-PI / 6: 0, PI / 6: PI / 4, PI: PI / 2} var side := Side.RIGHT var _debug_collision_shapes := DebugCollisionShapes.new() var _cooldown_timer: float var _hit_window_timer: float var _swoop_effect_timer: float @onready var _attack_shape_node: CollisionShape3D = $AttackShape @onready var _swoop_mesh_node: MeshInstance3D = $SwoopMesh @onready var _attack_shape: CylinderShape3D = _attack_shape_node.shape as CylinderShape3D @onready var _swoop_mesh: SphereMesh = _swoop_mesh_node.mesh as SphereMesh func _ready() -> void: Debugger.add_event("attacked") attacked.connect(func() -> void: Debugger.event_emitted("attacked", [])) body_entered.connect(_on_body_entered) position.y = Projectile.HEIGHT _set_collision_size(_attack_radius) _debug_collision_shapes.init(get_children(), self, _collision_debug_material) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("attack"): _attack() func _process(delta: float) -> void: if _cooldown_timer > 0: _cooldown_timer -= delta if _hit_window_timer > 0: _hit_window_timer -= delta elif _debug_collision_shapes.is_visible: _debug_collision_shapes.set_visibility(false) if _swoop_effect_timer > 0: _swoop_effect_timer -= delta (_swoop_mesh_node.material_override as StandardMaterial3D).albedo_color = Color( 1, 1, 1, _swoop_effect_timer / _swoop_effect_time ) _swoop_mesh_node.visible = _swoop_effect_timer > 0 Debugger.text("_cooldown_timer", _cooldown_timer, 2) Debugger.text("_hit_window_timer", _hit_window_timer, 2) Debugger.text("_swoop_effect_timer", _swoop_effect_timer, 2) Debugger.vector( "fghdh", global_position, ( global_position + global_basis.z.rotated(Vector3.UP, _attack_max_angle) * _attack_radius ) ) Debugger.vector( "fghdh2", global_position, ( global_position + global_basis.z.rotated(Vector3.UP, -_attack_max_angle) * _attack_radius ) ) for dir_angle: float in _direction_angles: Debugger.line( "fghdh3" + str(dir_angle), global_position, ( global_position + global_basis.z.rotated(Vector3.UP, dir_angle) * _attack_radius ), Color.BLUE ) func _physics_process(_delta: float) -> void: monitoring = _hit_window_timer > 0 func is_hitting() -> bool: return _cooldown_timer > 0 func _attack() -> void: if _cooldown_timer > 0: return attacked.emit() _cooldown_timer = _cooldown_time _hit_window_timer = _hit_window_time _swoop_effect_timer = _swoop_effect_time _debug_collision_shapes.set_visibility(true) side = Side.LEFT if side == Side.RIGHT else Side.RIGHT func _hit_projectile(projectile: Projectile) -> void: var diff := projectile.global_position - global_position diff.y = 0 var angle := global_basis.z.signed_angle_to(diff, Vector3.UP) Debugger.vector("ASDSAD", global_position, global_position + global_basis.z) Debugger.vector("ASDSAD2", global_position, global_position + diff) Debugger.text("angle", rad_to_deg(angle), 2) if angle > _attack_max_angle or angle < -_attack_max_angle: return var angle_sign := -1.0 if side == Side.RIGHT else 1.0 var angle_signed := angle * angle_sign Debugger.text("side", Side.find_key(side), 2) Debugger.text("angle_signed", rad_to_deg(angle_signed), 2) var prev_dir_angle: float = -_attack_max_angle for dir_angle: float in _direction_angles.keys(): if angle_signed > prev_dir_angle and angle_signed <= dir_angle: Debugger.text("prev_dir_angle", rad_to_deg(prev_dir_angle), 2) Debugger.text("dir_angle", rad_to_deg(dir_angle), 2) var new_direction := global_basis.z.rotated( Vector3.UP, (_direction_angles[dir_angle] as float) * angle_sign ) Debugger.vector( "ASDSAD3", projectile.global_position, projectile.global_position + new_direction ) projectile.set_velocity(new_direction * _hit_projectile_speed) break prev_dir_angle = dir_angle func _set_collision_size(radius: float) -> void: _attack_shape.radius = radius _swoop_mesh.radius = radius _swoop_mesh.height = radius func _on_body_entered(node: Node3D) -> void: if _hit_window_timer <= 0: return if node is Projectile: _hit_projectile(node as Projectile)