signalis-eb/godot/src/player_input.gd

159 lines
4.3 KiB
GDScript

class_name PlayerInput extends Node
enum Device {
Unknown,
KeyboardMouse,
Gamepad
}
class GameAction:
static var Run = 'run'
static var Interact = 'interact'
static var Ready = 'ready'
static var Fire = 'fire'
class UIAction:
static var Accept = 'ui_accept'
static var Select = 'ui_select'
static var Cancel = 'ui_cancel'
static var FocusNext = 'ui_focus_next'
static var FocusPrev = 'ui_focus_prev'
static var Left = 'ui_left'
static var Right = 'ui_right'
static var Up = 'ui_up'
static var Down = 'ui_down'
static var PageUp = 'ui_page_up'
static var PageDown = 'ui_page_down'
static var Home = 'ui_home'
static var End = 'ui_end'
signal walk
signal run
signal interact
signal ready_weapon
signal unready_weapon
signal fire
@export_flags_3d_physics var ground_collision_mask: int = 0
var last_known_device: Device = Device.Unknown
var _is_running: bool = false
var is_running: bool:
get: return _is_running
set(value):
if _is_running != value:
_is_running = value
if value: run.emit()
else: walk.emit()
var _is_interacting: bool = false
var is_interacting: bool:
get: return _is_interacting
set(value):
if _is_interacting != value:
_is_interacting = value
if value: interact.emit()
var _is_weapon_ready: bool = false
var is_weapon_ready: bool:
get: return _is_weapon_ready
set(value):
if _is_weapon_ready != value:
_is_weapon_ready = value
if value: ready_weapon.emit()
else: unready_weapon.emit()
var _is_firing: bool = false
var is_firing: bool:
get: return _is_firing
set(value):
if _is_firing != value:
_is_firing = value
if value: fire.emit()
var movement_dir: Vector2:
get: return Input.get_vector(
'move_left', 'move_right',
'move_up', 'move_down'
).normalized()
var _active_camera: Camera3D:
get: return get_viewport().get_camera_3d()
var _mouse_position: Vector2:
get: return get_viewport().get_mouse_position()
var _analog_dir: Vector2:
get: return Input.get_vector(
'look_left', 'look_right',
'look_up', 'look_down'
).normalized()
func _process(_delta: float) -> void:
is_running = Input.is_action_pressed(GameAction.Run)
func _get_device(event: InputEvent) -> Device:
if event is InputEventMouse or event is InputEventKey:
return Device.KeyboardMouse
elif event is InputEventJoypadButton or event is InputEventJoypadMotion:
return Device.Gamepad
else:
return Device.Unknown
func _unhandled_input(event: InputEvent) -> void:
last_known_device = _get_device(event)
if event.is_action_pressed(GameAction.Interact):
is_interacting = true
if event.is_action_released(GameAction.Interact):
is_interacting = false
if event.is_action_pressed(GameAction.Ready):
is_weapon_ready = true
if event.is_action_released(GameAction.Ready):
is_weapon_ready = false
if event.is_action_pressed(GameAction.Fire):
is_firing = true
if event.is_action_released(GameAction.Fire):
is_firing = false
func next_velocity(speed: float, dir: Vector2 = movement_dir) -> Vector3:
return Vector3(dir.x, 0, dir.y) * speed
func get_mouse_look() -> Vector3:
var cam: Camera3D = _active_camera
if not cam:
return Vector3.ZERO
var mouse_pos = _mouse_position
var ray_origin = cam.project_ray_origin(mouse_pos)
var ray_direction = cam.project_ray_normal(mouse_pos)
var plane_normal = Vector3.UP
var plane_d = 1.5 # make this read from the weapon transform, or at least export it
var denominator = plane_normal.dot(ray_direction)
if abs(denominator) < 0.0001:
return Vector3(ray_origin.x, 1.5, ray_origin.z)
var t = (plane_d - plane_normal.dot(ray_origin)) / denominator
var intersection_point = ray_origin + ray_direction * t
return intersection_point
func get_look_target(position: Vector3) -> Option:
match last_known_device:
Device.KeyboardMouse:
return Option.some(get_mouse_look())
Device.Gamepad:
var pos = _analog_dir
if pos.is_zero_approx():
return Option.none
return Option.some(Vector3(pos.x, 0, pos.y) + position)
_: return Option.none