Files
MaidEngine/nodes/wall_renderer.gd
2026-04-07 10:22:30 -04:00

206 lines
6.2 KiB
GDScript

class_name WallRenderer
extends Node2D
## Renders wall textures by sampling segments from the aux_terrain texture atlas
## and compositing them onto tile edges. Each edge is made of two half-segments.
const TILE_SIZE := 100.0
## Source atlas rects (x, y, w, h) from aux_terrain.BMP
## Each edge has two half-segments that together span the full tile edge.
# -- Left edge --
const LEFT_UPPER_RECT := Rect2(0, 103, 20, 50)
const LEFT_LOWER_RECT := Rect2(0, 53, 20, 50)
# -- Right edge --
const RIGHT_UPPER_RECT := Rect2(186, 103, 20, 50)
const RIGHT_LOWER_RECT := Rect2(186, 53, 20, 50)
# -- Top edge --
const TOP_LEFT_RECT := Rect2(103, 0, 50, 20)
const TOP_RIGHT_RECT := Rect2(53, 0, 50, 20)
# -- Bottom edge --
const BOTTOM_LEFT_RECT := Rect2(103, 186, 50, 20)
const BOTTOM_RIGHT_RECT := Rect2(53, 186, 50, 20)
# -- Inner corners (drawn where two perpendicular wall edges meet) --
const INNER_CORNER_UPPER_LEFT_RECT := Rect2(0, 0, 50, 50)
const INNER_CORNER_UPPER_RIGHT_RECT := Rect2(156, 0, 50, 50)
const INNER_CORNER_LOWER_LEFT_RECT := Rect2(0, 156, 50, 50)
const INNER_CORNER_LOWER_RIGHT_RECT := Rect2(156, 156, 50, 50)
## Wall thickness in game pixels (how far the border extends into the tile)
const WALL_THICKNESS := 20.0
## Inner corner piece size in game pixels (quarter of a tile)
const CORNER_SIZE := 50.0
## Half the tile edge length
const HALF_EDGE := TILE_SIZE / 2.0
@export var atlas_texture: Texture2D
# Each entry is [dest_rect: Rect2, source_rect: Rect2]
var _segments: Array = []
func draw_walls_for_layout(map_layout: MapLayout) -> void:
_segments.clear()
if not map_layout or not atlas_texture:
queue_redraw()
return
var tile_edges := _collect_tile_edges(map_layout)
for tile in tile_edges:
var edges: Array = tile_edges[tile]
_build_tile_walls(tile, edges)
queue_redraw()
func _draw() -> void:
if not atlas_texture:
return
for seg in _segments:
draw_texture_rect_region(atlas_texture, seg[0], seg[1])
func _collect_tile_edges(map_layout: MapLayout) -> Dictionary:
## Returns {Vector2i: Array[StringName]} mapping each tile to its wall edge directions.
var tile_edges: Dictionary = {}
var walls := map_layout.get_walls()
for wall in walls:
var tile_a: Vector2i = wall[0]
var tile_b: Vector2i = wall[1]
var diff: Vector2i = tile_b - tile_a
# Determine which edge of tile_a faces the wall
var edge_a := _direction_to_edge(diff)
if edge_a != &"":
if not tile_edges.has(tile_a):
tile_edges[tile_a] = []
tile_edges[tile_a].append(edge_a)
# Determine which edge of tile_b faces the wall (opposite direction)
var edge_b := _direction_to_edge(-diff)
if edge_b != &"" and map_layout.is_tile_valid(tile_b):
if not tile_edges.has(tile_b):
tile_edges[tile_b] = []
tile_edges[tile_b].append(edge_b)
return tile_edges
func _direction_to_edge(dir: Vector2i) -> StringName:
match dir:
Vector2i.RIGHT:
return &"right"
Vector2i.LEFT:
return &"left"
Vector2i.UP:
return &"top"
Vector2i.DOWN:
return &"bottom"
return &""
func _build_tile_walls(tile: Vector2i, edges: Array) -> void:
var tile_origin := Vector2(tile) * TILE_SIZE
for edge in edges:
_build_edge_segments(tile_origin, edge)
# TODO: Outer corner segments
_build_outer_corners(tile, tile_origin, edges)
# Inner corner segments (for non-rectangular room support)
_build_inner_corners(tile, tile_origin, edges)
func _build_edge_segments(tile_origin: Vector2, edge: StringName) -> void:
var seg_a_rect: Rect2
var seg_b_rect: Rect2
var seg_a_offset: Vector2
var seg_b_offset: Vector2
var seg_a_size: Vector2
var seg_b_size: Vector2
match edge:
&"left":
seg_a_rect = LEFT_UPPER_RECT
seg_b_rect = LEFT_LOWER_RECT
seg_a_size = Vector2(WALL_THICKNESS, HALF_EDGE)
seg_b_size = Vector2(WALL_THICKNESS, HALF_EDGE)
seg_a_offset = Vector2(0, 0)
seg_b_offset = Vector2(0, HALF_EDGE)
&"right":
seg_a_rect = RIGHT_UPPER_RECT
seg_b_rect = RIGHT_LOWER_RECT
seg_a_size = Vector2(WALL_THICKNESS, HALF_EDGE)
seg_b_size = Vector2(WALL_THICKNESS, HALF_EDGE)
seg_a_offset = Vector2(TILE_SIZE - WALL_THICKNESS, 0)
seg_b_offset = Vector2(TILE_SIZE - WALL_THICKNESS, HALF_EDGE)
&"top":
seg_a_rect = TOP_LEFT_RECT
seg_b_rect = TOP_RIGHT_RECT
seg_a_size = Vector2(HALF_EDGE, WALL_THICKNESS)
seg_b_size = Vector2(HALF_EDGE, WALL_THICKNESS)
seg_a_offset = Vector2(0, 0)
seg_b_offset = Vector2(HALF_EDGE, 0)
&"bottom":
seg_a_rect = BOTTOM_LEFT_RECT
seg_b_rect = BOTTOM_RIGHT_RECT
seg_a_size = Vector2(HALF_EDGE, WALL_THICKNESS)
seg_b_size = Vector2(HALF_EDGE, WALL_THICKNESS)
seg_a_offset = Vector2(0, TILE_SIZE - WALL_THICKNESS)
seg_b_offset = Vector2(HALF_EDGE, TILE_SIZE - WALL_THICKNESS)
_queue_segment(tile_origin + seg_a_offset, seg_a_size, seg_a_rect)
_queue_segment(tile_origin + seg_b_offset, seg_b_size, seg_b_rect)
func _queue_segment(pos: Vector2, target_size: Vector2, source_rect: Rect2) -> void:
_segments.append([Rect2(pos, target_size), source_rect])
func _build_outer_corners(_tile: Vector2i, _tile_origin: Vector2, _edges: Array) -> void:
# TODO: Implement outer corner segments
# Check pairs of adjacent edges (e.g., "top" + "left" → top-left outer corner)
# and draw the corner piece from the atlas.
pass
func _build_inner_corners(_tile: Vector2i, tile_origin: Vector2, edges: Array) -> void:
## Draws decorative corner pieces wherever two perpendicular wall edges meet
## on the same tile (e.g., a top wall + left wall produces an upper-left corner).
var has_top := edges.has(&"top")
var has_bottom := edges.has(&"bottom")
var has_left := edges.has(&"left")
var has_right := edges.has(&"right")
var corner_size := Vector2(CORNER_SIZE, CORNER_SIZE)
if has_top and has_left:
_queue_segment(
tile_origin + Vector2(0, 0),
corner_size,
INNER_CORNER_UPPER_LEFT_RECT
)
if has_top and has_right:
_queue_segment(
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, 0),
corner_size,
INNER_CORNER_UPPER_RIGHT_RECT
)
if has_bottom and has_left:
_queue_segment(
tile_origin + Vector2(0, TILE_SIZE - CORNER_SIZE),
corner_size,
INNER_CORNER_LOWER_LEFT_RECT
)
if has_bottom and has_right:
_queue_segment(
tile_origin + Vector2(TILE_SIZE - CORNER_SIZE, TILE_SIZE - CORNER_SIZE),
corner_size,
INNER_CORNER_LOWER_RIGHT_RECT
)