class_name Thought extends RefCounted ## A single mood modifier entry, either state-driven (PERSISTENT) or ## one-shot event-driven (EVENT). ## ## PERSISTENT thoughts are added / removed by Pawn._refresh_persistent_thoughts ## each sim tick based on the pawn's current state (e.g. is_hungry()). ## They have no ticks_remaining — they exist for exactly as long as the ## triggering state is true. ## ## EVENT thoughts fire once on a game event (eating a meal, sleeping in a bed) ## and decay after ticks_remaining reaches zero. They stack up to max_stacks — ## for example, eating multiple meals in a row adds stacks to &"ate_meal" ## rather than duplicating entries. ## ## Save/load contract: ## var t2 := Thought.from_dict(t.to_dict()) ## assert(t2.id == t.id and t2.stacks == t.stacks) ## ## docs/architecture.md "MoodSystem" + MAX_STACKS_PER_THOUGHT = 5 (locked). enum Lifetime { PERSISTENT, ## Present while a triggering state is true; Pawn manages add/remove. EVENT, ## Fires once on a transition; decays after ticks_remaining ticks. } ## Hard cap per thought type (docs/architecture.md "MoodSystem" — locked). const MAX_STACKS_PER_THOUGHT: int = 5 ## Unique identifier, e.g. &"hungry", &"well_rested". Used as the merge key. var id: StringName = &"" ## Human-readable label for Audit logs and future pawn-detail UI. ## Not i18n'd here; call Strings.t("thought." + id) for player-visible text. var label: String = "" ## Mood delta per stack. Negative = bad, positive = good. ## Typical range -8..+8. Capped at max_stacks when computing mood. var modifier: int = 0 ## Whether this thought decays over time (EVENT) or tracks live state (PERSISTENT). var lifetime: Lifetime = Lifetime.EVENT ## Per-thought stack ceiling. Usually MAX_STACKS_PER_THOUGHT; some thoughts are ## capped lower (e.g. hungry is max_stacks=1 because it's binary present/absent). var max_stacks: int = MAX_STACKS_PER_THOUGHT # ── Runtime state (owned by the Pawn after add_thought()) ──────────────────── ## Number of times this thought is currently stacked (1..max_stacks). var stacks: int = 1 ## Remaining sim ticks before this EVENT thought expires. PERSISTENT ignores this. var ticks_remaining: int = 0 # ── save / load ─────────────────────────────────────────────────────────────── func to_dict() -> Dictionary: return { "id": String(id), "label": label, "modifier": modifier, "lifetime": lifetime, "max_stacks": max_stacks, "stacks": stacks, "ticks_remaining": ticks_remaining, } static func from_dict(d: Dictionary) -> Thought: var t := Thought.new() t.id = StringName(d.get("id", "")) t.label = str(d.get("label", "")) t.modifier = int(d.get("modifier", 0)) t.lifetime = int(d.get("lifetime", Lifetime.EVENT)) as Lifetime t.max_stacks = int(d.get("max_stacks", MAX_STACKS_PER_THOUGHT)) t.stacks = int(d.get("stacks", 1)) t.ticks_remaining = int(d.get("ticks_remaining", 0)) return t