batrix/scripts/gameplay/player/player_attacker.gd

163 lines
4.1 KiB
GDScript

class_name PlayerAttacker
extends Area3D
signal attacked
signal did_hit
enum Side { RIGHT, LEFT }
@export_group("References")
@export var _attack_shape_node: CollisionShape3D
@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_group("Hits")
@export var _hit_projectile_speed: float = 35
@export var _direction_angles: Dictionary[float, float] = {0.0: 0.0, PI: PI / 2.0}
@export var _hit_stop_time_scale := 0.05
@export var _hit_stop_duration := 0.25
var side := Side.RIGHT
var _cooldown_timer: float
var _hit_window_timer: float
var _queue_hit_stop: bool
@onready var _attack_shape: CylinderShape3D = _attack_shape_node.shape as CylinderShape3D
func _ready() -> void:
Debugger.add_event("attacked")
attacked.connect(func() -> void: Debugger.event_emitted("attacked", []))
area_entered.connect(_on_area_entered)
position.y = Projectile.HEIGHT
_set_collision_size(attack_radius)
func _process(delta: float) -> void:
if _cooldown_timer > 0:
_cooldown_timer -= delta
if _hit_window_timer > 0:
_hit_window_timer -= delta
if _queue_hit_stop:
_queue_hit_stop = false
_hit_stop(_hit_stop_time_scale, _hit_stop_duration)
Debugger.text("_cooldown_timer", _cooldown_timer, 2)
Debugger.text("_hit_window_timer", _hit_window_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.keys():
Debugger.line(
"fghdh3" + str(dir_angle),
global_position,
(
global_position
+ (
global_basis.z.rotated(
Vector3.UP, dir_angle * (-1.0 if side == Side.LEFT else 1.0)
)
* 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
_cooldown_timer = _cooldown_time
_hit_window_timer = _hit_window_time
side = Side.LEFT if side == Side.RIGHT else Side.RIGHT
attacked.emit()
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.hit(new_direction * _hit_projectile_speed)
_queue_hit_stop = true
break
prev_dir_angle = dir_angle
func _set_collision_size(radius: float) -> void:
_attack_shape.radius = radius
func _hit_stop(time_scale: float, duration: float) -> void:
Engine.time_scale = time_scale
await get_tree().create_timer(time_scale * duration).timeout
Engine.time_scale = 1
func _on_area_entered(node: Node3D) -> void:
if _hit_window_timer <= 0:
return
if node is Projectile:
_hit_projectile(node as Projectile)
did_hit.emit()