feat(content): bake the Banc du Vigilant site and its contract tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51634820bd
commit
a1493434e4
7 files changed
+454
No files matched your search
@@ -0,0 +1,121 @@
|
|||||||
|
[gd_resource type="Resource" script_class="DiveSiteDefinition" format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://game/domain/sites/site_placement.gd" id="1_n3xpu"]
|
||||||
|
[ext_resource type="Script" path="res://game/domain/sites/site_route.gd" id="2_36hd6"]
|
||||||
|
[ext_resource type="Script" path="res://game/domain/sites/dive_site_definition.gd" id="3_vix3p"]
|
||||||
|
[ext_resource type="Resource" path="res://content/sites/zone1_vigilant_terrain.res" id="4_i0v8k"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_rqt47"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"prospection_vessel"
|
||||||
|
kind = 0
|
||||||
|
position_m = Vector3(-12, -37.10001, 108)
|
||||||
|
yaw_radians = 0.35
|
||||||
|
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)
|
||||||
|
yaw_radians = 1.1
|
||||||
|
footprint_m = Vector3(12, 3, 4)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_yljal"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"decoy_coaster"
|
||||||
|
kind = 1
|
||||||
|
position_m = Vector3(140, -35.265244, 40)
|
||||||
|
yaw_radians = -0.6
|
||||||
|
footprint_m = Vector3(26, 6, 8)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_aqgwj"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"secondary_item"
|
||||||
|
kind = 2
|
||||||
|
position_m = Vector3(-6, -61.61916, 157)
|
||||||
|
footprint_m = Vector3(1, 1, 1)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_aervf"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"debris_1"
|
||||||
|
kind = 3
|
||||||
|
position_m = Vector3(-12, -45.756382, 134)
|
||||||
|
yaw_radians = 0.4
|
||||||
|
footprint_m = Vector3(3, 2, 2)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_qgawl"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"debris_2"
|
||||||
|
kind = 3
|
||||||
|
position_m = Vector3(-10, -48.518223, 139)
|
||||||
|
yaw_radians = 1.2
|
||||||
|
footprint_m = Vector3(6, 2.5, 2.5)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_q0ace"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"debris_3"
|
||||||
|
kind = 3
|
||||||
|
position_m = Vector3(-9, -53.75445, 146)
|
||||||
|
footprint_m = Vector3(2, 1.5, 2)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_hd5cl"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"debris_4"
|
||||||
|
kind = 3
|
||||||
|
position_m = Vector3(-7, -58.142864, 152)
|
||||||
|
yaw_radians = 2.0
|
||||||
|
footprint_m = Vector3(4, 1.5, 1.5)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_wy8hl"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"vessel_anchor"
|
||||||
|
kind = 5
|
||||||
|
position_m = Vector3(0, -14, -250)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_e72vy"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"echine"
|
||||||
|
position_m = Vector3(75, -20, -50)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_3n33i"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"deux_freres"
|
||||||
|
position_m = Vector3(-151, -18, -125)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_cdmnh"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"rides"
|
||||||
|
position_m = Vector3(0, -25, -230)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_u8dqp"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"saignee_entrance"
|
||||||
|
position_m = Vector3(-60, -34, -140)
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_3uttw"]
|
||||||
|
script = ExtResource("1_n3xpu")
|
||||||
|
placement_id = &"rupture"
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
maximum_depth_m = 48.0
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("3_vix3p")
|
||||||
|
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)
|
||||||
|
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")])
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bseks0pf7k0t4"
|
||||||
|
path="res://.godot/imported/zone1_vigilant_terrain_preview.png-c84b076e243497f30f994bef5c7d9142.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://docs/briefs/captures/zone1_vigilant_terrain_preview.png"
|
||||||
|
dest_files=["res://.godot/imported/zone1_vigilant_terrain_preview.png-c84b076e243497f30f994bef5c7d9142.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
@@ -131,6 +131,14 @@ printf 'Quality gate: site baker unit tests\n'
|
|||||||
2>&1 | tee "$QUALITY_TEMP_DIRECTORY/site-baker-unit-console.log"
|
2>&1 | tee "$QUALITY_TEMP_DIRECTORY/site-baker-unit-console.log"
|
||||||
check_godot_log "site baker unit tests" "$QUALITY_TEMP_DIRECTORY/site-baker-unit-console.log"
|
check_godot_log "site baker unit tests" "$QUALITY_TEMP_DIRECTORY/site-baker-unit-console.log"
|
||||||
|
|
||||||
|
printf 'Quality gate: site content contract tests\n'
|
||||||
|
"$GODOT_EXECUTABLE" --headless \
|
||||||
|
--log-file "$QUALITY_TEMP_DIRECTORY/site-content-contract.log" \
|
||||||
|
--path . \
|
||||||
|
--script tests/contract/run_site_content_tests.gd \
|
||||||
|
2>&1 | tee "$QUALITY_TEMP_DIRECTORY/site-content-contract-console.log"
|
||||||
|
check_godot_log "site content contract tests" "$QUALITY_TEMP_DIRECTORY/site-content-contract-console.log"
|
||||||
|
|
||||||
printf 'Quality gate: production foundation integration\n'
|
printf 'Quality gate: production foundation integration\n'
|
||||||
"$GODOT_EXECUTABLE" --headless \
|
"$GODOT_EXECUTABLE" --headless \
|
||||||
--log-file "$QUALITY_TEMP_DIRECTORY/foundation-integration.log" \
|
--log-file "$QUALITY_TEMP_DIRECTORY/foundation-integration.log" \
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd")
|
||||||
|
const DiveSiteDefinition = preload("res://game/domain/sites/dive_site_definition.gd")
|
||||||
|
const TerrainHeightField = preload("res://game/domain/sites/terrain_height_field.gd")
|
||||||
|
const SitePlacement = preload("res://game/domain/sites/site_placement.gd")
|
||||||
|
const SiteRoute = preload("res://game/domain/sites/site_route.gd")
|
||||||
|
const DiveDifficultyProfile = preload("res://game/domain/rules/dive_difficulty_profile.gd")
|
||||||
|
const DiveUpgradeEffects = preload("res://game/domain/rules/dive_upgrade_effects.gd")
|
||||||
|
const DiveAccessibilityAdjustments = preload(
|
||||||
|
"res://game/domain/rules/dive_accessibility_adjustments.gd"
|
||||||
|
)
|
||||||
|
const DiveRulesResolver = preload("res://game/domain/rules/dive_rules_resolver.gd")
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
var _failures: Array[String] = []
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
_test_zone1_content()
|
||||||
|
|
||||||
|
if _failures.is_empty():
|
||||||
|
print("Site content contract tests: %d passed, 0 failed." % TEST_COUNT)
|
||||||
|
quit(0)
|
||||||
|
else:
|
||||||
|
for failure: String in _failures:
|
||||||
|
push_error(failure)
|
||||||
|
print("Site content contract tests: %d failed." % _failures.size())
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
|
||||||
|
func _test_zone1_content() -> void:
|
||||||
|
var definition := load(SITE_PATH) as DiveSiteDefinition
|
||||||
|
_expect(definition != null, "zone 1 definition loads")
|
||||||
|
if definition == null:
|
||||||
|
return
|
||||||
|
_expect(definition.validation_error().is_empty(), "zone 1 definition is valid: %s" % definition.validation_error())
|
||||||
|
var field := definition.height_field()
|
||||||
|
if field == null:
|
||||||
|
return
|
||||||
|
var rules = DiveRulesResolver.new().resolve(
|
||||||
|
DiveDifficultyProfile.standard(),
|
||||||
|
DiveUpgradeEffects.new(),
|
||||||
|
DiveAccessibilityAdjustments.new()
|
||||||
|
)
|
||||||
|
var certified_m: float = rules.certified_depth_m
|
||||||
|
var critical_m: float = rules.critical_depth_m
|
||||||
|
|
||||||
|
_expect(definition.playable_bounds_m.is_equal_approx(Rect2(-300.0, -300.0, 600.0, 600.0)), "playable zone is 600 x 600 m")
|
||||||
|
_expect(field.minimum_elevation_m() >= -88.5 and field.maximum_elevation_m() <= -19.5, "terrain stays between 20 m and 88 m deep")
|
||||||
|
_expect(field.cell_size_m() == 1.5 and field.columns() == 513 and field.rows() == 513, "terrain grid is 513 x 513 cells of 1.5 m")
|
||||||
|
|
||||||
|
var targets := definition.placements_of_kind(SitePlacement.Kind.WRECK_TARGET)
|
||||||
|
var decoys := definition.placements_of_kind(SitePlacement.Kind.WRECK_DECOY)
|
||||||
|
var items := definition.placements_of_kind(SitePlacement.Kind.SECONDARY_ITEM)
|
||||||
|
var anchors := definition.placements_of_kind(SitePlacement.Kind.VESSEL_ANCHOR)
|
||||||
|
var debris := definition.placements_of_kind(SitePlacement.Kind.DEBRIS)
|
||||||
|
var landmarks := definition.placements_of_kind(SitePlacement.Kind.LANDMARK)
|
||||||
|
_expect(targets.size() == 1, "exactly one target wreck")
|
||||||
|
_expect(decoys.size() == 2, "exactly two decoy wrecks")
|
||||||
|
_expect(items.size() == 1, "exactly one secondary item")
|
||||||
|
_expect(anchors.size() == 1, "exactly one vessel anchor")
|
||||||
|
_expect(debris.size() >= 3, "a debris trail of at least three pieces")
|
||||||
|
_expect(landmarks.size() >= 4, "at least four named landmarks")
|
||||||
|
|
||||||
|
for wreck: SitePlacement in targets + decoys:
|
||||||
|
_expect(-wreck.position_m.y <= certified_m, "%s lies above the certified depth" % wreck.placement_id)
|
||||||
|
_expect(_rests_on_terrain(field, wreck), "%s rests on or in the seabed" % wreck.placement_id)
|
||||||
|
if items.size() == 1:
|
||||||
|
var item_depth := -items[0].position_m.y
|
||||||
|
_expect(item_depth > certified_m and item_depth <= critical_m, "secondary item is between 100 %% and 115 %% of the certified depth (%.1f m)" % item_depth)
|
||||||
|
_expect(_rests_on_terrain(field, items[0]), "secondary item rests on the seabed")
|
||||||
|
for piece: SitePlacement in debris:
|
||||||
|
if -piece.position_m.y < 45.0 or -piece.position_m.y > 66.0 or not _rests_on_terrain(field, piece):
|
||||||
|
_failures.append("debris %s is off the slope or floating" % piece.placement_id)
|
||||||
|
_expect(true, "debris pieces checked individually")
|
||||||
|
|
||||||
|
if anchors.size() == 1:
|
||||||
|
var seabed_under_anchor := -field.elevation_at(anchors[0].position_m.x, anchors[0].position_m.z)
|
||||||
|
_expect(seabed_under_anchor >= 20.0 and seabed_under_anchor <= 30.0, "vessel anchors over 20-30 m of seabed")
|
||||||
|
var spawn := definition.spawn_position_m
|
||||||
|
_expect(spawn.y - field.elevation_at(spawn.x, spawn.z) >= 8.0, "spawn has at least 8 m of water under the keel")
|
||||||
|
_expect(is_equal_approx(-spawn.y, 10.0), "spawn is 10 m deep")
|
||||||
|
|
||||||
|
var safe := definition.route(&"safe_ridge")
|
||||||
|
var risky := definition.route(&"risky_groove")
|
||||||
|
_expect(safe != null and safe.maximum_depth_m == 32.0, "safe route is declared with a 32 m ceiling")
|
||||||
|
_expect(risky != null and risky.maximum_depth_m == 48.0, "risky route is declared with a 48 m ceiling")
|
||||||
|
if safe != null and risky != null and targets.size() == 1:
|
||||||
|
_expect(_route_clears_terrain(field, safe), "safe route clears the terrain by %.0f m" % ROUTE_CLEARANCE_M)
|
||||||
|
_expect(_route_clears_terrain(field, risky), "risky route clears the terrain by %.0f m" % ROUTE_CLEARANCE_M)
|
||||||
|
var target := targets[0].position_m
|
||||||
|
_expect(
|
||||||
|
_xz_distance(safe.waypoints_m[-1], target) <= 25.0 and _xz_distance(risky.waypoints_m[-1], target) <= 25.0,
|
||||||
|
"both routes end within 25 m of the target"
|
||||||
|
)
|
||||||
|
_expect(_route_length(risky) < _route_length(safe) * 0.9, "risky route is shorter than the safe route")
|
||||||
|
|
||||||
|
var spaced := true
|
||||||
|
for i in landmarks.size():
|
||||||
|
for j in range(i + 1, landmarks.size()):
|
||||||
|
if _xz_distance(landmarks[i].position_m, landmarks[j].position_m) < MINIMUM_LANDMARK_SPACING_M:
|
||||||
|
spaced = false
|
||||||
|
_expect(spaced, "landmarks are at least 60 m apart")
|
||||||
|
|
||||||
|
|
||||||
|
func _rests_on_terrain(field: TerrainHeightField, placement: SitePlacement) -> bool:
|
||||||
|
var seabed := field.elevation_at(placement.position_m.x, placement.position_m.z)
|
||||||
|
var bottom := placement.position_m.y - placement.footprint_m.y * 0.5
|
||||||
|
return bottom >= seabed - placement.footprint_m.y and bottom <= seabed + 1.0
|
||||||
|
|
||||||
|
|
||||||
|
func _route_clears_terrain(field: TerrainHeightField, route: SiteRoute) -> bool:
|
||||||
|
for i in range(route.waypoints_m.size() - 1):
|
||||||
|
var a := route.waypoints_m[i]
|
||||||
|
var b := route.waypoints_m[i + 1]
|
||||||
|
var steps := maxi(int(ceilf(a.distance_to(b) / 2.0)), 1)
|
||||||
|
for step in range(steps + 1):
|
||||||
|
var point := a.lerp(b, float(step) / float(steps))
|
||||||
|
if point.y - field.elevation_at(point.x, point.z) < ROUTE_CLEARANCE_M:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _route_length(route: SiteRoute) -> float:
|
||||||
|
var length := 0.0
|
||||||
|
for i in range(route.waypoints_m.size() - 1):
|
||||||
|
length += route.waypoints_m[i].distance_to(route.waypoints_m[i + 1])
|
||||||
|
return length
|
||||||
|
|
||||||
|
|
||||||
|
func _xz_distance(a: Vector3, b: Vector3) -> float:
|
||||||
|
return Vector2(a.x, a.z).distance_to(Vector2(b.x, b.z))
|
||||||
|
|
||||||
|
|
||||||
|
func _expect(condition: bool, label: String) -> void:
|
||||||
|
if not condition:
|
||||||
|
_failures.append(label)
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
class_name Zone1VigilantRecipe
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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 the vessel (z = -300), 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.
|
||||||
|
SiteTerrainBaker.Roughness.new(1933, 0.02, 0.6),
|
||||||
|
]
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
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),
|
||||||
|
_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),
|
||||||
|
_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, 157.0), 0.0, Vector3(1.0, 1.0, 1.0), 0.0),
|
||||||
|
_on_seabed(field, &"debris_1", SitePlacement.Kind.DEBRIS,
|
||||||
|
Vector2(-12.0, 134.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, -250.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, Vector3(-60.0, -34.0, -140.0)),
|
||||||
|
_marker(&"rupture", SitePlacement.Kind.LANDMARK, Vector3(40.0, -40.0, 125.0)),
|
||||||
|
]
|
||||||
|
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(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(-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
|
||||||
Reference in new issue
Block a user