Integrate gauges into cockpit dashboard
This commit is contained in:
1 parent
0b79bf9b82
commit
53288b48c8
7 files changed
+213
-17
No files matched your search
@@ -20,7 +20,7 @@ production sous `game/` n'en dépend.
|
|||||||
télémétrie typés.
|
télémétrie typés.
|
||||||
- Habitacle 3D solidaire de la coque : hublot occultant, consoles, commandes,
|
- Habitacle 3D solidaire de la coque : hublot occultant, consoles, commandes,
|
||||||
éclairage, structure de coque renforcée, conduites, jauges analogiques et
|
éclairage, structure de coque renforcée, conduites, jauges analogiques et
|
||||||
emplacement réservé au sonar.
|
tableau d'instruments structuré, et emplacement réservé au sonar.
|
||||||
- Instruments physiques à valeurs réelles ; le HUD reste réservé aux alertes,
|
- Instruments physiques à valeurs réelles ; le HUD reste réservé aux alertes,
|
||||||
à l'éjection et aux états terminaux.
|
à l'éjection et aux états terminaux.
|
||||||
- Pause qui suspend les ticks, confirmations de sortie et premier remappage avec
|
- Pause qui suspend les ticks, confirmations de sortie et premier remappage avec
|
||||||
|
|||||||
@@ -811,6 +811,9 @@ Complément du 3 octobre 2026 : le plafond, le boîtier supérieur et les faces
|
|||||||
jauges utilisent des textures bitmap dédiées. Les cadrans reçoivent en plus des
|
jauges utilisent des textures bitmap dédiées. Les cadrans reçoivent en plus des
|
||||||
graduations, une aiguille peinte et une vitre distincte ; le plafonnier est
|
graduations, une aiguille peinte et une vitre distincte ; le plafonnier est
|
||||||
réduit et protégé par une grille pour conserver des proportions crédibles.
|
réduit et protégé par une grille pour conserver des proportions crédibles.
|
||||||
|
Les jauges de charge batterie et de pression coque sont regroupées dans un
|
||||||
|
binnacle solidaire du tableau de bord, hors du hublot. La façade du tableau est
|
||||||
|
structurée par des rails, séparateurs, vis et banques de commutateurs physiques.
|
||||||
|
|
||||||
- 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.
|
||||||
|
|||||||
@@ -12,8 +12,19 @@ var _pressure_needle: Node3D
|
|||||||
func _init(materials: CockpitMaterialPalette) -> void:
|
func _init(materials: CockpitMaterialPalette) -> void:
|
||||||
_materials = materials
|
_materials = materials
|
||||||
name = "AnalogGauges"
|
name = "AnalogGauges"
|
||||||
_battery_needle = _add_gauge("BatteryGauge", Vector3(-0.34, 0.48, -1.14), "BAT")
|
_build_binnacle()
|
||||||
_pressure_needle = _add_gauge("PressureGauge", Vector3(0.34, 0.48, -1.14), "BAR")
|
_battery_needle = _add_gauge(
|
||||||
|
"BatteryGauge",
|
||||||
|
Vector3(-0.095, 0.315, 0.145),
|
||||||
|
"%"
|
||||||
|
)
|
||||||
|
_pressure_needle = _add_gauge(
|
||||||
|
"PressureGauge",
|
||||||
|
Vector3(0.095, 0.315, 0.145),
|
||||||
|
"BAR"
|
||||||
|
)
|
||||||
|
_add_binnacle_label("CHARGE BAT.", Vector3(-0.095, 0.235, 0.160))
|
||||||
|
_add_binnacle_label("PRESSION COQUE", Vector3(0.095, 0.235, 0.160))
|
||||||
|
|
||||||
|
|
||||||
func present(battery_ratio: float, pressure_ratio: float) -> void:
|
func present(battery_ratio: float, pressure_ratio: float) -> void:
|
||||||
@@ -25,11 +36,39 @@ 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 _build_binnacle() -> void:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"BinnacleHousing",
|
||||||
|
self,
|
||||||
|
Vector3(0.0, 0.290, 0.095),
|
||||||
|
Vector3(0.40, 0.180, 0.105),
|
||||||
|
_materials.overhead_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"BinnacleFoot",
|
||||||
|
self,
|
||||||
|
Vector3(0.0, 0.205, 0.075),
|
||||||
|
Vector3(0.47, 0.040, 0.14),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _add_binnacle_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(gauge_name: String, position: Vector3, caption: String) -> Node3D:
|
func _add_gauge(gauge_name: String, position: Vector3, caption: String) -> Node3D:
|
||||||
var gauge := Node3D.new()
|
var gauge := Node3D.new()
|
||||||
gauge.name = gauge_name
|
gauge.name = gauge_name
|
||||||
gauge.position = position
|
gauge.position = position
|
||||||
gauge.scale = Vector3(0.78, 0.78, 0.78)
|
gauge.scale = Vector3(0.62, 0.62, 0.62)
|
||||||
add_child(gauge)
|
add_child(gauge)
|
||||||
CockpitMeshFactory.add_box(
|
CockpitMeshFactory.add_box(
|
||||||
"MountingPlate",
|
"MountingPlate",
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
class_name CockpitDashboardDetailing
|
||||||
|
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 = "DashboardDetailing"
|
||||||
|
_build_structural_frame()
|
||||||
|
_build_switch_banks()
|
||||||
|
|
||||||
|
|
||||||
|
func control_count() -> int:
|
||||||
|
return _control_count
|
||||||
|
|
||||||
|
|
||||||
|
func apply_screen_materials(dashboard: 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 _build_structural_frame() -> void:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"TopRail",
|
||||||
|
self,
|
||||||
|
Vector3(0.0, 0.255, 0.115),
|
||||||
|
Vector3(1.50, 0.055, 0.055),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"BottomRail",
|
||||||
|
self,
|
||||||
|
Vector3(0.0, -0.205, 0.115),
|
||||||
|
Vector3(1.50, 0.050, 0.055),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
for x: float in [-0.755, 0.755]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"SideRail",
|
||||||
|
self,
|
||||||
|
Vector3(x, 0.025, 0.115),
|
||||||
|
Vector3(0.050, 0.46, 0.055),
|
||||||
|
_materials.dark_steel
|
||||||
|
)
|
||||||
|
for x: float in [-0.238, 0.238]:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"ScreenSeparator",
|
||||||
|
self,
|
||||||
|
Vector3(x, 0.015, 0.118),
|
||||||
|
Vector3(0.038, 0.39, 0.048),
|
||||||
|
_materials.brass
|
||||||
|
)
|
||||||
|
for screw_position: Vector2 in [
|
||||||
|
Vector2(-0.72, -0.18),
|
||||||
|
Vector2(-0.72, 0.23),
|
||||||
|
Vector2(0.72, -0.18),
|
||||||
|
Vector2(0.72, 0.23),
|
||||||
|
Vector2(-0.24, -0.18),
|
||||||
|
Vector2(0.24, -0.18),
|
||||||
|
]:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"PanelScrew",
|
||||||
|
self,
|
||||||
|
Vector3(screw_position.x, screw_position.y, 0.151),
|
||||||
|
0.010,
|
||||||
|
0.010,
|
||||||
|
_materials.brass,
|
||||||
|
Vector3(90.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _build_switch_banks() -> void:
|
||||||
|
_add_switch_bank(-0.59, "AUX")
|
||||||
|
_add_switch_bank(0.59, "POMPES")
|
||||||
|
|
||||||
|
|
||||||
|
func _add_switch_bank(center_x: float, caption: String) -> void:
|
||||||
|
CockpitMeshFactory.add_box(
|
||||||
|
"SwitchPlate",
|
||||||
|
self,
|
||||||
|
Vector3(center_x, -0.205, 0.145),
|
||||||
|
Vector3(0.25, 0.060, 0.020),
|
||||||
|
_materials.overhead_steel
|
||||||
|
)
|
||||||
|
for offset: float in [-0.075, -0.025, 0.025, 0.075]:
|
||||||
|
CockpitMeshFactory.add_cylinder(
|
||||||
|
"ToggleSwitch",
|
||||||
|
self,
|
||||||
|
Vector3(center_x + offset, -0.195, 0.171),
|
||||||
|
0.006,
|
||||||
|
0.030,
|
||||||
|
_materials.dark_steel,
|
||||||
|
Vector3(90.0, 0.0, 0.0)
|
||||||
|
)
|
||||||
|
_control_count += 1
|
||||||
|
var label := Label3D.new()
|
||||||
|
label.name = "%sLabel" % caption.to_pascal_case()
|
||||||
|
label.text = caption
|
||||||
|
label.font_size = 10
|
||||||
|
label.pixel_size = 0.00055
|
||||||
|
label.modulate = Color(0.72, 0.69, 0.56)
|
||||||
|
label.position = Vector3(center_x, -0.235, 0.165)
|
||||||
|
add_child(label)
|
||||||
@@ -5,6 +5,9 @@ const DiveUpdate = preload("res://game/domain/dive/dive_update.gd")
|
|||||||
const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd")
|
const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd")
|
||||||
const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd")
|
const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd")
|
||||||
const CockpitAnalogGauges = preload("res://game/presentation/cockpit/cockpit_analog_gauges.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 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")
|
||||||
|
|
||||||
@@ -14,9 +17,8 @@ var _dark_steel: Material
|
|||||||
var _overhead_steel: Material
|
var _overhead_steel: Material
|
||||||
var _brass: Material
|
var _brass: Material
|
||||||
var _copper: Material
|
var _copper: Material
|
||||||
var _rubber: Material
|
|
||||||
var _warm_emission: StandardMaterial3D
|
|
||||||
var _analog_gauges: CockpitAnalogGauges
|
var _analog_gauges: CockpitAnalogGauges
|
||||||
|
var _dashboard_detailing: CockpitDashboardDetailing
|
||||||
var _warm_practical: OmniLight3D
|
var _warm_practical: OmniLight3D
|
||||||
var _emergency_light: OmniLight3D
|
var _emergency_light: OmniLight3D
|
||||||
|
|
||||||
@@ -28,7 +30,7 @@ func _ready() -> void:
|
|||||||
_build_life_support_pipes()
|
_build_life_support_pipes()
|
||||||
_build_overhead_console()
|
_build_overhead_console()
|
||||||
_build_panel_hardware()
|
_build_panel_hardware()
|
||||||
_build_analog_gauges()
|
_build_dashboard_instrumentation()
|
||||||
_build_practical_lights()
|
_build_practical_lights()
|
||||||
|
|
||||||
|
|
||||||
@@ -74,6 +76,14 @@ func textures_are_loaded() -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func dashboard_control_count() -> int:
|
||||||
|
return 0 if _dashboard_detailing == null else _dashboard_detailing.control_count()
|
||||||
|
|
||||||
|
|
||||||
|
func gauges_are_dashboard_mounted() -> bool:
|
||||||
|
return _analog_gauges != null and _analog_gauges.get_parent() == get_parent().get_node("Dashboard")
|
||||||
|
|
||||||
|
|
||||||
func _create_materials() -> void:
|
func _create_materials() -> void:
|
||||||
_materials = CockpitMaterialPalette.new()
|
_materials = CockpitMaterialPalette.new()
|
||||||
_painted_steel = _materials.painted_steel
|
_painted_steel = _materials.painted_steel
|
||||||
@@ -81,8 +91,6 @@ func _create_materials() -> void:
|
|||||||
_overhead_steel = _materials.overhead_steel
|
_overhead_steel = _materials.overhead_steel
|
||||||
_brass = _materials.brass
|
_brass = _materials.brass
|
||||||
_copper = _materials.copper
|
_copper = _materials.copper
|
||||||
_rubber = _materials.rubber
|
|
||||||
_warm_emission = _materials.warm_emission
|
|
||||||
|
|
||||||
|
|
||||||
func _apply_materials_to_existing_cockpit() -> void:
|
func _apply_materials_to_existing_cockpit() -> void:
|
||||||
@@ -91,7 +99,6 @@ func _apply_materials_to_existing_cockpit() -> void:
|
|||||||
[
|
[
|
||||||
"CabinShell/LeftWall",
|
"CabinShell/LeftWall",
|
||||||
"CabinShell/RightWall",
|
"CabinShell/RightWall",
|
||||||
"Dashboard/MainPanel",
|
|
||||||
"LeftConsole",
|
"LeftConsole",
|
||||||
"RightConsole",
|
"RightConsole",
|
||||||
]
|
]
|
||||||
@@ -112,6 +119,7 @@ func _apply_materials_to_existing_cockpit() -> void:
|
|||||||
[
|
[
|
||||||
"CabinShell/Ceiling",
|
"CabinShell/Ceiling",
|
||||||
"PortholeAssembly/TopBulkhead",
|
"PortholeAssembly/TopBulkhead",
|
||||||
|
"Dashboard/MainPanel",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
_apply_material_to_paths(
|
_apply_material_to_paths(
|
||||||
@@ -125,8 +133,6 @@ func _apply_materials_to_existing_cockpit() -> void:
|
|||||||
"VerticalKnob",
|
"VerticalKnob",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func _apply_material_to_paths(material: Material, paths: Array[String]) -> void:
|
func _apply_material_to_paths(material: Material, paths: Array[String]) -> void:
|
||||||
var cockpit := get_parent()
|
var cockpit := get_parent()
|
||||||
for path: String in paths:
|
for path: String in paths:
|
||||||
@@ -241,7 +247,7 @@ func _build_overhead_console() -> void:
|
|||||||
root,
|
root,
|
||||||
Vector3(0.0, 0.895, -0.985),
|
Vector3(0.0, 0.895, -0.985),
|
||||||
Vector3(0.13, 0.010, 0.026),
|
Vector3(0.13, 0.010, 0.026),
|
||||||
_warm_emission,
|
_materials.warm_emission,
|
||||||
Vector3(-8.0, 0.0, 0.0)
|
Vector3(-8.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
for guard_index in 5:
|
for guard_index in 5:
|
||||||
@@ -262,7 +268,7 @@ func _build_overhead_console() -> void:
|
|||||||
Vector3(x, 0.965, -0.985),
|
Vector3(x, 0.965, -0.985),
|
||||||
0.004,
|
0.004,
|
||||||
0.018,
|
0.018,
|
||||||
_rubber,
|
_materials.rubber,
|
||||||
Vector3(90.0, 0.0, 0.0)
|
Vector3(90.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -302,9 +308,15 @@ func _build_panel_hardware() -> void:
|
|||||||
root.add_child(label)
|
root.add_child(label)
|
||||||
|
|
||||||
|
|
||||||
func _build_analog_gauges() -> void:
|
func _build_dashboard_instrumentation() -> void:
|
||||||
|
var dashboard := get_parent().get_node_or_null("Dashboard") as Node3D
|
||||||
|
if dashboard == null:
|
||||||
|
return
|
||||||
|
_dashboard_detailing = CockpitDashboardDetailing.new(_materials)
|
||||||
|
dashboard.add_child(_dashboard_detailing)
|
||||||
|
_dashboard_detailing.apply_screen_materials(dashboard)
|
||||||
_analog_gauges = CockpitAnalogGauges.new(_materials)
|
_analog_gauges = CockpitAnalogGauges.new(_materials)
|
||||||
add_child(_analog_gauges)
|
dashboard.add_child(_analog_gauges)
|
||||||
|
|
||||||
|
|
||||||
func _build_practical_lights() -> void:
|
func _build_practical_lights() -> void:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ var rubber: Material
|
|||||||
var gauge_face: StandardMaterial3D
|
var gauge_face: StandardMaterial3D
|
||||||
var gauge_glass: StandardMaterial3D
|
var gauge_glass: StandardMaterial3D
|
||||||
var needle_paint: StandardMaterial3D
|
var needle_paint: StandardMaterial3D
|
||||||
|
var instrument_screen: ShaderMaterial
|
||||||
var warm_emission: StandardMaterial3D
|
var warm_emission: StandardMaterial3D
|
||||||
|
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ func _init() -> void:
|
|||||||
)
|
)
|
||||||
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()
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
@@ -167,6 +169,30 @@ func _glass_material() -> StandardMaterial3D:
|
|||||||
return material
|
return material
|
||||||
|
|
||||||
|
|
||||||
|
func _screen_material() -> ShaderMaterial:
|
||||||
|
var shader := Shader.new()
|
||||||
|
shader.code = """
|
||||||
|
shader_type spatial;
|
||||||
|
render_mode diffuse_burley, specular_schlick_ggx;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec2 centered = UV - vec2(0.5);
|
||||||
|
float vignette = 1.0 - smoothstep(0.28, 0.72, length(centered));
|
||||||
|
float scanline = 0.94 + 0.06 * sin(UV.y * 720.0);
|
||||||
|
float edge = mix(0.68, 1.0, vignette);
|
||||||
|
vec3 phosphor = vec3(0.025, 0.105, 0.11) * edge * scanline;
|
||||||
|
ALBEDO = phosphor;
|
||||||
|
EMISSION = phosphor * 0.38;
|
||||||
|
METALLIC = 0.08;
|
||||||
|
ROUGHNESS = 0.18;
|
||||||
|
SPECULAR = 0.82;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
var material := ShaderMaterial.new()
|
||||||
|
material.shader = shader
|
||||||
|
return material
|
||||||
|
|
||||||
|
|
||||||
func _emissive_material(color: Color, energy: float) -> StandardMaterial3D:
|
func _emissive_material(color: Color, energy: float) -> StandardMaterial3D:
|
||||||
var material := StandardMaterial3D.new()
|
var material := StandardMaterial3D.new()
|
||||||
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const ProductionInputAdapter = preload(
|
|||||||
"res://game/infrastructure/input/production_input_adapter.gd"
|
"res://game/infrastructure/input/production_input_adapter.gd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TEST_COUNT := 56
|
const TEST_COUNT := 58
|
||||||
|
|
||||||
var _failures: Array[String] = []
|
var _failures: Array[String] = []
|
||||||
|
|
||||||
@@ -112,6 +112,8 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void:
|
|||||||
_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 overhead and analog gauge textures")
|
||||||
|
_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("kWh" in instruments.resources_text() and "kW" in instruments.resources_text(), "battery instrument has exact units")
|
_expect("kWh" in instruments.resources_text() and "kW" in instruments.resources_text(), "battery instrument has exact units")
|
||||||
_expect("L/min" in instruments.resources_text(), "oxygen instrument has volume and consumption")
|
_expect("L/min" in instruments.resources_text(), "oxygen instrument has volume and consumption")
|
||||||
_expect("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure")
|
_expect("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure")
|
||||||
|
|||||||
Reference in new issue
Block a user