123 lines
4.7 KiB
GDScript
123 lines
4.7 KiB
GDScript
class_name TerrainChunkDefinition
|
|
extends Resource
|
|
|
|
@export var stable_id: StringName
|
|
@export var label := ""
|
|
@export var packed_scene: PackedScene
|
|
@export var primary_mesh_name: StringName
|
|
@export_flags("0 degrees", "90 degrees", "180 degrees", "270 degrees")
|
|
var allowed_rotation_mask := 15
|
|
@export_range(0.01, 100.0, 0.01) var selection_weight := 1.0
|
|
## Negative values allow unlimited placements; zero disables random placement.
|
|
@export_range(-1, 1024, 1) var maximum_placements := -1
|
|
@export var tags := PackedStringArray()
|
|
## Empty permits any non-water neighbor. Otherwise at least one listed tag
|
|
## must be present on the neighboring chunk. Water connectors are governed by
|
|
## their inlet/outlet topology instead.
|
|
@export var allowed_neighbor_tags := PackedStringArray()
|
|
## These tags always reject a non-water neighbor, even when it also carries an
|
|
## allowed tag.
|
|
@export var forbidden_neighbor_tags := PackedStringArray()
|
|
## Optional canonical per-edge overrides for allowed_neighbor_tags. Empty means
|
|
## the edge inherits the chunk-wide list. These describe the visible surface at
|
|
## a seam, such as a grass-backed coastline transition that must not meet sand.
|
|
@export_group("Directional Neighbor Rules")
|
|
@export var north_allowed_neighbor_tags := PackedStringArray()
|
|
@export var east_allowed_neighbor_tags := PackedStringArray()
|
|
@export var south_allowed_neighbor_tags := PackedStringArray()
|
|
@export var west_allowed_neighbor_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.
|
|
@export var required_neighbor_tags := PackedStringArray()
|
|
@export_range(0, 4, 1) var minimum_required_neighbors := 0
|
|
## Matching already-placed neighbors increase this definition's selection
|
|
## weight. This shapes regions without turning a visual preference into a hard
|
|
## generation constraint.
|
|
@export var preferred_neighbor_tags := PackedStringArray()
|
|
## Coastal transition pieces should normally migrate toward the generated
|
|
## region's perimeter while remaining legal in the interior.
|
|
@export var prefers_map_boundary := false
|
|
## Edges that descend from land into the surrounding ocean. Every rotated edge
|
|
## in this mask must face outside the generated grid; it may never meet another
|
|
## terrain chunk.
|
|
@export_flags("North", "East", "South", "West") var ocean_facing_edges := 0
|
|
@export_flags("North", "East", "South", "West") var water_inlet_edges := 0
|
|
@export_flags("North", "East", "South", "West") var water_outlet_edges := 0
|
|
## Optional generated water footprint in the chunk's unrotated local X/Z plane.
|
|
## Zero means this chunk contributes no standalone water surface.
|
|
@export var water_surface_size := Vector2.ZERO
|
|
@export var water_surface_offset := Vector2.ZERO
|
|
|
|
|
|
func allows_quarter_turn(quarter_turns: int) -> bool:
|
|
var normalized_turns := posmod(quarter_turns, 4)
|
|
return (allowed_rotation_mask & (1 << normalized_turns)) != 0
|
|
|
|
|
|
func allows_non_water_neighbor(neighbor: TerrainChunkDefinition) -> bool:
|
|
return _allows_neighbor_with_tags(neighbor, allowed_neighbor_tags)
|
|
|
|
|
|
func allows_non_water_neighbor_on_edge(
|
|
neighbor: TerrainChunkDefinition,
|
|
edge: TerrainChunkTopology.Edge,
|
|
) -> bool:
|
|
return _allows_neighbor_with_tags(
|
|
neighbor,
|
|
allowed_neighbor_tags_for_edge(edge),
|
|
)
|
|
|
|
|
|
func allowed_neighbor_tags_for_edge(
|
|
edge: TerrainChunkTopology.Edge,
|
|
) -> PackedStringArray:
|
|
var edge_tags := directional_allowed_neighbor_tags(edge)
|
|
return allowed_neighbor_tags if edge_tags.is_empty() else edge_tags
|
|
|
|
|
|
func directional_allowed_neighbor_tags(
|
|
edge: TerrainChunkTopology.Edge,
|
|
) -> PackedStringArray:
|
|
match edge:
|
|
TerrainChunkTopology.Edge.NORTH:
|
|
return north_allowed_neighbor_tags
|
|
TerrainChunkTopology.Edge.EAST:
|
|
return east_allowed_neighbor_tags
|
|
TerrainChunkTopology.Edge.SOUTH:
|
|
return south_allowed_neighbor_tags
|
|
TerrainChunkTopology.Edge.WEST:
|
|
return west_allowed_neighbor_tags
|
|
return PackedStringArray()
|
|
|
|
|
|
func _allows_neighbor_with_tags(
|
|
neighbor: TerrainChunkDefinition,
|
|
allowed_tags: PackedStringArray,
|
|
) -> bool:
|
|
if neighbor == null:
|
|
return false
|
|
for forbidden_tag: String in forbidden_neighbor_tags:
|
|
if forbidden_tag in neighbor.tags:
|
|
return false
|
|
if allowed_tags.is_empty():
|
|
return true
|
|
return has_any_tag(neighbor.tags, allowed_tags)
|
|
|
|
|
|
func prefers_neighbor(neighbor: TerrainChunkDefinition) -> bool:
|
|
return (
|
|
neighbor != null
|
|
and has_any_tag(neighbor.tags, preferred_neighbor_tags)
|
|
)
|
|
|
|
|
|
static func has_any_tag(
|
|
available_tags: PackedStringArray,
|
|
requested_tags: PackedStringArray,
|
|
) -> bool:
|
|
for requested_tag: String in requested_tags:
|
|
if requested_tag in available_tags:
|
|
return true
|
|
return false
|