139 lines
5.6 KiB
GDScript
139 lines
5.6 KiB
GDScript
# CharacterPawn.gd
|
|
extends OrbitalBody3D
|
|
class_name CharacterPawn3D
|
|
|
|
## Core Parameters
|
|
@export var collision_energy_loss: float = 0.3
|
|
@export var base_inertia: float = 1.0 # Pawn's inertia without suit
|
|
|
|
## Input Buffers
|
|
var _move_input: Vector2 = Vector2.ZERO
|
|
var _roll_input: float = 0.0
|
|
var _vertical_input: float = 0.0
|
|
var _interact_input: PlayerController3D.KeyInput = PlayerController3D.KeyInput.new(false, false, false)
|
|
var _l_click_input: PlayerController3D.KeyInput = PlayerController3D.KeyInput.new(false, false, false)
|
|
var _r_click_input: PlayerController3D.KeyInput = PlayerController3D.KeyInput.new(false, false, false)
|
|
var _pitch_yaw_input: Vector2 = Vector2.ZERO
|
|
|
|
## Rotation Variables
|
|
@onready var camera_anchor: Marker3D = $CameraAnchor
|
|
@onready var camera_pivot: Node3D = $CameraAnchor/CameraPivot
|
|
@onready var camera: Camera3D = $CameraAnchor/CameraPivot/SpringArm/Camera3D
|
|
@export_range(0.1, PI / 2.0) var max_yaw_rad: float = deg_to_rad(80.0)
|
|
@export_range(-PI / 2.0 + 0.01, 0) var min_pitch_rad: float = deg_to_rad(-75.0)
|
|
@export_range(0, PI / 2.0 - 0.01) var max_pitch_rad: float = deg_to_rad(60.0)
|
|
@export var head_turn_lerp_speed: float = 15.0
|
|
|
|
## Movement Components
|
|
@onready var eva_suit_component: EVAMovementComponent = $EVAMovementComponent
|
|
@onready var zero_g_movemement_component: ZeroGMovementComponent = $ZeroGMovementComponent
|
|
|
|
## Physics State (Managed by Pawn)
|
|
@export var angular_damping: float = 0.95 # Base damping
|
|
|
|
## Other State Variables
|
|
var current_gravity: Vector3 = Vector3.ZERO # TODO: Implement gravity detection
|
|
var overlapping_ladder_area: Area3D = null
|
|
@onready var grip_detector: Area3D = $GripDetector
|
|
|
|
# Constants for State Checks
|
|
const WALKABLE_GRAVITY_THRESHOLD: float = 1.0
|
|
|
|
func _ready():
|
|
# find movement components
|
|
if eva_suit_component: print("Found EVA Suit Controller")
|
|
if zero_g_movemement_component: print("Found Zero-G Movement Controller")
|
|
|
|
# Connect grip detector signals
|
|
if grip_detector and zero_g_movemement_component:
|
|
print("GripDetector Area3D node found")
|
|
grip_detector.area_entered.connect(zero_g_movemement_component.on_grip_area_entered)
|
|
grip_detector.area_exited.connect(zero_g_movemement_component.on_grip_area_exited)
|
|
else:
|
|
printerr("GripDetector Area3D node not found on CharacterPawn!")
|
|
|
|
if name == str(multiplayer.get_unique_id()):
|
|
camera.make_current()
|
|
camera.process_mode = Node.PROCESS_MODE_ALWAYS
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
camera_pivot.global_transform = camera_anchor.get_global_transform_interpolated()
|
|
|
|
|
|
func _physics_process(_delta: float):
|
|
_apply_mouse_rotation()
|
|
|
|
_reset_inputs()
|
|
|
|
|
|
func _integrate_forces(state: PhysicsDirectBodyState3D):
|
|
if not is_multiplayer_authority(): return
|
|
super (state)
|
|
|
|
|
|
# print("Integrating forces for pawn %s" % name)
|
|
# print(" Move Input: %s, Vertical Input: %f, Roll Input: %f" % [_move_input, _vertical_input, _roll_input])
|
|
|
|
# Zero-G Movement
|
|
if zero_g_movemement_component:
|
|
# We pass the physics state
|
|
zero_g_movemement_component.process_movement(state, _move_input, _vertical_input, _roll_input, _l_click_input, _r_click_input)
|
|
|
|
# EVA Suit Movement
|
|
if eva_suit_component and zero_g_movemement_component.movement_state == ZeroGMovementComponent.MovementState.IDLE:
|
|
eva_suit_component.process_eva_movement(state, _move_input, _vertical_input, _roll_input, _r_click_input)
|
|
|
|
|
|
# --- Universal Rotation ---
|
|
func _apply_mouse_rotation():
|
|
if _pitch_yaw_input != Vector2.ZERO:
|
|
camera_anchor.rotate_y(-_pitch_yaw_input.x)
|
|
|
|
# Apply Pitch LOCALLY to pivot
|
|
camera_anchor.rotate_object_local(Vector3.RIGHT, _pitch_yaw_input.y)
|
|
camera_anchor.rotation.x = clamp(camera_anchor.rotation.x, min_pitch_rad, max_pitch_rad)
|
|
|
|
_pitch_yaw_input = Vector2.ZERO
|
|
|
|
camera_anchor.rotation.z = 0.0
|
|
|
|
# --- Universal Integration & Collision ---
|
|
func _integrate_angular_velocity(delta: float):
|
|
# (Same integration logic as before using Basis or Quaternions)
|
|
if angular_velocity.length_squared() > 0.0001:
|
|
rotate(angular_velocity.normalized(), angular_velocity.length() * delta)
|
|
|
|
# Prevent drift if velocity is very small
|
|
if angular_velocity.length_squared() < 0.0001:
|
|
angular_velocity = Vector3.ZERO
|
|
|
|
# --- Input Setters/Resets (Add vertical to set_movement_input) ---
|
|
func set_movement_input(move: Vector2, roll: float, vertical: float): _move_input = move; _roll_input = roll; _vertical_input = vertical
|
|
func set_interaction_input(interact_input: PlayerController3D.KeyInput): _interact_input = interact_input
|
|
func set_rotation_input(pitch_yaw_input: Vector2): _pitch_yaw_input += pitch_yaw_input
|
|
func set_click_input(l_action: PlayerController3D.KeyInput, r_action: PlayerController3D.KeyInput):
|
|
_l_click_input = l_action
|
|
_r_click_input = r_action
|
|
func _reset_inputs():
|
|
_move_input = Vector2.ZERO
|
|
_roll_input = 0.0
|
|
_vertical_input = 0.0
|
|
_interact_input = PlayerController3D.KeyInput.new(false, false, false)
|
|
_l_click_input = PlayerController3D.KeyInput.new(false, false, false)
|
|
_r_click_input = PlayerController3D.KeyInput.new(false, false, false)
|
|
_pitch_yaw_input = Vector2.ZERO # Keep _r_held
|
|
|
|
# --- Helper Functions ---
|
|
func _on_ladder_area_entered(area: Area3D): if area.is_in_group("Ladders"): overlapping_ladder_area = area
|
|
func _on_ladder_area_exited(area: Area3D): if area == overlapping_ladder_area: overlapping_ladder_area = null
|
|
func _reset_head_yaw(delta: float):
|
|
# Smoothly apply the reset target to the actual pivot rotation
|
|
camera_anchor.rotation.y = lerpf(camera_anchor.rotation.y, 0.0, delta * head_turn_lerp_speed)
|
|
|
|
# TODO: Re-enable when multiplayer authority per pawn is functional
|
|
# func _notification(what: int) -> void:
|
|
# match what:
|
|
# NOTIFICATION_ENTER_TREE:
|
|
# set_multiplayer_authority(int(name))
|