mirror of
https://gitlab.com/open-fpsz/open-fpsz.git
synced 2026-04-29 16:25:35 +00:00
152 lines
4.5 KiB
GDScript
152 lines
4.5 KiB
GDScript
extends RigidBody3D
|
|
class_name Player
|
|
|
|
enum PlayerState { PLAYER_ALIVE, PLAYER_DEAD }
|
|
|
|
# constants
|
|
const max_energy : float = 100.0
|
|
const energy_charge_rate : float = 10 # energy per second
|
|
const energy_drain_rate : float = 20 # energy per second
|
|
const ground_speed : float = 48 / 3.6 # m/s
|
|
const jetpack_vertical_force : float = 800
|
|
const jetpack_horizontal_force : float = 400
|
|
const jetpack_force_factor : float = 2
|
|
|
|
# parameters
|
|
@export var max_floor_angle : float = 60
|
|
@export var energy: float = 100.0
|
|
var g : float = ProjectSettings.get_setting("physics/3d/default_gravity") # in m/s²
|
|
var gravity : Vector3 = g * ProjectSettings.get_setting("physics/3d/default_gravity_vector")
|
|
|
|
@export var player_id = 1:
|
|
set(id):
|
|
player_id = id
|
|
$PlayerInput.set_multiplayer_authority(id)
|
|
|
|
@export var player_state = PlayerState.PLAYER_ALIVE
|
|
|
|
@onready var input : PlayerInput = $PlayerInput
|
|
@onready var camera = $SpringArm3D/Camera3D
|
|
|
|
@onready var hud = $HUD
|
|
@onready var shape_cast = $ShapeCast3D
|
|
@onready var weapon = $SpringArm3D/Inventory/SpaceGun
|
|
@onready var animation_player : AnimationPlayer = $AnimationPlayer
|
|
|
|
@onready var spring_arm_height = $SpringArm3D.position.y
|
|
|
|
signal died(player)
|
|
signal energy_changed(energy)
|
|
|
|
var _jumping = false
|
|
|
|
@onready var _original_weapon_transform : Transform3D = weapon.transform
|
|
|
|
func _ready():
|
|
energy_changed.connect(hud._on_energy_changed)
|
|
$HealthComponent.health_changed.connect(hud._on_health_changed)
|
|
$HealthComponent.health_zeroed.connect(_die)
|
|
if player_id == multiplayer.get_unique_id():
|
|
camera.current = true
|
|
else:
|
|
$HUD.hide()
|
|
input.fired_primary.connect(_fire_primary)
|
|
input.jumped.connect(_jump)
|
|
|
|
func _fire_primary():
|
|
if player_state == PlayerState.PLAYER_DEAD:
|
|
return
|
|
if not weapon.can_fire():
|
|
return
|
|
var current_weapon_transform = weapon.transform
|
|
weapon.transform = _original_weapon_transform
|
|
weapon.fire_primary()
|
|
weapon.transform = current_weapon_transform
|
|
animation_player.stop()
|
|
animation_player.play("shoot")
|
|
|
|
func _jump():
|
|
if player_state == PlayerState.PLAYER_DEAD:
|
|
return
|
|
_jumping = true
|
|
|
|
func _is_on_floor() -> bool:
|
|
return shape_cast.is_colliding()
|
|
|
|
func _is_skiing() -> bool:
|
|
return input.skiing
|
|
|
|
func _handle_jetpack(delta, direction):
|
|
if input.jetting:
|
|
if energy > 0:
|
|
var up_vector = Vector3.UP * jetpack_vertical_force * jetpack_force_factor
|
|
var side_vector = direction * jetpack_horizontal_force * jetpack_force_factor
|
|
apply_force(up_vector + side_vector)
|
|
energy -= energy_drain_rate * delta
|
|
else:
|
|
energy += energy_charge_rate * delta
|
|
|
|
energy = clamp(energy, 0, max_energy)
|
|
energy_changed.emit(energy)
|
|
|
|
func _process(delta):
|
|
if player_state == PlayerState.PLAYER_DEAD:
|
|
return
|
|
%SpringArm3D.global_transform.basis = Basis.from_euler(Vector3(input.camera_rotation.y, input.camera_rotation.x, 0.0))
|
|
|
|
if animation_player.current_animation == "shoot":
|
|
pass
|
|
else:
|
|
animation_player.play("idle")
|
|
|
|
func _physics_process(delta):
|
|
if player_state == PlayerState.PLAYER_DEAD:
|
|
return
|
|
|
|
# retrieve user's direction vector
|
|
var _input_dir = input.direction
|
|
# compute direction in local space
|
|
var _direction = (transform.basis * Vector3(_input_dir.x, 0, _input_dir.y)).normalized()
|
|
# adjust direction based on spring arm rotation
|
|
_direction = _direction.rotated(Vector3.UP, $SpringArm3D.rotation.y)
|
|
|
|
_handle_jetpack(delta, _direction)
|
|
|
|
# handle ski
|
|
if _is_skiing():
|
|
physics_material_override.friction = 0
|
|
else:
|
|
physics_material_override.friction = 1
|
|
|
|
if _is_on_floor():
|
|
if not _direction.is_zero_approx() and not _is_skiing():
|
|
# retrieve collision normal
|
|
var normal = shape_cast.get_collision_normal(0)
|
|
# calculate the angle between the ground normal and the up vector
|
|
var slope_angle = rad_to_deg(acos(normal.dot(Vector3.UP)))
|
|
# check if the slope angle exceeds the maximum slope angle
|
|
if slope_angle <= max_floor_angle:
|
|
# adjust direction based on the floor normal to align with the slope
|
|
_direction = _direction.slide(normal)
|
|
|
|
linear_velocity = lerp(linear_velocity, _direction * ground_speed, .1)
|
|
|
|
if _jumping:
|
|
linear_velocity.y = sqrt(2 * abs((mass * gravity * delta).y) * 1)
|
|
|
|
_jumping = false
|
|
|
|
func _die():
|
|
player_state = PlayerState.PLAYER_DEAD
|
|
animation_player.stop()
|
|
animation_player.play("death")
|
|
var tween = create_tween()
|
|
tween.tween_interval(2)
|
|
tween.tween_callback(func(): died.emit(self))
|
|
tween.tween_callback(func(): animation_player.stop())
|
|
|
|
func respawn(location):
|
|
linear_velocity = Vector3()
|
|
$HealthComponent.heal_full()
|
|
position = location
|
|
player_state = PlayerState.PLAYER_ALIVE
|