class_name Player extends CharacterBody3D enum State { NORMAL, DASHING, LOCKED, } const MOVE_SPEED := 5. const DASH_SPEED := 25. static var instance: Player @export var shake_noise: FastNoiseLite var shake_duration := 0. @onready var camera_transform: Transform3D = %Camera3D.transform var damage_clock := 0. var aim_angle: float var health := 3: set(v): health = v if is_node_ready(): %HealthLabel.text = "Health: %d" % v var state := State.NORMAL var dash_direction: Vector2 var stamina := 1.0 func _init() -> void: instance = self func _ready() -> void: health = health func _process_stamina(delta: float) -> void: stamina = move_toward(stamina, 1., delta * 0.2) %StaminaBar.value = stamina func _process_movement() -> void: var input = Input.get_vector("move_left", "move_right", "move_up", "move_down") input.normalized() velocity = Vector3(input.x, 0., input.y) * MOVE_SPEED move_and_slide() if Input.is_action_just_pressed("dash") and stamina >= 1.: stamina = 0. dash_direction = input state = State.DASHING await get_tree().create_timer(.25, false).timeout state = State.LOCKED await get_tree().create_timer(.5, false).timeout state = State.NORMAL func _process_dash() -> void: velocity = Vector3(dash_direction.x, 0., dash_direction.y) * DASH_SPEED move_and_slide() func _process_aim() -> void: var viewport_mouse_pos := get_viewport().get_mouse_position() var r_origin: Vector3 = %Camera3D.project_ray_origin(viewport_mouse_pos) var r_dir: Vector3 = %Camera3D.project_ray_normal(viewport_mouse_pos) # y = mx + b # 0 = mx + b # -b / m = x var t := -r_origin.y / r_dir.y var world_mouse_pos = r_origin + r_dir * t var to_mouse_pos = world_mouse_pos - global_position var angle = Vector2(to_mouse_pos.x, -to_mouse_pos.z).angle() %Cannon.rotation.y = angle aim_angle = angle %Reticle.position = to_mouse_pos var fire_clock := 0. func _process_shoot(delta: float) -> void: var clock_mul := 1. match state: State.DASHING: clock_mul = 2.5 State.LOCKED: clock_mul = 2.5 fire_clock -= delta * clock_mul if Input.is_action_pressed("fire") and fire_clock <= 0.: var dir := Vector3.RIGHT.rotated(Vector3.UP, aim_angle) var player_projectile: PlayerProjectile = preload("res://player/projectile/player_projectile.tscn").instantiate() player_projectile.init(dir) player_projectile.global_position = global_position + Vector3.UP * 0.5 add_sibling(player_projectile) fire_clock = 60. / 125. func _process_cam_shake(delta: float) -> void: shake_duration -= delta %Camera3D.transform = camera_transform if shake_duration <= 0: return var x := shake_noise.get_noise_1d(shake_duration * 10000) var y := shake_noise.get_noise_1d(-shake_duration * 10000) var v := \ camera_transform.basis.x * x + \ camera_transform.basis.y * y print("v = ", v) %Camera3D.position += v * .5 func _process(delta: float) -> void: match state: State.NORMAL: _process_movement() State.DASHING: _process_dash() State.LOCKED: pass _process_aim() _process_shoot(delta) _process_cam_shake(delta) _process_stamina(delta) damage_clock -= delta func damage(damager: Node3D) -> void: if state == State.DASHING: damager.queue_free() else: if damage_clock <= 0.: health -= 1 damage_clock = 3 shake_duration = .25 if health == 0: get_tree().reload_current_scene()