Files
Ransom-s-wake/game/presentation/cockpit/cockpit_analog_gauges.gd
T
OlivierandClaude Opus 5.5 2f9b7c0894
Quality Gate / Quality Gate (pull_request) Successful in 9m4s
feat(cockpit): replace procedural cockpit with twelve scripted models
Instance twelve Blender-scripted glTF models in the cockpit scene and
rebuild the cabin around them: gauge panel, sonar, switch panel, alert
box, speaker, porthole, hull wall, caged lamps, gas bottle, handrails
and side consoles. Presentation only; no domain, application or world
rule changes beyond enabling glow in the test environment.

- Needles, alert lamps and sonar screen are driven by the existing
  snapshots; the alert box now carries SYS/ATTN/ALRM.
- Depth dial reads percent of certified depth.
- Sonar display restyled as a turquoise holographic relief with a
  shader grid and height scale; SubViewport matches the glass (960x604).
- Cabin lighting retuned: warm pooled lamp light, cool porthole rim,
  lamp shadows, dial backlight.
- Procedural stand-ins removed (bulkhead, tanks, consoles, levers,
  indicator columns, placeholder pipe run, dial face PNGs).
- Tests adapted where nodes disappeared; one assertion removed
  (pipe_segment_count) until the pipe kit is placed.
- Model .glb files are committed without LFS; textures Godot extracts
  next to them at import are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:36:57 +02:00

102 lines
3.2 KiB
GDScript

class_name CockpitAnalogGauges
extends Node3D
## Drives the imported gauge panel model: four needles and its six pilot lights.
## The model scene is instanced by `cockpit_interior.tscn`; this script only
## rotates needles and sets emission, it never rebuilds geometry.
const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh_factory.gd")
## Printed dial sweep: ratio 0 points lower-left, ratio 1 lower-right, clockwise.
const NEEDLE_DEGREES_AT_ZERO := 120.0
const NEEDLE_DEGREES_AT_FULL := -120.0
## The snapshot carries no per-instrument lamp state, so the panel pilot lights
## stay dark; the alert level is reported by `CockpitAlertBox`.
const PILOT_LIGHT_REST_ENERGY := 0.0
## Scales the dial backlight baked in the model (1.0 = as exported), so the
## paper reads as lit ivory without outshining the cabin's warm lamps.
const DIAL_BACKLIGHT_ENERGY := 0.7
const NEEDLE_NAMES: Array[String] = [
"Needle_Oxygen",
"Needle_Battery",
"Needle_Hull",
"Needle_Depth",
]
var _needles: Dictionary[String, Node3D] = {}
var _dials: MeshInstance3D
var _pilot_light_materials: Array[BaseMaterial3D] = []
func _ready() -> void:
for needle_name: String in NEEDLE_NAMES:
var needle := find_child(needle_name, true, false) as Node3D
if needle == null:
push_error("Gauge panel model has no %s node" % needle_name)
continue
_needles[needle_name] = needle
_dials = find_child("GaugeDials", true, false) as MeshInstance3D
var dial_material := CockpitMeshFactory.own_surface_material(_dials)
if dial_material != null:
dial_material.emission_energy_multiplier = DIAL_BACKLIGHT_ENERGY
for light: Node in find_children("Light_*", "MeshInstance3D", true, false):
var material := CockpitMeshFactory.own_surface_material(light as MeshInstance3D)
if material != null:
material.emission_energy_multiplier = PILOT_LIGHT_REST_ENERGY
_pilot_light_materials.append(material)
func present(
oxygen_ratio: float,
battery_ratio: float,
hull_ratio: float,
depth_ratio: float
) -> void:
_set_needle_ratio("Needle_Oxygen", oxygen_ratio)
_set_needle_ratio("Needle_Battery", battery_ratio)
_set_needle_ratio("Needle_Hull", hull_ratio)
_set_needle_ratio("Needle_Depth", depth_ratio)
func gauge_count() -> int:
return _needles.size()
func pilot_light_count() -> int:
return _pilot_light_materials.size()
func lit_pilot_light_count() -> int:
var count := 0
for material: BaseMaterial3D in _pilot_light_materials:
if material.emission_energy_multiplier > 0.0:
count += 1
return count
func needle_degrees(needle_name: String) -> float:
var needle: Node3D = _needles.get(needle_name)
return 0.0 if needle == null else needle.rotation_degrees.z
func battery_needle_degrees() -> float:
return needle_degrees("Needle_Battery")
func dial_texture_is_loaded() -> bool:
if _dials == null or _dials.mesh == null or _dials.mesh.get_surface_count() == 0:
return false
var material := _dials.mesh.surface_get_material(0) as BaseMaterial3D
return material != null and material.albedo_texture != null
func _set_needle_ratio(needle_name: String, ratio: float) -> void:
var needle: Node3D = _needles.get(needle_name)
if needle != null:
needle.rotation_degrees.z = lerpf(
NEEDLE_DEGREES_AT_ZERO,
NEEDLE_DEGREES_AT_FULL,
clampf(ratio, 0.0, 1.0)
)