extends SceneTree const VerticalSliceRoot = preload( "res://game/composition/vertical_slice/vertical_slice_root.gd" ) const MainMenuScreen = preload("res://game/presentation/main_menu/main_menu_screen.gd") const CockpitHud = preload("res://game/presentation/cockpit/cockpit_hud.gd") const CockpitDetailing = preload( "res://game/presentation/cockpit/cockpit_detailing.gd" ) const CockpitPressureBulkhead = preload( "res://game/presentation/cockpit/cockpit_pressure_bulkhead.gd" ) const CockpitAlertBox = preload("res://game/presentation/cockpit/cockpit_alert_box.gd") const CockpitAnalogGauges = preload( "res://game/presentation/cockpit/cockpit_analog_gauges.gd" ) const CockpitSonarConsole = preload( "res://game/presentation/cockpit/cockpit_sonar_console.gd" ) const CockpitSwitchPanel = preload( "res://game/presentation/cockpit/cockpit_switch_panel.gd" ) const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd") const PauseMenu = preload("res://game/presentation/pause/pause_menu.gd") const ControlsSettingsScreen = preload( "res://game/presentation/settings/controls_settings_screen.gd" ) const ProductionInputAdapter = preload( "res://game/infrastructure/input/production_input_adapter.gd" ) const SunlitShallowsEnvironment = preload( "res://game/world/sites/sunlit_shallows_environment.gd" ) const WreckActor = preload("res://game/world/wrecks/wreck_actor.gd") const SonarService = preload("res://game/application/sonar/sonar_service.gd") const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd") const TEST_COUNT := 99 var _failures: Array[String] = [] func _init() -> void: _run.call_deferred() func _run() -> void: change_scene_to_file( "res://game/composition/vertical_slice/vertical_slice_root.tscn" ) await process_frame await physics_frame var composition := current_scene as VerticalSliceRoot _expect(composition != null, "production composition loads") if composition != null: await _test_vehicle_slice(composition) _expect( int(ProjectSettings.get_setting("physics/common/physics_ticks_per_second")) == 60, "project physics runs at 60 Hz" ) var project_features: PackedStringArray = ProjectSettings.get_setting( "application/config/features" ) _expect("Forward Plus" in project_features, "production project uses Forward+") var exit_code := 0 if _failures.is_empty(): print("Vertical slice foundation integration: %d passed, 0 failed." % TEST_COUNT) else: for failure: String in _failures: push_error(failure) print("Vertical slice foundation integration: %d failed." % _failures.size()) exit_code = 1 Input.action_release(ProductionInputAdapter.ACTION_FORWARD) Input.action_release(ProductionInputAdapter.ACTION_RIGHT) if current_scene != null: current_scene.queue_free() await process_frame await process_frame quit(exit_code) func _test_vehicle_slice(composition: VerticalSliceRoot) -> void: var menu := composition.active_screen() as MainMenuScreen _expect(menu != null, "composition starts on main menu") if menu == null: return var continue_button := menu.get_node("%ContinueButton") as Button _expect(continue_button.disabled, "continue is disabled without save") var new_game_button := menu.get_node("%NewGameButton") as Button new_game_button.pressed.emit() await process_frame await physics_frame var actor = composition.submarine_actor() var camera = composition.camera_controller() var instruments = composition.cockpit_instrument_panel() var detailing: CockpitDetailing = ( instruments.detailing() if instruments != null else null ) var pressure_bulkhead := ( instruments.get_node_or_null("%CurvedPressureBulkhead") as CockpitPressureBulkhead if instruments != null else null ) var analog_gauges := ( instruments.get_node_or_null("Dashboard/AnalogGauges") as CockpitAnalogGauges if instruments != null else null ) var alert_box := ( instruments.get_node_or_null("AlertBox") as CockpitAlertBox if instruments != null else null ) var switch_panel := ( instruments.get_node_or_null("SwitchPanel") as CockpitSwitchPanel if instruments != null else null ) var adapter := composition.input_adapter() as ProductionInputAdapter var hud := composition.active_screen() as CockpitHud var sonar_service = composition.sonar_service() var sonar_display = composition.sonar_display() var active_world := composition.active_world() var site: SunlitShallowsEnvironment = null var wreck: WreckActor = null var floor_body: StaticBody3D = null if active_world != null: site = active_world.get_node_or_null("Environment") as SunlitShallowsEnvironment if site != null: wreck = site.get_node_or_null("HebatAllahWreck") as WreckActor floor_body = site.get_node_or_null("FlatFloor") as StaticBody3D _expect(active_world != null and active_world.name == "SunlitShallowsWorld", "new game loads sunlit shallows world") _expect(site != null, "sunlit environment is active") _expect( floor_body != null and floor_body.get_node_or_null("Collision") is CollisionShape3D, "active dive has a physical seabed" ) _expect( wreck != null and wreck.get_node_or_null("Collision") is CollisionShape3D, "active dive contains the collidable Hebat Allah wreck" ) _expect( actor != null and wreck != null and (wreck.global_position - actor.global_position).dot(-actor.global_basis.z) > 0.0 and actor.global_position.distance_to(wreck.global_position) < SonarService.MAXIMUM_RANGE_M, "wreck starts ahead of the submarine within sonar range" ) _expect(actor != null, "production submarine actor is assembled") _expect(camera != null, "cockpit camera is attached to the submarine") _expect(instruments != null, "physical cockpit instruments are assembled") _expect(detailing != null, "cockpit structural detailing is assembled") _expect(pressure_bulkhead != null, "curved pressure bulkhead is assembled") _expect(analog_gauges != null, "dashboard analog instruments are assembled") _expect(alert_box != null, "alert box is assembled") _expect(switch_panel != null, "switch panel is assembled") _expect(hud != null, "critical HUD overlay is assembled") _expect(sonar_service != null, "production sonar service is assembled") _expect(sonar_display != null, "shared sonar presentation is assembled") _expect(composition.latest_update() != null, "dive session starts with a typed update") if ( actor == null or camera == null or instruments == null or detailing == null or pressure_bulkhead == null or analog_gauges == null or alert_box == null or switch_panel == null or adapter == null or hud == null ): return _expect(pressure_bulkhead.porthole_frame() is MeshInstance3D, "cockpit has a physical porthole frame") _expect(instruments.get_node("%Dashboard") is Node3D, "cockpit has a physical dashboard") _expect(instruments.get_node("%SonarScreen") is MeshInstance3D, "cockpit reserves a physical sonar display") var sonar_monitor := instruments.get_node("SonarMonitor") as CockpitSonarConsole var sonar_screen := instruments.get_node("%SonarScreen") as MeshInstance3D var sonar_housing := sonar_monitor.housing() var cockpit_camera := camera.get_node("Camera") as Camera3D var electrical_bank := detailing.get_node("SideEquipment/ElectricalBank") as Node3D _expect(sonar_monitor.position.x > 0.5, "cockpit sonar is mounted on the right side") _expect( _sonar_screen_faces_camera(sonar_screen, cockpit_camera), "right-side sonar display faces the pilot" ) _expect( _sonar_screen_is_ahead_of_bank(sonar_screen, electrical_bank), "right-side electrical equipment does not hide the sonar display" ) _expect( _sonar_screen_clears_housing(sonar_screen, sonar_housing), "sonar texture clears its housing without depth overlap" ) _expect( instruments.find_children("SonarReferenceTrace", "MeshInstance3D", true).is_empty(), "cockpit sonar does not invent a decorative profile" ) _expect( _shell_surrounds(pressure_bulkhead.hull_shell(), cockpit_camera.global_position), "pressure hull shell surrounds the pilot" ) _expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot") _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.dashboard_control_count() >= 8, "dashboard exposes physical auxiliary controls") _expect(detailing.side_equipment_count() >= 3, "cockpit sidewalls expose a gas bottle and junction boxes") _expect(detailing.side_control_count() >= 3, "cockpit sidewalls expose a valve 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.rectangular_whitebox_is_hidden(), "porthole rectangular whitebox is hidden") _expect(pressure_bulkhead.glass_is_on_dedicated_layer(), "porthole glass sits alone on its render layer") _expect(pressure_bulkhead.glass_roughness() >= 0.4, "porthole glass is rough enough to smear lamp reflections") _expect(detailing.fill_light_ignores_porthole_glass(), "porthole fill light does not reflect in the glass") _expect(analog_gauges.gauge_count() == 4, "dashboard exposes four primary analog instruments") _expect(alert_box.indicator_count() == 3, "alert box exposes three pilot lights") _expect(alert_box.active_indicator_count() == 1, "exactly one pilot light reflects the alert state") _expect( alert_box.active_indicator_name() == "Lamp_Alerte", "normal telemetry lights the green system lamp" ) _expect( analog_gauges.pilot_light_count() == 6 and analog_gauges.lit_pilot_light_count() == 0, "gauge panel pilot lights rest dark without per-instrument data" ) _expect( switch_panel.control_count() == 10 and switch_panel.controls_are_at_rest(), "decorative switches stay in their rest pose" ) _expect( _sonar_texture_matches_glass(instruments.sonar_texture(), sonar_screen), "sonar render target has the aspect ratio of the monitor glass" ) _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("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure") _expect(not hud.alert_overlay_visible(), "normal telemetry stays on physical instruments only") _expect( instruments.sonar_texture() == sonar_display.sonar_texture(), "physical and expanded sonar views share one texture" ) _expect(sonar_display.idle_prompt_visible(), "idle sonar explains how to emit a scan") adapter.sonar_scan_requested.emit() var first_scan = sonar_service.current_scan() _expect(first_scan != null, "manual sonar command publishes a typed scan") _expect(first_scan.samples.observed_count() > 1000, "reference acquisition captures dense bathymetry") var has_wreck_contact := false for contact in first_scan.contacts: if contact.material == SonarMaterial.Value.METAL: has_wreck_contact = true _expect(has_wreck_contact, "manual sonar sees the metal wreck in the active world") _expect("RELEVÉ EN COURS" in sonar_display.status_text(), "sonar display reports progressive reveal") _expect(not sonar_display.idle_prompt_visible(), "sonar data replaces the idle instruction") for frame in 18: await physics_frame _expect(sonar_display.built_row_count() > 0, "renderer reveals near rows incrementally") _expect(sonar_display.built_triangle_count() >= 90, "sonar renders a dense relief surface, not a line") _expect(sonar_display.built_grid_segment_count() > 0, "sonar overlays a perspective grid on the relief") var scale_texts: PackedStringArray = sonar_display.depth_scale_texts() _expect( scale_texts.size() >= 2 and scale_texts[0] == "%.0f m" % first_scan.maximum_elevation_m and scale_texts[scale_texts.size() - 1] == "%.0f m" % first_scan.minimum_elevation_m, "sonar height scale shows the real elevation span of the scan" ) adapter.sonar_view_toggle_requested.emit() _expect(sonar_display.is_expanded(), "sonar can expand without pausing the dive") adapter.sonar_view_toggle_requested.emit() _expect(not sonar_display.is_expanded(), "expanded sonar returns to cockpit view") var start_position: Vector3 = actor.global_position var start_wreck_distance: float = ( actor.global_position.distance_to(wreck.global_position) if wreck != null else INF ) var starting_battery: float = composition.latest_update().snapshot.battery.remaining_kwh Input.action_press(ProductionInputAdapter.ACTION_FORWARD) for frame in 35: await physics_frame Input.action_release(ProductionInputAdapter.ACTION_FORWARD) _expect(actor.global_position.distance_to(start_position) > 0.1, "keyboard propulsion moves the submarine") _expect( wreck != null and actor.global_position.distance_to(wreck.global_position) < start_wreck_distance - 0.1, "forward propulsion closes the distance to the wreck" ) _expect(actor.velocity.length() > 0.1, "propulsion builds inertial velocity") _expect( composition.latest_update().snapshot.battery.remaining_kwh < starting_battery, "propulsion effort drains the domain battery" ) var start_heading: float = actor.rotation.y Input.action_press(ProductionInputAdapter.ACTION_RIGHT) for frame in 24: await physics_frame Input.action_release(ProductionInputAdapter.ACTION_RIGHT) _expect(absf(actor.rotation.y - start_heading) > 0.01, "keyboard steering changes submarine heading") _expect(absf(actor.current_yaw_velocity_rps()) > 0.01, "yaw keeps angular inertia after release") for frame in 90: await physics_frame var settled_heading: float = actor.rotation.y var initial_look_yaw: float = camera.look_yaw_radians() var cockpit_transform: Transform3D = instruments.global_transform var mouse_motion := InputEventMouseMotion.new() mouse_motion.relative = Vector2(40.0, -20.0) adapter._unhandled_input(mouse_motion) await physics_frame _expect(absf(camera.look_yaw_radians() - initial_look_yaw) > 0.01, "mouse rotates the cockpit camera") _expect(absf(actor.rotation.y - settled_heading) < 0.001, "mouse does not steer the submarine") _expect(instruments.global_transform.is_equal_approx(cockpit_transform), "cockpit remains fixed while the player looks around") var paused_sequence: int = composition.latest_update().snapshot.tick_sequence var paused_sonar_progress: float = sonar_service.current_scan().reveal_progress adapter.pause_requested.emit() await process_frame _expect(paused, "pause menu suspends the scene tree") var pause_menu := composition.active_overlay() as PauseMenu _expect(pause_menu != null, "pause overlay is displayed") await process_frame await process_frame _expect( composition.latest_update().snapshot.tick_sequence == paused_sequence, "dive ticks are frozen while paused" ) _expect( is_equal_approx(sonar_service.current_scan().reveal_progress, paused_sonar_progress), "sonar reveal is frozen while paused" ) var controls_button := pause_menu.get_node("%ControlsButton") as Button controls_button.pressed.emit() await process_frame var controls := composition.active_overlay() as ControlsSettingsScreen _expect(controls != null, "controls settings open over the paused dive") _expect(adapter.binding_snapshots().size() == 11, "all gameplay keyboard bindings are exposed") var rebind_results: Array = [] adapter.rebind_result_available.connect( func(result) -> void: rebind_results.append(result) ) adapter.begin_rebind(ProductionInputAdapter.ACTION_FORWARD) adapter._unhandled_input(_pressed_key(KEY_UP)) _expect(not rebind_results.is_empty() and rebind_results.back().accepted, "free key remapping is accepted") _expect( _binding_for(adapter, ProductionInputAdapter.ACTION_FORWARD) == "Up", "binding snapshot reflects the remapped key" ) adapter.begin_rebind(ProductionInputAdapter.ACTION_BACKWARD) adapter._unhandled_input(_pressed_key(KEY_UP)) _expect( not rebind_results.back().accepted and rebind_results.back().conflicting_action_id == ProductionInputAdapter.ACTION_FORWARD, "binding conflict identifies the existing action" ) var swap_result = adapter.resolve_pending_conflict(true) _expect(swap_result.accepted, "conflicting bindings can be swapped") _expect( _binding_for(adapter, ProductionInputAdapter.ACTION_BACKWARD) == "Up", "requested action receives the conflicting key" ) adapter.restore_default_bindings() _expect( _binding_for(adapter, ProductionInputAdapter.ACTION_FORWARD) == "W", "default bindings can be restored" ) adapter.pause_requested.emit() await process_frame _expect(composition.active_overlay() is PauseMenu, "escape from settings returns to pause") adapter.pause_requested.emit() await process_frame _expect(not paused, "second escape resumes the dive") for frame in 3: await physics_frame if composition.latest_update().snapshot.tick_sequence > paused_sequence: break _expect( composition.latest_update().snapshot.tick_sequence > paused_sequence, "fixed dive ticks resume after pause" ) analog_gauges.present(0.0, 0.5, 1.0, 0.25) _expect( is_equal_approx(analog_gauges.needle_degrees("Needle_Oxygen"), 120.0), "empty gauge needle rests at the lower-left end of the printed scale" ) _expect( is_zero_approx(analog_gauges.needle_degrees("Needle_Battery")), "half-scale gauge needle points straight up" ) _expect( is_equal_approx(analog_gauges.needle_degrees("Needle_Hull"), -120.0), "full gauge needle reaches the lower-right end clockwise" ) _expect( is_equal_approx(analog_gauges.needle_degrees("Needle_Depth"), 60.0), "depth needle uses the same printed sweep" ) _expect( _alert_lamp_for(alert_box, DiveAlertLevel.Value.NORMAL) == "Lamp_Alerte", "normal level lights only the system lamp" ) _expect( _alert_lamp_for(alert_box, DiveAlertLevel.Value.ATTENTION) == "Lamp_Attention" and _alert_lamp_for(alert_box, DiveAlertLevel.Value.RETURN) == "Lamp_Attention", "attention and return levels light only the attention lamp" ) _expect( _alert_lamp_for(alert_box, DiveAlertLevel.Value.CRITICAL) == "Lamp_Danger" and _alert_lamp_for(alert_box, DiveAlertLevel.Value.FAILURE) == "Lamp_Danger", "critical and failure levels light only the danger lamp" ) func _pressed_key(keycode: Key) -> InputEventKey: var event := InputEventKey.new() event.physical_keycode = keycode event.pressed = true return event func _sonar_screen_faces_camera( sonar_screen: MeshInstance3D, cockpit_camera: Camera3D ) -> bool: if sonar_screen == null or cockpit_camera == null: return false var direction_to_camera := ( cockpit_camera.global_position - sonar_screen.global_position ).normalized() return sonar_screen.global_basis.z.normalized().dot(direction_to_camera) > 0.85 func _sonar_screen_is_ahead_of_bank( sonar_screen: MeshInstance3D, electrical_bank: Node3D ) -> bool: if sonar_screen == null or electrical_bank == null: return false for child: Node in electrical_bank.get_children(): if child is MeshInstance3D and child.name.begins_with("JunctionBox"): if sonar_screen.global_position.z <= (child as MeshInstance3D).global_position.z: return false return true ## The imported housing is an arbitrary mesh, so the clearance is measured on ## its triangles: looking straight at the glass, nothing of the housing may sit ## level with or in front of the picture. func _sonar_screen_clears_housing( sonar_screen: MeshInstance3D, sonar_housing: MeshInstance3D ) -> bool: if sonar_screen == null or sonar_housing == null or sonar_housing.mesh == null: return false var glass := sonar_screen.mesh.get_aabb() var screen_to_housing := ( sonar_housing.global_transform.affine_inverse() * sonar_screen.global_transform ) var outward := (screen_to_housing.basis * Vector3.BACK).normalized() var triangles := sonar_housing.mesh.get_faces() if triangles.is_empty(): return false for column in 5: for row in 3: var on_glass := Vector3( lerpf(glass.position.x, glass.end.x, 0.1 + 0.2 * column), lerpf(glass.position.y, glass.end.y, 0.1 + 0.4 * row), glass.position.z - 0.005 ) var origin := screen_to_housing * on_glass for index in range(0, triangles.size(), 3): if Geometry3D.ray_intersects_triangle( origin, outward, triangles[index], triangles[index + 1], triangles[index + 2] ) != null: return false return true func _shell_surrounds(shell: MeshInstance3D, point: Vector3) -> bool: if shell == null or shell.mesh == null: return false var local := shell.global_transform.affine_inverse() * point return shell.mesh.get_aabb().grow(-0.05).has_point(local) func _sonar_texture_matches_glass(texture: Texture2D, sonar_screen: MeshInstance3D) -> bool: if texture == null or sonar_screen == null: return false var glass := sonar_screen.mesh.get_aabb().size var texture_ratio := float(texture.get_width()) / float(texture.get_height()) return absf(texture_ratio - glass.x / glass.y) < 0.01 func _alert_lamp_for(alert_box: CockpitAlertBox, level: DiveAlertLevel.Value) -> String: alert_box.present(level) return alert_box.active_indicator_name() if alert_box.active_indicator_count() == 1 else "" func _binding_for(adapter: ProductionInputAdapter, action_id: StringName) -> String: for binding in adapter.binding_snapshots(): if binding.action_id == action_id: return binding.binding_text return "" func _expect(condition: bool, label: String) -> void: if not condition: _failures.append(label)