fa7bbacb22
particles on kestrel are working, landing gear and hatch animations are not
658 lines
20 KiB
GDScript
658 lines
20 KiB
GDScript
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_body")
|
|
|
|
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 _instruments_on: 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")
|
|
|
|
|
|
var _hull_driver: Node = null
|
|
|
|
|
|
func _bind_kestrel() -> void:
|
|
if kestrel == null:
|
|
for name in ["kestrel_body", "Kestrel", "KestrelBody"]:
|
|
kestrel = get_node_or_null(name) as Node3D
|
|
if kestrel:
|
|
break
|
|
if kestrel == null:
|
|
for c in find_children("*", "Node", true, false):
|
|
if "abort_burn" in c and "gear_deploy" in c:
|
|
kestrel = c as Node3D
|
|
break
|
|
_hull_driver = kestrel
|
|
if kestrel and not ("abort_burn" in kestrel):
|
|
for c in kestrel.find_children("*", "Node", true, false):
|
|
if "abort_burn" in c and "gear_deploy" in c:
|
|
_hull_driver = c
|
|
break
|
|
print("Kestrel hull=", kestrel, " driver=", _hull_driver)
|
|
|
|
|
|
func _hull() -> Node:
|
|
return _hull_driver if _hull_driver else kestrel
|
|
|
|
|
|
func _set_hull(prop: String, value: Variant) -> void:
|
|
var h := _hull()
|
|
if h and prop in h:
|
|
h.set(prop, value)
|
|
|
|
|
|
func _drive_hull(thrusting: bool, pitch: float, yaw: float, roll: float) -> void:
|
|
_set_hull("abort_burn", thrusting)
|
|
_set_hull("att_pitch", pitch)
|
|
_set_hull("att_yaw", yaw)
|
|
_set_hull("att_roll", roll)
|
|
var h := _hull()
|
|
if h and h.has_method("drive"):
|
|
h.drive(thrusting, pitch, yaw, roll)
|
|
|
|
|
|
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):
|
|
_instruments_on = not _instruments_on
|
|
_set_hull("instruments_demo", 1.0 if _instruments_on else 0.35)
|
|
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)
|
|
_drive_hull(thrusting, input_pitch, input_yaw, input_roll)
|
|
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
|
|
)
|