Build validated industrial submarine cockpit
This commit is contained in:
1 parent
dffe790926
commit
fdebbafeae
19 files changed
+906
-277
No files matched your search
@@ -63,6 +63,8 @@ hors reprise après échec. Ces nombres seront corrigés par playtest.
|
|||||||
### Plongée
|
### Plongée
|
||||||
|
|
||||||
- POV intérieur et hublot circulaire.
|
- POV intérieur et hublot circulaire.
|
||||||
|
- Direction visuelle du cockpit alignée sur
|
||||||
|
`docs/visual_targets/cockpit-objective-v1.jpg`, sans exigence pixel perfect.
|
||||||
- Pilotage inertiel avec retour immédiat des commandes.
|
- Pilotage inertiel avec retour immédiat des commandes.
|
||||||
- Oxygène, batterie électrique, profondeur, pression et intégrité.
|
- Oxygène, batterie électrique, profondeur, pression et intégrité.
|
||||||
- Alarmes texte, couleur et son.
|
- Alarmes texte, couleur et son.
|
||||||
@@ -817,10 +819,10 @@ structurée par des rails, séparateurs, vis et banques de commutateurs physique
|
|||||||
|
|
||||||
Passe du 4 octobre 2026 : les quatre volumes rectangulaires autour du hublot
|
Passe du 4 octobre 2026 : les quatre volumes rectangulaires autour du hublot
|
||||||
sont remplacés par une cloison annulaire bombée continue, serrée entre deux
|
sont remplacés par une cloison annulaire bombée continue, serrée entre deux
|
||||||
raidisseurs métalliques et boulonnée. Le tableau reprend la hiérarchie de la
|
raidisseurs métalliques et boulonnée. La cible approuvée
|
||||||
référence `design.jpg` : quatre cadrans principaux de tailles variées, petits
|
`docs/visual_targets/cockpit-objective-v1.jpg` fixe la hiérarchie : quatre
|
||||||
afficheurs de valeurs exactes et trois voyants encastrés dont un seul reflète
|
cadrans principaux de tailles variées, petits afficheurs de valeurs exactes,
|
||||||
l'état d'alerte courant.
|
sonar bathymétrique séparé, équipements latéraux et lampes navales en cage.
|
||||||
|
|
||||||
- Construire une coque intérieure 3D fixe par rapport au véhicule, indépendante
|
- Construire une coque intérieure 3D fixe par rapport au véhicule, indépendante
|
||||||
du regard de la caméra.
|
du regard de la caméra.
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Cibles visuelles validées
|
||||||
|
|
||||||
|
Ce dossier contient les références visuelles explicitement approuvées pour la
|
||||||
|
production. Elles cadrent une intention et une hiérarchie visuelle ; elles ne
|
||||||
|
constituent pas une exigence de reproduction pixel perfect.
|
||||||
|
|
||||||
|
## Cockpit du mini-sous-marin
|
||||||
|
|
||||||
|
`cockpit-objective-v1.jpg` est la cible validée le 4 octobre 2026 pour la vue
|
||||||
|
intérieure du sous-marin. Elle a été générée spécifiquement pour Ransom's Wake
|
||||||
|
avec le générateur d'images OpenAI intégré, à partir de la direction artistique
|
||||||
|
fournie par le projet.
|
||||||
|
|
||||||
|
La scène de production doit en conserver les caractéristiques principales :
|
||||||
|
|
||||||
|
- grand hublot circulaire cerclé d'acier boulonné ;
|
||||||
|
- tableau inférieur dense avec quatre cadrans analogiques lisibles ;
|
||||||
|
- sonar bathymétrique séparé sur le côté droit ;
|
||||||
|
- réservoirs, vannes et commandes de survie sur le côté gauche ;
|
||||||
|
- boîtiers électriques, gaines et câbles sur le côté droit ;
|
||||||
|
- deux lampes navales chaudes protégées par une cage ;
|
||||||
|
- acier peint usé, métal brossé, laiton vieilli et caoutchouc sombre.
|
||||||
|
|
||||||
|
Les valeurs exactes restent produites par les contrats de télémétrie du jeu.
|
||||||
|
La présentation peut simplifier le nombre de petites pièces pour garantir la
|
||||||
|
lisibilité à 720p et les performances de la verticale.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 742 KiB |
@@ -20,3 +20,18 @@ hub, glass, text, numbers, logo or watermark.
|
|||||||
Both source outputs were resized to 1024 × 1024 and stripped of metadata for
|
Both source outputs were resized to 1024 × 1024 and stripped of metadata for
|
||||||
runtime use. The original generated outputs remain in the local Codex image
|
runtime use. The original generated outputs remain in the local Codex image
|
||||||
generation store and are not required by the game.
|
generation store and are not required by the game.
|
||||||
|
|
||||||
|
## Dedicated instrument faces
|
||||||
|
|
||||||
|
`oxygen_gauge_face_albedo_v2.png`, `battery_gauge_face_albedo_v2.png`,
|
||||||
|
`hull_gauge_face_albedo_v2.png` and `depth_gauge_face_albedo_v2.png` were
|
||||||
|
generated on 4 October 2026 with the same built-in tool. The approved cockpit
|
||||||
|
target and then the oxygen face were used only as project-owned style
|
||||||
|
references.
|
||||||
|
|
||||||
|
Prompt summary: orthographic mechanical submarine gauges in aged ivory enamel,
|
||||||
|
with exact French labels and units, dense numerical scales, restrained patina,
|
||||||
|
and green, amber and red operating sectors. The generated needles, bezels and
|
||||||
|
glass were explicitly excluded because those parts are modeled and animated in
|
||||||
|
Godot. The source outputs were resized to 1024 × 1024 and given a circular alpha
|
||||||
|
mask for flat, distortion-free mounting inside the physical bezels.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
@@ -13,7 +13,6 @@ var _depth_needle: Node3D
|
|||||||
var _indicator_materials: Array[StandardMaterial3D] = []
|
var _indicator_materials: Array[StandardMaterial3D] = []
|
||||||
var _active_indicator_count := 0
|
var _active_indicator_count := 0
|
||||||
|
|
||||||
|
|
||||||
func _init(materials: CockpitMaterialPalette) -> void:
|
func _init(materials: CockpitMaterialPalette) -> void:
|
||||||
_materials = materials
|
_materials = materials
|
||||||
name = "AnalogGauges"
|
name = "AnalogGauges"
|
||||||
@@ -21,33 +20,28 @@ func _init(materials: CockpitMaterialPalette) -> void:
|
|||||||
_build_indicator_bank()
|
_build_indicator_bank()
|
||||||
_oxygen_needle = _add_gauge(
|
_oxygen_needle = _add_gauge(
|
||||||
"OxygenGauge",
|
"OxygenGauge",
|
||||||
Vector3(-0.50, 0.12, 0.145),
|
Vector3(-0.52, 0.12, 0.145),
|
||||||
"%",
|
0.94,
|
||||||
0.72
|
_materials.oxygen_gauge_face
|
||||||
)
|
)
|
||||||
_battery_needle = _add_gauge(
|
_battery_needle = _add_gauge(
|
||||||
"BatteryGauge",
|
"BatteryGauge",
|
||||||
Vector3(-0.22, 0.12, 0.145),
|
Vector3(-0.22, 0.12, 0.145),
|
||||||
"%",
|
0.94,
|
||||||
0.72
|
_materials.battery_gauge_face
|
||||||
)
|
)
|
||||||
_hull_needle = _add_gauge(
|
_hull_needle = _add_gauge(
|
||||||
"HullGauge",
|
"HullGauge",
|
||||||
Vector3(0.05, 0.12, 0.145),
|
Vector3(0.09, 0.12, 0.145),
|
||||||
"%",
|
0.86,
|
||||||
0.56
|
_materials.hull_gauge_face
|
||||||
)
|
)
|
||||||
_depth_needle = _add_gauge(
|
_depth_needle = _add_gauge(
|
||||||
"DepthGauge",
|
"DepthGauge",
|
||||||
Vector3(0.40, 0.12, 0.145),
|
Vector3(0.47, 0.12, 0.145),
|
||||||
"%",
|
1.12,
|
||||||
0.90
|
_materials.depth_gauge_face
|
||||||
)
|
)
|
||||||
_add_cluster_label("OXYGÈNE", Vector3(-0.50, 0.025, 0.170))
|
|
||||||
_add_cluster_label("BATTERIE", Vector3(-0.22, 0.025, 0.170))
|
|
||||||
_add_cluster_label("COQUE", Vector3(0.05, 0.045, 0.170))
|
|
||||||
_add_cluster_label("PROFONDEUR", Vector3(0.40, 0.005, 0.170))
|
|
||||||
|
|
||||||
|
|
||||||
func present(
|
func present(
|
||||||
oxygen_ratio: float,
|
oxygen_ratio: float,
|
||||||
@@ -66,15 +60,12 @@ func present(
|
|||||||
func battery_needle_degrees() -> float:
|
func battery_needle_degrees() -> float:
|
||||||
return 0.0 if _battery_needle == null else _battery_needle.rotation_degrees.z
|
return 0.0 if _battery_needle == null else _battery_needle.rotation_degrees.z
|
||||||
|
|
||||||
|
|
||||||
func indicator_count() -> int:
|
func indicator_count() -> int:
|
||||||
return _indicator_materials.size()
|
return _indicator_materials.size()
|
||||||
|
|
||||||
|
|
||||||
func gauge_count() -> int:
|
func gauge_count() -> int:
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
|
|
||||||
func active_indicator_count() -> int:
|
func active_indicator_count() -> int:
|
||||||
return _active_indicator_count
|
return _active_indicator_count
|
||||||
|
|
||||||
@@ -84,18 +75,17 @@ func _build_instrument_cluster() -> void:
|
|||||||
"InstrumentClusterHousing",
|
"InstrumentClusterHousing",
|
||||||
self,
|
self,
|
||||||
Vector3(-0.04, 0.105, 0.095),
|
Vector3(-0.04, 0.105, 0.095),
|
||||||
Vector3(1.26, 0.255, 0.105),
|
Vector3(1.42, 0.31, 0.105),
|
||||||
_materials.overhead_steel
|
_materials.overhead_steel
|
||||||
)
|
)
|
||||||
CockpitMeshFactory.add_box(
|
CockpitMeshFactory.add_box(
|
||||||
"InstrumentClusterFoot",
|
"InstrumentClusterFoot",
|
||||||
self,
|
self,
|
||||||
Vector3(-0.04, -0.025, 0.075),
|
Vector3(-0.04, -0.025, 0.075),
|
||||||
Vector3(1.32, 0.040, 0.14),
|
Vector3(1.48, 0.040, 0.14),
|
||||||
_materials.dark_steel
|
_materials.dark_steel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _build_indicator_bank() -> void:
|
func _build_indicator_bank() -> void:
|
||||||
var root := Node3D.new()
|
var root := Node3D.new()
|
||||||
root.name = "IndicatorBank"
|
root.name = "IndicatorBank"
|
||||||
@@ -111,7 +101,7 @@ func _build_indicator_bank() -> void:
|
|||||||
CockpitMeshFactory.add_cylinder(
|
CockpitMeshFactory.add_cylinder(
|
||||||
"%sBezel" % captions[indicator_index],
|
"%sBezel" % captions[indicator_index],
|
||||||
root,
|
root,
|
||||||
Vector3(0.205, y, 0.156),
|
Vector3(0.68, y, 0.156),
|
||||||
0.016,
|
0.016,
|
||||||
0.012,
|
0.012,
|
||||||
_materials.brass,
|
_materials.brass,
|
||||||
@@ -122,7 +112,7 @@ func _build_indicator_bank() -> void:
|
|||||||
CockpitMeshFactory.add_cylinder(
|
CockpitMeshFactory.add_cylinder(
|
||||||
"%sLens" % captions[indicator_index],
|
"%sLens" % captions[indicator_index],
|
||||||
root,
|
root,
|
||||||
Vector3(0.205, y, 0.165),
|
Vector3(0.68, y, 0.165),
|
||||||
0.009,
|
0.009,
|
||||||
0.008,
|
0.008,
|
||||||
material,
|
material,
|
||||||
@@ -134,10 +124,9 @@ func _build_indicator_bank() -> void:
|
|||||||
label.font_size = 8
|
label.font_size = 8
|
||||||
label.pixel_size = 0.00042
|
label.pixel_size = 0.00042
|
||||||
label.modulate = Color(0.70, 0.67, 0.56)
|
label.modulate = Color(0.70, 0.67, 0.56)
|
||||||
label.position = Vector3(0.235, y, 0.168)
|
label.position = Vector3(0.71, y, 0.168)
|
||||||
root.add_child(label)
|
root.add_child(label)
|
||||||
|
|
||||||
|
|
||||||
func _indicator_material(color: Color) -> StandardMaterial3D:
|
func _indicator_material(color: Color) -> StandardMaterial3D:
|
||||||
var material := StandardMaterial3D.new()
|
var material := StandardMaterial3D.new()
|
||||||
material.albedo_color = color * 0.12
|
material.albedo_color = color * 0.12
|
||||||
@@ -148,7 +137,6 @@ func _indicator_material(color: Color) -> StandardMaterial3D:
|
|||||||
material.emission_energy_multiplier = 0.04
|
material.emission_energy_multiplier = 0.04
|
||||||
return material
|
return material
|
||||||
|
|
||||||
|
|
||||||
func _update_indicator_bank(alert_level: DiveAlertLevel.Value) -> void:
|
func _update_indicator_bank(alert_level: DiveAlertLevel.Value) -> void:
|
||||||
var active_index := 0
|
var active_index := 0
|
||||||
if alert_level >= DiveAlertLevel.Value.CRITICAL:
|
if alert_level >= DiveAlertLevel.Value.CRITICAL:
|
||||||
@@ -162,22 +150,11 @@ func _update_indicator_bank(alert_level: DiveAlertLevel.Value) -> void:
|
|||||||
_active_indicator_count = 1
|
_active_indicator_count = 1
|
||||||
|
|
||||||
|
|
||||||
func _add_cluster_label(caption: String, position: Vector3) -> void:
|
|
||||||
var label := Label3D.new()
|
|
||||||
label.name = "%sLabel" % caption.to_pascal_case().replace(" ", "")
|
|
||||||
label.text = caption
|
|
||||||
label.font_size = 12
|
|
||||||
label.pixel_size = 0.00055
|
|
||||||
label.modulate = Color(0.76, 0.72, 0.60)
|
|
||||||
label.position = position
|
|
||||||
add_child(label)
|
|
||||||
|
|
||||||
|
|
||||||
func _add_gauge(
|
func _add_gauge(
|
||||||
gauge_name: String,
|
gauge_name: String,
|
||||||
position: Vector3,
|
position: Vector3,
|
||||||
caption: String,
|
gauge_scale: float,
|
||||||
gauge_scale: float
|
face_material: Material
|
||||||
) -> Node3D:
|
) -> Node3D:
|
||||||
var gauge := Node3D.new()
|
var gauge := Node3D.new()
|
||||||
gauge.name = gauge_name
|
gauge.name = gauge_name
|
||||||
@@ -206,42 +183,40 @@ func _add_gauge(
|
|||||||
_materials.brass,
|
_materials.brass,
|
||||||
Vector3(90.0, 0.0, 0.0)
|
Vector3(90.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
var bezel := MeshInstance3D.new()
|
_add_bezel(gauge, 0.108, 0.046, _materials.dark_steel)
|
||||||
var bezel_mesh := CylinderMesh.new()
|
_add_bezel(gauge, 0.099, 0.041, _materials.brushed_steel)
|
||||||
bezel_mesh.top_radius = 0.095
|
|
||||||
bezel_mesh.bottom_radius = 0.095
|
|
||||||
bezel_mesh.height = 0.035
|
|
||||||
bezel_mesh.radial_segments = 32
|
|
||||||
bezel.mesh = bezel_mesh
|
|
||||||
bezel.material_override = _materials.brushed_steel
|
|
||||||
bezel.rotation_degrees.x = 90.0
|
|
||||||
gauge.add_child(bezel)
|
|
||||||
var face := MeshInstance3D.new()
|
var face := MeshInstance3D.new()
|
||||||
var face_mesh := CylinderMesh.new()
|
var printed_face := QuadMesh.new()
|
||||||
face_mesh.top_radius = 0.078
|
printed_face.size = Vector2(0.178, 0.178)
|
||||||
face_mesh.bottom_radius = 0.078
|
face.mesh = printed_face
|
||||||
face_mesh.height = 0.039
|
face.position.z = 0.031
|
||||||
face_mesh.radial_segments = 32
|
face.material_override = face_material
|
||||||
face.mesh = face_mesh
|
|
||||||
face.material_override = _materials.gauge_face
|
|
||||||
face.rotation_degrees.x = 90.0
|
|
||||||
face.position.z = 0.003
|
|
||||||
gauge.add_child(face)
|
gauge.add_child(face)
|
||||||
_add_dial_ticks(gauge)
|
|
||||||
_add_scale_label(gauge, "0", Vector3(-0.052, -0.018, 0.028))
|
|
||||||
_add_scale_label(gauge, "50", Vector3(0.0, 0.052, 0.028))
|
|
||||||
_add_scale_label(gauge, "100", Vector3(0.052, -0.018, 0.028))
|
|
||||||
var needle_root := Node3D.new()
|
var needle_root := Node3D.new()
|
||||||
needle_root.name = "Needle"
|
needle_root.name = "Needle"
|
||||||
needle_root.position.z = 0.027
|
needle_root.position.z = 0.038
|
||||||
gauge.add_child(needle_root)
|
gauge.add_child(needle_root)
|
||||||
CockpitMeshFactory.add_box(
|
CockpitMeshFactory.add_box(
|
||||||
"NeedleBlade",
|
"NeedleBlade",
|
||||||
needle_root,
|
needle_root,
|
||||||
Vector3(0.0, 0.034, 0.0),
|
Vector3(0.0, 0.030, 0.0),
|
||||||
Vector3(0.008, 0.068, 0.006),
|
Vector3(0.006, 0.060, 0.006),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"NeedleTip",
|
||||||
|
needle_root,
|
||||||
|
Vector3(0.0, 0.061, 0.001),
|
||||||
|
Vector3(0.004, 0.024, 0.006),
|
||||||
_materials.needle_paint
|
_materials.needle_paint
|
||||||
)
|
)
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"NeedleCounterweight",
|
||||||
|
needle_root,
|
||||||
|
Vector3(0.0, -0.014, -0.001),
|
||||||
|
Vector3(0.013, 0.030, 0.007),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
CockpitMeshFactory.add_cylinder(
|
CockpitMeshFactory.add_cylinder(
|
||||||
"NeedleHub",
|
"NeedleHub",
|
||||||
needle_root,
|
needle_root,
|
||||||
@@ -251,57 +226,38 @@ func _add_gauge(
|
|||||||
_materials.dark_steel,
|
_materials.dark_steel,
|
||||||
Vector3(90.0, 0.0, 0.0)
|
Vector3(90.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
var label := Label3D.new()
|
|
||||||
label.name = "Caption"
|
|
||||||
label.text = caption
|
|
||||||
label.font_size = 12
|
|
||||||
label.pixel_size = 0.0007
|
|
||||||
label.modulate = Color(0.11, 0.12, 0.10)
|
|
||||||
label.position = Vector3(0.0, -0.044, 0.032)
|
|
||||||
gauge.add_child(label)
|
|
||||||
var glass := MeshInstance3D.new()
|
var glass := MeshInstance3D.new()
|
||||||
glass.name = "GaugeGlass"
|
glass.name = "GaugeGlass"
|
||||||
var glass_mesh := CylinderMesh.new()
|
var glass_mesh := CylinderMesh.new()
|
||||||
glass_mesh.top_radius = 0.081
|
glass_mesh.top_radius = 0.089
|
||||||
glass_mesh.bottom_radius = 0.081
|
glass_mesh.bottom_radius = 0.089
|
||||||
glass_mesh.height = 0.004
|
glass_mesh.height = 0.004
|
||||||
glass_mesh.radial_segments = 40
|
glass_mesh.radial_segments = 40
|
||||||
glass.mesh = glass_mesh
|
glass.mesh = glass_mesh
|
||||||
glass.material_override = _materials.gauge_glass
|
glass.material_override = _materials.gauge_glass
|
||||||
glass.rotation_degrees.x = 90.0
|
glass.rotation_degrees.x = 90.0
|
||||||
glass.position.z = 0.036
|
glass.position.z = 0.050
|
||||||
glass.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
glass.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||||
gauge.add_child(glass)
|
gauge.add_child(glass)
|
||||||
return needle_root
|
return needle_root
|
||||||
|
|
||||||
|
|
||||||
func _add_dial_ticks(gauge: Node3D) -> void:
|
func _add_bezel(
|
||||||
for tick_index in 11:
|
gauge: Node3D,
|
||||||
var ratio := float(tick_index) / 10.0
|
radius: float,
|
||||||
var angle_degrees := lerpf(125.0, -125.0, ratio)
|
height: float,
|
||||||
var angle_radians := deg_to_rad(angle_degrees)
|
material: Material
|
||||||
var direction := Vector2(-sin(angle_radians), cos(angle_radians))
|
) -> void:
|
||||||
var is_major := tick_index % 5 == 0
|
var bezel := MeshInstance3D.new()
|
||||||
var tick_length := 0.015 if is_major else 0.009
|
var bezel_mesh := CylinderMesh.new()
|
||||||
var tick := CockpitMeshFactory.add_box(
|
bezel_mesh.top_radius = radius
|
||||||
"MajorTick" if is_major else "MinorTick",
|
bezel_mesh.bottom_radius = radius
|
||||||
gauge,
|
bezel_mesh.height = height
|
||||||
Vector3(direction.x * 0.064, direction.y * 0.064, 0.029),
|
bezel_mesh.radial_segments = 48
|
||||||
Vector3(0.004 if is_major else 0.0025, tick_length, 0.003),
|
bezel.mesh = bezel_mesh
|
||||||
_materials.dark_steel
|
bezel.material_override = material
|
||||||
)
|
bezel.rotation_degrees.x = 90.0
|
||||||
tick.rotation_degrees.z = angle_degrees
|
gauge.add_child(bezel)
|
||||||
|
|
||||||
|
|
||||||
func _add_scale_label(gauge: Node3D, text: String, position: Vector3) -> void:
|
|
||||||
var label := Label3D.new()
|
|
||||||
label.name = "Scale%s" % text
|
|
||||||
label.text = text
|
|
||||||
label.font_size = 11
|
|
||||||
label.pixel_size = 0.00052
|
|
||||||
label.modulate = Color(0.09, 0.085, 0.065)
|
|
||||||
label.position = position
|
|
||||||
gauge.add_child(label)
|
|
||||||
|
|
||||||
|
|
||||||
func _set_needle_ratio(needle: Node3D, ratio: float) -> void:
|
func _set_needle_ratio(needle: Node3D, ratio: float) -> void:
|
||||||
|
|||||||
@@ -23,18 +23,25 @@ func control_count() -> int:
|
|||||||
return _control_count
|
return _control_count
|
||||||
|
|
||||||
|
|
||||||
func apply_screen_materials(dashboard: Node3D) -> void:
|
func hide_obsolete_elements(cockpit: Node3D) -> void:
|
||||||
for screen_name: String in ["ResourcesScreen", "NavigationScreen", "IntegrityScreen"]:
|
|
||||||
var screen := dashboard.get_node_or_null(screen_name) as MeshInstance3D
|
|
||||||
if screen != null:
|
|
||||||
screen.material_override = _materials.instrument_screen
|
|
||||||
|
|
||||||
|
|
||||||
func hide_placeholder_indicators(cockpit: Node3D) -> void:
|
|
||||||
for indicator_name: String in ["CyanIndicator", "AmberIndicator", "RedIndicator"]:
|
for indicator_name: String in ["CyanIndicator", "AmberIndicator", "RedIndicator"]:
|
||||||
var indicator := cockpit.get_node_or_null(indicator_name) as MeshInstance3D
|
var indicator := cockpit.get_node_or_null(indicator_name) as MeshInstance3D
|
||||||
if indicator != null:
|
if indicator != null:
|
||||||
indicator.visible = false
|
indicator.visible = false
|
||||||
|
for readout_name: String in [
|
||||||
|
"ResourcesFrame",
|
||||||
|
"ResourcesScreen",
|
||||||
|
"ResourcesLabel",
|
||||||
|
"NavigationFrame",
|
||||||
|
"NavigationScreen",
|
||||||
|
"NavigationLabel",
|
||||||
|
"IntegrityFrame",
|
||||||
|
"IntegrityScreen",
|
||||||
|
"IntegrityLabel",
|
||||||
|
]:
|
||||||
|
var readout := cockpit.get_node_or_null("Dashboard/%s" % readout_name) as Node3D
|
||||||
|
if readout != null:
|
||||||
|
readout.visible = false
|
||||||
|
|
||||||
|
|
||||||
func _build_structural_frame() -> void:
|
func _build_structural_frame() -> void:
|
||||||
@@ -60,14 +67,6 @@ func _build_structural_frame() -> void:
|
|||||||
Vector3(0.050, 0.46, 0.055),
|
Vector3(0.050, 0.46, 0.055),
|
||||||
_materials.dark_steel
|
_materials.dark_steel
|
||||||
)
|
)
|
||||||
for x: float in [-0.238, 0.238]:
|
|
||||||
CockpitMeshFactory.add_box(
|
|
||||||
"ScreenSeparator",
|
|
||||||
self,
|
|
||||||
Vector3(x, -0.075, 0.118),
|
|
||||||
Vector3(0.038, 0.16, 0.048),
|
|
||||||
_materials.brass
|
|
||||||
)
|
|
||||||
for screw_position: Vector2 in [
|
for screw_position: Vector2 in [
|
||||||
Vector2(-0.72, -0.18),
|
Vector2(-0.72, -0.18),
|
||||||
Vector2(-0.72, 0.23),
|
Vector2(-0.72, 0.23),
|
||||||
|
|||||||
@@ -10,6 +10,15 @@ const CockpitDashboardDetailing = preload(
|
|||||||
)
|
)
|
||||||
const CockpitMaterialPalette = preload("res://game/presentation/cockpit/cockpit_material_palette.gd")
|
const CockpitMaterialPalette = preload("res://game/presentation/cockpit/cockpit_material_palette.gd")
|
||||||
const CockpitMeshFactory = preload("res://game/presentation/cockpit/cockpit_mesh_factory.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"
|
||||||
|
)
|
||||||
|
|
||||||
var _materials: CockpitMaterialPalette
|
var _materials: CockpitMaterialPalette
|
||||||
var _painted_steel: Material
|
var _painted_steel: Material
|
||||||
@@ -18,8 +27,9 @@ var _brass: Material
|
|||||||
var _copper: Material
|
var _copper: Material
|
||||||
var _analog_gauges: CockpitAnalogGauges
|
var _analog_gauges: CockpitAnalogGauges
|
||||||
var _dashboard_detailing: CockpitDashboardDetailing
|
var _dashboard_detailing: CockpitDashboardDetailing
|
||||||
var _warm_practical: OmniLight3D
|
var _side_equipment: CockpitSideEquipment
|
||||||
var _emergency_light: OmniLight3D
|
var _sonar_console: CockpitSonarConsole
|
||||||
|
var _practical_lighting: CockpitPracticalLighting
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@@ -27,10 +37,9 @@ func _ready() -> void:
|
|||||||
_apply_materials_to_existing_cockpit()
|
_apply_materials_to_existing_cockpit()
|
||||||
_build_pressure_hull_ribs()
|
_build_pressure_hull_ribs()
|
||||||
_build_life_support_pipes()
|
_build_life_support_pipes()
|
||||||
_build_overhead_console()
|
|
||||||
_build_panel_hardware()
|
_build_panel_hardware()
|
||||||
_build_dashboard_instrumentation()
|
_build_dashboard_instrumentation()
|
||||||
_build_practical_lights()
|
_build_equipment_assemblies()
|
||||||
|
|
||||||
|
|
||||||
func present(update: DiveUpdate, telemetry: VehicleTelemetry) -> void:
|
func present(update: DiveUpdate, telemetry: VehicleTelemetry) -> void:
|
||||||
@@ -50,10 +59,7 @@ func present(update: DiveUpdate, telemetry: VehicleTelemetry) -> void:
|
|||||||
snapshot.alerts.maximum
|
snapshot.alerts.maximum
|
||||||
)
|
)
|
||||||
var alert_level: DiveAlertLevel.Value = snapshot.alerts.maximum
|
var alert_level: DiveAlertLevel.Value = snapshot.alerts.maximum
|
||||||
_emergency_light.visible = alert_level >= DiveAlertLevel.Value.CRITICAL
|
_practical_lighting.present(alert_level)
|
||||||
_warm_practical.light_energy = (
|
|
||||||
0.28 if alert_level >= DiveAlertLevel.Value.CRITICAL else 0.72
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func pressure_hull_rib_count() -> int:
|
func pressure_hull_rib_count() -> int:
|
||||||
@@ -74,7 +80,10 @@ func textures_are_loaded() -> bool:
|
|||||||
return (
|
return (
|
||||||
_materials != null
|
_materials != null
|
||||||
and _materials.overhead_steel.albedo_texture != null
|
and _materials.overhead_steel.albedo_texture != null
|
||||||
and _materials.gauge_face.albedo_texture != null
|
and _materials.oxygen_gauge_face.albedo_texture != null
|
||||||
|
and _materials.battery_gauge_face.albedo_texture != null
|
||||||
|
and _materials.hull_gauge_face.albedo_texture != null
|
||||||
|
and _materials.depth_gauge_face.albedo_texture != null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -82,6 +91,24 @@ func dashboard_control_count() -> int:
|
|||||||
return 0 if _dashboard_detailing == null else _dashboard_detailing.control_count()
|
return 0 if _dashboard_detailing == null else _dashboard_detailing.control_count()
|
||||||
|
|
||||||
|
|
||||||
|
func side_equipment_count() -> int:
|
||||||
|
if _side_equipment == null:
|
||||||
|
return 0
|
||||||
|
return _side_equipment.tank_count() + _side_equipment.junction_box_count()
|
||||||
|
|
||||||
|
|
||||||
|
func side_control_count() -> int:
|
||||||
|
return 0 if _side_equipment == null else _side_equipment.control_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 gauges_are_dashboard_mounted() -> bool:
|
func gauges_are_dashboard_mounted() -> bool:
|
||||||
return _analog_gauges != null and _analog_gauges.get_parent() == get_parent().get_node("Dashboard")
|
return _analog_gauges != null and _analog_gauges.get_parent() == get_parent().get_node("Dashboard")
|
||||||
|
|
||||||
@@ -219,58 +246,13 @@ func _build_life_support_pipes() -> void:
|
|||||||
0.052,
|
0.052,
|
||||||
_copper
|
_copper
|
||||||
)
|
)
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
CockpitMeshFactory.add_cylinder(
|
"UpperBranchPipe",
|
||||||
"OverheadCrossPipe",
|
|
||||||
root,
|
root,
|
||||||
Vector3(0.0, 0.93, -1.12),
|
Vector3(x, 0.84, -1.12),
|
||||||
0.031,
|
Vector3(side * 0.72, 0.77, -1.10),
|
||||||
1.82,
|
0.019,
|
||||||
_copper,
|
_brass
|
||||||
Vector3(0.0, 0.0, 90.0)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func _build_overhead_console() -> void:
|
|
||||||
var root := Node3D.new()
|
|
||||||
root.name = "OverheadConsole"
|
|
||||||
add_child(root)
|
|
||||||
CockpitMeshFactory.add_box(
|
|
||||||
"BreakerHousing",
|
|
||||||
root,
|
|
||||||
Vector3(0.0, 0.94, -1.06),
|
|
||||||
Vector3(0.30, 0.09, 0.12),
|
|
||||||
_materials.overhead_steel,
|
|
||||||
Vector3(-8.0, 0.0, 0.0)
|
|
||||||
)
|
|
||||||
CockpitMeshFactory.add_box(
|
|
||||||
"WarmLightDiffuser",
|
|
||||||
root,
|
|
||||||
Vector3(0.0, 0.895, -0.985),
|
|
||||||
Vector3(0.13, 0.010, 0.026),
|
|
||||||
_materials.warm_emission,
|
|
||||||
Vector3(-8.0, 0.0, 0.0)
|
|
||||||
)
|
|
||||||
for guard_index in 5:
|
|
||||||
var guard_x := -0.05 + guard_index * 0.025
|
|
||||||
CockpitMeshFactory.add_box(
|
|
||||||
"LampGuard",
|
|
||||||
root,
|
|
||||||
Vector3(guard_x, 0.896, -0.968),
|
|
||||||
Vector3(0.005, 0.045, 0.008),
|
|
||||||
_dark_steel,
|
|
||||||
Vector3(-8.0, 0.0, 0.0)
|
|
||||||
)
|
|
||||||
for switch_index in 5:
|
|
||||||
var x := -0.10 + switch_index * 0.05
|
|
||||||
CockpitMeshFactory.add_cylinder(
|
|
||||||
"BreakerSwitch",
|
|
||||||
root,
|
|
||||||
Vector3(x, 0.965, -0.985),
|
|
||||||
0.004,
|
|
||||||
0.018,
|
|
||||||
_materials.rubber,
|
|
||||||
Vector3(90.0, 0.0, 0.0)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -290,23 +272,6 @@ func _build_panel_hardware() -> void:
|
|||||||
Vector3(90.0, 0.0, 0.0)
|
Vector3(90.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
for side: float in [-1.0, 1.0]:
|
|
||||||
CockpitMeshFactory.add_box(
|
|
||||||
"ServiceBox",
|
|
||||||
root,
|
|
||||||
Vector3(side * 0.89, 0.08, -1.10),
|
|
||||||
Vector3(0.18, 0.34, 0.19),
|
|
||||||
_painted_steel,
|
|
||||||
Vector3(0.0, side * -12.0, 0.0)
|
|
||||||
)
|
|
||||||
var label := Label3D.new()
|
|
||||||
label.name = "ServicePlate"
|
|
||||||
label.text = "O₂" if side < 0.0 else "ELEC"
|
|
||||||
label.font_size = 16
|
|
||||||
label.pixel_size = 0.0009
|
|
||||||
label.modulate = Color(0.72, 0.69, 0.56)
|
|
||||||
label.position = Vector3(side * 0.89, 0.10, -0.99)
|
|
||||||
root.add_child(label)
|
|
||||||
|
|
||||||
|
|
||||||
func _build_dashboard_instrumentation() -> void:
|
func _build_dashboard_instrumentation() -> void:
|
||||||
@@ -315,36 +280,17 @@ func _build_dashboard_instrumentation() -> void:
|
|||||||
return
|
return
|
||||||
_dashboard_detailing = CockpitDashboardDetailing.new(_materials)
|
_dashboard_detailing = CockpitDashboardDetailing.new(_materials)
|
||||||
dashboard.add_child(_dashboard_detailing)
|
dashboard.add_child(_dashboard_detailing)
|
||||||
_dashboard_detailing.apply_screen_materials(dashboard)
|
_dashboard_detailing.hide_obsolete_elements(get_parent())
|
||||||
_dashboard_detailing.hide_placeholder_indicators(get_parent())
|
|
||||||
_analog_gauges = CockpitAnalogGauges.new(_materials)
|
_analog_gauges = CockpitAnalogGauges.new(_materials)
|
||||||
dashboard.add_child(_analog_gauges)
|
dashboard.add_child(_analog_gauges)
|
||||||
|
|
||||||
|
|
||||||
func _build_practical_lights() -> void:
|
func _build_equipment_assemblies() -> void:
|
||||||
_warm_practical = OmniLight3D.new()
|
_side_equipment = CockpitSideEquipment.new(_materials)
|
||||||
_warm_practical.name = "WarmPracticalLight"
|
add_child(_side_equipment)
|
||||||
_warm_practical.position = Vector3(0.0, 0.82, -0.50)
|
var sonar_monitor := get_parent().get_node_or_null("SonarMonitor") as Node3D
|
||||||
_warm_practical.light_color = Color(1.0, 0.52, 0.22)
|
if sonar_monitor != null:
|
||||||
_warm_practical.light_energy = 0.58
|
_sonar_console = CockpitSonarConsole.new(_materials)
|
||||||
_warm_practical.omni_range = 2.2
|
sonar_monitor.add_child(_sonar_console)
|
||||||
_warm_practical.shadow_enabled = false
|
_practical_lighting = CockpitPracticalLighting.new(_materials)
|
||||||
add_child(_warm_practical)
|
add_child(_practical_lighting)
|
||||||
|
|
||||||
var cool_fill := OmniLight3D.new()
|
|
||||||
cool_fill.name = "CoolRearFill"
|
|
||||||
cool_fill.position = Vector3(0.0, 0.42, 0.35)
|
|
||||||
cool_fill.light_color = Color(0.24, 0.46, 0.47)
|
|
||||||
cool_fill.light_energy = 0.48
|
|
||||||
cool_fill.omni_range = 3.0
|
|
||||||
cool_fill.shadow_enabled = false
|
|
||||||
add_child(cool_fill)
|
|
||||||
|
|
||||||
_emergency_light = OmniLight3D.new()
|
|
||||||
_emergency_light.name = "EmergencyLight"
|
|
||||||
_emergency_light.position = Vector3(-0.58, 0.44, -0.83)
|
|
||||||
_emergency_light.light_color = Color(1.0, 0.04, 0.015)
|
|
||||||
_emergency_light.light_energy = 1.4
|
|
||||||
_emergency_light.omni_range = 2.4
|
|
||||||
_emergency_light.visible = false
|
|
||||||
add_child(_emergency_light)
|
|
||||||
@@ -267,67 +267,67 @@ mesh = SubResource("UnitBoxMesh")
|
|||||||
material_override = SubResource("PaintedMaterial")
|
material_override = SubResource("PaintedMaterial")
|
||||||
|
|
||||||
[node name="ResourcesFrame" type="MeshInstance3D" parent="Dashboard"]
|
[node name="ResourcesFrame" type="MeshInstance3D" parent="Dashboard"]
|
||||||
position = Vector3(-0.5, -0.075, 0.088)
|
position = Vector3(-0.49, -0.115, 0.14)
|
||||||
scale = Vector3(0.45, 0.115, 0.025)
|
scale = Vector3(0.34, 0.065, 0.025)
|
||||||
mesh = SubResource("UnitBoxMesh")
|
mesh = SubResource("UnitBoxMesh")
|
||||||
material_override = SubResource("BrassMaterial")
|
material_override = SubResource("BrassMaterial")
|
||||||
|
|
||||||
[node name="ResourcesScreen" type="MeshInstance3D" parent="Dashboard"]
|
[node name="ResourcesScreen" type="MeshInstance3D" parent="Dashboard"]
|
||||||
position = Vector3(-0.5, -0.075, 0.102)
|
position = Vector3(-0.49, -0.115, 0.155)
|
||||||
scale = Vector3(0.41, 0.085, 1)
|
scale = Vector3(0.305, 0.044, 1)
|
||||||
mesh = SubResource("ScreenQuadMesh")
|
mesh = SubResource("ScreenQuadMesh")
|
||||||
material_override = SubResource("ScreenMaterial")
|
material_override = SubResource("ScreenMaterial")
|
||||||
|
|
||||||
[node name="ResourcesLabel" type="Label3D" parent="Dashboard"]
|
[node name="ResourcesLabel" type="Label3D" parent="Dashboard"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(-0.5, -0.075, 0.108)
|
position = Vector3(-0.49, -0.115, 0.163)
|
||||||
pixel_size = 0.00082
|
pixel_size = 0.00055
|
||||||
font_size = 14
|
font_size = 10
|
||||||
outline_size = 2
|
outline_size = 2
|
||||||
text = "BAT — kWh · O₂ — %\n— kW · — L/min"
|
text = "BAT — kWh · O₂ — %\n— kW · — L/min"
|
||||||
modulate = Color(0.45, 0.89, 0.9, 1)
|
modulate = Color(0.76, 0.74, 0.6, 1)
|
||||||
|
|
||||||
[node name="NavigationFrame" type="MeshInstance3D" parent="Dashboard"]
|
[node name="NavigationFrame" type="MeshInstance3D" parent="Dashboard"]
|
||||||
position = Vector3(0, -0.075, 0.088)
|
position = Vector3(0, -0.115, 0.14)
|
||||||
scale = Vector3(0.4, 0.115, 0.025)
|
scale = Vector3(0.30, 0.065, 0.025)
|
||||||
mesh = SubResource("UnitBoxMesh")
|
mesh = SubResource("UnitBoxMesh")
|
||||||
material_override = SubResource("BrassMaterial")
|
material_override = SubResource("BrassMaterial")
|
||||||
|
|
||||||
[node name="NavigationScreen" type="MeshInstance3D" parent="Dashboard"]
|
[node name="NavigationScreen" type="MeshInstance3D" parent="Dashboard"]
|
||||||
position = Vector3(0, -0.075, 0.102)
|
position = Vector3(0, -0.115, 0.155)
|
||||||
scale = Vector3(0.36, 0.085, 1)
|
scale = Vector3(0.265, 0.044, 1)
|
||||||
mesh = SubResource("ScreenQuadMesh")
|
mesh = SubResource("ScreenQuadMesh")
|
||||||
material_override = SubResource("ScreenMaterial")
|
material_override = SubResource("ScreenMaterial")
|
||||||
|
|
||||||
[node name="NavigationLabel" type="Label3D" parent="Dashboard"]
|
[node name="NavigationLabel" type="Label3D" parent="Dashboard"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(0, -0.075, 0.108)
|
position = Vector3(0, -0.115, 0.163)
|
||||||
pixel_size = 0.00082
|
pixel_size = 0.00055
|
||||||
font_size = 14
|
font_size = 10
|
||||||
outline_size = 2
|
outline_size = 2
|
||||||
text = "PROF. — / — m\nVIT. — m/s"
|
text = "PROF. — / — m\nVIT. — m/s"
|
||||||
modulate = Color(0.45, 0.89, 0.9, 1)
|
modulate = Color(0.76, 0.74, 0.6, 1)
|
||||||
|
|
||||||
[node name="IntegrityFrame" type="MeshInstance3D" parent="Dashboard"]
|
[node name="IntegrityFrame" type="MeshInstance3D" parent="Dashboard"]
|
||||||
position = Vector3(0.5, -0.075, 0.088)
|
position = Vector3(0.49, -0.115, 0.14)
|
||||||
scale = Vector3(0.45, 0.115, 0.025)
|
scale = Vector3(0.34, 0.065, 0.025)
|
||||||
mesh = SubResource("UnitBoxMesh")
|
mesh = SubResource("UnitBoxMesh")
|
||||||
material_override = SubResource("BrassMaterial")
|
material_override = SubResource("BrassMaterial")
|
||||||
|
|
||||||
[node name="IntegrityScreen" type="MeshInstance3D" parent="Dashboard"]
|
[node name="IntegrityScreen" type="MeshInstance3D" parent="Dashboard"]
|
||||||
position = Vector3(0.5, -0.075, 0.102)
|
position = Vector3(0.49, -0.115, 0.155)
|
||||||
scale = Vector3(0.41, 0.085, 1)
|
scale = Vector3(0.305, 0.044, 1)
|
||||||
mesh = SubResource("ScreenQuadMesh")
|
mesh = SubResource("ScreenQuadMesh")
|
||||||
material_override = SubResource("ScreenMaterial")
|
material_override = SubResource("ScreenMaterial")
|
||||||
|
|
||||||
[node name="IntegrityLabel" type="Label3D" parent="Dashboard"]
|
[node name="IntegrityLabel" type="Label3D" parent="Dashboard"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(0.5, -0.075, 0.108)
|
position = Vector3(0.49, -0.115, 0.163)
|
||||||
pixel_size = 0.00082
|
pixel_size = 0.00055
|
||||||
font_size = 14
|
font_size = 10
|
||||||
outline_size = 2
|
outline_size = 2
|
||||||
text = "COQUE — % · — bar\nLIMITE — bar"
|
text = "COQUE — % · — bar\nLIMITE — bar"
|
||||||
modulate = Color(0.45, 0.89, 0.9, 1)
|
modulate = Color(0.76, 0.74, 0.6, 1)
|
||||||
|
|
||||||
[node name="ReturnLabel" type="Label3D" parent="Dashboard"]
|
[node name="ReturnLabel" type="Label3D" parent="Dashboard"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
@@ -388,24 +388,24 @@ mesh = SubResource("KnobMesh")
|
|||||||
material_override = SubResource("BrassMaterial")
|
material_override = SubResource("BrassMaterial")
|
||||||
|
|
||||||
[node name="SonarMonitor" type="Node3D" parent="."]
|
[node name="SonarMonitor" type="Node3D" parent="."]
|
||||||
position = Vector3(0.72, 0.4, -1.24)
|
position = Vector3(0.74, 0.42, -1.18)
|
||||||
rotation_degrees = Vector3(-5, -22, -2)
|
rotation_degrees = Vector3(-5, -22, -2)
|
||||||
|
|
||||||
[node name="Housing" type="MeshInstance3D" parent="SonarMonitor"]
|
[node name="Housing" type="MeshInstance3D" parent="SonarMonitor"]
|
||||||
scale = Vector3(0.32, 0.24, 0.08)
|
scale = Vector3(0.5, 0.34, 0.1)
|
||||||
mesh = SubResource("UnitBoxMesh")
|
mesh = SubResource("UnitBoxMesh")
|
||||||
material_override = SubResource("RubberMaterial")
|
material_override = SubResource("PaintedMaterial")
|
||||||
|
|
||||||
[node name="SonarScreen" type="MeshInstance3D" parent="SonarMonitor"]
|
[node name="SonarScreen" type="MeshInstance3D" parent="SonarMonitor"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(0, 0.025, 0.06)
|
position = Vector3(-0.025, 0.012, 0.06)
|
||||||
scale = Vector3(0.265, 0.165, 1)
|
scale = Vector3(0.36, 0.235, 1)
|
||||||
mesh = SubResource("ScreenQuadMesh")
|
mesh = SubResource("ScreenQuadMesh")
|
||||||
material_override = SubResource("ScreenMaterial")
|
material_override = SubResource("ScreenMaterial")
|
||||||
|
|
||||||
[node name="SonarPlaceholder" type="Label3D" parent="SonarMonitor"]
|
[node name="SonarPlaceholder" type="Label3D" parent="SonarMonitor"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(0, 0.025, 0.068)
|
position = Vector3(-0.025, 0.012, 0.068)
|
||||||
pixel_size = 0.0009
|
pixel_size = 0.0009
|
||||||
font_size = 16
|
font_size = 16
|
||||||
outline_size = 2
|
outline_size = 2
|
||||||
|
|||||||
@@ -4,8 +4,17 @@ extends RefCounted
|
|||||||
const NAVAL_STEEL_TEXTURE_PATH := (
|
const NAVAL_STEEL_TEXTURE_PATH := (
|
||||||
"res://game/presentation/cockpit/assets/naval_painted_steel_albedo.png"
|
"res://game/presentation/cockpit/assets/naval_painted_steel_albedo.png"
|
||||||
)
|
)
|
||||||
const GAUGE_FACE_TEXTURE_PATH := (
|
const OXYGEN_GAUGE_FACE_TEXTURE_PATH := (
|
||||||
"res://game/presentation/cockpit/assets/analog_gauge_face_albedo.png"
|
"res://game/presentation/cockpit/assets/oxygen_gauge_face_albedo_v2.png"
|
||||||
|
)
|
||||||
|
const BATTERY_GAUGE_FACE_TEXTURE_PATH := (
|
||||||
|
"res://game/presentation/cockpit/assets/battery_gauge_face_albedo_v2.png"
|
||||||
|
)
|
||||||
|
const HULL_GAUGE_FACE_TEXTURE_PATH := (
|
||||||
|
"res://game/presentation/cockpit/assets/hull_gauge_face_albedo_v2.png"
|
||||||
|
)
|
||||||
|
const DEPTH_GAUGE_FACE_TEXTURE_PATH := (
|
||||||
|
"res://game/presentation/cockpit/assets/depth_gauge_face_albedo_v2.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
var painted_steel: Material
|
var painted_steel: Material
|
||||||
@@ -15,11 +24,22 @@ var brushed_steel: StandardMaterial3D
|
|||||||
var brass: Material
|
var brass: Material
|
||||||
var copper: Material
|
var copper: Material
|
||||||
var rubber: Material
|
var rubber: Material
|
||||||
var gauge_face: StandardMaterial3D
|
var oxygen_gauge_face: StandardMaterial3D
|
||||||
|
var battery_gauge_face: StandardMaterial3D
|
||||||
|
var hull_gauge_face: StandardMaterial3D
|
||||||
|
var depth_gauge_face: StandardMaterial3D
|
||||||
var gauge_glass: StandardMaterial3D
|
var gauge_glass: StandardMaterial3D
|
||||||
var needle_paint: StandardMaterial3D
|
var needle_paint: StandardMaterial3D
|
||||||
var instrument_screen: ShaderMaterial
|
var instrument_screen: ShaderMaterial
|
||||||
var warm_emission: StandardMaterial3D
|
var warm_emission: StandardMaterial3D
|
||||||
|
var safety_yellow: StandardMaterial3D
|
||||||
|
var signal_red: StandardMaterial3D
|
||||||
|
var indicator_green: StandardMaterial3D
|
||||||
|
var indicator_amber: StandardMaterial3D
|
||||||
|
var indicator_red: StandardMaterial3D
|
||||||
|
var sonar_grid: StandardMaterial3D
|
||||||
|
var sonar_trace: StandardMaterial3D
|
||||||
|
var warm_glass: StandardMaterial3D
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
@@ -39,12 +59,12 @@ func _init() -> void:
|
|||||||
)
|
)
|
||||||
overhead_steel = _textured_material(
|
overhead_steel = _textured_material(
|
||||||
_load_texture(NAVAL_STEEL_TEXTURE_PATH),
|
_load_texture(NAVAL_STEEL_TEXTURE_PATH),
|
||||||
Color(0.64, 0.69, 0.68),
|
Color(0.88, 0.91, 0.88),
|
||||||
0.64,
|
0.64,
|
||||||
0.56,
|
0.56,
|
||||||
Vector3(1.6, 1.6, 1.6)
|
Vector3(1.6, 1.6, 1.6)
|
||||||
)
|
)
|
||||||
brushed_steel = _plain_material(Color(0.27, 0.30, 0.29), 0.82, 0.30)
|
brushed_steel = _plain_material(Color(0.42, 0.44, 0.42), 0.82, 0.30)
|
||||||
brass = _weathered_material(
|
brass = _weathered_material(
|
||||||
Color(0.28, 0.18, 0.045),
|
Color(0.28, 0.18, 0.045),
|
||||||
Color(0.08, 0.16, 0.13),
|
Color(0.08, 0.16, 0.13),
|
||||||
@@ -60,16 +80,22 @@ func _init() -> void:
|
|||||||
0.12
|
0.12
|
||||||
)
|
)
|
||||||
rubber = _plain_material(Color(0.012, 0.018, 0.018), 0.0, 0.92)
|
rubber = _plain_material(Color(0.012, 0.018, 0.018), 0.0, 0.92)
|
||||||
gauge_face = _textured_material(
|
oxygen_gauge_face = _dedicated_gauge_material(OXYGEN_GAUGE_FACE_TEXTURE_PATH)
|
||||||
_load_texture(GAUGE_FACE_TEXTURE_PATH),
|
battery_gauge_face = _dedicated_gauge_material(BATTERY_GAUGE_FACE_TEXTURE_PATH)
|
||||||
Color(0.78, 0.74, 0.64),
|
hull_gauge_face = _dedicated_gauge_material(HULL_GAUGE_FACE_TEXTURE_PATH)
|
||||||
0.02,
|
depth_gauge_face = _dedicated_gauge_material(DEPTH_GAUGE_FACE_TEXTURE_PATH)
|
||||||
0.72
|
|
||||||
)
|
|
||||||
gauge_glass = _glass_material()
|
gauge_glass = _glass_material()
|
||||||
needle_paint = _plain_material(Color(0.68, 0.055, 0.022), 0.12, 0.44)
|
needle_paint = _plain_material(Color(0.68, 0.055, 0.022), 0.12, 0.44)
|
||||||
instrument_screen = _screen_material()
|
instrument_screen = _screen_material()
|
||||||
warm_emission = _emissive_material(Color(0.96, 0.52, 0.18), 2.4)
|
warm_emission = _emissive_material(Color(0.96, 0.52, 0.18), 2.4)
|
||||||
|
safety_yellow = _plain_material(Color(0.48, 0.31, 0.035), 0.48, 0.53)
|
||||||
|
signal_red = _plain_material(Color(0.42, 0.025, 0.015), 0.38, 0.48)
|
||||||
|
indicator_green = _emissive_material(Color(0.05, 0.82, 0.24), 2.8)
|
||||||
|
indicator_amber = _emissive_material(Color(1.0, 0.43, 0.035), 2.8)
|
||||||
|
indicator_red = _emissive_material(Color(0.9, 0.045, 0.018), 2.8)
|
||||||
|
sonar_grid = _emissive_material(Color(0.02, 0.20, 0.22), 0.55)
|
||||||
|
sonar_trace = _emissive_material(Color(0.12, 0.78, 0.78), 1.8)
|
||||||
|
warm_glass = _warm_glass_material()
|
||||||
|
|
||||||
|
|
||||||
func _weathered_material(
|
func _weathered_material(
|
||||||
@@ -161,6 +187,17 @@ func _load_texture(resource_path: String) -> ImageTexture:
|
|||||||
return ImageTexture.create_from_image(image)
|
return ImageTexture.create_from_image(image)
|
||||||
|
|
||||||
|
|
||||||
|
func _dedicated_gauge_material(resource_path: String) -> StandardMaterial3D:
|
||||||
|
var material := _textured_material(
|
||||||
|
_load_texture(resource_path),
|
||||||
|
Color(0.94, 0.92, 0.86),
|
||||||
|
0.02,
|
||||||
|
0.66
|
||||||
|
)
|
||||||
|
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
return material
|
||||||
|
|
||||||
|
|
||||||
func _glass_material() -> StandardMaterial3D:
|
func _glass_material() -> StandardMaterial3D:
|
||||||
var material := StandardMaterial3D.new()
|
var material := StandardMaterial3D.new()
|
||||||
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
@@ -203,3 +240,14 @@ func _emissive_material(color: Color, energy: float) -> StandardMaterial3D:
|
|||||||
material.emission = color
|
material.emission = color
|
||||||
material.emission_energy_multiplier = energy
|
material.emission_energy_multiplier = energy
|
||||||
return material
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
func _warm_glass_material() -> StandardMaterial3D:
|
||||||
|
var material := StandardMaterial3D.new()
|
||||||
|
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||||
|
material.albedo_color = Color(1.0, 0.48, 0.12, 0.72)
|
||||||
|
material.roughness = 0.18
|
||||||
|
material.emission_enabled = true
|
||||||
|
material.emission = Color(1.0, 0.28, 0.045)
|
||||||
|
material.emission_energy_multiplier = 2.6
|
||||||
|
return material
|
||||||
@@ -46,6 +46,28 @@ static func add_cylinder(
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
static func add_cylinder_between(
|
||||||
|
node_name: String,
|
||||||
|
parent: Node3D,
|
||||||
|
start: Vector3,
|
||||||
|
end: Vector3,
|
||||||
|
radius: float,
|
||||||
|
material: Material
|
||||||
|
) -> MeshInstance3D:
|
||||||
|
var direction := end - start
|
||||||
|
var instance := add_cylinder(
|
||||||
|
node_name,
|
||||||
|
parent,
|
||||||
|
(start + end) * 0.5,
|
||||||
|
radius,
|
||||||
|
direction.length(),
|
||||||
|
material
|
||||||
|
)
|
||||||
|
if not direction.is_zero_approx():
|
||||||
|
instance.quaternion = Quaternion(Vector3.UP, direction.normalized())
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
static func add_sphere(
|
static func add_sphere(
|
||||||
node_name: String,
|
node_name: String,
|
||||||
parent: Node3D,
|
parent: Node3D,
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
class_name CockpitPracticalLighting
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd")
|
||||||
|
const CockpitMaterialPalette = preload(
|
||||||
|
"res://game/presentation/cockpit/cockpit_material_palette.gd"
|
||||||
|
)
|
||||||
|
const CockpitMeshFactory = preload(
|
||||||
|
"res://game/presentation/cockpit/cockpit_mesh_factory.gd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _materials: CockpitMaterialPalette
|
||||||
|
var _warm_lights: Array[OmniLight3D] = []
|
||||||
|
var _emergency_light: OmniLight3D
|
||||||
|
|
||||||
|
|
||||||
|
func _init(materials: CockpitMaterialPalette) -> void:
|
||||||
|
_materials = materials
|
||||||
|
name = "PracticalLighting"
|
||||||
|
_add_caged_lamp(-0.76, -8.0)
|
||||||
|
_add_caged_lamp(0.76, 8.0)
|
||||||
|
_add_fill_light()
|
||||||
|
_add_emergency_light()
|
||||||
|
|
||||||
|
|
||||||
|
func present(alert_level: DiveAlertLevel.Value) -> void:
|
||||||
|
_emergency_light.visible = alert_level >= DiveAlertLevel.Value.CRITICAL
|
||||||
|
for light: OmniLight3D in _warm_lights:
|
||||||
|
light.light_energy = 0.22 if alert_level >= DiveAlertLevel.Value.CRITICAL else 0.48
|
||||||
|
|
||||||
|
|
||||||
|
func practical_light_count() -> int:
|
||||||
|
return _warm_lights.size()
|
||||||
|
|
||||||
|
|
||||||
|
func _add_caged_lamp(x: float, roll_degrees: float) -> void:
|
||||||
|
var root := Node3D.new()
|
||||||
|
root.name = "CagedLamp"
|
||||||
|
root.position = Vector3(x, 0.88, -1.16)
|
||||||
|
root.rotation_degrees.z = roll_degrees
|
||||||
|
add_child(root)
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"LampHousing",
|
||||||
|
root,
|
||||||
|
Vector3(0.0, 0.0, -0.025),
|
||||||
|
Vector3(0.14, 0.11, 0.055),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_sphere(
|
||||||
|
"WarmBulb",
|
||||||
|
root,
|
||||||
|
Vector3(0.0, 0.0, 0.04),
|
||||||
|
0.038,
|
||||||
|
_materials.warm_glass
|
||||||
|
)
|
||||||
|
for x_offset: float in [-0.048, -0.024, 0.0, 0.024, 0.048]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"LampGuard",
|
||||||
|
root,
|
||||||
|
Vector3(x_offset, 0.0, 0.095),
|
||||||
|
Vector3(0.007, 0.10, 0.007),
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
for y_offset: float in [-0.04, 0.04]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"LampGuardRail",
|
||||||
|
root,
|
||||||
|
Vector3(0.0, y_offset, 0.095),
|
||||||
|
Vector3(0.115, 0.007, 0.007),
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
var light := OmniLight3D.new()
|
||||||
|
light.name = "WarmPracticalLight"
|
||||||
|
light.position = Vector3(0.0, -0.03, 0.16)
|
||||||
|
light.light_color = Color(1.0, 0.55, 0.26)
|
||||||
|
light.light_energy = 0.48
|
||||||
|
light.omni_range = 1.75
|
||||||
|
light.shadow_enabled = false
|
||||||
|
root.add_child(light)
|
||||||
|
_warm_lights.append(light)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_fill_light() -> void:
|
||||||
|
var cool_fill := OmniLight3D.new()
|
||||||
|
cool_fill.name = "CoolRearFill"
|
||||||
|
cool_fill.position = Vector3(0.0, 0.42, 0.35)
|
||||||
|
cool_fill.light_color = Color(0.24, 0.46, 0.47)
|
||||||
|
cool_fill.light_energy = 0.36
|
||||||
|
cool_fill.omni_range = 3.0
|
||||||
|
cool_fill.shadow_enabled = false
|
||||||
|
add_child(cool_fill)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_emergency_light() -> void:
|
||||||
|
_emergency_light = OmniLight3D.new()
|
||||||
|
_emergency_light.name = "EmergencyLight"
|
||||||
|
_emergency_light.position = Vector3(-0.58, 0.44, -0.83)
|
||||||
|
_emergency_light.light_color = Color(1.0, 0.04, 0.015)
|
||||||
|
_emergency_light.light_energy = 1.4
|
||||||
|
_emergency_light.omni_range = 2.4
|
||||||
|
_emergency_light.visible = false
|
||||||
|
add_child(_emergency_light)
|
||||||
@@ -26,6 +26,7 @@ func _ready() -> void:
|
|||||||
_build_curved_panels()
|
_build_curved_panels()
|
||||||
_build_outer_rib()
|
_build_outer_rib()
|
||||||
_build_fasteners()
|
_build_fasteners()
|
||||||
|
_build_clamp_plates()
|
||||||
|
|
||||||
|
|
||||||
func panel_count() -> int:
|
func panel_count() -> int:
|
||||||
@@ -53,7 +54,7 @@ func _style_existing_frame() -> void:
|
|||||||
frame.material_override = _materials.brushed_steel
|
frame.material_override = _materials.brushed_steel
|
||||||
for child: Node in get_parent().get_children():
|
for child: Node in get_parent().get_children():
|
||||||
if child is MeshInstance3D and child.name.begins_with("Bolt"):
|
if child is MeshInstance3D and child.name.begins_with("Bolt"):
|
||||||
(child as MeshInstance3D).material_override = _materials.dark_steel
|
(child as MeshInstance3D).material_override = _materials.brass
|
||||||
|
|
||||||
|
|
||||||
func _build_curved_panels() -> void:
|
func _build_curved_panels() -> void:
|
||||||
@@ -155,12 +156,37 @@ func _build_outer_rib() -> void:
|
|||||||
func _build_fasteners() -> void:
|
func _build_fasteners() -> void:
|
||||||
for fastener_index in FASTENER_COUNT:
|
for fastener_index in FASTENER_COUNT:
|
||||||
var angle := (fastener_index + 0.5) * TAU / float(FASTENER_COUNT)
|
var angle := (fastener_index + 0.5) * TAU / float(FASTENER_COUNT)
|
||||||
|
var fastener_position := Vector3(
|
||||||
|
cos(angle) * 0.96,
|
||||||
|
sin(angle) * 0.96,
|
||||||
|
0.12
|
||||||
|
)
|
||||||
CockpitMeshFactory.add_cylinder(
|
CockpitMeshFactory.add_cylinder(
|
||||||
"OuterFastener",
|
"OuterFastener",
|
||||||
self,
|
self,
|
||||||
Vector3(cos(angle) * 0.96, sin(angle) * 0.96, 0.105),
|
fastener_position,
|
||||||
0.014,
|
0.020,
|
||||||
0.018,
|
0.018,
|
||||||
_materials.brass,
|
_materials.brass,
|
||||||
Vector3(90.0, 0.0, 0.0)
|
Vector3(90.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
|
CockpitMeshFactory.add_sphere(
|
||||||
|
"FastenerCap",
|
||||||
|
self,
|
||||||
|
fastener_position + Vector3(0.0, 0.0, 0.012),
|
||||||
|
0.017,
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_clamp_plates() -> void:
|
||||||
|
for plate_index in 12:
|
||||||
|
var angle := (plate_index + 0.5) * TAU / 12.0
|
||||||
|
var plate := CockpitMeshFactory.add_box(
|
||||||
|
"PressureClamp",
|
||||||
|
self,
|
||||||
|
Vector3(cos(angle) * 0.88, sin(angle) * 0.88, 0.105),
|
||||||
|
Vector3(0.11, 0.055, 0.028),
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
plate.rotation_degrees.z = rad_to_deg(angle) + 90.0
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
class_name CockpitSideEquipment
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
const CockpitMaterialPalette = preload(
|
||||||
|
"res://game/presentation/cockpit/cockpit_material_palette.gd"
|
||||||
|
)
|
||||||
|
const CockpitMeshFactory = preload(
|
||||||
|
"res://game/presentation/cockpit/cockpit_mesh_factory.gd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _materials: CockpitMaterialPalette
|
||||||
|
var _tank_count := 0
|
||||||
|
var _junction_box_count := 0
|
||||||
|
var _control_count := 0
|
||||||
|
|
||||||
|
|
||||||
|
func _init(materials: CockpitMaterialPalette) -> void:
|
||||||
|
_materials = materials
|
||||||
|
name = "SideEquipment"
|
||||||
|
_build_left_life_support_bank()
|
||||||
|
_build_right_electrical_bank()
|
||||||
|
_build_cable_looms()
|
||||||
|
_build_control_rails()
|
||||||
|
|
||||||
|
|
||||||
|
func tank_count() -> int:
|
||||||
|
return _tank_count
|
||||||
|
|
||||||
|
|
||||||
|
func junction_box_count() -> int:
|
||||||
|
return _junction_box_count
|
||||||
|
|
||||||
|
|
||||||
|
func control_count() -> int:
|
||||||
|
return _control_count
|
||||||
|
|
||||||
|
|
||||||
|
func _build_left_life_support_bank() -> void:
|
||||||
|
var root := Node3D.new()
|
||||||
|
root.name = "LifeSupportBank"
|
||||||
|
add_child(root)
|
||||||
|
for tank_data: Vector3 in [
|
||||||
|
Vector3(-0.78, 0.55, -1.17),
|
||||||
|
Vector3(-0.68, 0.47, -1.19),
|
||||||
|
]:
|
||||||
|
_add_tank(root, tank_data)
|
||||||
|
|
||||||
|
_add_service_box(root, Vector3(-0.61, 0.42, -1.14), "O₂", -10.0)
|
||||||
|
_add_indicator_column(root, Vector3(-0.55, 0.39, -1.055))
|
||||||
|
_add_valve_wheel(root, Vector3(-0.66, 0.12, -1.08))
|
||||||
|
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"ManifoldPipe",
|
||||||
|
root,
|
||||||
|
Vector3(-0.78, 0.24, -1.13),
|
||||||
|
Vector3(-0.67, 0.09, -1.11),
|
||||||
|
0.018,
|
||||||
|
_materials.copper
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"ManifoldReturn",
|
||||||
|
root,
|
||||||
|
Vector3(-0.68, 0.25, -1.15),
|
||||||
|
Vector3(-0.59, 0.02, -1.10),
|
||||||
|
0.014,
|
||||||
|
_materials.brass
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_tank(root: Node3D, tank_position: Vector3) -> void:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"OxygenTank",
|
||||||
|
root,
|
||||||
|
tank_position,
|
||||||
|
0.055,
|
||||||
|
0.43,
|
||||||
|
_materials.safety_yellow
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_sphere(
|
||||||
|
"TankTop",
|
||||||
|
root,
|
||||||
|
tank_position + Vector3(0.0, 0.215, 0.0),
|
||||||
|
0.055,
|
||||||
|
_materials.safety_yellow
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_sphere(
|
||||||
|
"TankBottom",
|
||||||
|
root,
|
||||||
|
tank_position - Vector3(0.0, 0.215, 0.0),
|
||||||
|
0.055,
|
||||||
|
_materials.safety_yellow
|
||||||
|
)
|
||||||
|
for offset_y: float in [-0.13, 0.13]:
|
||||||
|
var strap := MeshInstance3D.new()
|
||||||
|
strap.name = "TankStrap"
|
||||||
|
var torus := TorusMesh.new()
|
||||||
|
torus.inner_radius = 0.054
|
||||||
|
torus.outer_radius = 0.066
|
||||||
|
torus.rings = 20
|
||||||
|
torus.ring_segments = 6
|
||||||
|
strap.mesh = torus
|
||||||
|
strap.material_override = _materials.dark_steel
|
||||||
|
strap.position = tank_position + Vector3(0.0, offset_y, 0.0)
|
||||||
|
root.add_child(strap)
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"TankValve",
|
||||||
|
root,
|
||||||
|
tank_position + Vector3(0.0, 0.29, 0.0),
|
||||||
|
0.017,
|
||||||
|
0.075,
|
||||||
|
_materials.brass
|
||||||
|
)
|
||||||
|
_tank_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
func _build_right_electrical_bank() -> void:
|
||||||
|
var root := Node3D.new()
|
||||||
|
root.name = "ElectricalBank"
|
||||||
|
add_child(root)
|
||||||
|
_add_service_box(root, Vector3(0.72, 0.65, -1.17), "ELEC", 11.0)
|
||||||
|
_add_service_box(root, Vector3(0.73, 0.36, -1.16), "POMPES", 8.0)
|
||||||
|
_add_vent(root, Vector3(0.705, 0.69, -1.095))
|
||||||
|
_add_vent(root, Vector3(0.715, 0.40, -1.085))
|
||||||
|
_add_knob(root, Vector3(0.66, 0.55, -1.08))
|
||||||
|
_add_knob(root, Vector3(0.68, 0.25, -1.07))
|
||||||
|
_add_indicator_column(root, Vector3(0.795, 0.49, -1.075))
|
||||||
|
|
||||||
|
for pipe_x: float in [0.64, 0.82]:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"ElectricalConduit",
|
||||||
|
root,
|
||||||
|
Vector3(pipe_x, 0.70, -1.20),
|
||||||
|
0.017,
|
||||||
|
0.58,
|
||||||
|
_materials.copper
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_service_box(
|
||||||
|
root: Node3D,
|
||||||
|
box_position: Vector3,
|
||||||
|
caption: String,
|
||||||
|
yaw_degrees: float
|
||||||
|
) -> void:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"JunctionBox",
|
||||||
|
root,
|
||||||
|
box_position,
|
||||||
|
Vector3(0.17, 0.23, 0.12),
|
||||||
|
_materials.overhead_steel,
|
||||||
|
Vector3(0.0, yaw_degrees, 0.0)
|
||||||
|
)
|
||||||
|
var label := Label3D.new()
|
||||||
|
label.name = "%sPlate" % caption.to_pascal_case()
|
||||||
|
label.text = caption
|
||||||
|
label.font_size = 12
|
||||||
|
label.pixel_size = 0.00065
|
||||||
|
label.modulate = Color(0.13, 0.16, 0.14)
|
||||||
|
label.position = box_position + Vector3(0.0, 0.075, 0.067)
|
||||||
|
root.add_child(label)
|
||||||
|
for corner: Vector2 in [
|
||||||
|
Vector2(-0.065, -0.085),
|
||||||
|
Vector2(-0.065, 0.085),
|
||||||
|
Vector2(0.065, -0.085),
|
||||||
|
Vector2(0.065, 0.085),
|
||||||
|
]:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"BoxFastener",
|
||||||
|
root,
|
||||||
|
box_position + Vector3(corner.x, corner.y, 0.065),
|
||||||
|
0.008,
|
||||||
|
0.01,
|
||||||
|
_materials.dark_steel,
|
||||||
|
Vector3(90.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
_junction_box_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
func _add_vent(root: Node3D, center: Vector3) -> void:
|
||||||
|
for row in 5:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"VentSlit",
|
||||||
|
root,
|
||||||
|
center + Vector3(0.0, (row - 2) * 0.018, 0.0),
|
||||||
|
Vector3(0.085, 0.007, 0.009),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_knob(root: Node3D, knob_position: Vector3) -> void:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"ControlKnob",
|
||||||
|
root,
|
||||||
|
knob_position,
|
||||||
|
0.027,
|
||||||
|
0.026,
|
||||||
|
_materials.rubber,
|
||||||
|
Vector3(90.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
_control_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
func _add_indicator_column(root: Node3D, center: Vector3) -> void:
|
||||||
|
var indicator_materials: Array[StandardMaterial3D] = [
|
||||||
|
_materials.indicator_green,
|
||||||
|
_materials.indicator_amber,
|
||||||
|
_materials.indicator_red,
|
||||||
|
]
|
||||||
|
for index in 3:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"PilotLightBezel",
|
||||||
|
root,
|
||||||
|
center + Vector3(0.0, (1 - index) * 0.055, -0.004),
|
||||||
|
Vector3(0.038, 0.038, 0.018),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_sphere(
|
||||||
|
"PilotLight",
|
||||||
|
root,
|
||||||
|
center + Vector3(0.0, (1 - index) * 0.055, 0.010),
|
||||||
|
0.013,
|
||||||
|
indicator_materials[index]
|
||||||
|
)
|
||||||
|
_control_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
func _add_valve_wheel(root: Node3D, wheel_position: Vector3) -> void:
|
||||||
|
var wheel := MeshInstance3D.new()
|
||||||
|
wheel.name = "ValveWheel"
|
||||||
|
var torus := TorusMesh.new()
|
||||||
|
torus.inner_radius = 0.034
|
||||||
|
torus.outer_radius = 0.046
|
||||||
|
torus.rings = 20
|
||||||
|
torus.ring_segments = 7
|
||||||
|
wheel.mesh = torus
|
||||||
|
wheel.material_override = _materials.signal_red
|
||||||
|
wheel.position = wheel_position
|
||||||
|
wheel.rotation_degrees.x = 90.0
|
||||||
|
root.add_child(wheel)
|
||||||
|
for angle_degrees: float in [0.0, 45.0, 90.0, 135.0]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"ValveSpoke",
|
||||||
|
root,
|
||||||
|
wheel_position + Vector3(0.0, 0.0, 0.002),
|
||||||
|
Vector3(0.075, 0.007, 0.008),
|
||||||
|
_materials.signal_red,
|
||||||
|
Vector3(0.0, 0.0, angle_degrees)
|
||||||
|
)
|
||||||
|
_control_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
func _build_cable_looms() -> void:
|
||||||
|
var root := Node3D.new()
|
||||||
|
root.name = "CableLooms"
|
||||||
|
add_child(root)
|
||||||
|
var left_points: Array[Vector3] = [
|
||||||
|
Vector3(-0.82, 0.93, -1.15),
|
||||||
|
Vector3(-0.72, 0.87, -1.16),
|
||||||
|
Vector3(-0.64, 0.73, -1.14),
|
||||||
|
Vector3(-0.61, 0.58, -1.12),
|
||||||
|
]
|
||||||
|
var right_points: Array[Vector3] = [
|
||||||
|
Vector3(0.84, 0.91, -1.15),
|
||||||
|
Vector3(0.75, 0.86, -1.16),
|
||||||
|
Vector3(0.68, 0.73, -1.14),
|
||||||
|
Vector3(0.65, 0.59, -1.12),
|
||||||
|
]
|
||||||
|
_add_cable_path(root, left_points)
|
||||||
|
_add_cable_path(root, right_points)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_cable_path(root: Node3D, points: Array[Vector3]) -> void:
|
||||||
|
for point_index in points.size() - 1:
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"ArmouredCable",
|
||||||
|
root,
|
||||||
|
points[point_index],
|
||||||
|
points[point_index + 1],
|
||||||
|
0.012,
|
||||||
|
_materials.rubber
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_control_rails() -> void:
|
||||||
|
var root := Node3D.new()
|
||||||
|
root.name = "ControlRails"
|
||||||
|
add_child(root)
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"LowerSteelRail",
|
||||||
|
root,
|
||||||
|
Vector3(-0.82, -0.34, -1.12),
|
||||||
|
Vector3(0.82, -0.34, -1.12),
|
||||||
|
0.026,
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"LeftGrip",
|
||||||
|
root,
|
||||||
|
Vector3(-0.58, -0.34, -1.11),
|
||||||
|
Vector3(-0.18, -0.34, -1.11),
|
||||||
|
0.034,
|
||||||
|
_materials.rubber
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"RightGrip",
|
||||||
|
root,
|
||||||
|
Vector3(0.18, -0.34, -1.11),
|
||||||
|
Vector3(0.58, -0.34, -1.11),
|
||||||
|
0.034,
|
||||||
|
_materials.rubber
|
||||||
|
)
|
||||||
|
for x: float in [-0.82, 0.82]:
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"RailSupport",
|
||||||
|
root,
|
||||||
|
Vector3(x, -0.34, -1.12),
|
||||||
|
Vector3(x, -0.20, -1.08),
|
||||||
|
0.022,
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
class_name CockpitSonarConsole
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
const CockpitMaterialPalette = preload(
|
||||||
|
"res://game/presentation/cockpit/cockpit_material_palette.gd"
|
||||||
|
)
|
||||||
|
const CockpitMeshFactory = preload(
|
||||||
|
"res://game/presentation/cockpit/cockpit_mesh_factory.gd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _materials: CockpitMaterialPalette
|
||||||
|
var _control_count := 0
|
||||||
|
|
||||||
|
|
||||||
|
func _init(materials: CockpitMaterialPalette) -> void:
|
||||||
|
_materials = materials
|
||||||
|
name = "SonarConsoleDetailing"
|
||||||
|
_build_bezel()
|
||||||
|
_build_graticule()
|
||||||
|
_build_control_strip()
|
||||||
|
_build_mounting_hardware()
|
||||||
|
|
||||||
|
|
||||||
|
func control_count() -> int:
|
||||||
|
return _control_count
|
||||||
|
|
||||||
|
|
||||||
|
func _build_bezel() -> void:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"UpperBezel",
|
||||||
|
self,
|
||||||
|
Vector3(-0.025, 0.151, 0.073),
|
||||||
|
Vector3(0.41, 0.034, 0.035),
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"LowerBezel",
|
||||||
|
self,
|
||||||
|
Vector3(-0.025, -0.126, 0.073),
|
||||||
|
Vector3(0.41, 0.034, 0.035),
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
for x: float in [-0.225, 0.175]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"SideBezel",
|
||||||
|
self,
|
||||||
|
Vector3(x, 0.012, 0.073),
|
||||||
|
Vector3(0.035, 0.31, 0.035),
|
||||||
|
_materials.brushed_steel
|
||||||
|
)
|
||||||
|
var label := Label3D.new()
|
||||||
|
label.name = "SonarPlate"
|
||||||
|
label.text = "SONAR · BATHYMÉTRIE"
|
||||||
|
label.font_size = 12
|
||||||
|
label.pixel_size = 0.00062
|
||||||
|
label.modulate = Color(0.7, 0.73, 0.64)
|
||||||
|
label.position = Vector3(-0.04, 0.156, 0.097)
|
||||||
|
add_child(label)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_control_strip() -> void:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"ControlStrip",
|
||||||
|
self,
|
||||||
|
Vector3(0.235, 0.012, 0.072),
|
||||||
|
Vector3(0.075, 0.31, 0.04),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
var lamps: Array[StandardMaterial3D] = [
|
||||||
|
_materials.indicator_amber,
|
||||||
|
_materials.indicator_amber,
|
||||||
|
_materials.indicator_green,
|
||||||
|
]
|
||||||
|
for index in 3:
|
||||||
|
var y := 0.095 - index * 0.073
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"SonarLampBezel",
|
||||||
|
self,
|
||||||
|
Vector3(0.235, y, 0.101),
|
||||||
|
Vector3(0.044, 0.044, 0.018),
|
||||||
|
_materials.brass
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_sphere(
|
||||||
|
"SonarLamp",
|
||||||
|
self,
|
||||||
|
Vector3(0.235, y, 0.116),
|
||||||
|
0.013,
|
||||||
|
lamps[index]
|
||||||
|
)
|
||||||
|
_control_count += 1
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"SonarGainKnob",
|
||||||
|
self,
|
||||||
|
Vector3(0.235, -0.102, 0.108),
|
||||||
|
0.024,
|
||||||
|
0.026,
|
||||||
|
_materials.rubber,
|
||||||
|
Vector3(90.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
_control_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
func _build_graticule() -> void:
|
||||||
|
for x: float in [-0.145, -0.085, -0.025, 0.035, 0.095]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"SonarGridVertical",
|
||||||
|
self,
|
||||||
|
Vector3(x, 0.012, 0.076),
|
||||||
|
Vector3(0.0015, 0.215, 0.002),
|
||||||
|
_materials.sonar_grid
|
||||||
|
)
|
||||||
|
for y: float in [-0.075, -0.025, 0.025, 0.075]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"SonarGridHorizontal",
|
||||||
|
self,
|
||||||
|
Vector3(-0.025, y, 0.076),
|
||||||
|
Vector3(0.33, 0.0015, 0.002),
|
||||||
|
_materials.sonar_grid
|
||||||
|
)
|
||||||
|
var trace_points: Array[Vector3] = [
|
||||||
|
Vector3(-0.185, -0.035, 0.080),
|
||||||
|
Vector3(-0.135, -0.020, 0.080),
|
||||||
|
Vector3(-0.085, -0.055, 0.080),
|
||||||
|
Vector3(-0.030, -0.005, 0.080),
|
||||||
|
Vector3(0.025, -0.030, 0.080),
|
||||||
|
Vector3(0.075, 0.030, 0.080),
|
||||||
|
Vector3(0.125, 0.075, 0.080),
|
||||||
|
]
|
||||||
|
for point_index in trace_points.size() - 1:
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"SonarReferenceTrace",
|
||||||
|
self,
|
||||||
|
trace_points[point_index],
|
||||||
|
trace_points[point_index + 1],
|
||||||
|
0.0025,
|
||||||
|
_materials.sonar_trace
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_mounting_hardware() -> void:
|
||||||
|
for corner: Vector2 in [
|
||||||
|
Vector2(-0.205, -0.105),
|
||||||
|
Vector2(-0.205, 0.13),
|
||||||
|
Vector2(0.195, -0.105),
|
||||||
|
Vector2(0.195, 0.13),
|
||||||
|
]:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"SonarFastener",
|
||||||
|
self,
|
||||||
|
Vector3(corner.x, corner.y, 0.103),
|
||||||
|
0.008,
|
||||||
|
0.012,
|
||||||
|
_materials.brass,
|
||||||
|
Vector3(90.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_cylinder_between(
|
||||||
|
"LeftSonarHandle",
|
||||||
|
self,
|
||||||
|
Vector3(-0.255, -0.09, 0.06),
|
||||||
|
Vector3(-0.255, 0.11, 0.06),
|
||||||
|
0.013,
|
||||||
|
_materials.brass
|
||||||
|
)
|
||||||
@@ -19,7 +19,7 @@ const ProductionInputAdapter = preload(
|
|||||||
"res://game/infrastructure/input/production_input_adapter.gd"
|
"res://game/infrastructure/input/production_input_adapter.gd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TEST_COUNT := 65
|
const TEST_COUNT := 69
|
||||||
|
|
||||||
var _failures: Array[String] = []
|
var _failures: Array[String] = []
|
||||||
|
|
||||||
@@ -128,9 +128,13 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void:
|
|||||||
_expect(detailing.pressure_hull_rib_count() >= 6, "cockpit exposes reinforced pressure-hull structure")
|
_expect(detailing.pressure_hull_rib_count() >= 6, "cockpit exposes reinforced pressure-hull structure")
|
||||||
_expect(detailing.pipe_segment_count() >= 13, "cockpit exposes a complete life-support pipe run")
|
_expect(detailing.pipe_segment_count() >= 13, "cockpit exposes a complete life-support pipe run")
|
||||||
_expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot")
|
_expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot")
|
||||||
_expect(detailing.textures_are_loaded(), "cockpit loads its overhead and analog gauge textures")
|
_expect(detailing.textures_are_loaded(), "cockpit loads its dedicated analog gauge textures")
|
||||||
_expect(detailing.gauges_are_dashboard_mounted(), "analog gauges are mounted on the dashboard")
|
_expect(detailing.gauges_are_dashboard_mounted(), "analog gauges are mounted on the dashboard")
|
||||||
_expect(detailing.dashboard_control_count() >= 8, "dashboard exposes physical auxiliary controls")
|
_expect(detailing.dashboard_control_count() >= 8, "dashboard exposes physical auxiliary controls")
|
||||||
|
_expect(detailing.side_equipment_count() >= 5, "cockpit sidewalls expose tanks and junction boxes")
|
||||||
|
_expect(detailing.side_control_count() >= 9, "cockpit sidewalls expose valves and pilot controls")
|
||||||
|
_expect(detailing.sonar_control_count() >= 4, "sonar console exposes physical lamps and gain control")
|
||||||
|
_expect(detailing.practical_light_count() == 2, "cockpit uses two caged practical lights")
|
||||||
_expect(pressure_bulkhead.panel_count() == 1, "porthole bulkhead uses one continuous curved shell")
|
_expect(pressure_bulkhead.panel_count() == 1, "porthole bulkhead uses one continuous curved shell")
|
||||||
_expect(pressure_bulkhead.rectangular_whitebox_is_hidden(), "porthole rectangular whitebox is hidden")
|
_expect(pressure_bulkhead.rectangular_whitebox_is_hidden(), "porthole rectangular whitebox is hidden")
|
||||||
_expect(analog_gauges.gauge_count() == 4, "dashboard exposes four primary analog instruments")
|
_expect(analog_gauges.gauge_count() == 4, "dashboard exposes four primary analog instruments")
|
||||||
|
|||||||
Reference in new issue
Block a user