Initial commit
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
extends Node3D
|
||||
## Kestrel EC-01 — analog 0..1 clip driver.
|
||||
## Nodes: hatch_crew, hatch_ladder, hatch_dock, LandingGear, SeatCamera.
|
||||
##
|
||||
## Effect controls:
|
||||
## rcs_direction fires only the bells that push that way. Off fires nothing.
|
||||
## abort_burn holds all four abort motors.
|
||||
## Ship axes: nose +Y, starboard +X, dorsal +Z.
|
||||
|
||||
@export_range(0.0, 1.0) var gear_deploy := 1.0 : set = set_gear_deploy
|
||||
@export_range(0.0, 1.0) var hatch_crew_open := 0.0 : set = set_hatch_crew_open
|
||||
@export_range(0.0, 1.0) var hatch_ladder_deploy := 0.0 : set = set_hatch_ladder_deploy
|
||||
@export_range(0.0, 1.0) var hatch_dock_open := 0.0 : set = set_hatch_dock_open
|
||||
@export_range(0.0, 1.0) var instruments_demo := 0.4 : set = set_instruments_demo
|
||||
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||
@export var abort_burn := false
|
||||
## Cabin strips and fill. On drops them to a low watch. It does not swap to battle red.
|
||||
@export var cabin_dim := false : set = set_cabin_dim
|
||||
|
||||
var _rcs: Array[GPUParticles3D] = []
|
||||
var _abort: Array[GPUParticles3D] = []
|
||||
var _cabin_ready := false
|
||||
var _cabin_lights: Array[Light3D] = []
|
||||
var _cabin_rest: Array[Dictionary] = []
|
||||
var _cabin_mats: Array[Dictionary] = []
|
||||
|
||||
const _RCS_THRUST := [
|
||||
Vector3.ZERO,
|
||||
Vector3(0, 1, 0),
|
||||
Vector3(0, -1, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(-1, 0, 0),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(0, 0, -1),
|
||||
]
|
||||
|
||||
func _ready() -> void:
|
||||
_seek("gear_deploy", gear_deploy)
|
||||
_arm_all("Vfx_RCS_*", 8, 0.14, 8.0, 14.0, 7.0, Color(0.86, 0.95, 1.0), 1.2, _rcs)
|
||||
_arm_all("Vfx_Abort_*", 20, 0.18, 12.0, 22.0, 7.0, Color(1.0, 0.62, 0.3), 3.0, _abort)
|
||||
_collect_cabin()
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
var thrust: Vector3 = _RCS_THRUST[clampi(rcs_direction, 0, _RCS_THRUST.size() - 1)]
|
||||
var ship := global_transform.basis
|
||||
for p in _rcs:
|
||||
if thrust == Vector3.ZERO:
|
||||
p.emitting = false
|
||||
else:
|
||||
var exhaust := ship.inverse() * p.global_transform.basis.y
|
||||
p.emitting = exhaust.dot(-thrust) > 0.7
|
||||
for p in _abort:
|
||||
p.emitting = abort_burn
|
||||
|
||||
func set_cabin_dim(v: bool) -> void:
|
||||
cabin_dim = v
|
||||
if _cabin_ready:
|
||||
_apply_cabin()
|
||||
|
||||
func _collect_cabin() -> void:
|
||||
_cabin_lights.clear()
|
||||
_cabin_rest.clear()
|
||||
_cabin_mats.clear()
|
||||
for n in find_children("*", "Light3D", true, false):
|
||||
var nm := String(n.name)
|
||||
if nm.begins_with("Nav"):
|
||||
continue
|
||||
if not (nm.begins_with("PointLight_") or nm == "CabinFill" or nm == "PanelLight" or nm.begins_with("CabinLamp")):
|
||||
var p := n.get_parent()
|
||||
var cabin := false
|
||||
while p:
|
||||
if String(p.name) == "CabinLights":
|
||||
cabin = true
|
||||
break
|
||||
p = p.get_parent()
|
||||
if not cabin:
|
||||
continue
|
||||
_cabin_lights.append(n)
|
||||
_cabin_rest.append({"energy": n.light_energy, "color": n.light_color})
|
||||
for n in find_children("*", "MeshInstance3D", true, false):
|
||||
var nm := String(n.name)
|
||||
if not nm.begins_with("LightStrip"):
|
||||
continue
|
||||
var mesh := n as MeshInstance3D
|
||||
var mat := mesh.get_active_material(0)
|
||||
if mat == null or not (mat is StandardMaterial3D):
|
||||
continue
|
||||
var copy := (mat as StandardMaterial3D).duplicate() as StandardMaterial3D
|
||||
mesh.set_surface_override_material(0, copy)
|
||||
_cabin_mats.append({"mat": copy, "energy": copy.emission_energy_multiplier, "color": copy.emission, "on": copy.emission_enabled})
|
||||
_cabin_ready = true
|
||||
_apply_cabin()
|
||||
|
||||
func _apply_cabin() -> void:
|
||||
for i in _cabin_lights.size():
|
||||
var lamp := _cabin_lights[i]
|
||||
var rest: Dictionary = _cabin_rest[i]
|
||||
if cabin_dim:
|
||||
lamp.light_color = rest.color
|
||||
lamp.light_energy = float(rest.energy) * 0.16
|
||||
else:
|
||||
lamp.light_color = rest.color
|
||||
lamp.light_energy = float(rest.energy)
|
||||
for row in _cabin_mats:
|
||||
var mat := row.mat as StandardMaterial3D
|
||||
if cabin_dim:
|
||||
mat.emission_enabled = true
|
||||
mat.emission = row.color
|
||||
mat.emission_energy_multiplier = float(row.energy) * 0.18
|
||||
else:
|
||||
mat.emission_enabled = bool(row.on)
|
||||
mat.emission = row.color
|
||||
mat.emission_energy_multiplier = float(row.energy)
|
||||
|
||||
func set_gear_deploy(v: float) -> void:
|
||||
gear_deploy = v
|
||||
_seek("gear_deploy", v)
|
||||
|
||||
func set_hatch_crew_open(v: float) -> void:
|
||||
hatch_crew_open = v
|
||||
_seek("hatch_crew_open", v)
|
||||
|
||||
func set_hatch_ladder_deploy(v: float) -> void:
|
||||
hatch_ladder_deploy = v
|
||||
_seek("hatch_ladder_deploy", v)
|
||||
|
||||
func set_hatch_dock_open(v: float) -> void:
|
||||
hatch_dock_open = v
|
||||
_seek("hatch_dock_open", v)
|
||||
|
||||
func set_instruments_demo(v: float) -> void:
|
||||
instruments_demo = v
|
||||
_seek("instruments_demo", v)
|
||||
|
||||
func _seek(clip: StringName, t: float) -> void:
|
||||
var ap := get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||
if ap == null:
|
||||
return
|
||||
if not ap.has_animation(clip):
|
||||
return
|
||||
var len := ap.get_animation(clip).length
|
||||
ap.play(clip)
|
||||
ap.seek(clampf(t, 0.0, 1.0) * len, true)
|
||||
ap.pause()
|
||||
|
||||
func _arm_all(pattern: String, amount: int, life: float, vmin: float, vmax: float, spread: float, color: Color, reach: float, into: Array[GPUParticles3D]) -> void:
|
||||
for n in find_children(pattern, "Node3D", true, false):
|
||||
if n is GPUParticles3D:
|
||||
continue
|
||||
var p := _make_plume(n, amount, life, vmin, vmax, spread, color, reach)
|
||||
if p:
|
||||
into.append(p)
|
||||
|
||||
func _make_plume(socket: Node3D, amount: int, life: float, vmin: float, vmax: float, spread: float, color: Color, reach: float) -> GPUParticles3D:
|
||||
if socket.get_node_or_null("Plume"):
|
||||
return null
|
||||
var p := GPUParticles3D.new()
|
||||
p.name = "Plume"
|
||||
p.amount = amount
|
||||
p.lifetime = life
|
||||
p.visibility_aabb = AABB(Vector3(-reach, 0, -reach), Vector3(reach * 2.0, reach * 4.0, reach * 2.0))
|
||||
var proc := ParticleProcessMaterial.new()
|
||||
proc.direction = Vector3(0, 1, 0)
|
||||
proc.spread = spread
|
||||
proc.initial_velocity_min = vmin
|
||||
proc.initial_velocity_max = vmax
|
||||
proc.gravity = Vector3.ZERO
|
||||
proc.scale_min = 0.08
|
||||
proc.scale_max = 0.32
|
||||
proc.color = color
|
||||
p.process_material = proc
|
||||
var draw := StandardMaterial3D.new()
|
||||
draw.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
draw.blend_mode = BaseMaterial3D.BLEND_MODE_ADD
|
||||
draw.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
draw.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES
|
||||
draw.albedo_color = color
|
||||
p.material_override = draw
|
||||
p.draw_pass_1 = QuadMesh.new()
|
||||
p.emitting = false
|
||||
var dir := socket.position
|
||||
if dir.length() < 0.001:
|
||||
dir = Vector3.UP
|
||||
dir = (socket.quaternion.inverse() * dir).normalized()
|
||||
var y := dir
|
||||
var up := Vector3.FORWARD if absf(y.dot(Vector3.UP)) > 0.9 else Vector3.UP
|
||||
var x := up.cross(y).normalized()
|
||||
var z := x.cross(y).normalized()
|
||||
p.basis = Basis(x, y, z)
|
||||
socket.add_child(p)
|
||||
return p
|
||||
Reference in New Issue
Block a user