class_name Corpse extends Node2D
## Phase 14 — pawn corpse. Spawns when a Pawn dies, carries the deceased
## pawn's identity for the burial / cremation / grave-marker pipeline.
##
## DECAY MODEL (design.md "Death & corpses"):
##   decay 0..50   — fresh (no dirt contribution beyond the existing tile)
##   decay 50..100 — rotting (DirtinessSystem.bump per tick, "rotting body in colony" thought severity ramps)
##   decay 100     — rotted: corpse is destroyed, EventBus.corpse_rotted_away fires
##
## Hauling: corpses haul-route through StorageDestinations exactly like Items,
## but accept-filter is gated by Item.TYPE_CORPSE on the destination side.
## GraveyardZone (Agent B) is the destination class that opts in.
##
## When a corpse reaches a GraveSlot (Agent B), the slot consumes it and spawns
## a GraveMarker carrying the deceased identity. EventBus.corpse_buried fires.
##
## When a corpse is fed to a CremationPyre (Agent C), it's consumed and an
## ash item drops + "cremated friend" mood thought fires.
##
## Save round-trip carries: tile, deceased_name, deceased_portrait_color,
## decay, death_tick, death_cause.

const DECAY_PER_TICK: float = 0.05  ## ~33 in-game min from fresh→rotted at 1×
const DECAY_FRESH_MAX: float = 50.0
const DECAY_ROTTED: float = 100.0

## Tile position (mirrors Pawn/Item convention).
@export var tile: Vector2i = Vector2i.ZERO

## Deceased identity — carried to the GraveMarker on burial.
@export var deceased_name: String = ""
@export var deceased_portrait_color: Color = Color.WHITE

## Decay 0..100. Drives visual + dirty contribution + mood-thought severity.
@export var decay: float = 0.0

## Sim tick at death; useful for "how long ago" alerts.
@export var death_tick: int = 0

## Brief categorization, used by mood thoughts ("died of bleeding", "killed by wolf").
@export var death_cause: StringName = &""


func _ready() -> void:
	World.register_corpse(self)
	EventBus.sim_tick.connect(_on_sim_tick)
	z_index = 4  # above floors, below pawns


func _exit_tree() -> void:
	World.unregister_corpse(self)


## Public setup helper called by the death pipeline. Mirrors Wall.setup() shape.
func setup(p_tile: Vector2i, p_name: String, p_color: Color, p_cause: StringName) -> void:
	tile = p_tile
	global_position = Vector2(tile.x * 16 + 8, tile.y * 16 + 8)
	deceased_name = p_name
	deceased_portrait_color = p_color
	death_cause = p_cause
	death_tick = Sim.tick
	queue_redraw()


## True for HaulingProvider — corpses haul like items.
## The matching filter on the StorageDestination side is `Item.TYPE_CORPSE`.
func get_item_type() -> StringName:
	return &"corpse"


## True when decay is past the rotting threshold; mood thought severity ramps from here.
func is_rotting() -> bool:
	return decay >= DECAY_FRESH_MAX


func _on_sim_tick(_n: int) -> void:
	decay += DECAY_PER_TICK
	if is_rotting():
		# Bump dirtiness at this tile (Phase 13 hook). +5/h at decay=100 per design.md.
		if World.dirtiness_system != null:
			World.dirtiness_system.bump(tile, 0.04)  # ~+8/in-game-min
	if decay >= DECAY_ROTTED:
		Audit.log("corpse", "%s rotted away at %s" % [deceased_name, tile])
		EventBus.corpse_rotted_away.emit(self)
		queue_free()


# ── visual: simple procedural sprite ─────────────────────────────────────────
func _draw() -> void:
	# Body color desaturates as decay progresses; turns olive-green when rotting.
	var t := clampf(decay / DECAY_ROTTED, 0.0, 1.0)
	var fresh := Color(0.70, 0.60, 0.55)
	var rotten := Color(0.45, 0.50, 0.30)
	var body_color := fresh.lerp(rotten, t)
	# Lay-down silhouette: 14×6 rect centered.
	draw_rect(Rect2(-7, -3, 14, 6), body_color)
	# Head dot
	draw_circle(Vector2(-9, 0), 3, deceased_portrait_color.lerp(body_color, 0.3))


# ── save / load ──────────────────────────────────────────────────────────────

func to_dict() -> Dictionary:
	return {
		"class_id": &"corpse",
		"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,
		"decay": decay,
		"death_tick": death_tick,
		"cause": String(death_cause),
	}


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 * 16 + 8, tile.y * 16 + 8)
	deceased_name = d.get("name", "")
	deceased_portrait_color = Color(d.get("color_r", 1.0), d.get("color_g", 1.0), d.get("color_b", 1.0))
	decay = d.get("decay", 0.0)
	death_tick = int(d.get("death_tick", 0))
	death_cause = StringName(d.get("cause", ""))
	queue_redraw()
