Files
OlivierandClaude Fable 5.1 f0072a3e11
Quality Gate / Quality Gate (pull_request) Successful in 6m55s
Add sunlit shallows site with localized sun shafts
New visual test site under game/world/sites: flat sandy floor at
-40 m, calm surface at 0 m, fixed daytime sun ahead of and above
the submarine. Light shafts come from a shadow-only surface
occluder whose alpha-scissor ripples open only inside a pool a few
tens of metres ahead; a subtractive FogVolume cancels the global
volumetric fog above the water, and an unshaded ceiling mesh shows
the lit underside of the surface inside that pool. A composed
world (sunlit_shallows_world.tscn) mounts the submarine, cockpit
and camera rig in it, with the projectors kept out of the fog.

Cockpit: the porthole glass moves to its own render layer with a
rougher material and the porthole fill light excludes that layer,
so interior lamps no longer mirror as hard specular points.

Quality gate: new integration suite for the site (42 checks), three
glass assertions in the foundation suite, and the Input-access
architecture rule now matches both path separators so the gate runs
on Windows. project.godot raises the volumetric fog froxel grid to
128 so the shafts stay readable.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 23:18:56 +02:00

169 lines
5.4 KiB
GDScript

class_name CockpitDetailing
extends Node3D
const DiveUpdate = preload("res://game/domain/dive/dive_update.gd")
const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd")
const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd")
const CockpitAlertBox = preload("res://game/presentation/cockpit/cockpit_alert_box.gd")
const CockpitAnalogGauges = preload("res://game/presentation/cockpit/cockpit_analog_gauges.gd")
const CockpitDashboardDetailing = preload(
"res://game/presentation/cockpit/cockpit_dashboard_detailing.gd"
)
const CockpitMaterialPalette = preload("res://game/presentation/cockpit/cockpit_material_palette.gd")
const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh_factory.gd")
const CockpitSideEquipment = preload(
"res://game/presentation/cockpit/cockpit_side_equipment.gd"
)
const CockpitSonarConsole = preload(
"res://game/presentation/cockpit/cockpit_sonar_console.gd"
)
const CockpitPracticalLighting = preload(
"res://game/presentation/cockpit/cockpit_practical_lighting.gd"
)
const CockpitSwitchPanel = preload(
"res://game/presentation/cockpit/cockpit_switch_panel.gd"
)
var _materials: CockpitMaterialPalette
var _painted_steel: Material
var _dark_steel: Material
var _analog_gauges: CockpitAnalogGauges
var _alert_box: CockpitAlertBox
var _switch_panel: CockpitSwitchPanel
var _gas_bottle: Node3D
var _dashboard_detailing: CockpitDashboardDetailing
var _side_equipment: CockpitSideEquipment
var _sonar_console: CockpitSonarConsole
var _practical_lighting: CockpitPracticalLighting
func _ready() -> void:
_create_materials()
_apply_materials_to_existing_cockpit()
_build_dashboard_instrumentation()
_build_equipment_assemblies()
func present(update: DiveUpdate, telemetry: VehicleTelemetry) -> void:
if update == null or update.snapshot == null or telemetry == null:
return
var snapshot = update.snapshot
var alert_level: DiveAlertLevel.Value = snapshot.alerts.maximum
if _analog_gauges != null:
_analog_gauges.present(
snapshot.oxygen.ratio,
snapshot.battery.ratio,
snapshot.hull.ratio,
clampf(
snapshot.depth.current_m
/ maxf(snapshot.depth.certified_m, 0.001),
0.0,
1.0
)
)
if _alert_box != null:
_alert_box.present(alert_level)
_practical_lighting.present(alert_level)
func battery_needle_degrees() -> float:
return 0.0 if _analog_gauges == null else _analog_gauges.battery_needle_degrees()
func textures_are_loaded() -> bool:
return (
_materials != null
and _materials.overhead_steel.albedo_texture != null
and _analog_gauges != null
and _analog_gauges.dial_texture_is_loaded()
)
func dashboard_control_count() -> int:
return 0 if _switch_panel == null else _switch_panel.control_count()
func side_equipment_count() -> int:
if _side_equipment == null:
return 0
return _side_equipment.junction_box_count() + (0 if _gas_bottle == null else 1)
func side_control_count() -> int:
if _side_equipment == null:
return 0
var bottle_valve_count := (
0
if _gas_bottle == null
else _gas_bottle.find_children("*ValveWheel", "Node3D", true, false).size()
)
return _side_equipment.control_count() + bottle_valve_count
func sonar_control_count() -> int:
return 0 if _sonar_console == null else _sonar_console.control_count()
func practical_light_count() -> int:
return 0 if _practical_lighting == null else _practical_lighting.practical_light_count()
func fill_light_ignores_porthole_glass() -> bool:
return _practical_lighting != null and _practical_lighting.fill_light_ignores_porthole_glass()
func gauges_are_dashboard_mounted() -> bool:
return _analog_gauges != null and _analog_gauges.get_parent() == get_parent().get_node("Dashboard")
func _create_materials() -> void:
_materials = CockpitMaterialPalette.new()
_painted_steel = _materials.painted_steel
_dark_steel = _materials.dark_steel
func _apply_materials_to_existing_cockpit() -> void:
_apply_material_to_paths(
_painted_steel,
[
"CabinShell/LeftWall",
"CabinShell/RightWall",
]
)
_apply_material_to_paths(_dark_steel, ["CabinShell/RearBulkhead"])
_apply_material_to_paths(
_materials.overhead_steel,
["CabinShell/Ceiling"]
)
func _apply_material_to_paths(material: Material, paths: Array[String]) -> void:
var cockpit := get_parent()
for path: String in paths:
var mesh := cockpit.get_node_or_null(path) as MeshInstance3D
if mesh != null:
mesh.material_override = material
func _build_dashboard_instrumentation() -> void:
var dashboard := get_parent().get_node_or_null("Dashboard") as Node3D
if dashboard == null:
return
_dashboard_detailing = CockpitDashboardDetailing.new()
dashboard.add_child(_dashboard_detailing)
_dashboard_detailing.hide_obsolete_elements(get_parent())
_analog_gauges = dashboard.get_node_or_null("AnalogGauges") as CockpitAnalogGauges
func _build_equipment_assemblies() -> void:
_side_equipment = CockpitSideEquipment.new(_materials)
add_child(_side_equipment)
var cockpit := get_parent()
_sonar_console = cockpit.get_node_or_null("SonarMonitor") as CockpitSonarConsole
_alert_box = cockpit.get_node_or_null("AlertBox") as CockpitAlertBox
_switch_panel = cockpit.get_node_or_null("SwitchPanel") as CockpitSwitchPanel
_gas_bottle = cockpit.get_node_or_null("GasBottle") as Node3D
_practical_lighting = CockpitPracticalLighting.new(_materials)
add_child(_practical_lighting)
# The alert box hangs between the left lamp and the gas bottle: casting, it
# puts the bottle in full shadow.
CockpitMeshFactory.disable_shadow_casting(_alert_box)