class_name BigRockNode extends Node2D ## Permanent stone outcrop — a 2×2 immovable boulder that never depletes. ## ## BigRockNode marks its footprint impassable in the pathfinder and registers ## itself with World so the designation system can locate it. A QuarryWorkbench ## can be built on one of its tiles; external code sets is_quarry_site = true ## and quarry_workbench once construction completes. ## ## Draw convention: position is stamped at the TOP-LEFT pixel corner of the ## 2×2 footprint (tile * TILE_SIZE_PX). The visual centre of the 32×32 area ## sits at local (16, 16), so all _draw_* helpers are centred there. ## ## Save/load: to_dict / from_dict round-trip tile + is_quarry_site. ## The quarry_workbench reference is an entity pointer reconstructed by the ## save layer (SaveSystem wires it after both entities are spawned). const TILE_SIZE_PX: int = 16 ## Top-left tile of the 2×2 footprint. var tile: Vector2i = Vector2i.ZERO ## Footprint size in tiles (always 2×2; declared as a var so external code can ## read it uniformly without special-casing BigRockNode vs hypothetical future nodes). var footprint: Vector2i = Vector2i(2, 2) ## True once a Quarry workbench has been completed on this outcrop. ## Flipped by external code (designation / world.gd) — this file only declares it. var is_quarry_site: bool = false ## The QuarryWorkbench that sits on this node after construction. ## null until assigned by the designation completion handler. var quarry_workbench = null # ── lifecycle ───────────────────────────────────────────────────────────────── func _ready() -> void: # Position at top-left pixel corner so footprint_tiles() aligns with world coords. position = Vector2(tile.x * TILE_SIZE_PX, tile.y * TILE_SIZE_PX) # Block pathfinding on every tile in the footprint. for t in footprint_tiles(): if World.pathfinder != null: World.pathfinder.set_cell_walkable(t, false) World.register_big_rock_node(self) queue_redraw() func _exit_tree() -> void: World.unregister_big_rock_node(self) # ── public API ──────────────────────────────────────────────────────────────── ## One-shot initialiser. Call after add_child() so _ready() has fired. func setup(p_tile: Vector2i) -> void: tile = p_tile position = Vector2(tile.x * TILE_SIZE_PX, tile.y * TILE_SIZE_PX) queue_redraw() Audit.log("big_rock_node", "spawned at tile %s" % tile) ## Returns the four tiles covered by this node's 2×2 footprint. ## Used by designation, save/load, and inspection code. func footprint_tiles() -> Array[Vector2i]: return [ tile, tile + Vector2i(1, 0), tile + Vector2i(0, 1), tile + Vector2i(1, 1), ] ## True when tile_to_check falls inside the 2×2 footprint. func is_at(tile_to_check: Vector2i) -> bool: return ( tile_to_check.x >= tile.x and tile_to_check.x < tile.x + footprint.x and tile_to_check.y >= tile.y and tile_to_check.y < tile.y + footprint.y ) # ── save / load ─────────────────────────────────────────────────────────────── func to_dict() -> Dictionary: return { "class_id": &"big_rock_node", "tile_x": tile.x, "tile_y": tile.y, "is_quarry_site": is_quarry_site, } ## Restore from a dict produced by to_dict(). quarry_workbench is reconnected ## by the save layer after both entities are spawned. static func from_dict(d: Dictionary) -> Dictionary: return { "tile_x": int(d.get("tile_x", 0)), "tile_y": int(d.get("tile_y", 0)), "is_quarry_site": bool(d.get("is_quarry_site", false)), } # ── render ──────────────────────────────────────────────────────────────────── ## Draw a procedural pile of grey rocks centred at local (16, 16) — ## the geometric centre of the 32×32 footprint area. ## Three layers create depth: large base blob → medium mid rock → small cap. func _draw() -> void: var cx: float = 16.0 var cy: float = 16.0 # Colour palette — warm grey tones suggesting weathered granite. var base_fill := Color(0.60, 0.58, 0.55) # large base ellipse var mid_fill := Color(0.45, 0.44, 0.42) # medium middle rock var top_fill := Color(0.55, 0.53, 0.50) # small perched cap var outline_col := Color(0.20, 0.18, 0.16, 0.70) # subtle dark rim # Bottom: large flattened blob — widest at the base to read as a ground-hugging mass. var base_w: float = 24.0 var base_h: float = 16.0 var base_rect := Rect2(Vector2(cx - base_w / 2.0, cy - base_h / 2.0 + 2.0), Vector2(base_w, base_h)) draw_rect(base_rect, base_fill) # Dark outline arc around the base blob. draw_rect(base_rect, outline_col, false, 1.0) # Middle: slightly elevated, narrower rock sitting on top of the base. var mid_w: float = 16.0 var mid_h: float = 12.0 var mid_oy: float = -4.0 # shift up from centre var mid_rect := Rect2(Vector2(cx - mid_w / 2.0, cy - mid_h / 2.0 + mid_oy), Vector2(mid_w, mid_h)) draw_rect(mid_rect, mid_fill) draw_rect(mid_rect, outline_col, false, 1.0) # Top: small cap perched on the very top. var top_w: float = 8.0 var top_h: float = 6.0 var top_oy: float = -10.0 # above the mid rock var top_rect := Rect2(Vector2(cx - top_w / 2.0, cy - top_h / 2.0 + top_oy), Vector2(top_w, top_h)) draw_rect(top_rect, top_fill) draw_rect(top_rect, outline_col, false, 1.0)