extends RefCounted const SiteTerrainBaker = preload("res://tools/site_terrain_baker.gd") const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd") const SiteTerrainData = preload("res://game/domain/sites/site_terrain_data.gd") const TerrainHeightField = preload("res://game/domain/sites/terrain_height_field.gd") const DiveSiteDefinition = preload("res://game/domain/sites/dive_site_definition.gd") const SitePlacement = preload("res://game/domain/sites/site_placement.gd") const SiteRoute = preload("res://game/domain/sites/site_route.gd") # Groove axis (la Saignée): north entrance to the shelf break. const GROOVE_START := Vector2(-60.0, -140.0) const GROOVE_END := Vector2(-10.0, 120.0) # Ridge axis (l'Échine). Starts south of the vessel so its crest never rises above 20 m. const RIDGE_START := Vector2(70.0, -150.0) const RIDGE_END := Vector2(80.0, 90.0) const BREAK_Z := 125.0 # Strata apply from here south: past the groove's end cap (120 m) and still 3 m before # the break's smoothstep has dropped half a metre. const STRATA_START_Z := 118.0 # The target's centre sits this far up the groove from GROOVE_END, so its 28 m hull # stops one groove half-width short of the end cap and rests on the flat groove floor. const TARGET_SET_BACK_M := 22.0 # The groove tapers to nothing over one half-width at each end: the entrance landmark # sits two half-widths inside, where the groove has its full depth. const ENTRANCE_INSET_M := 16.0 static func recipe() -> SiteTerrainBaker.Recipe: var result := SiteTerrainBaker.Recipe.new() result.columns = 513 result.rows = 513 result.cell_size_m = 1.5 result.origin_x_m = -384.0 result.origin_z_m = -384.0 result.floor_elevation_m = -88.0 result.rock_slope_degrees = 20.0 result.rock_reflectivity = 0.72 result.sand_reflectivity = 0.45 result.primitives = [ # Plateau: 24 m deep at z = -300 (about 25 m at the mooring), 6 m deeper over 400 m toward the south. SiteTerrainBaker.SlopedPlane.new(24.0, 0.0, -0.015, 0.0, -300.0), # L'Échine: long north-south rock ridge, 6 m high, flanks steeper than 20°. SiteTerrainBaker.Ridge.new(RIDGE_START, RIDGE_END, 12.0, 6.0, 25.0), # Les Deux Frères: twin pinnacles, 20 m apart, 6 m tall (crests stay below 20 m deep). SiteTerrainBaker.Blob.new(Vector2(-160.0, -130.0), 8.4, 8.4, 6.0), SiteTerrainBaker.Blob.new(Vector2(-142.0, -121.0), 8.4, 8.4, 6.0), # La Saignée: 16 m wide groove, 9 m deep, steep walls, the risky route. SiteTerrainBaker.Groove.new(GROOVE_START, GROOVE_END, 8.0, 9.0, 4.0), # Basin and patch reef for the steel coaster decoy, east of the ridge. SiteTerrainBaker.Blob.new(Vector2(140.0, 40.0), 30.0, 30.0, -8.0), SiteTerrainBaker.Blob.new(Vector2(125.0, 30.0), 7.7, 7.7, 2.5), SiteTerrainBaker.Blob.new(Vector2(155.0, 55.0), 7.7, 7.7, 2.5), SiteTerrainBaker.Blob.new(Vector2(135.0, 60.0), 7.7, 7.7, 2.5), # Shelf break (la rupture) then the drop-off (le tombant), clamped at -88 m. SiteTerrainBaker.Step.new(Vector2(0.0, BREAK_Z), Vector2(0.0, 1.0), 16.0, 10.0, 0.7), # Scree cones at the foot of the drop-off. SiteTerrainBaker.Blob.new(Vector2(-40.0, 200.0), 6.3, 6.3, 2.0), SiteTerrainBaker.Blob.new(Vector2(-5.0, 205.0), 6.3, 6.3, 2.0), SiteTerrainBaker.Blob.new(Vector2(25.0, 198.0), 6.3, 6.3, 2.0), # Les Rides: sand ripples near the vessel only. SiteTerrainBaker.Ripples.new(Vector2(1.0, 0.35), 9.0, 0.45, Vector2(0.0, -220.0), 90.0), # Deterministic roughness everywhere: the original 50 m texture, plus hummocks # and swales from 250 m down to 15 m (five octaves) so the plateau is never flat. SiteTerrainBaker.Roughness.new(1933, 0.02, 0.6), SiteTerrainBaker.Roughness.new(1934, 0.004, 2.2, 5), SiteTerrainBaker.Roughness.new(1936, 0.012, 1.2, 4), # Hummocks of 20 m down to 5 m, the scale the pilot sees through the fog. SiteTerrainBaker.Roughness.new(1937, 0.05, 0.6, 3), ] result.modifiers = [ # La rupture et le tombant are a carbonate drop-off: 4 m strata on the break # (full effect) and fainter ledges on the 35 degree talus below. The band starts # south of the target hull and of la Saignée's end cap, so neither changes. # Crumbly rock on every slope steeper than the sand rule, smooth sand elsewhere. # Before the strata so the ledges stay flat. SiteTerrainBaker.SlopeRoughness.new(1935, 0.12, 0.7, 20.0, 40.0), SiteTerrainBaker.Strata.new( 4.0, 0.85, 25.0, 40.0, Rect2(-384.0, STRATA_START_Z, 768.0, 384.0 - STRATA_START_Z), Vector2(0.008, 0.0), 1938, 2.0, 0.45 ), ] result.material_patches = [ SiteTerrainBaker.MaterialPatch.new(Vector2(140.0, 40.0), 28.0, SonarMaterial.Value.ROCK), ] return result static func definition(terrain: SiteTerrainData) -> DiveSiteDefinition: var field := TerrainHeightField.from_data(terrain) # The target hull's long axis (local +X) lies along la Saignée. var groove_axis := (GROOVE_END - GROOVE_START).normalized() var groove_yaw := atan2(-groove_axis.y, groove_axis.x) var target_xz := GROOVE_END - groove_axis * TARGET_SET_BACK_M var entrance_xz := GROOVE_START + groove_axis * ENTRANCE_INSET_M var entrance_m := Vector3(entrance_xz.x, field.elevation_at(entrance_xz.x, entrance_xz.y), entrance_xz.y) var result := DiveSiteDefinition.new() result.site_id = &"zone1_vigilant" result.display_name = "Banc du Vigilant" result.terrain = terrain result.playable_bounds_m = Rect2(-300.0, -300.0, 600.0, 600.0) # Spawn beside the mooring, facing south toward the site. result.spawn_position_m = Vector3(0.0, -10.0, -210.0) result.spawn_yaw_radians = PI result.placements = [ _on_seabed(field, &"prospection_vessel", SitePlacement.Kind.WRECK_TARGET, target_xz, groove_yaw, Vector3(28.0, 8.0, 9.0), 2.0), _on_seabed(field, &"decoy_yacht", SitePlacement.Kind.WRECK_DECOY, Vector2(-30.0, -150.0), 1.1, Vector3(12.0, 3.0, 4.0), 0.5), _on_seabed(field, &"decoy_coaster", SitePlacement.Kind.WRECK_DECOY, Vector2(140.0, 40.0), -0.6, Vector3(26.0, 6.0, 8.0), 1.2), _on_seabed(field, &"secondary_item", SitePlacement.Kind.SECONDARY_ITEM, Vector2(-6.0, 160.0), 0.0, Vector3(1.0, 1.0, 1.0), 0.0), _on_seabed(field, &"debris_1", SitePlacement.Kind.DEBRIS, Vector2(-12.0, 136.0), 0.4, Vector3(3.0, 2.0, 2.0), 0.3), _on_seabed(field, &"debris_2", SitePlacement.Kind.DEBRIS, Vector2(-10.0, 139.0), 1.2, Vector3(6.0, 2.5, 2.5), 0.4), _on_seabed(field, &"debris_3", SitePlacement.Kind.DEBRIS, Vector2(-9.0, 146.0), 0.0, Vector3(2.0, 1.5, 2.0), 0.2), _on_seabed(field, &"debris_4", SitePlacement.Kind.DEBRIS, Vector2(-7.0, 152.0), 2.0, Vector3(4.0, 1.5, 1.5), 0.3), _marker(&"vessel_anchor", SitePlacement.Kind.VESSEL_ANCHOR, Vector3(0.0, -14.0, -210.0)), _marker(&"echine", SitePlacement.Kind.LANDMARK, Vector3(75.0, -20.0, -50.0)), _marker(&"deux_freres", SitePlacement.Kind.LANDMARK, Vector3(-151.0, -18.0, -125.0)), _marker(&"rides", SitePlacement.Kind.LANDMARK, Vector3(0.0, -25.0, -230.0)), _marker(&"saignee_entrance", SitePlacement.Kind.LANDMARK, entrance_m), _marker(&"rupture", SitePlacement.Kind.LANDMARK, Vector3(40.0, -40.0, 125.0)), ] result.routes = [ _route(&"safe_ridge", 32.0, [ Vector3(0.0, -14.0, -210.0), Vector3(70.0, -16.0, -200.0), Vector3(76.0, -16.0, -100.0), Vector3(78.0, -16.0, 0.0), Vector3(76.0, -18.0, 80.0), Vector3(40.0, -24.0, 105.0), Vector3(0.0, -24.0, 100.0), Vector3(-14.0, -30.0, 98.0), ]), _route(&"risky_groove", 48.0, [ Vector3(0.0, -14.0, -210.0), Vector3(-60.0, -20.0, -150.0), Vector3(-50.0, -31.0, -100.0), Vector3(-35.0, -33.0, -20.0), Vector3(-22.0, -34.0, 60.0), Vector3(-14.0, -33.0, 98.0), ]), ] return result static func _on_seabed( field: TerrainHeightField, id: StringName, kind: SitePlacement.Kind, xz: Vector2, yaw_radians: float, footprint_m: Vector3, burial_m: float ) -> SitePlacement: var placement := SitePlacement.new() placement.placement_id = id placement.kind = kind placement.footprint_m = footprint_m placement.yaw_radians = yaw_radians var seabed := field.elevation_at(xz.x, xz.y) placement.position_m = Vector3(xz.x, seabed + footprint_m.y * 0.5 - burial_m, xz.y) return placement static func _marker(id: StringName, kind: SitePlacement.Kind, position_m: Vector3) -> SitePlacement: var placement := SitePlacement.new() placement.placement_id = id placement.kind = kind placement.position_m = position_m return placement static func _route(id: StringName, maximum_depth_m: float, waypoints: Array) -> SiteRoute: var route := SiteRoute.new() route.route_id = id route.maximum_depth_m = maximum_depth_m route.waypoints_m = PackedVector3Array(waypoints) return route