Terrain des sites de plongée : champ de hauteur authoré, Banc du Vigilant, formes variées et rendu sous-marin #4

Open
olix21 wants to merge 47 commits from olix21/worktree-terrain-site-zone1 AGit into main
pull from: olix21/worktree-terrain-site-zone1
4 changed files with 63 additions and 19 deletions
Showing only changes of commit be16d2ee07 - Show all commits

No files matched your search

+8 -7
View File
@@ -9,15 +9,15 @@
script = ExtResource("1_n3xpu")
placement_id = &"prospection_vessel"
kind = 0
position_m = Vector3(-12, -37.10001, 108)
yaw_radians = 0.35
position_m = Vector3(-14.154642, -37.198338, 98.39586)
yaw_radians = -1.380808047881126
footprint_m = Vector3(28, 8, 9)
[sub_resource type="Resource" id="Resource_xcojm"]
script = ExtResource("1_n3xpu")
placement_id = &"decoy_yacht"
kind = 1
position_m = Vector3(-40, -24.655516, -200)
position_m = Vector3(-30, -25.199738, -150)
yaw_radians = 1.1
footprint_m = Vector3(12, 3, 4)
@@ -71,7 +71,7 @@ footprint_m = Vector3(4, 1.5, 1.5)
script = ExtResource("1_n3xpu")
placement_id = &"vessel_anchor"
kind = 5
position_m = Vector3(0, -14, -250)
position_m = Vector3(0, -14, -210)
[sub_resource type="Resource" id="Resource_e72vy"]
script = ExtResource("1_n3xpu")
@@ -101,13 +101,13 @@ position_m = Vector3(40, -40, 125)
[sub_resource type="Resource" id="Resource_xvesq"]
script = ExtResource("2_36hd6")
route_id = &"safe_ridge"
waypoints_m = PackedVector3Array(0, -14, -250, 70, -16, -200, 76, -16, -100, 78, -16, 0, 76, -18, 80, 40, -24, 105, 0, -26, 100, -14, -30, 98)
waypoints_m = PackedVector3Array(0, -14, -210, 70, -16, -200, 76, -16, -100, 78, -16, 0, 76, -18, 80, 40, -24, 105, 0, -26, 100, -14, -30, 98)
maximum_depth_m = 32.0
[sub_resource type="Resource" id="Resource_lcu7s"]
script = ExtResource("2_36hd6")
route_id = &"risky_groove"
waypoints_m = PackedVector3Array(0, -14, -250, -60, -20, -150, -50, -31, -100, -35, -33, -20, -22, -34, 60, -14, -33, 98)
waypoints_m = PackedVector3Array(0, -14, -210, -60, -20, -150, -50, -31, -100, -35, -33, -20, -22, -34, 60, -14, -33, 98)
maximum_depth_m = 48.0
[resource]
@@ -116,6 +116,7 @@ site_id = &"zone1_vigilant"
display_name = "Banc du Vigilant"
terrain = ExtResource("4_i0v8k")
playable_bounds_m = Rect2(-300, -300, 600, 600)
spawn_position_m = Vector3(0, -10, -250)
spawn_position_m = Vector3(0, -10, -210)
spawn_yaw_radians = 3.141592653589793
placements = Array[ExtResource("1_n3xpu")]([SubResource("Resource_rqt47"), SubResource("Resource_xcojm"), SubResource("Resource_yljal"), SubResource("Resource_aqgwj"), SubResource("Resource_aervf"), SubResource("Resource_qgawl"), SubResource("Resource_q0ace"), SubResource("Resource_hd5cl"), SubResource("Resource_wy8hl"), SubResource("Resource_e72vy"), SubResource("Resource_3n33i"), SubResource("Resource_cdmnh"), SubResource("Resource_u8dqp"), SubResource("Resource_3uttw")])
routes = Array[ExtResource("2_36hd6")]([SubResource("Resource_xvesq"), SubResource("Resource_lcu7s")])
+3 -3
View File
@@ -86,7 +86,7 @@ crédibilité, conformément à la direction artistique.
| Champ de hauteur | 513 × 513 cellules, 1,5 m par cellule, élévations float32 (`SiteTerrainData`) |
| Profondeur min / max du terrain | 20 m / 88 m |
| Profondeur d'écrasement coque I | hors terrain : le fond ne tue jamais par lui-même |
| Mouillage du navire | centre nord du plateau, 24 m de fond |
| Mouillage du navire | centre nord du plateau, 25 m de fond |
| Point de départ du sous-marin | sous le navire, 10 m de profondeur |
Les distances sont dimensionnées pour l'autonomie Standard (environ 1,2 km,
@@ -150,14 +150,14 @@ correspond pas au rapport.
### 4.4 Les deux routes vers la cible
**Route sûre — par l'Échine (≈ 470 m, 26–32 m).** Depuis le navire, suivre la
**Route sûre — par l'Échine (≈ 450 m, 26–32 m).** Depuis le navire, suivre la
crête longue vers le sud. Elle est lumineuse, large, sans obstacle, et se lit
d'un seul scan. Elle mène au bord du plateau au-dessus de la Saignée : il
faut ensuite descendre dans le sillon par son extrémité nord, à faible
pente. Coût : temps et batterie. Récompense : zéro dégât, retour facile à
estimer.
**Route risquée — par la Saignée (≈ 370 m, 36–48 m).** Un sillon de 12 à
**Route risquée — par la Saignée (≈ 340 m, 36–48 m).** Un sillon de 12 à
18 m de large, aux parois de 8 à 10 m, qui coupe le plateau en diagonale et
débouche directement sur la proue de la cible. Sombre (les projecteurs
deviennent nécessaires), étroit (collisions), en pente (la batterie de
+36 -1
View File
@@ -15,7 +15,11 @@ const DiveRulesResolver = preload("res://game/domain/rules/dive_rules_resolver.g
const SITE_PATH := "res://content/sites/zone1_vigilant.tres"
const ROUTE_CLEARANCE_M := 3.0
const MINIMUM_LANDMARK_SPACING_M := 60.0
const TEST_COUNT := 30
const MAXIMUM_ANCHOR_TO_WRECK_M := 320.0
const MAXIMUM_WRECK_SPACING_M := 260.0
const HULL_HALF_LENGTH_M := 14.0
const HULL_SEABED_TOLERANCE_M := 2.0
const TEST_COUNT := 34
var _failures: Array[String] = []
@@ -107,6 +111,26 @@ func _test_zone1_content() -> void:
spaced = false
_expect(spaced, "landmarks are at least 60 m apart")
var wrecks := targets + decoys
if anchors.size() == 1:
var anchor := anchors[0].position_m
var farthest := 0.0
for wreck: SitePlacement in wrecks:
farthest = maxf(farthest, _xz_distance(anchor, wreck.position_m))
_expect(farthest <= MAXIMUM_ANCHOR_TO_WRECK_M, "every wreck lies within 320 m of the mooring (farthest %.0f m)" % farthest)
var widest := 0.0
for i in wrecks.size():
for j in range(i + 1, wrecks.size()):
widest = maxf(widest, _xz_distance(wrecks[i].position_m, wrecks[j].position_m))
_expect(widest <= MAXIMUM_WRECK_SPACING_M, "every pair of wrecks lies within 260 m (widest %.0f m)" % widest)
if targets.size() == 1:
var target_xz := Vector2(targets[0].position_m.x, targets[0].position_m.z)
var spawn_xz := Vector2(spawn.x, spawn.z)
var yaw := definition.spawn_yaw_radians
var spawn_forward := Vector2(-sin(yaw), -cos(yaw))
_expect(spawn_forward.dot(target_xz - spawn_xz) > 0.0, "the spawn faces the target wreck")
_expect(_hull_lies_along_seabed(field, targets[0]), "the target hull lies along the groove floor")
func _rests_on_terrain(field: TerrainHeightField, placement: SitePlacement) -> bool:
var seabed := field.elevation_at(placement.position_m.x, placement.position_m.z)
@@ -114,6 +138,17 @@ func _rests_on_terrain(field: TerrainHeightField, placement: SitePlacement) -> b
return bottom >= seabed - placement.footprint_m.y and bottom <= seabed + 1.0
func _hull_lies_along_seabed(field: TerrainHeightField, hull: SitePlacement) -> bool:
var centre := Vector2(hull.position_m.x, hull.position_m.z)
var long_axis := Vector2(cos(hull.yaw_radians), -sin(hull.yaw_radians))
var centre_seabed := field.elevation_at(centre.x, centre.y)
for side: float in [-1.0, 1.0]:
var end := centre + long_axis * HULL_HALF_LENGTH_M * side
if absf(field.elevation_at(end.x, end.y) - centre_seabed) > HULL_SEABED_TOLERANCE_M:
return false
return true
func _route_clears_terrain(field: TerrainHeightField, route: SiteRoute) -> bool:
for i in range(route.waypoints_m.size() - 1):
var a := route.waypoints_m[i]
+16 -8
View File
@@ -16,6 +16,9 @@ const GROOVE_END := Vector2(-10.0, 120.0)
const RIDGE_START := Vector2(70.0, -150.0)
const RIDGE_END := Vector2(80.0, 90.0)
const BREAK_Z := 125.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
static func recipe() -> SiteTerrainBaker.Recipe:
@@ -30,7 +33,7 @@ static func recipe() -> SiteTerrainBaker.Recipe:
result.rock_reflectivity = 0.72
result.sand_reflectivity = 0.45
result.primitives = [
# Plateau: 24 m deep at the vessel (z = -300), 6 m deeper over 400 m toward the south.
# 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),
@@ -63,18 +66,23 @@ static func recipe() -> SiteTerrainBaker.Recipe:
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 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)
result.spawn_position_m = Vector3(0.0, -10.0, -250.0)
result.spawn_yaw_radians = 0.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,
Vector2(-12.0, 108.0), 0.35, Vector3(28.0, 8.0, 9.0), 2.0),
target_xz, groove_yaw, Vector3(28.0, 8.0, 9.0), 2.0),
_on_seabed(field, &"decoy_yacht", SitePlacement.Kind.WRECK_DECOY,
Vector2(-40.0, -200.0), 1.1, Vector3(12.0, 3.0, 4.0), 0.5),
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,
@@ -87,7 +95,7 @@ static func definition(terrain: SiteTerrainData) -> DiveSiteDefinition:
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, -250.0)),
_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)),
@@ -96,12 +104,12 @@ static func definition(terrain: SiteTerrainData) -> DiveSiteDefinition:
]
result.routes = [
_route(&"safe_ridge", 32.0, [
Vector3(0.0, -14.0, -250.0), Vector3(70.0, -16.0, -200.0), Vector3(76.0, -16.0, -100.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, -26.0, 100.0), Vector3(-14.0, -30.0, 98.0),
]),
_route(&"risky_groove", 48.0, [
Vector3(0.0, -14.0, -250.0), Vector3(-60.0, -20.0, -150.0), Vector3(-50.0, -31.0, -100.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),
]),
]