diff --git a/content/sites/vehicle_test.tres b/content/sites/vehicle_test.tres index 2d2df84..eb82678 100644 --- a/content/sites/vehicle_test.tres +++ b/content/sites/vehicle_test.tres @@ -23,6 +23,6 @@ script = ExtResource("3_usfet") site_id = &"vehicle_test" display_name = "Site de calibration du véhicule" terrain = ExtResource("4_awe7g") -playable_bounds_m = Rect2(-90, -176, 180, 204) +playable_bounds_m = Rect2(-90, -176, 180, 246) spawn_position_m = Vector3(0, -12, 12) placements = Array[ExtResource("1_j2ii4")]([SubResource("Resource_5juth"), SubResource("Resource_tboiu")]) diff --git a/content/sites/vehicle_test_terrain.res b/content/sites/vehicle_test_terrain.res index 02a7762..fd14ed2 100644 Binary files a/content/sites/vehicle_test_terrain.res and b/content/sites/vehicle_test_terrain.res differ diff --git a/docs/briefs/captures/vehicle_test_terrain_preview.png b/docs/briefs/captures/vehicle_test_terrain_preview.png index 5367085..834319c 100644 Binary files a/docs/briefs/captures/vehicle_test_terrain_preview.png and b/docs/briefs/captures/vehicle_test_terrain_preview.png differ diff --git a/game/composition/vertical_slice/dive_world.tscn b/game/composition/vertical_slice/dive_world.tscn new file mode 100644 index 0000000..a4454d3 --- /dev/null +++ b/game/composition/vertical_slice/dive_world.tscn @@ -0,0 +1,43 @@ +[gd_scene load_steps=7 format=3] + +[ext_resource type="PackedScene" path="res://game/world/sites/dive_site.tscn" id="1_site"] +[ext_resource type="PackedScene" path="res://game/world/submarine/submarine_actor.tscn" id="2_submarine"] +[ext_resource type="PackedScene" path="res://game/presentation/cockpit/cockpit_camera_rig.tscn" id="3_camera"] +[ext_resource type="PackedScene" path="res://game/presentation/cockpit/cockpit_interior.tscn" id="4_cockpit"] + +[sub_resource type="ParticleProcessMaterial" id="SuspendedMotion"] +emission_shape = 3 +emission_box_extents = Vector3(45, 28, 70) +direction = Vector3(0.1, 0.04, 0) +spread = 180.0 +initial_velocity_min = 0.02 +initial_velocity_max = 0.09 +gravity = Vector3(0, 0, 0) +scale_min = 0.025 +scale_max = 0.08 + +[sub_resource type="SphereMesh" id="SuspendedParticleMesh"] +radius = 0.035 +height = 0.07 + +[node name="DiveWorld" type="Node3D"] + +[node name="DiveSite" parent="." instance=ExtResource("1_site")] +unique_name_in_owner = true + +[node name="SubmarineActor" parent="." instance=ExtResource("2_submarine")] +unique_name_in_owner = true + +[node name="SuspendedParticles" type="GPUParticles3D" parent="SubmarineActor"] +amount = 360 +lifetime = 14.0 +preprocess = 14.0 +visibility_aabb = AABB(-50, -35, -78, 100, 70, 156) +process_material = SubResource("SuspendedMotion") +draw_pass_1 = SubResource("SuspendedParticleMesh") + +[node name="CockpitInterior" parent="SubmarineActor" instance=ExtResource("4_cockpit")] +unique_name_in_owner = true + +[node name="CockpitCameraRig" parent="SubmarineActor" instance=ExtResource("3_camera")] +unique_name_in_owner = true diff --git a/game/composition/vertical_slice/vehicle_test_world.tscn b/game/composition/vertical_slice/vehicle_test_world.tscn deleted file mode 100644 index 74dbbbb..0000000 --- a/game/composition/vertical_slice/vehicle_test_world.tscn +++ /dev/null @@ -1,20 +0,0 @@ -[gd_scene load_steps=5 format=3] - -[ext_resource type="PackedScene" path="res://game/world/sites/vehicle_test_environment.tscn" id="1_environment"] -[ext_resource type="PackedScene" path="res://game/world/submarine/submarine_actor.tscn" id="2_submarine"] -[ext_resource type="PackedScene" path="res://game/presentation/cockpit/cockpit_camera_rig.tscn" id="3_camera"] -[ext_resource type="PackedScene" path="res://game/presentation/cockpit/cockpit_interior.tscn" id="4_cockpit"] - -[node name="VehicleTestWorld" type="Node3D"] - -[node name="Environment" parent="." instance=ExtResource("1_environment")] - -[node name="SubmarineActor" parent="." instance=ExtResource("2_submarine")] -unique_name_in_owner = true -position = Vector3(0, -12, 12) - -[node name="CockpitInterior" parent="SubmarineActor" instance=ExtResource("4_cockpit")] -unique_name_in_owner = true - -[node name="CockpitCameraRig" parent="SubmarineActor" instance=ExtResource("3_camera")] -unique_name_in_owner = true diff --git a/game/composition/vertical_slice/vertical_slice_root.gd b/game/composition/vertical_slice/vertical_slice_root.gd index 2c2c4e8..860bd05 100644 --- a/game/composition/vertical_slice/vertical_slice_root.gd +++ b/game/composition/vertical_slice/vertical_slice_root.gd @@ -29,6 +29,12 @@ const SubmarineTelemetryAdapter = preload( const ReferenceBathymetryAcquisition = preload( "res://game/world/sonar/reference_bathymetry_acquisition.gd" ) +const DiveSiteDefinition = preload("res://game/domain/sites/dive_site_definition.gd") +const DiveSite = preload("res://game/world/sites/dive_site.gd") +const PlayableBoundsRule = preload("res://game/application/sites/playable_bounds_rule.gd") +const PlayableBoundsStatus = preload( + "res://game/application/sites/playable_bounds_status.gd" +) const ProductionInputAdapter = preload( "res://game/infrastructure/input/production_input_adapter.gd" ) @@ -51,9 +57,8 @@ const ConfirmationDialogScreen = preload( const RebindResult = preload("res://game/application/input/rebind_result.gd") const MAIN_MENU_SCENE = preload("res://game/presentation/main_menu/main_menu_screen.tscn") -const VEHICLE_TEST_WORLD_SCENE_PATH := ( - "res://game/composition/vertical_slice/vehicle_test_world.tscn" -) +const DIVE_WORLD_SCENE_PATH := "res://game/composition/vertical_slice/dive_world.tscn" +const DEFAULT_SITE_PATH := "res://content/sites/vehicle_test.tres" const COCKPIT_HUD_SCENE = preload("res://game/presentation/cockpit/cockpit_hud.tscn") const SONAR_DISPLAY_SCENE = preload( "res://game/presentation/sonar/sonar_display.tscn" @@ -87,6 +92,10 @@ var _sonar_service: SonarService var _sonar_display: SonarDisplay var _latest_update: DiveUpdate var _active_world: Node3D +var _selected_site_path := DEFAULT_SITE_PATH +var _dive_site: DiveSite +var _bounds_rule: PlayableBoundsRule +var _bounds_status: PlayableBoundsStatus var _active_screen: Control var _active_overlay: Control var _vehicle_command_sequence := 0 @@ -105,7 +114,7 @@ func _ready() -> void: _show_main_menu() -func _start_vehicle_test() -> void: +func _start_dive() -> void: var rules = DiveRulesResolver.new().resolve( DiveDifficultyProfile.standard(), DiveUpgradeEffects.new(), @@ -117,12 +126,29 @@ func _start_vehicle_test() -> void: if not session.is_ready(): return - var world_scene := load(VEHICLE_TEST_WORLD_SCENE_PATH) as PackedScene + var definition := load(_selected_site_path) as DiveSiteDefinition + if definition == null: + push_error("dive site definition %s cannot be loaded" % _selected_site_path) + return + var definition_error := definition.validation_error() + if not definition_error.is_empty(): + push_error("dive site %s is invalid: %s" % [_selected_site_path, definition_error]) + return + var world_scene := load(DIVE_WORLD_SCENE_PATH) as PackedScene if world_scene == null: return var world := world_scene.instantiate() as Node3D _replace_active_world(world) + _dive_site = world.get_node("%DiveSite") as DiveSite + var site_error := _dive_site.configure(definition) + if not site_error.is_empty(): + push_error("dive site %s cannot be built: %s" % [_selected_site_path, site_error]) + _show_main_menu() + return _actor = world.get_node("%SubmarineActor") as SubmarineActor + _actor.global_transform = _dive_site.spawn_transform() + _bounds_rule = PlayableBoundsRule.new(definition.playable_bounds_m) + _bounds_status = _bounds_rule.evaluate(_actor.global_position) _camera_controller = world.get_node("%CockpitCameraRig") as CockpitCameraController _instrument_panel = world.get_node("%CockpitInterior") as CockpitInstrumentPanel _hud = COCKPIT_HUD_SCENE.instantiate() as CockpitHud @@ -154,6 +180,7 @@ func _start_vehicle_test() -> void: _runner.next_sequence() ) _hud.present(_latest_update, initial_telemetry) + _hud.present_bounds(_bounds_status) _instrument_panel.present(_latest_update, initial_telemetry) _camera_controller.present_telemetry(initial_telemetry) _input_adapter.set_gameplay_enabled(true) @@ -196,6 +223,8 @@ func _physics_process(delta: float) -> void: return_route ) _hud.present(_latest_update, telemetry) + _bounds_status = _bounds_rule.evaluate(_actor.global_position) + _hud.present_bounds(_bounds_status) _instrument_panel.present(_latest_update, telemetry) _sonar_service.advance(delta) _present_sonar() @@ -243,6 +272,18 @@ func sonar_display() -> SonarDisplay: return _sonar_display +func select_dive_site(definition_path: String) -> void: + _selected_site_path = definition_path + + +func dive_site() -> DiveSite: + return _dive_site + + +func latest_bounds_status() -> PlayableBoundsStatus: + return _bounds_status + + func _finish_dive_motion() -> void: _dive_terminal = true _input_adapter.set_gameplay_enabled(false) @@ -444,6 +485,9 @@ func _show_main_menu() -> void: _pilot_use_case = null _telemetry_adapter = null _actor = null + _dive_site = null + _bounds_rule = null + _bounds_status = null _camera_controller = null _instrument_panel = null _hud = null @@ -457,7 +501,7 @@ func _show_main_menu() -> void: _replace_active_world(null) var screen := MAIN_MENU_SCENE.instantiate() as MainMenuScreen _replace_active_screen(screen) - screen.new_game_requested.connect(_start_vehicle_test) + screen.new_game_requested.connect(_start_dive) screen.quit_requested.connect(_quit_game) diff --git a/game/presentation/cockpit/cockpit_hud.gd b/game/presentation/cockpit/cockpit_hud.gd index f79e3c0..747f533 100644 --- a/game/presentation/cockpit/cockpit_hud.gd +++ b/game/presentation/cockpit/cockpit_hud.gd @@ -5,9 +5,12 @@ const DiveUpdate = preload("res://game/domain/dive/dive_update.gd") const DiveAlertLevel = preload("res://game/domain/dive/dive_alert_level.gd") const DiveTerminalReason = preload("res://game/domain/dive/dive_terminal_reason.gd") const VehicleTelemetry = preload("res://game/application/dive/vehicle_telemetry.gd") +const PlayableBoundsStatus = preload("res://game/application/sites/playable_bounds_status.gd") @onready var _alert_panel: PanelContainer = %AlertPanel @onready var _alert_label: Label = %AlertLabel +@onready var _bounds_panel: PanelContainer = %BoundsPanel +@onready var _bounds_label: Label = %BoundsLabel @onready var _ejection_panel: PanelContainer = %EjectionPanel @onready var _ejection_label: Label = %EjectionLabel @onready var _terminal_panel: PanelContainer = %TerminalPanel @@ -29,6 +32,21 @@ func present(update: DiveUpdate, telemetry: VehicleTelemetry) -> void: ] +func present_bounds(status: PlayableBoundsStatus) -> void: + if status == null or not status.requires_warning(): + _bounds_panel.visible = false + return + _bounds_panel.visible = true + if status.state == PlayableBoundsStatus.State.OUTSIDE: + _bounds_label.text = tr("HORS ZONE · FAITES DEMI-TOUR") + else: + _bounds_label.text = "%s · %d m" % [tr("LIMITE DE ZONE"), int(status.distance_to_edge_m)] + + +func bounds_warning_visible() -> bool: + return _bounds_panel.visible + + func alert_overlay_visible() -> bool: return _alert_panel.visible diff --git a/game/presentation/cockpit/cockpit_hud.tscn b/game/presentation/cockpit/cockpit_hud.tscn index ddc071b..c62e728 100644 --- a/game/presentation/cockpit/cockpit_hud.tscn +++ b/game/presentation/cockpit/cockpit_hud.tscn @@ -57,6 +57,27 @@ theme_override_font_sizes/font_size = 20 text = "ALERTE · ATTENTION" horizontal_alignment = 1 +[node name="BoundsPanel" type="PanelContainer" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_left = 560.0 +offset_top = 88.0 +offset_right = -560.0 +offset_bottom = 136.0 +grow_horizontal = 2 +theme_override_styles/panel = SubResource("AlertStyle") + +[node name="BoundsLabel" type="Label" parent="BoundsPanel"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(1, 0.82, 0.42, 1) +theme_override_font_sizes/font_size = 18 +text = "LIMITE DE ZONE" +horizontal_alignment = 1 + [node name="EjectionPanel" type="PanelContainer" parent="."] unique_name_in_owner = true visible = false diff --git a/game/world/sites/vehicle_test_environment.gd b/game/world/sites/vehicle_test_environment.gd deleted file mode 100644 index aeceefd..0000000 --- a/game/world/sites/vehicle_test_environment.gd +++ /dev/null @@ -1,153 +0,0 @@ -class_name VehicleTestEnvironment -extends Node3D - -const TERRAIN_COLUMNS := 61 -const TERRAIN_ROWS := 69 -const TERRAIN_WIDTH_M := 180.0 -const TERRAIN_LENGTH_M := 200.0 -const TERRAIN_NEAR_Z := 28.0 - - -func _ready() -> void: - _add_bathymetric_terrain() - _place_calibration_target() - _add_suspended_particles() - - -func _add_suspended_particles() -> void: - var particles := GPUParticles3D.new() - particles.name = "SuspendedParticles" - particles.position = Vector3(0.0, -34.0, -42.0) - particles.amount = 360 - particles.lifetime = 14.0 - particles.preprocess = 14.0 - particles.visibility_aabb = AABB( - Vector3(-50.0, -35.0, -78.0), - Vector3(100.0, 70.0, 156.0) - ) - - var motion := ParticleProcessMaterial.new() - motion.emission_shape = ParticleProcessMaterial.EMISSION_SHAPE_BOX - motion.emission_box_extents = Vector3(45.0, 28.0, 70.0) - motion.direction = Vector3(0.1, 0.04, 0.0) - motion.spread = 180.0 - motion.initial_velocity_min = 0.02 - motion.initial_velocity_max = 0.09 - motion.gravity = Vector3.ZERO - motion.scale_min = 0.025 - motion.scale_max = 0.08 - particles.process_material = motion - - var particle_mesh := SphereMesh.new() - particle_mesh.radius = 0.035 - particle_mesh.height = 0.07 - particles.draw_pass_1 = particle_mesh - add_child(particles) - - -func _add_bathymetric_terrain() -> void: - var surface := SurfaceTool.new() - surface.begin(Mesh.PRIMITIVE_TRIANGLES) - for row in range(TERRAIN_ROWS - 1): - for column in range(TERRAIN_COLUMNS - 1): - var top_left := _terrain_vertex(row, column) - var top_right := _terrain_vertex(row, column + 1) - var bottom_left := _terrain_vertex(row + 1, column) - var bottom_right := _terrain_vertex(row + 1, column + 1) - _add_terrain_triangle(surface, top_left, top_right, bottom_left) - _add_terrain_triangle(surface, top_right, bottom_right, bottom_left) - surface.generate_normals() - var mesh := surface.commit() - - var body := StaticBody3D.new() - body.name = "BathymetricTerrain" - body.set_meta("sonar_material", 1) - body.set_meta("sonar_reflectivity", 0.72) - var mesh_instance := MeshInstance3D.new() - mesh_instance.name = "Mesh" - mesh_instance.mesh = mesh - var material := StandardMaterial3D.new() - material.albedo_color = Color(0.075, 0.19, 0.19) - material.roughness = 0.92 - mesh_instance.material_override = material - body.add_child(mesh_instance) - var collision := CollisionShape3D.new() - collision.name = "Collision" - var height_map := HeightMapShape3D.new() - height_map.map_width = TERRAIN_COLUMNS - height_map.map_depth = TERRAIN_ROWS - var height_data := PackedFloat32Array() - height_data.resize(TERRAIN_COLUMNS * TERRAIN_ROWS) - for row in TERRAIN_ROWS: - for column in TERRAIN_COLUMNS: - var vertex := _terrain_vertex(row, column) - height_data[row * TERRAIN_COLUMNS + column] = vertex.y - height_map.map_data = height_data - collision.shape = height_map - collision.position = Vector3( - 0.0, - 0.0, - TERRAIN_NEAR_Z - TERRAIN_LENGTH_M * 0.5 - ) - collision.scale = Vector3( - TERRAIN_WIDTH_M / float(TERRAIN_COLUMNS - 1), - 1.0, - TERRAIN_LENGTH_M / float(TERRAIN_ROWS - 1) - ) - body.add_child(collision) - add_child(body) - - -func _terrain_vertex(row: int, column: int) -> Vector3: - var x := lerpf( - -TERRAIN_WIDTH_M * 0.5, - TERRAIN_WIDTH_M * 0.5, - float(column) / float(TERRAIN_COLUMNS - 1) - ) - var z := TERRAIN_NEAR_Z - ( - TERRAIN_LENGTH_M * float(row) / float(TERRAIN_ROWS - 1) - ) - return Vector3(x, _terrain_height(x, z), z) - - -func _terrain_height(x: float, z: float) -> float: - var distance_ratio := clampf( - (TERRAIN_NEAR_Z - z) / TERRAIN_LENGTH_M, - 0.0, - 1.0 - ) - var base := lerpf(-40.0, -78.0, distance_ratio) - var left_ridge := 13.0 * exp( - -(pow(x + 24.0, 2.0) / 210.0 + pow(z + 48.0, 2.0) / 980.0) - ) - var right_ridge := 15.0 * exp( - -(pow(x - 25.0, 2.0) / 250.0 + pow(z + 58.0, 2.0) / 1150.0) - ) - var channel := -8.5 * exp( - -(pow(x, 2.0) / 120.0 + pow(z + 66.0, 2.0) / 2500.0) - ) - var rear_rise := 8.0 * exp( - -(pow(x + 5.0, 2.0) / 900.0 + pow(z + 125.0, 2.0) / 620.0) - ) - var undulation := 1.8 * sin(x * 0.075) * cos(z * 0.048) - return base + left_ridge + right_ridge + channel + rear_rise + undulation - - -func _add_terrain_triangle( - surface: SurfaceTool, - a: Vector3, - b: Vector3, - c: Vector3 -) -> void: - surface.set_uv(Vector2(a.x, a.z) * 0.02) - surface.add_vertex(a) - surface.set_uv(Vector2(b.x, b.z) * 0.02) - surface.add_vertex(b) - surface.set_uv(Vector2(c.x, c.z) * 0.02) - surface.add_vertex(c) - - -func _place_calibration_target() -> void: - var target := get_node_or_null("SonarCalibrationTarget") as Node3D - if target != null: - target.position.y = _terrain_height(target.position.x, target.position.z) + 2.5 diff --git a/game/world/sites/vehicle_test_environment.tscn b/game/world/sites/vehicle_test_environment.tscn deleted file mode 100644 index ac53448..0000000 --- a/game/world/sites/vehicle_test_environment.tscn +++ /dev/null @@ -1,75 +0,0 @@ -[gd_scene load_steps=8 format=3] - -[ext_resource type="Script" path="res://game/world/sites/vehicle_test_environment.gd" id="1_environment"] - -[sub_resource type="Environment" id="EnvironmentUnderwater"] -background_mode = 1 -background_color = Color(0.005, 0.045, 0.065, 1) -ambient_light_source = 3 -ambient_light_color = Color(0.12, 0.32, 0.37, 1) -ambient_light_energy = 0.42 -fog_enabled = true -fog_light_color = Color(0.07, 0.25, 0.3, 1) -fog_light_energy = 0.48 -fog_density = 0.014 -fog_height = -4.0 -fog_height_density = 0.025 -volumetric_fog_enabled = true -volumetric_fog_density = 0.025 -volumetric_fog_length = 72.0 - -[sub_resource type="SphereMesh" id="BeaconMesh"] -radius = 0.55 -height = 1.1 - -[sub_resource type="StandardMaterial3D" id="MetalTargetMaterial"] -albedo_color = Color(0.24, 0.18, 0.08, 1) -metallic = 0.88 -roughness = 0.27 - -[sub_resource type="BoxMesh" id="MetalTargetMesh"] -size = Vector3(8, 5, 7) - -[sub_resource type="BoxShape3D" id="MetalTargetShape"] -size = Vector3(8, 5, 7) - -[sub_resource type="StandardMaterial3D" id="BeaconMaterial"] -shading_mode = 0 -albedo_color = Color(0.2, 0.78, 0.9, 1) -emission_enabled = true -emission = Color(0.2, 0.78, 0.9, 1) -emission_energy_multiplier = 5.0 - -[node name="VehicleTestEnvironment" type="Node3D"] -script = ExtResource("1_environment") - -[node name="WorldEnvironment" type="WorldEnvironment" parent="."] -environment = SubResource("EnvironmentUnderwater") - -[node name="AmbientDirection" type="DirectionalLight3D" parent="."] -rotation_degrees = Vector3(-52, -24, 0) -light_color = Color(0.39, 0.65, 0.7, 1) -light_energy = 0.38 -shadow_enabled = true - -[node name="NavigationBeacon" type="MeshInstance3D" parent="."] -position = Vector3(0, -15, -22) -mesh = SubResource("BeaconMesh") -material_override = SubResource("BeaconMaterial") - -[node name="BeaconLight" type="OmniLight3D" parent="NavigationBeacon"] -omni_range = 12.0 -light_color = Color(0.2, 0.78, 0.9, 1) -light_energy = 4.0 - -[node name="SonarCalibrationTarget" type="StaticBody3D" parent="."] -position = Vector3(0, -60, -58) -metadata/sonar_material = 2 -metadata/sonar_reflectivity = 1.0 - -[node name="Mesh" type="MeshInstance3D" parent="SonarCalibrationTarget"] -mesh = SubResource("MetalTargetMesh") -material_override = SubResource("MetalTargetMaterial") - -[node name="Collision" type="CollisionShape3D" parent="SonarCalibrationTarget"] -shape = SubResource("MetalTargetShape") diff --git a/tests/integration/run_vertical_slice_foundation.gd b/tests/integration/run_vertical_slice_foundation.gd index adbf33b..e1a5dae 100644 --- a/tests/integration/run_vertical_slice_foundation.gd +++ b/tests/integration/run_vertical_slice_foundation.gd @@ -5,6 +5,7 @@ const VerticalSliceRoot = preload( ) const MainMenuScreen = preload("res://game/presentation/main_menu/main_menu_screen.gd") const CockpitHud = preload("res://game/presentation/cockpit/cockpit_hud.gd") +const PlayableBoundsStatus = preload("res://game/application/sites/playable_bounds_status.gd") const CockpitDetailing = preload( "res://game/presentation/cockpit/cockpit_detailing.gd" ) @@ -19,7 +20,7 @@ const ProductionInputAdapter = preload( "res://game/infrastructure/input/production_input_adapter.gd" ) -const TEST_COUNT := 78 +const TEST_COUNT := 83 var _failures: Array[String] = [] @@ -75,6 +76,7 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void: 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 + composition.select_dive_site("res://content/sites/vehicle_test.tres") new_game_button.pressed.emit() await process_frame await physics_frame @@ -110,6 +112,22 @@ func _test_vehicle_slice(composition: VerticalSliceRoot) -> void: _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") + _expect(composition.dive_site() != null, "composition instantiates the selected dive site") + _expect( + composition.dive_site() != null + and composition.dive_site().definition().site_id == &"vehicle_test", + "selected site definition drives the world" + ) + _expect( + actor != null and actor.global_position.is_equal_approx(Vector3(0.0, -12.0, 12.0)), + "submarine spawns at the site spawn point" + ) + _expect( + composition.latest_bounds_status() != null + and composition.latest_bounds_status().state == PlayableBoundsStatus.State.INSIDE, + "spawn is inside the playable bounds" + ) + _expect(hud != null and not hud.bounds_warning_visible(), "no bounds warning inside the zone") if ( actor == null or camera == null diff --git a/tools/sites/vehicle_test_terrain_recipe.gd b/tools/sites/vehicle_test_terrain_recipe.gd index b207867..a13a716 100644 --- a/tools/sites/vehicle_test_terrain_recipe.gd +++ b/tools/sites/vehicle_test_terrain_recipe.gd @@ -11,7 +11,7 @@ const SitePlacement = preload("res://game/domain/sites/site_placement.gd") static func recipe() -> SiteTerrainBaker.Recipe: var result := SiteTerrainBaker.Recipe.new() result.columns = 61 - result.rows = 69 + result.rows = 83 result.cell_size_m = 3.0 result.origin_x_m = -90.0 result.origin_z_m = -176.0 @@ -35,7 +35,7 @@ static func definition(terrain: SiteTerrainData) -> DiveSiteDefinition: result.site_id = &"vehicle_test" result.display_name = "Site de calibration du véhicule" result.terrain = terrain - result.playable_bounds_m = Rect2(-90.0, -176.0, 180.0, 204.0) + result.playable_bounds_m = Rect2(-90.0, -176.0, 180.0, 246.0) result.spawn_position_m = Vector3(0.0, -12.0, 12.0) result.spawn_yaw_radians = 0.0 var target := SitePlacement.new()