Initial commit

This commit is contained in:
scorpion21211-cmd
2026-10-03 23:29:41 -04:00
commit 965f0d7584
10586 changed files with 1030294 additions and 0 deletions
+632
View File
@@ -0,0 +1,632 @@
extends CharacterBody3D
## Kestrel EC-01 capsule — flight, cameras, enter/exit.
## Hull clips + plumes live on child "Kestrel" via kestrel_driver.gd.
@onready var interaction_area: Area3D = $InteractionArea
@onready var camera_pivot: Node3D = get_node_or_null("CameraPivot")
@onready var capsule_camera: Camera3D = get_node_or_null("CameraPivot/CapsuleCamera")
@onready var third_person_camera: Camera3D = $ThirdPersonCamera
@onready var interact_prompt: Label = get_node_or_null("/root/Node3D/HUD/InteractPrompt")
@onready var pause_menu: Control = get_node_or_null("/root/Node3D/HUD/PauseMenu")
@onready var body_collision: CollisionShape3D = get_node_or_null("CollisionShape3D")
@onready var flight_label: Label = get_node_or_null("/root/Node3D/HUD/FlightLabel")
@onready var kestrel: Node3D = get_node_or_null("Kestrel")
var player_inside: bool = false
var player_ref: CharacterBody3D = null
var using_third_person: bool = false
var f_was_down: bool = false
var mass: float = 6500.0
var thrust_force: float = 180000.0
var rotation_speed: float = 1.8
var surface_friction: float = 6.0
const MOON_RADIUS: float = 173700.0
const MOON_G: float = 1.62
const MOON_SOI: float = 400000.0
const EARTH_RADIUS: float = 637100.0
const EARTH_G: float = 9.81
const EARTH_SOI: float = 20000000.0
const CAM_FAR: float = 1000000.0
const NEAR_SURFACE_MARGIN: float = 300.0
var _moon_colliders: Array[CollisionObject3D] = []
var _crust_exception: bool = false
var free_look: bool = false
var look_sensitivity: float = 0.003
var orbit_yaw: float = 0.0
var orbit_pitch: float = 0.4
var orbit_distance: float = 14.0
var orbit_pitch_min: float = -1.2
var orbit_pitch_max: float = 1.2
var cockpit_yaw: float = 0.0
var cockpit_pitch: float = 0.0
var cockpit_look_limit_yaw: float = 1.45
var cockpit_look_limit_up: float = 1.0
var cockpit_look_limit_down: float = 0.55
var head_local: Transform3D = Transform3D.IDENTITY
var telem_body: String = "—"
var telem_alt: float = 0.0
var telem_speed: float = 0.0
var telem_vspeed: float = 0.0
var telem_hspeed: float = 0.0
var telem_vcirc: float = 0.0
var telem_dh: float = 0.0
var telem_earth_rng: float = 0.0
var telem_thrust: bool = false
var telem_has_alt: bool = false
var _gear_down: bool = true
var _hatch_crew_open: bool = false
var _hatch_dock_open: bool = false
var _ladder_out: bool = false
var _cabin_dim: bool = false
var _v_was: bool = false
func _ready() -> void:
_ensure_actions()
if interaction_area:
interaction_area.monitoring = true
interaction_area.monitorable = true
interaction_area.body_entered.connect(_on_body_entered)
interaction_area.body_exited.connect(_on_body_exited)
if camera_pivot:
camera_pivot.top_level = false
head_local = camera_pivot.transform
if capsule_camera:
capsule_camera.near = 0.05
capsule_camera.far = CAM_FAR
capsule_camera.current = false
if third_person_camera:
third_person_camera.near = 0.1
third_person_camera.far = CAM_FAR
third_person_camera.current = false
if flight_label:
flight_label.visible = false
_bind_kestrel()
_collect_moon_colliders()
call_deferred("_update_crust_exception")
func _bind_kestrel() -> void:
if kestrel == null:
kestrel = get_node_or_null("Kestrel")
print("Kestrel hull=", kestrel, " driver=", kestrel.get_script() if kestrel else null)
func _hull() -> Node:
return kestrel if kestrel else null
func _set_hull(prop: String, value: Variant) -> void:
var h := _hull()
if h and prop in h:
h.set(prop, value)
func _moon() -> Node3D:
return get_node_or_null("/root/Node3D/SolarSystem/Moon") as Node3D
func _earth() -> Node3D:
return get_node_or_null("/root/Node3D/SolarSystem/Earth") as Node3D
func _habitat() -> Node3D:
return get_node_or_null("/root/Node3D/SolarSystem/Moon/LovellAnchor/Station/Habitat") as Node3D
func _collect_moon_colliders() -> void:
_moon_colliders.clear()
var moon := _moon()
if moon == null:
return
for n in moon.find_children("*", "PhysicsBody3D", true, false):
if n == self:
continue
if str(n.name).begins_with("Shell"):
continue
if str(n.name).begins_with("Moon") and n is CollisionObject3D:
_moon_colliders.append(n as CollisionObject3D)
func _inside_young() -> bool:
var habitat := _habitat()
if habitat == null:
return false
var lp: Vector3 = habitat.to_local(global_position)
var radial: float = Vector2(lp.x, lp.z).length()
return radial < 2000.0 and lp.y < 80.0 and lp.y > -120.0
func _update_crust_exception() -> void:
var want: bool = _inside_young()
if want == _crust_exception:
return
_crust_exception = want
for body in _moon_colliders:
if not is_instance_valid(body):
continue
if want:
add_collision_exception_with(body)
else:
remove_collision_exception_with(body)
func _active_gravity() -> Dictionary:
var moon := _moon()
if moon:
var d_m: float = global_position.distance_to(moon.global_position)
if d_m >= 1.0 and d_m <= MOON_SOI:
var to_c: Vector3 = moon.global_position - global_position
return {
"name": "Luna",
"center": moon.global_position,
"radius": MOON_RADIUS,
"g": MOON_G,
"accel": to_c.normalized() * MOON_G,
"dist": d_m
}
var earth := _earth()
if earth:
var d_e: float = global_position.distance_to(earth.global_position)
if d_e >= 1.0 and d_e <= EARTH_SOI:
var to_c2: Vector3 = earth.global_position - global_position
return {
"name": "Earth",
"center": earth.global_position,
"radius": EARTH_RADIUS,
"g": EARTH_G,
"accel": to_c2.normalized() * EARTH_G,
"dist": d_e
}
return {"name": "—", "center": Vector3.ZERO, "radius": 0.0, "g": 0.0, "accel": Vector3.ZERO, "dist": 0.0}
func _gravity_accel() -> Vector3:
return _active_gravity()["accel"]
func _update_telemetry() -> void:
telem_speed = velocity.length()
telem_thrust = Input.is_key_pressed(KEY_SPACE)
var earth := _earth()
if earth:
telem_earth_rng = maxf(0.0, global_position.distance_to(earth.global_position) - EARTH_RADIUS)
else:
telem_earth_rng = 0.0
var body: Dictionary = _active_gravity()
telem_body = str(body["name"])
if telem_body == "—":
telem_has_alt = false
telem_alt = 0.0
telem_vspeed = 0.0
telem_hspeed = telem_speed
telem_vcirc = 0.0
telem_dh = 0.0
return
telem_has_alt = true
var r: float = float(body["dist"])
var rad: float = float(body["radius"])
var g: float = float(body["g"])
telem_alt = r - rad
var radial: Vector3 = (global_position - body["center"]).normalized()
telem_vspeed = velocity.dot(radial)
telem_hspeed = (velocity - radial * telem_vspeed).length()
telem_vcirc = sqrt(g * rad * rad / r) if r > 1.0 else 0.0
telem_dh = telem_hspeed - telem_vcirc
func _update_flight_hud() -> void:
if flight_label == null or not player_inside:
return
flight_label.visible = true
var mode := "3P" if using_third_person else "1P"
var thr := "THRUST" if telem_thrust else "IDLE"
var vs_sign := "+" if telem_vspeed >= 0.0 else ""
var alt_line: String
var vcirc_line: String
var dh_line: String
if telem_has_alt:
alt_line = "ALT %8.0f m\n" % telem_alt
vcirc_line = "VCIRC %8.1f m/s\n" % telem_vcirc
var dh_sign := "+" if telem_dh >= 0.0 else ""
dh_line = "ΔH %s%7.1f m/s\n" % [dh_sign, telem_dh]
else:
alt_line = "ALT —\n"
vcirc_line = "VCIRC —\n"
dh_line = "ΔH —\n"
flight_label.text = (
"[%s] %s | BODY %s\n" % [mode, thr, telem_body]
+ alt_line
+ "SPD %8.1f m/s\n" % telem_speed
+ "VS %s%7.1f m/s\n" % [vs_sign, telem_vspeed]
+ "H %8.1f m/s\n" % telem_hspeed
+ vcirc_line
+ dh_line
+ "EARTH %8.0f m" % telem_earth_rng
)
func _clear_flight_hud() -> void:
if flight_label:
flight_label.visible = false
flight_label.text = ""
func _on_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
player_ref = body
if interact_prompt and not player_inside:
interact_prompt.text = "Press F to Enter"
interact_prompt.visible = true
func _on_body_exited(body: Node3D) -> void:
if body.is_in_group("player") and not player_inside:
player_ref = null
if interact_prompt:
interact_prompt.visible = false
func _unhandled_input(event: InputEvent) -> void:
if not player_inside:
return
if event.is_action_pressed("ui_cancel"):
if pause_menu and pause_menu.has_method("open_pause") and player_ref:
pause_menu.open_pause(player_ref)
get_viewport().set_input_as_handled()
return
if _event_toggles(event, "capsule_gear", KEY_G):
_gear_down = not _gear_down
_set_hull("gear_deploy", 1.0 if _gear_down else 0.0)
get_viewport().set_input_as_handled()
elif _event_toggles(event, "capsule_hatch_crew", KEY_H):
_hatch_crew_open = not _hatch_crew_open
_set_hull("hatch_crew_open", 1.0 if _hatch_crew_open else 0.0)
get_viewport().set_input_as_handled()
elif _event_toggles(event, "capsule_hatch_dock", KEY_J):
_hatch_dock_open = not _hatch_dock_open
_set_hull("hatch_dock_open", 1.0 if _hatch_dock_open else 0.0)
get_viewport().set_input_as_handled()
elif _event_toggles(event, "capsule_ladder", KEY_L):
_ladder_out = not _ladder_out
_set_hull("hatch_ladder_deploy", 1.0 if _ladder_out else 0.0)
get_viewport().set_input_as_handled()
elif _event_toggles(event, "capsule_instruments", KEY_I):
_set_hull("instruments_demo", 1.0)
get_viewport().set_input_as_handled()
elif _event_toggles(event, "capsule_cabin_light", KEY_K):
_cabin_dim = not _cabin_dim
_set_hull("cabin_dim", _cabin_dim)
get_viewport().set_input_as_handled()
func _ensure_actions() -> void:
var binds := {
"interact": KEY_F,
"toggle_first_person": KEY_V,
"capsule_gear": KEY_G,
"capsule_hatch_crew": KEY_H,
"capsule_hatch_dock": KEY_J,
"capsule_ladder": KEY_L,
"capsule_instruments": KEY_I,
"capsule_cabin_light": KEY_K,
}
for action in binds.keys():
if InputMap.has_action(action):
continue
InputMap.add_action(action)
var ev := InputEventKey.new()
ev.physical_keycode = binds[action]
InputMap.action_add_event(action, ev)
func _event_toggles(event: InputEvent, action: String, key: Key) -> bool:
if InputMap.has_action(action) and event.is_action_pressed(action):
return true
if event is InputEventKey and event.pressed and not event.echo and event.physical_keycode == key:
return true
return false
func _act_pressed(action: String) -> bool:
return InputMap.has_action(action) and Input.is_action_pressed(action)
func _act_just(action: String) -> bool:
return InputMap.has_action(action) and Input.is_action_just_pressed(action)
func _process(delta: float) -> void:
var interact_now := _act_just("interact") or _just_key(KEY_F)
if not player_inside and player_ref and interact_now:
f_was_down = true
enter_capsule()
return
if not player_inside:
f_was_down = Input.is_physical_key_pressed(KEY_F)
_update_crust_exception()
return
if interact_now and not f_was_down:
f_was_down = true
exit_capsule()
return
f_was_down = _act_pressed("interact") or Input.is_physical_key_pressed(KEY_F)
if _act_just("toggle_first_person"):
toggle_camera()
elif not InputMap.has_action("toggle_first_person") and _just_key(KEY_V):
toggle_camera()
var was_free = free_look
free_look = Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT)
if was_free and not free_look and not using_third_person:
cockpit_yaw = 0.0
cockpit_pitch = 0.0
if Input.is_key_pressed(KEY_ALT):
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
handle_flight(delta)
_update_telemetry()
_update_flight_hud()
if using_third_person:
update_third_person_camera()
if third_person_camera and not third_person_camera.current:
third_person_camera.make_current()
else:
update_cockpit_camera()
if capsule_camera and not capsule_camera.current:
capsule_camera.make_current()
func _just_key(key: Key) -> bool:
var down := Input.is_physical_key_pressed(key)
if key == KEY_V:
var edge := down and not _v_was
_v_was = down
return edge
return false
func _input(event: InputEvent) -> void:
if not player_inside:
return
if Input.is_key_pressed(KEY_ALT):
return
if event is InputEventMouseMotion:
if using_third_person:
orbit_yaw -= event.relative.x * look_sensitivity
orbit_pitch += event.relative.y * look_sensitivity
orbit_pitch = clampf(orbit_pitch, orbit_pitch_min, orbit_pitch_max)
elif free_look:
cockpit_yaw -= event.relative.x * look_sensitivity
cockpit_pitch -= event.relative.y * look_sensitivity
cockpit_yaw = clamp(cockpit_yaw, -cockpit_look_limit_yaw, cockpit_look_limit_yaw)
cockpit_pitch = clamp(cockpit_pitch, -cockpit_look_limit_down, cockpit_look_limit_up)
func enter_capsule() -> void:
if player_inside or player_ref == null:
return
player_inside = true
if interact_prompt:
interact_prompt.visible = false
player_ref.reparent(self)
player_ref.position = Vector3(0, 0.5, 0)
player_ref.rotation = Vector3.ZERO
player_ref.set_physics_process(false)
player_ref.set_process(true)
player_ref.set_process_input(false)
player_ref.set_process_unhandled_input(false)
if player_ref.has_method("set_in_vehicle"):
player_ref.set_in_vehicle(true)
var player_col = player_ref.get_node_or_null("CollisionShape3D")
if player_col:
player_col.disabled = true
var player_cam = player_ref.get_node_or_null("CameraPivot/Camera3D")
if player_cam:
player_cam.current = false
player_ref.visible = false
cockpit_yaw = 0.0
cockpit_pitch = 0.0
orbit_yaw = 0.0
orbit_pitch = 0.4
using_third_person = false
f_was_down = true
free_look = false
if camera_pivot:
camera_pivot.top_level = false
camera_pivot.transform = head_local
if third_person_camera:
third_person_camera.current = false
if capsule_camera:
capsule_camera.near = 0.05
capsule_camera.far = CAM_FAR
capsule_camera.current = true
capsule_camera.make_current()
velocity = Vector3.ZERO
FloatingOrigin.set_focus(self)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if flight_label:
flight_label.visible = true
_update_crust_exception()
func exit_capsule() -> void:
if not player_inside or player_ref == null:
return
var exiting_player: CharacterBody3D = player_ref
player_inside = false
velocity = Vector3.ZERO
free_look = false
f_was_down = false
using_third_person = false
_set_hull("abort_burn", false)
_set_hull("rcs_direction", 0)
_clear_flight_hud()
if body_collision:
body_collision.disabled = false
if is_instance_valid(camera_pivot):
camera_pivot.top_level = false
camera_pivot.transform = head_local
if is_instance_valid(capsule_camera):
capsule_camera.current = false
if is_instance_valid(third_person_camera):
third_person_camera.current = false
var station := get_node_or_null("/root/Node3D/SolarSystem/Moon/LovellAnchor/Station")
var dest: Node = station if station else get_tree().current_scene
exiting_player.reparent(dest)
exiting_player.global_position = global_position + global_transform.basis.y * 4.0
var player_cam_pivot = exiting_player.get_node_or_null("CameraPivot")
if player_cam_pivot:
player_cam_pivot.rotation = Vector3.ZERO
exiting_player.visible = true
exiting_player.set_physics_process(true)
exiting_player.set_process(true)
exiting_player.set_process_input(true)
exiting_player.set_process_unhandled_input(true)
exiting_player.process_mode = Node.PROCESS_MODE_INHERIT
var player_col = exiting_player.get_node_or_null("CollisionShape3D")
if player_col:
player_col.disabled = false
if exiting_player.has_method("set_in_vehicle"):
exiting_player.set_in_vehicle(false)
var player_cam = exiting_player.get_node_or_null("CameraPivot/Camera3D")
if player_cam:
player_cam.current = true
player_cam.make_current()
if exiting_player.has_method("restore_from_vehicle"):
exiting_player.restore_from_vehicle()
FloatingOrigin.set_focus(exiting_player)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
player_ref = null
func toggle_camera() -> void:
using_third_person = not using_third_person
if using_third_person:
if capsule_camera:
capsule_camera.current = false
if third_person_camera:
third_person_camera.near = 0.1
third_person_camera.far = CAM_FAR
third_person_camera.make_current()
else:
if third_person_camera:
third_person_camera.current = false
if camera_pivot:
camera_pivot.top_level = false
camera_pivot.transform = head_local
if capsule_camera:
capsule_camera.make_current()
func update_cockpit_camera() -> void:
if not camera_pivot:
return
camera_pivot.top_level = false
var b: Basis = head_local.basis
b = b.rotated(b.x, cockpit_pitch)
b = b.rotated(b.y, cockpit_yaw)
b = b.orthonormalized()
if abs(b.determinant()) < 0.5:
camera_pivot.transform = head_local
return
camera_pivot.transform = Transform3D(b, head_local.origin)
func update_third_person_camera() -> void:
if not third_person_camera:
return
var cy := cos(orbit_yaw)
var sy := sin(orbit_yaw)
var cp := cos(orbit_pitch)
var sp := sin(orbit_pitch)
var offset := Vector3(sy * cp, sp, cy * cp) * orbit_distance
var local_off := Vector3(offset.x, -offset.z, offset.y)
var cam_pos := global_position + global_transform.basis * local_off
third_person_camera.global_position = cam_pos
var target := global_position + global_transform.basis.y * 1.5
var forward := target - cam_pos
if forward.length_squared() < 0.0001:
return
forward = forward.normalized()
var ref_up := global_transform.basis.z
if abs(forward.dot(ref_up)) > 0.92:
ref_up = global_transform.basis.x
var right := ref_up.cross(forward)
if right.length_squared() < 0.0001:
return
right = right.normalized()
var up := forward.cross(right).normalized()
var cam_basis := Basis(-right, up, -forward).orthonormalized()
if abs(cam_basis.determinant()) < 0.5:
return
third_person_camera.global_basis = cam_basis
func handle_flight(delta: float) -> void:
_update_crust_exception()
var input_pitch := 0.0
var input_roll := 0.0
var input_yaw := 0.0
if Input.is_key_pressed(KEY_W):
input_pitch -= 1.0
if Input.is_key_pressed(KEY_S):
input_pitch += 1.0
if Input.is_key_pressed(KEY_A):
input_yaw += 1.0
if Input.is_key_pressed(KEY_D):
input_yaw -= 1.0
if Input.is_key_pressed(KEY_Q):
input_roll -= 1.0
if Input.is_key_pressed(KEY_E):
input_roll += 1.0
rotate_object_local(Vector3.RIGHT, input_pitch * rotation_speed * delta)
rotate_object_local(Vector3.FORWARD, -input_yaw * rotation_speed * delta)
rotate_object_local(Vector3.UP, input_roll * rotation_speed * delta)
var thrusting := Input.is_key_pressed(KEY_SPACE)
_set_hull("abort_burn", thrusting)
_set_hull("att_pitch", input_pitch)
_set_hull("att_yaw", input_yaw)
_set_hull("att_roll", input_roll)
_set_hull("rcs_direction", 0)
var acceleration: Vector3 = _gravity_accel()
if thrusting:
acceleration += global_transform.basis.y * (thrust_force / mass)
velocity += acceleration * delta
var near_surface := is_on_floor() or _crust_exception
if not near_surface:
var moon := _moon()
if moon and global_position.distance_to(moon.global_position) < MOON_RADIUS + NEAR_SURFACE_MARGIN:
near_surface = true
var earth := _earth()
if earth and global_position.distance_to(earth.global_position) < EARTH_RADIUS + NEAR_SURFACE_MARGIN:
near_surface = true
if near_surface:
if body_collision:
body_collision.disabled = false
move_and_slide()
if is_on_floor():
velocity.x = move_toward(velocity.x, 0.0, surface_friction * delta)
velocity.z = move_toward(velocity.z, 0.0, surface_friction * delta)
if velocity.length() < 0.08:
velocity = Vector3.ZERO
else:
if body_collision:
body_collision.disabled = true
global_position += velocity * delta
PhysicsServer3D.body_set_state(
get_rid(),
PhysicsServer3D.BODY_STATE_TRANSFORM,
global_transform
)
PhysicsServer3D.body_set_state(
get_rid(),
PhysicsServer3D.BODY_STATE_LINEAR_VELOCITY,
velocity
)