From dba3c1aa18270292ce31e2348bd05dbe6bf96be5 Mon Sep 17 00:00:00 2001 From: LOIRELAB <66545056+LoireLab@users.noreply.github.com> Date: Sat, 19 Jul 2025 21:11:47 +0200 Subject: [PATCH] Refine holy-war mod with deity spheres --- docs/holy-war.rst | 24 +++++++ holy-war.lua | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 docs/holy-war.rst create mode 100644 holy-war.lua diff --git a/docs/holy-war.rst b/docs/holy-war.rst new file mode 100644 index 000000000..b25dda91d --- /dev/null +++ b/docs/holy-war.rst @@ -0,0 +1,24 @@ +holy-war +======== + +.. dfhack-tool:: + :summary: Start wars when religions clash. + :tags: fort gameplay diplomacy + +This tool compares the spheres of influence represented by the gods of +nearby civilizations with those worshipped by your civilization and +represented in fortress temples. If no spheres overlap, or if the +historical record shows a ``religious_persecution_grudge`` between the +two peoples, the civilization is set to war. + +Usage +----- + +:: + + holy-war [--dry-run] + +When run without options, wars are declared immediately on all +qualifying civilizations and an announcement is displayed. With +``--dry-run``, the tool only reports which civilizations would be +affected without actually changing diplomacy. diff --git a/holy-war.lua b/holy-war.lua new file mode 100644 index 000000000..83836f862 --- /dev/null +++ b/holy-war.lua @@ -0,0 +1,160 @@ +-- Trigger wars based on conflicts between deity spheres +--@ module = true + +local utils = require('utils') + +local help = [====[ +holy-war +======== + +Provoke wars with civilizations that do not share deity spheres with +your civilization or the temples in your fortress. Religious +persecution grudges in the historical record also trigger war. + +Usage: + holy-war [--dry-run] + +If ``--dry-run`` is specified, no diplomatic changes are made and a list of +potential targets is printed instead. +]====] + +local valid_args = utils.invert({ 'dry-run', 'help' }) + +local RELIGIOUS_PERSECUTION_GRUDGE = + df.vague_relationship_type.RELIGIOUS_PERSECUTION_GRUDGE or + df.vague_relationship_type.religious_persecution_grudge + +local function merge(dst, src) + for k in pairs(src) do dst[k] = true end +end + +local function get_deity_spheres(hfid) + local spheres = {} + local hf = df.historical_figure.find(hfid) + if hf and hf.info and hf.info.metaphysical then + for _, sph in ipairs(hf.info.metaphysical.spheres) do + spheres[sph] = true + end + end + return spheres +end + +local function get_civ_spheres(civ) + local spheres = {} + for _, deity_id in ipairs(civ.relations.deities) do + merge(spheres, get_deity_spheres(deity_id)) + end + return spheres +end + +local function get_fort_spheres() + local spheres = {} + for _, bld in ipairs(df.global.world.buildings.all) do + if bld:getType() == df.building_type.Temple then + local dtype = bld.deity_type + if dtype == df.religious_practice_type.WORSHIP_HFID then + merge(spheres, get_deity_spheres(bld.deity_data.HFID)) + elseif dtype == df.religious_practice_type.RELIGION_ENID then + local rciv = df.historical_entity.find(bld.deity_data.Religion) + if rciv then merge(spheres, get_civ_spheres(rciv)) end + end + end + end + return spheres +end + +local function union(a, b) + local u = {} + merge(u, a) + merge(u, b) + return u +end + +local function share(p1, p2) + for k in pairs(p1) do + if p2[k] then return true end + end + return false +end + +local function get_civ_hists(civ) + local hfs = {} + for _, id in ipairs(civ.histfig_ids) do hfs[id] = true end + return hfs +end + +local function has_religious_grudge(p_hfs, t_hfs) + if not RELIGIOUS_PERSECUTION_GRUDGE then return false end + for _, set in ipairs(df.global.world.history.relationship_events) do + for i = 0, set.next_element-1 do + if set.relationship[i] == RELIGIOUS_PERSECUTION_GRUDGE then + local src = set.source_hf[i] + local tgt = set.target_hf[i] + if (p_hfs[src] and t_hfs[tgt]) or (p_hfs[tgt] and t_hfs[src]) then + return true + end + end + end + end + return false +end + +local function change_relation(target, relation) + local pciv = df.historical_entity.find(df.global.plotinfo.civ_id) + for _, state in ipairs(pciv.relations.diplomacy.state) do + if state.group_id == target.id then + state.relation = relation + end + end + for _, state in ipairs(target.relations.diplomacy.state) do + if state.group_id == pciv.id then + state.relation = relation + end + end +end + +local function main(...) + local args = utils.processArgs({...}, valid_args) + + if args.help then + print(help) + return + end + + local dry_run = args['dry-run'] + local pciv = df.historical_entity.find(df.global.plotinfo.civ_id) + local player_spheres = union(get_civ_spheres(pciv), get_fort_spheres()) + local player_hfs = get_civ_hists(pciv) + + for _, civ in ipairs(df.global.world.entities.all) do + if civ.type == 0 and civ.id ~= pciv.id then + local status + for _, state in ipairs(pciv.relations.diplomacy.state) do + if state.group_id == civ.id then + status = state.relation + break + end + end + if status == 0 or status == nil then -- peace or unknown + local civ_spheres = get_civ_spheres(civ) + local civ_hfs = get_civ_hists(civ) + if not share(player_spheres, civ_spheres) or + has_religious_grudge(player_hfs, civ_hfs) then + local name = dfhack.translation.translateName(civ.name, true) + if dry_run then + print(('Would declare war on %s over divine conflict.'):format(name)) + else + change_relation(civ, 1) -- war + dfhack.gui.showAnnouncement( + ('Religious persecution sparks war with %s!'):format(name), + COLOR_RED, true) + end + end + end + end + end +end + +if not dfhack_flags.module then + main(...) +end