Generate complete freshwater and elevated terrain features

This commit is contained in:
Alexander Sellite 2026-08-22 18:38:17 -04:00
parent 219c462181
commit f2b96f2c1f
7 changed files with 2105 additions and 141 deletions

View file

@ -25,6 +25,23 @@ const EXPECTED_IDS: Array[String] = [
"chunk_0018",
"chunk_0019",
"chunk_0020",
"chunk_0021",
"chunk_0022",
"chunk_0023",
"chunk_0024",
"chunk_0025",
"chunk_0026",
"chunk_0027",
"chunk_0028",
"chunk_0029",
"chunk_0030",
"chunk_0031",
"chunk_0032",
"chunk_9999",
"chunk_9998",
"chunk_9997",
"chunk_9996",
"chunk_9995",
"chunk_spawn",
]
const REQUIRED_IDS: Array[String] = [
@ -57,8 +74,25 @@ const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = {
&"chunk_0016": 4,
&"chunk_0017": 4,
&"chunk_0018": 4,
&"chunk_0019": 4,
&"chunk_0019": 1,
&"chunk_0020": 4,
&"chunk_0021": 4,
&"chunk_0022": 4,
&"chunk_0023": 4,
&"chunk_0024": 4,
&"chunk_0025": 4,
&"chunk_0026": 4,
&"chunk_0027": 4,
&"chunk_0028": 4,
&"chunk_0029": 4,
&"chunk_0030": 4,
&"chunk_0031": 4,
&"chunk_0032": 4,
&"chunk_9999": 4,
&"chunk_9998": 4,
&"chunk_9997": 4,
&"chunk_9996": 4,
&"chunk_9995": 4,
&"chunk_spawn": 1,
}
@ -104,15 +138,32 @@ func _validate_catalog() -> void:
var pond := CATALOG.definition_for_id(&"chunk_0004")
var cliff_sea_edge := CATALOG.definition_for_id(&"chunk_0014")
var cliff_sea_corner := CATALOG.definition_for_id(&"chunk_0015")
var cliff_sea_transition_right := CATALOG.definition_for_id(&"chunk_0017")
var cliff_sea_transition_left := CATALOG.definition_for_id(&"chunk_0018")
var cliff_beach_transition_right := CATALOG.definition_for_id(&"chunk_0019")
var cliff_beach_transition_left := CATALOG.definition_for_id(&"chunk_0020")
var cliff_sea_transition_right := CATALOG.definition_for_id(&"chunk_9999")
var cliff_sea_transition_left := CATALOG.definition_for_id(&"chunk_9998")
var cliff_beach_transition_right := CATALOG.definition_for_id(&"chunk_9997")
var cliff_beach_transition_left := CATALOG.definition_for_id(&"chunk_9996")
var stream_variant := CATALOG.definition_for_id(&"chunk_0016")
var lake_edge := CATALOG.definition_for_id(&"chunk_0017")
var lake_corner := CATALOG.definition_for_id(&"chunk_0018")
var lake_fill := CATALOG.definition_for_id(&"chunk_0019")
var lake_diagonal := CATALOG.definition_for_id(&"chunk_0020")
var lake_narrow := CATALOG.definition_for_id(&"chunk_0021")
var lake_regular_to_narrow := CATALOG.definition_for_id(&"chunk_0022")
var lake_narrow_to_regular := CATALOG.definition_for_id(&"chunk_0023")
var river_edge := CATALOG.definition_for_id(&"chunk_0024")
var river_edge_variant := CATALOG.definition_for_id(&"chunk_0025")
var river_source_right := CATALOG.definition_for_id(&"chunk_0029")
var river_source_left := CATALOG.definition_for_id(&"chunk_0030")
var river_outlet_right := CATALOG.definition_for_id(&"chunk_0031")
var river_outlet_left := CATALOG.definition_for_id(&"chunk_0032")
var river_cap := CATALOG.definition_for_id(&"chunk_9995")
var cliff_top := CATALOG.definition_for_id(&"chunk_0009")
var cliff_corner := CATALOG.definition_for_id(&"chunk_0010")
var cliff_edge := CATALOG.definition_for_id(&"chunk_0011")
var cliff_ramp := CATALOG.definition_for_id(&"chunk_0012")
var cliff_third_tier_edge := CATALOG.definition_for_id(&"chunk_0026")
var cliff_third_tier_corner := CATALOG.definition_for_id(&"chunk_0027")
var cliff_third_tier_top := CATALOG.definition_for_id(&"chunk_0028")
var spawn := CATALOG.definition_for_id(&"chunk_spawn")
_check(
(
@ -132,10 +183,27 @@ func _validate_catalog() -> void:
and cliff_beach_transition_right != null
and cliff_beach_transition_left != null
and stream_variant != null
and lake_edge != null
and lake_corner != null
and lake_fill != null
and lake_diagonal != null
and lake_narrow != null
and lake_regular_to_narrow != null
and lake_narrow_to_regular != null
and river_edge != null
and river_edge_variant != null
and river_source_right != null
and river_source_left != null
and river_outlet_right != null
and river_outlet_left != null
and river_cap != null
and cliff_top != null
and cliff_corner != null
and cliff_edge != null
and cliff_ramp != null
and cliff_third_tier_edge != null
and cliff_third_tier_corner != null
and cliff_third_tier_top != null
and spawn != null
),
"The authored biome-rule definitions must be available.",
@ -157,10 +225,27 @@ func _validate_catalog() -> void:
and cliff_beach_transition_right != null
and cliff_beach_transition_left != null
and stream_variant != null
and lake_edge != null
and lake_corner != null
and lake_fill != null
and lake_diagonal != null
and lake_narrow != null
and lake_regular_to_narrow != null
and lake_narrow_to_regular != null
and river_edge != null
and river_edge_variant != null
and river_source_right != null
and river_source_left != null
and river_outlet_right != null
and river_outlet_left != null
and river_cap != null
and cliff_top != null
and cliff_corner != null
and cliff_edge != null
and cliff_ramp != null
and cliff_third_tier_edge != null
and cliff_third_tier_corner != null
and cliff_third_tier_top != null
and spawn != null
):
_check(
@ -314,10 +399,18 @@ func _validate_catalog() -> void:
== (1 << int(TerrainChunkTopology.Edge.NORTH)),
"A pond must terminate either end of an inland freshwater chain.",
)
_check(
pond.maximum_placements == 2,
"Generated worlds must contain only one pond/stream chain.",
)
_check(
stream != null
and is_equal_approx(stream.selection_weight, 0.3)
and is_equal_approx(stream_variant.selection_weight, 0.3)
and stream.maximum_placements == 2
and stream_variant.maximum_placements == 1
and "pond" in stream.tags
and "river" not in stream.tags
and stream_variant.tags == stream.tags
and stream_variant.allowed_neighbor_tags
== stream.allowed_neighbor_tags
@ -331,10 +424,98 @@ func _validate_catalog() -> void:
stream.water_surface_offset.y,
),
(
"Both stream visuals must share one combined selection weight "
"Both pond-stream visuals must share one combined selection weight "
+ "and mirrored water footprints."
),
)
_check(
(
not lake_edge.participates_in_base_solver
and not lake_corner.participates_in_base_solver
and not lake_fill.participates_in_base_solver
and not lake_diagonal.participates_in_base_solver
and not lake_narrow.participates_in_base_solver
and not lake_regular_to_narrow.participates_in_base_solver
and not lake_narrow_to_regular.participates_in_base_solver
and lake_edge.water_inlet_edges == lake_edge.water_outlet_edges
and lake_edge.water_inlet_edges == 7
and lake_corner.water_inlet_edges
== lake_corner.water_outlet_edges
and lake_corner.water_inlet_edges == 6
and lake_fill.water_inlet_edges
== lake_fill.water_outlet_edges
and lake_fill.water_inlet_edges == 15
and lake_fill.water_surface_size == Vector2(10.0, 10.0)
and lake_diagonal.water_inlet_edges
== lake_diagonal.water_outlet_edges
and lake_diagonal.water_inlet_edges == 6
and lake_diagonal.maximum_placements == 4
and lake_diagonal.water_surface_polygon.size() == 6
and lake_narrow.water_inlet_edges == 14
and lake_narrow.water_outlet_edges == 14
and lake_narrow.water_surface_polygon.size() == 4
and lake_regular_to_narrow.water_inlet_edges == 14
and lake_regular_to_narrow.water_outlet_edges == 14
and lake_regular_to_narrow.water_surface_polygon.size() == 5
and lake_narrow_to_regular.water_inlet_edges == 14
and lake_narrow_to_regular.water_outlet_edges == 14
and lake_narrow_to_regular.water_surface_polygon.size() == 5
and "lake" in lake_edge.tags
and "lake" in lake_corner.tags
and "lake" in lake_fill.tags
and "lake" in lake_diagonal.tags
and "lake" in lake_narrow.tags
and "lake" in lake_regular_to_narrow.tags
and "lake" in lake_narrow_to_regular.tags
),
"The deterministic lake ring and bottom must remain connected freshwater chunks.",
)
_check(
(
not river_edge.participates_in_base_solver
and not river_edge_variant.participates_in_base_solver
and not river_source_right.participates_in_base_solver
and not river_source_left.participates_in_base_solver
and not river_outlet_right.participates_in_base_solver
and not river_outlet_left.participates_in_base_solver
and not river_cap.participates_in_base_solver
and river_edge.water_inlet_edges == 14
and river_edge.water_outlet_edges == 14
and river_edge_variant.water_inlet_edges == 14
and river_edge_variant.water_outlet_edges == 14
and river_source_right.water_inlet_edges == 6
and river_source_right.water_outlet_edges == 6
and river_source_left.water_inlet_edges == 3
and river_source_left.water_outlet_edges == 3
and river_outlet_right.water_inlet_edges == 14
and river_outlet_right.water_outlet_edges == 14
and river_outlet_left.water_inlet_edges == 11
and river_outlet_left.water_outlet_edges == 11
and river_cap.water_inlet_edges == 6
and river_cap.water_outlet_edges == 6
and river_edge.water_surface_polygon.size() == 13
and river_edge_variant.water_surface_polygon.size() == 13
and river_source_right.water_surface_polygon.size() == 13
and river_source_left.water_surface_polygon.size() == 13
and river_outlet_right.water_surface_polygon.size() == 19
and river_outlet_left.water_surface_polygon.size() == 19
and river_outlet_right.ocean_facing_edges == 2
and river_outlet_left.ocean_facing_edges == 2
and river_cap.water_surface_polygon.size() == 6
and river_cap.primary_mesh_name == &"chunk_0020"
and "river" in river_edge.tags
and "river" in river_edge_variant.tags
and "river_source_right" in river_source_right.tags
and "river_source_left" in river_source_left.tags
and "river_outlet_right" in river_outlet_right.tags
and "river_outlet_left" in river_outlet_left.tags
and "river" in river_cap.tags
),
(
"The river banks, cliff source, ocean outlet, and legacy caps "
+ "must remain connected freshwater chunks."
),
)
for cliff_definition: TerrainChunkDefinition in [
cliff_top,
cliff_corner,
@ -348,6 +529,19 @@ func _validate_catalog() -> void:
and cliff_definition.base_layer_mesh_name == &"chunk_0000",
"Every second-tier piece must overlay level-one grass inland.",
)
for cliff_definition: TerrainChunkDefinition in [
cliff_third_tier_top,
cliff_third_tier_corner,
cliff_third_tier_edge,
]:
_check(
cliff_definition.overlay_only
and cliff_definition.must_be_interior
and cliff_definition.base_layer_scene != null
and cliff_definition.base_layer_mesh_name == &"chunk_0000"
and "elevation_3" in cliff_definition.tags,
"Every third-tier piece must overlay level-one grass inland.",
)
for coastal_cliff: TerrainChunkDefinition in [
cliff_sea_edge,
cliff_sea_corner,
@ -449,6 +643,13 @@ func _validate_catalog() -> void:
and cliff_ramp.maximum_placements > 0,
"The second-tier ramp must remain available without being required.",
)
_check(
cliff_third_tier_top.minimum_required_neighbors == 4
and "elevation_3" in cliff_third_tier_top.required_neighbor_tags
and "ramp" not in cliff_third_tier_edge.tags
and "ramp" not in cliff_third_tier_corner.tags,
"The third-tier set must form a closed, intentionally unramped plateau.",
)
func _validate_profiles() -> void:
@ -1099,6 +1300,242 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
),
"No rotated diagonal edge may accept the opposite surface.",
)
var lake_diagonal_northwest := _find_variant(generator, &"chunk_0020", 0)
var lake_diagonal_northeast := _find_variant(generator, &"chunk_0020", 3)
var lake_narrow_top := _find_variant(generator, &"chunk_0021", 0)
var lake_narrow_left := _find_variant(generator, &"chunk_0021", 1)
var lake_regular_to_narrow := _find_variant(generator, &"chunk_0022", 0)
var lake_narrow_to_regular := _find_variant(generator, &"chunk_0023", 0)
var lake_regular_top := _find_variant(generator, &"chunk_0017", 3)
_check(
(
lake_diagonal_northwest != null
and lake_diagonal_northeast != null
and lake_narrow_top != null
and lake_narrow_left != null
and lake_regular_to_narrow != null
and lake_narrow_to_regular != null
and lake_regular_top != null
),
"Every authored diagonal lake perimeter variant must be available.",
)
if (
lake_diagonal_northwest != null
and lake_diagonal_northeast != null
and lake_narrow_top != null
and lake_narrow_left != null
and lake_regular_to_narrow != null
and lake_narrow_to_regular != null
and lake_regular_top != null
):
_check(
generator._edges_are_compatible(
lake_diagonal_northwest,
TerrainChunkTopology.Edge.EAST,
lake_narrow_top,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
lake_diagonal_northwest,
TerrainChunkTopology.Edge.SOUTH,
lake_narrow_left,
TerrainChunkTopology.Edge.NORTH,
),
"Both diagonal banks must join the exact narrow lake profile.",
)
_check(
generator._edges_are_compatible(
lake_narrow_top,
TerrainChunkTopology.Edge.EAST,
lake_narrow_to_regular,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
lake_narrow_to_regular,
TerrainChunkTopology.Edge.EAST,
lake_regular_top,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
lake_regular_top,
TerrainChunkTopology.Edge.EAST,
lake_regular_to_narrow,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
lake_regular_to_narrow,
TerrainChunkTopology.Edge.EAST,
lake_narrow_top,
TerrainChunkTopology.Edge.WEST,
),
"The narrow lake run must transition exactly into the regular edge.",
)
_check(
generator._edges_are_compatible(
lake_diagonal_northwest,
TerrainChunkTopology.Edge.EAST,
lake_narrow_to_regular,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
lake_narrow_to_regular,
TerrainChunkTopology.Edge.EAST,
lake_regular_to_narrow,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
lake_regular_to_narrow,
TerrainChunkTopology.Edge.EAST,
lake_diagonal_northeast,
TerrainChunkTopology.Edge.WEST,
),
"The five-chunk lake side must remain valid without a narrow run.",
)
var river_cap_northwest := _find_variant(generator, &"chunk_9995", 0)
var river_cap_northeast := _find_variant(generator, &"chunk_9995", 3)
var river_cap_southwest := _find_variant(generator, &"chunk_9995", 1)
var river_cap_southeast := _find_variant(generator, &"chunk_9995", 2)
var river_edge_top := _find_variant(generator, &"chunk_0024", 0)
var river_edge_variant_top := _find_variant(generator, &"chunk_0025", 0)
var river_edge_variant_bottom := _find_variant(generator, &"chunk_0025", 2)
var river_source_right := _find_variant(generator, &"chunk_0029", 0)
var river_source_left := _find_variant(generator, &"chunk_0030", 0)
var river_outlet_right := _find_variant(generator, &"chunk_0031", 0)
var river_outlet_left := _find_variant(generator, &"chunk_0032", 0)
var beach_east := _find_variant(generator, &"chunk_0002", 0)
_check(
(
river_cap_northwest != null
and river_cap_northeast != null
and river_cap_southwest != null
and river_cap_southeast != null
and river_edge_top != null
and river_edge_variant_top != null
and river_edge_variant_bottom != null
and river_source_right != null
and river_source_left != null
and river_outlet_right != null
and river_outlet_left != null
and beach_east != null
),
(
"Every authored river-bank, source, outlet, and legacy-cap variant "
+ "must be available."
),
)
if (
river_cap_northwest != null
and river_cap_northeast != null
and river_cap_southwest != null
and river_cap_southeast != null
and river_edge_top != null
and river_edge_variant_top != null
and river_edge_variant_bottom != null
and river_source_right != null
and river_source_left != null
and river_outlet_right != null
and river_outlet_left != null
and beach_east != null
):
_check(
generator._edges_are_compatible(
river_source_right,
TerrainChunkTopology.Edge.SOUTH,
river_source_left,
TerrainChunkTopology.Edge.NORTH,
)
and generator._edges_are_compatible(
river_source_right,
TerrainChunkTopology.Edge.EAST,
river_edge_top,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
river_source_left,
TerrainChunkTopology.Edge.EAST,
river_edge_variant_bottom,
TerrainChunkTopology.Edge.WEST,
),
"The paired cliff source must open directly into both river banks.",
)
_check(
generator._edges_are_compatible(
river_edge_top,
TerrainChunkTopology.Edge.EAST,
river_outlet_right,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
river_edge_variant_bottom,
TerrainChunkTopology.Edge.EAST,
river_outlet_left,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
river_outlet_right,
TerrainChunkTopology.Edge.SOUTH,
river_outlet_left,
TerrainChunkTopology.Edge.NORTH,
),
"Both river banks must terminate in the paired beach outlet.",
)
_check(
generator._edges_are_compatible(
beach_east,
TerrainChunkTopology.Edge.SOUTH,
river_outlet_right,
TerrainChunkTopology.Edge.NORTH,
)
and generator._edges_are_compatible(
river_outlet_left,
TerrainChunkTopology.Edge.SOUTH,
beach_east,
TerrainChunkTopology.Edge.NORTH,
),
"The ocean outlet must remain straddled by ordinary beach tiles.",
)
_check(
generator._edges_are_compatible(
river_cap_northwest,
TerrainChunkTopology.Edge.EAST,
river_edge_top,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
river_edge_top,
TerrainChunkTopology.Edge.EAST,
river_edge_variant_top,
TerrainChunkTopology.Edge.WEST,
)
and generator._edges_are_compatible(
river_edge_variant_top,
TerrainChunkTopology.Edge.EAST,
river_cap_northeast,
TerrainChunkTopology.Edge.WEST,
),
"Both river-bank visuals must chain exactly between composite caps.",
)
_check(
generator._edges_are_compatible(
river_cap_northwest,
TerrainChunkTopology.Edge.SOUTH,
river_cap_southwest,
TerrainChunkTopology.Edge.NORTH,
)
and generator._edges_are_compatible(
river_edge_top,
TerrainChunkTopology.Edge.SOUTH,
river_edge_variant_bottom,
TerrainChunkTopology.Edge.NORTH,
)
and generator._edges_are_compatible(
river_cap_northeast,
TerrainChunkTopology.Edge.SOUTH,
river_cap_southeast,
TerrainChunkTopology.Edge.NORTH,
),
"Opposing river banks and end caps must close the water channel.",
)
for stable_id: StringName in [
&"chunk_0005",
&"chunk_0006",
@ -1106,10 +1543,10 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
&"chunk_0008",
&"chunk_0014",
&"chunk_0015",
&"chunk_0017",
&"chunk_0018",
&"chunk_0019",
&"chunk_0020",
&"chunk_9999",
&"chunk_9998",
&"chunk_9997",
&"chunk_9996",
]:
for quarter_turns: int in 4:
var coast_variant := _find_variant(
@ -1161,8 +1598,8 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
func _validate_generation_summary(generator: TerrainChunkGenerator) -> void:
var summary := generator._build_summary()
_check(
int(summary.get("variant_count", 0)) == 85,
"The current catalog must expose all 85 authored rotations.",
int(summary.get("variant_count", 0)) == 150,
"The current catalog must expose all 150 authored rotations.",
)
_check(
int(summary.get("solver_variant_count", 0))