road-rage-tank/player/player.gd

158 lines
3.8 KiB
GDScript

class_name Player
extends CharacterBody3D
signal died
enum State {
NORMAL,
DASHING,
LOCKED,
}
const MOVE_SPEED := 7.5
const DASH_SPEED := 35.
const DASH_COST := .5
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
%HealthBar.update(v)
var state := State.NORMAL
var dash_direction: Vector2
var stamina := 1.0
@export var guns: Array[Gun]
var gun_index := 0:
set(v):
gun_index = v % guns.size()
%GunLabel.text = gun.name
var gun: Gun:
get: return guns[gun_index]
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 exp_lerp(a: Variant, b: Variant, decay: float, dt: float) -> Variant:
return lerp(a, b, 1 - exp(-decay * dt))
func _process_movement(delta: float) -> void:
var input = Input.get_vector("move_left", "move_right", "move_up", "move_down")
input.normalized()
var mul := 10. if DebugMenu.high_speed_hack else 1.
var desired_velocity = Vector3(input.x, 0., input.y) * MOVE_SPEED * mul
velocity = exp_lerp(velocity, desired_velocity, 20, delta)
move_and_slide()
if Input.is_action_just_pressed("dash") and stamina >= DASH_COST and not input.is_zero_approx():
stamina -= DASH_COST
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
func _process_shoot(delta: float) -> void:
var clock_mul := 1.
# match state:
# State.DASHING: clock_mul = 2.5
# State.LOCKED: clock_mul = 2.5
gun.fire_clock -= delta * clock_mul
if Input.is_action_pressed("fire") and gun.fire_clock <= 0.:
gun.fire(aim_angle)
func _process_cam_shake(delta: float) -> void:
shake_duration -= delta
%Camera3D.transform = camera_transform
if DebugMenu.zoomed_out:
%Camera3D.position += %Camera3D.basis.z * 20.
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
%Camera3D.position += v * .75
func _process(delta: float) -> void:
if not Level.is_active(): return
match state:
State.NORMAL: _process_movement(delta)
State.DASHING: _process_dash()
State.LOCKED: pass
_process_aim()
_process_shoot(delta)
_process_cam_shake(delta)
_process_stamina(delta)
damage_clock -= delta
if Input.is_action_just_pressed("sharpshooter"):
gun_index = 0
if Input.is_action_just_pressed("minigun"):
gun_index = 1
if Input.is_action_just_pressed("mortar"):
gun_index = 2
func damage(damager: Node3D) -> void:
if health <= 0: return
if state == State.DASHING:
damager.queue_free()
else:
if damage_clock <= 0.:
health -= 1
damage_clock = 3
shake_duration = .25
if health <= 0:
died.emit()