Files
millimeters-of-aluminum/scenes/characters/pilot_ball.gd
2025-10-21 08:28:11 +02:00

206 lines
6.9 KiB
GDScript

extends CharacterBody2D
class_name PilotBall
# --- Movement Constants (Friction Simulation) ---
# When in open space (no module overlap), movement is zeroed out quickly.
const EXTERIOR_DRAG_FACTOR: float = 0.05
# When pushing off hullplates (low friction, slow acceleration)
const INTERIOR_SLUGGISH_SPEED: float = 100.0
const INTERIOR_SLUGGISH_ACCEL: float = 5 # Low acceleration, simulating mass and small push
# When gripping a ladder (high friction, direct control)
const LADDER_SPEED: float = 100.0
const LADDER_ACCEL: float = 20 # High acceleration, simulating direct grip
@onready var camera: Camera2D = $Camera2D
@onready var overlap_area: Area2D = $OverlapDetector
@onready var ui_container: Control = $CanvasLayer/UIContainer
var nearby_station: SystemStation = null
var current_station: SystemStation = null
# --- State Variables ---
enum MovementState {
NO_CONTROL,
ZERO_G_INTERIOR,
LADDER_GRIP,
IN_STATION
}
var current_state: MovementState = MovementState.NO_CONTROL
var ladder_area: Area2D = null # Area of the ladder currently overlapped
var is_grabbing_ladder: bool = false # True if 'Space' is held while on ladder
# --- Overlap Detection (Assuming you use Area2D for detection) ---
var overlapping_modules: int = 0
# --- Ladder Constants ---
const LAUNCH_VELOCITY: float = 300.0
var _movement_input: Vector2 = Vector2.ZERO
var _interact_just_pressed: bool = false
var _interact_held: bool = false
# --- PUBLIC INPUT METHODS (Called by the PlayerController) ---
func set_movement_input(input_dir: Vector2):
_movement_input = input_dir
func set_interaction_input(just_pressed: bool, is_held: bool):
_interact_just_pressed = just_pressed
_interact_held = is_held
# --- New: Physics Initialization (Assuming CharacterBody2D is parented to the scene root or Ship) ---
# NOTE: CharacterBody2D cannot inherit OrbitalBody2D, so we manage its velocity manually.
func _ready():
# Set up overlap signals if they aren't already connected in the scene file
# You must have an Area2D child on PilotBall to detect overlaps.
overlap_area.body_entered.connect(on_body_entered)
overlap_area.body_exited.connect(on_body_exited)
overlap_area.area_entered.connect(_on_station_area_entered)
overlap_area.area_exited.connect(_on_station_area_exited)
camera.make_current()
func on_body_entered(body: Node2D):
# Detect Modules (which all inherit OrbitalBody2D via StructuralPiece)
if body is StructuralPiece:
overlapping_modules += 1
# Detect Ladders
if body is Ladder:
ladder_area = body.find_child("ClimbArea") # Assuming the Ladder has a specific Area2D for climbing
func on_body_exited(body: Node2D):
if body is StructuralPiece:
overlapping_modules -= 1
if body is Ladder:
if body.find_child("ClimbArea") == ladder_area:
ladder_area = null
is_grabbing_ladder = false # Force detach if the ladder moves away
# --- NEW: Functions to be called by the Station ---
func enter_station_state():
current_state = MovementState.IN_STATION
velocity = Vector2.ZERO # FIX: Stop all movement when entering a station
func exit_station_state():
# When leaving, transition to a sensible default state.
current_state = MovementState.ZERO_G_INTERIOR
func _physics_process(delta):
# This script now runs on the server and its state is synced to clients.
# It no longer checks for local input authority.
if current_state == MovementState.IN_STATION:
move_and_slide()
return
_update_movement_state() # This function now uses the new variables
process_interaction() # Process any interaction presses
# Reset input flags for the next frame
_interact_just_pressed = false
_interact_held = false
# The 'input_dir' now comes from our variable, not the Input singleton.
var input_dir = _movement_input
match current_state:
MovementState.ZERO_G_INTERIOR:
_sluggish_movement(input_dir, delta)
MovementState.LADDER_GRIP:
_ladder_movement(input_dir, delta)
# Reset input for the next frame
_movement_input = Vector2.ZERO
move_and_slide()
# This function is called every physics frame by _physics_process().
func process_interaction():
# If the interact button was not pressed this frame, do nothing.
if not _interact_just_pressed:
return
# Priority 1: Disengage from a station if we are in one.
if current_station:
current_station.disengage(self)
current_station = null
return
# Priority 2: Occupy a nearby station if we are not in one.
elif is_instance_valid(nearby_station):
current_station = nearby_station
current_station.occupy(self)
return
# Priority 3: Handle ladder launch logic.
# This part of the old logic was in _handle_interaction_input,
# but it's cleaner to check for the release of the button here.
if current_state == MovementState.LADDER_GRIP and not _interact_held:
# Launch the player away from the ladder when the interact button is released.
var launch_direction = - _movement_input.normalized()
if launch_direction == Vector2.ZERO:
# Default launch: use the character's forward direction
launch_direction = Vector2.UP.rotated(rotation)
velocity = launch_direction * LAUNCH_VELOCITY
# Immediately switch to zero-G interior state
is_grabbing_ladder = false
current_state = MovementState.ZERO_G_INTERIOR
# --- State Machine Update ---
func _update_movement_state():
# Priority 1: Ladder Grip
# This now checks the variable instead of the Input singleton.
if ladder_area and _interact_held:
is_grabbing_ladder = true
current_state = MovementState.LADDER_GRIP
return
# Priority 2: Interior Zero-G (must overlap a module/piece AND not be grabbing)
if overlapping_modules > 0:
if is_grabbing_ladder:
# If we were grabbing a ladder but released 'interact', we transition to zero-G interior
is_grabbing_ladder = false
current_state = MovementState.ZERO_G_INTERIOR
return
current_state = MovementState.ZERO_G_INTERIOR
return
# Priority 3: No Control (floating free)
is_grabbing_ladder = false
current_state = MovementState.NO_CONTROL
# --- Movement Implementations ---
func _sluggish_movement(input_dir: Vector2, delta: float):
# Simulates pushing off the wall: slow acceleration, but minimal drag
var target_velocity = input_dir * INTERIOR_SLUGGISH_ACCEL
velocity = velocity + target_velocity * delta
#velocity.lerp(velocity + interi, INTERIOR_SLUGGISH_ACCEL)
func _ladder_movement(input_dir: Vector2, delta: float):
# Simulates direct grip: fast acceleration, perfect control
var target_velocity = input_dir * LADDER_SPEED
velocity = velocity.lerp(target_velocity, LADDER_ACCEL * delta)
# --- New Functions for Station Interaction ---
func _on_station_area_entered(area: Area2D):
if area.get_parent() is SystemStation:
nearby_station = area.get_parent()
print("Near station: ", nearby_station.name)
func _on_station_area_exited(area: Area2D):
if area.get_parent() == nearby_station:
nearby_station = null
# Stations will call this to get the node where they should place their UIs.
func get_ui_container() -> Control:
return ui_container