From 5d55afcc26123538b21697df0c40fd5843de951d Mon Sep 17 00:00:00 2001 From: Voyager Date: Thu, 20 Aug 2026 21:42:50 -0400 Subject: [PATCH] Expand procedural terrain generation --- tests/generated_world_runtime_validation.gd | 42 +- .../terrain_chunk_compatibility_report.gd | 1 + .../terrain_chunk_generator_test.tscn | 1 + tests/terrain_chunk_generator_validation.gd | 347 ++++++- tools/blender/export_terrain_chunks.py | 20 +- .../biomes/definitions/biome_coast.tres | 1 + .../biomes/definitions/biome_forest.tres | 2 +- .../biomes/definitions/biome_pine_forest.tres | 2 +- .../biomes/definitions/biome_plains.tres | 1 + world/generation/chunks/assets/chunk_0001.glb | Bin 7660 -> 7660 bytes world/generation/chunks/assets/chunk_0002.glb | Bin 14472 -> 14472 bytes world/generation/chunks/assets/chunk_0003.glb | Bin 24376 -> 24372 bytes world/generation/chunks/assets/chunk_0004.glb | Bin 27232 -> 27748 bytes world/generation/chunks/assets/chunk_0011.glb | Bin 19420 -> 19428 bytes world/generation/chunks/assets/chunk_0013.glb | Bin 0 -> 27908 bytes .../chunks/assets/chunk_0013.glb.import | 45 + .../chunks/assets/chunk_0013_grass_lite.png | Bin 0 -> 6317 bytes .../assets/chunk_0013_grass_lite.png.import | 45 + .../chunks/assets/chunk_0013_sand.png | Bin 0 -> 6289 bytes .../chunks/assets/chunk_0013_sand.png.import | 45 + world/generation/chunks/assets/chunk_0014.glb | Bin 0 -> 19420 bytes .../chunks/assets/chunk_0014.glb.import | 45 + .../chunks/assets/chunk_0014_cliff_wall.png | Bin 0 -> 9025 bytes .../assets/chunk_0014_cliff_wall.png.import | 45 + .../chunks/assets/chunk_0014_grass_lite.png | Bin 0 -> 6317 bytes .../assets/chunk_0014_grass_lite.png.import | 45 + world/generation/chunks/assets/chunk_0015.glb | Bin 0 -> 19908 bytes .../chunks/assets/chunk_0015.glb.import | 45 + .../chunks/assets/chunk_0015_cliff_wall.png | Bin 0 -> 9025 bytes .../assets/chunk_0015_cliff_wall.png.import | 45 + .../chunks/assets/chunk_0015_grass_lite.png | Bin 0 -> 6317 bytes .../assets/chunk_0015_grass_lite.png.import | 45 + world/generation/chunks/assets/chunk_0016.glb | Bin 0 -> 26224 bytes .../chunks/assets/chunk_0016.glb.import | 45 + .../chunks/assets/chunk_0016_dirt.png | Bin 0 -> 5589 bytes .../chunks/assets/chunk_0016_dirt.png.import | 45 + .../chunks/assets/chunk_0016_dirt_wall.png | Bin 0 -> 3734 bytes .../assets/chunk_0016_dirt_wall.png.import | 45 + .../chunks/assets/chunk_0016_grass_lite.png | Bin 0 -> 6317 bytes .../assets/chunk_0016_grass_lite.png.import | 45 + .../chunks/definitions/chunk_0003.tres | 2 +- .../chunks/definitions/chunk_0013.tres | 28 + .../chunks/definitions/chunk_0014.tres | 24 + .../chunks/definitions/chunk_0015.tres | 23 + .../chunks/definitions/chunk_0016.tres | 19 + .../chunks/terrain_chunk_catalog.tres | 8 +- world/generation/generated_world_region.tscn | 1 + world/generation/terrain_chunk_catalog.gd | 12 +- world/generation/terrain_chunk_definition.gd | 41 +- world/generation/terrain_chunk_generator.gd | 970 ++++++++++++++---- world/generation/terrain_chunk_variant.gd | 17 +- 51 files changed, 1924 insertions(+), 223 deletions(-) create mode 100644 world/generation/chunks/assets/chunk_0013.glb create mode 100644 world/generation/chunks/assets/chunk_0013.glb.import create mode 100644 world/generation/chunks/assets/chunk_0013_grass_lite.png create mode 100644 world/generation/chunks/assets/chunk_0013_grass_lite.png.import create mode 100644 world/generation/chunks/assets/chunk_0013_sand.png create mode 100644 world/generation/chunks/assets/chunk_0013_sand.png.import create mode 100644 world/generation/chunks/assets/chunk_0014.glb create mode 100644 world/generation/chunks/assets/chunk_0014.glb.import create mode 100644 world/generation/chunks/assets/chunk_0014_cliff_wall.png create mode 100644 world/generation/chunks/assets/chunk_0014_cliff_wall.png.import create mode 100644 world/generation/chunks/assets/chunk_0014_grass_lite.png create mode 100644 world/generation/chunks/assets/chunk_0014_grass_lite.png.import create mode 100644 world/generation/chunks/assets/chunk_0015.glb create mode 100644 world/generation/chunks/assets/chunk_0015.glb.import create mode 100644 world/generation/chunks/assets/chunk_0015_cliff_wall.png create mode 100644 world/generation/chunks/assets/chunk_0015_cliff_wall.png.import create mode 100644 world/generation/chunks/assets/chunk_0015_grass_lite.png create mode 100644 world/generation/chunks/assets/chunk_0015_grass_lite.png.import create mode 100644 world/generation/chunks/assets/chunk_0016.glb create mode 100644 world/generation/chunks/assets/chunk_0016.glb.import create mode 100644 world/generation/chunks/assets/chunk_0016_dirt.png create mode 100644 world/generation/chunks/assets/chunk_0016_dirt.png.import create mode 100644 world/generation/chunks/assets/chunk_0016_dirt_wall.png create mode 100644 world/generation/chunks/assets/chunk_0016_dirt_wall.png.import create mode 100644 world/generation/chunks/assets/chunk_0016_grass_lite.png create mode 100644 world/generation/chunks/assets/chunk_0016_grass_lite.png.import create mode 100644 world/generation/chunks/definitions/chunk_0013.tres create mode 100644 world/generation/chunks/definitions/chunk_0014.tres create mode 100644 world/generation/chunks/definitions/chunk_0015.tres create mode 100644 world/generation/chunks/definitions/chunk_0016.tres diff --git a/tests/generated_world_runtime_validation.gd b/tests/generated_world_runtime_validation.gd index 3a7a297..501deb9 100644 --- a/tests/generated_world_runtime_validation.gd +++ b/tests/generated_world_runtime_validation.gd @@ -94,11 +94,13 @@ func _validate_generated_region( assert(_placement_count(placements, "chunk_0001") >= 1) assert(_placement_count(placements, "chunk_0002") >= 1) assert(_placement_count(placements, "chunk_0003") >= 1) + assert(_placement_count(placements, "chunk_0016") >= 1) assert(_placement_count(placements, "chunk_0004") >= 1) assert(_placement_count(placements, "chunk_0005") >= 1) assert(_placement_count(placements, "chunk_0006") >= 1) assert(_placement_count(placements, "chunk_0007") >= 1) assert(_placement_count(placements, "chunk_0008") >= 1) + assert(_placement_count(placements, "chunk_0013") >= 1) assert(_placement_count(placements, "chunk_0009") == 1) assert(_placement_count(placements, "chunk_0010") == 4) assert( @@ -350,7 +352,9 @@ func _validate_biome_catalog( var coordinate: Vector2i = record.get("coordinate", Vector2i.ZERO) var biome_id := region.get_biome_at(coordinate) var tags: PackedStringArray = record.get("tags", PackedStringArray()) - if "grass" in tags or "sand" in tags: + if "mixed_surface" in tags: + assert(biome_id == &"") + elif "grass" in tags or "sand" in tags: assert(biome_id != &"") var biome := catalog.definition_for_id(biome_id) assert(biome != null and biome.supports_chunk_tags(tags)) @@ -465,11 +469,14 @@ func _validate_authored_chunk_surfaces( ) -> void: var generated := generator.get_generated_chunks_root() var found_beach := false + var found_stream := false + var found_alternate_stream := false var found_pond := false var found_grass_ocean_edge := false var found_grass_beach_transition := false var found_grass_ocean_corner := false var found_beach_ocean_corner := false + var found_grass_sand_diagonal := false for chunk_root: Node in generated.get_children(): if chunk_root.name.begins_with("chunk_0002r"): var beach := chunk_root.find_child( @@ -479,6 +486,27 @@ func _validate_authored_chunk_surfaces( var material := beach.get_active_material(0) assert(material != null and material.resource_name == "sand") found_beach = true + elif ( + chunk_root.name.begins_with("chunk_0003r") + or chunk_root.name.begins_with("chunk_0016r") + ): + var stream_id := ( + &"chunk_0016" + if chunk_root.name.begins_with("chunk_0016r") + else &"chunk_0003" + ) + var stream := chunk_root.find_child( + String(stream_id), true, false + ) as MeshInstance3D + assert(stream != null and stream.mesh != null) + var stream_materials := _material_names(stream) + assert("dirt" in stream_materials) + assert("grass_lite" in stream_materials) + assert("dirt_wall" in stream_materials) + if stream_id == &"chunk_0016": + found_alternate_stream = true + else: + found_stream = true elif chunk_root.name.begins_with("chunk_0004r"): var pond := chunk_root.find_child( "chunk_0004", true, false @@ -532,12 +560,24 @@ func _validate_authored_chunk_surfaces( var beach_corner_materials := _material_names(beach_ocean_corner) assert("sand" in beach_corner_materials) found_beach_ocean_corner = true + elif chunk_root.name.begins_with("chunk_0013r"): + var grass_sand_diagonal := chunk_root.find_child( + "chunk_0013", true, false + ) as MeshInstance3D + assert(grass_sand_diagonal != null and grass_sand_diagonal.mesh != null) + var diagonal_materials := _material_names(grass_sand_diagonal) + assert("grass_lite" in diagonal_materials) + assert("sand" in diagonal_materials) + found_grass_sand_diagonal = true assert(found_beach) + assert(found_stream) + assert(found_alternate_stream) assert(found_pond) assert(found_grass_ocean_edge) assert(found_grass_beach_transition) assert(found_grass_ocean_corner) assert(found_beach_ocean_corner) + assert(found_grass_sand_diagonal) func _material_names(mesh_instance: MeshInstance3D) -> PackedStringArray: diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd b/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd index 04976a9..94b557e 100644 --- a/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd +++ b/tests/manual/terrain_chunk_generator/terrain_chunk_compatibility_report.gd @@ -122,4 +122,5 @@ func _make_generator() -> TerrainChunkGenerator: "chunk_0008", ] ) + generator.grass_sand_smoothing_enabled = true return generator diff --git a/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn index 7e12544..34a74e2 100644 --- a/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn +++ b/tests/manual/terrain_chunk_generator/terrain_chunk_generator_test.tscn @@ -27,6 +27,7 @@ build_collision = true show_chunk_labels = false force_center_chunk_id = &"chunk_spawn" required_chunk_ids = PackedStringArray("chunk_spawn", "chunk_0001", "chunk_0002", "chunk_0003", "chunk_0004", "chunk_0005", "chunk_0006", "chunk_0007", "chunk_0008") +grass_sand_smoothing_enabled = true elevated_cliff_feature_enabled = true [node name="Player" parent="." instance=ExtResource("4_player")] diff --git a/tests/terrain_chunk_generator_validation.gd b/tests/terrain_chunk_generator_validation.gd index 467c154..b583a1f 100644 --- a/tests/terrain_chunk_generator_validation.gd +++ b/tests/terrain_chunk_generator_validation.gd @@ -17,6 +17,10 @@ const EXPECTED_IDS: Array[String] = [ "chunk_0010", "chunk_0011", "chunk_0012", + "chunk_0013", + "chunk_0014", + "chunk_0015", + "chunk_0016", "chunk_spawn", ] const REQUIRED_IDS: Array[String] = [ @@ -32,7 +36,7 @@ const REQUIRED_IDS: Array[String] = [ ] const TEST_SEED := 13001 const CONNECTOR_STRESS_SEED := 287 -const EXPECTED_SOLVER_VARIANT_COUNT := 31 +const EXPECTED_SOLVER_VARIANT_COUNT := 35 const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = { &"chunk_0000": 4, &"chunk_0001": 4, @@ -47,6 +51,10 @@ const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = { &"chunk_0010": 4, &"chunk_0011": 4, &"chunk_0012": 4, + &"chunk_0013": 4, + &"chunk_0014": 4, + &"chunk_0015": 4, + &"chunk_0016": 4, &"chunk_spawn": 1, } @@ -60,6 +68,7 @@ func _initialize() -> void: func _run() -> void: _validate_catalog() _validate_profiles() + _validate_second_tier_coastal_layout() _validate_generation() _finish() @@ -87,6 +96,10 @@ func _validate_catalog() -> void: var grass_beach_transition := CATALOG.definition_for_id(&"chunk_0006") var grass_ocean_corner := CATALOG.definition_for_id(&"chunk_0007") var beach_ocean_corner := CATALOG.definition_for_id(&"chunk_0008") + var grass_sand_diagonal := CATALOG.definition_for_id(&"chunk_0013") + var cliff_sea_edge := CATALOG.definition_for_id(&"chunk_0014") + var cliff_sea_corner := CATALOG.definition_for_id(&"chunk_0015") + var stream_variant := CATALOG.definition_for_id(&"chunk_0016") 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") @@ -101,6 +114,10 @@ func _validate_catalog() -> void: and grass_beach_transition != null and grass_ocean_corner != null and beach_ocean_corner != null + and grass_sand_diagonal != null + and cliff_sea_edge != null + and cliff_sea_corner != null + and stream_variant != null and cliff_top != null and cliff_corner != null and cliff_edge != null @@ -117,6 +134,10 @@ func _validate_catalog() -> void: and grass_beach_transition != null and grass_ocean_corner != null and beach_ocean_corner != null + and grass_sand_diagonal != null + and cliff_sea_edge != null + and cliff_sea_corner != null + and stream_variant != null and cliff_top != null and cliff_corner != null and cliff_edge != null @@ -235,6 +256,48 @@ func _validate_catalog() -> void: ), "The beach corner must join two straight beach ocean edges.", ) + _check( + grass_sand_diagonal.must_be_interior + and not grass_sand_diagonal.participates_in_base_solver + and "mixed_surface" in grass_sand_diagonal.tags, + ( + "The grass-sand diagonal must remain an inland post-process " + + "mixed-surface chunk." + ), + ) + _check( + grass_sand_diagonal.surface_tags_for_edge( + TerrainChunkTopology.Edge.NORTH + ) == PackedStringArray(["sand"]) + and grass_sand_diagonal.surface_tags_for_edge( + TerrainChunkTopology.Edge.WEST + ) == PackedStringArray(["sand"]) + and grass_sand_diagonal.surface_tags_for_edge( + TerrainChunkTopology.Edge.EAST + ) == PackedStringArray(["grass"]) + and grass_sand_diagonal.surface_tags_for_edge( + TerrainChunkTopology.Edge.SOUTH + ) == PackedStringArray(["grass"]), + "The diagonal's authored edges must retain two sand and two grass sides.", + ) + var stream := CATALOG.definition_for_id(&"chunk_0003") + _check( + stream != null + and is_equal_approx(stream.selection_weight, 0.3) + and is_equal_approx(stream_variant.selection_weight, 0.3) + and stream_variant.tags == stream.tags + and stream_variant.allowed_neighbor_tags + == stream.allowed_neighbor_tags + and stream_variant.water_inlet_edges == stream.water_inlet_edges + and stream_variant.water_outlet_edges == stream.water_outlet_edges + and stream_variant.water_surface_size == stream.water_surface_size + and stream_variant.water_surface_offset + == stream.water_surface_offset, + ( + "Both stream visuals must share one combined selection weight " + + "and identical terrain/water behavior." + ), + ) for cliff_definition: TerrainChunkDefinition in [ cliff_top, cliff_corner, @@ -248,6 +311,43 @@ func _validate_catalog() -> void: and cliff_definition.base_layer_mesh_name == &"chunk_0000", "Every second-tier piece must overlay level-one grass inland.", ) + for coastal_cliff: TerrainChunkDefinition in [ + cliff_sea_edge, + cliff_sea_corner, + ]: + _check( + coastal_cliff.overlay_only + and coastal_cliff.base_layer_scene == null + and not coastal_cliff.must_be_interior + and coastal_cliff.prefers_map_boundary + and "coast" in coastal_cliff.tags + and "elevation_2_sea_edge" in coastal_cliff.tags, + ( + "Each second-tier sea piece must be a complete coastal " + + "overlay without an inland grass base." + ), + ) + _check( + cliff_sea_edge.ocean_facing_edges + == (1 << int(TerrainChunkTopology.Edge.EAST)), + "The second-tier sea edge must face east before rotation.", + ) + _check( + cliff_sea_corner.ocean_facing_edges + == ( + (1 << int(TerrainChunkTopology.Edge.EAST)) + | (1 << int(TerrainChunkTopology.Edge.SOUTH)) + ), + "The second-tier sea corner must face east and south before rotation.", + ) + _check( + cliff_sea_corner.minimum_required_neighbors == 2 + and ( + "elevation_2_sea_edge" + in cliff_sea_corner.required_neighbor_tags + ), + "The second-tier sea corner must join two straight sea edges.", + ) _check( cliff_top.minimum_required_neighbors == 4 and "elevation_2" in cliff_top.required_neighbor_tags, @@ -293,6 +393,91 @@ func _validate_profiles() -> void: ) +func _validate_second_tier_coastal_layout() -> void: + var generator := TerrainChunkGenerator.new() + generator.catalog = CATALOG + generator.grid_size = Vector2i(4, 4) + generator.generate_on_ready = false + generator.build_collision = false + generator.force_center_chunk_id = &"" + root.add_child(generator) + var keys := PackedStringArray() + for row: int in generator.grid_size.y: + for column: int in generator.grid_size.x: + var at_north := row == 0 + var at_east := column == generator.grid_size.x - 1 + var at_south := row == generator.grid_size.y - 1 + var at_west := column == 0 + if at_north and at_west: + keys.append("chunk_0015@2") + elif at_north and at_east: + keys.append("chunk_0015@1") + elif at_south and at_east: + keys.append("chunk_0015@0") + elif at_south and at_west: + keys.append("chunk_0015@3") + elif at_north: + keys.append("chunk_0014@1") + elif at_east: + keys.append("chunk_0014@0") + elif at_south: + keys.append("chunk_0014@3") + elif at_west: + keys.append("chunk_0014@2") + else: + keys.append("chunk_0009@0") + _check( + generator.generate_from_placement_keys(keys), + ( + "The second-tier sea edge and corner must form a complete " + + "raised coastal perimeter." + ), + ) + if generator.placement_keys().size() == keys.size(): + _check( + _all_neighbor_edges_match(generator), + "Every raised coastal-perimeter seam must match.", + ) + _check( + _count_placements(generator.placement_keys(), "chunk_0014") == 8 + and _count_placements( + generator.placement_keys(), "chunk_0015" + ) == 4, + "The raised coastal perimeter must use its straight and corner pieces.", + ) + for record: Dictionary in generator.placement_records(): + var stable_id: StringName = record.get("stable_id", &"") + if stable_id not in [&"chunk_0014", &"chunk_0015"]: + continue + var coordinate: Vector2i = record.get( + "coordinate", Vector2i.ZERO + ) + var variant := _find_variant( + generator, + stable_id, + int(record.get("rotation_quarters", 0)), + ) + _check( + variant != null + and generator._variant_respects_ocean_boundary( + variant, coordinate + ), + "Every second-tier sea opening must face outside the terrain grid.", + ) + var generated := generator.get_generated_chunks_root() + if generated != null: + for child: Node in generated.get_children(): + var stable_id := StringName( + child.get_meta(&"terrain_chunk_id", &"") + ) + if stable_id in [&"chunk_0014", &"chunk_0015"]: + _check( + child.get_node_or_null("TerrainBaseLayer") == null, + "Second-tier sea pieces must not render inland grass over ocean.", + ) + generator.free() + + func _validate_generation() -> void: var first := _make_generator() root.add_child(first) @@ -308,6 +493,10 @@ func _validate_generation() -> void: _placements_contain(first_keys, stable_id), "Generated diagnostic must contain %s." % stable_id, ) + _check( + _placements_contain(first_keys, "chunk_0013"), + "Generated terrain must replace sharp grass-sand corners with diagonals.", + ) _check( _all_neighbor_edges_match(first), "Every generated neighboring edge must match.", @@ -416,8 +605,12 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: ) var stream := CATALOG.definition_for_id(&"chunk_0003") - _check(stream != null, "The stream definition must be available.") - if stream == null: + var alternate_stream := CATALOG.definition_for_id(&"chunk_0016") + _check( + stream != null and alternate_stream != null, + "Both stream visual definitions must be available.", + ) + if stream == null or alternate_stream == null: return for quarter_turns: int in 4: var stream_variant := _find_variant( @@ -429,7 +622,16 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: stream_variant != null, "The stream must support rotation %d." % quarter_turns, ) - if stream_variant == null: + var alternate_variant := _find_variant( + generator, + &"chunk_0016", + quarter_turns, + ) + _check( + alternate_variant != null, + "The alternate stream must support rotation %d." % quarter_turns, + ) + if stream_variant == null or alternate_variant == null: continue var expected_inlet := 1 << int(TerrainChunkTopology.rotated_edge( TerrainChunkTopology.Edge.NORTH, @@ -449,6 +651,25 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: == expected_outlet, "The rotated stream outlet must follow its authored orientation.", ) + _check( + alternate_variant.rotated_edge_mask( + alternate_stream.water_inlet_edges + ) == expected_inlet + and alternate_variant.rotated_edge_mask( + alternate_stream.water_outlet_edges + ) == expected_outlet, + "The alternate stream connectors must rotate exactly like the original.", + ) + for edge_value: int in TerrainChunkTopology.Edge.values(): + var edge := edge_value as TerrainChunkTopology.Edge + _check( + stream_variant.profile(edge).signature( + TerrainChunkGenerator.CONSTRAINT_PROFILE_QUANTIZATION + ) == alternate_variant.profile(edge).signature( + TerrainChunkGenerator.CONSTRAINT_PROFILE_QUANTIZATION + ), + "Both stream visuals must expose identical terrain edge profiles.", + ) var beach := CATALOG.definition_for_id(&"chunk_0002") _check(beach != null, "The beach definition must be available.") @@ -611,6 +832,62 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: ), "The beach corner's west side must join a rotated straight beach.", ) + var cliff_top_zero := _find_variant(generator, &"chunk_0009", 0) + var cliff_sea_edge_zero := _find_variant(generator, &"chunk_0014", 0) + var cliff_sea_edge_south := _find_variant(generator, &"chunk_0014", 3) + var cliff_sea_corner_zero := _find_variant(generator, &"chunk_0015", 0) + _check( + cliff_top_zero != null + and cliff_sea_edge_zero != null + and cliff_sea_edge_south != null + and cliff_sea_corner_zero != null, + "The second-tier sea edge variants must be available.", + ) + if ( + cliff_top_zero != null + and cliff_sea_edge_zero != null + and cliff_sea_edge_south != null + and cliff_sea_corner_zero != null + ): + _check( + cliff_sea_edge_zero.profile( + TerrainChunkTopology.Edge.EAST + ).points.is_empty(), + "The second-tier sea edge must remain open on its ocean side.", + ) + _check( + cliff_sea_corner_zero.profile( + TerrainChunkTopology.Edge.EAST + ).points.is_empty() + and cliff_sea_corner_zero.profile( + TerrainChunkTopology.Edge.SOUTH + ).points.is_empty(), + "The second-tier sea corner must retain both ocean openings.", + ) + _check( + generator._edges_are_compatible( + cliff_sea_edge_zero, + TerrainChunkTopology.Edge.WEST, + cliff_top_zero, + TerrainChunkTopology.Edge.EAST, + ), + "The second-tier sea edge must join the raised grass top inland.", + ) + _check( + generator._edges_are_compatible( + cliff_sea_corner_zero, + TerrainChunkTopology.Edge.NORTH, + cliff_sea_edge_zero, + TerrainChunkTopology.Edge.SOUTH, + ) + and generator._edges_are_compatible( + cliff_sea_corner_zero, + TerrainChunkTopology.Edge.WEST, + cliff_sea_edge_south, + TerrainChunkTopology.Edge.EAST, + ), + "The second-tier sea corner must join two straight sea edges.", + ) var flat_grass := _find_variant(generator, &"chunk_0000", 0) var flat_sand := _find_variant(generator, &"chunk_0001", 0) _check( @@ -649,11 +926,57 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: ), "No transition rotation may accept sand behind it.", ) + for quarter_turns: int in 4: + var diagonal := _find_variant( + generator, + &"chunk_0013", + quarter_turns, + ) + _check( + diagonal != null, + "The grass-sand diagonal must support rotation %d." + % quarter_turns, + ) + if diagonal == null: + continue + for source_edge_value: int in TerrainChunkTopology.Edge.values(): + var source_edge := source_edge_value as TerrainChunkTopology.Edge + var edge := TerrainChunkTopology.rotated_edge( + source_edge, + quarter_turns, + ) + var neighbor_edge := TerrainChunkTopology.opposite_edge(edge) + var expects_sand := source_edge in [ + TerrainChunkTopology.Edge.NORTH, + TerrainChunkTopology.Edge.WEST, + ] + var expected := flat_sand if expects_sand else flat_grass + var rejected := flat_grass if expects_sand else flat_sand + _check( + generator._edges_are_compatible( + diagonal, + edge, + expected, + neighbor_edge, + ), + "Each rotated diagonal edge must accept its authored surface.", + ) + _check( + not generator._edges_are_compatible( + diagonal, + edge, + rejected, + neighbor_edge, + ), + "No rotated diagonal edge may accept the opposite surface.", + ) for stable_id: StringName in [ &"chunk_0005", &"chunk_0006", &"chunk_0007", &"chunk_0008", + &"chunk_0014", + &"chunk_0015", ]: for quarter_turns: int in 4: var coast_variant := _find_variant( @@ -674,15 +997,12 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: ), "%s must never place its ocean edge inside the map." % stable_id, ) - if stable_id != &"chunk_0007" and stable_id != &"chunk_0008": + var coast_definition := CATALOG.definition_for_id(stable_id) + if coast_definition == null: continue var boundary_coordinate := Vector2i(3, 3) var ocean_edges := coast_variant.rotated_edge_mask( - ( - grass_ocean_corner.ocean_facing_edges - if stable_id == &"chunk_0007" - else beach_ocean_corner.ocean_facing_edges - ) + coast_definition.ocean_facing_edges ) for edge_value: int in TerrainChunkTopology.Edge.values(): if (ocean_edges & (1 << edge_value)) == 0: @@ -701,15 +1021,15 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void: coast_variant, boundary_coordinate, ), - "Each coast corner rotation must fit its matching map corner.", + "Each coast rotation must fit its matching open map boundary.", ) func _validate_generation_summary(generator: TerrainChunkGenerator) -> void: var summary := generator._build_summary() _check( - int(summary.get("variant_count", 0)) == 53, - "The current catalog must expose all 53 authored rotations.", + int(summary.get("variant_count", 0)) == 69, + "The current catalog must expose all 69 authored rotations.", ) _check( int(summary.get("solver_variant_count", 0)) @@ -762,6 +1082,7 @@ func _make_generator() -> TerrainChunkGenerator: generator.build_collision = false generator.force_center_chunk_id = &"chunk_spawn" generator.required_chunk_ids = PackedStringArray(REQUIRED_IDS) + generator.grass_sand_smoothing_enabled = true generator.maximum_backtracks = 100000 return generator diff --git a/tools/blender/export_terrain_chunks.py b/tools/blender/export_terrain_chunks.py index 654227e..02c7b95 100644 --- a/tools/blender/export_terrain_chunks.py +++ b/tools/blender/export_terrain_chunks.py @@ -282,15 +282,21 @@ def _discover_chunks(tolerance: float) -> list[ChunkSource]: # Coastline meshes may intentionally stop before the positive-X edge, # leaving the surrounding ocean visible. Positive X is the canonical # authored ocean direction; Godot supplies the quarter-turn variants. - # Elevated inland cliff overlays intentionally stop at their downhill - # edge. The runtime places them over the existing flat grass chunk, so - # these omissions are not coastline openings or holes in the final cell. - layered_inland_cliff = "cliff_2" in label - allow_open_east_edge = "ocean_edge" in label or layered_inland_cliff + # Elevated inland cliff overlays also stop at their downhill edge, + # while second-tier sea pieces carry that edge down to ocean depth. + layered_cliff = "cliff_2" in label + allow_open_east_edge = ( + "ocean_edge" in label + or "sea_edge" in label + or "sea_corner" in label + or layered_cliff + ) # Corner pieces additionally leave canonical negative Y open. Their - # two marked ocean edges rotate together at runtime. + # two marked outer edges rotate together at runtime. allow_open_south_edge = ( - "ocean_edge_corner" in label or layered_inland_cliff + "ocean_edge_corner" in label + or "sea_corner" in label + or (layered_cliff and "corner" in label) ) bound_problems = _validate_bounds( bounds, diff --git a/world/generation/biomes/definitions/biome_coast.tres b/world/generation/biomes/definitions/biome_coast.tres index d1864d3..635181b 100644 --- a/world/generation/biomes/definitions/biome_coast.tres +++ b/world/generation/biomes/definitions/biome_coast.tres @@ -17,4 +17,5 @@ script = ExtResource("1_definition") stable_id = &"biome_coast" label = "coast" required_chunk_tags = PackedStringArray("sand") +forbidden_chunk_tags = PackedStringArray("mixed_surface") prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_sand_tree")]) diff --git a/world/generation/biomes/definitions/biome_forest.tres b/world/generation/biomes/definitions/biome_forest.tres index a660042..21cd724 100644 --- a/world/generation/biomes/definitions/biome_forest.tres +++ b/world/generation/biomes/definitions/biome_forest.tres @@ -27,6 +27,6 @@ script = ExtResource("1_definition") stable_id = &"biome_forest" label = "forest" required_chunk_tags = PackedStringArray("grass") -forbidden_chunk_tags = PackedStringArray("spawn") +forbidden_chunk_tags = PackedStringArray("spawn", "mixed_surface") region_weight = 1.4 prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")]) diff --git a/world/generation/biomes/definitions/biome_pine_forest.tres b/world/generation/biomes/definitions/biome_pine_forest.tres index 6601a46..eec39a4 100644 --- a/world/generation/biomes/definitions/biome_pine_forest.tres +++ b/world/generation/biomes/definitions/biome_pine_forest.tres @@ -27,6 +27,6 @@ script = ExtResource("1_definition") stable_id = &"biome_pine_forest" label = "pine forest" required_chunk_tags = PackedStringArray("grass") -forbidden_chunk_tags = PackedStringArray("spawn") +forbidden_chunk_tags = PackedStringArray("spawn", "mixed_surface") region_weight = 1.2 prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")]) diff --git a/world/generation/biomes/definitions/biome_plains.tres b/world/generation/biomes/definitions/biome_plains.tres index c979cb8..46b8c71 100644 --- a/world/generation/biomes/definitions/biome_plains.tres +++ b/world/generation/biomes/definitions/biome_plains.tres @@ -24,5 +24,6 @@ script = ExtResource("1_definition") stable_id = &"biome_plains" label = "plains" required_chunk_tags = PackedStringArray("grass") +forbidden_chunk_tags = PackedStringArray("mixed_surface") region_weight = 1.0 prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")]) diff --git a/world/generation/chunks/assets/chunk_0001.glb b/world/generation/chunks/assets/chunk_0001.glb index 0b9bc50d7933ed8d575aa0fbf43e48fec7a82f72..6014230170f6b2214dc997668e1fe28c5572d647 100644 GIT binary patch delta 45 kcmaE3{l7#XNhf7M4W&Y6jC8kP*}oUYKl}Gf*uJW z{`-GNyP>e&&a7jP=Wmwt<@{xLm@bdr#?7jo=|XqegH6*+*@NK^t~aYf`SADR+xKhA zhm-ce-<6qWEVKc4H=q8Zngl?@^{V&%n)2b@^Ml`4ljovgw>__z!r+H&z^Bu%cW#8} zkE{DL5&!3LF^c#!R=31D^sq-7!58`PCYj+2BVWi3B43CW`2w^^V6;eJG=U*OVT`zp z3ophuOp_KcW>y$88ayETF@i#Z;fqoE-~sWY(ZXnS@PO=Fz%;U;#&u2){h<{JhA$Ef zEfNeZ5(q643@s82-EC7WNQOamkm?}SK>}{_IADY>jOO~>V4oW+jOO~>poQ~i7*k$E zb$zbRPPiY&!l|S34|62gcb>e772tF34|62h9(${D=kJUM8KG1JM((1 o!qDX(f&PCH5Kkl^S|lJ^Bp}*(Vha*th$Kam6cP@;-nNtDKdM`+!vFvP delta 1464 zcmZ9Lu}T9$6h-q5qD&!LD}F@gFSZat)FzD(uoR|Emk$suY-~lX%~XPQZ1NBG)4FHw zUUnzNaOS=@*_?fotd^_g+wW1@EkfQcx;QSfYa!2OvpGGHda1dk>mlE+&+D^Vx*qcL zW32swDQe?J$Y(DH{egXUQS~nVeLnR9A-8nBi#O|^D%dh)Wj=Q~d>$qjcmRbizHje0 ze1X`-yVt8P>SD;>r`y^WnOr+1CZ%bWhD?OI$&1fYU#Od&ydein-jHna24oXq^+klq zh(ZJ*g463#jcY9Pq=tg-tW-nlx|)%?f>H(P>k39{#H9+V&qV2qnrNwl)O*3SVdJr5 z=0j5=Mm7;6n~0H3#K zVyHtRGGqiHf)GLTdibG2CaGK+B1+v%7*a4XB%3^(Y$8ZD5mjGAl#D1u6mrGv5rfVB zQ^06SgvcgBWD_B>i4fUDOnuSUiWqgRgvt1yMgmiasq*-bAnz{(shJ3pO$5m%g5=7J V+PEAJh?sH!Q-~@2csM>h`3IK2!=L~F diff --git a/world/generation/chunks/assets/chunk_0003.glb b/world/generation/chunks/assets/chunk_0003.glb index 45708a3a7a3b6d24e43f0441a302dfd2bf5f548b..a1996ebe0680fa94b0fa01148df1318bfe54534a 100644 GIT binary patch delta 1755 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 C{SJ}< delta 40 ycmV+@0N4NImjT?D0S#wtR7L^-007)e003N(4d0RQbdm5jlb!)I0XMVL0TD_p5e}IE diff --git a/world/generation/chunks/assets/chunk_0013.glb b/world/generation/chunks/assets/chunk_0013.glb new file mode 100644 index 0000000000000000000000000000000000000000..b5aa63c7d5e5eb04cbf0d6e63b9b55369f025db9 GIT binary patch literal 27908 zcmeIZcRbeb_c;DEGfT)Sxv50UXYNcX@x_pU?aI{eJ%X{qee9k29X@;rK;!2$jOAvg~(!KwMW`MbIW<8(xfaN$zI;=&Sg zJc2yoZowg50sf#YE-cCuE65Y#0uFfOMSpW4Jo2_NJpOn;knxxj_5^wS16y$P4G44gbu;yHb#vvB4-F1; z15ayVJc8jgj=o-@ZjfN0bFhJ1DBjoC%f%!h%)`^)Ed=aic%1PeZt4NP0l{W&k)dJ1 zkX{Ur7aTAW5`bg){kGYMcOle5G&WW+hUb5d8G`qB{oh3s`|qQn^FlcYb^234V_&?# zo3N;u6bcpW<>wXZ75-ZT9~v6$G1IhC zH!?EOa1`Z{7l*)bVlNkDBJsDpgaSZE{0|N#1$l@LrT*2S^m>Of>m7>45^g$lt<-Dx z7lQW-^mT(aL#+}J7VLt$1{L|g+W-fUtRLRv_i3HO+}+)RExgo#23fG+uW59{h9gD&BJT_1v@|4@D8{Xf+Q z`t<*p-Cz5EuaE41XaBc8a-z^4|D`@~D%vH!+_q?qXc%aJAj z>wSqyi~rX%|ECPZWKqAYKeL#OG%DlzBmBpriOWL6|BC}TNm)ts3HJMQb*TaCjH%@>$*~0N=8N+KGAWwYF7+X(=mkKCW_hs!75T& zgMZ`OkYsHk9bDTw9-h%!Z;K^>jtY{(O8D#Zj|{|!Cq5%u1b`OuP2>aD@kOnF^$}0h zDN0|()g>;BK%9F?=pMUta zzdZ6ml%rh!@XCMrKouG%EwCr7gugs)ogCNpS6t>;mmf8?-F zEsNF_z19|@CaqD=b zpYdB~J&)odKnv{?Ym0cI9Qj5zXd#}cgLtA2%8RIjc%shSx;i5{Q6HTfrveUPA)aW9 zc%m(Coh_0R>x}q6ew5ew0sfC4;Q#npcfaT!h_*z3Cv|r^ ziMb)3m>Yp#=ZDx=+Ut6T;Ij=QEW{J@C&ob=h$rSx;Ncl9#1r#JJTZTQ{C8Y}ybY9L zAszu*8SC;#GGhLSC+1J!*ZCplpRq1~q?6Hx5fRq9oBl{ zpE~?ghkttDpC0(%^}vu9Yuk{QrOLkoYa7&48Ods%__wa@EfJm(zqYr8eh&SX5qYFT zguk|(+n*48IF;JBp#3#!sWN^23_*X5TB^8Y@e_O_isB-Q^bsZMthYft0<^!T`>#HT zwnSej52T0U6MYhGT?x5as;to^Wkk^)G50mPq)hZta6pAnGel7x)}EIL6vac&NEcBQ z4^fmWqR4O5K1PBaq9{*9ksYE)A5o-_C{Z8P-Z|?5q2_CJNf}Y3hbYpUzHa-wrnUq* zdj2c7R529Xtc3im(IsU>(SGN4Cb%PXJ|;qq)}Ad@s#Z!sPNYl9=o#r39KhO$6zwBG zGITyf(X+9}nl7S9jwq7f`5gmMBu5mT6H$U4A;%?U-f0JtI3rksYFIb${@?u1F8n@xkwUt=$3N`L#Pl6x|)7 z$SukDC!ABQ5-~397NH15JhniMfZXzx(7s2-4R8#MHHO}`5^WlvFA|y zwX^^2HS~<)BTDQ^#3MlQ5k>J4Mez|u@exJw5k>J4{d>o+3ANP4bTCrP7PG=A0aIdp zSODgMQ30mH_^}Yo6Qc%9jVWV6m^VfPm=ypB*qAs z5mUk9u?TE4;LVsYmW0J%Y=GIYy_g&3gs}o<#T2k$%m-ru%!0|o|KK0MaDZ``B<6zQ zF)qMdm;`2lnPJ-jZ^y*3{g@H94e&Nh3=713v8{l&VxpKGW`l76=D*9&m1aX2CyB|huR1L z7Qi?mwlP%92w)q;4uA^#0&IoYworK+01nI=vJe3*f^C3`2SRS%05nid3&`ITU?bGg z1_1Ks^MZ2HOFQ;3V4#*a+@J5PHTCup#VhfU{cwwt&-eKuyd6 zn?p54fFf)LJABZqCV)*~hXLwf2iOj(#s&4a1#An|lmLpb4eSU(2U-EPf*oo&yBlCP zI6n*2#1*hBREh@b>;l*YD!Uc##Tl?O+^8r}gz>N=54ui(oxoZKbR7XZg0(WCn#$nO z9W1G!>-hnkp*H)VHoO4-P&3p|OaNX`T@|SA4gf!>`d+vbMgUK^H#xW~HUJ;EPf56c zW&m&KFJb5x9Dpx$AF8+(Ksb0r_oN083fef>l>`U`k8I#!FF-JOWCjm%06~xq6GYz! z5C$1ALPQmS5b(Yk-g{wy0QgAKhA!WUX}}Hd1c<}PF?G0ka)2m|8&iTF;RcAs$S_6d z9WsDOYzrn0-M0lG2BW~Fperc=qA_-CH@qe600+SDPVlA;a0ondg9mkhWYA`ZT`7PB z@VEs$C;}vbB?){6>;{O3sALdD3E&`PMh=;10Hi=v3Wy>NkO+OpNqE=9pz~6p8#chF zP$~g$fL^kOcYy(VZ6kD^JM^?N^lTWWi5)gJ)S{>1pn-$a>u77308KzZMG3e=3Eu^T zB*a8Zjb!M7U_3mKd#DW7sl?5@$GmTk`_|gbp~j-@dbsMTbt0cT9se5G z8__aTrG?o~MeTa@>&I-o0G5zYFNWg{J(sHcZKAQu`tEE)(N`Xd${ki(qDNJ1=4#2= zI-ELiq-M;$)bGX|?qc==ffmKrf5sSD?u$A1Yu_MWT?z_k$&{FGf=nM~sk46ZU=1omfh@n`yM5JF5G#5BiSgKVf5qm*8OiH_oVQ3uu&*~Q()nWOu6AOd#-0J zot!t}RO*vH{o4wTjF%__3U=M;#oA77e65$( zFMm>E#IllZrGGacBT1u5yH_7P%zmJ0SRiqiC)wLcQDt7skquhX<*R%@zTOGhh|4@6 zdZd^l^QYcXp$GKjk~y;94`AJ=j|9Fk$Bvjh)yEGP%n4DN#$9LJNO9Nd!|en3z{SoM zeesm6zB@_Wt-%u+0`zLajlbequn3jsxa!OKEeaI#ZHyrtN*60Hn_N_R(8x)xn20x= z?;*J{PzS%lFz1P=K9M**)BN$vu02s_UGbdTI#cU29b}k6ATxoG*9W*x|}g?iqc>3ID?gB8-Li!l@K`I;F)}a)Id!q$!Ks;B*5aB zTwe~OfPIA%{YP0=x^kM?oVl<-yL&Bd_Poq1<9+(onIxNEH%~8-bgFnNs6FANB|n_B zxUcr%t(ch;NAegk*dWCn}n@FIpwHh57U^T5o(=(dOFd3UQ@h2)f(!v)6F*dtN-3LBcnAzT~BjZVLS?^2Dy*cV9>EY@~I|JC=9WBY-)Sl!Nv9 z&Xq7+TGkio9Wn_gJ6;6Ueck_B?$L4kT*KSyj=gP5jt?1c`XuQtZ8iMfG`DzfPB1|x zY(|Uv1Bd2dq?MNEE!!VTw50p_)NbU5eDf2`A9u6d??`sBE3$pC@5|>fi$@L7Y#oOL zE{0_?L_cj+_UH&H6zO_zvL%7?mF;Gh?SRrZoy^agRK{9CQ7l%pRvzqbM&gjsLJ!P9jbGm!tBRW z>6ne8ha8m`agw)EB<-IDg*JlA*E=69|}V%4YOsCv4~K2antT-W<%$~-N^{bJIF@5egMqK+jh-2{b2OBG5SUt@Q}rS^8g3MEy*1>2Jm|)_)7JpAICS*kAIk-t-tThgryyP zJ@}MGjKcrdZ5&_Na4_HV!(wMm%WS%4l}E0Ql`>eD=tsA=EeNXHYi9V%Q`P@?hh>h( zylE$WsCRDOgrWMK++|z7=liJqOC-;BsW_{P?scqw@C?&ciC_%9qO`gqJ@3_(eSs$Q z+@{s#!&%DS{gLEtHi|JD$zMJE_JC@0-t;dAn);x5Ogk*QBtnb1DaO6i#PXK+_N6}f z31*Fx5E|PxxNE!U$~>oMzu!G;x|7#p^DauSeoFKUnWYn--^nU;uPXi*xk2^Uz!1_h z)!Wh=+r0LDQZl`^?eBf^QS8oF&s~j#JxIZxbEz==kmTO*e`gT=+g|~6F9~Tx_ zeqxYye_Etfv&S@}i>JRiY243mBR}qIrfima@loq*=9}60D7zNdC-hz=izo5VJG4K@ zjYhl~iV~Sq`r+85lxi>0`cS^T@K^pvP0k7O-UsI>*=Q$1$JHaL&vdl49B#%g^S`4X zQGM|Jqaj|U{j7!aB^}0h7Bq|dcd~GiIQg-~jdMw61yROUe6YE^?`(5(*Id)xs03Gz z;hESGRqnU@YTGxJ>g3h6Q-TT3*qrUXPFsVe+7tHBQqh;r;le8X*y5$6fDzTgXR^^U zc03)DCyc8%-SW7aonm}gGNZ-kYW@bb$<--CjBdZW@3vUhPhRxzb>~mDo6s!2;PN0( z)_P(bja{b1X4FnT=M24RY#!cQ5c5TPr^4xild8t5Uv7u69$0-mcChrgm*-W=Dr&!J z+i$0yVWPdlt3MvS=5rW*%eC|Nt>@nRXxRGBOszByE(kJM{D|Xk`E@!^G3KM;=tuJH zj9%fHZzi&?&NRNKdL>o7S6=dDC;Ns}`QSxqeKpzVlSZu@A7M+yruO#&g|F+?kDhrR zzS!@>c5h-^w0-ZFcR1TpktwpsXimvDvV!l-6An^p7RWn%VtV<0r0;BW#RtPDpWaQ* zY9$s0lq#GS@rcctOT1)Xn*Vt-2d-4Z#(Z3&zaeQtHxPF{-3nBCi_h4E2!FH#K@`KeN$ zhR%>5+d-P}bfvYNyZ7p`xbu^_JCn2{NZX(ZS}A*U+0-;VIjZ)>UZOX2_v0#37$2XA zQBb~ot9&Hjq2>d%tvbaVY1xgRJ@&+$Jg$+q_xYl(s4(-PflC?2ZptdungU)`yYDWk z#vMECvd~~QVlDFxGvK;0+gK`c4bNy&q13b~e%+OpZqxL)$~r&K)NeRJsastTMIVTq?{S7Tfm$IuMWpZ zlgkRS9InyFyl@%*)E=FszxXh^O>aBM*dv}$x!^oC73Ws-I=bUp967>Dw`||@xK5U$ zGqAqa!-A@@=@Q9lrTAYjL-ijT^*$_?ilA5VeCz8NK*koR!jZsSB*WZHTC20~YdD?V zJ+0-&YhB59EJ^1+#)-wnn+FJQb)$Q=zxe1AD!g;)B-vQ4YusyvH14}rty3=A@!7W~ zixPin*V=3COpq#|JkzNrYwwkJg@*g^sS^3EU2_f^`QEuE0@FH8(uGOzg0aw8Fb^Ct1IO$ zUX$mh*1z8HF2rA@51##Oo+lc(di>TU^Ah9Omui|Z-+PP1*n+v9OL5=scrWe0z{jvx zxK#QiH`kl1UxP}I^){6Xx%hjK%&iEi*xhzskw2K<@chQ-QOt;g?LJ4Uni=Ko)$W8B zt=222`@<#~>@31@>HCYWR+5~h7WoxTgL&HFxLdqO%7b4rikozCU)`>EDtES{*X(JA zTLL!Nx}7}sEOoU-%=3pa*XOOz-`iwt_o831JNQLfkl70}+0~%k{X0bcoK~c_M5WeS zA9Z?6K4Y1P3!)CLIBL7)D=GGVL>GT|fm!n40(VBG^`vg0Sc9qt-_xNXA5WH%TRYqJ zv#jD-shZ5~dfj?%)LhF6Wx*PAWX|osAwp58KB!BN<0Emt<~z?LkmlCPUmbp(N5hmh zg32qv$4kAYgY6K_#;#2l$8&8*hfYel%ySeu=?)YnU7wkBT$z5iN%Cgwt=(HW)T}tk z_*?Kbyz9^PXM7 zcPg)tlrUXOndjecVD#wgPVOfO8CunDLiJB?su>MEXo-5iedFopBzGauFJG~#M+mYbA0s+mA||Z(_r>>5h>=r;czJ8 zO8|)zbGn|d{}N3#^>Ew$E1DT^n(klUcAn*G7IU?IwYq)Grlf5vh4U*f1TIWg%X5Y* zkvIM#`P?Aw^uGFaSEH!JP(iP>+3m*z84r#;PqkxGjMQxy-{2tibWw4in=@x;v6<8c z#YD;a(tF*TsLNw5215Jwm2nTk^doAeebcK|e3R1xtTZ1j{Je6(R7N3{S&bV{A&?k* z-?93`Yk{X`T8E1dC{i#C;7SEaij}Evx}WiyJ73K-ViUpJCdbZe_3H_@0tqQa(0ym# zCr*3I$!^QGTT_kbIBjwj$RYid@<>19a8KhMTG2=5zKPF+KE8XqmFJ#2PQg3veIgUv zkAsEAW6lpMewvfD=k|y5X3PZy@Opl>?>>8z)i9tXE#LzpCkbD_i7uN-HqS1S`5{R?ImrS6(zosV8@byN3rdZ;Su zO!AEGQ%+XVwv9*358UZJxPv(%Bsgz^T_f)1$*5}vmW!*)fzi(yh6e;$_Kc2Msb&=y zioP?7A~<@##tL&w|&f-Z-MD?BT& zXUf;Udb#1)>b2scr3R+f#qADK>XX#*rL-MG-@`;6=m~GYdUlKmXatKNTUhS1N}9Vu z`*zM@d~B)rf}27Nn^|Z~vKblXjZ;kf8222~<9>Cl zH-Ad;^nm{GPS2xCx#hiLIQAYmnb}C%Jkc|BXm*j)ukmKi z_JN@ScM1WLR>ztsWqcS@c5;FLw7#9&w$%hH`aN-HADKSA{HpZ9`;M+_s!VCDk&k#b zd|uAwd+F988C*B*Qr_aTrQM{dxo!{E^sE%G#a&{ziTq;XkxNDR0~uzz6PNks+Rp8! zF_UI$ompMx-8}fA+WflD7K5wi_lA2idWk^i{-$@*|=<*PZZ7Na{`?hMUUe*6BmHnutsE0U)=N+ZGg@mV={=7{~O zv-JG(!prKhH{{o%Ih@9)$bTHr8+d4AYbtalh{os4!QsKDah$WePF4&$x(~Z6QD>Mo z-#uoXiF>qRWqDk4#$Ag3wM6mP2dAAXNzA0qE-Gc6+?8>cy|q2;y2=BbZcjh?Gt5~0 z?P9PJpT^6u<=GTRo2uSZldW!4p;ctETvGU;Bi>t>icXZC>>QlUdu`ho$@=+GsWOB4 z!;fELE37xD$R3#8&Xlh{vb#CAWpFfL3zjxCP;=Jhg2RS{wxN&|JBpH;oRPYuOA;?O zz35hTp?+fP=UD&xh48sUc3W?}3bIafU-VM)LIDPjBj_i?|Ze%Vc#e|#v^yy%&D>23Mp5`}cuD`Dk32)p81;zoSW^_9zT)H0V*=fj!%OpAn^)O7 zJZ)lRRLU;yD0zQB1pcw@YTt&$VB@zjIdZ)%tmGOA=Z149(lO(uOYJ;-_HH<)Cwh~% zS)UKkhde91{_ym<$AJ+|Hbc=}yseFvo6o~$4 z7*`oFCxLz8n&BJR#PI&3h+FmO-Sb!Mhhofa?40>mKG%IdVIP^dETU6=F*Ytnxx>1L zMeRKPnRHYPiv#_9j>L?BnS{OPgLIowUhgk1j31tDe0?r(i|4lDbCa65BaJbnck;Z* zN^tijLu5bkJ=E!bdVjocoT9$w&>am-p#N)>scjQ$B-eNPSXPcLr@D+x{V=w8pAj3L z?+j1+eap{RP*+_4vMbkzi}S`dwf(O?;wv1~-WoJIUI-c?>=XSYBcplyPeLLE|xbY>%9`}Q-1jUWi#IKDF2>uTD~cJ&Q5DiN{b2J^q-sF z*itTR&VSGWe-FTPGz~SX)SQwK|F7R!ZzG5ZvLcng{2I5Wi);HU$Ep0~i+;-qfA3fm z6aMx=)JJln4zeT4i9V1ClKqh*UIm1(kgNy*E#f}n(RZwg&jeltN!R2=eIzIHMXiKw z0zy2|mcXm5?fr`m{9XHx4)A~ctmDylM2p}VEwoSM3FrMSM?Q#hLOw+O*1vMJPyD_q z@O{<&5bL-6Uu;1})FH%bLmtgundy@6F@Z=>)Fhi(3Ec7b*Y!o-sNv@jJ&TPa==< z{KK!)L3tA8sD^*|bv}vxi@`$s=$vQ~cohOm;8AR}kR0WN7UGHa1UbY&iy&_!umm1` zKN_u!b^V5Ph`oV$;{6i%b$*Do%~)3-q=Nu0M2Wd+15aFtC!U4CtE}xHo|r%4iTM-c zzvB|*u!olRx}1@mm^0#uITQGGw#1yZ*X4|Kh&dykxR5R}PXe#9wu5-$xd^=SI{EKd zNRI9pt^Ya;LClljbDb@*|B)QQn(g0yLo#BXh$rSiv_txcC+3WJVm%2wJQH<@xgj|* zAA+s&Umf&J)FVYPj0{y z?-WsDJjDOWZQVPLNYn z`r0agM-=%*6zwBQ+(-V<^V*rvyauX?EkPeqq>t!YOxxe*Uz>4Rn`L2b zLlpT(6zL<1^btk=5nVecnlnLn#q|4}=y`4S1=%4vvO{$3oJ@psuFb-*wjqk*tUa&I z#2`ELjO-9ac8IRk2hGE<~qEh!XA4of2(O477)GU88G0 z*XtrVx=%!jK8bT1YqK60;U3Wb+N=jgsN0%86Tv^q8$A;N<&I`|P#%b)Jl1A;U`7X? zQG7&Eu85-eh@$w2qPU2n`$ZJRMHJl!qNvV@qPU2nxQL>7XeJ27Tbm79B0Qsf&@+mQ zD2j(DiiaqQhbW4ND2j(DiiaqQhbW4ND2j(D;hdmDI44lTIe`++2b6FwpoDV)C7g$_ z59a|&I1gbT&I6Qi9-xHt07ZULF32yU=pNVTTCbxx=o!U96vaVw?Y%&~z6NW3jP}>g zhT@<-6bDgs4m7KT;vtISA&UGWiu@vq{H#5rnXd=R$Pc2(52DC!ZPtjDFmr_V|8)il z_20kG1bM;a0{krwKo#Z(958agZecUv z&G0?_XqZDlvn>oT;SmXw4x0dPf^YmE$I@Y%;W&UMb^^=0-%T$V5ebTpa5Vm%tGK{+QSn- z0cHb&VM-tjKoaH!oMB$U9Y7qrj9r51j>`ZBSPpg+rciPK^kEty1ZEYY0Q6w$AQ9sQ z%nLIKMc5gbdnf|Xh8YYOnAq?J(84OPQkW{K0I1{Lvu8^;4|h0gGV%Gd*t z!A%!H#ZCfjfLly~N*(~9h1-gN3i|_Wg4;Rt>K8+Yp9K(r>ifVO;R>(^W`TU+7MuV`uv2h97Jw~aKIsZ}73NQ_08qpH%3-*{ zcz}&Cs}ln^>j%Jy9f3Qy0c-3APrE_KSc4~5z^>q1 z53F4PyMRAQ*mnl(47y5SjR%Yef3jfh2-p$)nLuO*zz&d$21K(5Y!C4_!Wm-#$G{1N zpl;ECqoJB~a4%7Squ^F};eI0lN5ZYsLeE41j)0Eggx(7W91a~x4}BX3I1G9hO>Tt( z4h65eU>5>71UyN=8yW`ooS?4=5CMKs|EU8+LTuCr`vIcCFM0=L0K%ctHgFqE0P%3! zrf|a|07=jt`p`jJ0AislHK7yv0MLx0GITdHzyWw;k>=C|K`> z{Y=1_U?B~k+u0C<3Va#?oB+QYz>gZhap*o<=wfz&Q}AY(LD!1{oP{^W0Nyq>fIN7E z_rcrA4{!!Pb5-DzfEC~*OrXibCkGDTG_m`*lsCTxg3wyl3P;s+7 zCP3QhL5;AU-Z$l6tB$1fd193By(l%3t7a3@u+3x2gRjCSKgBc1bMwfm(om&QRnK*0 zrcr5->t5b7mVQ5;d3=yLA^R)?UWARDTm>T~bsyy?*RXX? z*TR=AV{*H+u+J=6szuXgW)~B(!>5wd5|W*sGp}Z@$hI%^E%;a*3vM@x3+&R{(Jo4t z#fnRsS|HUZd_j>$ep()9mXOYfJ74R4QMEYk+M!~DYxe1RZ#U1sa>)o{=@_21zWVWu zApvot!!u4;Xx<{1P#93apEOvU7LIbbT$4_9F~j(y{mfg={HU<#Q|y;k z$TdQrw;!Q;`Lp?UAp3E{I?HTniu?Kd45C-MdYuHweLme3-8FnP%{pXt&M4G`+9r!$ zgOrL7qb1YXOR8&qe@wjTXUq8OOJ8UK2;5PtH{q;XNS5LX)6!nYgcA2v^>)La4t$ewBT7E0QK?s)8wlbB~iZKGX6-_c`(%krw?xH8i% zD#?i?>73oC&l?)dIMe6tZ`9`b(mP;d{L%cdulk8&8KhQh&x6h7Zrp#jT~^teoN6ki zP-3WQ`li{we*PxE=y>wVYVrJTX&NdvpKzKOixbyOF~_5B_;1Jin8aG^_k`J=W(>b~ z%$GUtyYJ@Lq`P!uCdzH_!Iu|mQ+}Su28K-^XL~sQ?9gL*rhCOYlqURA`^sL8u45zt z%)t&M85F#S3?mYfl}pKM@~5-2-W=sysSEt1b9{rErEjk}PI%_c`!a^jPOmGao^(&E zDTGJ4<_0!xe@*@ULy|()P-Wke-Y%|TsnMEu;Y;5eJAafIZ+Jz!Sv8SVRd-opn8QO} zle~MHCF)Gh#*@xLZ_Qb8p84z#5sTwGVbHB-8CXCNfzVP#09-ej{sXmUCBkU~7Zot8R&W(x<)G`t|ojw3O?5 zr_i|hvR_XPU5Q`_WG^c>5!_RK*oy0soK`2>>v;Ci!Nh@&J7i@ylep07FJ1Q95c8ZR zkFV{Ekzf6_fv$U{^DXZSK3(JIDRQ~oF&wzBm~;oP7lno^iSkQ)!5513@M5y=C*%Xe z_8)Ju1i4(Xzj89IEwr$RYq?U?e0=!XvUr-G&%T6P;-_XpZ?L>hJpQa@I_FS|l1T)| z^?`ttgdI00B2#R0ufI7pV8a&h)-Y-!iqhnS*~O2(F6!h5dva?mcjg`SddSXEuNben zjqyF*OWUDSTB3H8A8s7{EM0ZffAX5T#@s}q^3-gM+nc1MlSbO(C)&n^Ie1Pn85zd9 z@1>=#bkaHe#r{ZOqup!i(U-UIlcleHtV+zTZ5h+N!KQz8{@S#IPf4W}$tW&)zGrV$ zWTRgX&%3na!MYe%Z&2e>O=;=ixeG%Dea~$_1flky=-S%%+?FVe~XH2gZ?q~8o^iDm<;F$Mk%X_sI?3P(d z&F3Sf>Ggh{iOD+GFma`_!6bYB!h&! z5Aqzl%e+q8k@>ez3f`7}Ctn(>>m&G_C+Fdou?!myantm2Z7ubS$5VtAj?7We8iyn+ zu54{tnM~v9e$sU46c6cJzvQlQQr(KmT~=r8ET4(g-j|<{e7;3ib?l;+rh=4P@~=w1 zYhQyoBE4&GomBJtG`h_EnntacHz`CvQ_7%)L84=CpO4HY(kkP<=2&Kk-=_d7G76Tt z^UudGoICo=Ti(L;UOkNq*`!++OM(NFP-BQ#>a&nQVS2it+Q})7%h?SSdLPDK79KS# z|AHBQ@Z+7CHstDJVT;VWtoNyjqp~1ou$sCjR;z+`St=>bNU^M0ChPdXVcINi9&^7~ zT$0LiYiO*f%u4fX!NFQ9m%3n@w)j&?ANsXsX>g9D0tFS(Pb+0{_zEVojoIboJ>FZ! zV}v~ACr5gbZ(%W|Z_9$)7TuMd#gQo@fTD!J=fR0mTI+!fZ? z6ZZ61B&*fD(rE4T&Xe2Hd{}FH;w7}+EjW{yjHRcVDxO*KB5|nl*iSO2o_xF7n#D57 z;kwVsy-U**x+#0EDV5~jvS9F-ij|_~Z??zLd|gNl-y!T<82hS2vd8Itf}v6CU@yesT4zF zm#$r+%IftqPfA@lylublk>pwS8$T2>-wNHJoYjpFs;4iQ5^S64)U{rWojuMTa6ZY= zcJL%Sh5ai|yS=o6;U2stCe*WUV=0c%P*I7DznBV39o)-!_uY)7Z_VDebQz@4Pvg?-zWw-J2I`Q(#>QW_K#MfBWLkcCQ^(W<6URR!_+>L}>_g zCP;;Qs4kSIOdb=iGjth@aJphJ&m5C)c|lIU^3B*jIsq25{hkpH8}F>_>3gte)r(@z zy5iiEx(tiUa;d3Z3_VTF`bu)e*>_LkPhYAl{QSY~E@`4hjEGO!Z2Fang_>}!^!sWQ z$>TJyKWHtcoHtqC+$x}6P`*@@DP_*Z%%~wKbi{ax?v;VyboZsYjg+O&qE_x`TK{me zwHEAF=(=s=gF9b0RL4itmeMYG@v`mmF}v|St|k;xomxG;(R{uYEx8V zr9|on#Sxt+k{yv-&kX8&EihK4zUZKRT%xc`g;QfT+8{pW>e05#{0&u87`a1ummVKm z&<84Nw(rb>2SskOyKHNiRdsyV_(ya;|K zt0uqgp}~{um(DcYl4T|Ne&PI`tC8lI=T$qM*9T26m^*)ZDMZ5%(|Ii0`v%>9ifVhjGHk*MPEGAAL{UPEE}R-11ZUhC*o~ZTE+-zf)_I@cG%2Jw8R*B%sicx?g9v zFw?=pEe{veTYK4~f8OF(?q1n#&hk2Y$H{N+Iq)(2D((gC#JypVmopqOO5AfwZW32O zziH6sAl=m?sT;D+&ojTixM`!y1Oo#J!#2;F+y^Umtxv44_0LEIVAo|QBu|ieHFT>| zcvjh+o4Z|J6eW50fOU+kVl-!0+53Ym9_qEf(qow$rD*stGD($VM^86dZ3x~&8IV}6 zb?YDrW-L|W(yCKi`0<4B;RL>mVs_0;4}+%ppEwFU{HRkD$18eC>jnSgxx*IHvs1pe zu6;Bbjr?3j&UW5~RFjWv*F)-~gWUbDW3{pE<=-MqbM{&dShos&U9RXK%e_$ajFh@- zS!R`2_^`%tX0a0`T-Qp^M)e3ilX88qyO(}nuuEX6S2TOq@WSNn9y#Swca|^dyF9h` z?l-KZ>vLjNT^aGen>5y_{jIxojJeM0)Kf*JQvWL{QQ>c`L(D?W>G-+i50&ZPI%{1+ zn{Tq``Q?XvZ^kE*3u{F+HuXB~w2Uwc`hL-SLy(VZYcX~u@9Ncwv`x0P?j^imp6|>e zC-o9r?GSYpOCtAU;X8%5FPq-9D}VHy5LuvG_lYQ9zmel2_g-p@|G53>sPMj&?vrCm zqn65d)IY&5`|l;=&QLtB*Qb_v>F@HGt1OJyr~TQETRYv{PMC<&b-Xxu_XL-k#wKM~ zX%!9g>)*akCwf2bzr0cYnOu9?KC)=8Spmu(gI4kzuV(k-wavfnFmH}COnY2(=j_P2 zR|Pfo6p>%mxz2VaGl+biiY3)(K2T(_@G_#x_`C)El_MV9^q;>l-Er2PsBj<4u(q$6 zlzDt}GHmJAxikk3cPg)Y&Wt>@FGZ8fF5a^>?b*TeVWs|wX7cUI$sPkKhR4a9eS$j= zl^(0EKJ&&tKS&{lrMQsO_>Hin?Bdz@`x|fEDQ!tOoA;yZU0Rh?%**DtvNwh8kH~r+ zrFtPbQOK-_X9?8#Ov9U{TZ;RhoNXrGm^zQ~&nUF|jBYno9-GojXCJUK)>R#B{GKZ4 zU!5aGSvEVmTu@v%-ruH_!y&9wsJ_Lrws>#{ZeN?-uusdmsVOd#j~a@ze2!`SBvMM7 z4qlXz**nqgL(TDgvy&X{(}(-BN7y!Be>{I^pijoSTH#VZh=* zF5=%T9vRVp8J+hN>{6M_WQ<(?UPR$3H-8~VSvtMMn1pN}=V`|J3db+1HNM`;U#qs) zyl3y&<4(C{`oJCMtolO@RRix@-imiNRUh}wv1={d8=dRX{g}K&e56#6-rLVYNVj!k zD^udPFAJ-2S(hReUs!$Etu-0i8bpzWy*j8|#$6+D`BdEMcRp=Rt}lt4<1gA2Cri?H zPA$)zqlg@P|9$-2tBY9N<_xKRJ+58ZN?nOxiaN9Jc?*P_33R`(6tX>}->Uv$RHuTo zYE}DOhspAumAKU=wyA!##FWKp#%8xA1#%^V@y7)QCoYIKkF09EtM==nO_L?PGtpn#IPvRgnBu1Q z&E{oY<24%t4#XJZsr%}iw92c0ITr6ZdE2Ee+&Ns7M{RJnSR;+dd}*^P#Tl zH_H#VwS){*Hg4`$ z4WT(xaqy{p(Zc9gx$?@8v7A!7&Dw)E>^~kb-57VFKje<7T*-(~n`uM!>}#F8^Vjxl znYp66ajbUN2i^1O9H)HL#i?tOKjkYv@)*|*3@+q8>)RhIk@5M{ z7jD^~c%-TkBTMprAuGqFqP6W7V|%fYgGYj3I;DvDk6%rUPRHg#e0u5Mesx|l?nyd( zc%#zk;2shKwR>it&fh&Nv{y7;(sNPb)4OYYm7UJ}KC?-kj%DF+ziE_px+AfRrMgF}dc$e;^JT9iqz=+`RQIw8E+kVwIQ5O|=1aPwn%ndi zUwVgkd-I$6kUA)SlNRkw(`@AIglhK0I&Mj>=wwanfS@Cm{6OjcT3@01t zt_ptUwu;Q_+_HPMkYTT%cEeyNLz?8F?k98+EeB^7U5wOpu}iPX+8IO2(pfa! z9yamNHN*!vjgoKMWmY8G;(qjgg@PZ)3m>_BJI|ca?_p18LgjJJQBhYC6@+cf1ow^K zpq%&ooP0LrX4a1G0unzwt%CfcbQo+drj+c%r|<aB3m&+sh-tN3H9voOKEgnNG17_&uAuNG7|rdZ55!SEVAYZHwttbK!{) zC)+K;OR*w-)_cw2{{y*t8j4TedWEM}PmVgxDPBJn_a^quZQ7d`f^#CHH{zS~>?Vmy~~0cGqu@2&W^@?IQaC-W$IJOXSdJkrm1Z%-rg1mg$=g)b5f_u2xQvMSLWGFRVqa9?5%fBe{tD| z&!#~vY(KZ&w$dQJfc<*ArevHwB{uI5y|-aY5e5CU2ZhPW;ni>Yx1Uwwr-W^b{PSo0 zvfXeN=}D8A&x1zE~YHeA!(46cB$VZur6mYIPe^)+4;m(XN9=e zeVq;SYJCHzgI3;+t6w`u)?8hk`(3gIH!->t8$Iz|DU*x7WTU35<~v`H_mx4PH!N>x zm8)!g&%^U=zOS(Efos7_F{jL)?G%MKL!WzfMZP6fj2B)f5LJasVq*F<#XNU3(2Zq+XerWz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2kHkM5+@%cHwl;k02lE|L_t(|+TC4ymZQ9ny^;+{RnIDB8?%ks|0;f6m4GeH zAGQfec-UZ)^l;91zV5C^0A1^OCDH%R^C0GxjrZKv=iJhw6+1SkaoC{+T1RWx1RFH=?sPk(j- zhie74TLyvME|r_gF+yM;PInD~-wU6>hQjG58a_7x12k=BzJ3|MU+W+s1xVIkI{Wa9 zQy(~hU<3$__ofIyN$*VO?m?+YI|>7`@NB|BZvheo_46{>41m)w;}mcHh>Iu)-x$7A z`;RCjA`mI`;XX+6K)_N#Luq;sw0v4NyaUw*^`D`oyH-KUu>JHs@cxHBLihwW3}YE= z=7b+!5L46)Q(qV0>xW?%PL?^=x7#fEr12&njWDbJr!+y=D*TKe!=L)_gJ3nDf*G(8 zV-^4jsOKMsbu_EmKl1sb*_%RuH@fJ9O=$wx=v}n1-v>D8QUz2MA^=2zd$T{7gCuk! z0yvzft0NDupP+7zIE0I~E^!g}7etoz3Bk z1SWu)httnY!8>0PrUq}GCB)7Ez?lx)C1?ZV=`cSgSKS9kHmdz7{Pe_g4j_$uFp8lC zjs8oHEy)c8G?O^_d;rXvig4mp0E$x7Okvud@GS;YezXz}1;tqC!F*wG_Qt+UGKG)N z#DXEea_5XI4MHxt|9=($cR<)6u&J27I|cK=5_1|pq^zk@q(I5J9SJ>wVNUbS0l3z+ z2mo&smoGunGn($b>hdKjd7NZn9UR60gpgzB9jR=JQOgkF(7f4GYy}L%0$jRmp35|d_8GMb320V%QAO}4!w#(C?&drO?c7( zln|rqMVU;qW1CRCc}(?Zn*gPdQ0N;UfNU8`UX z>Npn+stWIu@O5xp$G*T_CKUVS&rGn50TJN6?Dql2KxjKc(;W*0iGub5LXv+V%h-giYJ3d&yK@Pr+wsqGnjTp9BVt+Mbbogc#x#>VT9_NdGnXGi-$OrQAtMg$>^Xfy>V1^xnh{3uTv)mz-yB6m$X_7qle%bs&h3@-`zkfR@AC zakcbO;5pHfU6oG3LtoCo_g~5qx@&?;_Zv?6yzutN@GO8gb?!$A=o&@WcvwehyIcG| z3j$FnLb#Z7_-!nh~tl3|1B3LDe(1U z4#^v&vgfA&>HdAy3zJK(F$k)d03$*0?R8z{hYS=?df8t;^^-@YqR?uRL{1$o_8hzx z1hl~f#M<)d%J}{dXT4}hI4OJ&vO+Gw^hkHS=Cj&S~!8RL{N~OXlBD5#7n8y8h0I->U2+ho> z?FE7Lv`hF?6znY_+v!>;{NyWTrhSjN>|ViJ%k#Ya5^h>g_yTXr7H$#N8U?#FBe>RK zJm)Ba?Y??2tI;inK; zrA&Il!1XLcT}6y;ItaG>Uyk3VYAcm%*&J$nuW-^%#O2oaa;|6U5Vaq|F3 zb&#cZM-;q!X2w!iyJ{Zy&ozqcSs*hl;U(849x(g!9ca*P$5{-Pv3-}MHvR2^ZFBDo zGV>%CEyu2Z{{&62^l)v@I0qqPlQ`PbJ+dt98W?Q=|Qn0O~ zp+CSn+T?AkDx6cmNpLPPgA;_rWXT)t$} z4QZwJmSNb#Na#7Mj-nCe9x=LF8lody^S=XB*mcnO=s zz`lvS0I4bD4c$S>`RYz#K*F3>Vx~zFsyQ6v^00bp$ye&ruDk8uWltLR#(Mx4F9 z%kaF#d4OaZ+U|YbK_TNtv%(LjNtd<41`763AW&5p=1IV}S3=Vnbf+-fVspD$ub2ly>HX_Y01Qz>Y#eO7oe* zQVRClA7=!FN7GPx0I3E5?n0K>9dx=-Rd&vvd-66UJDQ%BK~O>R!E+aU{nFusr;=%C zuLiCOZR}h4nbiO=&My7@(cmav-s2x6!MoUMMhY0uo9p|z*YzwAR{VY`zWKw&FK2y|@PVdXrltrVUPJZ6BaJ<>lvl zJ3pzrs@-qOMae=NNue5ukh$?^DtnfaM3+3+_DV1=6o%HOj8J7iKnX=FX|5;5_d$M< ziDUJ*RNYxO_yA2GxbL~AFR@_~Qma0^4|9pq^o=;wrs48Hz6@qbHZn6!JF5r zIXn!K0st6^Y2HF4-;o}u{wW{{{fov^a15o zgj^Pao$7uuet%VOE*oVG-%+8C(+FW=r|ARXvt)kYJBT60SikFINPm#K>7arCNV8o> zB!D%Z2Ajr0I9+L%tqLKDnWZ`#%kW;SaRpa3!s0fs8O>$Qz$5(EF6RRGG0hJ&OmGX4 zm$iri5 z{`2(gxRb{i{%8;=-O|ygiT3(Ljb8*n#a9RKH2T{*P^?sSm}hH74VlIoc<=JFc+smq zy4EoETh^SYcV6R;AyVM9akjjzsdna0w5r0puZD9L06?uoYS&m@R1nyUXAaMzDtBzY0^a23p z-AZA_^%OLN^&yO`S?NljF}FcPDAd{6YiJhTyClL)k63>f2)tEX&SlNGvayqN767@E zH=R+qF9n}N*Gdf_C;?)zZ1W;3h-JSiuy(W}L{kfQ2x)I?X+yHZ7={WC%MYaGm0RA~ zmNjGZ8lYfG)}Vs4?&)9q?BowLx_!GR8yZ4b9Rz>(YG0=EO9n#B$$)DCAen)j1xf=$ z$kjRhe&zI!zt>rSL^o$M*BUclae1S-e9La`qOuk{1B+HB#|)&N{aQYNa@is&q31~9 zo!6KjkmcpKil0AHg}Azf_h^>^u&mcv!VsrwkpN*>5}5UcRJ=TbPqCs0)Avm2N%ZTzm0D^f`rMny3KEjt=?lsa00@dBbXw)+STVl z==QeJG@ZaTr&K8<+V<`zeEz-V%$L=zVa7lE0i_eMh4U#LwtZ^xGoEQaX5vpoZ_2sZs6koi{K(?Mq`?(z7mU zfUU!}-Ua~E6VPg)U~k;|MITPDT6NUoY+kMKyzP}BXZ;uruzDM5zt5H}VgGL~Ez348 z@ehp)gD>0eh-Ou-4*&qw)4c!M)pUe5lZQYV8ZXWI`SX{nj?+0r1AOFRWW03x2r1B9 z8Ev;7^^~Ng*@rcJwKj^*&Qmt?tz-6I6Mh=}lomTt3<;0+Ge{A$jl4Lxadv+3iiWkl z$sY{8@{S%qKWuifkufa)FaY@014y74G3d|(Kod2A$05u$KtN!0&9pbaXMJ6$sv7id zbULXH07MjQV=tj0xL$^L@^1lP6pKr2ZubhX3EIHOtwoIDyvVd1q86`@6tG>eLtT3h zf*kvw^ZVN?qv^0Z9dtefx&Hz2w>Dtru;9zNFf-Yn&NfuR($C89Bf3W ze>zD6@znNVvKSo{4X6UndDzC>o|kF18Y3@C$@614(PWT6wy;@-k!CA#sdF+r)vC4= zm`Jaww~_s^p`iD-6TkKzUNuAM34f;x!`bHfuqIG%IxruzKSY1#BaBgl&nDy}pONjpuzpz@=sGyXl;B7VCRM=o>9g6g>*ATGdhGAYWQH7c_G85^wNRu5qLz zqHn9nu5toT7lw7ZX$09)5@?S)&pq{_N|smYW2TS@d|@I=M8h1yH%?5=+86LCjUluW z4kmi|rc+Nq+l2bWs{|rGjG%evINE=T6`V%JPFCv1r{1GDyh}=|X$&0XB|;IAu+?M3 zl~&}QFZ;&!Rp&7Yhb`J-jyXj%Vg6adiBB25E#ckROxcFBriSgtvu+|3dFV@q0K*mvUxvY zbrX<;o^I?+TxA^nfLIMGew@)$)T!U6$>zh$u|X4v7kJSk2l4uqDBNYkG;+1eZ?tjon@SVm9;o_1v)G;smjJ8erw?1pItU z*2-F2Q!TA=3TvB#+Sk;69q%QUtJhy<+Gaipj8nbEky(u((Fgnbi<9m}i&b}7} zM1Y;zmFJ59Q1_<7A}C#DAQRI|>06KSd<}EepR~%Fozif3yt?qbT1EnCyG;=uZD9TC9)X3$}`(b?o7u8OOfbw~qNnz%xr9sKEYCm6`P{u>Uydm%z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2kHkM5jhL*>Re+002kFsL_t(|+U;HIva2|cm1G0S>Gzyxn&0z~9RjvAKa2?^ zzC$i&=A)~+s(WuXV9ByBOH24a|NUPA4VN(n=4l2*gs$&^hyW4cKiA)N>Fs|IIw2xB zK?(?n2(a+WjfeaSZsLRvSD-!PVK@%`2^sAV0#q5y;hiqZ>@fbMq~RwY0}P^hYRbKiAv zGZv<4+F6s!_3{x77agkabd{f+xL!u8MfYgUnR!cTs1 z%S$7|Fbt0uAcE`l0z{wN1&9QiIv`YlixMEBvudP4p?cF#XBmn5mSEDWRJHInB5v$W5E~$TI<6?ou&!8t}9Yd z1cat1;&Nst1?HW@rNGPv#@h%=DNtIip?XM$r+Z#8 zr_x)6s=tMG`bm`6_%oH=vUQf=*}04S|Eyp16b||b&F5yEZ=&`aEa9VBYhj+|q>s^L z0iwGM)4Pn2z(U!1U2CqlVOV(akDS@d_6w|oVYB=0qrTBg=$(i%f=3g}`aAv5M=h;@ zwEFBL!Jef6sr#;fuG!<3mxNcf1@NIu=q0^!#LTy4ez*M7yA-9`5tM@_-5n3KKGmm- zrNFARL>V=qBr}7yY@sw8XxV1ZILj80@I^Ut-s|fExD4%?JC=ouqYD4mTF-QMPOCtD z#r;b}z>1ApMHU7EiYn|Gz6J|}W?=w^U@>U7v$Q#wsLzIz>0$3Q>a#cCF$mAY4KUo9_>xG{)*}Uw!R7TJ# zylr}a1gx+!n9Mc`2pDU23VT+Ru~PtFj%8u^lFt({zqTrMTbdGf$B}Fle)((>-~uH| zd*`ZJVREVUvxWYm0M^=5(vW9g{=-Wa)k>{dbP9H+!uvc#%aH+Z6mnhxj0Gl!PIrKW zKc@t;nCy^`;*Kc$wQ%i8k_SRpY|d36F7D#ys1Olg;H)*%%=t>OlMa?*apP4N zAZVBwj4{yn{Xwrl7UlcwHO~eJ5p<{Dr(`X{r!wG5tWYK*K)z6bgkYJ$ShL9|JA@2I zkuT|=Z?$orX1I^{?;Rz~aKE=b-^??Z0@Tj2&`c_KzN$)Ug$iYC8MKFV`*#p<8K|mK zEGn>xSRc_jN5&Z7e;IVwf!69HI5-gjV+>69sc8p$*cO*4!17ORwz1aB$EWkY=AP}b zDI%a_sl(cEMxg>DCTRskWC1_iVV?2@i*}&{%+mzc+Fk0p7I9;(U9Fagz;vIWANs5? zRpr{%`Z9=ZYes;(GMVmlxEA1nlGVQKlZJ&pQg<4()^CM?Bf95!gTF{}1cS|$vR5RRj%msn8n!)uMH~|54BT7UnMW{O2I#nh@iwlGZt1Z@ zMDXXIKkM&%W#WQ@mQP6Kl)&XK8X}n!PfmSiJPQe|U`dJcU5aqIT;Mj|+`D1vr{HT? zrBtyM;YGL@Ot15?>|Sf7tXKiwAcXVHJg#`TZOtO&h>~(j03XhAx%aZCr`MgM*`CkX zTkV*JwQA-^5^fH7>P@WARcO*t6R(u|D|!-r)N*Fjka9MI3cx>(o=JJxwI2AdUDjHd z?o+X!Q4?y0U~;ZhK*+KDmNTfS2ex8Yie}np^{J!s#r*Hsm^|bV_S1*dmk472%9p z8ZuLhsz^seq>_a$JBsiH3s7@}J^LHJ`{{@x%+tJvnIZz?ZG_9^0!pi_C0LtWSJf}G z$U5gV*cCWI;iN}JJ2hp5|Ewsn^l}<_YO}kRLT&Bd`V`;_Le3_Fb4-mjO}-E_!#vMx zHxB}?P&`!tjdN}7ZwLbg7Vx6j<)fXB)oMCcWXHA&V*}}B%NBrM6D_%OF^AQ2a4roD zakJ^`8mW-C1xx7R+@FqAs`3C%=xrwM>`ouc78rP!sY~s=8myjs*RToAeMhSc`Za=x zPd%u;H;pzjS>tH~YQdEZ4JGW3_S$z<-&7=+H;+3LswsE1s)* z)T}rlVXG5V4v?w@&1x0pdo5@>>D?|2VcWj`)~f|5v98XSY)|ZcEn5(jT{8^}7dd~y zjv;)6ZJ*w7s{*J9-J5SyE@iz+Igl-Wz<1BwbekF5I$`;YXVgwDVX{*sDYhcpENZP1 z(KbX%o}n4x<%$@!x$8%4D{mBI?)fJ=TUbbmli&pi9oN1n=4GTvK@Ia8m4+`#rVMN0 z_U|nVmar8{u;w4m*vGBaGsxdpLh)9c1M$hK+|G{)dZm@AIRbKL^z%-%)&g4wYwfxS zIkb-mgN1y>E<`GI)c<%FSo1#Kv)IO#9EfWdv(0}qN96Tl0aD?Mhf_V9`)L7Qu4hpJ z@I!>buvr#Yf?l+1mOS_D=Sqbtu|B1hyH*fq>wPr|DZO1F3iS1db|WrqpMBrMG|gb< zx!$;n1(vNF{9b3zeTP}hCT4|Sa%^~?#~1^;>wto7b$CFC@l`oOQx{DH88#~kgb!;{ z#SNzjJ#>Bdv8D)*&TUnKhxLgZQEZBY?-{uKJB{NlxCZY^e{r@Ap6e{Y?#4=4_I>Y? z8E>M3Ut8Cf83kS69b7yMEOuumrBVs+<9!8+3J3@<<=g9Zhu1t3#pLcZY=V?=HD&43 zK%u#YiMDf_VI0S`Rn1>r*PWwS2ZU48)Y6^<;k-4p^MkIwA#?j%gaFSkM+DdF71SoW zQ2P7e&QgbWRb;TI3;)?_m+&|FqiY<;^DV$T7yEJd8 zS*xAXwTxD&IfsYK5Y4>3Og=fGkj0i^nx?g%@#tm60)a$(FKejpi>>GNvUW#{$U+5# zvgTlC|$?%b$j8`4r@qL;6{d4a4mhv+@s zNz*jLcpID7OlkEJu7}&h3(}=KB|sfS?|Mytd#{ebsVTRv>i`5^cFRjB7;)$b2^jel zEQ2q)8Dku`??GSWxzR+&JoRetUNCr$<#N_%ao(y-cTBYs_^2bY+j7-7LOLAEP1Cfd zo430-7XkUBqQtLwRRCzOTuI=AQ=?U4?c6O25PQavkZQOLQ3d!!2V&p%&~+Wm)4bL{ zLeCA&ek4Uj$ss!HOinF`FTlv!wB$m-Gi(07kmc(Jyem7mhfzrF70UHPzs?zY z{<|qZ%=;%*7T}O0miXoxVS7fa$8jVe;t)DHPTfy|Dw#RyAf62m*II)NuZo8UkD~Jw z;G_=@W9JYM0T3K$vX#mET))#sOEzfXG639i+vQiH3}#I12jO9j+(q!2XZO~Z-J^uyMqA$P&K7vze%&7>Xa^t zfGDIfT)h*@aqKD6Un|P$NuYivFs4V_DbnzXRyObJY`d}QPu_U(s00|IV**7e10~7{ zj@Kd3WZ|=Y!m9XpQ*$T^!lsN;gzGH)dC62`ZxV1E{Q~v6fu;gAF-#LdCe`= zHDt*r&J9L#V{oTwdKt^w_36RQIX|an7V?WFXh=L!PO}WpU>8CA-EPH#;kiio%)j9j zwOEu27;@ z4Xui2Ar0~J6U1Ul=c{Jnj9Fc>={M54>2>$Jorqo=+%SZwq{mxR%_Ab{ z`?k|Xgu$%o%}q5B(u5Ifa>j0Hnbb98hV;X_?ZxhNB*WF4l8AipJyewj#`t_d-|?_fr_Oz^wz@ud}mP6?qXS? zQ)V{M=`LEUw_*W-f#5kR5nz#GTACFNyx<^mSOlq@g4LE1!y_*qmw^2i=|Kj14bR161J;f z@A+jr}nSTBGC`d&g@_`-Y z+rT~PrHCZ!`tF?tc;V*x<(=Q80DcjSHPA~RC6GSLf%2fnkjH8(c2q0O+gB}HhD^Uh z=-x@I9j^UIox@W4D*Jx@ycp}yLV<<$>=GQ1)TjZ$UfSZShnISTVuO2PHdbsgCF zO$ggG%`i_BT&|a@qxIQ>t}B5AHWP*Em$QFa07P7A+OC%?yuZVH;SH)d@)7e$Uk|y? z5m_ju;Bvjd?RNW^-{H|xYWaW59M|b}%AB*YBZ3hzO)8z#sfR`t{OF zHNw2Ji$!#piL^!m`k{wE|NL3k9Sgq&s{y8}JPbS%S0=@OqSJIX%=;{>)M6PC5r)gK z;`+)QzH`>Bxg5-F7wmf3WlyC)I|B1QmzjLq-u3F!loxhZO1IgR{BrZV${7}5pAeBE z;h*VP;g{|W?L@VVY>7oDH4PsA7;XutTfhAfAjH$JyHEsQ9`Pt`{`M@rEt7^1L30Y5 ztAo@!p!MGLIjXF4;ghFa5lGj#UFv*O4t}H1bHr0QrQI#SQ@611;7OOFu__hrtw9dq zvnA+CqKg`tYeO;#vV`rJJ<(_7N^o+YDcn9_-p3kAQXD#pRJ0z_J_9Ij4WAWJfr_rV z00V+*+c-HNKt~+A62hTRUT1(Tiv}-M{QtZXV3F{1Yt9Yh^kVl=&4QeX4nCT4%>hk9 z$5id2#hfiXFpD&3_A~9|u_&y@Vn*>8HJW`E8fLmrkD9}Q%ND}7V=(V5cJ2cQT6Rq_ z9fg&Y(^C$pu5qr8EC<_V2-Q+~3$-w{Spo|JyfB(m&`QJ>DaA1QJSy)z@BLg~$g)|O zL7o}id0}*~poqz%N!!f!IF2v*^;pe)OHy5mkaaL`3(!LD#IH>hB580(YnM-GechU> zs(Zq|O&d{_e$}XOZT<*c`Et3eW7f8VCX>;}a{iIfTah-~QuHH(mtIK*2346u^DXcp zn-E*`6amy(S8#0Ho^a?KA}FO4sH=ke{SIa}FMNbCQ|yO9ymKLHN7&TQ07^S;-V&|3 zm*suz2bx2osNi=sgABv4&KNB9gbB%+_#e9544|^X4Oa5b+9zpli!y=lyoSqg?riq2 zDS-ikcuLr%mF-XUH6#_lT3d}pYma~MH0OApXAqXR46iKlAy{~q#uX?1pAbB4NVnI< z5QsE}%+m~azJu=eV^2b$kawKHv1dG+#&xihFwC<7VXnf_z2zH3er~4+TkaI?_yKkb z&<{P_{=K;Y1u(DSS_~fU|0q6For(Zgk}vx?xX()9r($mN3HSSbmr1PS#X+ETx8mxb z|0b*(7ek5F7A4h;qe;VT=T`+FBG8>)K?z;oB@;GbYIkSe6#z&jwY%|24a7|%XMZm# zg6p3v%+tKi^eJY7?*g zO!!Yrt15}6{Ep9cC3k)I-~+-EbEu0JMPNci zqysn`u-2YdC9FLQTfS54y`PkGU|SU~_kAzVSv*zC78oDRx1pB1-(d+ugn61BGPdh} z;(g-53%a9nlX!X#VZE1Kp3grIWHK&08d!K=FSnUt&7s|t3QMfD&~;sM z-cAWUTMYLnP&W&)3Uw>Tt;wozmB(2K?#9e(n!M{AV`^$vgDy=QZXF4SH!F=>^Q@Oz z>#RF^B+j;Ci`%JdC|b8Mbx|Qp9H((8Z&6Dn>o;O(GZ9gA#|IUl1r6+H1yYvU{rF75 z)vRIbE?b~(nC@+mWgu3Ig6RNP!85Aoe!l}CL=#t~RI;$f@)F2VsH!MRmn<$MP_>@q z=}}#KbjF@p9%W%rpqesY>!OKqP8-n5YAXJiIPktA0<$%sHr)Kri5NdwER|&Q8%u=4 z>GYh|iB3Loq)y!!%8gh^EkIaJ7^6a&Vmj zRHA|XVmp4|&&Id&LeJKkar@-uL$RF$y|A#A`82X1=ic{ydgu8*jUNrA4n<@|5Djm;hfpSpC|Y0G%y0${ACJ*QO>a+5Rvzg`~o zJX{9oYjNrgT00000NkvXX Hu0mjfqaXRk literal 0 HcmV?d00001 diff --git a/world/generation/chunks/assets/chunk_0013_sand.png.import b/world/generation/chunks/assets/chunk_0013_sand.png.import new file mode 100644 index 0000000..cd4178a --- /dev/null +++ b/world/generation/chunks/assets/chunk_0013_sand.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://btjhnb6owbjj2" +path.s3tc="res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.s3tc.ctex" +path.etc2="res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "adfa66b8fe9da7f3450d3ddfd432b86a" +} + +[deps] + +source_file="res://world/generation/chunks/assets/chunk_0013_sand.png" +dest_files=["res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.s3tc.ctex", "res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.etc2.ctex"] + +[params] + +compress/mode=2 +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=0 diff --git a/world/generation/chunks/assets/chunk_0014.glb b/world/generation/chunks/assets/chunk_0014.glb new file mode 100644 index 0000000000000000000000000000000000000000..13eef5ac79af4a89176a825ecaae0e6c072a354e GIT binary patch literal 19420 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 literal 0 HcmV?d00001 diff --git a/world/generation/chunks/assets/chunk_0014.glb.import b/world/generation/chunks/assets/chunk_0014.glb.import new file mode 100644 index 0000000..3d4bcb8 --- /dev/null +++ b/world/generation/chunks/assets/chunk_0014.glb.import @@ -0,0 +1,45 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://50aolv5pg8d2" +path="res://.godot/imported/chunk_0014.glb-636ccc776ff9575e58a9db68c3f273af.scn" + +[deps] + +source_file="res://world/generation/chunks/assets/chunk_0014.glb" +dest_files=["res://.godot/imported/chunk_0014.glb-636ccc776ff9575e58a9db68c3f273af.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +mesh_library/use_node_names_as_mesh_names=false +array_mesh/deduplicate_surfaces=true +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +gltf/naming_version=2 +gltf/embedded_image_handling=1 +gltf/texture_map_mode=1 diff --git a/world/generation/chunks/assets/chunk_0014_cliff_wall.png b/world/generation/chunks/assets/chunk_0014_cliff_wall.png new file mode 100644 index 0000000000000000000000000000000000000000..3fe157f58237f17fdca91532dd58dd47fb9d8335 GIT binary patch literal 9025 zcmV-HBfi{;P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2kHkM4<|PQe33x_03ZNKL_t(|+ULS<)=7W_L$=0{1&=h-)2(iIEm>PuCX-9V zTYU&dFi1u)BACpjTJ->;Dw)h+#QSaEZ++yazkJ~EZvCTO^xYP`V}N(-9Rs{u?-=0S zddC3oR`b>IZ+t%(pv8MQ?0kTq9DISF9DLE}A_U}WAA<0e|8~6JE4Pyx;a zq>=dP{XfKCeDhzH6MgshaXfgA`#Zmfz-s{jJ_PX9sq`~n{P0hqjRI!^0Ptu3=RcmCq4FCzzy9)@ z|AyVpd-&+iXL$VbD|B01_?!Rt-?7tv507sCKCHDMgupom#u$t-2t5x{Nf-wRXE9bI z{OZY{73cwq4Syf~_*w0Hy}9ubLT>|{3Oqmi2Gju$c0U1Q0v;9F=f-N3{lDnDOBvwb z{M$cpceWpa5CSQai#X>%NTRkpuZ7se@R<+i9RBj#{|W&3{LarIm4KJTsW-5Fv_Im&(p0Am!GBcP1KnnXi!Ot?{TVoO~WDFl4{0x8h^v?_b-WUxK0;eVJ?Y)mq(1kK8`~1W0Pq7p3;_qMn zZ}jEMr3|p$-dm0*=Crjo8vXuhH4bqOG^gr!dv~$Xx&cBc{`Sc+KE3yI0KkjWeH@RT z;-g!i0{{+szlUCH8=t@TuK)ml^$-6U0pG;Ky-(2&x|8RhUe`-8hnu}oFGr7?-P`!; z_*eM!@C$^ZmA!v=`+fZS=&$i6dV)_6zktsJa3O-F4`z%?zR#NMy~sv0T4KL@IHO*l z_n&0%*@;9Lt7jw{0NjMntTi~?eGgh`JU#sqEz!e%?;iSR#T$QP>o%m4cz$vp+Dhz% zyKv6p!`&YN0Dkl8Zx$F~(?7ruZhclsNTW5F@EU8DR05?O!Sh;h&cd7Q?LqJ3na_T3 z^T$*Be&Zu(oe0)2>f@i@{y7BqXM}K_X6}=lKgP5E4R9tv2?hBN|Nh@O&b>_ir&?f! zyD10f)tmC9{}hMagVNuJ-7NqBV-&{N83K1VdWpI`tCqHnjV?x`AP8`kBtsnuYV zpNp`miCNd;)@n#Oz3F-4ClaawR5QRb;sbz+Mi4;@#wzSGSFx8z-AU!o_QY%z(H? ziwR(Ym9A6ZCcuGbW&^ zgwNZEV*Af0ud4@5M(f0XrW~8wH?X&Vn1R4|tHL9QM;X02p)6>h|AC59u!htK!N?uD3!1JMHJPU^X7RGV%9u?fLP4s!xTt)oLwS)E@wTr}Ki5Vn> zf;#}Z28dY&=rCvyH~{+69+-R&eEQT~O}rBUgY#nz;&99Jbm>9z1>4lSQN>5 zs`;-ZJ24o%S$PB2t|mn;8fXpTI6j}&qYRvL#A*c01!y>@fR!`kNQI;*Je_C+326nK zjzD+-YfZJAH;LSyzIp;e2)a851p;E5F+eNu5Dy1)>M0G&EJ_;n0w{C&L@siBnh@_i zlJ9#{X7bxxh{m%a^jI47b`Jm$ME#?Tz{a9qIC>m#mr4|D)#tp)S=t-^1uh1f-SKm@Io89=Gk)qkG&6?Y8)2)ao)T#Zj)RCHki zQREc`L5Q)8>N5bP6iOxFB%g<`^1+2G-q7=^*_0_;t@im%0H1D)pD z$QLyyT_tFi5+5i`fQ|aEOAHaf>0H^;xMFlb#nfJvceGI7cO-NL6*eDt;eawO)q(70QF+|v# zi7l)GGOyAU>F9K(=H$A9n)83F(zb@u(Cbb|oceCa$N z4^9zwdRdt;@jKuY5W>&Ghcg$*#jew)$g5h;T9CJ_5;({N6~#~Il}M?EsO}-P|<9&M4Em^t|^u= zFEIhGptD3PnUxNw8&5vOYlvaxuiOdPX%y6idix z&|qKUnHIIGTtMERPoCA`>})pd?1vq+I-3Q=U)(9)>;a<--^8?lfdlaU<*p|ov{^gY zdVB$t=Fq4>*a>SfK>zG?f%tV+TIw~`c4WbgU*?{YTx&cb6g&+_x*81}ga<2CrDRsX znnu2J8Z9hZ!_fep_Qtic@1y}3p^rE)$4!zox13Y9`1=#F!eAbROaZz;IYD z8Lhy>@N_zeDeUwhRe6}z7_;(tx^k5`Lrs8Cz*!)lsUsYe(%OwE^HocX5*X{CZIedU ziPy!J2f@;?bpPxW-Hl$U0Of~cWGu_b#kW-jL3v<;t&sFci#rzHpX4^qXV4Ac3@jMz zTy-;LtbQkw*PhdY^$9<9qtjtp^`f9Du98a5gtn*G%=HFlH%17Ub1;NlShGMBm1_{= zY^?^>RlSB;4hd&ACK-BBOi&5~n$rMz?IFS*n5alh!9vEP`b1w!*lwd|?*ty?cu@;< zHg_OL$@?@Nkzzrzf3L$bQY~#PWsI#20Gi3@wfqQ~^rcC;)RhrG57L?tzZw8O#p$c3 z6F+p>E5)RX_vd>Ij0cM*V4m;QHE6b6){+(kVd(_cy!t|Eq!>?F19d@CuCSfewIWuoNmJBBS;s81z16xyh%v)IxW4V zx3OwuTizc(Po00+HH{pV#8pNhJOKCFNz32F#isH2>{3AB+KE6syj#tdjG`Gc5s%Nn zqhWs`u&~sQoHHZ12trKb-&?S?4w&GsUR$_S65436n#C>omahC-x8&EQhqY!hv1AHV zIpH4SII7GvU5GnnhHE3v)h<&>c&)GA)Ctdrk}>i^Pn}LGe4QL6sn066gmF;TO#0@l zSh8)SeyN+s_~kOBY}gi__f!(kW%?>)WKO|((u7y08CFDo+4ZZntxm88nHy9EoKFbAupO)haXvc9*?^?oAl#XLf!S z;-`RcQ7T-e6t5hVsd-4HgQzvpYt=Ix-i{K7yrcLG}kF%=&?dcUv=LvF3d10Hfa2AG;W(6XtdsGngDN3Q{qmPDg zQ0N3-=y82c&QU&m?h{|?+SQx#v96%cs-6X-Q>dEE2VeroodcLL0i~5S zY}UvHz((M~X$e}xmQt9+)tl^Hp7>?Kp42t8gkzOyx0U7XM$a;_y`JIeI>^8J8ab3s z<|MQbuUvj%lnov!Fr)I30no`arNs5`!RsHS*ktK^6s* z7$xUogf-NE(o*k%)JQNf_V)JJX9}Z2ODZLyw7anH&L1t?n0m4-c|cihfJ#dU?N#*V z8*A{7VHsmDw2Vxf`dA>Zt)v{4YXIw9t%D>o3{j+};&FxfU>Ok17hhg4>gh|@7^|9u zRdNh`6}MDyK;`zuUI+JWu6t%(mAmUqd|lRxr73K*Ng2=N7-fM0Oq~-%bRO}W8Moq6 zgGnTwJeUBX1X4+mlo<&HN&;NO?_Bw8gNr176D`9RU98Drj7u0HA-9>iMBdNU+9LV> zi!Gnd7n3M$CI=}@;`_v{O|28kW^x?gr$tjRa1IE9`9q;Nr|<-Yjn9^L_`Yq6(eK}} zbUcp;*WH^(aET2R+=G;}j3^j|9Q9YaUfx8USNhxS7KX#ox$faYH9+AnbPTG?Yu^kJ zFr&NU)FAr!nK6>cVTd*|)#QpRKy&e|Ch$tYQCRS+Q*o)^NOBzT$# zGA;LA6->$WFG)NYa2W8ciIR}3RFCK1Oy?|g%^C#5I}n|{guvcn#87x4sPI9B55idC zKF%}5xulq~iN0;Fac4ka5f;!|!deZ_Yb{L<#R#%aEEf;52aE^D$ZN9n0Hbv<%&Zih zuM0Pon)G>>t&MgE)+Lor#A#q~Iww35);bWcwV<0g)J!2w8ph=fv`Ue-OLbvWOjp@b zlWvBTqveGXMkcnhvH;@zgcm^vD!Lf^SSO}IiOV@Si_c??0Vz?#8Uo0f&Z~Aun+=JI z%LhKX1|?ikG=@CK0Zqf$zyrYi5N0wJoOrF7&0NN;!dW=jY%WqdXel)TNo{{QQ@noh zw2trC=$Y!KiyZ)G9gKBZDOqxV-o-OO5y#dkpUQt`UHu1X*f^Cm7Y~xkrkaDANI?=S z0f7Z2X2zT=;j&)9n~cYEK*j*`3|m{1`Yq5rV)Sw-vM!QjW|VPo{&m^zOrnJ@CTz1pa}!erJ!wDjXu35vwGK2*lGyLUNiHODg!|T$f;plH`F{=n6i%Z z6xWUcG=OD!g?#j_2*`MDd?{((b*O)f?_a35!hf8a0MFyqo5HDaZRSV3aIRWA0&N@xu46**;DQ2qBQM1myx)u~;J|ew|tS z^Cye=K>)bqjDdLA$M<2KXU0I9_u!YMYSJTGtX3dwwR3#JbDYveKmY=ef+AGL*z)7w z2*ukzxn!vqu!{8O-wVU#F@trT&m)AFjXoQW25T!SO^I*F_X7l01%~p!udM-!j%!Op zsF$4yjQU4l0JIjhJ7Vjs&V<2@F&K`9D{zubx~tjsonAr+&5C%Pv-KHZ*@>N6DO}dP z!Tf4@0%mf61DUurSxPh=^_~ZPBw?1S6VmuxLknjd)+J{GFPm$W=0nI8Gr+Qh7Nsl` zSVHJ}27_PK37W(Hi==OFLS{!uLiiNP6FQmu&obh#79e79B8`eph|jL)kkzVG@WQEK zO)j1uL~wQLYud+ZtV=3u#sla`F5r?V8iO&u^vK)v{NMQ#Hk@@3LO`jv=b*?{>pE#_ z3rx*ZF{O;5wQd9yFJh%tG`2*tgCJ~l-f`Y^jiXT^@!!f|Qw7uTDKMyK4Wc-@P$CN# zXTu^ELdeb{>c5!fU}qMXpsuE3G;#k@;-~*!R(|6unog|MGvRwK|^V=vgWipwM!Zr8@HorlOH?6lNyjm-N>AeDr%27(JHtuB|qg<8&A>!8a*qUXAqmWACf-tav19QpYTt!o2$W`2y`NT*5`1%A;o&S{bFbJ~~IL>X(oXZ&U!nBN_P}((4uB9V? z`84{KBxvje0h0+?7m6e%U&NdPph4!O%9Sv|h2Vu13RL})xrHe%P;=OZJDGVIsoGAx zloV@BsZpGrTcsc-9lSHIEz)aIEDu%Nsw!>9K@j98g*t#FLl#j$(uKcARD#5(&7%3u zNNNh#r}~;y0MBTVI4Z3nNMZov1w!P{4Xk7^j{yY|-;@d3?Ox%Cos@kC$HNTZQQ}Zm zPg*tah?lANN`Df(^rY)`Ps1+b;JRwq7z@tpYo&FXfeYUT5r8m zLWpdrR%iPF@%RiPXaj`8stACa8O&XIb`j%=21wn%I&z!(ltmh#Kh$8lv+0}zr38$z!eWnr zDxCCVoSYR)!eFdmt;0s!Z^&Hdn4S$Fj^bLxPuG(tiHiK-gAXRpZ!7Zvbq=}6udTKR zV-1F5h5j&tRytugN+EI#gN+JZ0-~<#u#>Yw!s9Ov7;ybQ7Q4$HHtMDqi6+7(y_)Ok{Yo}dhZ;PakZRL z_=3UrcD_P4*q&0G6ug>6tu!Eyu{4o5IrPlSR(cmlnUWrMAarC_u? zchK*j;nm4mAut|ejI%yLyVFLy-2x#=wDZmJNo6Y97=x!zpW^P_yOm6-P-}R5`iG-4`RGIi{K`+g_o6sosA>}XETQ+q^fK%Rv1O9 zICx?Wq%t@gNrXPf?#?#+0#4^%0Bs$twfOG4@6I(A?&#Tb?Cm$_y(ktRZmn zW+L4i0YaYx1q5);;qx|H}zN+_v72!U1#uV25$-rgP< z3=9}x6+i`^J$r_;v$Ikh7X$%18*Pw3`jbDdwQGa`ozTPSAVM@6!A_=AQcB^s!qSni zf>G>lwP(tyUZ0K|O}C_!VsCE`yKNKK?*C7`7ZMuP$RZ%!~c?PGA-FMWBB_&cPX#9Q_&(VbvH;#|wRf(P+Ft{8S^Hym?bfd}noXeWMnJT{-8# zvm3B#hUg9ep&W$!03k5OAR3N}In+5sqcOrxd)6LJuKoJOi{y$fL8pWX5Z2sH>i3g| zj+-~9r(wT(^(wn3XEk6{HuwRACeJ|m0=hI>V_Yz~Ydjo*3xS|j6uFyB2AR8-bq>#7 zzQIl}M667qX)BJ>NjRfsqpc2bFmOhJa36#TM58hE60`92@4mwAgY8Vvl6{wOMlVEi z9U;keCwUoe)u`~mh>Uxb0jQ5?G+wHnoO6iB5yJLlV1tn*Q$rk_j^T^~6=7r8-|zPk z1OYZTHxbA2#LhW56)&DR;tZTMa7fCvQ>L@lA{v!P`m|Dr#{_;D%t+C+J&Yt_S|(<~ zwXaX>M-QDESB{@^I);oTw5pEKJUu!=Ya=m5%V-Mck~a1Ub8%DvCubd^XoNcW%4iL( zG#GUC@h{H8sxhb+0u8%zU%q^aFbuN*iB(ZW@1D!lD&aojaa0Kym6V{2f)iWtEG~J^ zY9Oa2>k~h!HG9w)2c_znOKUCS@fdqji`CklMmVLkLg6 z4}xSPa}41iLBtfa7_A}W7|sEnAdNa(jA_29)&bf$a8{w>C&aacEr4YiOAt!IDiUZy zc<_TTIjf;Rnzqw7+WQ*!3LsXAaC7I9hfD)*dp8f!YHuW3W-LwL~O~t8?TyLg3R< zs1SrL+`j+*G|7WxWX$Qw37#JvLDke;)>enqrJ8DX@{3>m!UbNkiTek4u(h*S2r%9p zALGg6$Iz-CBLv=J;pU9H7285-6r4a5Xe0-Ual9CxAOr|JT8Y7&OTM$oVvM1?*+Z+< zg6|0c4rl#S3`+`NBWafo{V01j&ku0-!6R@kN|cMi^P}fDdiJcAjrRosD`8k`llz=W z1`z`6+_+gt{4^4`v%8C4Zxi2s`wdRtoK$N8i&Dv4X5W-{pmcG5u9UhE+&Txu$|9(+ zIZbS15QeyQaDd)sZz0a!+uX+EZ@9s2iV@-$Ns?`9NxYQ;fY*6g;U%-xKq2wpAdrE zckaL%i`Pry@6Ni+zYoGTw)bwJ)$V|E0p}c^J$;I!r_aha%D7C8X;Xbq_a3~Djc#|M zEn=LVp5XP%BiMO-jlUJS})`I0lJ&p zjm%Q3-NJkCKg92T`(=3m&n0{Bc|L9&+(Bn!V}5ANXJ!w=ByZDneX5Q19=2iV#!@6&gLA!xP1g;(i{-hc2QBR-HE zSGTjbj|Y!FL94xyG1ao?dHBJjTEx$TwyoV8(}zWU2FM7}-CSw7Mz`C={{H^bz3*)F zaPR$((AnrN1lqz@3wQ6|PZ;0#R~xWMDaFCz?WLP~c<&*$cJ>M%k`RJBckj)hUI6fY z9}geBk01Q-N4Wp+L%jFsW3*c3jJA7YuauFv5V(E+AzGc~guQ$J9yT^M8t*|FW~vHe zO||6e#j{&G+e=Nw;obL^nu_h6ZFDv^KnG{1E2l#9wq$z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007 zbV*G`2kHkM5+@%cHwl;k02lE|L_t(|+TC4ymZQ9ny^;+{RnIDB8?%ks|0;f6m4GeH zAGQfec-UZ)^l;91zV5C^0A1^OCDH%R^C0GxjrZKv=iJhw6+1SkaoC{+T1RWx1RFH=?sPk(j- zhie74TLyvME|r_gF+yM;PInD~-wU6>hQjG58a_7x12k=BzJ3|MU+W+s1xVIkI{Wa9 zQy(~hU<3$__ofIyN$*VO?m?+YI|>7`@NB|BZvheo_46{>41m)w;}mcHh>Iu)-x$7A z`;RCjA`mI`;XX+6K)_N#Luq;sw0v4NyaUw*^`D`oyH-KUu>JHs@cxHBLihwW3}YE= z=7b+!5L46)Q(qV0>xW?%PL?^=x7#fEr12&njWDbJr!+y=D*TKe!=L)_gJ3nDf*G(8 zV-^4jsOKMsbu_EmKl1sb*_%RuH@fJ9O=$wx=v}n1-v>D8QUz2MA^=2zd$T{7gCuk! z0yvzft0NDupP+7zIE0I~E^!g}7etoz3Bk z1SWu)httnY!8>0PrUq}GCB)7Ez?lx)C1?ZV=`cSgSKS9kHmdz7{Pe_g4j_$uFp8lC zjs8oHEy)c8G?O^_d;rXvig4mp0E$x7Okvud@GS;YezXz}1;tqC!F*wG_Qt+UGKG)N z#DXEea_5XI4MHxt|9=($cR<)6u&J27I|cK=5_1|pq^zk@q(I5J9SJ>wVNUbS0l3z+ z2mo&smoGunGn($b>hdKjd7NZn9UR60gpgzB9jR=JQOgkF(7f4GYy}L%0$jRmp35|d_8GMb320V%QAO}4!w#(C?&drO?c7( zln|rqMVU;qW1CRCc}(?Zn*gPdQ0N;UfNU8`UX z>Npn+stWIu@O5xp$G*T_CKUVS&rGn50TJN6?Dql2KxjKc(;W*0iGub5LXv+V%h-giYJ3d&yK@Pr+wsqGnjTp9BVt+Mbbogc#x#>VT9_NdGnXGi-$OrQAtMg$>^Xfy>V1^xnh{3uTv)mz-yB6m$X_7qle%bs&h3@-`zkfR@AC zakcbO;5pHfU6oG3LtoCo_g~5qx@&?;_Zv?6yzutN@GO8gb?!$A=o&@WcvwehyIcG| z3j$FnLb#Z7_-!nh~tl3|1B3LDe(1U z4#^v&vgfA&>HdAy3zJK(F$k)d03$*0?R8z{hYS=?df8t;^^-@YqR?uRL{1$o_8hzx z1hl~f#M<)d%J}{dXT4}hI4OJ&vO+Gw^hkHS=Cj&S~!8RL{N~OXlBD5#7n8y8h0I->U2+ho> z?FE7Lv`hF?6znY_+v!>;{NyWTrhSjN>|ViJ%k#Ya5^h>g_yTXr7H$#N8U?#FBe>RK zJm)Ba?Y??2tI;inK; zrA&Il!1XLcT}6y;ItaG>Uyk3VYAcm%*&J$nuW-^%#O2oaa;|6U5Vaq|F3 zb&#cZM-;q!X2w!iyJ{Zy&ozqcSs*hl;U(849x(g!9ca*P$5{-Pv3-}MHvR2^ZFBDo zGV>%CEyu2Z{{&62^l)v@I0qqPlQ`PbJ+dt98W?Q=|Qn0O~ zp+CSn+T?AkDx6cmNpLPPgA;_rWXT)t$} z4QZwJmSNb#Na#7Mj-nCe9x=LF8lody^S=XB*mcnO=s zz`lvS0I4bD4c$S>`RYz#K*F3>Vx~zFsyQ6v^00bp$ye&ruDk8uWltLR#(Mx4F9 z%kaF#d4OaZ+U|YbK_TNtv%(LjNtd<41`763AW&5p=1IV}S3=Vnbf+-fVspD$ub2ly>HX_Y01Qz>Y#eO7oe* zQVRClA7=!FN7GPx0I3E5?n0K>9dx=-Rd&vvd-66UJDQ%BK~O>R!E+aU{nFusr;=%C zuLiCOZR}h4nbiO=&My7@(cmav-s2x6!MoUMMhY0uo9p|z*YzwAR{VY`zWKw&FK2y|@PVdXrltrVUPJZ6BaJ<>lvl zJ3pzrs@-qOMae=NNue5ukh$?^DtnfaM3+3+_DV1=6o%HOj8J7iKnX=FX|5;5_d$M< ziDUJ*RNYxO_yA2GxbL~AFR@_~Qma0^4|9pq^o=;wrs48Hz6@qbHZn6!JF5r zIXn!K0st6^Y2HF4-;o}u{wW{{{fov^a15o zgj^Pao$7uuet%VOE*oVG-%+8C(+FW=r|ARXvt)kYJBT60SikFINPm#K>7arCNV8o> zB!D%Z2Ajr0I9+L%tqLKDnWZ`#%kW;SaRpa3!s0fs8O>$Qz$5(EF6RRGG0hJ&OmGX4 zm$iri5 z{`2(gxRb{i{%8;=-O|ygiT3(Ljb8*n#a9RKH2T{*P^?sSm}hH74VlIoc<=JFc+smq zy4EoETh^SYcV6R;AyVM9akjjzsdna0w5r0puZD9L06?uoYS&m@R1nyUXAaMzDtBzY0^a23p z-AZA_^%OLN^&yO`S?NljF}FcPDAd{6YiJhTyClL)k63>f2)tEX&SlNGvayqN767@E zH=R+qF9n}N*Gdf_C;?)zZ1W;3h-JSiuy(W}L{kfQ2x)I?X+yHZ7={WC%MYaGm0RA~ zmNjGZ8lYfG)}Vs4?&)9q?BowLx_!GR8yZ4b9Rz>(YG0=EO9n#B$$)DCAen)j1xf=$ z$kjRhe&zI!zt>rSL^o$M*BUclae1S-e9La`qOuk{1B+HB#|)&N{aQYNa@is&q31~9 zo!6KjkmcpKil0AHg}Azf_h^>^u&mcv!VsrwkpN*>5}5UcRJ=TbPqCs0)Avm2N%ZTzm0D^f`rMny3KEjt=?lsa00@dBbXw)+STVl z==QeJG@ZaTr&K8<+V<`zeEz-V%$L=zVa7lE0i_eMh4U#LwtZ^xGoEQaX5vpoZ_2sZs6koi{K(?Mq`?(z7mU zfUU!}-Ua~E6VPg)U~k;|MITPDT6NUoY+kMKyzP}BXZ;uruzDM5zt5H}VgGL~Ez348 z@ehp)gD>0eh-Ou-4*&qw)4c!M)pUe5lZQYV8ZXWI`SX{nj?+0r1AOFRWW03x2r1B9 z8Ev;7^^~Ng*@rcJwKj^*&Qmt?tz-6I6Mh=}lomTt3<;0+Ge{A$jl4Lxadv+3iiWkl z$sY{8@{S%qKWuifkufa)FaY@014y74G3d|(Kod2A$05u$KtN!0&9pbaXMJ6$sv7id zbULXH07MjQV=tj0xL$^L@^1lP6pKr2ZubhX3EIHOtwoIDyvVd1q86`@6tG>eLtT3h zf*kvw^ZVN?qv^0Z9dtefx&Hz2w>Dtru;9zNFf-Yn&NfuR($C89Bf3W ze>zD6@znNVvKSo{4X6UndDzC>o|kF18Y3@C$@614(PWT6wy;@-k!CA#sdF+r)vC4= zm`Jaww~_s^p`iD-6TkKzUNuAM34f;x!`bHfuqIG%IxruzKSY1#BaBgl&nDy}pONjpuzpz@=sGyXl;B7VCRM=o>9g6g>*ATGdhGAYWQH7c_G85^wNRu5qLz zqHn9nu5toT7lw7ZX$09)5@?S)&pq{_N|smYW2TS@d|@I=M8h1yH%?5=+86LCjUluW z4kmi|rc+Nq+l2bWs{|rGjG%evINE=T6`V%JPFCv1r{1GDyh}=|X$&0XB|;IAu+?M3 zl~&}QFZ;&!Rp&7Yhb`J-jyXj%Vg6adiBB25E#ckROxcFBriSgtvu+|3dFV@q0K*mvUxvY zbrX<;o^I?+TxA^nfLIMGew@)$)T!U6$>zh$u|X4v7kJSk2l4uqDBNYkG;+1eZ?tjon@SVm9;o_1v)G;smjJ8erw?1pItU z*2-F2Q!TA=3TvB#+Sk;69q%QUtJhy<+Gaipj8nbEky(u((Fgnbi<9m}i&b}7} zM1Y;zmFJ59Q1_<7A}C#DAQRI|>06KSd<}EepR~%Fozif3yt?qbT1EnCyG;=uZD9TC9)X3$}`(b?o7u8OOfbw~qNnz%xr9sKEYCm6`P{u>Uydm%irbkxJU4eCm1R*T_26P=XXfA|XC(J9cX73LbN~yp zGqaNWF_OEQfen!Jv;IlBlJo2NkUN;zgL&lYKi3$^9UPyVKQ6Eu_!;~)^C7o4ceVPd z`lq~^mAiwj5i2Vj7x|;0y@{K-i?xZ}A2y#mx|`aWt6M)ee@@Qt=HhM+w)BS^OBZl7 zMt0V2=HLxFnYt*NyP4S8S(~Xjx?5T~n7e}YeaKBsT+JmM?HpY+%)Q*)UBKJ(A-4tx z=mp*YI1Kwgb)-$qe&&MJ&yUfE{NMXDv$M9aF!BWZ`G4k!H&UkTbn(qaQv0zpJ@Qs#lJDg$w>aoAlE+{-Jiz(Yi)M$ zzJK@3!TVSR|E+Ja+jBIRZUr{y2S`8fX< ziTay557+-jo%5f)^RoSK)Y-Ve<@@{QdH?49Kjn&#l^y)>{ewO?ANS*3_vbzQM}2{> zUTJGP@T)L6KOYbK&ui1+|1$V^c^(!1$nKx@_&Ld0!u-I$D=eV01t-nU=1Vy1wa5p1n@ct3{k-l z6}*NBAcLh5!2=3RA%VHb-~kOpswh20M!-h^n@5(FkyHcYz{ddx1BN}qCc|I^;i~pj z9Aa*eF#tRPS&Au&0l>R>#9I?+@SN04Mh*Oa`2{stDHH&%!K}a?0B~mofN#bCz@G*H zI8Is3s)FDJ7*qMDlEA~`zr41RL@)>5Nk+#N0GKHr2ZUFVhzFPn>n5ux1^W$_2%Uz6 zf(QQ5!m+HRn1uFLrxDNg zgQ};1!FO+}@rT>P00tm1u#t_F+ATXm?sEIXu+GQBro2;f=n4w$r>t?Jx<_>&+#cf| zQb-NV5%|qD!;gRggOgT%{k@O!Gijgf2k9AF|Bj*DhRxcXqqEd#|30p$@3N$~xBkk{ zV9Xb5iS7$UXM8wgA;LPC4NZSIPI?8OfhHnE9Gl^csnIZAhU?|DHfxRXTn(%z<7`!| z?+oLGvDb#e0CIsmqrfw{3kIv|)@!@-`;wsc6CEDXifER}Yo~5`<*UU(8fsRKv}p*p zX4X*XWh*QoD*(^FZZ)kJn-Jos<+>X4;7V<|sy;gg4L_1Q&Zz3@0jk zsIYw?Rw^Ta09AWr*DdM@few9k!aj!-IKi@k>3IJ#nig&aw_W`vo|cjvjqY?!SmuXl zr<$0A^$XN`UtR82s43{P-29>zEwQ1RyHlZ~`v_t2OAt4b8J^wOP!dBl^+_akBb?Rm z5hgfFDSXpS3yhbA$J8XTtmXG5oMJQdw=pC=xz|S_##}kUcr{PVumSkD+a%%coaOT( zKduVqGQ|UVns-7Jts!T24C-rEV0m}cpf#|_0m&erI+3&xN;h)Icfp}~Wp$*N*XH^o zqvJuxxs<;6p~|6X%irodRM)-e!)SYOp+znQut~ha-Wwife_o3QQ3s|+OpGdY2^gUl z!#L(p^^O3Y=_sr6p`&Yj3P|oD)0tX+>T4^wduII!oRWNuoTdB1=sw;c%dK@rIXJP@24B!07uMJ7O@GAjDxms zKd9)vv!0s}5%vM0ugtcIO6{Ulhtbx?TIba5pw2TAuXbn`8ZxnAs%XS!AQhFZ1gbp9 z%dCTFS*iAhBu9i&dB~7`g{);<1%mg7**#yPv_*;2QZ)xFdVK+s&b_hrUowk~+LX#jB#btw)XJ%L&wO;d$CZaPKlp7;|8Ju9dmhQ*GMdG{9LAXM`eNJaH@T`bPnZdFCWl9VRL zGhRKp%+9u3d5+Eg?LM&%@%^>I_Rc;#Uzt-|UQ07gvdA-bqh!KST&oTEl>kN6 zPkzFe(35Dy)^E|L+edIl`0v~U(a?p>(ClD3H9Yp*sC*xoaH#2y&dg8-2?}esOMu;b zzbV8-`6@QJvNV7~>}cQJU@HO%Q~AZ5k;qx%)8aQ{HVKkSL`>&|t=ORYE>Q3MkKLim zdsNFdD+}?7jI@EYNL>FbGYA!VsCuIOw$@zt8BgMMcQwQc3&_m4u~X_6KvGg-%_OqP zMjwu#$qk3d&&D>8j)wgO9o&nKCD0}!iIr|UnZ49!y=Tf8dqNEx6YpknXUT7!NA8oK zO(`R5(&#D*3x-Gdh}FZT#Dr>RTpyYm8S(*5B+e|ozQ9duSNf2oeS z$3(WUt4n*k$zWdW7yo|GuuE_Fr1civL09<_(s2me;mI^UbPFfN`)L!wZQ2rf?k@qY z$Xh!nhm8X^7{JSr)tO0bHfV>3E>c?ec^BH*NVY8Xx4OfJB1_e4C1^TD3NN}l&ln{P zq+%TS;Tmr@f!Hmd^=`-^`Ro%lw3&a7ckQwdf5TVie1dwrmRpj{;v(?9< zPwiQY4e`Y&_!Uftl!IxFYKDrK_2z`gYRtZ02Px&9Uuq%u-F}e0>md^R2_+6we^tN( zNU`SB$rbXgSQj@+r?vE@2%qe`FqlzDBU!#z4mK&{ev~V-&y|@iA+iG-;{`jAot@~# zaV1!%k-kkkDtw7alyW6a1Bp^0oF|c>sm)O{DCd>!2#_o+TI1St>@s|&a+)72&-<>; zkcg#t3CMLeXzJ^MFOVgzwi83Lb>m8ieI!oxspE?Uk@1ffdM)&7HI)VVBAxv6oA%}O zS?Zm#PQo+;1o2sZMR~=p%6D2ztlvS@^y+T{Qz6B;YBk@A#~KD6lALhvbyba(+o;so zlK@U-Ni#UAFTJ$eEcejOtQ<5U{qHZAZU>~oo^UOEdwLqXei-1b-~N08*}CXF#bmZe zW`}RtbA8U6PheAN2UJMcOF z{DI$+7}lB$Fqp&!n^HS$-vlk{#ig!2UwOHk<3 z36&Q>DGYERo-#}1)~urGF_Po+gPT$yBPT^nMeIkHD~|8om$i_R1Z%5mm@0VtJ-y5H zDS&l^`R;anmDX^1gM_lHW!6R-0e3X>`^|@$Ge$J6TYtLthm2GqpKnUb-$2CZ)*i9z z+ws*0AHKq^aODf~bEfp+!A9`AoO3IP^Um%ncc6>|7y0T2{Z7pF@{P-xvmWPTwz&P< zgf9$6er%HJm9Tt=@_IqUxz5YDsTmjoBbCE%xR0^;b!jx~Mfr}>#Qx^);nScz#}a`I z7E8axqo87glC7>LG1Gj*oXBVJCYf^7vQ7OQV zDQT~^#eNwLUT6Sz=UJJX0fSW1bzAD7v9-`OpuaJh8{plr5TPhU2NyBvb^v-!0U0=X z(@{n?QvKTh&2BPfu#6{UC%6bYU_Q&?ic4DIS4sO7BP&_hljI3(ZEgDqh?KUJEjbQJ z4T=-U=HtJ7_2Jl(-X|qNGF5Q)T#l6)Gkm%@M#Wr26kdwKx{BuGg_!^ANVBshjU^qP zOF)t2{o#iamUkxTYURSMsIHM;PGk8AOYM+ z>t%7umDj{?@99Y4@1pzEoscSpKcgU(bmXAchGot)oB_s+)$=~wAYMl7$XW%!nl#1% z-mksdSL-nzPv%mSX z`3Gn^G8331t*GY5O~9F$mh3{U)jjuL6?jSZQM=>2nM}Z|mfgId2bns9r<8$QIWU=h z;=Bgdsg($1k?BSJ1j9#$<<8L0xVqQ5tMNXYf9|BXonhXFykPr`%BkRl_&T*AE)R1) zE8Rq ze%yO2G}@&v<0X;j*OIYDAkmM{U=KO37x+~wG~ClHZKaXZ`|!OEf7E%6mkT#fv!3fs zXT9|_+=wGnTdj9)Y^^Ny_^MQ2*4N*eW)Mo+J$S#M+nyIW# zcL^=Unnfypi{er~S0j0BxGp5KTx4B6U%4I5Xwd|jOSn_WNY<-Qol9Piw3aZLIaop* z-7tyjcbVSshom*lzCT_Dl<{!~@FT=EV7l%;1uk^x++>Wo@1p5zd5}h{=2cffWWlpM zyg&e~^hwFuZI;Sh7SY+&hRLdl<~1Pc92Doi!FTc<-EP#wb|rjc>TIydxTQ;*7t54xR;(I>Wn^$LV36LIPixP`!3N98~BjOldbxm z1v*z^Mh=;j7Q*zM-3d9vOY;u88jpH%Np(a|IBQ2+Yl+$(+;9YxVbsE{WWD9N6fUz9 z{5)g1>Ac|jgI%MWy-ifkX1^910(^08B2d7EjZyIrc^L6egMqdXr0GFf&~i``15r?7frAeR24MY*fyRZ{FRYm3TQ%bUlz?i2s=1h%~&Em>) zgYDj5iBU3CQxyo0PW(OrZ@*EKW}PmOe^lf5ip&c?OfFU3Kg{M~72cOO)BRG%Y#mtH z)3^)+Okj-JuG<+_9<}Z#lUm;4BGEqcq z5uYU$7R^O)`8IGzjf|*spH!Ni8)5k4-zH%Ts3O6~pL57Osf%speedkye;gIw&^Abd z)b=BeSVY9m25#dQzTI(BMATXD( zG9D6RHHDZ$+E0wnBSeYP^z9u9p}-7ubxGaElk{&+@qVZH?ELQ9>BTJC{4^sr{qmZ& zSbTmi>x$YtM{mP6DW61dU;#rxeonAK4gWl;PF8`9ugipT7>eJ>`(5Z@$+YTg8iOCV z;m9!`yjRKbztq1ks)%Uo3oSd8&|G_ApajaU3e7z{L$%{R-zgtKjebt;WV`N#kxpnQ z`8I%G7V3EN+@uzl2ow1&S0aryI^nM^7@SL8*#)M` z8Zpvjs!N+*-@VJvyJ*7bI43sblGue0C_(I*yK-k4lxKzoK2t0)NV>4UKD!>(4nC?x z+&D7aTDur2Fci!^ICLNS#(OXMZ112~!_Mrw^AWTX{W_ z_FX7rT48?o{$m(#j_XnJ_3anyxl=KUfoN3vDo#XM0maU|9|ffx_d;_WbT?H|mr90v zwXDdh0gNjQl9Oa6~BV>H6fRi>>=#tQBwFkk8?y9 zO9uY3gZjl~vAVM8Lg;mGIfz_1CeM4DcRnBrD@*#LlGeArlH~&4R(ZKHYb3%=D)A?; zYo^S4lC?H-SZ?~xHMiN|Bbeq*KsV8lZP%u zRv??q1|-i)_Vh|!%;a^AXT3|v_^o~L1$|RuKR~u^f1V3JJSVdM(40s-J(ptv%>dD1 zRO>Bb;*RkuG{<32LEoJ4E>Ihp-al(xeX?|=WbkXx@H;V#mpESItU0hD&mpEck#^Z9r-4qUKL?WtVRXq=Fs2`hO9UhJdLCGe*- zdcccRCw6EpQ?~cc9aUUjZPfYIqyl;TaB&D6INzqq$YPfa?o7E)uFn>0*49DqUf>&V zeFxq8%1;mJ>ZvnTIwRO-hRn}Q`V$>erIgPYSXV8MmF z%8`FMm|^IZR#E`{0FhJYFJ#raAQOhhMo4tSGPVAu&dp#1c(g za{J7(BNp&-Zn`$htiTX9uyf8eOdqWkk8T^Evy;0-p0&{k_KGu8p zUT+tT_JB)sTPurP6AERrr)M*hko^2xeHaz-SfjCrhwwn(QOz93Gvw?2K;o}o{U5j< z^uKF<8)9lZw+brWD2pr-h&CnQU{o5~DlBO%GoYq?QGOloZf5_vqeRmy6GZKvYVKSc zkgv#4b|0M8)a2gkZGv3>?fx~xCz$z*6Ot!)xII?7K0Kmt3n_}e4!DB9Y^xrH4RTT0 z@JZwwX~zLc24>GEZ$|?v7scJ=vn>B< z{4hh`nL=63cEYkE%+KDHB8~ds1KFEu4OK)s!0F1nPdg<0X>wp|bPKw%HoR97U>G~~ zR@ZCA@gliG_QS!)N*`8Mw+CPx{Y3*5ByHJhGpD6H|4f5rfA;Ji zbMlI2OQdI*>}O8?8~RcAvCsGKdV#<91s~~OHvb#?*ZzO+BRT_JhFw=!7UB0^qC_qSaAE1BPQ{~P$p{u#R&^|t4cS_=I$e$2z8-XngD_ZLP2n83YsRsap$6=wwx7Jw1J0C2z%2cQK= z05X6Q3@HIZfE>UFaKR84paN(BQh*2yi2ynP9jwO!hAaR!zy|KNLxCX_@C0B6chnKU z5COmhu)v*m6fi^quYvp70Ju309(VvWxN}brhV%e2xF-(+2*8j4p!nIjXZZhiFCLYK z2h#U7xbsg6{Lzcw_Zy6-(D7fsXw;UWPPaAJw6Hgrnpzm(&{9_JIReZ`u36DogHg>o zgF$fZ*Uv9Seux<3Habqr27)9S`(8oEb52ZDI(Rba=xI?W%J=H|!AY~qNVAfseJavD z1K!Z5e=_d9v0riY+brquT|IKRD=;`+!frJS^!!c))v9-hySntEA2`%HnL2j#6~WE> z=)=3`8<kvC?BpOI(i+6AGi!rAM%ebrgxLMl~f=`K!vX_cke`cjeJjxQZW23 zE}R9u;%)>wB06m$0AU1?{)xnuHRJDOt8o4|r_}&Jb3Bnw#6Q~Hafb)2W^Oz{1CQ#T z{7YhbZx%%Q%Gc^X99Gb^irj5MU07{%*hRF|cz95B2wdSmEvV*@9Tb_p9WAd2I36S+ z(_*{tPkksojAOM%QBco(#uz)<3B{=61HzM7%#Osc^58mQ|s1ID?9b6{{ zQlWS25dr+kWR;@tJ7-4yW2=qsN0TET#+OBUlv)-YPP7PueI|1GB3w}%mEwlEj0J&< z1y)kD5lA5JI{w(j4Fv2wjeKPn=6PgsYjExv?{JH0)P9NR`~HFB)}oe&!v}|WH6$}P zgbmV`C<`hp`3Mdc4q{QGPKIJlamRTwAf`GlAbJ16;Rf4OsL%7xh(-9Y@?Z(iRGx3h z9|kbPOryw)>B*3k@U1!Vmi7g_0&e0Mfom(D^tLgc7$)6q5)Y%*ma`oG}4{9w=Z_Mkv{70bkeVW+l?^w|`fVe3`JNAS zVHxU_ZE=v8DbiPWkL*M(3J-mS`f$I(H`1nXSE6ei-a-evJ(c;R@BuSoDdfva#1az# zKAyf=JV;NT7m>|^vBna@H2^A3g&sC--KHE8d`O zPPivUGZx(uIE@y7Z!(P2G3i2yV4SHK!Sq&r>6CF&Zgfp)q|VIxw2cg3+phOI4SE@b z21h6RI#ghkG@2Mt%axSjOX}0&{AHoQ-^?30*q&uNnn_Ni( zCCRnf2yYvF7*0qty1KsVcb=d>2r{s&rdwOS@g{wlCk)H~Y~}7aG~z%S)nrU}7XqFv zFG*<~k`cDhBToa=DVD(MLnm%&xU&7xm72*M)CVdg%x9D5S3$FSj~~GA*k2z*<^no! zIIYDM$5%iX7kz2?F4P)X{8>|M4gpQ3X!l3kkWYc{rK91EgOjUl_$>1^l4QM><;Ucd zR&vcki!`1qJkMeu29=ky7m(Kw9@(Kw6=qqAR+ctK77gBU0%^dSI)^b_5Jb=y1l18j zZ^MRNip2;9V*oE!^me3(RrJl41$UtiWlapk504Ph+(Zb*$xD#JqToyq`^c&{afRZi z*HM|7gt*WuFdDPwbmmmFEjAeJ@j^qLp3O=|LZ$t{uv4HOS-fNoGnZ^9ZbSn*Q|^YF zHf1tICo{2IQEvyMEX!m}5)`fFr1QgPB#cuJR1WIA($LX`m!>zPG}Yy>iqhYE1BwYV z7kFx#0@8vyJHvB^m@scEtUAsGoVMNYQ9<}o% z-&_ze{0_U%`3Eu9VzuI__oTkm?QlcLIZ;D&uURA>WoWPf&UQCkC{pg34*>39g9)#srMYF z!9h8gr!uZlQ~ee30wtu(|R_->kI(l~5WZ ze~BWMhJVX*B{XZ}Q)b4yy(-t~o)IhPcLr)(l2Fmc9P}=`s-_fR!)?{^hMKOTmSV=Y zLSE+;&Ydnm%5v?s0eynD;w=eG=Gc@8E2I_7wgSOhLubW_dI7eI4VgTVi#hEtAjRRD z1z@2eO*7d<8II6UVO_t`ArCXk;h#a$x?V{#yZB_F9SSBK`lws{SkQafS4VcLjSoIG z08R3)Ag0rM)e=!=-Hr3avF=X$c?B)LWq1)b6l)(E1Jv-{Cu#m|_L=el2b=Grs-G&m z9V^3TrMyHzmC1ObrBhP@IhRU;fs$-#8c)3BB-VXS4C)`)EsJL)=cR<|L_L7<_SA-}O& zyu9D0b+P_5y^wwG5W2(^bK^+;nNg>=l)Rdy_iAzjWWezxST&s0%0M7cHs6xndGKMW zyF|_Rv=P{JGP8a1tiHuKCgdw3_x%31+G5Q@k=KQ6lCznSOcjvq)n71ocif7T-@FES`v!l31LXx{P(G1XyR#G>D6iIyz4pfF}3~<+8>}6JmU1> zGU96%45eDD&%zd0%8o*kO28uH0f94-z1EKflX&3jt<*cLhmMm7aLK&`D=K7FfkZ>mKcn z;J_L9ST#{RP_8?o`>Lq|o%f^4Gc0wyixma7*tjlp&VlZfoEbSvK^&5ym7@=E4yQ%- zq(LEd-0-ifvcFrHJRSG5;_E&_V2Xj8R~fgflg#j!uA)m}V_}1+%Bz!PNKZy7AyAfN zEr7t^*OO)T@rP>2>V?gcU`!3EsudQ4bz$qRej&{zjVV81HD+wIo{rs6rZ2jjtDG~n zwr9t^w--TF&z}k1YqUtWya!Q$xYAo67zA~6w^c<$R<9#4`@$C@>Fd~A(OGy~HqnPY zx1dXRE!}TiQy$N|VvWA$B;BF*|&QRV91~Oy3bn7TC{b55rAl;jWpWsHk4%O+T&5T#s*>mK^|B`O+lvL9@ zZ3?&UhgY*pGOvVUph~9>X>KV6VbYiRSlSa!`51KE?|Jr#($kSMA5&nvzSOuq>|rj& z8kPcE5oXUBhX+13X+n#68wXduI+ckQz+TSX-8ZOP1HB?`_P82iQ7Hz|>5@t%XEnEi$-jbUd z?}e&@3IbHd1mL|-)=ixDRz{&qyszL|(nxOBp}_JGM7-&xis;7eN&0l~hLh;EsaS15 zbq~85lRcw(OeHdqF}Txk3zvRcB>Q19`H2YXmkhKHd_ef)Zg~lYh}#^)$HM4~Z6Bl< z*l23woWm(Dt496l1Zr~xMuQH-(011SEwleK^$f*RpC~H zh$np!3t5@;ePDeduCg|iyI8z)dhd1)>Qh_=LCA}Ryu}``sufW>czdMF!k0XE9caYC z2cL`bQa6|kKGA6x86%c?(WzN`-E2c^7jVQ{h#H@oI@t+|-N4de4Wb3;>ES zoH@f!tCEq}fvSMGY{a1BU4k7xQ>W3~LBXmB2qXY%`5RT$Atj+mOD;+c%u>{+m4+E0 zk|+~~K``P=&Ibhy(dwdr_T4YKOtI6I>Q?%KfyCJO?HhG9f zN+F__iUjV8UG6%%eY^tx{$%qUe#w#^=<2MzI{w+gu2{uEd5B6m^?LhZZ)c6k*mr94 z`WKoLXxFeljOdC>eY}Gs-|5E6a_HWskiiLq))vcZm6bW;XKE{KdS~-oZ<$0yIXhsE zE0!?1S5ul4{Gnl9c6g=tWAX9jo|@?De59ygtKR37m^XMFPvS>A3rqsYYHB9qvN$N- z!-*?wtmxG=prox<8S1^)K%cj&$@s{0l~Q%9+q5}T6wzS8{_Z(~2HzU-I&ax0PJ@>M z=Ha#!BU8-kvR;+d%aib<5eaNR#Fev^=zX&ZsY*xbn?jvcrWLNgmTTAA7x zKP55jkWe}~A3XJ(am2d#^`#wYu@3nxHc>{J#7*ec(d{~Z;M;&CzYo;JX%n$11==t$ zv6+PU?<5PDY*!4G;RT?J3SHh#44EUkhLCDa(Q`t3q_zbgDB^yz^zg@8F9~!~rPnXQ ztR_q<^_D3LdCN6!JH1@YldDmx&f9cMqK z5zX^S7#C!kGy*Kk0}P{Y^WYBz@2c`f%W|L#)Zb=&f;EO5$c9Q|gD6VovYQ@&?v!5b zvWC~~Nl{Ddoyy!Z$hkD{%9-S<4>e1+Zc5(TmmXz**Os%k%RkU@jn9{)^p&zF!=|8f z$Mh}rKGCVoCrlXz2Q_J{^*HMa-nu+-C|;tVnrqP-IrI?HK&~H)5n|ZTiMUkTrlDMu z0bkS(gC=EI+0M_;XDty5#O3L!^6Oh_4o66s7SdkSGJ1>Z*aynV!|iLHekHIyiZ{VC zW2ui($h_7jt*$I2OtuBS>sN+QvLmS$7Oy*pT^Zw{eZny^Wcju$KZ9GI5vu8e|@VWsYDP zkPlLmeU*d<4=o3&-+QQkWfu}n>5swGgJV!JXc8ljOGeg|{y}nibZVab>OPt&cLXPq z)}9)cPGS9~w^FeZW5~jVaHT{_oz)2iF8E3!pSR=)6c#(0;&It-A3c7!^3F{#9&TnS z^QluLGUj4PQe6x!ZW!U&vu49($*gDB3;WpNJ>wq^*2K9)*I3+;%7$%Mke;v>`jdtm z8ecd+fpi|>A{QpZY$eBXpmz1y-rra2dyY9WkB2cxt7EH)MuwS#)16D{l>x`jbYXy{ zFnG!$D~RiadsGpb@Ty!RetSh>o6+qvKBz|kExRqWbJ?_4#f$9ikysdNxAvOw2uJUS z!nE4RcKa%4OHtz|*kiigy2zupOo}KU;cAr_aZqbvV_^FSY->+}0(aI=6`0x8h&;&| zr{1ZRu^zC~RLM%c*2Yf!G=k{U{Akq;d6~SE3S^4-eOt)&a<3cZ-A&XLp3SKxI)o<` zY=xmqQ#aPu0_(S-IAXB$&A2z(G{s_(!+gBaqh_%XbgDi0kOS!>-SZ3>+sInF{|-g(Q!e@+6VBdxB4H~#~se@ zq<<@pCIT#pV%&A;bX)?KKXQmZ>7S;{9SX28xP!HxE{Bsn|XyU!gcya{8bwi{;7f39Th zL#5ECjzVASM~9(Zo=U!XB_^rJ)$6WHH&En+x8rVed}YxPJIwDEw0TrkTv$IXc1f&K z_m!2zUnCwr4<2f)D9>IeR=AD2?!}DitPR|tA;(DqJ(L(|pwvWdPD1Gy^C5S5WG`D;yvTvZ zz%)Co?xT*%kPTLNyP~y)93Pxc7xE|N3nlURsX1XKcsR0G??79S+V8X}sQj~aZcb>T zFU5fOn=wINv;7w)*_M5x85gUmMK)h3bStz-?HXKtyIr%sNQnZFuY$=n`euJ@tiXL- z5dQGI6sIQYbbAG{XNxVwJ$sZQG&NS3AP&ZsfnG7vao_?9qy-)RqocYKA}Y>SUgB+P zHhj$WQ)8eWyFtM8d&Nj-S1cQDTsVVMfDI1W z8*fMpmq4iW_gp%1oJ40iuzr*#DP|i?`K@gb z6+Wx86~*K?IDjx&Op7!*8zpN)NbTH~otNEW3slLDSB8w$p(l>$Sl?PAzu|3GAxFfq z>>AzgS5<7v>HMI!Uh-h5<-b9np;R%&vF5|P1()<=D0l#m`eh$n zu#>dSG&CgFlf02&F{bBuqa!KG(9|_(%w3=mjzmRN_z>(+v^hT<>xx@H76QW$N=!~U z1NqY)Li=m#liIlug$dhbJ391Od1Ed~Mf@l5#cy6aV_HzJZHkJnc4mi?3lq}DjH8J^ zzwk@uwdiZ`*hoJ~W}xR^n(?y4uf~Zek5xc_eXbqOrFX-mr_*` zMZgv3buy*Z9ta{4UI!;@3o1|SYvE8bYggE67#4!YUBZVjI%XBu*>Hh$Z@kiZ zrM4tYTZK~9tZcm^Qfi`Bj%ubawyKP%nhRz0_I+Y#n>1nkfs_*1s-j=QI-Y6-_m+Hq zbM;xq24SY;Izs~(wR+7dT~BKM3S#1)t)fvge639EQ086Vab zPjngP%nJlTkZky+~Ccgeg?lETjunKYgZsw=O= zjN|GWp1JFGe)n`Vfy0kj=tVvL0V&}yidWIt5jcNZ?J9JNY`xnhid zzPPo4aT+lp{P=2q&F%n`r4QG`v#|@5`L2NGPL=L>f-8y(x4;To{>!^Q@&x>MaxV@E z6Pz2eR4;?OF1+@ay(_ttl(dE=ZqJ)KK7Q7_u?n+Sk?V2+;h{Wb)m07+7WaZ|du1L{ z8CzsrFX%aTn7cMLs6)m|Y!*`|EY>n1-LeEpCR=~<`B>JRejYcZL^*f;V5ucpp}Zl8 zZNhRj%QB!R-moo*neHWL-|AI#>g>`2L<4#d=?`e8x7L;xY43k3~w8d*E7kGCS!%$V$&1LWjRxqW!OU7M3G<>Y8fMd5<95FSfn zd6^g&2?^I2OB6as$S1t6u1<{*2L!=SVE;|TuJh0V%|Wg|x7ST8xZ z5mZeKBeZ2)j|?7P#qDo?ay)V9b$`nj7Z-=kEu&ayOqExJEFA)I?Wn}cyKm++&*B%S zc4or}{?Tp-yE=r|nT1O3u|tQa_?!$hcLj=&c4tR;#zVoUf(3i(dhD zxB=gGXB|O}P52H9kWUzT1CX)yk>bZi@lEnc7O-_I`AiclR^45HjNXi57ApybN1I+$ z{C1HT9+$~eQex8a;*7q9#*)iegZmO9lzLS33bL6~*G!-FgRLN85MofCCgz7s1(@`f z6nB(S!}cyE7Rd^Qd~1fdFS5J^76p|kPOMJSK~5y0@L*@B=ygYDhwa__A^Qqr34W6MthlupNGubx z*_FeKCm;xJ9-b%t$GszbyjUzbyz59=Tuw4W4eG^p)8zQcPp|9MUiO3haX-n7*9*l^ zn&?tnbT?Ehe}9UajsGkz*Ofm!hN`|SZcI>!U(A+*O={Q*VLO5Q9NyJg1E8@adSeyJ ze>r4@T*-;+c6Q7%(p|>(76%f!#l@)IHCSa&jg?3)`B8iKBrb}3Otpel57Ka1R{qtmR~Q%y1jH7VxAZLKeX3rdK*HS0?qN{ z(d4@5(XGV2jzU=Z%JW4s8Yg(>Z}2Y=NVOuJT(=V<6rdb@cu>b}94Ygdxj^G~~j@Hr$zVpEg)(^?UeMHx08KcbCpyP*whBX3|m zYZ~gF!LcqAl57la^pBMhQ-GFD*S^NIU@eGSV`hnBT2aDAIk)2Gl7ip_btAj7WTB5-qKzk+pZUyF_7~J8$mKV8^@l7(T<#>sziVjIOnXk2w7z<9 z7sl~g6ZvF)k(}ayWRY3O$lKrEt;OqfEh3qw@-xSqZP?l6rT+UaBEsc%#I;C$;V|_@ z!T0Jd{kdqt3!Vp^_|Y@+>xU!FpE^*xxf?GW@}>>~T_Ls!bAYB%zF{)W%FbCm8>X0ztk>|SPEr%}dLV0V!)G#&*;pO%^SzmJOi-6o30O{S z5Y=VbxPoZTT!{UoR=(8jb12e#=znAG4UAIx9C`~o5H5;Ynwe2ge*q_kC*a~j*1x#r z1N^9CN)t`!>PTCQRfC?q0lGNcIqJdcau4d*zP-6k99pm*EbQz&^WlVND4A75SJ<=N zsO5koQVQW$gwLAln&M&Og~iI-Ub9WrZhK#L73lVkhBQ_#5llo#`N0gpjW6k2zn5-W z_;`PR|E2BF?$qhvNn1S0?a|Zs4@VNM7dvM4x?3@gK4#TPuivA+_j@4I&E5)a^LZfV z=H|vf6vMM9Loiir*!=dfPETM#Uh(7F(nU%})PA$A`x7X$>#g*uK_uY}>m x?4NP6#L)CtJ5L)`zBK0~Wd1+9TOIVc&1{$SkN-C;;HCYN-GK literal 0 HcmV?d00001 diff --git a/world/generation/chunks/assets/chunk_0015.glb.import b/world/generation/chunks/assets/chunk_0015.glb.import new file mode 100644 index 0000000..7078d3b --- /dev/null +++ b/world/generation/chunks/assets/chunk_0015.glb.import @@ -0,0 +1,45 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://d26fd6sjsh50x" +path="res://.godot/imported/chunk_0015.glb-0a28f27956d19c7965f7bc9c838f1b29.scn" + +[deps] + +source_file="res://world/generation/chunks/assets/chunk_0015.glb" +dest_files=["res://.godot/imported/chunk_0015.glb-0a28f27956d19c7965f7bc9c838f1b29.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +mesh_library/use_node_names_as_mesh_names=false +array_mesh/deduplicate_surfaces=true +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +gltf/naming_version=2 +gltf/embedded_image_handling=1 +gltf/texture_map_mode=1 diff --git a/world/generation/chunks/assets/chunk_0015_cliff_wall.png b/world/generation/chunks/assets/chunk_0015_cliff_wall.png new file mode 100644 index 0000000000000000000000000000000000000000..3fe157f58237f17fdca91532dd58dd47fb9d8335 GIT binary patch literal 9025 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 literal 0 HcmV?d00001 diff --git a/world/generation/chunks/assets/chunk_0016_dirt.png.import b/world/generation/chunks/assets/chunk_0016_dirt.png.import new file mode 100644 index 0000000..00b774f --- /dev/null +++ b/world/generation/chunks/assets/chunk_0016_dirt.png.import @@ -0,0 +1,45 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dxy8u3vyyu2on" +path.s3tc="res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.s3tc.ctex" +path.etc2="res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} +generator_parameters={ +"md5": "07d90b3c46a4c3d0c172bed143dc9436" +} + +[deps] + +source_file="res://world/generation/chunks/assets/chunk_0016_dirt.png" +dest_files=["res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.s3tc.ctex", "res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.etc2.ctex"] + +[params] + +compress/mode=2 +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=0 diff --git a/world/generation/chunks/assets/chunk_0016_dirt_wall.png b/world/generation/chunks/assets/chunk_0016_dirt_wall.png new file mode 100644 index 0000000000000000000000000000000000000000..dcc23644fcd3157f7fdae3c099a2e4224901bc1d GIT binary patch literal 3734 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 PackedStringArray: "%s must define both base-layer scene and mesh name." % definition.stable_id ) - if definition.overlay_only and definition.base_layer_scene == null: + if ( + definition.overlay_only + and definition.base_layer_scene == null + and definition.ocean_facing_edges == 0 + ): errors.append( - "%s is overlay-only but has no base-layer scene." - % definition.stable_id + ( + "%s is an inland overlay but has no base-layer scene." + % definition.stable_id + ) ) if definition.allowed_rotation_mask == 0: errors.append("%s allows no rotations." % definition.stable_id) diff --git a/world/generation/terrain_chunk_definition.gd b/world/generation/terrain_chunk_definition.gd index 2d4c548..ca38dc3 100644 --- a/world/generation/terrain_chunk_definition.gd +++ b/world/generation/terrain_chunk_definition.gd @@ -12,9 +12,14 @@ extends Resource @export var base_layer_scene: PackedScene @export var base_layer_mesh_name: StringName ## Overlay-only chunks replace a reserved base-terrain placement after the -## ordinary terrain solve. They remain in placement manifests and runtime -## output, but do not inflate every cell's global solver domain. +## ordinary terrain solve. Inland overlays normally provide base_layer_scene; +## complete coastal overlays can instead carry their own descent to sea level. +## They remain in placement manifests and runtime output, but do not inflate +## every cell's global solver domain. @export var overlay_only := false +## Post-process pieces remain available as authored variants and in placement +## manifests, but do not participate in the ordinary base-terrain solve. +@export var participates_in_base_solver := true @export_group("") @export_flags("0 degrees", "90 degrees", "180 degrees", "270 degrees") var allowed_rotation_mask := 15 @@ -38,6 +43,16 @@ var allowed_rotation_mask := 15 @export var south_allowed_neighbor_tags := PackedStringArray() @export var west_allowed_neighbor_tags := PackedStringArray() @export_group("") +## Optional canonical descriptions of the visible terrain surface at each +## edge. Empty edges inherit the definition's ordinary tags. Mixed-surface +## chunks use these to prevent a visually grass edge from joining sand (or the +## reverse) even when the chunk itself carries both biome tags. +@export_group("Directional Surface Rules") +@export var north_surface_tags := PackedStringArray() +@export var east_surface_tags := PackedStringArray() +@export var south_surface_tags := PackedStringArray() +@export var west_surface_tags := PackedStringArray() +@export_group("") ## When non-empty, at least this many in-grid cardinal neighbors must carry one ## of the listed tags. This supports statements such as "three sides around the ## spawn must be safe grass" without hard-coding a particular chunk ID. @@ -106,6 +121,28 @@ func directional_allowed_neighbor_tags( return PackedStringArray() +func surface_tags_for_edge( + edge: TerrainChunkTopology.Edge, +) -> PackedStringArray: + var edge_tags := directional_surface_tags(edge) + return tags if edge_tags.is_empty() else edge_tags + + +func directional_surface_tags( + edge: TerrainChunkTopology.Edge, +) -> PackedStringArray: + match edge: + TerrainChunkTopology.Edge.NORTH: + return north_surface_tags + TerrainChunkTopology.Edge.EAST: + return east_surface_tags + TerrainChunkTopology.Edge.SOUTH: + return south_surface_tags + TerrainChunkTopology.Edge.WEST: + return west_surface_tags + return PackedStringArray() + + func _allows_neighbor_with_tags( neighbor: TerrainChunkDefinition, allowed_tags: PackedStringArray, diff --git a/world/generation/terrain_chunk_generator.gd b/world/generation/terrain_chunk_generator.gd index 091179e..8f6235d 100644 --- a/world/generation/terrain_chunk_generator.gd +++ b/world/generation/terrain_chunk_generator.gd @@ -7,6 +7,9 @@ const CONSTRAINT_PROFILE_QUANTIZATION := 0.001 const CANDIDATE_WEIGHT_SCALE := 10000.0 const LARGE_GRID_LIGHTWEIGHT_THRESHOLD := 128 const LARGE_GRID_PROPAGATION_INTERVAL := 8 +# Keep packed domain bits below the signed 64-bit sign bit. Catalogs larger +# than this remain correct through the unpacked solver path. +const MAX_PACKED_SOLVER_VARIANTS := 62 @export var catalog: TerrainChunkCatalog @export var grid_size := Vector2i(7, 7) @@ -16,6 +19,13 @@ const LARGE_GRID_PROPAGATION_INTERVAL := 8 @export var show_chunk_labels := false @export var force_center_chunk_id: StringName = &"chunk_0000" @export var required_chunk_ids := PackedStringArray() +@export_group("Grass-Sand Smoothing") +@export var grass_sand_smoothing_enabled := false +@export var smoothing_grass_chunk_id: StringName = &"chunk_0000" +@export var smoothing_sand_chunk_id: StringName = &"chunk_0001" +@export var smoothing_diagonal_chunk_id: StringName = &"chunk_0013" +@export_range(1, 64, 1) var maximum_smoothing_placements := 16 +@export_group("") @export_group("Elevated Inland Feature") @export var elevated_cliff_feature_enabled := false @export var elevated_cliff_base_chunk_id: StringName = &"chunk_0000" @@ -37,10 +47,22 @@ var _variants: Array[TerrainChunkVariant] = [] # Rotations with identical terrain edges and connector directions share one # solver candidate, then resolve back to an authored rotation after solving. var _solver_variants: Array[TerrainChunkVariant] = [] +var _solver_definitions: Array[TerrainChunkDefinition] = [] +var _solver_variants_by_definition: Dictionary[StringName, Array] = {} +var _definition_variant_masks: Dictionary[StringName, int] = {} +var _required_neighbor_variant_masks: Dictionary[StringName, int] = {} var _equivalent_rotations: Dictionary[String, PackedInt32Array] = {} var _solver_variant_indices: Dictionary = {} var _edge_compatibility := PackedByteArray() +var _edge_compatibility_masks := PackedInt64Array() +var _static_cell_candidate_masks := PackedInt64Array() +var _neighbor_indices: Array[PackedInt32Array] = [] var _placements: Array[TerrainChunkVariant] = [] +var _placement_counts: Dictionary[StringName, int] = {} +var _unfilled_cells := 0 +var _required_stable_ids: Array[StringName] = [] +var _selection_missing_required_count := 0 +var _selection_missing_required_mask := 0 var _random := RandomNumberGenerator.new() var _generated_chunks: Node3D var _backtrack_count := 0 @@ -60,25 +82,27 @@ func generate() -> bool: previous_placements.assign(_placements) _random.seed = generation_seed _backtrack_count = 0 - _placements.clear() - _placements.resize(grid_size.x * grid_size.y) + _reset_placements(grid_size.x * grid_size.y) if not _prepare_elevated_feature_region(): - _placements.assign(previous_placements) + _assign_placements(previous_placements) return false if not _solve_cell(0): - _placements.assign(previous_placements) + _assign_placements(previous_placements) push_error( "Terrain generation could not solve a %dx%d grid with seed %d." % [grid_size.x, grid_size.y, generation_seed] ) return false _resolve_equivalent_rotations() + if not _apply_grass_sand_smoothing(): + _assign_placements(previous_placements) + return false if not _apply_elevated_feature(): - _placements.assign(previous_placements) + _assign_placements(previous_placements) return false var solution_root := _build_solution_root() if solution_root == null: - _placements.assign(previous_placements) + _assign_placements(previous_placements) return false _replace_generated_chunks(solution_root) var summary := _build_summary() @@ -112,11 +136,11 @@ func generate_from_placement_keys(keys: PackedStringArray) -> bool: var previous_placements: Array[TerrainChunkVariant] = [] previous_placements.assign(_placements) - _placements.assign(resolved) + _assign_placements(resolved) _backtrack_count = 0 var solution_root := _build_solution_root() if solution_root == null: - _placements.assign(previous_placements) + _assign_placements(previous_placements) return false _replace_generated_chunks(solution_root) var summary := _build_summary() @@ -199,6 +223,10 @@ func _prepare_catalog() -> bool: _variants.clear() _solver_variants.clear() + _solver_definitions.clear() + _solver_variants_by_definition.clear() + _definition_variant_masks.clear() + _required_neighbor_variant_masks.clear() _equivalent_rotations.clear() _solver_variant_indices.clear() for definition: TerrainChunkDefinition in catalog.definitions: @@ -214,7 +242,10 @@ func _prepare_catalog() -> bool: return false for variant: TerrainChunkVariant in analyzed: _variants.append(variant) - if definition.overlay_only: + if ( + definition.overlay_only + or not definition.participates_in_base_solver + ): continue var constraint_key := _constraint_key(variant) var rotations: PackedInt32Array = _equivalent_rotations.get( @@ -232,11 +263,77 @@ func _prepare_catalog() -> bool: push_error("Terrain chunk catalog produced no base-terrain solver variants.") return false for index: int in _solver_variants.size(): - _solver_variant_indices[_solver_variants[index]] = index + var variant := _solver_variants[index] + _solver_variant_indices[variant] = index + var definition_variants: Array = _solver_variants_by_definition.get( + variant.definition.stable_id, + [], + ) + definition_variants.append(variant) + if definition_variants.size() == 1: + _solver_definitions.append(variant.definition) + _solver_variants_by_definition[ + variant.definition.stable_id + ] = definition_variants + if index < MAX_PACKED_SOLVER_VARIANTS: + _definition_variant_masks[variant.definition.stable_id] = ( + int( + _definition_variant_masks.get( + variant.definition.stable_id, + 0, + ) + ) + | (1 << index) + ) _build_edge_compatibility_cache() return true +func _reset_placements(cell_count: int) -> void: + _placements.clear() + _placements.resize(cell_count) + _placement_counts.clear() + _unfilled_cells = cell_count + + +func _assign_placements(layout: Array[TerrainChunkVariant]) -> void: + _placements.assign(layout) + _rebuild_placement_tracking() + + +func _rebuild_placement_tracking() -> void: + _placement_counts.clear() + _unfilled_cells = 0 + for placement: TerrainChunkVariant in _placements: + if placement == null: + _unfilled_cells += 1 + continue + _adjust_placement_count(placement.definition.stable_id, 1) + + +func _set_placement(index: int, placement: TerrainChunkVariant) -> void: + var previous := _placements[index] + if previous == placement: + return + if previous == null: + _unfilled_cells -= 1 + else: + _adjust_placement_count(previous.definition.stable_id, -1) + _placements[index] = placement + if placement == null: + _unfilled_cells += 1 + else: + _adjust_placement_count(placement.definition.stable_id, 1) + + +func _adjust_placement_count(stable_id: StringName, difference: int) -> void: + var updated := int(_placement_counts.get(stable_id, 0)) + difference + if updated <= 0: + _placement_counts.erase(stable_id) + return + _placement_counts[stable_id] = updated + + func _constraint_key(variant: TerrainChunkVariant) -> String: return "%s|%s" % [ variant.definition.stable_id, @@ -248,6 +345,10 @@ func _build_edge_compatibility_cache() -> void: var variant_count := _solver_variants.size() _edge_compatibility.resize(variant_count * 4 * variant_count) _edge_compatibility.fill(0) + _edge_compatibility_masks.resize( + variant_count * 4 if _packed_solver_domains_are_available() else 0 + ) + _edge_compatibility_masks.fill(0) for first_index: int in variant_count: var first := _solver_variants[first_index] for first_edge_value: int in TerrainChunkTopology.Edge.values(): @@ -260,14 +361,19 @@ func _build_edge_compatibility_cache() -> void: first_edge, second_index, ) - _edge_compatibility[cache_index] = int( - _calculate_edge_compatibility( + var compatible := _calculate_edge_compatibility( first, first_edge, second, second_edge, - ) ) + _edge_compatibility[cache_index] = int(compatible) + if compatible and _packed_solver_domains_are_available(): + var mask_index := first_index * 4 + int(first_edge) + _edge_compatibility_masks[mask_index] = ( + _edge_compatibility_masks[mask_index] + | (1 << second_index) + ) func _edge_compatibility_index( @@ -284,11 +390,13 @@ func _edge_compatibility_index( func _validate_generation_requirements() -> bool: var seen_required: Dictionary[StringName, bool] = {} + _required_stable_ids.clear() for required_id_value: String in required_chunk_ids: var required_id := StringName(required_id_value) if seen_required.has(required_id): continue seen_required[required_id] = true + _required_stable_ids.append(required_id) var definition := catalog.definition_for_id(required_id) if definition == null: push_error("Required terrain chunk %s is not in the catalog." % required_id) @@ -296,6 +404,15 @@ func _validate_generation_requirements() -> bool: if definition.maximum_placements == 0: push_error("Required terrain chunk %s permits no placements." % required_id) return false + if ( + definition.overlay_only + or not definition.participates_in_base_solver + ): + push_error( + "Required terrain chunk %s does not participate in the base solver." + % required_id + ) + return false if seen_required.size() > grid_size.x * grid_size.y: push_error("The terrain grid has fewer cells than required chunk IDs.") return false @@ -325,36 +442,52 @@ func _solve_cell(placed_count: int) -> bool: ) if _backtrack_count >= maximum_backtracks: return false + _refresh_selection_required_cache() if not _requirements_can_still_be_satisfied(): return false var next_cell := _select_next_cell(placed_count) var index := int(next_cell.get("index", -1)) if index < 0: return false - var candidates := next_cell.get( - "candidates", - [], - ) as Array[TerrainChunkVariant] + var candidates: Array[TerrainChunkVariant] = [] + candidates.assign(next_cell.get("candidates", [])) for candidate: TerrainChunkVariant in _weighted_candidate_order( candidates, index, ): - _placements[index] = candidate + _set_placement(index, candidate) if _solve_cell(placed_count + 1): return true - _placements[index] = null + _set_placement(index, null) _backtrack_count += 1 if _backtrack_count >= maximum_backtracks: break return false +func _refresh_selection_required_cache() -> void: + _selection_missing_required_count = 0 + _selection_missing_required_mask = 0 + for stable_id: StringName in _required_stable_ids: + if int(_placement_counts.get(stable_id, 0)) > 0: + continue + _selection_missing_required_count += 1 + _selection_missing_required_mask |= int( + _definition_variant_masks.get(stable_id, 0) + ) + + func _select_next_cell(placed_count: int) -> Dictionary: - if ( - _placements.size() >= LARGE_GRID_LIGHTWEIGHT_THRESHOLD - and placed_count % LARGE_GRID_PROPAGATION_INTERVAL != 0 - ): - return _select_next_large_grid_cell() + if _packed_solver_domains_are_available(): + if ( + _placements.size() >= LARGE_GRID_LIGHTWEIGHT_THRESHOLD + and placed_count % LARGE_GRID_PROPAGATION_INTERVAL != 0 + ): + return _select_next_large_grid_cell() + return _select_next_large_grid_propagated_cell() + if _placements.size() >= LARGE_GRID_LIGHTWEIGHT_THRESHOLD: + if placed_count % LARGE_GRID_PROPAGATION_INTERVAL != 0: + return _select_next_large_grid_cell_unpacked() # Rebuild and propagate the small domain table on every branch. This catches # unsupported connector chains before they turn into a deep recursive dead # end, while keeping placement state simple and deterministic. @@ -385,6 +518,221 @@ func _select_next_cell(placed_count: int) -> Dictionary: return {"index": best_index, "candidates": best_candidates} +func _select_next_large_grid_propagated_cell() -> Dictionary: + var domains: Dictionary[int, int] = {} + for index: int in _placements.size(): + if _placements[index] != null: + continue + var candidate_mask := _compatible_candidate_mask(index) + if candidate_mask == 0: + return {"index": index, "candidates": []} + domains[index] = candidate_mask + if not _propagate_domain_masks(domains): + return {"index": -1, "candidates": []} + + var best_index := -1 + var best_mask := 0 + var best_size := _solver_variants.size() + 1 + for index: int in domains: + var candidate_mask := domains[index] + var candidate_count := _mask_bit_count(candidate_mask) + if best_index < 0 or candidate_count < best_size: + best_index = index + best_mask = candidate_mask + best_size = candidate_count + if candidate_count == 1: + break + return { + "index": best_index, + "candidates": _candidates_from_mask(best_mask), + } + + +func _candidates_from_mask(candidate_mask: int) -> Array[TerrainChunkVariant]: + var result: Array[TerrainChunkVariant] = [] + for candidate_index: int in _solver_variants.size(): + if (candidate_mask & (1 << candidate_index)) != 0: + result.append(_solver_variants[candidate_index]) + return result + + +func _mask_bit_count(candidate_mask: int) -> int: + var count := 0 + var remaining := candidate_mask + while remaining != 0: + remaining &= remaining - 1 + count += 1 + return count + + +func _propagate_domain_masks(domains: Dictionary[int, int]) -> bool: + var changed := true + while changed: + changed = false + for index: int in domains.keys(): + var candidates := domains[index] + var supported := 0 + for candidate_index: int in _solver_variants.size(): + var candidate_bit := 1 << candidate_index + if ( + (candidates & candidate_bit) != 0 + and _domain_candidate_has_mask_support( + candidate_index, + index, + domains, + ) + ): + supported |= candidate_bit + if supported == 0: + return false + if supported != candidates: + domains[index] = supported + changed = true + var required_result := _constrain_required_domain_masks(domains) + if required_result < 0: + return false + if required_result > 0: + changed = true + if not _placed_neighbor_requirements_have_domain_mask_support(domains): + return false + return true + + +func _domain_candidate_has_mask_support( + candidate_index: int, + index: int, + domains: Dictionary[int, int], +) -> bool: + var candidate := _solver_variants[candidate_index] + var possible_required_neighbors := 0 + var required_neighbor_mask := _required_neighbor_variant_mask( + candidate.definition + ) + for edge_value: int in TerrainChunkTopology.Edge.values(): + var edge := edge_value as TerrainChunkTopology.Edge + var neighbor_index := _neighbor_indices[index][edge_value] + if neighbor_index < 0: + continue + var placed_neighbor := _placements[neighbor_index] + if placed_neighbor != null: + if _definition_matches_required_neighbor_tags( + candidate.definition, + placed_neighbor.definition, + ): + possible_required_neighbors += 1 + continue + var support_mask := ( + int(domains.get(neighbor_index, 0)) + & _edge_compatibility_masks[candidate_index * 4 + int(edge)] + ) + if ( + candidate.definition.maximum_placements >= 0 + and _definition_placement_count(candidate.definition) + 2 + > candidate.definition.maximum_placements + ): + support_mask &= ~int( + _definition_variant_masks.get( + candidate.definition.stable_id, + 0, + ) + ) + if support_mask == 0: + return false + if (support_mask & required_neighbor_mask) != 0: + possible_required_neighbors += 1 + return ( + possible_required_neighbors + >= candidate.definition.minimum_required_neighbors + ) + + +func _required_neighbor_variant_mask( + definition: TerrainChunkDefinition, +) -> int: + if _required_neighbor_variant_masks.has(definition.stable_id): + return _required_neighbor_variant_masks[definition.stable_id] + var result := 0 + for candidate_index: int in _solver_variants.size(): + if _definition_matches_required_neighbor_tags( + definition, + _solver_variants[candidate_index].definition, + ): + result |= 1 << candidate_index + _required_neighbor_variant_masks[definition.stable_id] = result + return result + + +func _constrain_required_domain_masks( + domains: Dictionary[int, int], +) -> int: + var changed := false + for stable_id: StringName in _missing_required_ids(): + var definition_mask := int(_definition_variant_masks.get(stable_id, 0)) + var supporting_cell := -1 + var supporting_cell_count := 0 + for index: int in domains: + if (domains[index] & definition_mask) == 0: + continue + supporting_cell = index + supporting_cell_count += 1 + if supporting_cell_count > 1: + break + if supporting_cell_count == 0: + return -1 + if supporting_cell_count != 1: + continue + var forced_mask := domains[supporting_cell] & definition_mask + if forced_mask == 0: + return -1 + if forced_mask != domains[supporting_cell]: + domains[supporting_cell] = forced_mask + changed = true + return 1 if changed else 0 + + +func _placed_neighbor_requirements_have_domain_mask_support( + domains: Dictionary[int, int], +) -> bool: + for index: int in _placements.size(): + var placement := _placements[index] + if placement == null or placement.definition.minimum_required_neighbors <= 0: + continue + var placement_variant_index := int( + _solver_variant_indices.get(placement, -1) + ) + if placement_variant_index < 0: + return false + var required_neighbor_mask := _required_neighbor_variant_mask( + placement.definition + ) + var possible_neighbors := 0 + for edge_value: int in TerrainChunkTopology.Edge.values(): + var edge := edge_value as TerrainChunkTopology.Edge + var neighbor_index := _neighbor_indices[index][edge_value] + if neighbor_index < 0: + continue + var neighbor := _placements[neighbor_index] + if neighbor != null: + if _definition_matches_required_neighbor_tags( + placement.definition, + neighbor.definition, + ): + possible_neighbors += 1 + continue + var compatible_required_mask := ( + int(domains.get(neighbor_index, 0)) + & required_neighbor_mask + & _edge_compatibility_masks[ + placement_variant_index * 4 + int(edge) + ] + ) + if compatible_required_mask != 0: + possible_neighbors += 1 + if possible_neighbors < placement.definition.minimum_required_neighbors: + return false + return true + + func _select_next_large_grid_cell() -> Dictionary: # Full all-cell arc propagation scales cubically as the map grows. Large # worlds instead use the same compatibility checks with a frontier-aware MRV @@ -395,7 +743,33 @@ func _select_next_large_grid_cell() -> Dictionary: for index: int in _placements.size(): if _placements[index] != null: continue - var candidates := _compatible_candidates(index) + var candidate_mask := _compatible_candidate_mask(index) + if candidate_mask == 0: + return {"index": index, "candidates": []} + var candidates := _candidates_from_mask(candidate_mask) + var placed_neighbors := _placed_neighbor_count(index) + if ( + best_index < 0 + or candidates.size() < best_candidates.size() + or ( + candidates.size() == best_candidates.size() + and placed_neighbors > best_placed_neighbors + ) + ): + best_index = index + best_candidates = candidates + best_placed_neighbors = placed_neighbors + return {"index": best_index, "candidates": best_candidates} + + +func _select_next_large_grid_cell_unpacked() -> Dictionary: + var best_index := -1 + var best_candidates: Array[TerrainChunkVariant] = [] + var best_placed_neighbors := -1 + for index: int in _placements.size(): + if _placements[index] != null: + continue + var candidates := _compatible_candidates_unpacked(index) if candidates.is_empty(): return {"index": index, "candidates": candidates} var placed_neighbors := _placed_neighbor_count(index) @@ -414,20 +788,10 @@ func _select_next_large_grid_cell() -> Dictionary: func _placed_neighbor_count(index: int) -> int: - var coordinate := Vector2i(index % grid_size.x, index / grid_size.x) var count := 0 - for edge_value: int in TerrainChunkTopology.Edge.values(): - var neighbor_coordinate := ( - coordinate - + TerrainChunkTopology.grid_offset( - edge_value as TerrainChunkTopology.Edge - ) - ) - if not _coordinate_is_inside_grid(neighbor_coordinate): + for neighbor_index: int in _neighbor_indices[index]: + if neighbor_index < 0: continue - var neighbor_index := ( - neighbor_coordinate.y * grid_size.x + neighbor_coordinate.x - ) if _placements[neighbor_index] != null: count += 1 return count @@ -622,42 +986,99 @@ func _constrain_required_domains(domains: Dictionary) -> int: func _compatible_candidates(index: int) -> Array[TerrainChunkVariant]: + if not _packed_solver_domains_are_available(): + return _compatible_candidates_unpacked(index) + return _candidates_from_mask(_compatible_candidate_mask(index)) + + +func _compatible_candidates_unpacked( + index: int, +) -> Array[TerrainChunkVariant]: var result: Array[TerrainChunkVariant] = [] var missing_required := _missing_required_ids() - var remaining_cells := _unfilled_cell_count() - var must_place_missing_required := missing_required.size() >= remaining_cells + var must_place_missing_required := ( + missing_required.size() >= _unfilled_cells + ) for candidate: TerrainChunkVariant in _solver_variants: if ( must_place_missing_required and not missing_required.has(candidate.definition.stable_id) ): continue - if not _candidate_can_occupy_cell(candidate, index): - continue - result.append(candidate) + if _candidate_can_occupy_cell(candidate, index): + result.append(candidate) return result -func _candidate_can_occupy_cell( +func _packed_solver_domains_are_available() -> bool: + return _solver_variants.size() <= MAX_PACKED_SOLVER_VARIANTS + + +func _compatible_candidate_mask(index: int) -> int: + if index < 0 or index >= _static_cell_candidate_masks.size(): + return 0 + var result := int(_static_cell_candidate_masks[index]) + if _selection_missing_required_count >= _unfilled_cells: + result &= _selection_missing_required_mask + if result == 0: + return 0 + result = _mask_matching_placed_neighbors(result, index) + if result == 0: + return 0 + + for definition: TerrainChunkDefinition in _solver_definitions: + var definition_mask := int( + _definition_variant_masks.get(definition.stable_id, 0) + ) + if (result & definition_mask) == 0: + continue + var definition_variants: Array = _solver_variants_by_definition.get( + definition.stable_id, + [], + ) + if ( + definition_variants.is_empty() + or not _candidate_dynamic_cell_rules_are_satisfied( + definition_variants[0] as TerrainChunkVariant, + index, + ) + ): + result &= ~definition_mask + return result + + +func _mask_matching_placed_neighbors(candidate_mask: int, index: int) -> int: + var result := candidate_mask + for edge_value: int in TerrainChunkTopology.Edge.values(): + var neighbor_index := _neighbor_indices[index][edge_value] + if neighbor_index < 0: + continue + var neighbor := _placements[neighbor_index] + if neighbor == null: + continue + var neighbor_variant_index := int( + _solver_variant_indices.get(neighbor, -1) + ) + if neighbor_variant_index < 0: + return 0 + var neighbor_edge := TerrainChunkTopology.opposite_edge( + edge_value as TerrainChunkTopology.Edge + ) + result &= _edge_compatibility_masks[ + neighbor_variant_index * 4 + int(neighbor_edge) + ] + if result == 0: + return 0 + return result + + +func _candidate_dynamic_cell_rules_are_satisfied( candidate: TerrainChunkVariant, index: int, ) -> bool: if not _definition_has_capacity(candidate.definition): return false - var coordinate := Vector2i(index % grid_size.x, index / grid_size.x) - if ( - _elevated_feature_reserved_grass_indices.has(index) - and candidate.definition.stable_id != elevated_cliff_base_chunk_id - ): - return false - var center := Vector2i(grid_size.x / 2, grid_size.y / 2) - var center_index := center.y * grid_size.x + center.x - if ( - coordinate == center - and force_center_chunk_id != &"" - and candidate.definition.stable_id != force_center_chunk_id - ): - return false + var center_index := (grid_size.y / 2) * grid_size.x + grid_size.x / 2 if ( index != center_index and _placements[center_index] == null @@ -667,17 +1088,96 @@ func _candidate_can_occupy_cell( >= candidate.definition.maximum_placements - 1 ): return false + return ( + _candidate_neighbor_requirement_can_still_be_met_at_index( + candidate.definition, + index, + ) + and _candidate_preserves_placed_neighbor_requirements_at_index( + candidate.definition, + index, + ) + ) + + +func _candidate_neighbor_requirement_can_still_be_met_at_index( + definition: TerrainChunkDefinition, + index: int, +) -> bool: + if definition.minimum_required_neighbors <= 0: + return true + var possible_neighbors := 0 + for neighbor_index: int in _neighbor_indices[index]: + if neighbor_index < 0: + continue + var neighbor := _placements[neighbor_index] + if ( + neighbor == null + or _definition_matches_required_neighbor_tags( + definition, + neighbor.definition, + ) + ): + possible_neighbors += 1 + return possible_neighbors >= definition.minimum_required_neighbors + + +func _candidate_preserves_placed_neighbor_requirements_at_index( + candidate_definition: TerrainChunkDefinition, + candidate_index: int, +) -> bool: + for neighbor_index: int in _neighbor_indices[candidate_index]: + if neighbor_index < 0: + continue + var neighbor := _placements[neighbor_index] + if neighbor == null or neighbor.definition.minimum_required_neighbors <= 0: + continue + var possible_neighbors := 0 + for requirement_neighbor_index: int in _neighbor_indices[neighbor_index]: + if requirement_neighbor_index < 0: + continue + if requirement_neighbor_index == candidate_index: + if _definition_matches_required_neighbor_tags( + neighbor.definition, + candidate_definition, + ): + possible_neighbors += 1 + continue + var requirement_neighbor := _placements[requirement_neighbor_index] + if ( + requirement_neighbor == null + or _definition_matches_required_neighbor_tags( + neighbor.definition, + requirement_neighbor.definition, + ) + ): + possible_neighbors += 1 + if possible_neighbors < neighbor.definition.minimum_required_neighbors: + return false + return true + + +func _candidate_can_occupy_cell( + candidate: TerrainChunkVariant, + index: int, +) -> bool: + var coordinate := Vector2i(index % grid_size.x, index / grid_size.x) + if ( + _elevated_feature_reserved_grass_indices.has(index) + and candidate.definition.stable_id != elevated_cliff_base_chunk_id + ): + return false + var center := Vector2i(grid_size.x / 2, grid_size.y / 2) + if ( + coordinate == center + and force_center_chunk_id != &"" + and candidate.definition.stable_id != force_center_chunk_id + ): + return false return ( _variant_respects_ocean_boundary(candidate, coordinate) and _candidate_matches_placed_neighbors(candidate, coordinate) - and _candidate_neighbor_requirement_can_still_be_met( - candidate.definition, - coordinate, - ) - and _candidate_preserves_placed_neighbor_requirements( - candidate.definition, - coordinate, - ) + and _candidate_dynamic_cell_rules_are_satisfied(candidate, index) ) @@ -685,6 +1185,7 @@ func _prepare_elevated_feature_region() -> bool: _elevated_feature_center = Vector2i(-1, -1) _elevated_feature_reserved_grass_indices.clear() if not elevated_cliff_feature_enabled: + _build_grid_solver_caches() return true for stable_id: StringName in [ elevated_cliff_base_chunk_id, @@ -733,9 +1234,155 @@ func _prepare_elevated_feature_region() -> bool: _elevated_feature_reserved_grass_indices[ coordinate.y * grid_size.x + coordinate.x ] = true + _build_grid_solver_caches() return true +func _build_grid_solver_caches() -> void: + _neighbor_indices.clear() + _neighbor_indices.resize(_placements.size()) + _static_cell_candidate_masks.resize(_placements.size()) + _static_cell_candidate_masks.fill(0) + var center := Vector2i(grid_size.x / 2, grid_size.y / 2) + for index: int in _placements.size(): + var coordinate := Vector2i(index % grid_size.x, index / grid_size.x) + var neighbors := PackedInt32Array([-1, -1, -1, -1]) + for edge_value: int in TerrainChunkTopology.Edge.values(): + var neighbor_coordinate := ( + coordinate + + TerrainChunkTopology.grid_offset( + edge_value as TerrainChunkTopology.Edge + ) + ) + if _coordinate_is_inside_grid(neighbor_coordinate): + neighbors[edge_value] = ( + neighbor_coordinate.y * grid_size.x + + neighbor_coordinate.x + ) + _neighbor_indices[index] = neighbors + + if not _packed_solver_domains_are_available(): + continue + var candidate_mask := 0 + for candidate_index: int in _solver_variants.size(): + var candidate := _solver_variants[candidate_index] + if ( + _elevated_feature_reserved_grass_indices.has(index) + and candidate.definition.stable_id + != elevated_cliff_base_chunk_id + ): + continue + if ( + coordinate == center + and force_center_chunk_id != &"" + and candidate.definition.stable_id != force_center_chunk_id + ): + continue + if not _variant_respects_ocean_boundary(candidate, coordinate): + continue + candidate_mask |= 1 << candidate_index + _static_cell_candidate_masks[index] = candidate_mask + + +func _apply_grass_sand_smoothing() -> bool: + if not grass_sand_smoothing_enabled: + return true + var diagonal_definition := catalog.definition_for_id( + smoothing_diagonal_chunk_id + ) + if diagonal_definition == null: + push_error( + "Grass-sand smoothing references missing chunk %s." + % smoothing_diagonal_chunk_id + ) + return false + if diagonal_definition.participates_in_base_solver: + push_error( + "Grass-sand smoothing chunk %s must be excluded from the base solver." + % smoothing_diagonal_chunk_id + ) + return false + + var candidates: Array[Dictionary] = [] + var center := Vector2i(grid_size.x / 2, grid_size.y / 2) + for index: int in _placements.size(): + var placement := _placements[index] + if placement == null or placement.definition.stable_id not in [ + smoothing_grass_chunk_id, + smoothing_sand_chunk_id, + ]: + continue + var coordinate := Vector2i(index % grid_size.x, index / grid_size.x) + if ( + _coordinate_is_on_boundary(coordinate) + or _elevated_feature_reserved_grass_indices.has(index) + or ( + absi(coordinate.x - center.x) + + absi(coordinate.y - center.y) + <= 1 + ) + ): + continue + for quarter_turns: int in 4: + var diagonal := _authored_variant( + diagonal_definition, + quarter_turns, + ) + if ( + diagonal != null + and _candidate_matches_placed_neighbors(diagonal, coordinate) + ): + candidates.append({"index": index, "variant": diagonal}) + break + + var smoothing_random := RandomNumberGenerator.new() + smoothing_random.seed = generation_seed ^ 0xD1A60A1 + for candidate_index: int in range(candidates.size() - 1, 0, -1): + var swap_index := smoothing_random.randi_range(0, candidate_index) + var temporary := candidates[candidate_index] + candidates[candidate_index] = candidates[swap_index] + candidates[swap_index] = temporary + + var previous_placements: Array[TerrainChunkVariant] = [] + previous_placements.assign(_placements) + var replacement_count := 0 + for candidate: Dictionary in candidates: + if replacement_count >= maximum_smoothing_placements: + break + var index := int(candidate["index"]) + var coordinate := Vector2i(index % grid_size.x, index / grid_size.x) + var diagonal := candidate["variant"] as TerrainChunkVariant + if ( + diagonal == null + or not _candidate_matches_placed_neighbors(diagonal, coordinate) + or not _replacement_preserves_required_chunk(_placements[index]) + ): + continue + _set_placement(index, diagonal) + replacement_count += 1 + + var validation_error := _resolved_layout_validation_error(_placements) + if not validation_error.is_empty(): + _assign_placements(previous_placements) + push_error( + "Grass-sand smoothing produced an invalid layout: " + + validation_error + ) + return false + return true + + +func _replacement_preserves_required_chunk( + original: TerrainChunkVariant, +) -> bool: + if original == null: + return false + var stable_id := original.definition.stable_id + if String(stable_id) not in required_chunk_ids: + return true + return _definition_placement_count(original.definition) > 1 + + func _apply_elevated_feature() -> bool: if not elevated_cliff_feature_enabled: return true @@ -781,7 +1428,10 @@ func _apply_elevated_feature() -> bool: ) return false var coordinate := _elevated_feature_center + (spec["offset"] as Vector2i) - _placements[coordinate.y * grid_size.x + coordinate.x] = variant + _set_placement( + coordinate.y * grid_size.x + coordinate.x, + variant, + ) var validation_error := _resolved_layout_validation_error(_placements) if not validation_error.is_empty(): push_error("Elevated cliff feature is invalid: " + validation_error) @@ -851,101 +1501,6 @@ func _coordinate_is_inside_grid(coordinate: Vector2i) -> bool: ) -func _candidate_neighbor_requirement_can_still_be_met( - definition: TerrainChunkDefinition, - coordinate: Vector2i, -) -> bool: - if definition.minimum_required_neighbors <= 0: - return true - var possible_neighbors := 0 - for edge_value: int in TerrainChunkTopology.Edge.values(): - var neighbor_coordinate := ( - coordinate - + TerrainChunkTopology.grid_offset( - edge_value as TerrainChunkTopology.Edge - ) - ) - if not _coordinate_is_inside_grid(neighbor_coordinate): - continue - var neighbor := _placements[ - neighbor_coordinate.y * grid_size.x + neighbor_coordinate.x - ] - if ( - neighbor == null - or _definition_matches_required_neighbor_tags( - definition, - neighbor.definition, - ) - ): - possible_neighbors += 1 - return possible_neighbors >= definition.minimum_required_neighbors - - -func _candidate_preserves_placed_neighbor_requirements( - definition: TerrainChunkDefinition, - coordinate: Vector2i, -) -> bool: - for edge_value: int in TerrainChunkTopology.Edge.values(): - var neighbor_coordinate := ( - coordinate - + TerrainChunkTopology.grid_offset( - edge_value as TerrainChunkTopology.Edge - ) - ) - if not _coordinate_is_inside_grid(neighbor_coordinate): - continue - var neighbor := _placements[ - neighbor_coordinate.y * grid_size.x + neighbor_coordinate.x - ] - if neighbor == null or neighbor.definition.minimum_required_neighbors <= 0: - continue - if not _placed_requirement_can_still_be_met_with_candidate( - neighbor.definition, - neighbor_coordinate, - definition, - coordinate, - ): - return false - return true - - -func _placed_requirement_can_still_be_met_with_candidate( - definition: TerrainChunkDefinition, - coordinate: Vector2i, - candidate_definition: TerrainChunkDefinition, - candidate_coordinate: Vector2i, -) -> bool: - var possible_neighbors := 0 - for edge_value: int in TerrainChunkTopology.Edge.values(): - var neighbor_coordinate := ( - coordinate - + TerrainChunkTopology.grid_offset( - edge_value as TerrainChunkTopology.Edge - ) - ) - if not _coordinate_is_inside_grid(neighbor_coordinate): - continue - if neighbor_coordinate == candidate_coordinate: - if _definition_matches_required_neighbor_tags( - definition, - candidate_definition, - ): - possible_neighbors += 1 - continue - var neighbor := _placements[ - neighbor_coordinate.y * grid_size.x + neighbor_coordinate.x - ] - if ( - neighbor == null - or _definition_matches_required_neighbor_tags( - definition, - neighbor.definition, - ) - ): - possible_neighbors += 1 - return possible_neighbors >= definition.minimum_required_neighbors - - func _definition_has_capacity(definition: TerrainChunkDefinition) -> bool: if definition.maximum_placements < 0: return true @@ -953,11 +1508,9 @@ func _definition_has_capacity(definition: TerrainChunkDefinition) -> bool: func _definition_placement_count(definition: TerrainChunkDefinition) -> int: - var count := 0 - for placement: TerrainChunkVariant in _placements: - if placement != null and placement.definition == definition: - count += 1 - return count + if definition == null: + return 0 + return int(_placement_counts.get(definition.stable_id, 0)) func _edges_are_compatible( @@ -1024,11 +1577,30 @@ func _calculate_edge_compatibility( or (first_inlet and second_outlet) ) return ( - first.allows_non_water_neighbor_on_edge(second, first_edge) + _edge_surfaces_are_compatible( + first, + first_edge, + second, + second_edge, + ) + and first.allows_non_water_neighbor_on_edge(second, first_edge) and second.allows_non_water_neighbor_on_edge(first, second_edge) ) +func _edge_surfaces_are_compatible( + first: TerrainChunkVariant, + first_edge: TerrainChunkTopology.Edge, + second: TerrainChunkVariant, + second_edge: TerrainChunkTopology.Edge, +) -> bool: + var first_tags := first.surface_tags(first_edge) + var second_tags := second.surface_tags(second_edge) + if first_tags.is_empty() or second_tags.is_empty(): + return true + return TerrainChunkDefinition.has_any_tag(first_tags, second_tags) + + func _variant_edge_has_connector( variant: TerrainChunkVariant, source_mask: int, @@ -1057,35 +1629,57 @@ func _requirements_can_still_be_satisfied() -> bool: func _definition_has_available_cell( definition: TerrainChunkDefinition, ) -> bool: + var definition_variants: Array = _solver_variants_by_definition.get( + definition.stable_id, + [], + ) + if ( + _packed_solver_domains_are_available() + and not definition_variants.is_empty() + ): + var definition_mask := int( + _definition_variant_masks.get(definition.stable_id, 0) + ) + for index: int in _placements.size(): + if _placements[index] != null: + continue + var candidate_mask := ( + int(_static_cell_candidate_masks[index]) & definition_mask + ) + if candidate_mask == 0: + continue + candidate_mask = _mask_matching_placed_neighbors( + candidate_mask, + index, + ) + if ( + candidate_mask != 0 + and _candidate_dynamic_cell_rules_are_satisfied( + definition_variants[0] as TerrainChunkVariant, + index, + ) + ): + return true + return false for index: int in _placements.size(): if _placements[index] != null: continue - for candidate: TerrainChunkVariant in _solver_variants: - if ( - candidate.definition == definition - and _candidate_can_occupy_cell(candidate, index) - ): + for candidate: TerrainChunkVariant in definition_variants: + if _candidate_can_occupy_cell(candidate, index): return true return false func _unfilled_cell_count() -> int: - var count := 0 - for placement: TerrainChunkVariant in _placements: - if placement == null: - count += 1 - return count + return _unfilled_cells func _missing_required_ids() -> Array[StringName]: - var found: Dictionary[StringName, bool] = {} - for placement: TerrainChunkVariant in _placements: - if placement != null: - found[placement.definition.stable_id] = true var result: Array[StringName] = [] - for required_id_value: String in required_chunk_ids: - var required_id := StringName(required_id_value) - if not found.has(required_id) and not result.has(required_id): + for required_id: StringName in _required_stable_ids: + if ( + int(_placement_counts.get(required_id, 0)) <= 0 + ): result.append(required_id) return result @@ -1335,13 +1929,7 @@ func _walkable_connectivity_validation_error( func _required_chunk_is_missing(stable_id: StringName) -> bool: if not required_chunk_ids.has(String(stable_id)): return false - for placement: TerrainChunkVariant in _placements: - if ( - placement != null - and placement.definition.stable_id == stable_id - ): - return false - return true + return int(_placement_counts.get(stable_id, 0)) <= 0 func _resolve_equivalent_rotations() -> void: diff --git a/world/generation/terrain_chunk_variant.gd b/world/generation/terrain_chunk_variant.gd index efb590a..ca45812 100644 --- a/world/generation/terrain_chunk_variant.gd +++ b/world/generation/terrain_chunk_variant.gd @@ -35,6 +35,10 @@ func allows_non_water_neighbor_on_edge( ) +func surface_tags(edge: TerrainChunkTopology.Edge) -> PackedStringArray: + return definition.surface_tags_for_edge(source_edge(edge)) + + func topology_signature(quantization: float) -> String: var tokens := PackedStringArray() for edge_value: int in TerrainChunkTopology.Edge.values(): @@ -44,9 +48,10 @@ func topology_signature(quantization: float) -> String: func constraint_signature(quantization: float) -> String: - return "%s|neighbors:%s|ocean:%d|in:%d|out:%d" % [ + return "%s|neighbors:%s|surfaces:%s|ocean:%d|in:%d|out:%d" % [ topology_signature(quantization), _neighbor_rule_signature(), + _surface_rule_signature(), rotated_edge_mask(definition.ocean_facing_edges), rotated_edge_mask(definition.water_inlet_edges), rotated_edge_mask(definition.water_outlet_edges), @@ -65,6 +70,16 @@ func _neighbor_rule_signature() -> String: return "/".join(edge_tokens) +func _surface_rule_signature() -> String: + var edge_tokens := PackedStringArray() + for edge_value: int in TerrainChunkTopology.Edge.values(): + var edge := edge_value as TerrainChunkTopology.Edge + var edge_tags: PackedStringArray = surface_tags(edge).duplicate() + edge_tags.sort() + edge_tokens.append(",".join(edge_tags)) + return "/".join(edge_tokens) + + func rotated_edge_mask(source_mask: int) -> int: var result := 0 for edge_value: int in TerrainChunkTopology.Edge.values():