feat(tools): irregular strata and angular boulder blocks

Strata gain a dip, a slow seeded warp that bends and notches the beds, and
a per-bed thickness and hardness jitter with ordered boundaries, so walls
never invert. BoulderField gains an angularity parameter: elongated
super-ellipse slabs with flat tops, sharp shoulders, random heading and
tilt, footprint area preserved. Both sites are re-baked with the new
settings; the coral heads stay rounded.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
OlivierandClaude Fable 5.1 committed 2026-10-06 23:31:11 +02:00
1 parent 34b39a8dbc
commit 61ef4a892d
15 files changed
+237 -46

No files matched your search

+69 -1
View File
@@ -8,7 +8,7 @@ const SiteTerrainData = preload("res://game/domain/sites/site_terrain_data.gd")
const ZONE1_TERRAIN_PATH := "res://content/sites/zone1_vigilant_terrain.res"
const TEST_COUNT := 48
const TEST_COUNT := 53
var _failures: Array[String] = []
@@ -24,6 +24,8 @@ func _init() -> void:
_test_boulder_field()
_test_strata()
_test_slope_roughness()
_test_irregular_strata()
_test_angular_boulders()
if _failures.is_empty():
print("Site baker unit tests: %d passed, 0 failed." % TEST_COUNT)
@@ -225,6 +227,72 @@ func _test_strata() -> void:
_expect(north_same and south_changed, "strata stay inside their region")
func _test_irregular_strata() -> void:
# 45 degree plane at 0.5 m per cell, beds of 2 m with dip, warp and thickness variation.
var recipe := _small_recipe()
recipe.columns = 81
recipe.rows = 81
recipe.cell_size_m = 0.5
recipe.origin_x_m = -20.0
recipe.origin_z_m = -20.0
recipe.primitives.append(SiteTerrainBaker.SlopedPlane.new(30.3, 1.0, 0.0, 0.0, 0.0))
var regular_recipe := _small_recipe()
regular_recipe.columns = 81
regular_recipe.rows = 81
regular_recipe.cell_size_m = 0.5
regular_recipe.origin_x_m = -20.0
regular_recipe.origin_z_m = -20.0
regular_recipe.primitives.append(SiteTerrainBaker.SlopedPlane.new(30.3, 1.0, 0.0, 0.0, 0.0))
regular_recipe.modifiers.append(SiteTerrainBaker.Strata.new(2.0, 0.9, 20.0, 35.0, Rect2()))
var regular := SiteTerrainBaker.bake(regular_recipe)
recipe.modifiers.append(SiteTerrainBaker.Strata.new(2.0, 0.9, 20.0, 35.0, Rect2(), Vector2(0.0, 0.05), 11, 1.0, 0.6))
var irregular := SiteTerrainBaker.bake(recipe)
var monotonic := true
var ledges := 0
var on_ledge := false
var off_grid_ledges := 0
for column in range(10, 70):
var here := irregular.elevations_m[irregular.index_of(column, 40)]
var next := irregular.elevations_m[irregular.index_of(column + 1, 40)]
# Ledges follow the bent beds, so a ledge may tilt back a little; the wall
# itself must never step down.
monotonic = monotonic and next >= here - 0.3
if next - here < 0.1:
if not on_ledge:
ledges += 1
var nearest_regular := roundf(here / 2.0) * 2.0
if absf(here - nearest_regular) > 0.2:
off_grid_ledges += 1
on_ledge = true
else:
on_ledge = false
_expect(monotonic, "irregular strata never cut a back-step deeper than 0.3 m")
_expect(ledges >= 4, "irregular strata still cut distinct ledges (%d)" % ledges)
_expect(off_grid_ledges >= 2, "irregular bed boundaries leave the regular 2 m grid (%d of %d)" % [off_grid_ledges, ledges])
var north := irregular.elevations_m[irregular.index_of(40, 10)]
var south := irregular.elevations_m[irregular.index_of(40, 70)]
var regular_north := regular.elevations_m[regular.index_of(40, 10)]
var regular_south := regular.elevations_m[regular.index_of(40, 70)]
_expect(absf((south - north) - (regular_south - regular_north)) > 0.3, "dip and warp move the ledges along the wall")
func _test_angular_boulders() -> void:
var round := SiteTerrainBaker.BoulderField.new(Vector2.ZERO, 0.0, 21, 1, 5.0, 5.0, 1.0, 0.0)
var angular := SiteTerrainBaker.BoulderField.new(Vector2.ZERO, 0.0, 21, 1, 5.0, 5.0, 1.0, 1.0)
_expect(round.boulders() == angular.boulders(), "angularity changes the shape, not the placement")
var round_ring := 0.0
var angular_ring := 0.0
var bounded := true
for step in 16:
var angle := TAU * float(step) / 16.0
round_ring += round.elevation_delta(1.5 * cos(angle), 1.5 * sin(angle))
var height := angular.elevation_delta(1.5 * cos(angle), 1.5 * sin(angle))
angular_ring += height
bounded = bounded and height <= 5.0 * (1.0 + SiteTerrainBaker.BoulderField.MAXIMUM_TILT) + 0.001
_expect(angular_ring > round_ring, "angular blocks have flatter tops than half-ellipsoids")
_expect(bounded, "angular block height stays within the tilt bound")
func _test_slope_roughness() -> void:
var recipe := _small_recipe()
recipe.primitives.append(SiteTerrainBaker.SlopedPlane.new(30.0, 0.0, 0.0, 0.0, 0.0))