Particle FX fix
particles on kestrel are working, landing gear and hatch animations are not
This commit is contained in:
@@ -10,11 +10,12 @@ extends CharacterBody3D
|
|||||||
@onready var pause_menu: Control = get_node_or_null("/root/Node3D/HUD/PauseMenu")
|
@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 body_collision: CollisionShape3D = get_node_or_null("CollisionShape3D")
|
||||||
@onready var flight_label: Label = get_node_or_null("/root/Node3D/HUD/FlightLabel")
|
@onready var flight_label: Label = get_node_or_null("/root/Node3D/HUD/FlightLabel")
|
||||||
@onready var kestrel: Node3D = get_node_or_null("Kestrel")
|
@onready var kestrel: Node3D = get_node_or_null("kestrel_body")
|
||||||
|
|
||||||
var player_inside: bool = false
|
var player_inside: bool = false
|
||||||
var player_ref: CharacterBody3D = null
|
var player_ref: CharacterBody3D = null
|
||||||
var using_third_person: bool = false
|
var using_third_person: bool = false
|
||||||
|
var f_was_down: bool = false
|
||||||
|
|
||||||
var mass: float = 6500.0
|
var mass: float = 6500.0
|
||||||
var thrust_force: float = 180000.0
|
var thrust_force: float = 180000.0
|
||||||
@@ -63,12 +64,12 @@ var _hatch_crew_open: bool = false
|
|||||||
var _hatch_dock_open: bool = false
|
var _hatch_dock_open: bool = false
|
||||||
var _ladder_out: bool = false
|
var _ladder_out: bool = false
|
||||||
var _cabin_dim: bool = false
|
var _cabin_dim: bool = false
|
||||||
var _thrust_input: float = 0.0
|
var _instruments_on: bool = false
|
||||||
|
var _v_was: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
GameplayInput.command_requested.connect(_handle_vehicle_command)
|
_ensure_actions()
|
||||||
GameplayInput.transient_input_cleared.connect(_clear_vehicle_input)
|
|
||||||
if interaction_area:
|
if interaction_area:
|
||||||
interaction_area.monitoring = true
|
interaction_area.monitoring = true
|
||||||
interaction_area.monitorable = true
|
interaction_area.monitorable = true
|
||||||
@@ -92,14 +93,31 @@ func _ready() -> void:
|
|||||||
call_deferred("_update_crust_exception")
|
call_deferred("_update_crust_exception")
|
||||||
|
|
||||||
|
|
||||||
|
var _hull_driver: Node = null
|
||||||
|
|
||||||
|
|
||||||
func _bind_kestrel() -> void:
|
func _bind_kestrel() -> void:
|
||||||
if kestrel == null:
|
if kestrel == null:
|
||||||
kestrel = get_node_or_null("Kestrel")
|
for name in ["kestrel_body", "Kestrel", "KestrelBody"]:
|
||||||
print("Kestrel hull=", kestrel, " driver=", kestrel.get_script() if kestrel else null)
|
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:
|
func _hull() -> Node:
|
||||||
return kestrel if kestrel else null
|
return _hull_driver if _hull_driver else kestrel
|
||||||
|
|
||||||
|
|
||||||
func _set_hull(prop: String, value: Variant) -> void:
|
func _set_hull(prop: String, value: Variant) -> void:
|
||||||
@@ -108,6 +126,16 @@ func _set_hull(prop: String, value: Variant) -> void:
|
|||||||
h.set(prop, value)
|
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:
|
func _moon() -> Node3D:
|
||||||
return get_node_or_null("/root/Node3D/SolarSystem/Moon") as Node3D
|
return get_node_or_null("/root/Node3D/SolarSystem/Moon") as Node3D
|
||||||
|
|
||||||
@@ -193,7 +221,7 @@ func _gravity_accel() -> Vector3:
|
|||||||
|
|
||||||
func _update_telemetry() -> void:
|
func _update_telemetry() -> void:
|
||||||
telem_speed = velocity.length()
|
telem_speed = velocity.length()
|
||||||
telem_thrust = _thrust_input > 0.0
|
telem_thrust = Input.is_key_pressed(KEY_SPACE)
|
||||||
var earth := _earth()
|
var earth := _earth()
|
||||||
if earth:
|
if earth:
|
||||||
telem_earth_rng = maxf(0.0, global_position.distance_to(earth.global_position) - EARTH_RADIUS)
|
telem_earth_rng = maxf(0.0, global_position.distance_to(earth.global_position) - EARTH_RADIUS)
|
||||||
@@ -262,7 +290,7 @@ func _on_body_entered(body: Node3D) -> void:
|
|||||||
if body.is_in_group("player"):
|
if body.is_in_group("player"):
|
||||||
player_ref = body
|
player_ref = body
|
||||||
if interact_prompt and not player_inside:
|
if interact_prompt and not player_inside:
|
||||||
interact_prompt.text = "[%s] Enter" % GameplayInput.binding_label(&"interact")
|
interact_prompt.text = "Press F to Enter"
|
||||||
interact_prompt.visible = true
|
interact_prompt.visible = true
|
||||||
|
|
||||||
|
|
||||||
@@ -273,61 +301,104 @@ func _on_body_exited(body: Node3D) -> void:
|
|||||||
interact_prompt.visible = false
|
interact_prompt.visible = false
|
||||||
|
|
||||||
|
|
||||||
## Handles semantic vehicle commands; proximity permits entry only on foot.
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
func _handle_vehicle_command(action: StringName, command_context: int) -> void:
|
if not player_inside:
|
||||||
if command_context != GameplayInput.context:
|
|
||||||
return
|
return
|
||||||
if action == &"interact":
|
if event.is_action_pressed("ui_cancel"):
|
||||||
if player_inside:
|
if pause_menu and pause_menu.has_method("open_pause") and player_ref:
|
||||||
exit_capsule()
|
pause_menu.open_pause(player_ref)
|
||||||
elif player_ref and interaction_area.overlaps_body(player_ref) and GameplayInput.context == GameplayInput.Context.ON_FOOT:
|
get_viewport().set_input_as_handled()
|
||||||
enter_capsule()
|
|
||||||
return
|
return
|
||||||
if not player_inside or GameplayInput.context != GameplayInput.Context.VEHICLE:
|
if _event_toggles(event, "capsule_gear", KEY_G):
|
||||||
return
|
|
||||||
match action:
|
|
||||||
&"toggle_view":
|
|
||||||
toggle_camera()
|
|
||||||
&"vehicle_toggle_gear":
|
|
||||||
_gear_down = not _gear_down
|
_gear_down = not _gear_down
|
||||||
_set_hull("gear_deploy", 1.0 if _gear_down else 0.0)
|
_set_hull("gear_deploy", 1.0 if _gear_down else 0.0)
|
||||||
&"vehicle_toggle_crew_hatch":
|
get_viewport().set_input_as_handled()
|
||||||
|
elif _event_toggles(event, "capsule_hatch_crew", KEY_H):
|
||||||
_hatch_crew_open = not _hatch_crew_open
|
_hatch_crew_open = not _hatch_crew_open
|
||||||
_set_hull("hatch_crew_open", 1.0 if _hatch_crew_open else 0.0)
|
_set_hull("hatch_crew_open", 1.0 if _hatch_crew_open else 0.0)
|
||||||
&"vehicle_toggle_dock_hatch":
|
get_viewport().set_input_as_handled()
|
||||||
|
elif _event_toggles(event, "capsule_hatch_dock", KEY_J):
|
||||||
_hatch_dock_open = not _hatch_dock_open
|
_hatch_dock_open = not _hatch_dock_open
|
||||||
_set_hull("hatch_dock_open", 1.0 if _hatch_dock_open else 0.0)
|
_set_hull("hatch_dock_open", 1.0 if _hatch_dock_open else 0.0)
|
||||||
&"capsule_toggle_ladder":
|
get_viewport().set_input_as_handled()
|
||||||
|
elif _event_toggles(event, "capsule_ladder", KEY_L):
|
||||||
_ladder_out = not _ladder_out
|
_ladder_out = not _ladder_out
|
||||||
_set_hull("hatch_ladder_deploy", 1.0 if _ladder_out else 0.0)
|
_set_hull("hatch_ladder_deploy", 1.0 if _ladder_out else 0.0)
|
||||||
&"vehicle_activate_instruments":
|
get_viewport().set_input_as_handled()
|
||||||
_set_hull("instruments_demo", 1.0)
|
elif _event_toggles(event, "capsule_instruments", KEY_I):
|
||||||
&"vehicle_toggle_cabin_lights":
|
_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
|
_cabin_dim = not _cabin_dim
|
||||||
_set_hull("cabin_dim", _cabin_dim)
|
_set_hull("cabin_dim", _cabin_dim)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
## Clears held cockpit look and visual thrust on menus, handoffs, or focus loss.
|
func _ensure_actions() -> void:
|
||||||
func _clear_vehicle_input() -> void:
|
var binds := {
|
||||||
free_look = false
|
"interact": KEY_F,
|
||||||
cockpit_yaw = 0.0
|
"toggle_first_person": KEY_V,
|
||||||
cockpit_pitch = 0.0
|
"capsule_gear": KEY_G,
|
||||||
_thrust_input = 0.0
|
"capsule_hatch_crew": KEY_H,
|
||||||
if player_inside:
|
"capsule_hatch_dock": KEY_J,
|
||||||
_set_hull("abort_burn", false)
|
"capsule_ladder": KEY_L,
|
||||||
_set_hull("att_pitch", 0.0)
|
"capsule_instruments": KEY_I,
|
||||||
_set_hull("att_yaw", 0.0)
|
"capsule_cabin_light": KEY_K,
|
||||||
_set_hull("att_roll", 0.0)
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
## Updates active vehicle input/cameras while retaining the original flight timing.
|
|
||||||
func _process(delta: float) -> void:
|
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:
|
if not player_inside:
|
||||||
if player_ref and interact_prompt:
|
f_was_down = Input.is_physical_key_pressed(KEY_F)
|
||||||
interact_prompt.text = "[%s] Enter" % GameplayInput.binding_label(&"interact")
|
|
||||||
_update_crust_exception()
|
_update_crust_exception()
|
||||||
return
|
return
|
||||||
_update_vehicle_look(delta)
|
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)
|
handle_flight(delta)
|
||||||
_update_telemetry()
|
_update_telemetry()
|
||||||
_update_flight_hud()
|
_update_flight_hud()
|
||||||
@@ -341,38 +412,30 @@ func _process(delta: float) -> void:
|
|||||||
capsule_camera.make_current()
|
capsule_camera.make_current()
|
||||||
|
|
||||||
|
|
||||||
## Updates cockpit/orbit look without letting menu input reach the vehicle camera.
|
func _just_key(key: Key) -> bool:
|
||||||
func _update_vehicle_look(delta: float) -> void:
|
var down := Input.is_physical_key_pressed(key)
|
||||||
var cursor_released := GameplayInput.is_held(&"vehicle_release_cursor")
|
if key == KEY_V:
|
||||||
if GameplayInput.gameplay_enabled():
|
var edge := down and not _v_was
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE if cursor_released else Input.MOUSE_MODE_CAPTURED
|
_v_was = down
|
||||||
var previous_freelook := free_look
|
return edge
|
||||||
free_look = GameplayInput.is_held(&"vehicle_freelook")
|
return false
|
||||||
if previous_freelook and not free_look and not using_third_person:
|
|
||||||
cockpit_yaw = 0.0
|
|
||||||
cockpit_pitch = 0.0
|
func _input(event: InputEvent) -> void:
|
||||||
var mouse_displacement := GameplayInput.consume_mouse_displacement()
|
if not player_inside:
|
||||||
if cursor_released:
|
|
||||||
return
|
return
|
||||||
var mouse_turn := mouse_displacement * look_sensitivity
|
if Input.is_key_pressed(KEY_ALT):
|
||||||
var controller_turn := GameplayInput.read_controller_look(delta)
|
return
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
if using_third_person:
|
if using_third_person:
|
||||||
_apply_vehicle_look(mouse_turn + controller_turn)
|
orbit_yaw -= event.relative.x * look_sensitivity
|
||||||
else:
|
orbit_pitch += event.relative.y * look_sensitivity
|
||||||
_apply_vehicle_look((mouse_turn if free_look else Vector2.ZERO) + controller_turn)
|
|
||||||
|
|
||||||
|
|
||||||
## Applies angular displacement in radians while preserving existing camera limits.
|
|
||||||
func _apply_vehicle_look(look_delta: Vector2) -> void:
|
|
||||||
if using_third_person:
|
|
||||||
orbit_yaw -= look_delta.x
|
|
||||||
orbit_pitch += look_delta.y
|
|
||||||
orbit_pitch = clampf(orbit_pitch, orbit_pitch_min, orbit_pitch_max)
|
orbit_pitch = clampf(orbit_pitch, orbit_pitch_min, orbit_pitch_max)
|
||||||
else:
|
elif free_look:
|
||||||
cockpit_yaw -= look_delta.x
|
cockpit_yaw -= event.relative.x * look_sensitivity
|
||||||
cockpit_pitch -= look_delta.y
|
cockpit_pitch -= event.relative.y * look_sensitivity
|
||||||
cockpit_yaw = clampf(cockpit_yaw, -cockpit_look_limit_yaw, cockpit_look_limit_yaw)
|
cockpit_yaw = clamp(cockpit_yaw, -cockpit_look_limit_yaw, cockpit_look_limit_yaw)
|
||||||
cockpit_pitch = clampf(cockpit_pitch, -cockpit_look_limit_down, cockpit_look_limit_up)
|
cockpit_pitch = clamp(cockpit_pitch, -cockpit_look_limit_down, cockpit_look_limit_up)
|
||||||
|
|
||||||
|
|
||||||
func enter_capsule() -> void:
|
func enter_capsule() -> void:
|
||||||
@@ -402,6 +465,7 @@ func enter_capsule() -> void:
|
|||||||
orbit_yaw = 0.0
|
orbit_yaw = 0.0
|
||||||
orbit_pitch = 0.4
|
orbit_pitch = 0.4
|
||||||
using_third_person = false
|
using_third_person = false
|
||||||
|
f_was_down = true
|
||||||
free_look = false
|
free_look = false
|
||||||
if camera_pivot:
|
if camera_pivot:
|
||||||
camera_pivot.top_level = false
|
camera_pivot.top_level = false
|
||||||
@@ -419,7 +483,6 @@ func enter_capsule() -> void:
|
|||||||
if flight_label:
|
if flight_label:
|
||||||
flight_label.visible = true
|
flight_label.visible = true
|
||||||
_update_crust_exception()
|
_update_crust_exception()
|
||||||
GameplayInput.set_context(GameplayInput.Context.VEHICLE)
|
|
||||||
|
|
||||||
|
|
||||||
func exit_capsule() -> void:
|
func exit_capsule() -> void:
|
||||||
@@ -429,6 +492,7 @@ func exit_capsule() -> void:
|
|||||||
player_inside = false
|
player_inside = false
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
free_look = false
|
free_look = false
|
||||||
|
f_was_down = false
|
||||||
using_third_person = false
|
using_third_person = false
|
||||||
_set_hull("abort_burn", false)
|
_set_hull("abort_burn", false)
|
||||||
_set_hull("rcs_direction", 0)
|
_set_hull("rcs_direction", 0)
|
||||||
@@ -469,7 +533,6 @@ func exit_capsule() -> void:
|
|||||||
FloatingOrigin.set_focus(exiting_player)
|
FloatingOrigin.set_focus(exiting_player)
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||||
player_ref = null
|
player_ref = null
|
||||||
GameplayInput.set_context(GameplayInput.Context.ON_FOOT)
|
|
||||||
|
|
||||||
|
|
||||||
func toggle_camera() -> void:
|
func toggle_camera() -> void:
|
||||||
@@ -535,25 +598,31 @@ func update_third_person_camera() -> void:
|
|||||||
third_person_camera.global_basis = cam_basis
|
third_person_camera.global_basis = cam_basis
|
||||||
|
|
||||||
|
|
||||||
## Reads signed flight intent while preserving the existing flight equations/timing.
|
|
||||||
func handle_flight(delta: float) -> void:
|
func handle_flight(delta: float) -> void:
|
||||||
_update_crust_exception()
|
_update_crust_exception()
|
||||||
var input_pitch := GameplayInput.read_axis(&"vehicle_pitch_up", &"vehicle_pitch_down")
|
var input_pitch := 0.0
|
||||||
var input_roll := GameplayInput.read_axis(&"vehicle_roll_left", &"vehicle_roll_right")
|
var input_roll := 0.0
|
||||||
var input_yaw := GameplayInput.read_axis(&"vehicle_yaw_right", &"vehicle_yaw_left")
|
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.RIGHT, input_pitch * rotation_speed * delta)
|
||||||
rotate_object_local(Vector3.FORWARD, -input_yaw * rotation_speed * delta)
|
rotate_object_local(Vector3.FORWARD, -input_yaw * rotation_speed * delta)
|
||||||
rotate_object_local(Vector3.UP, input_roll * rotation_speed * delta)
|
rotate_object_local(Vector3.UP, input_roll * rotation_speed * delta)
|
||||||
_thrust_input = GameplayInput.read_strength(&"vehicle_thrust")
|
var thrusting := Input.is_key_pressed(KEY_SPACE)
|
||||||
var thrusting := _thrust_input > 0.0
|
_drive_hull(thrusting, input_pitch, input_yaw, input_roll)
|
||||||
_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()
|
var acceleration: Vector3 = _gravity_accel()
|
||||||
if thrusting:
|
if thrusting:
|
||||||
acceleration += global_transform.basis.y * (thrust_force / mass) * _thrust_input
|
acceleration += global_transform.basis.y * (thrust_force / mass)
|
||||||
velocity += acceleration * delta
|
velocity += acceleration * delta
|
||||||
var near_surface := is_on_floor() or _crust_exception
|
var near_surface := is_on_floor() or _crust_exception
|
||||||
if not near_surface:
|
if not near_surface:
|
||||||
|
|||||||
+2
-2
File diff suppressed because one or more lines are too long
Binary file not shown.
+131
-86
@@ -1,3 +1,4 @@
|
|||||||
|
@tool
|
||||||
extends Node3D
|
extends Node3D
|
||||||
## Kestrel EC-01 plume driver. Godot 4.7.
|
## Kestrel EC-01 plume driver. Godot 4.7.
|
||||||
## Attach to the Kestrel child. Leave escape_capsule.gd on EscapeCapsule.
|
## Attach to the Kestrel child. Leave escape_capsule.gd on EscapeCapsule.
|
||||||
@@ -7,21 +8,27 @@ extends Node3D
|
|||||||
## listen_keys is off so walking the player does not light jets or shove the craft.
|
## listen_keys is off so walking the player does not light jets or shove the craft.
|
||||||
## Capsule sets abort_burn, att_pitch/yaw/roll, rcs_direction, hatches, cabin_dim.
|
## Capsule sets abort_burn, att_pitch/yaw/roll, rcs_direction, hatches, cabin_dim.
|
||||||
|
|
||||||
@export var listen_keys := false
|
@export var listen_keys := false # leave off; capsule owns G H J L K Space
|
||||||
@export_range(0.0, 1.0) var gear_deploy := 1.0
|
@export_range(0.0, 1.0) var gear_deploy := 1.0
|
||||||
@export_range(0.0, 1.0) var hatch_crew_open := 0.0
|
@export_range(0.0, 1.0) var hatch_crew_open := 0.0
|
||||||
@export_range(0.0, 1.0) var hatch_ladder_deploy := 0.0
|
@export_range(0.0, 1.0) var hatch_ladder_deploy := 0.0
|
||||||
@export_range(0.0, 1.0) var hatch_dock_open := 0.0
|
@export_range(0.0, 1.0) var hatch_dock_open := 0.0
|
||||||
@export_range(0.0, 1.0) var instruments_demo := 0.4
|
@export_range(0.0, 1.0) var instruments_demo := 0.4
|
||||||
@export var cabin_lights := true
|
@export var cabin_lights := true:
|
||||||
@export var cabin_dim := false
|
set(value):
|
||||||
|
cabin_lights = value
|
||||||
|
if is_inside_tree():
|
||||||
|
_apply_cabin()
|
||||||
|
@export var cabin_dim := false:
|
||||||
|
set(value):
|
||||||
|
cabin_dim = value
|
||||||
|
if is_inside_tree():
|
||||||
|
_apply_cabin()
|
||||||
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
@export_enum("Off", "Nose", "Tail", "Starboard", "Port", "Dorsal", "Ventral") var rcs_direction := 0
|
||||||
@export var att_pitch := 0.0
|
@export var att_pitch := 0.0
|
||||||
@export var att_yaw := 0.0
|
@export var att_yaw := 0.0
|
||||||
@export var att_roll := 0.0
|
@export var att_roll := 0.0
|
||||||
@export var abort_burn := false
|
@export var abort_burn := false
|
||||||
## Tick this in the inspector (or Remote tab while running) to self-test every jet.
|
|
||||||
@export var debug_fire := false
|
|
||||||
|
|
||||||
const _RIBBON := "shader_type spatial;
|
const _RIBBON := "shader_type spatial;
|
||||||
render_mode blend_add, unshaded, cull_disabled, depth_draw_never, fog_disabled;
|
render_mode blend_add, unshaded, cull_disabled, depth_draw_never, fog_disabled;
|
||||||
@@ -42,17 +49,14 @@ void fragment() {
|
|||||||
}"
|
}"
|
||||||
|
|
||||||
var _player: AnimationPlayer
|
var _player: AnimationPlayer
|
||||||
|
var _clip_players: Dictionary = {}
|
||||||
var _shader: Shader
|
var _shader: Shader
|
||||||
var _rcs: Array[Node3D] = []
|
var _rcs: Array[Node3D] = []
|
||||||
var _aborts: Array[Dictionary] = []
|
var _aborts: Array[Dictionary] = []
|
||||||
var _abort_hold := 0.0
|
var _abort_hold := 0.0
|
||||||
var _time := 0.0
|
var _time := 0.0
|
||||||
var _prev := {}
|
var _prev := {}
|
||||||
var _clip_cache := {}
|
|
||||||
var _warm_frames := 12
|
|
||||||
var _cabin_ready := false
|
var _cabin_ready := false
|
||||||
var _applied_dim := false
|
|
||||||
var _applied_lights := true
|
|
||||||
var _cabin_lights_n: Array[Light3D] = []
|
var _cabin_lights_n: Array[Light3D] = []
|
||||||
var _cabin_rest: Array[Dictionary] = []
|
var _cabin_rest: Array[Dictionary] = []
|
||||||
var _cabin_mats: Array[Dictionary] = []
|
var _cabin_mats: Array[Dictionary] = []
|
||||||
@@ -69,32 +73,32 @@ func _ready() -> void:
|
|||||||
_player = get_node_or_null("AnimationPlayer") as AnimationPlayer
|
_player = get_node_or_null("AnimationPlayer") as AnimationPlayer
|
||||||
if _player == null:
|
if _player == null:
|
||||||
_player = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
_player = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
if _player == null and get_parent():
|
||||||
|
_player = get_parent().find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
_split_clips()
|
||||||
_arm()
|
_arm()
|
||||||
|
if _aborts.is_empty():
|
||||||
|
_fallback_abort()
|
||||||
_collect_cabin()
|
_collect_cabin()
|
||||||
_apply_clips()
|
_apply_clips()
|
||||||
print("Kestrel driver ready: rcs=", _rcs.size(), " abort=", _aborts.size(),
|
var clips := PackedStringArray()
|
||||||
" cabin_lights=", _cabin_lights_n.size(), " strips=", _cabin_mats.size(),
|
if _player:
|
||||||
" anim_player=", _player != null)
|
clips = _player.get_animation_list()
|
||||||
if _rcs.is_empty():
|
print("Kestrel driver ready rcs=", _rcs.size(), " abort=", _aborts.size(), " clips=", clips, " lights=", _cabin_lights_n.size())
|
||||||
push_warning("Kestrel driver: no Vfx_RCS_* nodes found under " + str(get_path()))
|
|
||||||
if _aborts.is_empty():
|
|
||||||
push_warning("Kestrel driver: no Vfx_Abort_* nodes found under " + str(get_path()))
|
|
||||||
if _player == null:
|
|
||||||
push_warning("Kestrel driver: no AnimationPlayer found (gear/hatches will not move)")
|
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func drive(thrusting: bool, pitch: float, yaw: float, roll: float) -> void:
|
||||||
|
abort_burn = thrusting
|
||||||
|
att_pitch = pitch
|
||||||
|
att_yaw = yaw
|
||||||
|
att_roll = roll
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
_time += delta
|
_time += delta
|
||||||
if listen_keys:
|
if listen_keys and not Engine.is_editor_hint():
|
||||||
_read_keys()
|
_read_keys()
|
||||||
if debug_fire:
|
|
||||||
abort_burn = true
|
|
||||||
rcs_direction = 1 + int(_time) % 6
|
|
||||||
_apply_clips()
|
_apply_clips()
|
||||||
if _warm_frames > 0:
|
|
||||||
_warm_frames -= 1
|
|
||||||
# FIX: the capsule sets cabin_dim as a plain variable, so nothing re-applied it.
|
|
||||||
if cabin_dim != _applied_dim or cabin_lights != _applied_lights:
|
|
||||||
_apply_cabin()
|
_apply_cabin()
|
||||||
_burn(_wish(), delta)
|
_burn(_wish(), delta)
|
||||||
|
|
||||||
@@ -112,6 +116,7 @@ func _read_keys() -> void:
|
|||||||
instruments_demo = 0.15 if instruments_demo > 0.5 else 1.0
|
instruments_demo = 0.15 if instruments_demo > 0.5 else 1.0
|
||||||
if _pressed(KEY_K):
|
if _pressed(KEY_K):
|
||||||
cabin_lights = not cabin_lights
|
cabin_lights = not cabin_lights
|
||||||
|
_apply_cabin()
|
||||||
abort_burn = Input.is_physical_key_pressed(KEY_SPACE)
|
abort_burn = Input.is_physical_key_pressed(KEY_SPACE)
|
||||||
|
|
||||||
|
|
||||||
@@ -145,41 +150,64 @@ func _wish() -> Vector3:
|
|||||||
return (global_transform.basis * local).normalized()
|
return (global_transform.basis * local).normalized()
|
||||||
|
|
||||||
|
|
||||||
func _apply_clips() -> void:
|
var _anim_root: Node = null
|
||||||
_seek("gear_deploy", gear_deploy)
|
|
||||||
_seek("hatch_crew_open", hatch_crew_open)
|
|
||||||
_seek("hatch_ladder_deploy", hatch_ladder_deploy)
|
|
||||||
_seek("hatch_dock_open", hatch_dock_open)
|
|
||||||
_seek("instruments_demo", instruments_demo)
|
|
||||||
|
|
||||||
|
|
||||||
## FIX: only re-seek when the value actually changed. The old version called
|
func _split_clips() -> void:
|
||||||
## play()+seek()+pause() on five clips every physics frame.
|
_anim_root = self
|
||||||
func _clip_name(clip: StringName) -> StringName:
|
|
||||||
if _player.has_animation(clip):
|
|
||||||
return clip
|
|
||||||
for a in _player.get_animation_list():
|
|
||||||
if String(a).ends_with("/" + String(clip)):
|
|
||||||
return a
|
|
||||||
return &""
|
|
||||||
|
|
||||||
|
|
||||||
## Re-seeks only when the value changed, but keeps re-applying for the first few
|
|
||||||
## frames so the pose sticks after the AnimationPlayer finishes initializing.
|
|
||||||
func _seek(clip: StringName, t: float) -> void:
|
|
||||||
if _player == null:
|
if _player == null:
|
||||||
|
print("Kestrel clips: no AnimationPlayer")
|
||||||
return
|
return
|
||||||
var clip_id := _clip_name(clip)
|
var root := _player.get_node_or_null(_player.root_node)
|
||||||
if clip_id == &"":
|
if root:
|
||||||
|
_anim_root = root
|
||||||
|
print("Kestrel clips root=", _anim_root.name, " list=", _player.get_animation_list())
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_clips() -> void:
|
||||||
|
_pose("gear_deploy", gear_deploy)
|
||||||
|
_pose("hatch_crew_open", hatch_crew_open)
|
||||||
|
_pose("hatch_ladder_deploy", hatch_ladder_deploy)
|
||||||
|
_pose("hatch_dock_open", hatch_dock_open)
|
||||||
|
_pose("instruments_demo", instruments_demo)
|
||||||
|
|
||||||
|
|
||||||
|
func _pose(clip: StringName, t: float) -> void:
|
||||||
|
if _player == null or not _player.has_animation(clip):
|
||||||
return
|
return
|
||||||
t = clampf(t, 0.0, 1.0)
|
var anim := _player.get_animation(clip)
|
||||||
if _warm_frames <= 0 and _clip_cache.has(clip) and is_equal_approx(float(_clip_cache[clip]), t):
|
var at := clampf(t, 0.0, 1.0) * anim.length
|
||||||
return
|
var root: Node = _anim_root if _anim_root else self
|
||||||
_clip_cache[clip] = t
|
for i in anim.get_track_count():
|
||||||
var clip_len := _player.get_animation(clip_id).length
|
var full := String(anim.track_get_path(i))
|
||||||
_player.play(clip_id)
|
var node_path := full.get_slice(":", 0)
|
||||||
_player.seek(t * clip_len, true)
|
var prop := full.get_slice(":", 1)
|
||||||
_player.pause()
|
var node := root.get_node_or_null(NodePath(node_path))
|
||||||
|
if node == null:
|
||||||
|
node = find_child(node_path.get_file(), true, false)
|
||||||
|
if node == null:
|
||||||
|
continue
|
||||||
|
var typ := anim.track_get_type(i)
|
||||||
|
if typ == Animation.TYPE_ROTATION_3D:
|
||||||
|
var q := anim.rotation_track_interpolate(i, at)
|
||||||
|
if prop == "" or prop == "quaternion" or prop == "rotation":
|
||||||
|
(node as Node3D).quaternion = q
|
||||||
|
else:
|
||||||
|
node.set(prop, q)
|
||||||
|
elif typ == Animation.TYPE_POSITION_3D:
|
||||||
|
var pos := anim.position_track_interpolate(i, at)
|
||||||
|
if prop == "" or prop == "position":
|
||||||
|
(node as Node3D).position = pos
|
||||||
|
else:
|
||||||
|
node.set(prop, pos)
|
||||||
|
elif typ == Animation.TYPE_SCALE_3D:
|
||||||
|
var sc := anim.scale_track_interpolate(i, at)
|
||||||
|
if prop == "" or prop == "scale":
|
||||||
|
(node as Node3D).scale = sc
|
||||||
|
else:
|
||||||
|
node.set(prop, sc)
|
||||||
|
elif typ == Animation.TYPE_VALUE and prop != "":
|
||||||
|
node.set(prop, anim.value_track_interpolate(i, at))
|
||||||
|
|
||||||
|
|
||||||
func _collect_cabin() -> void:
|
func _collect_cabin() -> void:
|
||||||
@@ -219,24 +247,36 @@ func _collect_cabin() -> void:
|
|||||||
func _apply_cabin() -> void:
|
func _apply_cabin() -> void:
|
||||||
if not _cabin_ready:
|
if not _cabin_ready:
|
||||||
return
|
return
|
||||||
_applied_dim = cabin_dim
|
|
||||||
_applied_lights = cabin_lights
|
|
||||||
var dim := cabin_dim or not cabin_lights
|
var dim := cabin_dim or not cabin_lights
|
||||||
for i in _cabin_lights_n.size():
|
for i in _cabin_lights_n.size():
|
||||||
var lamp := _cabin_lights_n[i]
|
var lamp := _cabin_lights_n[i]
|
||||||
var rest: Dictionary = _cabin_rest[i]
|
var rest: Dictionary = _cabin_rest[i]
|
||||||
lamp.light_color = rest.color
|
lamp.light_color = rest.color
|
||||||
lamp.light_energy = float(rest.energy) * (0.08 if dim else 1.0)
|
lamp.light_energy = float(rest.energy) * (0.16 if dim else 1.0)
|
||||||
for row in _cabin_mats:
|
for row in _cabin_mats:
|
||||||
var mat := row.mat as StandardMaterial3D
|
var mat := row.mat as StandardMaterial3D
|
||||||
mat.emission_enabled = true if dim else bool(row.on)
|
mat.emission_enabled = true if dim else bool(row.on)
|
||||||
mat.emission = row.color
|
mat.emission = row.color
|
||||||
mat.emission_energy_multiplier = float(row.energy) * (0.05 if dim else 1.0)
|
mat.emission_energy_multiplier = float(row.energy) * (0.18 if dim else 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
func _scope() -> Array[Node]:
|
||||||
|
var found: Array[Node] = []
|
||||||
|
var roots: Array[Node] = [self]
|
||||||
|
if get_parent():
|
||||||
|
roots.append(get_parent())
|
||||||
|
for root in roots:
|
||||||
|
found.append(root)
|
||||||
|
for n in root.find_children("*", "Node", true, false):
|
||||||
|
found.append(n)
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
func _arm() -> void:
|
func _arm() -> void:
|
||||||
var i := 0
|
var i := 0
|
||||||
for n in find_children("*", "Node3D", true, false):
|
for n in _scope():
|
||||||
|
if not (n is Node3D):
|
||||||
|
continue
|
||||||
var nm := String(n.name)
|
var nm := String(n.name)
|
||||||
if nm.begins_with("Vfx_RCS_"):
|
if nm.begins_with("Vfx_RCS_"):
|
||||||
_rcs.append(_spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, float(i)))
|
_rcs.append(_spear(n, 1.2, 0.09, Color(0.863, 0.937, 1.0, 0.95), Color(0.41, 0.67, 1.0, 0.35), Color(0.25, 0.45, 0.8, 0.0), 0.82, 2, float(i)))
|
||||||
@@ -245,27 +285,34 @@ func _arm() -> void:
|
|||||||
_aborts.append(_make_abort(n))
|
_aborts.append(_make_abort(n))
|
||||||
|
|
||||||
|
|
||||||
|
func _fallback_abort() -> void:
|
||||||
|
var spots: Array[Vector3] = [Vector3(0.55, -0.35, 0.55), Vector3(-0.55, -0.35, 0.55), Vector3(0.55, -0.35, -0.55), Vector3(-0.55, -0.35, -0.55)]
|
||||||
|
for i in spots.size():
|
||||||
|
var s := Marker3D.new()
|
||||||
|
s.name = "Vfx_Abort_Fallback_%d" % i
|
||||||
|
s.position = spots[i]
|
||||||
|
add_child(s)
|
||||||
|
_aborts.append(_make_abort(s))
|
||||||
|
print("Kestrel driver built fallback abort plumes")
|
||||||
|
|
||||||
|
|
||||||
func _burn(wish: Vector3, delta: float) -> void:
|
func _burn(wish: Vector3, delta: float) -> void:
|
||||||
var ship := global_transform.basis
|
var ship := global_transform.basis
|
||||||
var ship_inv := ship.inverse()
|
var cmd := Vector3(att_pitch, att_roll, -att_yaw)
|
||||||
# FIX: capsule yaws about +Z by +att_yaw (rotate_object_local(FORWARD, -yaw) == +Z, +yaw),
|
|
||||||
# so the commanded angular velocity is (pitch, roll, +yaw). The old -att_yaw fired the
|
|
||||||
# opposite-side jets for yaw.
|
|
||||||
var cmd := Vector3(att_pitch, att_roll, att_yaw)
|
|
||||||
var turning := cmd.length() > 0.15
|
var turning := cmd.length() > 0.15
|
||||||
var origin := global_position
|
var origin := global_position
|
||||||
for ribbon in _rcs:
|
for ribbon in _rcs:
|
||||||
var want := 0.0
|
var want := 0.0
|
||||||
var exhaust: Vector3 = (ship_inv * ribbon.global_transform.basis.y).normalized()
|
var exhaust: Vector3 = (ship.inverse() * ribbon.global_transform.basis.y).normalized()
|
||||||
if turning:
|
if turning:
|
||||||
var local_pos: Vector3 = ship_inv * (ribbon.global_position - origin)
|
var local_pos: Vector3 = ship.inverse() * (ribbon.global_position - origin)
|
||||||
if local_pos.length() < 0.05:
|
if local_pos.length() < 0.05:
|
||||||
local_pos = -exhaust
|
local_pos = -exhaust
|
||||||
var torque: Vector3 = local_pos.cross(-exhaust)
|
var torque: Vector3 = local_pos.cross(-exhaust)
|
||||||
if torque.dot(cmd) > 0.12:
|
if torque.dot(cmd) > 0.12:
|
||||||
want = 1.0
|
want = 1.0
|
||||||
if want < 0.5 and wish != Vector3.ZERO:
|
if want < 0.5 and wish != Vector3.ZERO:
|
||||||
if exhaust.dot(-(ship_inv * wish)) > 0.7:
|
if exhaust.dot(-(ship.inverse() * wish)) > 0.7:
|
||||||
want = 1.0
|
want = 1.0
|
||||||
var burn := float(ribbon.get_meta("burn"))
|
var burn := float(ribbon.get_meta("burn"))
|
||||||
burn = _smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
burn = _smooth(burn, want, delta, 0.08 if want > burn else 0.14)
|
||||||
@@ -306,8 +353,6 @@ func _burn(wish: Vector3, delta: float) -> void:
|
|||||||
light.light_energy = _abort_hold * 8.0 if on else 0.0
|
light.light_energy = _abort_hold * 8.0 if on else 0.0
|
||||||
|
|
||||||
|
|
||||||
## Exhaust direction in the socket's parent space. The model puts each Vfx_ socket's
|
|
||||||
## translation along its exhaust direction (RCS = +Y, Abort = -Y), so position is the aim.
|
|
||||||
func _exhaust(socket: Node3D) -> Vector3:
|
func _exhaust(socket: Node3D) -> Vector3:
|
||||||
var dir := socket.position
|
var dir := socket.position
|
||||||
if dir.length() < 0.001:
|
if dir.length() < 0.001:
|
||||||
@@ -365,13 +410,13 @@ func _taper(length: float, w0: float, w1: float) -> ArrayMesh:
|
|||||||
return st.commit()
|
return st.commit()
|
||||||
|
|
||||||
|
|
||||||
func _crossed(socket: Node3D, mesh: ArrayMesh, mat: ShaderMaterial, planes: int, node_name: String, length: float, opacity: float, flick_seed: float) -> Node3D:
|
func _crossed(socket: Node3D, mesh: ArrayMesh, mat: ShaderMaterial, planes: int, node_name: String, length: float, opacity: float, seed: float) -> Node3D:
|
||||||
var g := Node3D.new()
|
var g := Node3D.new()
|
||||||
g.name = node_name
|
g.name = node_name
|
||||||
g.set_meta("mat", mat)
|
g.set_meta("mat", mat)
|
||||||
g.set_meta("len", length)
|
g.set_meta("len", length)
|
||||||
g.set_meta("opacity", opacity)
|
g.set_meta("opacity", opacity)
|
||||||
g.set_meta("seed", flick_seed)
|
g.set_meta("seed", seed)
|
||||||
g.set_meta("burn", 0.0)
|
g.set_meta("burn", 0.0)
|
||||||
for i in planes:
|
for i in planes:
|
||||||
var m := MeshInstance3D.new()
|
var m := MeshInstance3D.new()
|
||||||
@@ -387,8 +432,8 @@ func _crossed(socket: Node3D, mesh: ArrayMesh, mat: ShaderMaterial, planes: int,
|
|||||||
return g
|
return g
|
||||||
|
|
||||||
|
|
||||||
func _spear(socket: Node3D, length: float, width: float, root: Color, mid: Color, tip: Color, opacity: float, planes: int, flick_seed: float) -> Node3D:
|
func _spear(socket: Node3D, length: float, width: float, root: Color, mid: Color, tip: Color, opacity: float, planes: int, seed: float) -> Node3D:
|
||||||
return _crossed(socket, _sheet(length, width), _ribbon_mat(root, mid, tip), planes, "Spear", length, opacity, flick_seed)
|
return _crossed(socket, _sheet(length, width), _ribbon_mat(root, mid, tip), planes, "Spear", length, opacity, seed)
|
||||||
|
|
||||||
|
|
||||||
func _taper_ribbon(socket: Node3D, length: float, w0: float, w1: float, root: Color, mid: Color, tip: Color, opacity: float, planes: int, node_name: String) -> Node3D:
|
func _taper_ribbon(socket: Node3D, length: float, w0: float, w1: float, root: Color, mid: Color, tip: Color, opacity: float, planes: int, node_name: String) -> Node3D:
|
||||||
@@ -407,8 +452,8 @@ func _set_spear(ribbon: Node3D, amount: float, time: float) -> void:
|
|||||||
if a < 0.02:
|
if a < 0.02:
|
||||||
_set_opacity(ribbon, 0.0)
|
_set_opacity(ribbon, 0.0)
|
||||||
return
|
return
|
||||||
var flick_seed := float(ribbon.get_meta("seed"))
|
var seed := float(ribbon.get_meta("seed"))
|
||||||
var flick := 0.82 + 0.18 * sin(time * 37.0 + flick_seed * 2.1)
|
var flick := 0.82 + 0.18 * sin(time * 37.0 + seed * 2.1)
|
||||||
ribbon.scale = Vector3.ONE
|
ribbon.scale = Vector3.ONE
|
||||||
_set_opacity(ribbon, float(ribbon.get_meta("opacity")) * minf(a, 1.0) * flick)
|
_set_opacity(ribbon, float(ribbon.get_meta("opacity")) * minf(a, 1.0) * flick)
|
||||||
|
|
||||||
@@ -534,11 +579,11 @@ func _spot(socket: Node3D) -> SpotLight3D:
|
|||||||
func _make_abort(socket: Node3D) -> Dictionary:
|
func _make_abort(socket: Node3D) -> Dictionary:
|
||||||
var core := _taper_ribbon(socket, 2.1, 0.06, 0.20, Color(1.0, 0.97, 0.92, 1.0), Color(1.0, 0.45, 0.12, 0.72), Color(0.40, 0.08, 0.02, 0.0), 0.95, 3, "AbortCore")
|
var core := _taper_ribbon(socket, 2.1, 0.06, 0.20, Color(1.0, 0.97, 0.92, 1.0), Color(1.0, 0.45, 0.12, 0.72), Color(0.40, 0.08, 0.02, 0.0), 0.95, 3, "AbortCore")
|
||||||
var sheath := _taper_ribbon(socket, 5.2, 0.10, 0.78, Color(1.0, 0.55, 0.18, 1.0), Color(1.0, 0.28, 0.06, 0.55), Color(0.35, 0.06, 0.01, 0.0), 0.82, 3, "AbortSheath")
|
var sheath := _taper_ribbon(socket, 5.2, 0.10, 0.78, Color(1.0, 0.55, 0.18, 1.0), Color(1.0, 0.28, 0.06, 0.55), Color(0.35, 0.06, 0.01, 0.0), 0.82, 3, "AbortSheath")
|
||||||
return {
|
var row: Dictionary = {}
|
||||||
"core": core,
|
row["core"] = core
|
||||||
"sheath": sheath,
|
row["sheath"] = sheath
|
||||||
"sparks": _sparks(socket, "AbortCoreSparks", 10, 0.15, 18.0, 28.0, 1.6, 0.05, 6.0),
|
row["sparks"] = _sparks(socket, "AbortCoreSparks", 10, 0.15, 18.0, 28.0, 1.6, 0.05, 6.0)
|
||||||
"sheath_sparks": _sparks(socket, "AbortSheathSparks", 18, 0.26, 14.0, 22.0, 4.5, 0.09, 8.0),
|
row["sheath_sparks"] = _sparks(socket, "AbortSheathSparks", 18, 0.26, 14.0, 22.0, 4.5, 0.09, 8.0)
|
||||||
"flash": _abort_flash(socket),
|
row["flash"] = _abort_flash(socket)
|
||||||
"light": _spot(socket),
|
row["light"] = _spot(socket)
|
||||||
}
|
return row
|
||||||
|
|||||||
Reference in New Issue
Block a user