From 0ab42627e87b397f4d76d03f6a1be66fa54bfbee Mon Sep 17 00:00:00 2001 From: LOIRELAB <66545056+LoireLab@users.noreply.github.com> Date: Sat, 19 Jul 2025 19:19:18 +0200 Subject: [PATCH] Add chronicle script to record fortress events --- chronicle.lua | 128 +++++++++++++++++++++++++++++++++++++++++++++ docs/chronicle.rst | 29 ++++++++++ 2 files changed, 157 insertions(+) create mode 100644 chronicle.lua create mode 100644 docs/chronicle.rst diff --git a/chronicle.lua b/chronicle.lua new file mode 100644 index 000000000..d750dbbbf --- /dev/null +++ b/chronicle.lua @@ -0,0 +1,128 @@ +-- Chronicles fortress events (deaths, artifacts, invasions) +--@module = true +--@enable = true + +local eventful = require('plugins.eventful') +local utils = require('utils') + +local GLOBAL_KEY = 'chronicle' + +local function get_default_state() + return { + entries = {}, + last_artifact_id = -1, + known_invasions = {}, + } +end + +state = state or get_default_state() + +local function persist_state() + dfhack.persistent.saveSiteData(GLOBAL_KEY, state) +end + +local function format_date(year, ticks) + local julian_day = math.floor(ticks / 1200) + 1 + local month = math.floor(julian_day / 28) + 1 + local day = julian_day % 28 + return string.format('%03d-%02d-%02d', year, month, day) +end + +local function add_entry(text) + table.insert(state.entries, text) + persist_state() +end + +local function on_unit_death(unit_id) + local unit = df.unit.find(unit_id) + if not unit then return end + local name = dfhack.units.getReadableName(unit) + local date = format_date(df.global.cur_year, df.global.cur_year_tick) + add_entry(string.format('Death of %s on %s', name, date)) +end + +eventful.onUnitDeath[GLOBAL_KEY] = on_unit_death + +local function check_artifacts() + local last_id = state.last_artifact_id + for _, rec in ipairs(df.global.world.artifacts.all) do + if rec.id > last_id then + local name = dfhack.translation.translateName(rec.name) + local date = format_date(rec.year, rec.year_ticks or 0) + add_entry(string.format('Artifact "%s" created on %s', name, date)) + last_id = rec.id + end + end + state.last_artifact_id = last_id +end + +local function check_invasions() + for _, inv in ipairs(df.global.plotinfo.invasions.list) do + if inv.flags.active and not state.known_invasions[inv.id] then + state.known_invasions[inv.id] = true + local date = format_date(df.global.cur_year, df.global.cur_year_tick) + add_entry(string.format('Invasion started on %s', date)) + end + end +end + +local function event_loop() + if not state.enabled then return end + check_artifacts() + check_invasions() + dfhack.timeout(1200, 'ticks', event_loop) +end + +local function do_enable() + state.enabled = true + event_loop() +end + +local function do_disable() + state.enabled = false +end + +local function load_state() + state = get_default_state() + utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) +end + +-- State change hook + +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc == SC_MAP_UNLOADED then + eventful.onUnitDeath[GLOBAL_KEY] = nil + state.enabled = false + return + end + if sc ~= SC_MAP_LOADED or not dfhack.world.isFortressMode() then + return + end + + load_state() + if state.enabled then + do_enable() + end +end + +if dfhack_flags.module then return end + +local args = {...} +local cmd = args[1] or 'print' + +if cmd == 'enable' then + do_enable() +elseif cmd == 'disable' then + do_disable() +elseif cmd == 'clear' then + state.entries = {} + persist_state() +elseif cmd == 'print' then + for _, entry in ipairs(state.entries) do + print(entry) + end +else + print(dfhack.script_help()) +end + +persist_state() diff --git a/docs/chronicle.rst b/docs/chronicle.rst new file mode 100644 index 000000000..7239a22fe --- /dev/null +++ b/docs/chronicle.rst @@ -0,0 +1,29 @@ +chronicle +========= + +.. dfhack-tool:: + :summary: Record fortress events like deaths, artifacts, and invasions. + :tags: fort gameplay + +This tool automatically records notable events in a chronicle that is stored +with your save. The chronicle contains entries for unit deaths, newly created +artifacts, and the start of invasions. + +Usage +----- + +:: + + chronicle enable + chronicle disable + chronicle print + chronicle clear + +``chronicle enable`` + Start recording events in the current fortress. +``chronicle disable`` + Stop recording events. +``chronicle print`` + Print all recorded events. +``chronicle clear`` + Delete the chronicle.