class_name GraveyardZone extends StorageDestination ## Phase 14 — Graveyard floor zone. Accepts corpses only. ## ## Structurally identical to StockpileZone but locked to Item.TYPE_CORPSE. ## The `accepts()` override adds a Corpse type-check so Items of type &"corpse" ## that are NOT Corpse entities never match (belt-and-suspenders). ## ## Visual: darker brownish overlay (brown-grey to read as turned earth) vs the ## yellow-tinted StockpileZone overlay. ## ## One slot per tile — corpses are large; one per cell mirrors design.md. ## ## Register/unregister with World.stockpiles happen automatically in ## _ready / _exit_tree — no external wiring needed. ## ## See docs/implementation.md Phase 14 "GraveyardZone". ## Region in tile coordinates. @export var region: Rect2i = Rect2i(0, 0, 4, 4) ## Player-visible label shown in zone inspect UI (Phase 17). @export var label: String = "Graveyard" const _TILE_PX: int = 16 ## Earthen-brown fill at two priority extremes — graveyard zones are fixed ## priority NORMAL; other Priority values are valid but unlikely to be set by ## the player in Phase 14. Tint distinguishes from StockpileZone yellow. const _FILL_COLOR: Color = Color(0.40, 0.28, 0.18, 0.18) # dark earth brown const _BORDER_COLOR: Color = Color(0.30, 0.20, 0.12, 0.55) # deeper border func _ready() -> void: z_index = -1 accepted_types = [Item.TYPE_CORPSE] World.register_stockpile(self) queue_redraw() func _exit_tree() -> void: World.unregister_stockpile(self) # ── StorageDestination overrides ───────────────────────────────────────────── func accepts(item) -> bool: if priority == StorageDestination.Priority.OFF: return false # Must be type corpse AND an actual Corpse entity (not a regular Item). if not item.has_method("get_item_type"): return false if item.get_item_type() != Item.TYPE_CORPSE: return false # Only accept if there is a dug GraveSlot at an open tile in our region. return _find_open_grave_slot() != null func covers_tile(tile: Vector2i) -> bool: return region.has_point(tile) ## Scan region for an open dug GraveSlot. Returns the slot or null. ## "Open" means the slot is in DUG state (not yet occupied / being hauled to). func find_drop_position(item) -> Vector2i: if not accepts(item): return Vector2i(-1, -1) var slot = _find_open_grave_slot() if slot == null: return Vector2i(-1, -1) return slot.tile # ── drawing ─────────────────────────────────────────────────────────────────── func _draw() -> void: var tile_px := float(_TILE_PX) var rect_px := Rect2( Vector2(region.position) * tile_px, Vector2(region.size) * tile_px ) draw_rect(rect_px, _FILL_COLOR, true) draw_rect(rect_px, _BORDER_COLOR, false, 1.0) # Small cross marker in the top-left corner of the zone as a visual cue. var cross_center := Vector2(region.position) * tile_px + Vector2(tile_px * 0.5, tile_px * 0.5) var cross_color := Color(_BORDER_COLOR.r, _BORDER_COLOR.g, _BORDER_COLOR.b, 0.70) draw_line(cross_center + Vector2(0.0, -4.0), cross_center + Vector2(0.0, 4.0), cross_color, 1.5) draw_line(cross_center + Vector2(-3.0, -1.5), cross_center + Vector2(3.0, -1.5), cross_color, 1.5) # ── save / load ─────────────────────────────────────────────────────────────── func to_dict() -> Dictionary: return { "class_id": &"graveyard_zone", "region_x": region.position.x, "region_y": region.position.y, "region_w": region.size.x, "region_h": region.size.y, "priority": int(priority), "label": label, } func from_dict(d: Dictionary) -> void: region = Rect2i( int(d.get("region_x", 0)), int(d.get("region_y", 0)), int(d.get("region_w", 4)), int(d.get("region_h", 4)) ) priority = d.get("priority", StorageDestination.Priority.NORMAL) as StorageDestination.Priority label = str(d.get("label", "Graveyard")) queue_redraw() # ── internal ───────────────────────────────────────────────────────────────── ## Returns the first GraveSlot within the region that is in DUG state and not ## already being hauled to. Returns null if none found. func _find_open_grave_slot(): for x in range(region.position.x, region.position.x + region.size.x): for y in range(region.position.y, region.position.y + region.size.y): var cell := Vector2i(x, y) # Look for a GraveSlot in World.stockpiles that covers this cell # and is in DUG state. for dest in World.stockpiles: if dest == self: continue if not dest.has_method("is_grave_slot_dug"): continue if dest.covers_tile(cell) and dest.is_grave_slot_dug(): return dest return null