229 lines
6.1 KiB
GDScript
229 lines
6.1 KiB
GDScript
extends CharacterBody3D
|
|
|
|
# Member variables
|
|
const QUAKE = 0.0625 / 2
|
|
const g = -800 * QUAKE
|
|
const MAX_SPEED = 320 * QUAKE
|
|
const MAX_AIR_SPEED = 30 * QUAKE
|
|
const FRICTION = 4# * QUAKE
|
|
const STOP_SPEED = 100 * QUAKE
|
|
const ACCEL = 15# * QUAKE
|
|
const AIR_ACCEL = 2# * QUAKE
|
|
const FORWARD_SPEED = 200 * QUAKE
|
|
const SIDE_SPEED = 350 * QUAKE
|
|
const UP_SPEED = 270 * QUAKE
|
|
const JUMP_SPEED = 270 * QUAKE
|
|
|
|
@export
|
|
var interact_range: float = 5.0
|
|
@export
|
|
var hold_force = 400
|
|
@export
|
|
var max_hold_velocity: float = 1000.0
|
|
|
|
# const DEACCEL= 8
|
|
# const MAX_SLOPE_ANGLE = 30https://www.youtube.com/watch?v=v3zT3Z5apaM
|
|
|
|
@onready
|
|
var _camera = $CSGMesh3D/Camera3D
|
|
@onready
|
|
var _ball = $Ball
|
|
@onready
|
|
var _collider = $CollisionShape3D
|
|
@onready
|
|
var _flashlight: SpotLight3D = $Flashlight
|
|
|
|
var _paused = false
|
|
var _noclip = false
|
|
|
|
var _held_object: RigidBody3D
|
|
#var _held_object_distance: float
|
|
|
|
|
|
func _ready():
|
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
|
|
|
|
|
func _process(_delta):
|
|
# Do this on the render tick so it doesnt jitter at higher FPS
|
|
_ball.global_position = _camera.global_position - _camera.global_transform.basis.z * interact_range
|
|
|
|
|
|
func _physics_process(delta):
|
|
# Shamelessly stolen code from quake :)
|
|
|
|
var fmove = 0.0
|
|
var smove = 0.0
|
|
var upmove = 0.0
|
|
|
|
if Input.is_action_pressed("move_forward"):
|
|
fmove += -FORWARD_SPEED
|
|
if Input.is_action_pressed("move_backward"):
|
|
fmove += FORWARD_SPEED
|
|
if Input.is_action_pressed("move_left"):
|
|
smove += -SIDE_SPEED
|
|
if Input.is_action_pressed("move_right"):
|
|
smove += SIDE_SPEED
|
|
#if Input.is_action_pressed("up"):
|
|
# upmove += UP_SPEED
|
|
if Input.is_action_pressed("run"):
|
|
fmove *= 2.0
|
|
smove *= 2.0
|
|
|
|
var wishvel = Vector3()
|
|
|
|
if _noclip:
|
|
wishvel = _camera.global_basis.z * fmove + _camera.global_basis.x * smove
|
|
else:
|
|
wishvel = basis.z * fmove + basis.x * smove
|
|
|
|
if _noclip:
|
|
wishvel.y += upmove
|
|
|
|
var wishdir = wishvel.normalized()
|
|
var wishspeed = wishvel.length()
|
|
|
|
if wishspeed > MAX_SPEED:
|
|
wishvel *= MAX_SPEED / wishspeed
|
|
wishspeed = MAX_SPEED
|
|
|
|
if Input.is_action_just_pressed("jump") and is_on_floor():
|
|
velocity.y += JUMP_SPEED
|
|
|
|
var vel = velocity
|
|
if _noclip:
|
|
vel = wishvel * 2
|
|
elif is_on_floor():
|
|
vel = _apply_friction(vel, delta)
|
|
vel = _apply_accel(vel, wishdir, wishspeed, delta)
|
|
else:
|
|
vel = _apply_air_accel(vel, wishvel, wishspeed, delta)
|
|
|
|
if not _noclip and not is_on_floor():
|
|
vel.y += g * delta
|
|
|
|
velocity = vel
|
|
move_and_slide()
|
|
|
|
if _held_object:
|
|
_apply_holding_force(delta)
|
|
|
|
|
|
func _apply_friction(vel: Vector3, delta: float):
|
|
var speed = sqrt(vel.x ** 2 + vel.z ** 2)
|
|
if(speed == 0.0): return vel
|
|
|
|
# TODO: edge detect friction?
|
|
var friction = FRICTION
|
|
|
|
var control = 0.0
|
|
if speed < STOP_SPEED:
|
|
control = STOP_SPEED
|
|
else:
|
|
control = speed
|
|
|
|
var newspeed = speed - delta * control * friction
|
|
|
|
if newspeed < 0.0:
|
|
newspeed = 0.0
|
|
else:
|
|
newspeed /= speed
|
|
|
|
return vel * newspeed
|
|
|
|
|
|
func _apply_accel(vel: Vector3, wishdir: Vector3, wishspeed: float, delta: float):
|
|
var currentspeed = vel.dot(wishdir)
|
|
var addspeed = wishspeed - currentspeed
|
|
if addspeed <= 0.0:
|
|
return vel
|
|
|
|
var accelspeed = ACCEL * delta * wishspeed
|
|
if accelspeed > addspeed:
|
|
accelspeed = addspeed
|
|
|
|
return vel + accelspeed * wishdir
|
|
|
|
|
|
func _apply_air_accel(vel: Vector3, wishvel: Vector3, wishspeed: float, delta: float):
|
|
var wishveloc = wishvel.normalized()
|
|
var wishspd = wishvel.length()
|
|
|
|
if wishspd > MAX_AIR_SPEED:
|
|
wishspd = MAX_AIR_SPEED
|
|
|
|
var currentspeed = vel.dot(wishveloc)
|
|
var addspeed = wishspd - currentspeed
|
|
if addspeed <= 0:
|
|
return vel
|
|
|
|
var accelspeed = AIR_ACCEL * wishspeed * delta
|
|
if accelspeed > addspeed:
|
|
accelspeed = addspeed
|
|
|
|
return vel + accelspeed * wishveloc
|
|
|
|
|
|
func _apply_holding_force(delta):
|
|
var hold_point = _camera.global_position - _camera.global_transform.basis.z * 3.0
|
|
var delta_position = hold_point - _held_object.global_position
|
|
var move_speed = min(delta_position.length() * hold_force, max_hold_velocity)
|
|
_held_object.linear_velocity = delta_position.normalized() * move_speed * delta
|
|
|
|
|
|
func _input(event):
|
|
if event.is_action_pressed("pause"):
|
|
_paused = not _paused
|
|
if _paused:
|
|
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
|
else:
|
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
|
|
|
if _paused: return
|
|
|
|
|
|
if event.is_action_pressed('noclip'):
|
|
_noclip = not _noclip
|
|
if _noclip:
|
|
_collider.disabled = true
|
|
else:
|
|
_collider.disabled = false
|
|
|
|
if event.is_action_pressed('flashlight'):
|
|
_flashlight.visible = not _flashlight.visible
|
|
|
|
# Camera
|
|
if event is InputEventMouseMotion:
|
|
rotation.y += -event.relative.x * 0.005
|
|
_camera.rotation.x += -event.relative.y * 0.005
|
|
_camera.rotation.x = clamp(_camera.rotation.x, deg_to_rad(-90), deg_to_rad(90))
|
|
|
|
# Rotate flashlight
|
|
_flashlight.rotation.x = _camera.rotation.x
|
|
|
|
if event.is_action_pressed("interact"):
|
|
# Handle dropping things
|
|
if _held_object:
|
|
#_held_object.drop()
|
|
_held_object = null
|
|
return
|
|
|
|
var space_state = get_world_3d().direct_space_state
|
|
# use global coordinates, not local to node
|
|
var ray_end = _camera.global_position - _camera.global_transform.basis.z * interact_range
|
|
var query = PhysicsRayQueryParameters3D.create(_camera.global_position, ray_end)
|
|
# Don't collide with ourselves
|
|
query.exclude = [self]
|
|
var result = space_state.intersect_ray(query)
|
|
if result.has('collider'):
|
|
var other: Node3D = result.collider
|
|
if other.has_method('interact'):
|
|
other.interact(self)
|
|
if other.is_in_group('can_hold'):
|
|
_held_object = other
|
|
#_held_object_distance = (Vector3(result.position) - _camera.global_position).length()
|
|
|
|
|
|
func force_drop():
|
|
if _held_object:
|
|
_held_object = null
|