class_name Board extends Node3D @export var dims: Vector2i # idea: board has very little logic, delegates to something else # l8r tho # literally just linearly search to find pieces at a position # children # for zooming out to see the whole puzzle func top_left_aabb() -> AABB: return AABB(position, Vector3.ONE*0.1) func bottom_right_aabb() -> AABB: return AABB(position + Vector3(dims.x, 0, dims.y), Vector3.ONE*0.1) # todo: Array[Piece] func pieces() -> Array: return get_children() func pieces_at(pos: Vector2i) -> Array[Piece]: var out: Array[Piece] = [] for piece in get_children(): if piece.lpos == pos: out.push_back(piece) return out func find_piece_at(pos: Vector2i, filter: Callable) -> Piece: var at := pieces_at(pos) var idx := at.find_custom(filter) if idx < 0: return return at[idx] func any_at(pos: Vector2i, filter: Callable) -> bool: return pieces_at(pos).any(filter) func solid_at(pos: Vector2i) -> bool: return any_at(pos, func(p): return p.type == Piece.Type.Wall or p.type == Piece.Type.Box) func passable_at(pos: Vector2i) -> bool: return not solid_at(pos) func remove_piece(piece: Piece): remove_child(piece) func add_piece(piece: Piece): add_child(piece) func add_pieces(piece: Array[Piece]): for p in piece: add_piece(p) static func from_string(src: String) -> Board: var lines := src.lstrip("\n").rstrip("\n").split("\n") var width := 0 for line in lines: width = max(width, line.length()) var dims := Vector2i(width, lines.size()) var board := Board.new() board.dims = dims for y in range(lines.size()): var line := lines[y] for x in range(line.length()): var c := line[x] var pos := Vector2i(x, y) match c: ".": board.add_piece(Piece.goal(pos)) "*": board.add_pieces([Piece.goal(pos), Piece.box(pos)]) "+": board.add_pieces([Piece.goal(pos), Piece.player(pos)]) "$": board.add_piece(Piece.box(pos)) "@": board.add_piece(Piece.player(pos)) "#": board.add_piece(Piece.wall(pos)) return board