Files
stickman/scripts/terrain_block.gd
T
ryan 1f91f3d2e5 Add headless regression tests for Phase 4b features
- Implement test for popup anchor behavior in rule-builder menus to ensure consistent anchor positioning during menu transitions.
- Create tests for stage logic, including mode transitions, toolbar visibility, and status bar updates.
- Add terrain drag-painting tests to verify correct block placement behavior and conflict handling.
- Introduce walk waypoint tests to check for arrival conditions and position stability after navigation.
2026-09-04 15:08:08 -04:00

123 lines
4.3 KiB
GDScript

@tool
class_name TerrainBlock
extends StaticBody2D
## TerrainBlock - Reusable StaticBody2D vector-terrain component (Phase 1).
##
## A self-contained terrain block: a StaticBody2D root with three children built
## in code — a Polygon2D (interior fill), a Line2D (crisp vector outline, closed
## by appending the first vertex, rounded joints/caps), and a CollisionPolygon2D
## configured with BUILD_SOLIDS so concave terrain blocks collide correctly.
## Fully @tool: exported properties update the children live in the editor.
# ---------------------------------------------------------------------------
# Child node names
# ---------------------------------------------------------------------------
const POLYGON_NODE_NAME := "Polygon2D"
const OUTLINE_NODE_NAME := "Outline"
const COLLISION_NODE_NAME := "CollisionPolygon2D"
# ---------------------------------------------------------------------------
# Exported properties
# ---------------------------------------------------------------------------
## The terrain polygon's vertices (local space). The unified setter pushes the
## array to the Polygon2D and CollisionPolygon2D, and closes the Line2D loop by
## appending the first vertex to the end.
@export var polygon_points: PackedVector2Array = PackedVector2Array():
set(value):
polygon_points = value
_apply_points()
## Interior fill color (Polygon2D).
@export var fill_color: Color = Color(0.25, 0.55, 0.25, 1.0):
set(value):
fill_color = value
if _polygon != null:
_polygon.color = value
## Outline color (Line2D).
@export var outline_color: Color = Color(0.05, 0.10, 0.05, 1.0):
set(value):
outline_color = value
if _outline != null:
_outline.default_color = value
## Outline width in pixels (Line2D).
@export var outline_width: float = 2.0:
set(value):
outline_width = value
if _outline != null:
_outline.width = value
## Registry id of the spawn template that produced this block (Phase 4b). Used
## by the terrain drag-painting three-state overlap query to detect same-type
## overlaps. Plain var (not inspector-editable).
var spawn_id: String = ""
# ---------------------------------------------------------------------------
# Internal node references (built in _ready, @tool-safe)
# ---------------------------------------------------------------------------
var _polygon: Polygon2D
var _outline: Line2D
var _collision: CollisionPolygon2D
# ---------------------------------------------------------------------------
# Lifecycle
# ---------------------------------------------------------------------------
func _ready() -> void:
_ensure_children()
_apply_points()
_apply_style()
# ---------------------------------------------------------------------------
# Internal build / apply
# ---------------------------------------------------------------------------
func _ensure_children() -> void:
_polygon = get_node_or_null(NodePath(POLYGON_NODE_NAME)) as Polygon2D
if _polygon == null:
_polygon = Polygon2D.new()
_polygon.name = POLYGON_NODE_NAME
add_child(_polygon)
_outline = get_node_or_null(NodePath(OUTLINE_NODE_NAME)) as Line2D
if _outline == null:
_outline = Line2D.new()
_outline.name = OUTLINE_NODE_NAME
_outline.joint_mode = Line2D.LINE_JOINT_ROUND
_outline.begin_cap_mode = Line2D.LINE_CAP_ROUND
_outline.end_cap_mode = Line2D.LINE_CAP_ROUND
add_child(_outline)
_collision = get_node_or_null(NodePath(COLLISION_NODE_NAME)) as CollisionPolygon2D
if _collision == null:
_collision = CollisionPolygon2D.new()
_collision.name = COLLISION_NODE_NAME
_collision.build_mode = CollisionPolygon2D.BUILD_SOLIDS
add_child(_collision)
## Pushes the current polygon_points to the Polygon2D and CollisionPolygon2D,
## and appends the first vertex to the Line2D to close the border loop.
func _apply_points() -> void:
if _polygon != null:
_polygon.polygon = polygon_points
if _collision != null:
_collision.polygon = polygon_points if polygon_points.size() >= 3 else PackedVector2Array()
if _outline != null:
var outline_points := polygon_points.duplicate()
if not outline_points.is_empty():
outline_points.append(polygon_points[0])
_outline.points = outline_points
func _apply_style() -> void:
if _polygon != null:
_polygon.color = fill_color
if _outline != null:
_outline.default_color = outline_color
_outline.width = outline_width