class_name GraveMarker extends Node2D ## Phase 14 — permanent grave marker entity. Spawned by GraveSlot.accept_corpse() ## when a corpse is buried. Carries the deceased's identity for the tap-to-inspect ## UI (Phase 17). ## ## Visual: procedural 16×16 stone cross / gravestone (dark grey palette). ## ## Registered with World.register_grave_marker on _ready; unregistered on ## _exit_tree (should never happen in normal play — markers persist for the save). ## ## Save round-trip: to_dict() / from_dict() for Phase 16. The world scene's ## save handler iterates World.grave_markers. ## ## See docs/implementation.md Phase 14 "GraveMarker". const TILE_SIZE_PX: int = 16 ## Tile position — set once at spawn, never changed. @export var tile: Vector2i = Vector2i.ZERO ## Deceased identity — mirrored from the Corpse at burial time. @export var deceased_name: String = "" @export var deceased_portrait_color: Color = Color(0.70, 0.60, 0.55) ## Cause of death, carried from Corpse.death_cause. @export var death_cause: StringName = &"" ## Sim tick at death (from Corpse.death_tick — NOT the burial tick). @export var death_tick: int = 0 func _ready() -> void: World.register_grave_marker(self) z_index = 2 # above floors, below pawns func _exit_tree() -> void: World.unregister_grave_marker(self) ## One-shot initialiser. Call after add_child() — _ready() fires first. func setup(p_tile: Vector2i, p_name: String, p_color: Color, p_cause: StringName, p_death_tick: int) -> void: tile = p_tile global_position = Vector2(tile.x * TILE_SIZE_PX + TILE_SIZE_PX / 2.0, tile.y * TILE_SIZE_PX + TILE_SIZE_PX) deceased_name = p_name deceased_portrait_color = p_color death_cause = p_cause death_tick = p_death_tick queue_redraw() Audit.log("grave_marker", "placed at %s for '%s' (cause=%s)" % [tile, deceased_name, death_cause]) # ── visual: procedural gravestone + cross ───────────────────────────────────── func _draw() -> void: # Stone base: dark grey rectangular slab, bottom-anchored like walls. # Local origin is at tile bottom-centre (matching Wall._draw convention). var stone_base := Color(0.45, 0.44, 0.42) var stone_dark := Color(0.30, 0.29, 0.27) var stone_light := Color(0.62, 0.60, 0.58) var cross_col := Color(0.25, 0.24, 0.22) # Main slab — 10 px wide × 12 px tall, bottom-anchored in the 16×16 cell. draw_rect(Rect2(-5.0, -13.0, 10.0, 13.0), stone_base) # Rounded top hint — just a lighter top strip. draw_rect(Rect2(-5.0, -13.0, 10.0, 3.0), stone_light) # Left shadow edge. draw_line(Vector2(-5.0, -13.0), Vector2(-5.0, 0.0), stone_dark, 1.0) # Cross etched on the slab face: vertical + horizontal bar. draw_line(Vector2(0.0, -11.0), Vector2(0.0, -3.0), cross_col, 1.5) draw_line(Vector2(-3.0, -9.0), Vector2(3.0, -9.0), cross_col, 1.5) # Outline. draw_rect(Rect2(-5.0, -13.0, 10.0, 13.0), stone_dark, false, 1.0) # ── save / load ────────────────────────────────────────────────────────────── func to_dict() -> Dictionary: return { "class_id": &"grave_marker", "tile_x": tile.x, "tile_y": tile.y, "name": deceased_name, "color_r": deceased_portrait_color.r, "color_g": deceased_portrait_color.g, "color_b": deceased_portrait_color.b, "cause": String(death_cause), "death_tick": death_tick, } func from_dict(d: Dictionary) -> void: tile = Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0))) global_position = Vector2(tile.x * TILE_SIZE_PX + TILE_SIZE_PX / 2.0, tile.y * TILE_SIZE_PX + TILE_SIZE_PX) deceased_name = d.get("name", "") deceased_portrait_color = Color( d.get("color_r", 0.70), d.get("color_g", 0.60), d.get("color_b", 0.55) ) death_cause = StringName(d.get("cause", "")) death_tick = int(d.get("death_tick", 0)) queue_redraw()