Module:MissionaryDailyOpsBVR
From MicrasWiki
Jump to navigationJump to search
Documentation for this module may be created at Module:MissionaryDailyOpsBVR/doc
-- Module:MissionaryDailyOpsBVR
-- Daily missionary tasking ledger (Temple Bank / Reformed Stripping Path).
--
-- Embedded requirements:
-- 1) CITY FLAGS: follow Module:GeneralPortOperations flag-naming pattern, with overrides,
-- BUT any missing flag file falls back to the Imperial Trade Union flag.
-- 2) CULT FLAGS (logos): taken directly from Module:GeneralPortOperations addLogo() mappings
-- for ritual cults (and Temple of Aprobelle logo). Missing files fall back to ITU flag.
-- 3) CULT LINKS: each cult links to its canonical “Mystery” section target (Divine page section)
-- per Reformed_Stripping_Path#Planetary_Devotion (including the special “The …” headings).
-- Temple Aprobelle links to [[Temple_Aprobelle]] (not a Mystery section).
-- 4) OPS ALERT: integrates Module:BassaridiaForecast getOpsData() (badge + condition + overlays).
-- 5) Avoid “too many expensive function calls”:
-- - No candidate-search loops.
-- - Flag existence checks are cached and done at most once per unique file used.
-- - General Port content scan is ONLY executed if showEcon=yes.
--
-- Usage:
-- {{#invoke:MissionaryDailyOpsBVR|makeTable}}
-- {{#invoke:MissionaryDailyOpsBVR|makeTable|region=Core}}
-- {{#invoke:MissionaryDailyOpsBVR|makeTable|includeThreat=yes}}
-- {{#invoke:MissionaryDailyOpsBVR|makeTable|showEcon=yes}}
-- {{#invoke:MissionaryDailyOpsBVR|makeCity|city=Vaeringheim}}
-- {{#invoke:MissionaryDailyOpsBVR|makeOrder|order=Temple Aprobelle}}
local p = {}
local cal = require('Module:BassaridianCalendar')
local DAYS_IN_YEAR = 183
--------------------------------------------------------------------------------
-- 0.1) DAILY CULT RELATIONS BRIDGE
-- This keeps the main relations logic in [[Module:CultRelationsDailyBVR]],
-- but lets this missionary module display it when requested.
--------------------------------------------------------------------------------
local cultRelOk, cultRel = pcall(require, "Module:CultRelationsDailyBVR")
if not cultRelOk then
cultRel = nil
end
local function renderCultRelations(args)
args = args or {}
if not cultRel or type(cultRel.makeTable) ~= "function" then
return "<div style='border:1px solid #aaa;background:#fff3cd;padding:6px;margin-top:0.5em;'>" ..
"''Daily cult-relations module unavailable. Make sure [[Module:CultRelationsDailyBVR]] exists.''" ..
"</div>"
end
local relArgs = {
year = args.year or args.psscyear,
day = args.day or args.dayofyear,
limit = args.relationsLimit or args.rellimit or "20",
events = args.relationEvents or "5",
showEvents = args.showRelationEvents or "yes",
compact = args.relationsCompact or "no"
}
local ok, result = pcall(cultRel.makeTable, { args = relArgs })
if ok then
return result
end
return "<div style='border:1px solid #aaa;background:#f8d7da;padding:6px;margin-top:0.5em;'>" ..
"''Daily cult-relations error: " .. tostring(result) .. "''" ..
"</div>"
end
--------------------------------------------------------------------------------
-- 0) BASIC UTILS
--------------------------------------------------------------------------------
local function trim(s)
s = tostring(s or "")
return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end
local function normalizeSpaces(s)
return (s or ""):gsub("[%s\u00A0]+", " ")
end
local function stripWiki(s)
s = tostring(s or "")
s = s:gsub("%{%b{}%}", "")
s = s:gsub("%[%[[^%]|]+|([^%]]+)%]%]", "%1")
s = s:gsub("%[%[([^%]]+)%]%]", "%1")
s = s:gsub("'''''", ""):gsub("'''", ""):gsub("''", "")
return trim(s)
end
--------------------------------------------------------------------------------
-- 1) CALENDAR PARSING
--------------------------------------------------------------------------------
local function parseCalendarString(s)
if type(s) ~= "string" then return nil end
local dayOfYear, month, year = s:match("^(%d+),%s+([%a]+)%s+%b()%s*,%s*(%d+)%s+PSSC")
if dayOfYear and year then
return tonumber(year), tonumber(dayOfYear), month
end
return nil
end
local function extractEventText(s)
if type(s) ~= "string" then return "" end
local ev = s:match("PSSC%s+–%s+(.-)%s+–%s+Proverb:")
return ev or ""
end
local function doyToMonthDom(doy)
if doy <= 61 then
return 1, doy
elseif doy <= 122 then
return 2, doy - 61
else
return 3, doy - 122
end
end
local function getDateInfo(args)
args = args or {}
local overrideYear = tonumber(args.year or args.psscyear)
local overrideDay = tonumber(args.day or args.dayofyear)
if overrideYear and overrideDay then
if overrideDay < 1 then overrideDay = 1 end
if overrideDay > DAYS_IN_YEAR then overrideDay = DAYS_IN_YEAR end
local m, dom = doyToMonthDom(overrideDay)
return {
year = overrideYear,
dayOfYear = overrideDay,
monthNum = m,
dom = dom,
text = string.format("%d/%d/%d PSSC", dom, m, overrideYear),
raw = string.format("%d/%d/%d PSSC", dom, m, overrideYear),
calendarEvent = "",
}
end
local raw = cal.getCurrentDate()
local y, doy = parseCalendarString(raw)
if not y or not doy then
y, doy = 0, 1
raw = tostring(raw or "")
end
local m, dom = doyToMonthDom(doy)
return {
year = y,
dayOfYear = doy,
monthNum = m,
dom = dom,
text = string.format("%d/%d/%d PSSC", dom, m, y),
raw = raw,
calendarEvent = extractEventText(raw),
}
end
--------------------------------------------------------------------------------
-- 2) DETERMINISTIC RNG
--------------------------------------------------------------------------------
local function hash31(str)
str = tostring(str or "")
local h = 0
for i = 1, #str do
h = (h * 131 + str:byte(i)) % 2147483647
end
return h
end
local function lcg(seed)
local s = seed % 2147483647
if s <= 0 then s = s + 2147483646 end
return function()
s = (1103515245 * s + 12345) % 2147483647
return s
end
end
local function pickFromList(nextInt, list, avoidValue)
if not list or #list == 0 then return nil end
if #list == 1 then return list[1] end
local tries = 0
while tries < 12 do
local idx = (nextInt() % #list) + 1
local v = list[idx]
if v ~= avoidValue then return v end
tries = tries + 1
end
for _, v in ipairs(list) do
if v ~= avoidValue then return v end
end
return list[1]
end
local function pickWeighted(nextInt, weighted)
local total = 0
for _, it in ipairs(weighted) do total = total + (it.w or 0) end
if total <= 0 then return weighted[1] end
local r = nextInt() % total
local acc = 0
for _, it in ipairs(weighted) do
acc = acc + (it.w or 0)
if r < acc then return it end
end
return weighted[#weighted]
end
--------------------------------------------------------------------------------
-- 3) EXPENSIVE-CALL CONTROL: cached file existence checks (1 per unique file)
--------------------------------------------------------------------------------
local fileExistsCache = {}
local function fileExists(fileName)
fileName = tostring(fileName or "")
if fileName == "" then return false end
if fileExistsCache[fileName] ~= nil then return fileExistsCache[fileName] end
local t = mw.title.new("File:" .. fileName)
local ok = (t and t.exists) or false
fileExistsCache[fileName] = ok
return ok
end
--------------------------------------------------------------------------------
-- 4) STYLE / SEVERITY
--------------------------------------------------------------------------------
local function sevStyle(sev)
if sev == "red" then
return ' style="background-color:#f8d7da;"'
elseif sev == "yellow" then
return ' style="background-color:#fff3cd;"'
else
return ' style="background-color:#d4edda;"'
end
end
local function urgencyScore(u)
u = tostring(u or ""):upper()
if u == "CRITICAL" then return 4 end
if u == "HIGH" then return 3 end
if u == "MEDIUM" then return 2 end
if u == "LOW" then return 1 end
return 0
end
--------------------------------------------------------------------------------
-- 5) CITY NETWORK
--------------------------------------------------------------------------------
local function cityLink(name)
return string.format("[[List_of_cities_in_Bassaridia_Vaeringheim#%s|%s]]", name, name)
end
local cityOrder = {
-- Core majors
{name="Vaeringheim", region="Core", tier="major"},
{name="Luminaria", region="Core", tier="major"},
{name="Serena", region="Core", tier="major"},
{name="Pyralis", region="Core", tier="major"},
{name="Symphonara", region="Core", tier="major"},
{name="Aurelia", region="Core", tier="major"},
{name="Somniumpolis", region="Core", tier="major"},
{name="Nexa", region="Core", tier="major"},
{name="Lunalis Sancta", region="Core", tier="major"},
{name="Sylvapolis", region="Core", tier="major"},
-- Core minors
{name="Saluria", region="Core", tier="minor"},
{name="Aetherium", region="Core", tier="minor"},
{name="Ferrum Citadel", region="Core", tier="minor"},
{name="Acheron", region="Core", tier="minor"},
{name="Erythros", region="Core", tier="minor"},
{name="Catonis Atrium", region="Core", tier="minor"},
{name="Delphica", region="Core", tier="minor"},
{name="Koinonía", region="Core", tier="minor"},
{name="Aureum", region="Core", tier="minor"},
-- Alperkin
{name="The Alpazkigz", region="Alperkin", tier="minor"},
{name="Odiferia", region="Alperkin", tier="minor"},
-- New South Jangsong
{name="Skýrophos", region="New South Jangsong", tier="major"},
{name="Bjornopolis", region="New South Jangsong", tier="major"},
{name="Aegirheim", region="New South Jangsong", tier="major"},
{name="Norsolyra", region="New South Jangsong", tier="major"},
{name="Thorsalon", region="New South Jangsong", tier="major"},
{name="Pelagia", region="New South Jangsong", tier="minor"},
{name="Myrene", region="New South Jangsong", tier="minor"},
{name="Thyrea", region="New South Jangsong", tier="minor"},
{name="Ephyra", region="New South Jangsong", tier="minor"},
{name="Halicarn", region="New South Jangsong", tier="minor"},
-- Haifan Bassaridia
{name="Keybir-Aviv", region="Haifan Bassaridia", tier="major"},
{name="Tel-Amin", region="Haifan Bassaridia", tier="major"},
{name="Diamandis", region="Haifan Bassaridia", tier="major"},
{name="Jogi", region="Haifan Bassaridia", tier="major"},
{name="Lewisburg", region="Haifan Bassaridia", tier="major"},
{name="Thermosalem", region="Haifan Bassaridia", tier="minor"},
{name="Akróstadium", region="Haifan Bassaridia", tier="minor"},
{name="Sufriya", region="Haifan Bassaridia", tier="minor"},
{name="Lykopolis", region="Haifan Bassaridia", tier="minor"},
-- Normark
{name="Ardclach", region="Normark", tier="major"},
{name="Riddersborg", region="Normark", tier="major"},
-- Ouriana
{name="Bashkim", region="Ouriana", tier="major"},
{name="Ourid", region="Ouriana", tier="major"},
{name="Tonar", region="Ouriana", tier="minor"},
-- Caledonia
{name="Fanghorn", region="Caledonia", tier="major"},
{name="Eikbu", region="Caledonia", tier="major"},
{name="Galvø", region="Caledonia", tier="major"},
{name="Hamarrfell", region="Caledonia", tier="major"},
{name="Sårensby", region="Caledonia", tier="minor"},
{name="Slevik", region="Caledonia", tier="minor"},
{name="Sjøsborg", region="Caledonia", tier="minor"},
{name="Storesund", region="Caledonia", tier="minor"},
{name="Notranskja", region="Caledonia", tier="minor"},
{name="Kaledonija", region="Caledonia", tier="minor"},
{name="Krlsgorod", region="Caledonia", tier="minor"},
{name="Skøda", region="Caledonia", tier="minor"},
}
local citySet = {}
local ctx = { usedTasksByCult = {} }
for _, c in ipairs(cityOrder) do citySet[c.name] = true end
--------------------------------------------------------------------------------
-- 5.0.1) HAIFAN BASSARIDIA / SOUTHERN STRAIT OPERATING ZONE
--
-- This does NOT lock missionary cults to these cities.
-- It only allows special Southern Strait tasking when the generated station
-- is one of the Haifan Bassaridian cities.
--------------------------------------------------------------------------------
local haifanBassaridiaCities = {
["Keybir-Aviv"] = true,
["Tel-Amin"] = true,
["Diamandis"] = true,
["Jogi"] = true,
["Lewisburg"] = true,
["Thermosalem"] = true,
["Akróstadium"] = true,
["Sufriya"] = true,
["Lykopolis"] = true,
}
local function isHaifanBassaridiaCity(cityName)
return haifanBassaridiaCities[tostring(cityName or "")] == true
end
local function regionPass(filter, cityRegion)
filter = tostring(filter or "All")
if filter == "" or filter == "All" then return true end
if filter == "Core" then return cityRegion == "Core" end
if filter == "NSJ" then return cityRegion == "New South Jangsong" end
if filter == "Haifan" then return cityRegion == "Haifan Bassaridia" end
if filter == "Caledonian" then return cityRegion == "Caledonia" end
return cityRegion == filter
end
--------------------------------------------------------------------------------
-- 5.1) CITY FLAGS (GeneralPortOperations style + fallback-to-ITU if missing)
--------------------------------------------------------------------------------
local ITU_FLAG = "Imperial Trade Union flag.png"
local function normCityForFlag(name)
-- Exactly the normalization used in Module:GeneralPortOperations
return (name or "")
:gsub("[%s%-’]", "")
:gsub("[Áá]", "A"):gsub("[Éé]", "E")
:gsub("[Íí]", "I"):gsub("[Óó]", "O")
:gsub("[Úú]", "U"):gsub("[Ýý]", "Y")
end
local cityFlagOverrides = {
["Skýrophos"] = "SkyrophosFlag.png", -- override used in GeneralPortOperations
["Slevik"] = "SlevikFlag.png.png", -- known oddity you’ve used elsewhere
["Sårensby"] = "SårensbyFlag.png", -- known to exist in your notes
}
local cityFlagMarkupCache = {}
local function getCityFlagMarkup(cityName)
if cityFlagMarkupCache[cityName] then return cityFlagMarkupCache[cityName] end
local candidate = cityFlagOverrides[cityName] or (normCityForFlag(cityName) .. "Flag.png")
local file = fileExists(candidate) and candidate or ITU_FLAG
local mk = string.format("[[File:%s|20px]]", file)
cityFlagMarkupCache[cityName] = mk
return mk
end
--------------------------------------------------------------------------------
-- 6) CULT FLAGS (logos) from Module:GeneralPortOperations addLogo() + fallback
--------------------------------------------------------------------------------
local cultLogoFile = {
-- Ritual cult logos from Module:GeneralPortOperations
["Order of Aurora Mystica"] = "Auroramystica.png",
["Harmony Sanctum"] = "Harmonysanctum.png",
["Ignis Aeternum"] = "Ignis Aeternum.png",
["Celestial Harmony Sect"] = "HarmonySect.png",
["Guild of Golden Shadows"] = "GoldenShadows.png",
["Azure Sentinel Sect"] = "AzureSentinel.png",
["Reverie Nebulous"] = "RevNeb.png",
["Eon Fellowship"] = "EonFel.png",
["Order of the Umbral Oracle"] = "Umbral.png",
["Mystery of the Verdant Embrace"] = "Verdant.png",
["Conclave Illuminara"] = "Conclave.png",
["Sanctum Vitalis"] = "Vitalis.png",
["Temple Alabaster"] = "Alabaster.png",
["Court of the Ironclad"] = "Ironclad.png",
["Accord Concordia"] = "Concordia.png",
["Mystery of the Stygian Veil"] = "StygianVe.png",
["Sylvan Fellowship"] = "Sylvan.png",
["Mystery of Red Mirth"] = "MirthFlag.png",
["Rex Catonis"] = "Catonis.png",
["Sanctum Delphica"] = "SanctumDel.png",
["Ordo Amicitia"] = "OrdoFl.png",
["Temple Illuminata"] = "AureumFlag.png",
-- Temple Aprobelle logo is mapped in GeneralPortOperations under serviceCompanies name:
["Temple Aprobelle"] = "TempleAprobelleLogo.png",
["Temple of Aprobelle"] = "TempleAprobelleLogo.png",
}
local cultFlagMarkupCache = {}
local function getCultFlagMarkup(cultName)
if cultFlagMarkupCache[cultName] then return cultFlagMarkupCache[cultName] end
local candidate = cultLogoFile[cultName]
local file = (candidate and fileExists(candidate)) and candidate or ITU_FLAG
local mk = string.format("[[File:%s|18px]] ", file)
cultFlagMarkupCache[cultName] = mk
return mk
end
--------------------------------------------------------------------------------
-- 7) CULT LINKS (canonical “Mystery” section targets)
--------------------------------------------------------------------------------
local cultTargets = {
["Order of Aurora Mystica"] = { page="Eos_(Reformed_Stripping_Path)", anchor="Order_of_Aurora_Mystica" },
["Harmony Sanctum"] = { page="Micras_(Reformed_Stripping_Path)", anchor="Harmony_Sanctum" },
["Ignis Aeternum"] = { page="Pyros_(Reformed_Stripping_Path)", anchor="Ignis_Aeternum" },
-- Special headings on those pages include “The …”
["Celestial Harmony Sect"] = { page="Indigo_(Reformed_Stripping_Path)", anchor="The_Celestial_Harmony_Sect" },
["Guild of Golden Shadows"] = { page="Chrysos_(Reformed_Stripping_Path)", anchor="The_Guild_of_Golden_Shadows" },
["Azure Sentinel Sect"] = { page="Thalassa_(Reformed_Stripping_Path)", anchor="Azure_Sentinel_Sect" },
["Reverie Nebulous"] = { page="Nephele_(Reformed_Stripping_Path)", anchor="Reverie_Nebulous" },
["Eon Fellowship"] = { page="Glinos_(Reformed_Stripping_Path)", anchor="Eon_Fellowship" },
["Order of the Umbral Oracle"] = { page="Noctis_(Reformed_Stripping_Path)", anchor="Order_of_the_Umbral_Oracle" },
["Mystery of the Verdant Embrace"] = { page="Opsithe_(Reformed_Stripping_Path)", anchor="Mystery_of_the_Verdant_Embrace" },
["Conclave Illuminara"] = { page="Tarsica_(Reformed_Stripping_Path)", anchor="Conclave_Illuminara" },
["Sanctum Vitalis"] = { page="Plateau_(Reformed_Stripping_Path)", anchor="Sanctum_Vitalis" },
["Temple Alabaster"] = { page="Ivory_(Reformed_Stripping_Path)", anchor="Temple_Alabaster" },
["Court of the Ironclad"] = { page="Fenrir_(Reformed_Stripping_Path)", anchor="Court_of_the_Ironclad" },
["Accord Concordia"] = { page="Lukedu_(Reformed_Stripping_Path)", anchor="Accord_Concordia" },
["Mystery of the Stygian Veil"] = { page="Styx_(Reformed_Stripping_Path)", anchor="Mystery_of_the_Stygian_Veil" },
["Sylvan Fellowship"] = { page="Faun_(Reformed_Stripping_Path)", anchor="Sylvan_Fellowship" },
["Mystery of Red Mirth"] = { page="Silenus_(Reformed_Stripping_Path)", anchor="Mystery_of_Red_Mirth" },
["Rex Catonis"] = { page="Cato_(Reformed_Stripping_Path)", anchor="Rex_Catonis" },
["Sanctum Delphica"] = { page="Sisera_(Reformed_Stripping_Path)", anchor="Sanctum_Delphica" },
["Ordo Amicitia"] = { page="Erasmus_(Reformed_Stripping_Path)", anchor="Ordo_Amicitia" },
["Temple Illuminata"] = { page="Nikolaj_(Reformed_Stripping_Path)", anchor="Temple_Illuminata" },
}
local function cultLink(cultName)
if cultName == "Temple Aprobelle" or cultName == "Temple of Aprobelle" then
return "[[Temple_Aprobelle|Temple Aprobelle]]"
end
local t = cultTargets[cultName]
if not t then
return string.format("[[%s]]", cultName)
end
return string.format("[[%s#%s|%s]]", t.page, t.anchor, cultName)
end
--------------------------------------------------------------------------------
-- 8) ORG UNITS (Missionary echelons)
--------------------------------------------------------------------------------
local org = {
Lampade = { code="L", size=8 },
Hetairoi = { code="H", size=12 },
Dodekade = { code="D", size=18 },
Kleisthenes = { code="K", size=25 },
Stoa = { code="S", size=40 },
Thiasos = { code="T", size=60 },
}
local function pickOrgUnit(nextInt, sev)
if sev == "green" then
return pickFromList(nextInt, {"Lampade","Hetairoi"}, nil)
elseif sev == "yellow" then
return pickFromList(nextInt, {"Hetairoi","Dodekade","Kleisthenes"}, nil)
else
return pickFromList(nextInt, {"Kleisthenes","Stoa","Thiasos"}, nil)
end
end
--------------------------------------------------------------------------------
-- 9) OPS ALERT (Module:BassaridiaForecast)
--------------------------------------------------------------------------------
local wxOk, wx = pcall(require, "Module:BassaridiaForecast")
if not wxOk then wx = nil end
local function wxColor(level)
level = tonumber(level) or 0
if level == 3 then return "#f8d7da" end
if level == 2 then return "#ffe5cc" end
if level == 1 then return "#fff3cd" end
return "#d4edda"
end
local function opsCell(cityName, year, dayOfYear)
if not wx or type(wx.getOpsData) ~= "function" then return "n/a", nil end
local op = wx.getOpsData(cityName, year, dayOfYear)
if not op or not op.alert then return "n/a", nil end
local lvl = tonumber(op.alert.level) or 0
local label = tostring(op.alert.label or "Green")
local cond = tostring(op.condition or "")
local bg = wxColor(lvl)
local cell = string.format(
"<span style='background:%s;padding:2px 6px;border:1px solid #ccc;border-radius:10px;white-space:nowrap;'>%s</span>%s",
bg, label, (cond ~= "" and ("<br/><span style='font-size:90%;'>" .. cond .. "</span>") or "")
)
return cell, op
end
local function bumpSevWeather(sev, op)
if not op or not op.alert then return sev end
local lvl = tonumber(op.alert.level) or 0
local imp = op.impacts or {}
if lvl == 2 and sev == "green" then sev = "yellow" end
if lvl >= 3 then sev = "red" end
if (imp.ground == "Hazard" or imp.sea == "Closed" or imp.air == "No-Go") and sev == "green" then
sev = "yellow"
end
return sev
end
local function weatherTaskOverlay(op)
if not op or not op.alert then return "" end
local lvl = tonumber(op.alert.level) or 0
if lvl < 2 then return "" end -- Orange/Red only
local haz = tostring(op.alert.hazard or "weather disruption")
local imp = op.impacts or {}
local cond = tostring(op.condition or "")
local base = " Weather-driven tasking: " .. haz .. " (" ..
string.format("Gnd %s / Sea %s / Air %s", imp.ground or "OK", imp.sea or "OK", imp.air or "OK") .. ")."
if cond == "Storm" or cond == "Rain" then
return base .. " Clinic/relief queues stabilized; rumor bulletins posted; shrine-district lanes marked."
elseif cond == "Blizzard" or cond == "Snow" then
return base .. " Warming-shelter checks; fuel/medicine custody verified; travel guidance posted."
elseif cond == "Fog" then
return base .. " Navigation warnings and crowd guidance in port/canal corridors; panic narratives suppressed."
elseif cond == "Dust" then
return base .. " Mask discipline and vendor hygiene enforcement prioritized; indoor refuge advisories posted."
else
return base .. " Welfare checks and public messaging prioritized."
end
end
--------------------------------------------------------------------------------
-- 10) OPTIONAL: THREAT WATCH (Module:ReligiousEvents)
--------------------------------------------------------------------------------
local function parseReligiousEventsTable(wikitext)
local map = {}
wikitext = tostring(wikitext or "")
for line in wikitext:gmatch("[^\n]+") do
if line:match("^|%s") and not line:match("^|%-%s*$") then
local parts = mw.text.split(line:gsub("^|%s*", ""), "%s*||%s*")
if #parts >= 4 then
local city = stripWiki(parts[2])
local ev = stripWiki(parts[3])
local urg = stripWiki(parts[4])
if city ~= "" and ev ~= "" then
map[city] = {event=ev, urg=urg}
end
end
end
end
return map
end
--------------------------------------------------------------------------------
-- 11) OPTIONAL: ECON SIGNALS (EXPENSIVE; ONLY if showEcon=yes)
--------------------------------------------------------------------------------
local function getEconSignals()
local title = mw.title.new("General Port of Lake Morovia")
if not title then return {} end
local ok, content = pcall(function() return title:getContent() end)
if not ok or not content or content == "" then return {} end
local signals = {}
local text = normalizeSpaces(content)
for _, c in ipairs(cityOrder) do
local n = c.name
local hits = 0
local pattern = n:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])","%%%1")
for _ in text:gmatch(pattern) do
hits = hits + 1
if hits >= 6 then break end
end
if hits >= 3 then
signals[n] = {summary=string.format("High Port linkage (mentions: %d)", hits)}
end
end
return signals
end
--------------------------------------------------------------------------------
-- 12) TASK HELPERS
--------------------------------------------------------------------------------
local function T(text, sev) return {text=text, sev=sev} end
-- 10 tasks per cult (kept compact so the module remains maintainable)
local tasksByCult = {
["Order of Aurora Mystica"] = {
T("First-light conciliation at a guild hall; signatories recorded with Temple Bank clerks; follow-up meeting scheduled.","green"),
T("Sunrise clarity bulletin: correct ‘official omen’ rumors before queues form; speakers briefed on approved language.","green"),
T("Dawn home-visit circuit for anxious households; practical referrals routed through shrine stewards.","green"),
T("Procession lane rehearsal for a morning rite; crush points marked; stewards drilled on staggered entry.","yellow"),
T("Targeted pamphlet sweep: remove forged ‘dawn decree’ leaflets; source stalls logged for follow-up.","yellow"),
T("Joint dawn patrol with civic stewards on canal/rail approaches; lighting faults logged; intimidation reports collected.","yellow"),
T("Witness-support escort at daybreak: safe path confirmed; hostile crowd avoidance plan posted.","yellow"),
T("Emergency dawn ceasefire convened after overnight clashes; terms read publicly; retaliation pledges collected.","red"),
T("Crisis dawn-vigil stabilization after a major scare; grief care + calm stations opened; clinic corridors protected.","red"),
T("High-risk dawn convoy escort for sacred/custody items; route denial and decoy timing implemented.","red"),
-- +8 added
T("Hilltop shrine schedule reset for first-light hymns; latecomer lanes separated; attendance log updated.","green"),
T("Dawn-worksite blessing for shift crews; safety reminders appended; foreman sign-off obtained.","green"),
T("Morning-lamp distribution and repair tickets; dark alleys flagged; follow-up patrol time posted.","green"),
T("Sunrise complaint intake booth at market edge; triage tags assigned; referrals routed to Harmony Sanctum.","yellow"),
T("Counter-badge check on ‘Aurora stewards’; impostors documented; credentials verified with Temple Bank.","yellow"),
T("Dawn festival route reroute after hazard report; new signage posted; crowd cadence rehearsed.","yellow"),
T("Red-posture dawn broadcast: scripted calm loop; speaker rotations scheduled; rumor nodes targeted for removal.","red"),
T("High-risk witness staging at daybreak: staggered arrivals; decoy meeting point; custody signatures secured.","red"),
},
["Harmony Sanctum"] = {
T("Neighborhood mediation circle: settle a vendor–labor dispute; written concord filed.","green"),
T("Public etiquette coaching at shrine square; queue discipline and nonviolent refusal scripts practiced.","green"),
T("Household conflict visits after complaint spike; behavior plan issued; check-in date set.","green"),
T("Festival crowd-lane plan review; bottlenecks marked; stewards assigned to separation corridors.","yellow"),
T("Rumor triage: map source nodes feeding panic; corrective bulletin cycle deployed.","yellow"),
T("Transit choke-point calming patrol; commuter separation corridor opened; escalation triggers documented.","yellow"),
T("Clerk oversight coaching after audit flags; corrective training delivered; sign-off obtained.","yellow"),
T("Emergency stabilization during riot flare; dispersal lanes held; care stations deployed at safe corners.","red"),
T("Night calm-litany enforcement after a panic episode; supervised dispersal and reunification tent opened.","red"),
T("Crisis reconciliation hearing after violence; restitution ledger initiated; witness protection arranged.","red"),
-- +8 added
T("Balance-rite for a disputed property line; boundary walk performed; neutral witnesses recorded.","green"),
T("Structured apology conference after school conflict; restitution terms set; guardian contacts logged.","green"),
T("Neighborhood rotation plan for shared resources; schedule posted; conflict triggers documented.","green"),
T("Inter-ward speaking forum moderated; turn order enforced; heckler removal protocol posted.","yellow"),
T("Counter-rumor ‘myth vs policy’ bulletin; distribution assigned by street; speaker script packet issued.","yellow"),
T("Market-stall spacing enforcement after crush near fountain; lane widths measured; re-layout posted.","yellow"),
T("Emergency anti-reprisal pact after stabbing; escort corridors established; no-retaliation oaths collected.","red"),
T("Mass grievance intake after unrest; testimony queue metered; witness shielding arranged; outcomes logged.","red"),
},
["Ignis Aeternum"] = {
T("Forge discipline rite: safety roles assigned; firewatch roster posted; tool control verified.","green"),
T("Heat advisory posting for labor crews; hydration discipline enforced; shade points designated.","green"),
T("Brazier and lantern inspection at a festival lane; safe distances marked; fuel rules posted.","green"),
T("Brushfire-risk patrol: illegal burns curtailed; emergency water points labeled; reporting chain rehearsed.","yellow"),
T("Arson-scare containment: inspect fuel stores and service corridors; contractors re-badged; locks audited.","yellow"),
T("Rumor suppression after ‘cursed flame’ claims; sanctioned narrative posted; agitator list started.","yellow"),
T("Kiln/oven quarter audit after sabotage chatter; witness statements logged; repair tickets issued.","yellow"),
T("Emergency response to a flare incident: evacuation lanes opened; burn triage support and calm messaging deployed.","red"),
T("Crisis crowd control at a pyre/procession pinch point; stampede pressure relieved; reunification tent opened.","red"),
T("High-risk perimeter posting at depots after credible arson threat; entry controlled; night watch rotated.","red"),
-- +8 added
T("Workshop ventilation check; soot exposure warnings posted; compliance sign-offs collected.","green"),
T("Approved fuel rationing note posted for braziers; prevent hoarding; vendor guidance issued.","green"),
T("Tool-blessing and inventory day; missing items reconciled; suspect list deferred to Golden Shadows.","green"),
T("Firebreak drill coordination near warehouses; muster lanes marked; timing rehearsed with stewards.","yellow"),
T("Controlled relight protocol after blackout; ignition permits checked; unsafe relights stopped.","yellow"),
T("Inspection sweep for illegal accelerants; confiscations logged; storage seals verified.","yellow"),
T("Major blaze containment support: perimeter discipline; water point control; after-action safety decree issued.","red"),
T("Arson-ring disruption support: suspect workshops sealed; witness protection arranged; public guidance broadcast.","red"),
},
["Celestial Harmony Sect"] = {
T("Cosmic-order lecture at shrine school; reduce anxiety about sky-signs; practical safety guidance appended.","green"),
T("Guided breathing session for canal workers; stress reduction paired with shift-safety reminder.","green"),
T("Calendar correction bulletin: prevent hoarding after false eclipse/omen claims; approved text distributed.","green"),
T("Quiet-zone enforcement near hospitals and shelters; regulated chanting and crowd spacing verified.","yellow"),
T("Counterfeit star-chart inquiry: vendor interviews; seizure log compiled; correlations forwarded.","yellow"),
T("Fog-night guidance patrol: speakers briefed; route markers placed; panic triggers documented.","yellow"),
T("Post-event debrief: update approved narratives; distribute scripts to shrine speakers; archive record filed.","yellow"),
T("Crisis counseling during mass scare; supervised calm stations opened; clinic access protected.","red"),
T("Emergency embedded messaging team with civic stewards during unrest; prevent retaliatory panic; crowd lanes held.","red"),
T("High-risk omen-driven gathering dispersed under controlled cadence; separation corridors opened; arrests avoided where possible.","red"),
-- +8 added
T("Twin-balance mediation between rival guild blocs; alternating speaking order enforced; compromise text drafted.","green"),
T("Opposites-pair workshop for stewards; role swapping practiced; conflict sensitivity notes filed.","green"),
T("Port/market ‘cycle briefing’: scarcity and surplus explained; calm purchasing guidance posted.","green"),
T("Symbol dispute review (Indigo/Momiji misuse); offending banners removed; replacement signage approved.","yellow"),
T("Diplomatic liaison support for visiting delegations; translation scripts issued; etiquette rules posted.","yellow"),
T("Coordinated counter-panic cadence: staggered announcements; prevent feedback loops; rumor nodes mapped.","yellow"),
T("Emergency unity convocation after sectarian flare; ceasefire terms read; neutral escorts assigned.","red"),
T("Mass ominal panic response: structured dispersal waves; calm stations staffed; clinic routes protected.","red"),
},
["Guild of Golden Shadows"] = {
T("Quiet intake: log forged vouchers and coercion attempts; route evidence to Temple Bank without public escalation.","green"),
T("Market observation rotation; identify counterfeit seals and pressure networks; ledger packet assembled.","green"),
T("Discrete tracing of a forged permit chain; map printer stalls and broker intermediaries; file correlation report.","green"),
T("Undercover purchase of fake shrine/custody seals; seize plates and ledgers; custody memo prepared.","yellow"),
T("Intercept suspicious manifest and cross-check corridor registry for evasions; flag repeats for inspection teams.","yellow"),
T("Witness-support arrangement for a threatened vendor; relocation and stipend bridge authorized.","yellow"),
T("Rumor suppression operation: remove false broadsheets; replace with official bulletin; source nodes catalogued.","yellow"),
T("Night extraction of compromised informant; safe route screened; temporary housing arranged under seal.","red"),
T("Emergency counter-coercion action during crowd surge; stabilize lanes; evidence chain protected.","red"),
T("High-risk liaison brief to War League contact: attribution summary + prevention plan; sealed addendum filed.","red"),
-- +8 added
T("Silent audit of a clerk’s ledger drawer; discrepancy list sealed; retraining notice drafted.","green"),
T("Courier-route verification for stipend packets; handoff witnesses arranged; route hazards mapped.","green"),
T("Counterfeit stamp reference kit issued to stewards; detection steps taught; escalation chain posted.","green"),
T("Targeted sweep for extortion collectors; victim intake protected; suspect descriptions logged.","yellow"),
T("Dockside inventory double-check after ‘lost crate’ claim; reconciliation performed; fraud flags filed.","yellow"),
T("Controlled public correction of a forged notice; replace postings; photograph originals; printer path traced.","yellow"),
T("Major embezzlement exposure: emergency ledger seizure; suspect detention support; restitution memo prepared.","red"),
T("High-risk port gate fraud surge: inspection teams reinforced; entry metering; evidence chain preserved.","red"),
},
["Azure Sentinel Sect"] = {
T("Waterfront perimeter walk; safety rope checks along canal districts; hazards logged for civil works.","green"),
T("Storm-drain inspection with engineers; blockages cleared; maintenance tickets filed.","green"),
T("Port-house briefing: embarkation discipline, theft prevention, and safe-lane etiquette posted.","green"),
T("Medical-supply escort across bridges; accountability signatures collected; chain-of-custody verified.","yellow"),
T("Harbor rumor correction: suppress false quarantine/closure claims; prevent stampede behavior at gates.","yellow"),
T("SAR standby after storm warnings; muster roster checked; equipment count verified.","yellow"),
T("Debris clearance coordination after gale; hazard markings placed; access lanes reopened in phases.","yellow"),
T("Emergency flood response: evacuation guidance at waterfront shrines; calm stations maintained; shelter intake controlled.","red"),
T("High-risk escort of officials through port-gate crowd; separation corridor enforced; decoy route staged.","red"),
T("Crisis harbor-closure messaging under red posture: notices distributed; prevent vigilante enforcement at ramps.","red"),
-- +8 added
T("Canal bridge inspection; loose stone and rail faults flagged; repair tickets prioritized.","green"),
T("Dockworker conduct briefing; safe-lift rules posted; harassment complaints intake opened.","green"),
T("Water purity spot-check at fountains; contamination warnings posted; sampling request filed.","green"),
T("Controlled queue plan at ferry ramps; entry tokens issued; crowd cadence rehearsed.","yellow"),
T("Suspicious skiff check support; manifests reviewed; contraband seized; custody signatures obtained.","yellow"),
T("Storm-surge rehearsal: shelter route posted; boat tie-down checks; public update schedule set.","yellow"),
T("Port lockdown support after violence; civilian zones cleared; rescue lanes protected; communications centralized.","red"),
T("Mass casualty waterfront incident: triage staging; family intake; perimeter discipline; after-action memo sealed.","red"),
T("CLASSIFIED tasking: covert counter-assassin sweep along port approaches; decoy route staged; boat manifests cross-checked; suspect surveillance handed off under seal; public posture maintained as routine SAR readiness.","red"),
},
["Reverie Nebulous"] = {
T("Dream-circle with strict moderation; de-escalation emphasized; referral cards issued for high anxiety.","green"),
T("Quiet art support for children after a scare; parent coaching provided; school liaison note filed.","green"),
T("Sleep-hygiene teaching for shift workers; rest scheduling guidance posted; rumor triggers explained.","green"),
T("Rumor mapping: identify dream narratives feeding panic; corrective story deployed with approved phrases.","yellow"),
T("Witness interviews after trance-gathering complaint; compliance conditions issued; repeat offenders logged.","yellow"),
T("Night calming near transit nodes; chanting regulated; separation corridors planned to prevent crush behavior.","yellow"),
T("Post-event narrative edit: reduce retaliatory impulses; speaker training conducted; script packet distributed.","yellow"),
T("Containment support for mass panic dream episode; supervised rest zones opened; clinic corridor protected.","red"),
T("Emergency counseling for families after fatal incident; grief rites paired with aid referrals.","red"),
T("High-risk ‘dream-breach’ report assessed with Aprobelle liaisons; misinformation suppressed; controlled bulletin issued.","red"),
-- +8 added
T("Dream journal distribution; privacy rules explained; escalation criteria posted for severe episodes.","green"),
T("Quiet ‘waking boundary’ lesson; discourage prophetic claims; practical coping steps posted.","green"),
T("Artist liaison with civic stewards; performance permits checked; safe audience limits posted.","green"),
T("Sweep for fraudulent ‘dream sellers’; counterfeit charms seized; victim refunds routed through Amicitia.","yellow"),
T("Night-market calming protocol: light levels raised; exit lanes marked; chant limits enforced.","yellow"),
T("Repeat nightmare cluster triage; trigger map compiled; clinic referrals issued; follow-up schedule set.","yellow"),
T("Mass hysteria response after shared-vision rumor; calm stations expanded; scripts looped; arrests minimized.","red"),
T("Violent trance incident containment: victim protection; offender transfer; witness shielding; records sealed.","red"),
},
["Eon Fellowship"] = {
T("Timing audit at market gates: shift rotations clarified; reduce conflict from missed windows.","green"),
T("Public calendar correction bulletin; prevent rumor-driven delays and missed rites; signage posted.","green"),
T("Clinic/shrine entry metering drill: queue cadence tested; stewards trained on timing scripts.","green"),
T("Witness-arrival scheduling for a hearing; time windows staged to deter intimidation; escort plan posted.","yellow"),
T("Transit-flow smoothing at rail/canal chokepoint; signage updated; alternate timing lane opened.","yellow"),
T("Record review: repeated stipend-delay disputes analyzed; corrective SOP issued; clerk sign-off obtained.","yellow"),
T("Night timing plan during fog disruption; safe intervals posted; navigation warnings coordinated.","yellow"),
T("Emergency stabilization after cascading delays: volunteer surge roster synchronized; safe movement hours posted.","red"),
T("Crisis schedule triage during unrest: curfew windows clarified; dispersal routes timed and enforced.","red"),
T("High-risk timing escort for custody transfer; decoy timing used; route denial staged.","red"),
-- +8 added
T("Endurance briefing for patrol rotations; fatigue flags logged; relief roster posted.","green"),
T("Winterization checklist posted (wind, ice, flood); supply needs tallied; maintenance tickets filed.","green"),
T("Shelter readiness audit; blanket inventory checked; entry rules posted; complaint intake opened.","green"),
T("Ration-line cadence enforcement; entry stamps issued; fights deterred; incident log filed.","yellow"),
T("Gate sabotage rumor response; key control audit; access tightened; night watch schedule posted.","yellow"),
T("Critical-utility staffing pact mediation; interim rota signed; enforcement stewards assigned.","yellow"),
T("Storm emergency shelter activation: intake discipline; ration control; rumor suppression; clinic route protected.","red"),
T("Civil disorder deterrence posture: curfew windows broadcast; safe lanes enforced; ringleaders detained.","red"),
},
["Order of the Umbral Oracle"] = {
T("Quiet-night rite with strict entry control; visitor conduct posted; disturbance triggers explained.","green"),
T("Counseling for anxious households after nightmare spike; referral paths established; follow-up logged.","green"),
T("Night watch at shrine steps to deter harassment; steward coordination and incident ledger maintained.","green"),
T("Risk bulletin for shadow-sign nights; discourage panic gatherings at transit nodes; routes marked.","yellow"),
T("Investigation of anonymous pamphlets claiming ‘oracle sanction’; counterfeit marks documented; plates traced.","yellow"),
T("Audit of unsanctioned night gatherings; compliance restrictions issued; repeat venues logged.","yellow"),
T("Post-incident narrative discipline: speaker scripts updated; rumor sources mapped; correction bulletins deployed.","yellow"),
T("Containment support for ‘demonic sign’ rumor under red posture: controlled messaging delivered; calm stations opened.","red"),
T("Witness protection for informant threatened by cult rival; safehouse route planned; decoy escort used.","red"),
T("Crisis nocturnal dispersal after crowd surge; safe corridors opened; reunification tent activated.","red"),
-- +8 added
T("Nocturnal observation log sweep; key anomalies summarized; sealed note sent to Temple Bank.","green"),
T("Lamp-and-lock check on vulnerable alleys; household guidance posted; repair tickets filed.","green"),
T("Confidential witness intake at a night gate; testimony recorded; protection options listed.","green"),
T("Blackout exploitation patrol; temporary lighting placed; entry control enforced; suspects logged.","yellow"),
T("Cultic graffiti catalog; symbols photographed; cleanup ordered; copycat deterrence notice posted.","yellow"),
T("Stalking complaint response; escort plan staged; restraining paperwork initiated; incident ledger updated.","yellow"),
T("Assassination rumor triage: safe corridors established; witness relocation; evidence sealed; briefings issued.","red"),
T("Kidnapping response support: gate checks tightened; informant intake opened; safehouse activation logged.","red"),
},
["Mystery of the Verdant Embrace"] = {
T("Canal cleanup rite; silt removed; flow restored; maintenance log filed.","green"),
T("Seed blessing for gardeners; pest-prevention guidance distributed; vendor hygiene note appended.","green"),
T("Wetland path maintenance; travel warnings posted; drowning prevention markers installed.","green"),
T("Aqueduct/sluice audit: leakage mapped; repair crews coordinated; access rules posted at pump houses.","yellow"),
T("Field mediation over irrigation rights; shared schedule drafted; signatures collected.","yellow"),
T("Vector mitigation at swamp edges; standing water treated; guidance posted at shelters.","yellow"),
T("Post-storm debris clearance with volunteers; hazard markings placed; lanes reopened in phases.","yellow"),
T("Emergency flood relief: sandbag lines, food staging, shelter support; calm messaging maintained.","red"),
T("Crisis canal-breach response: closure and reroute; crowd calming in shrine districts; clinic corridor protected.","red"),
T("High-risk contamination response at water points; sampling escorted; panic narratives suppressed.","red"),
-- +8 added
T("Apothecary batch check for adulterants; approved sources posted; corrective warnings issued.","green"),
T("Community garden rota mediation; tool access rules posted; theft prevention signage installed.","green"),
T("Midwife support circuit; recovery herbs issued; follow-up visits scheduled; privacy rules posted.","green"),
T("Illness cluster mapping near wetlands; symptom intake standardized; clinic referrals issued.","yellow"),
T("Contaminated produce recall support; vendor notices posted; refunds coordinated with Amicitia.","yellow"),
T("Pest outbreak triage briefing; safe treatment guidance; livestock protection steps posted.","yellow"),
T("Epidemic response support: quarantine boundaries marked; care kits staged; misinformation suppressed.","red"),
T("Poisoning event response: antidote routing; source trace; suspect packet assembled; water access controlled.","red"),
},
["Conclave Illuminara"] = {
T("Night-sky lecture: explain calendars; reduce omen anxiety; post practical safety guidance.","green"),
T("Quiet meditation ward at shrine; breathing protocols taught; de-escalation reminders posted.","green"),
T("Lantern-route inspection; reflective markers placed; hazard steps flagged.","green"),
T("Dream-space custody audit: offerings catalogued; safety signage posted; access rules clarified.","yellow"),
T("Counterfeit moon-token investigation; vendor interviews; seizure log compiled; restitution note drafted.","yellow"),
T("Controlled night gathering after rumor flare; calm-litany enforced; dispersal cadence rehearsed.","yellow"),
T("Blackout safety drill for storm season: guides staged; warning ropes placed; shelter route posted.","yellow"),
T("Emergency anti-panic messaging after ‘demonic sign’ claim; speaker scripts distributed; rumor sources logged.","red"),
T("Escort for sensitive nocturnal rite during unrest; perimeter observers posted; clinic access protected.","red"),
T("High-risk crowd-control during an overfull vigil; separation corridors held; reunification tent activated.","red"),
-- +8 added
T("Lunar-cycle briefing for stewards; ‘dark segment’ messaging standardized; posting schedule set.","green"),
T("Archive copy check: calendars and proclamations reconciled; corrections issued; stamps verified.","green"),
T("Diplomatic reading for visiting officials; neutral phrasing drafted; public reassurance script prepared.","green"),
T("Forgery sweep for ‘Conclave decree’ sheets; originals photographed; printer path traced.","yellow"),
T("Vigil capacity enforcement; entry tokens issued; exit lanes marked; medics staged.","yellow"),
T("Rumor correction loop after eclipse claims; public broadcasts timed; panic buying deterred.","yellow"),
T("Mass fear response after prolonged blackout; calm stations opened; shelter routes protected; patrol liaison engaged.","red"),
T("Crisis interpretive bulletin during unrest: ‘cycle framing’ delivered hourly; retaliatory panic prevented.","red"),
},
["Sanctum Vitalis"] = {
T("Clinic hygiene audit: water barrels sealed; entry protocols posted; handwashing station checks logged.","green"),
T("Vaccination schedule coordination with stewards; consent ledger reconciled; follow-up appointments scheduled.","green"),
T("Triage coaching circle: calm scripts practiced; escalation triggers clarified; volunteer assignments posted.","green"),
T("Food-handling inspection at market stalls; spoiled goods removed; corrective warnings issued.","yellow"),
T("Quarantine rumor suppression: correct false ‘plague’ claim; stabilize clinic queues; calm stations opened.","yellow"),
T("Vector mitigation near wetlands; standing water treated; guidance posted at dorms and shelters.","yellow"),
T("Witness interviews after ‘poisoned well’ claim; evidence packet assembled; sampling escorted.","yellow"),
T("Emergency fever center support: intake metering + calm stations established; separation corridor enforced.","red"),
T("Crisis medical escort for officials through crowd; protected clinic route held; decoy timing staged.","red"),
T("High-risk contamination response: water points shut temporarily; alternate distribution controlled; panic narratives suppressed.","red"),
-- +8 added
T("Spring/well inspection; contamination risks logged; repair tickets filed; public boil guidance posted if needed.","green"),
T("Clinic staff rota stabilization; break schedule enforced; burnout flags logged; relief volunteers assigned.","green"),
T("Worksite injury-prevention briefing; hazards recorded; employer compliance sign-off obtained.","green"),
T("Respiratory illness surge triage; mask distribution staged; ventilation rules posted; clinic flow adjusted.","yellow"),
T("Food-borne illness scare response; vendor recall notices; refund guidance; culprit investigation packet started.","yellow"),
T("Overcrowded clinic gate plan; entry metering tightened; conflict scripts deployed; escort corridor protected.","yellow"),
T("Mass casualty support: triage expansion; supply convoy prioritization; family intake; mortality ledger sealed.","red"),
T("Waterborne outbreak response: quarantine lines; alternate water distribution; daily briefings; misinformation suppressed.","red"),
},
["Temple Alabaster"] = {
T("Public conduct rite at shrine; rules reissued; volunteers coached on nonviolent enforcement.","green"),
T("Counseling circle for fearful households; practical aid referrals arranged; follow-up log kept.","green"),
T("Relief custody check: inventory counted; seals verified; theft-prevention signage posted.","green"),
T("Vendor compliance sweep for counterfeit purity seals; seizures logged; corrective warnings issued.","yellow"),
T("Witness interviews after shrine vandalism; sworn statements taken; intimidation complaints logged.","yellow"),
T("Night watch rotation on shrine steps after threat chatter; steward roster posted.","yellow"),
T("Escort of sacred item to storage; chain-of-custody verified; route screening documented.","yellow"),
T("Emergency crowd control during pilgrimage surge; lanes marked; calm stations opened; reunification tent active.","red"),
T("Crisis shelter activation in stone precinct during storm; intake discipline enforced; clinic route protected.","red"),
T("High-risk perimeter reinforcement after credible attack rumor; entry controlled; decoy dispersal route staged.","red"),
-- +8 added
T("Purity inspection of public wash points; contamination warnings posted; maintenance tickets filed.","green"),
T("Serenity hour enforcement in market lanes; noise caps posted; disputes mediated; incident log updated.","green"),
T("Sanitation compliance coaching for vendors; corrective steps issued; follow-up date scheduled.","green"),
T("Crowd cooling protocol outside shrine gates; separation lanes held; harassment complaints intake opened.","yellow"),
T("Contaminated goods sweep (dye/soap/food); items pulled; source traced; public reassurance posted.","yellow"),
T("Minor desecration repair coordination; evidence preserved; restoration rite scheduled; offenders logged.","yellow"),
T("Major desecration fallout: crowd calmed; arrests coordinated; restoration rites performed; retaliation deterred.","red"),
T("Toxic spill response support: exclusion zone enforced; cleanup supervised; health referrals issued; messaging standardized.","red"),
},
["Court of the Ironclad"] = {
T("Volunteer discipline training; nonviolent restraint emphasized; reporting scripts practiced.","green"),
T("Public order presence at shrine steps; etiquette enforced; minor disputes mediated on-site.","green"),
T("Oath workshop: pledge cards sealed for civic stewards; compliance expectations posted.","green"),
T("Route hardening walk: broken lamps and unsafe crossings flagged; repair tickets filed.","yellow"),
T("Contraband confiscation support; evidence logged for Temple Bank clerks; custody chain verified.","yellow"),
T("Night watch on canal bridges after sabotage chatter; checkpoint discipline enforced; escorts scheduled.","yellow"),
T("Post-incident intimidation review: complaints investigated; corrective discipline applied; report filed.","yellow"),
T("High-risk escort for witness leaving hearing; safehouse transfer arranged; decoy route used.","red"),
T("Emergency deterrence patrol after riot flare; separation corridors opened; care stations deployed.","red"),
T("Crisis reinforcement at clinic gate during panic surge; queue discipline established; dispersal cadence enforced.","red"),
-- +8 added
T("Endurance drill briefing; heat/fatigue limits posted; rotation enforcement logged.","green"),
T("Equipment discipline check; missing items reconciled; chain-of-command sign-off obtained.","green"),
T("Desert-procession conduct rules posted; hydration points marked; medical standby coordinated.","green"),
T("Rival faction confrontation risk patrol; meeting sites mapped; entry controls tightened; warnings issued.","yellow"),
T("Bloodmoon rumor suppression; discourage panic gatherings; authorized statement posted; agitator list started.","yellow"),
T("Arrest processing support after brawl surge; evidence tagging enforced; witness separation maintained.","yellow"),
T("Sectarian violence surge: unified deterrence posture; safe corridors held; ringleaders detained; reprisals prevented.","red"),
T("Critical sabotage threat: depot perimeter locked; entry metering; night watches doubled; public notice issued.","red"),
},
["Accord Concordia"] = {
T("Council mediation between neighborhood elders; written concord signed; follow-up date scheduled.","green"),
T("Public forum moderation; speaking order enforced; agitator removal avoided through de-escalation.","green"),
T("Vendor-stall settlement: restitution terms logged; compliance expectations posted.","green"),
T("Civic etiquette training for steward teams; de-escalation scripts practiced; lane protocol rehearsed.","yellow"),
T("Inter-cult boundary meeting; shared space rules posted at shrine gates; signage installed.","yellow"),
T("Rumor containment after inflammatory broadsides; corrective bulletin cycle launched; sources logged.","yellow"),
T("Audit of district clerk office after complaint surge; corrective plan countersigned; retraining scheduled.","yellow"),
T("Emergency truce enforcement during street clash; separation lines held; medical access protected.","red"),
T("Crisis reconciliation hearing after riot; pledge collection + witness support; retaliation prevention enforced.","red"),
T("High-risk mediation under red posture: rival leaders separated; agreement read publicly; dispersal routes staged.","red"),
-- +8 added
T("Harmony Gardens work party coordination; mixed crews paired; grievances logged; follow-up circle scheduled.","green"),
T("Shared-meal hosting plan; seating map prevents rival clustering; steward assignments posted.","green"),
T("Welcome rite for new migrants/work crews; support contacts listed; exploitation warnings posted.","green"),
T("Feast sabotage risk triage; ingredient chain verified; kitchen access controlled; guard liaison engaged.","yellow"),
T("Inter-guild boycott settlement; compromise pledge signed; enforcement stewards appointed; public notice issued.","yellow"),
T("Youth rivalry cooling via structured games; chaperones assigned; penalties posted for violence.","yellow"),
T("Emergency unity convocation after fatal clash; ceasefire sworn; escort corridors established; reprisals deterred.","red"),
T("Mass panic calming centers opened; volunteer surge; hourly briefings delivered; rumor nodes suppressed.","red"),
},
["Mystery of the Stygian Veil"] = {
T("Funerary custody check: offerings catalogued; tomb precinct rules posted; candle/fire safety verified.","green"),
T("Grief counseling circle; practical aid referrals arranged; follow-up home visits scheduled.","green"),
T("Mortuary hygiene audit; disease-prevention protocols verified; sanitation failures logged.","green"),
T("Rumor suppression after ‘restless dead’ claims; evidence bulletin issued; agitation triggers tracked.","yellow"),
T("Escort for body transfer; route secured; witnesses logged; custody chain verified.","yellow"),
T("Night vigil supervision to prevent stampede or retaliatory violence; separation corridor planned.","yellow"),
T("Witness interviews after desecration; sworn statements sealed; intimidation complaints recorded.","yellow"),
T("Crisis response after mass death incident: grief triage + order restoration; calm stations opened.","red"),
T("Emergency calm stations at cemetery gate during panic; supervised dispersal coordinated; clinic access protected.","red"),
T("High-risk memorial crowd control under red posture; perimeter held; reunification tent activated.","red"),
-- +8 added
T("Rite of transition for a recovering patient; symbolic veil-lifting; community support plan logged.","green"),
T("Inheritance dispute mediation after death; fair division terms drafted; coercion checks recorded.","green"),
T("Cemetery path safety inspection; uneven steps flagged; lantern markers placed; maintenance tickets filed.","green"),
T("Dusk procession route reset after hazard; entry cadence rehearsed; pinch points staffed.","yellow"),
T("Grave-robbery scare investigation support; patrol liaison; witness intake; rumor correction posted.","yellow"),
T("Overcrowded vigil management; capacity limits posted; exit lanes marked; medics staged.","yellow"),
T("After-violence mourning containment; safe corridors held; retaliation ban sworn; witness shielding arranged.","red"),
T("Mass panic after ‘demonic figure’ report near tomb precinct; authoritative denial; agitator detention; calm stations expanded.","red"),
},
["Sylvan Fellowship"] = {
T("Forest-path maintenance; travel warnings posted for pilgrims; hazard markers refreshed.","green"),
T("Wildlife safety briefing; reduce accidents on shrine trails; reporting chain posted.","green"),
T("Ecological repair planning: volunteer crews scheduled; debris staging plan filed.","green"),
T("Herb/timber vendor conduct audit; sustainable collection rules enforced; repeat violators logged.","yellow"),
T("Brushfire-watch patrol; illegal burns suppressed; emergency water points marked.","yellow"),
T("Mediation between trappers and villagers; shared schedule signed; conflict triggers identified.","yellow"),
T("Rumor containment after ‘forest curse’ claim; corrective bulletin cycle run; agitator list started.","yellow"),
T("Emergency shelter staging in forest precinct during storm; intake controlled; calm messaging enforced.","red"),
T("Crisis search for missing pilgrim after fog; guides deployed; safe corridors opened.","red"),
T("High-risk evacuation support under red weather alert; trail closures enforced; reunification tent activated.","red"),
-- +8 added
T("Trailhead sign refresh: safe routes marked; dangerous gullies flagged; escort contacts posted.","green"),
T("Livestock safety day; grazing boundaries walked; injury checks; neighbor oaths exchanged.","green"),
T("Waterway cleanup coordination; contamination risks logged; volunteer rota posted.","green"),
T("Illegal poaching/logging inquiry; evidence gathered; offender descriptions logged; patrol liaison engaged.","yellow"),
T("Flood/mud hazard reroute postings; bridge warnings issued; rescue staging areas marked.","yellow"),
T("Farmer–rancher conflict triage; grazing calendar set; penalties defined; enforcement stewards assigned.","yellow"),
T("Major flood response: shelter intake; animal rescue; public briefings; rumor suppression enforced.","red"),
T("Violent land access dispute: containment support; arrests coordinated; restitution terms drafted; reprisals deterred.","red"),
},
["Mystery of Red Mirth"] = {
T("Festival conduct briefing; prevent brawls and dangerous dares; de-escalation scripts posted.","green"),
T("Joy-warden patrol: intervene early in disputes; stabilize lines outside taverns and music halls.","green"),
T("Volunteer steward training: safe celebration guidance and nonviolent intervention.","green"),
T("Vendor compliance audit for intoxicants; counterfeit batches seized; sanitation notes issued.","yellow"),
T("Crowd choreography for parade route; crush-risk minimized; pinch points marked.","yellow"),
T("Rumor suppression after scandal broadsides; approved narrative posted; source stalls logged.","yellow"),
T("Night watch at tavern row after violence chatter; calm messaging distributed; escorts staged.","yellow"),
T("Emergency stabilization after riot flare; separation lanes + care stations deployed; clinic access protected.","red"),
T("Crisis safe escort for a key witness leaving hearing; safehouse transfer arranged; decoy route used.","red"),
T("High-risk crowd-control during a packed revel; dispersal cadence enforced; reunification tent activated.","red"),
-- +8 added
T("Mask/prop safety inspection for performers; sharp edges removed; permit list verified.","green"),
T("Controlled wine dosing guidance posted; hydration points marked; refusal rights explained.","green"),
T("Aftercare check for revelers; transport escorts staged; harassment complaints intake opened.","green"),
T("Stage rigging sabotage check; inspection log filed; access limited; suspects noted.","yellow"),
T("Brawl-prone venue warning and temporary closure protocol; staff trained; incident ledger updated.","yellow"),
T("Parade route split after crush-risk alert; alternate route posted; crowd cadence rehearsed.","yellow"),
T("Stampede incident response: triage lanes; family reunification; safety reforms mandated; public statement issued.","red"),
T("Coordinated sabotage campaign response: permit freeze; targeted arrests; controlled messaging; patrol liaison engaged.","red"),
},
["Rex Catonis"] = {
T("Civic order proclamation at court square; conduct rules reissued; complaint intake organized.","green"),
T("Docket triage for petty disputes; mediation attempted before formal hearing; outcomes logged.","green"),
T("Steward training on lawful dispersal and calm messaging; escalation triggers clarified.","green"),
T("Municipal fines ledger audit: correct corrupt entries; restitution schedule drafted.","yellow"),
T("Vendor permit compliance sweep; counterfeit permits seized; corrective notices posted.","yellow"),
T("Rumor suppression after ‘illegal decrees’ claim; clarifying bulletin posted; speaker scripts updated.","yellow"),
T("Escort for court clerks during sensitive hearing; intimidation deterrence; safe route posted.","yellow"),
T("Emergency order restoration after riot; curfew messaging + dispersal corridors; clinic access protected.","red"),
T("Crisis protection of court archives after arson threat; perimeter posted; custody chain verified.","red"),
T("High-risk courthouse crowd control under red posture; separation corridors held; arrests minimized.","red"),
-- +8 added
T("Permit renewal clinic; expired permits reissued; lawful signage posted; fine waivers granted for compliance.","green"),
T("Public notice board audit; false postings removed; official stamps verified; clerk sign-off obtained.","green"),
T("Restitution schedule review; payment plan adjusted; guarantors recorded; Temple Bank copy filed.","green"),
T("Price gouging complaint sweep; violations documented; fines assessed; public deterrence notice posted.","yellow"),
T("Curfew violation surge response; warning protocol standardized; repeat offenders flagged; incident log filed.","yellow"),
T("Gang intimidation intake; victim protection options posted; suspect descriptions routed to Ironclad.","yellow"),
T("Mass corruption scandal response: ledger seizure; official suspensions; prosecution packets assembled; public guidance issued.","red"),
T("Coordinated violence response: unified command liaison; detention processing; court schedule triage; after-action report filed.","red"),
},
["Sanctum Delphica"] = {
T("Archive custody inspection: seals verified; storage conditions checked; humidity warnings posted.","green"),
T("Scribe training on custody chain; convoy paperwork rehearsed; handoff scripts practiced.","green"),
T("Pilgrim etiquette workshop at library shrine; quiet-zone enforced; lane discipline posted.","green"),
T("Index confiscated pamphlets for signature motifs; correlation packet forwarded; source stalls logged.","yellow"),
T("Witness interviews after shrine vandalism; sworn statements taken; intimidation complaints recorded.","yellow"),
T("Doctrinal quarantine assistance: entry controls + rumor suppression; approved signage posted.","yellow"),
T("Court packet assembly for desecration suspects; evidence collated; custody memo sealed.","yellow"),
T("Night watch rotation at archive stacks following arson threat; entry controlled; escort-only rule applied.","red"),
T("Escort sensitive artifact transfer; route denial markers deployed; decoy timing staged.","red"),
T("Emergency custody lockdown after breach attempt; relocation plan activated; public narrative kept neutral.","red"),
-- +8 added
T("Forecast briefing for stewards: likely choke points listed; preventive signage posted; shift plan updated.","green"),
T("Public ‘clarity notice’ explaining rumor patterns; discourage prophecy claims; practical steps appended.","green"),
T("Queue triage workshop for clerks; intake categories standardized; escalation triggers defined.","green"),
T("Forgery detection surge response; verification booth staged; suspect stamps cataloged; seizures logged.","yellow"),
T("High-traffic archive day capacity rules enforced; entry tokens issued; silent zone posted; security liaison set.","yellow"),
T("False omen bulletin cycle after sky-sign rumors; broadcasts timed; hoarding deterrence included.","yellow"),
T("Archive fire incident response: document evacuation; custody chain preserved; public messaging controlled; arrests coordinated.","red"),
T("Mass panic after forged warning decree: authoritative retraction posted; printer network pursued; calm stations opened.","red"),
},
["Ordo Amicitia"] = {
T("Kindness distribution at shrine steps; ledger reconciled; needs list updated for tomorrow.","green"),
T("Family mediation after dispute; amity pledges collected; follow-up visits scheduled.","green"),
T("Volunteer training: calm communication and nonviolent intervention; escalation scripts practiced.","green"),
T("Clinic-queue kindness patrol; conflict prevention and assistance to elderly; complaints logged.","yellow"),
T("Rumor containment after defamatory pamphlets; corrective bulletin cycle launched; source stalls mapped.","yellow"),
T("Audit of relief supplies to prevent theft; custody seals verified; storage rules posted.","yellow"),
T("Witness support for targeted volunteer; safe route arranged; intimidation complaints recorded.","yellow"),
T("Emergency care stations during unrest; neutral aid posture announced; clinic corridor protected.","red"),
T("Crisis grief counseling after fatal incident; aid referrals coordinated; retaliation pledges collected.","red"),
T("High-risk shelter stabilization under red posture; intake controlled; reunification tent activated.","red"),
-- +8 added
T("Elder check-in circuit; medicine delivery; home repair requests logged; follow-up visits scheduled.","green"),
T("School buddy program setup; mentors paired; attendance tracked; guardian contacts logged.","green"),
T("Micro-grant assistance for small vendors; eligibility explained; Temple Bank receipts issued.","green"),
T("Aid line surge plan; entry metering; high-need triage; conflict scripts deployed.","yellow"),
T("Fraudulent charity collectors sweep; donations recovered; offenders documented; public warning posted.","yellow"),
T("Harassment at aid stations response; escorts assigned; offenders barred; reports sealed.","yellow"),
T("Disaster relief activation: shelter staffing; supply staging; daily reporting; rumor suppression enforced.","red"),
T("Mass displacement response: registration booth; family reunification; trauma referrals; records sealed.","red"),
},
["Temple Illuminata"] = {
T("Illumination vigil: lantern discipline and public calm guidance posted; safe routes marked.","green"),
T("Counseling for anxious households; referral paths established; follow-up log kept.","green"),
T("Archive testimonies and signs; witness statements catalogued; custody rules posted.","green"),
T("Night watch of sacred precinct; entry control enforced; conduct rules posted at gates.","yellow"),
T("Omen bulletin: correct false ‘dark star’ claim; discourage panic gatherings; scripts distributed.","yellow"),
T("Audit unsanctioned night gatherings; compliance restrictions issued; repeat venues logged.","yellow"),
T("Post-incident narrative discipline; rumor sources mapped; correction bulletins deployed.","yellow"),
T("Emergency messaging after ‘demonic symbol’ report; scripts distributed; calm stations opened.","red"),
T("Crisis crowd dispersal after night surge; guidance routes opened; reunification tent active.","red"),
T("High-risk escort for sensitive nocturnal rite during unrest; perimeter observers posted; decoy route used.","red"),
-- +8 added
T("Public symposium moderation; debate rules posted; minutes archived; speaker list verified.","green"),
T("Clarity counseling for civic leaders; action plan sealed; follow-up check scheduled.","green"),
T("Apprentice study circle; reading list issued; mentor pairing logged; conduct rules posted.","green"),
T("Heckler disruption response at lecture; removals enforced; incident log filed; next session relocated.","yellow"),
T("Forged ‘Temple order’ correction; official stamps verified; printer path traced; retraction posted.","yellow"),
T("Threat triage against philosophers/teachers; security escort arranged; venue access restricted.","yellow"),
T("Attack on lecture venue response: injuries triaged; perpetrators prosecuted; continuity plan issued; public calm statement posted.","red"),
T("Citywide incitement campaign response: counter-messaging; arrests; curfew guidance; safe corridors protected.","red"),
},
["Temple Aprobelle"] = {
T("Shrine-leader briefing: weekly rumor-control cycle; clarifications posted; complaint intake organized.","green"),
T("Grief-cadre home visits after accident; de-escalation coaching delivered; referrals filed.","green"),
T("School immunization coordination: schedule posted; consent ledger reconciled; follow-up reminders issued.","green"),
T("Clinic-queue stabilization drill: entry metering tested; conflict prevention scripts practiced.","yellow"),
T("Behavior audit of district office after complaint surge; corrective training issued; sign-off obtained.","yellow"),
T("Pattern-analysis intake: schism texts logged; signature correlation packet forwarded; printer stalls mapped.","yellow"),
T("Anomaly-response dispatch after contamination claims: care paired with narrative discipline; panic triggers logged.","yellow"),
T("Silencing-cadre activated after panic flare; controlled messaging + care stations deployed; retaliation pledges collected.","red"),
T("Emergency stabilization in evacuation/fever center: calming rites + rumor suppression; clinic corridor protected.","red"),
T("High-risk psychological stabilization after mass scare: rumor nodes neutralized; supervised dispersal enforced; evidence chain sealed.","red"),
-- +8 added
T("Speaker coaching for ward announcements; panic triggers removed; approved phrasing distributed.","green"),
T("Trust survey intake at market edges; key grievances logged; referrals routed to Harmony Sanctum.","green"),
T("School liaison check: bullying/harassment trend review; intervention plan filed; follow-up scheduled.","green"),
T("Targeted pamphlet purge after printer ID; removal teams assigned; replacement postings issued; suspects logged.","yellow"),
T("Crowd agitation forecast; patrol liaison; calm broadcast cadence set; escalation thresholds defined.","yellow"),
T("Workplace intimidation response; protective escort plan; offender sanctions recommended; compliance checks scheduled.","yellow"),
T("Mass panic containment: calm centers expanded; hourly briefings; agitator roundup coordinated; retaliatory impulses reduced.","red"),
T("Post-attack trauma response: debrief circles; retaliation bans; witness relocation; compliance sweep scheduled.","red"),
},
}
--------------------------------------------------------------------------------
-- 12.1) NOCTIC VAMPIRISM / NOCTIC-RABREV TASK INSERTS
--
-- These tasks add Noctic Vampirism and raw Noctic-Rabrev events to the cults
-- most likely to intervene: dream, health, purification, wetland, funerary,
-- hidden-knowledge, legal, charity, and counter-fraud orders.
--------------------------------------------------------------------------------
local function addCultTasks(cultName, list)
tasksByCult[cultName] = tasksByCult[cultName] or {}
for _, t in ipairs(list) do
table.insert(tasksByCult[cultName], t)
end
end
addCultTasks("Reverie Nebulous", {
T("[[Noctic Vampirism]] dream-entanglement intake: affected households given sleep-boundary cards; severe nightmare cases referred to [[Temple_Aprobelle|Temple Aprobelle]].","green"),
T("Noctic dream-circle for registered patients; blood-hunger language redirected into supervised journaling; family witnesses briefed.","green"),
T("Nightmare cluster triage after suspected raw [[Noctic-Rabrev]] smoke exposure; trigger map compiled; clinic referrals issued.","yellow"),
T("Shared-vision complaint linked to Noctic leaf use; dream narratives separated from rumor claims; speaker scripts distributed.","yellow"),
T("Noctic trance-gathering compliance review; unsanctioned sleep rites suspended; repeat venues logged for Temple Bank follow-up.","yellow"),
T("Crisis rest-ward activation after mass Noctic panic dream; supervised sleep zones opened; clinic corridor protected.","red"),
T("Violent blood-hunger dream episode contained; families separated safely; witness shielding and trauma referrals arranged.","red"),
T("High-risk dream-breach report involving suspected nocticine-Δ exposure; Aprobelle liaisons activated; public narrative held neutral.","red"),
})
addCultTasks("Sanctum Vitalis", {
T("[[Noctic Vampirism]] clinic screen: photophobia, anemia, fever, lesions, and blood-craving symptoms logged; dietary guidance issued.","green"),
T("Registered Noctic patient follow-up: milk, iron food, and blood-substitute stabilization plan checked; family care note filed.","green"),
T("Raw [[Noctic-Rabrev]] exposure triage at wetland clinic; symptomatic patients separated from ordinary fever queue.","yellow"),
T("Congenital Noctic case review; guardian consent ledger reconciled; lifelong monitoring schedule updated.","yellow"),
T("Market illness cluster assessed for raw-leaf adulteration; sampling escorted; vendor recall notice prepared.","yellow"),
T("Emergency hemorrhagic decline response in serious Noctic case; triage expansion, family shielding, and mortality ledger precautions enacted.","red"),
T("Necrotic lesion cluster after unauthorized induction rite; quarantine boundary marked; cult-health evaluation ordered.","red"),
T("High-risk fever center support after Noctic outbreak rumor; panic narratives suppressed while medical intake remains open.","red"),
})
addCultTasks("Temple Alabaster", {
T("Refined nocticine tincture custody check; dosage ledger reconciled; [[Temple Bank of the Reformed Stripping Path|Temple Bank]] seal verified.","green"),
T("Post-Eidolan recovery clinic review using licensed [[Noctic-Rabrev]] concentrate; patient response notes sealed under medical custody.","green"),
T("Purity inspection of Noctic balms and tinctures; raw-leaf contamination warnings posted; suspect jars removed.","yellow"),
T("Therapeutic nocticine distribution audit; storage seals verified; unauthorized substitutions routed to Golden Shadows.","yellow"),
T("Contaminated Noctic medicine recall; affected households contacted; clinic-stabilization instructions posted.","yellow"),
T("Emergency support for serious Noctic Vampirism collapse; calm ward opened; hemorrhage supplies and spiritual grounding teams staged.","red"),
T("Crisis purification rite after counterfeit nocticine batch harms pilgrims; evidence preserved; public reassurance issued.","red"),
T("High-risk medical custody transfer for unstable Noctic patient; decoy route used; receiving clinic countersigns sealed packet.","red"),
})
addCultTasks("Mystery of the Verdant Embrace", {
T("[[Noctic-Rabrev]] field-blessing with safety lecture; flower and raw-leaf handling rules reissued to cultivators.","green"),
T("Blood Orchard footpath inspection; leaf-storage huts checked; runoff risks and wetland access hazards logged.","green"),
T("Noctic crop custody audit after missing leaf bundles; field stewards interviewed; harvest tally reconciled.","yellow"),
T("Wetland illness cluster mapped near Noctic fields; raw-leaf exposure questions added to intake; clinic referrals issued.","yellow"),
T("Flood-damaged Noctic storage shed inspected; wet leaves quarantined; safe flower product separated from restricted material.","yellow"),
T("Emergency Blood Orchard closure after unauthorized raw-leaf distribution; field gates sealed; cultivator list turned over to Temple Bank.","red"),
T("Poisoning event tied to unprocessed Noctic leaves; antidote routing, source trace, and water access controls initiated.","red"),
T("High-risk wetland outbreak response: care kits staged, Noctic custody chain rebuilt, misinformation suppressed in shrine districts.","red"),
})
addCultTasks("Mystery of the Stygian Veil", {
T("Funerary guidance for household of controlled Noctic patient; blood-offering limits explained; mourning schedule recorded.","green"),
T("Noctic transformation counseling at cemetery gate; symbolic death language separated from unsafe raw-leaf use.","green"),
T("Terminal Noctic vigil supervision; family witnesses logged; raw-leaf access barred during mourning rites.","yellow"),
T("Blood-rite compliance check after ancestral-contact claims; permitted substitutes verified; coercion complaints recorded.","yellow"),
T("Rumor suppression after ‘Noctic dead returning’ claim; evidence bulletin posted; graveyard panic triggers tracked.","yellow"),
T("Crisis response after terminal Noctic transformation death; grief triage and crowd discipline established before nightfall.","red"),
T("Emergency calm stations at tomb precinct after blood-hunger panic; supervised dispersal and clinic access protected.","red"),
T("High-risk memorial for Noctic fatalities; retaliation prevention enforced; family shielding and witness support arranged.","red"),
})
addCultTasks("Order of the Umbral Oracle", {
T("Noctic night-watch at shuttered shrine; sleepwalk reports logged; entry rules posted for anxious households.","green"),
T("Covered-mirror guidance for suspected Noctic cases; families advised on safe vigils, naming restraint, and witness logs.","green"),
T("Skødan-style mirror scare assessed after Noctic fever report; false-revelation claims documented without public amplification.","yellow"),
T("Starless vigil compliance review; raw [[Noctic-Rabrev]] use questioned; repeat venues logged for Temple Bank follow-up.","yellow"),
T("Dream-mapping dispute involving Noctic patient; testimony sealed; dangerous prophecy language corrected.","yellow"),
T("Crisis nocturnal dispersal after mirror-haunting rumor; safe corridors opened; panic narratives suppressed.","red"),
T("Serious Noctic fever episode during covered-mirror rite; patient removed safely; household witnesses debriefed.","red"),
T("High-risk hidden-name panic in [[List of cities in Bassaridia Vaeringheim#Skøda|Skøda]]; Aprobelle and Sanctum Vitalis liaisons activated under seal.","red"),
})
addCultTasks("Conclave Illuminara", {
T("Tarsican phase briefing for Noctic stewards; raw-leaf rites rescheduled away from high-anxiety night windows.","green"),
T("Lunar calendar note issued for registered Noctic patients; dream disturbances and light-sensitivity triggers explained.","green"),
T("Nocticine-Δ rumor correction after celestial alignment claims; hoarding and unsafe induction warnings posted.","yellow"),
T("Moon-token and Noctic permit verification at night vigil; counterfeit ritual permissions seized.","yellow"),
T("Controlled interpretation session after Noctic dream surge; public cycle-framing delivered to reduce panic.","yellow"),
T("Emergency anti-panic bulletin after eclipse-linked Noctic outbreak claim; calm stations opened; clinic routes protected.","red"),
T("High-risk nocturnal rite escort for registered Noctic patients; perimeter observers posted; false prophecy claims suppressed.","red"),
T("Crisis interpretive response in Skødan mirror-vigil district; Tarsican language used to separate revelation from deception.","red"),
})
addCultTasks("Guild of Golden Shadows", {
T("Quiet audit of [[Noctic-Rabrev]] market stalls; forged raw-leaf permissions logged; suspect brokers mapped.","green"),
T("Noctic Fleet manifest cross-check; flower extract, tincture, and raw-leaf crates reconciled against Temple Bank seals.","green"),
T("Undercover purchase of counterfeit Noctic induction permit; printer path traced; custody memo prepared.","yellow"),
T("Raw-leaf diversion inquiry after Blood Orchard discrepancy; brokers interviewed; sealed attribution packet filed.","yellow"),
T("Counterfeit nocticine tincture sweep; suspect vials seized; affected vendors routed to Temple Alabaster inspection.","yellow"),
T("Night extraction of threatened Noctic witness; safe route screened; temporary housing arranged under seal.","red"),
T("Major Noctic smuggling ring disruption: ledger seizure, broker detention support, and public market guidance coordinated.","red"),
T("High-risk port-gate fraud surge involving raw Noctic leaf crates; inspection teams reinforced; evidence chain preserved.","red"),
})
addCultTasks("Temple Aprobelle", {
T("[[Noctic Vampirism]] rumor-control briefing: discourage vigilante panic; explain registration, clinic access, and family support routes.","green"),
T("Household anxiety visits after Noctic diagnosis; de-escalation coaching delivered; ostracism risks logged.","green"),
T("Pattern-analysis intake for Noctic panic stories; dream claims, blood-hunger rumors, and raw-leaf reports separated.","yellow"),
T("Noctic outbreak messaging cycle: official symptoms listed; false contagion claims corrected; clinic queues stabilized.","yellow"),
T("Behavior audit after harassment of registered Noctic patients; corrective training issued; offenders warned.","yellow"),
T("Silencing-cadre activated after Noctic panic flare; controlled messaging and care stations deployed before night crowds form.","red"),
T("Emergency psychological stabilization after serious Noctic blood-hunger incident; retaliation bans and witness relocation arranged.","red"),
T("High-risk mass scare after alleged Noctic attack; rumor nodes neutralized; supervised dispersal enforced; evidence chain sealed.","red"),
})
addCultTasks("Harmony Sanctum", {
T("Family mediation after Noctic diagnosis; care duties, privacy expectations, and visitor rules written into household concord.","green"),
T("Neighborhood explanation circle for registered Noctic household; stigma reduced; clinic and Temple Bank contacts posted.","green"),
T("Market-lane calming after Noctic rumor; queue discipline restored; harassment complaints logged.","yellow"),
T("Inter-family dispute over raw-leaf inheritance; compromise storage terms drafted; Temple Bank audit requested.","yellow"),
T("Noctic clinic crowd-lane plan reviewed; patient privacy route and public waiting area separated.","yellow"),
T("Emergency anti-reprisal pact after blood-hunger scare; escort corridors established; no-retaliation oaths collected.","red"),
T("Mass grievance intake after Noctic outbreak panic; testimony queue metered; witness shielding arranged; outcomes logged.","red"),
T("Crisis reconciliation hearing after violence against Noctic patient; restitution ledger initiated; family protection arranged.","red"),
})
addCultTasks("Azure Sentinel Sect", {
T("Canal escort for licensed Noctic medical supplies; handoff signatures collected; chain-of-custody verified.","green"),
T("Waterfront Noctic storage inspection after rain; flood exposure risks logged; damaged crates elevated.","green"),
T("Storm-drain and wet-market sweep near Noctic vendors; runoff contamination warnings posted.","yellow"),
T("Ferry-ramp crowd control during Noctic clinic transfer; privacy corridor and rescue lane maintained.","yellow"),
T("Suspicious skiff check involving raw Noctic leaf bundles; manifests reviewed; contraband seized under seal.","yellow"),
T("Emergency flood response at Noctic-Rabrev storehouse; restricted leaf material secured before dispersal through canals.","red"),
T("Port lockdown support after Noctic smuggling violence; civilian zones cleared; clinic route protected.","red"),
T("Mass casualty waterfront incident involving contaminated Noctic crates; triage staging and perimeter discipline enforced.","red"),
})
addCultTasks("Rex Catonis", {
T("Noctic registration clinic: voluntary induction rules explained; lawful forms checked; appeal route posted.","green"),
T("Public notice board update on raw [[Noctic-Rabrev]] restrictions; false decrees removed; clerk sign-off obtained.","green"),
T("Hearing docket triage for unauthorized Noctic induction complaint; mediation attempted where lawful; evidence preserved.","yellow"),
T("Raw-leaf permit compliance sweep; expired permissions suspended; corrective notices posted to shrine gates.","yellow"),
T("Coerced induction allegation reviewed; victim protection options posted; suspect descriptions routed to Ironclad.","yellow"),
T("Mass legal response after Noctic outbreak: unlawful vendors processed, custody ledgers seized, and public guidance issued.","red"),
T("Coordinated violence response after Noctic panic; detention processing, court schedule triage, and after-action report filed.","red"),
T("Emergency decree against unsafe raw-leaf distribution read publicly; clinic corridors protected during enforcement.","red"),
})
addCultTasks("Sanctum Delphica", {
T("Noctic case archive update: diagnosis records, raw-leaf permissions, and family witness notes catalogued.","green"),
T("Scribe training on Noctic custody language; distinguish raw leaf, flower extract, tincture, and nocticine concentrate.","green"),
T("Doctrinal quarantine assistance after Noctic rumor; entry controls and approved signage posted.","yellow"),
T("Forgery detection sweep for Noctic induction papers; suspect stamps catalogued; printer path traced.","yellow"),
T("Witness packet assembled for raw-leaf desecration or coercive induction case; evidence collated and sealed.","yellow"),
T("Emergency archive lockdown after Noctic registry breach; relocation plan activated; public narrative kept neutral.","red"),
T("Mass panic after forged Noctic warning decree; authoritative retraction posted; printer network pursued; calm stations opened.","red"),
T("High-risk transfer of Noctic medical ledgers; route denial markers deployed; decoy timing staged.","red"),
})
addCultTasks("Ordo Amicitia", {
T("Milk and iron-food distribution for Noctic households; needs list updated; stigma-free delivery route assigned.","green"),
T("Family support visits for congenital Noctic cases; caregiver relief scheduled; privacy rules posted.","green"),
T("Aid line surge plan after Noctic diagnosis cluster; high-need triage and conflict scripts deployed.","yellow"),
T("Fraudulent charity collectors using Noctic outbreak claims exposed; donations recovered; public warning posted.","yellow"),
T("Shelter intake support for families displaced by Noctic panic; family reunification and trauma referrals recorded.","yellow"),
T("Disaster relief activation after Noctic outbreak in wetland district; supply staging and rumor suppression enforced.","red"),
T("Mass displacement response after raw-leaf scare; registration booth opened; care packages and witness shielding arranged.","red"),
T("High-risk relief escort for registered Noctic patients leaving hostile neighborhood; safe housing arranged under seal.","red"),
})
local function shouldAZSGoCovert(nextInt, sev, threat)
-- Only under red posture and with a real threat record present
if sev ~= "red" or not threat then return false end
local u = urgencyScore(threat.urg)
if u < 3 then return false end -- HIGH/CRITICAL only
-- CRITICAL always qualifies; otherwise look for strong violence/sabotage signals
local ev = string.lower(tostring(threat.event or ""))
local signals =
ev:match("sabot") or ev:match("arson") or ev:match("bomb") or ev:match("terror") or
ev:match("assassin") or ev:match("hit") or ev:match("murder") or
ev:match("insurgent") or ev:match("pirate") or ev:match("rogue")
local qualifies = (u >= 4) or signals
if not qualifies then return false end
-- “Sometimes”: deterministic chance, higher for CRITICAL
local chance = (u >= 4) and 25 or 15 -- percent
return (nextInt() % 100) < chance
end
--------------------------------------------------------------------------------
-- 13) CITY PROFILES (bias order selection)
--------------------------------------------------------------------------------
local cityProfiles = {
["Vaeringheim"] = {weights={AUM=3,HSA=2,AZS=2,GGS=2,SVI=2,SDE=2,TAP=3}},
["Somniumpolis"] = {weights={RNB=3,SVI=2,GGS=2,TAP=3,AZS=1}},
["Luminaria"] = {weights={AUM=3,CON=2,HSA=2,TAP=2,SVI=1}},
["Lunalis Sancta"] = {weights={SDE=3,CON=2,TAP=2,HSA=1}},
["Delphica"] = {weights={SDE=3,TAP=2,CON=1,SVI=1}},
["Thermosalem"] = {weights={SVI=2,TAP=2,AZS=2,HSA=1}},
["Pyralis"] = {weights={IGA=2,SVI=2,TAP=2,AZS=2,AUM=1}},
["Keybir-Aviv"] = {weights={AZS=3,TAP=2,GGS=2,HSA=1}},
["Diamandis"] = {weights={TAP=2,SVI=2,AUM=1,RNB=1,MIR=1}},
-- Haifan Bassaridia / Southern Strait profiles
-- These make Southern Strait-relevant cults more likely when the city row
-- is one of the Haifan Bassaridian ports or inland-linked market cities.
["Keybir-Aviv"] = {weights={AZS=4,GGS=3,TAP=3,HSA=2,SDE=2,SVI=1}},
["Tel-Amin"] = {weights={GGS=4,AZS=3,VDE=3,HSA=2,TAP=2,SVI=1}},
["Diamandis"] = {weights={TAP=3,SVI=3,AUM=2,RNB=2,MIR=2,STY=1}},
["Jogi"] = {weights={GGS=4,TAP=3,AZS=3,MIR=2,SVI=2,SDE=1}},
["Lewisburg"] = {weights={HSA=3,GGS=3,TAP=2,SDE=2,AZS=2,ACC=1}},
["Thermosalem"] = {weights={SVI=4,TAP=3,AZS=2,HSA=2,ALB=2,CAT=1}},
["Akróstadium"] = {weights={HSA=3,TAP=3,MIR=3,GGS=2,AZS=1,CICL=1}},
["Sufriya"] = {weights={VDE=3,AZS=3,SVI=2,TAP=2,GGS=2,ALB=1}},
["Lykopolis"] = {weights={SDE=3,TAP=3,GGS=2,HSA=2,AZS=2,CAT=1}},
["Tonar"] = {weights={SDE=2,SVI=2,VDE=2,TAP=1,CICL=1}},
["Bashkim"] = {weights={VDE=3,SVI=2,TAP=1}},
["Ourid"] = {weights={VDE=3,SVI=2,TAP=1}},
["Ardclach"] = {weights={AZS=2,SVI=2,TAP=1,CON=1}},
-- Alperkin necromancy / ancestor-rite hotspots
-- These weights do not make ordinary ancestor veneration illicit.
-- They only make the relevant investigative, funerary, archival,
-- psychological-stability, purification, and health orders more likely
-- in cities where forbidden necromancy or corrupted ancestor rites may appear.
["The Alpazkigz"] = {weights={STY=5, UMB=5, SDE=3, TAP=3, SVI=2, ALB=2, VDE=2, RNB=1}},
["Odiferia"] = {weights={UMB=6, STY=5, SDE=4, TAP=4, SVI=3, ALB=3, VDE=3, GGS=2}},
["Erythros"] = {weights={STY=5, UMB=4, CON=3, SDE=3, TAP=3, RNB=2}},
["Nexa"] = {weights={SDE=6, UMB=4, STY=3, TAP=3, ILL=2, EOF=2}},
["Pyralis"] = {weights={STY=5, UMB=4, SDE=3, IGA=3, TAP=3, SVI=2, ALB=2}},
["Acheron"] = {weights={STY=6, UMB=5, SDE=3, TAP=3, SVI=3, ALB=2, VDE=2}},
["Aureum"] = {weights={GGS=4, SDE=4, UMB=4, STY=3, TAP=3, ALB=3, SVI=2, ILL=2}},
["Somniumpolis"] = {weights={RNB=5, UMB=4, STY=4, TAP=4, SDE=3, SVI=2, ALB=2}},
}
local function getProfile(cityName)
return cityProfiles[cityName] or {
weights={AUM=1,HSA=1,IGA=1,CHS=1,GGS=1,AZS=1,RNB=1,EOF=1,UMB=1,VDE=1,CON=1,SVI=1,ALB=1,CICL=1,ACC=1,STY=1,SYL=1,MIR=1,CAT=1,SDE=1,AMI=1,ILL=1,TAP=1}
}
end
--------------------------------------------------------------------------------
-- 14) ORDERS (missionary-capable cult list)
-- NOTE: These names must match GeneralPortOperations ritualCults list + Temple Aprobelle.
--------------------------------------------------------------------------------
local orders = {
{key="AUM", name="Order of Aurora Mystica"},
{key="HSA", name="Harmony Sanctum"},
{key="IGA", name="Ignis Aeternum"},
{key="CHS", name="Celestial Harmony Sect"},
{key="GGS", name="Guild of Golden Shadows"},
{key="AZS", name="Azure Sentinel Sect"},
{key="RNB", name="Reverie Nebulous"},
{key="EOF", name="Eon Fellowship"},
{key="UMB", name="Order of the Umbral Oracle"},
{key="VDE", name="Mystery of the Verdant Embrace"},
{key="CON", name="Conclave Illuminara"},
{key="SVI", name="Sanctum Vitalis"},
{key="ALB", name="Temple Alabaster"},
{key="CICL", name="Court of the Ironclad"},
{key="ACC", name="Accord Concordia"},
{key="STY", name="Mystery of the Stygian Veil"},
{key="SYL", name="Sylvan Fellowship"},
{key="MIR", name="Mystery of Red Mirth"},
{key="CAT", name="Rex Catonis"},
{key="SDE", name="Sanctum Delphica"},
{key="AMI", name="Ordo Amicitia"},
{key="ILL", name="Temple Illuminata"},
{key="TAP", name="Temple Aprobelle"},
}
local orderByName = {}
for _, o in ipairs(orders) do orderByName[o.name] = o end
--------------------------------------------------------------------------------
-- 15) SEVERITY + ORDER/TASK CHOICE
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- 14.1) NOCTIC CITY / CULT WEIGHTING
--
-- Noctic Vampirism is more likely to appear where Noctic-Rabrev, Alperkin
-- inheritance, dream rites, raw-leaf custody, or Skødan mirror-vigil customs
-- are already locally important.
--------------------------------------------------------------------------------
local nocticCityRisk = {
["Somniumpolis"] = 8,
["Odiferia"] = 8,
["Erythros"] = 7,
["Acheron"] = 6,
["Norsolyra"] = 6,
["Skøda"] = 6,
["The Alpazkigz"] = 6,
["Nexa"] = 4,
["Pyralis"] = 4,
["Vaeringheim"] = 3,
["Jogi"] = 3,
["Aurelia"] = 3,
}
local nocticOrderBoost = {
RNB = 4, -- Reverie Nebulous: dream entanglement, nightmares, trance
SVI = 4, -- Sanctum Vitalis: clinic response, anemia, lesions, fever
ALB = 3, -- Temple Alabaster: nocticine medicine and purity controls
VDE = 4, -- Verdant Embrace: Noctic-Rabrev fields, wetlands, plant custody
STY = 3, -- Stygian Veil: funerary rites, terminal transformation
UMB = 3, -- Umbral Oracle: night rites, shadow, hidden revelation
CON = 3, -- Conclave Illuminara: Tarsica, lunar timing, nocticines
GGS = 3, -- Golden Shadows: smuggling, forged permits, counter-fraud
TAP = 4, -- Temple Aprobelle: panic control, trauma, rumor suppression
HSA = 2, -- Harmony Sanctum: family/community conflict stabilization
SDE = 2, -- Sanctum Delphica: registries, documents, doctrine quarantine
AMI = 2, -- Ordo Amicitia: relief for afflicted households
AZS = 1, -- Azure Sentinel: canal/port custody and wetland movement
CAT = 1, -- Rex Catonis: legal registration and enforcement
}
local nocticCitySpecificBoost = {
["Somniumpolis"] = {RNB=5, TAP=4, SVI=4, ALB=3, VDE=3},
["Odiferia"] = {VDE=6, SVI=4, TAP=4, ALB=3, GGS=3},
["Erythros"] = {CON=4, RNB=4, STY=3, TAP=3, SDE=2},
["Acheron"] = {STY=6, SVI=3, ALB=3, UMB=3, TAP=3},
["Norsolyra"] = {VDE=5, RNB=4, SVI=3, SYL=2, TAP=3},
["Skøda"] = {UMB=6, CON=4, STY=4, SDE=3, TAP=3, GGS=2},
["The Alpazkigz"] = {VDE=4, STY=4, RNB=4, TAP=3, SVI=3},
["Jogi"] = {GGS=3, MIR=2, TAP=3, SVI=2},
["Aurelia"] = {CON=3, SDE=2, TAP=3, RNB=2},
["Vaeringheim"] = {GGS=3, TAP=3, SVI=2, ALB=2, CAT=2},
}
local function addNocticBoostsToWeights(weighted, seen, cityName)
local risk = nocticCityRisk[cityName] or 0
if risk <= 0 then return end
local citySpecific = nocticCitySpecificBoost[cityName] or {}
local riskBump = math.floor(risk / 2)
for _, it in ipairs(weighted) do
local base = nocticOrderBoost[it.key] or 0
local localBoost = citySpecific[it.key] or 0
if base > 0 or localBoost > 0 then
it.w = it.w + base + localBoost + riskBump
end
end
for key, base in pairs(nocticOrderBoost) do
if not seen[key] then
local localBoost = citySpecific[key] or 0
if base > 0 or localBoost > 0 then
weighted[#weighted+1] = {key=key, w=base + localBoost + riskBump}
seen[key] = true
end
end
end
end
--------------------------------------------------------------------------------
-- 14.2) ALPERKIN NECROMANCY / ANCESTOR-RITE DISTURBANCE WEIGHTING
--
-- This system distinguishes sanctioned Alperkin ancestor rites from forbidden
-- necromancy. It increases the odds that the correct cults are selected in
-- known hotspots, but the actual necromancy task remains rare and is handled
-- by maybeAlperkinNecromancyTask().
--------------------------------------------------------------------------------
local necromancyCityRisk = {
["The Alpazkigz"] = 10, -- Amanitian Lowlands / Gloom Forest ancestor rites
["Odiferia"] = 10, -- Southern Wetlands: forbidden swamp necromancy
["Pyralis"] = 8, -- Volcanic Tooth Caverns / preserved ancestors
["Erythros"] = 8, -- tomb spirits and ancestor voices
["Acheron"] = 8, -- revenants, roadside apparitions, canyon tombs
["Nexa"] = 7, -- forbidden scholarly necromancy
["Aureum"] = 7, -- elite corpse-preservation experiments
["Somniumpolis"] = 7, -- dream-necromancy and dead-speaking dreams
}
local necromancyOrderBoost = {
UMB = 6, -- Order of the Umbral Oracle: occult investigation / spirit-binding
STY = 6, -- Mystery of the Stygian Veil: funerary custody / tombs / the dead
SDE = 4, -- Sanctum Delphica: forbidden rites, manuscripts, quarantine
TAP = 4, -- Temple Aprobelle: panic, hysteria, public order
SVI = 2, -- Sanctum Vitalis: corpse handling / disease concerns
ALB = 2, -- Temple Alabaster: purification / contamination concerns
RNB = 2, -- Reverie Nebulous: dream-necromancy
VDE = 1, -- Verdant Embrace: wetlands, Gloom Forest, grave-grove settings
GGS = 1, -- Golden Shadows: elite funding, trafficking, covert evidence
CON = 1, -- Conclave Illuminara: nocturnal interpretation / lunar timing
}
local necromancyCitySpecificBoost = {
["The Alpazkigz"] = {STY=5, UMB=5, SDE=3, TAP=3, VDE=2},
["Odiferia"] = {UMB=6, STY=5, SDE=4, TAP=4, SVI=3, ALB=3, VDE=3},
["Pyralis"] = {STY=5, UMB=4, SDE=3, IGA=2, ALB=2},
["Erythros"] = {STY=5, UMB=4, CON=3, SDE=3, TAP=3},
["Acheron"] = {STY=6, UMB=5, SVI=3, ALB=2, VDE=2},
["Nexa"] = {SDE=6, UMB=4, STY=3, ILL=2, EOF=2},
["Aureum"] = {GGS=4, SDE=4, UMB=4, ALB=3, SVI=2},
["Somniumpolis"] = {RNB=5, UMB=4, STY=4, TAP=4, SDE=3},
}
local function addNecromancyBoostsToWeights(weighted, seen, cityName)
local risk = necromancyCityRisk[cityName] or 0
if risk <= 0 then return end
local citySpecific = necromancyCitySpecificBoost[cityName] or {}
local riskBump = math.floor(risk / 2)
for _, it in ipairs(weighted) do
local base = necromancyOrderBoost[it.key] or 0
local localBoost = citySpecific[it.key] or 0
if base > 0 or localBoost > 0 then
it.w = it.w + base + localBoost + riskBump
end
end
for key, base in pairs(necromancyOrderBoost) do
if not seen[key] then
local localBoost = citySpecific[key] or 0
if base > 0 or localBoost > 0 then
weighted[#weighted+1] = {key=key, w=base + localBoost + riskBump}
seen[key] = true
end
end
end
end
local function inferSeverity(cityTier, taskSev, threatUrg)
local sev = tostring(taskSev or "green")
if cityTier == "major" and sev == "green" then sev = "yellow" end
if urgencyScore(threatUrg) >= 3 then
sev = "red"
elseif urgencyScore(threatUrg) == 2 and sev == "green" then
sev = "yellow"
end
return sev
end
local function pickOrder(nextInt, cityName, threat)
local profile = getProfile(cityName)
local weighted = {}
local seen = {}
for key, w in pairs(profile.weights or {}) do
weighted[#weighted+1] = {key=key, w=tonumber(w) or 0}
seen[key] = true
end
-- Noctic city bump: prefer the cults most likely to handle raw Noctic-Rabrev,
-- Noctic Vampirism, dream disturbance, health evaluation, registration,
-- smuggling, and family/community stabilization.
addNocticBoostsToWeights(weighted, seen, cityName)
-- Alperkin necromancy bump: prefer the cults most likely to distinguish
-- sanctioned ancestor rites from forbidden spirit-binding, corpse animation,
-- grave desecration, illicit resurrection, and prohibited funerary texts.
addNecromancyBoostsToWeights(weighted, seen, cityName)
-- Threat bump: prefer Aprobelle + safety/health + shadows.
if threat and urgencyScore(threat.urg) >= 2 then
for _, it in ipairs(weighted) do
if it.key == "TAP" or it.key == "SVI" or it.key == "AZS" or it.key == "GGS" or it.key == "SDE" then
it.w = it.w + 2
end
end
end
local pickedKey = pickWeighted(nextInt, weighted).key
for _, o in ipairs(orders) do
if o.key == pickedKey then return o end
end
return orders[1]
end
local function pickTask(nextInt, cultName, sev, usedIdx)
local pool = tasksByCult[cultName] or tasksByCult["Harmony Sanctum"] or {}
local weighted = {}
for idx, t in ipairs(pool) do
if not (usedIdx and usedIdx[idx]) then
local w = 1
if t.sev == sev then w = 4
elseif sev == "red" and t.sev == "yellow" then w = 2
elseif sev == "yellow" and t.sev == "green" then w = 2 end
table.insert(weighted, {t=t, w=w, idx=idx})
end
end
if #weighted == 0 then
for idx, t in ipairs(pool) do
local w = 1
if t.sev == sev then w = 4
elseif sev == "red" and t.sev == "yellow" then w = 2
elseif sev == "yellow" and t.sev == "green" then w = 2 end
table.insert(weighted, {t=t, w=w, idx=idx})
end
end
if #weighted == 0 then
return {text="Routine rites and civic support (no task pool configured).", sev="green"}, nil
end
local picked = pickWeighted(nextInt, weighted)
return picked.t, picked.idx
end
--------------------------------------------------------------------------------
-- 15.0) RARE ALPERKIN NECROMANCY / FORBIDDEN ANCESTOR-RITE OPERATIONS
--
-- These are rare city-specific overrides. They should not replace ordinary
-- Alperkin ancestor traditions. The text deliberately distinguishes sanctioned
-- ancestor rites from forbidden necromancy involving spirit-binding, corpse
-- animation, grave desecration, illicit resurrection, or prohibited texts.
--------------------------------------------------------------------------------
local necromancyResponderKeys = {
UMB = true,
STY = true,
SDE = true,
TAP = true,
SVI = true,
ALB = true,
RNB = true,
GGS = true,
}
local necromancyResponderLabel = {
UMB = "Umbral Oracle occult-investigation mission",
STY = "Stygian Veil funerary-custody mission",
SDE = "Delphica restricted-rite review",
TAP = "Aprobelle panic-control intervention",
SVI = "Vitalis corpse-handling and disease-screening support",
ALB = "Alabaster purification and contamination-control support",
RNB = "Reverie Nebulous dream-necromancy assessment",
GGS = "Golden Shadows covert evidence inquiry",
}
local necromancyEventsByCity = {
["The Alpazkigz"] = {
T("ancestor-rite auditors are dispatched after elders report that preserved ancestral remains have been addressed through coercive rather than sanctioned rites; grave stewards separate accepted ancestor veneration from suspected spirit-binding.", "yellow"),
T("Gloom Forest burial paths are placed under watch after witnesses hear ancestral voices repeating commands from sealed family tombs; custody seals are checked and unauthorized night offerings are removed.", "yellow"),
T("a red-posture investigation begins after a prohibited rite allegedly binds ancestral spirits to exposed remains near a sacred burial grove; movement is restricted while funerary stewards and occult investigators secure the site.", "red"),
},
["Odiferia"] = {
T("wetland grave markers are found overturned beside binding cords, bone charms, and forbidden swamp-script; missionaries seal the path, protect witnesses, and separate local dark Alp customs from illicit necromancy.", "yellow"),
T("isolated swamp shrines are inspected after reports of rogue necromancers attempting to bargain with preserved dead; unauthorized offerings are seized and spirit-binding claims are recorded under seal.", "yellow"),
T("a red-level swamp cult response is activated after grave desecration, corpse movement, and forced ancestral invocations are reported in the southern wetlands; quarantine ropes are posted and night travel is suspended.", "red"),
},
["Pyralis"] = {
T("Volcanic Tooth Cavern stewards request assistance after preserved ancestral remains are found repositioned beside ash-script invocations; relic custody is rebuilt and unauthorized tomb access is suspended.", "yellow"),
T("mummified elite remains in a heated cavern tomb show signs of ritual tampering; missionaries verify which ancestor rites were sanctioned and which inscriptions match forbidden resurrection formulae.", "yellow"),
T("a red-posture tomb lockdown is ordered after a volcanic ancestor cult attempts to animate preserved remains using ember-offerings and prohibited names; cavern entrances are sealed pending doctrinal review.", "red"),
},
["Erythros"] = {
T("tomb-spirit reports intensify after families hear ancestral voices from sealed crypts; Stygian custody stewards verify offerings while Umbral witnesses test for coercive spirit-binding.", "yellow"),
T("ancestor-song vigils are paused after a chant leader repeats names not recorded in the family death ledgers; missionaries distinguish accepted memorial practice from necromantic invocation.", "yellow"),
T("a red-level response begins after tomb voices identify the living by hidden names and demand blood-offerings; the precinct is cleared, witnesses are shielded, and ritual transcripts are quarantined.", "red"),
},
["Nexa"] = {
T("a private study circle is investigated after students copy funerary diagrams from restricted manuscripts; pages are seized and participants are redirected into sanctioned theological review.", "yellow"),
T("forbidden scholarly necromancy is suspected after marginal notes describe corpse animation as a calendrical experiment; Delphica scribes quarantine the archive shelf and record all readers.", "yellow"),
T("a red-posture archive lockdown follows evidence of an attempted resurrection rite using prohibited commentaries; manuscripts are relocated under escort and public explanation is kept deliberately narrow.", "red"),
},
["Acheron"] = {
T("roadside apparitions near canyon tombs prompt funerary patrols; missionaries interview travelers, inspect grave ledges, and remove coercive offerings left for wandering dead.", "yellow"),
T("revenant claims spread after miners report figures walking from abandoned cliff burrows; Stygian stewards compare burial records while Umbral observers check for binding marks.", "yellow"),
T("a red-level canyon response is triggered after remains are found arranged beside petrified roots and spoken-name tablets; the road is closed and corpse custody is transferred under seal.", "red"),
},
["Aureum"] = {
T("elite-funded corpse-preservation experiments are reviewed after private patrons request unlawful ancestral consultation; Golden Shadows auditors trace payments while Delphica scribes seize ritual notes.", "yellow"),
T("a hidden preservation chamber beneath a wealthy household is inspected for illicit resurrection instruments; medical, purification, and funerary witnesses are assigned before remains are moved.", "yellow"),
T("a red-posture intervention begins after evidence suggests a patron funded spirit-binding to keep a dead adviser communicative; ledgers are seized and the household is placed under ritual quarantine.", "red"),
},
["Somniumpolis"] = {
T("dream-necromancy reports are triaged after mourners claim the dead are speaking through shared dreams; Reverie guides record narratives while Umbral witnesses screen for coercive invocations.", "yellow"),
T("sleep-circle permits are reviewed after a grieving household attempts to summon an ancestor through dream-binding; counselors separate mourning visions from forbidden manipulation of the dead.", "yellow"),
T("a red-level dream-necromancy response is activated after multiple sleepers repeat the same dead voice demanding physical remains; the household is stabilized and ritual notes are sealed.", "red"),
},
}
local necromancyJointCrisis = {
T("Exceptional Alperkin necromancy crisis: the Order of the Umbral Oracle, Mystery of the Stygian Veil, and Sanctum Delphica respond jointly after evidence emerges that a prohibited rite has successfully bound ancestral spirits to physical remains; ritual sites are quarantined, movement is restricted, corpse custody is rebuilt, and all manuscripts are placed under doctrinal seal.", "red"),
T("Exceptional cross-cult necromancy investigation: Umbral Oracle investigators, Stygian Veil funerary stewards, and Delphica archivists assume joint control after corpse animation, grave desecration, and forbidden resurrection formulae are linked to the same Alperkin ritual network; public statements stress that sanctioned ancestor rites remain lawful.", "red"),
}
local function maybeAlperkinNecromancyTask(nextInt, cityName, order, desiredSev)
local risk = necromancyCityRisk[cityName] or 0
if risk <= 0 then return nil end
if not order or not necromancyResponderKeys[order.key] then return nil end
-- Extremely rare joint crisis. Only the three principal responder cults
-- should carry this as the visible row order.
if order.key == "UMB" or order.key == "STY" or order.key == "SDE" then
local crisisChance = 2
if risk >= 10 then
crisisChance = 7
elseif risk >= 8 then
crisisChance = 4
end
if (nextInt() % 1000) < crisisChance then
return pickFromList(nextInt, necromancyJointCrisis, nil)
end
end
-- Ordinary necromancy incidents remain rare. Higher-risk cities and
-- already-heightened days increase the chance slightly.
local chance = math.floor(risk / 2)
if desiredSev == "yellow" then
chance = chance + 3
elseif desiredSev == "red" then
chance = chance + 6
end
if (nextInt() % 100) >= chance then
return nil
end
local pool = necromancyEventsByCity[cityName]
if not pool or #pool == 0 then return nil end
local chosen = pickFromList(nextInt, pool, nil)
local label = necromancyResponderLabel[order.key] or "Alperkin necromancy-response mission"
return T(
label .. ": " .. chosen.text ..
" Sanctioned ancestor rites remain distinguished from forbidden necromancy involving spirit-binding, corpse animation, grave desecration, illicit resurrection, or prohibited funerary texts.",
chosen.sev or "yellow"
)
end
--------------------------------------------------------------------------------
-- 15.1) SOUTHERN STRAIT MISSIONARY OPERATIONS
--
-- These special operations may appear only when the generated city row is one
-- of the Haifan Bassaridian cities. The city remains the listed station, but
-- the task text may describe movement across the Southern Strait, former
-- Maritime Markets corridors, pilgrimage routes, inland transfer points,
-- quay markets, ferry lines, canal mouths, or rail-linked cargo routes.
--------------------------------------------------------------------------------
local southernStraitMissionLabels = {
"Southern Strait missionary circuit",
"Southern Strait pilgrimage-route mission",
"Former Maritime Markets liaison mission",
"Southern Strait dockside intervention",
"Southern Strait inland-route mission",
"Southern Strait shrine-and-quay mission",
"Southern Strait corridor-stabilization mission",
}
local southernStraitGenericOps = {
T("missionaries move between shrine piers, ferry queues, inland transfer offices, and market lanes to stabilize lawful religious movement while recording interference by brokers, smugglers, or hostile cult agents.","yellow"),
T("missionaries conduct a multi-stop route audit through quay markets, road-transfer yards, and rail-linked cargo offices; local stewards collect testimony while Temple Bank clerks verify custody records.","yellow"),
T("missionaries establish a temporary calm station near a port-to-inland transfer point, separating pilgrims, cargo handlers, and anxious families while reports are routed back through the local Haifan station.","green"),
}
local southernStraitOpsByCultKey = {
AUM = {
T("Order of Aurora Mystica missionaries conduct dawn reassurance rites across multiple harbor shrines, inspect lamp stations for false-signal use, and gather civic complaints from travelers arriving before inspection hours.","green"),
T("Order of Aurora Mystica missionaries run a first-light grievance circuit from the quay to inland registry offices; witnesses reporting intimidation by brokers or smugglers are moved through protected morning routes.","yellow"),
T("Order of Aurora Mystica missionaries bless harbor beacons and shrine lamps while correcting rumors that false lights or unusual dawn colors signal divine approval for illicit passage.","yellow"),
},
HSA = {
T("Harmony Sanctum missionaries mediate a dispute between dockworkers, ferry crews, shrine porters, and caravan handlers; neutral lanes are marked so trade and pilgrimage traffic can continue without retaliation.","green"),
T("Harmony Sanctum missionaries establish a reconciliation table for families, vendors, and sailors divided by smuggling accusations; testimony is separated from rumor before being routed to Temple Bank clerks.","yellow"),
T("Harmony Sanctum missionaries conduct a multi-ward crowd discipline mission through quay markets and inland caravan yards, using steward lanes to prevent merchant quarrels from becoming anti-Bassaridian unrest.","yellow"),
},
IGA = {
T("Ignis Aeternum missionaries inspect lantern stores, brazier permits, and warehouse fuel rooms along the Southern Strait after rumors of arson cover for smuggling transfers.","yellow"),
T("Ignis Aeternum missionaries conduct firewatch rites at cargo depots and harbor shrines, marking illegal fuel caches and unsafe pyre materials for seizure by local authorities.","yellow"),
T("Ignis Aeternum missionaries support emergency evacuation after a dockside fire scare, keeping torch-bearing celebrants away from bonded warehouses and preserving evidence of possible sabotage.","red"),
},
CHS = {
T("Celestial Harmony Sect missionaries correct false omen charts circulating among sailors, pilgrims, and night-ferry crews; approved sky-sign guidance is posted at shrine piers and inland waiting halls.","green"),
T("Celestial Harmony Sect missionaries conduct a night-navigation reassurance circuit, separating legitimate star-lore from coded smuggler signals used near the former Maritime Markets routes.","yellow"),
T("Celestial Harmony Sect missionaries open quiet stations for travelers frightened by sky-sign rumors while clerks record which brokers spread the panic through ferry lines.","yellow"),
},
GGS = {
T("Guild of Golden Shadows missionaries trace forged shrine permits, false port vouchers, and suspicious cargo seals through dockside broker houses and inland rail offices.","yellow"),
T("Guild of Golden Shadows missionaries conduct a covert ledger comparison between market scribes, ferry agents, and warehouse clerks to expose cargo-identity laundering in former Maritime Markets networks.","yellow"),
T("Guild of Golden Shadows missionaries identify a counterfeit Temple seal chain moving through pilgrimage baggage; suspect stamps are catalogued and witness statements sealed for review.","red"),
T("Guild of Golden Shadows missionaries convene a Southern Strait trade-agreement parley with ferry stewards, orchard carriers, quay merchants, shrine porters, and inland caravan representatives; proposed terms are written into a local compact and forwarded to [[General Port of Lake Morovia]] registry clerks.","green"),
T("Guild of Golden Shadows missionaries establish a provisional trade agreement between a harbor community and nearby inland producers, using voucher reconciliation, witness seals, and Temple Bank notation to prevent old Maritime Markets brokers from distorting the terms.","yellow"),
T("Guild of Golden Shadows missionaries promote [[General Port of Lake Morovia]] membership among local merchants, small-craft owners, shrine suppliers, and caravan brokers by explaining registry protections, lawful access to supervised markets, and the commercial benefits of audited participation.","green"),
T("Guild of Golden Shadows missionaries conduct a General Port investment and membership briefing in a quay market, identifying trustworthy local partners, recording investor interest, and separating legitimate community capital from smuggler-backed broker money.","yellow"),
T("Guild of Golden Shadows missionaries organize a trade-normalization circuit across ferry landings and inland transfer yards, encouraging local communities to enter General Port membership while documenting forged contracts, intimidation, and false debt claims.","yellow"),
T("Guild of Golden Shadows missionaries broker a community-to-port supply arrangement for lawful agricultural, fishery, craft, or shrine goods; General Port membership forms, cargo-origin notes, and Temple Bank witness records are completed on site.","green"),
T("Guild of Golden Shadows missionaries investigate resistance to General Port membership after rumors spread that registry enrollment will erase local trading privileges; the mission corrects false claims while preserving valid community exemptions for review.","yellow"),
T("Guild of Golden Shadows missionaries expose a broker faction attempting to sabotage a new General Port trade agreement by planting false investment notes, counterfeit seals, and misleading debt ledgers among local merchants.","red"),
T("Guild of Golden Shadows missionaries escort local community representatives through the first stage of General Port enrollment, protecting membership papers, investment pledges, and cargo ledgers from theft, intimidation, or alteration by former Maritime Markets intermediaries.","yellow"),
T("Guild of Golden Shadows missionaries create a confidential list of community merchants suitable for future General Port investment partnerships, excluding suspected smugglers, forged-voucher carriers, and broker agents tied to unlawful corridor disruption.","yellow"),
},
AZS = {
T("Azure Sentinel Sect missionaries inspect ferry queues, canal approaches, rope lanes, pilgrim piers, and water-safety markers while documenting unsafe crossings used by smugglers.","yellow"),
T("Azure Sentinel Sect missionaries conduct a waterfront safety circuit across the Southern Strait, marking storm lanes, rescue points, and suspicious skiff landings for follow-up by port authorities.","yellow"),
T("Azure Sentinel Sect missionaries respond to a dangerous ferry-ramp surge near a shrine pier, opening rescue lanes and separating pilgrims from unregistered cargo handlers.","red"),
},
RNB = {
T("Reverie Nebulous missionaries record nightmare claims among sailors, ferry workers, and dockside families, distinguishing dream contagion rumors from Noctic cargo panic and ordinary smuggling fear.","yellow"),
T("Reverie Nebulous missionaries open supervised rest spaces for distressed travelers moving through the Southern Strait pilgrimage routes; rumor patterns are mapped by quay and ferry station.","yellow"),
T("Reverie Nebulous missionaries intervene after a mass dream-panic episode in a harbor ward, moving witnesses away from crowd pressure and preserving testimony under seal.","red"),
},
EOF = {
T("Eon Fellowship missionaries reconcile old Maritime Markets family oaths with current Bassaridian registry expectations, recording continuity pledges from ship families and hereditary port stewards.","green"),
T("Eon Fellowship missionaries conduct an intergenerational testimony circuit along the Southern Strait, preserving family records before broker houses can alter lineage claims or debt histories.","yellow"),
T("Eon Fellowship missionaries stabilize a dispute over ancestral trading privileges by redirecting claimants into formal Temple Bank testimony and registry review.","yellow"),
},
UMB = {
T("Order of the Umbral Oracle missionaries conduct a night-route inquiry through alleys, ferry shelters, and shrine back rooms where courier rumors suggest hidden anti-Bassaridian coordination.","yellow"),
T("Order of the Umbral Oracle missionaries interview night watchmen, lamplighters, and shrine attendants to separate legitimate nocturnal rites from covert courier drops.","yellow"),
T("Order of the Umbral Oracle missionaries activate a red-posture shadow-watch after masked agitators attempt to use pilgrimage crowds as cover for illicit ritual movement.","red"),
},
VDE = {
T("Mystery of the Verdant Embrace missionaries inspect plant offerings, medicinal herbs, orchard cargo, and market produce moving from the quay into inland shrine routes.","green"),
T("Mystery of the Verdant Embrace missionaries trace suspicious plant bundles through wet markets and ferry landings, checking whether ritual herbs are being used to conceal contraband.","yellow"),
T("Mystery of the Verdant Embrace missionaries secure a contaminated produce consignment after rumors connect it to illicit Noctic-Rabrev handling along the Southern Strait.","red"),
},
CON = {
T("Conclave Illuminara missionaries review Tarsica-timed pilgrimage movements, ferry schedules, and night-vigil routes to prevent lunar observances from being exploited by smugglers.","green"),
T("Conclave Illuminara missionaries correct false lunar-tide claims used by brokers to justify unauthorized night crossings; revised timing guidance is posted at harbor shrines.","yellow"),
T("Conclave Illuminara missionaries open a controlled moonlight vigil route after panic spreads through a ferry queue; stewards record names and prevent unsupervised dispersal.","yellow"),
},
SVI = {
T("Sanctum Vitalis missionaries escort clinic packets, triage notes, and medical supplies between harbor clinics, ferry stations, and inland care posts along the Southern Strait.","yellow"),
T("Sanctum Vitalis missionaries conduct health screening at a pilgrimage landing, separating fever, exhaustion, Noctic concerns, and ordinary travel sickness before rumors can spread.","yellow"),
T("Sanctum Vitalis missionaries activate a red-posture medical transfer after hostile crowds threaten registered patients moving from a dockside clinic to an inland shelter.","red"),
},
ALB = {
T("Temple Alabaster missionaries inspect sealed tinctures, balms, medicines, and purity records moving through Southern Strait warehouses and clinic piers.","green"),
T("Temple Alabaster missionaries identify counterfeit medicine jars in dockside cargo and redirect the consignment through Temple Bank custody for purity review.","yellow"),
T("Temple Alabaster missionaries conduct an emergency medicine-chain audit after a forged clinic seal appears in ferry freight moving inland.","red"),
},
CICL = {
T("Court of the Ironclad missionaries establish a protective witness lane for threatened shrine clerks, ferry stewards, and registry witnesses reporting smuggling activity.","yellow"),
T("Court of the Ironclad missionaries support a disciplined perimeter around a seized cargo stack, preventing vigilante interference while legal testimony is gathered.","yellow"),
T("Court of the Ironclad missionaries activate crisis protection for a high-risk detainee transfer connected to pirate recruiting near a pilgrimage route.","red"),
},
ACC = {
T("Accord Concordia missionaries negotiate temporary passage terms between ferry operators, caravan leaders, shrine porters, and market stewards to keep lawful movement open.","green"),
T("Accord Concordia missionaries draft a written concord between rival broker families accused of obstructing pilgrims and lawful cargo moving through the Southern Strait.","yellow"),
T("Accord Concordia missionaries convene an emergency truce after dockside violence threatens to spread into inland caravan yards and shrine courtyards.","red"),
},
STY = {
T("Mystery of the Stygian Veil missionaries conduct death-at-sea rites for recovered bodies while ensuring mourning families are not exploited by brokers or rumor sellers.","yellow"),
T("Mystery of the Stygian Veil missionaries escort funerary offerings through a contested harbor ward, separating legitimate mourning from illicit ritual theater.","yellow"),
T("Mystery of the Stygian Veil missionaries stabilize a red-posture mortuary transfer after violent rumors spread around unidentified remains from the Southern Strait.","red"),
},
SYL = {
T("Sylvan Fellowship missionaries inspect coastal groves, animal lanes, and shrine gardens near ferry roads where displaced wildlife and pilgrim traffic have begun to clash.","green"),
T("Sylvan Fellowship missionaries guide a low-impact pilgrimage route around sensitive shoreline habitat while documenting illegal cutting, trapping, and smuggler paths.","yellow"),
T("Sylvan Fellowship missionaries coordinate emergency animal-control and relief stations after floodwater drives coastal creatures into a crowded shrine district.","red"),
},
MIR = {
T("Mystery of Red Mirth missionaries manage reveler lanes during a dockside festival, separating licensed celebration from smuggler cover and unsafe intoxication.","yellow"),
T("Mystery of Red Mirth missionaries conduct a merriment-control circuit from ferry markets to inland tavern lanes, using steward chants to prevent crowd surges.","yellow"),
T("Mystery of Red Mirth missionaries activate a crisis dispersal rite after festival crowds are used to conceal courier drops and provoke anti-registry disorder.","red"),
},
CAT = {
T("Rex Catonis missionaries open a legal registration table for ferry workers, cargo handlers, and disputed household claims connected to Southern Strait movement.","green"),
T("Rex Catonis missionaries review petitions over seized cargo, blocked pilgrimage routes, and alleged abuse by brokers, routing valid claims into formal hearing dockets.","yellow"),
T("Rex Catonis missionaries conduct an emergency rights-and-duties reading after a seizure dispute threatens to turn into a dockside riot.","red"),
},
SDE = {
T("Sanctum Delphica missionaries audit shrine credentials, restricted rite permits, teaching licenses, and courier papers moving through the Southern Strait corridor.","yellow"),
T("Sanctum Delphica missionaries correct unsafe doctrine claims posted near ferry stations and market shrines, replacing them with approved quarantine and registry language.","yellow"),
T("Sanctum Delphica missionaries lock down a compromised archive packet after forged ritual permissions are discovered in inland-route baggage.","red"),
},
AMI = {
T("Ordo Amicitia missionaries distribute food, water, blankets, and family-support vouchers to pilgrims delayed by inspection queues or disrupted ferry schedules.","green"),
T("Ordo Amicitia missionaries escort displaced families between dockside shelters, shrine courtyards, and inland aid offices while shielding them from broker recruitment.","yellow"),
T("Ordo Amicitia missionaries activate a red-posture relief corridor after smuggling violence strands families near a port-to-inland transfer point.","red"),
},
ILL = {
T("Temple Illuminata missionaries inspect teaching booths, gold-market charity records, and illumination lamps used for public instruction near Southern Strait trade lanes.","green"),
T("Temple Illuminata missionaries correct false prosperity sermons used by brokers to lure pilgrims into unauthorized cargo schemes and debt traps.","yellow"),
T("Temple Illuminata missionaries secure educational records and donation ledgers after a counterfeit charity ring is uncovered in a quay market.","red"),
},
TAP = {
T("Temple Aprobelle missionaries conduct a rumor-control circuit across ferry queues, quay markets, shrine lanes, and inland transfer offices after panic spreads about smuggling raids.","yellow"),
T("Temple Aprobelle missionaries map rumor nodes tied to pirate brokers, false cult warnings, and anti-Bassaridian gossip, then issue corrective language through local stewards.","yellow"),
T("Temple Aprobelle missionaries activate a crisis social-harmony response after crowd panic threatens to block lawful pilgrimage and cargo movement through the Southern Strait.","red"),
},
}
local function maybeSouthernStraitCultTask(nextInt, cityName, order, desiredSev)
if not isHaifanBassaridiaCity(cityName) then return nil end
if not order or not order.key then return nil end
-- Frequency control:
-- Green days: occasional; Yellow days: common; Red days: very common.
local chance = 35
if desiredSev == "yellow" then
chance = 48
elseif desiredSev == "red" then
chance = 65
end
if (nextInt() % 100) >= chance then
return nil
end
local pool = southernStraitOpsByCultKey[order.key] or southernStraitGenericOps
local chosen = pickFromList(nextInt, pool, nil) or southernStraitGenericOps[1]
local label = pickFromList(nextInt, southernStraitMissionLabels, nil) or "Southern Strait missionary circuit"
return T(
label .. ": " .. chosen.text ..
" Mission remains staged and logged through " .. cityLink(cityName) ..
", but may operate across the wider Southern Strait, former Maritime Markets corridors, pilgrimage routes, ferry lines, and inland transfer points.",
chosen.sev or desiredSev or "yellow"
)
end
--------------------------------------------------------------------------------
-- 16) TEMPLE BANK NOTES
--------------------------------------------------------------------------------
local bankNotes = {
"Stipend disbursed after checklist verification; receipt countersigned by local steward.",
"Voucher reconciliation complete; supplies logged and stored under dual-key protocol.",
"Observer note: crowd discipline improved after posted guidance and steward coaching.",
"Repair authorization issued; labor scheduled at off-peak hours.",
"Custody ledger updated; medicines/artifacts sealed and moved to secure storage.",
"Training memo filed; corrective instruction issued to volunteers for queue discipline.",
}
--------------------------------------------------------------------------------
-- 17) BUILD ONE CITY ROW
--------------------------------------------------------------------------------
local function buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, ctx)
local cityName = c.name
local seed = hash31(string.format("mops:%d:%03d:%s", di.year, di.dayOfYear, cityName))
local nextInt = lcg(seed)
local th = includeThreat and threatMap and threatMap[cityName] or nil
local o = pickOrder(nextInt, cityName, th)
ctx.usedTasksByCult[o.name] = ctx.usedTasksByCult[o.name] or {}
local used = ctx.usedTasksByCult[o.name]
local threatUrg = th and th.urg or ""
-- Preliminary severity gives pickTask a useful target before the exact
-- task-driven severity is known.
local prelimSev = "green"
if c.tier == "major" then prelimSev = "yellow" end
if urgencyScore(threatUrg) >= 3 then
prelimSev = "red"
elseif urgencyScore(threatUrg) == 2 and prelimSev == "green" then
prelimSev = "yellow"
end
local baseTask, pickedIdx = pickTask(nextInt, o.name, prelimSev, used)
-- Rare Alperkin necromancy / forbidden ancestor-rite override.
-- This happens before the Southern Strait override because it is tied to
-- specific Alperkin and core hotspot cities rather than corridor operations.
local necromancyTask = maybeAlperkinNecromancyTask(nextInt, cityName, o, prelimSev)
if necromancyTask then
baseTask = necromancyTask
pickedIdx = nil
end
-- If this missionary assignment is stationed in Haifan Bassaridia, it may
-- become a Southern Strait-wide cult-specific operation. This does not move
-- the city row; it only expands the operating area described in the task.
local southernStraitTask = maybeSouthernStraitCultTask(nextInt, cityName, o, prelimSev)
if southernStraitTask then
baseTask = southernStraitTask
pickedIdx = nil
end
if pickedIdx then used[pickedIdx] = true end
local sev = inferSeverity(c.tier, baseTask.sev, threatUrg)
-- WX overlay
local opsBadge, wxData = opsCell(cityName, di.year, di.dayOfYear)
sev = bumpSevWeather(sev, wxData)
local taskText = baseTask.text .. weatherTaskOverlay(wxData)
-- AZS special case: rare clandestine/assassin operations under severe threat
if o.key == "AZS" and shouldAZSGoCovert(nextInt, sev, th) then
local covert = pickFromList(nextInt, azsCovertOps, nil)
if covert and covert.text then
taskText = covert.text .. weatherTaskOverlay(wxData)
sev = "red"
end
end
-- unit
local unitName = pickOrgUnit(nextInt, sev)
local unitMeta = org[unitName]
local unitIndex = (nextInt() % 12) + 1
local unitCode = string.format("%s-%s%d", o.key, unitMeta.code, unitIndex)
-- bank note
local bank = pickFromList(nextInt, bankNotes, nil)
-- econ
local econ = ""
if showEcon and econSignals and econSignals[cityName] then
econ = econSignals[cityName].summary or ""
end
return {
style = sevStyle(sev),
date = di.text,
city = string.format("%s %s", getCityFlagMarkup(cityName), cityLink(cityName)),
region = c.region,
ops = opsBadge,
order = getCultFlagMarkup(o.name) .. cultLink(o.name),
unit = string.format("%s (%d) [%s]", unitName, unitMeta.size, unitCode),
task = taskText,
bank = bank,
threat = (includeThreat and th and th.event) and th.event or "",
threatUrg = (includeThreat and th and th.urg) and th.urg or "",
sev = sev,
econ = econ,
}
end
--------------------------------------------------------------------------------
-- 18) MAIN TABLE
--------------------------------------------------------------------------------
function p.makeTable(frame)
local args = (frame and frame.args) or {}
local regionFilter = tostring(args.region or "All")
local showLegend = tostring(args.showlegend or "yes"):lower() ~= "no"
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local showRelations = tostring(args.showRelations or args.cultRelations or "no"):lower() == "yes"
local limit = tonumber(args.limit or "")
local di = getDateInfo(args)
-- Threat map (only if requested)
local threatMap = {}
if includeThreat then
local ok, rel = pcall(require, "Module:ReligiousEvents")
if ok and rel and type(rel.makeTable) == "function" then
threatMap = parseReligiousEventsTable(rel.makeTable())
end
end
-- Econ signals (expensive; only if requested)
local econSignals = {}
if showEcon then
econSignals = getEconSignals()
end
local out = {}
table.insert(out, "{| class='wikitable sortable'")
local header = "! style='white-space:nowrap;' | Date !! City !! Region !! Ops Alert !! Mystery (Cult) !! Unit !! Tasking"
if showEcon then header = header .. " !! Economic signal" end
header = header .. " !! Temple Bank Notes"
if includeThreat then header = header .. " !! Threat Watch" end
header = header .. " !! Severity"
table.insert(out, header)
local count = 0
for _, c in ipairs(cityOrder) do
if regionPass(regionFilter, c.region) then
local row = buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, ctx)
table.insert(out, "|-" .. row.style)
local line = string.format("| %s || %s || %s || %s || %s || %s || %s",
row.date, row.city, row.region, row.ops, row.order, row.unit, row.task
)
if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
line = line .. " || " .. row.bank
if includeThreat then
local threatCell = (row.threat ~= "" and (row.threat .. (row.threatUrg ~= "" and (" ("..row.threatUrg..")") or ""))) or "No reports / n.a."
line = line .. " || " .. threatCell
end
line = line .. " || " .. row.sev
table.insert(out, line)
count = count + 1
if limit and count >= limit then break end
end
end
table.insert(out, "|}")
if showLegend then
table.insert(out, "\n<div style='margin-top:0.5em; font-size:90%;'>")
table.insert(out, "<b>Legend:</b> ")
table.insert(out, "<span style='background:#d4edda;padding:2px 6px;border:1px solid #c3e6cb;'>Green</span> = Routine rites / civic support ")
table.insert(out, "<span style='background:#fff3cd;padding:2px 6px;border:1px solid #ffeeba;'>Yellow</span> = Heightened oversight / rumor control / targeted intervention ")
table.insert(out, "<span style='background:#f8d7da;padding:2px 6px;border:1px solid #f5c6cb;'>Red</span> = Crisis response / unrest / outbreak / high-risk operations")
table.insert(out, "<br/>Ops Alert badge reflects forecast-driven operational risk and aligns with War League / Hatch posture.")
table.insert(out, "<br/>For overseas deployments please see [[Bassaridian_involvement_in_Corum#Corumian_Missionary_Deployment_Reports|Corumian Missionary Deployment Reports]].")
table.insert(out, "</div>")
end
if di.calendarEvent and di.calendarEvent ~= "" then
table.insert(out, string.format("\n<div style='margin-top:0.5em; font-size:90%%;'><b>Calendar note:</b> %s</div>", di.calendarEvent))
end
if showRelations then
table.insert(out, "\n== Daily cult-relations pressure ==")
table.insert(out, renderCultRelations(args))
end
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- 19) SINGLE CITY VIEW
--------------------------------------------------------------------------------
function p.makeCity(frame)
local args = (frame and frame.args) or {}
local city = tostring(args.city or "")
if city == "" then return "''Error: city parameter required.''" end
if not citySet[city] then return "''Error: unknown city (must match canonical name).''" end
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local di = getDateInfo()
local threatMap = {}
if includeThreat then
local ok, rel = pcall(require, "Module:ReligiousEvents")
if ok and rel and type(rel.makeTable) == "function" then
threatMap = parseReligiousEventsTable(rel.makeTable())
end
end
local econSignals = {}
if showEcon then econSignals = getEconSignals() end
local region, tier = "Core", "minor"
for _, c in ipairs(cityOrder) do
if c.name == city then region, tier = c.region, c.tier break end
end
local row = buildCityRow(di, {name=city, region=region, tier=tier}, threatMap, includeThreat, showEcon, econSignals, { usedTasksByCult = {} })
local out = {}
table.insert(out, "{| class='wikitable'")
local header = "! Date !! City !! Region !! Ops Alert !! Mystery (Cult) !! Unit !! Tasking"
if showEcon then header = header .. " !! Economic signal" end
header = header .. " !! Temple Bank Notes"
if includeThreat then header = header .. " !! Threat Watch" end
header = header .. " !! Severity"
table.insert(out, header)
table.insert(out, "|-" .. row.style)
local line = string.format("| %s || %s || %s || %s || %s || %s || %s",
row.date, row.city, row.region, row.ops, row.order, row.unit, row.task
)
if showEcon then line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a") end
line = line .. " || " .. row.bank
if includeThreat then
local threatCell = (row.threat ~= "" and (row.threat .. (row.threatUrg ~= "" and (" ("..row.threatUrg..")") or ""))) or "No reports / n.a."
line = line .. " || " .. threatCell
end
line = line .. " || " .. row.sev
table.insert(out, line)
table.insert(out, "|}")
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- 20) ORDER-ONLY VIEW
--------------------------------------------------------------------------------
function p.makeOrder(frame)
local args = (frame and frame.args) or {}
local wanted = tostring(args.order or "")
if wanted == "" then
return "''Error: order parameter required (use exact order name).''"
end
local wantedOrder = orderByName[wanted]
if not wantedOrder then
return "''Error: unknown order name (must match module order names).''"
end
local includeThreat = tostring(args.includeThreat or "no"):lower() == "yes"
local showEcon = tostring(args.showEcon or "no"):lower() == "yes"
local di = getDateInfo()
local threatMap = {}
if includeThreat then
local ok, rel = pcall(require, "Module:ReligiousEvents")
if ok and rel and type(rel.makeTable) == "function" then
threatMap = parseReligiousEventsTable(rel.makeTable())
end
end
local econSignals = {}
if showEcon then econSignals = getEconSignals() end
local out = {}
table.insert(out, "{| class='wikitable sortable'")
local header = "! Date !! City !! Region !! Ops Alert !! Unit !! Tasking"
if showEcon then header = header .. " !! Economic signal" end
header = header .. " !! Temple Bank Notes"
if includeThreat then header = header .. " !! Threat Watch" end
header = header .. " !! Severity"
table.insert(out, header)
for _, c in ipairs(cityOrder) do
local row = buildCityRow(di, c, threatMap, includeThreat, showEcon, econSignals, { usedTasksByCult = {} })
-- Compare by cultLink target (stable) rather than raw text
if row.order:find(cultLink(wantedOrder.name), 1, true) or row.order:find(wantedOrder.name, 1, true) then
table.insert(out, "|-" .. row.style)
local line = string.format("| %s || %s || %s || %s || %s || %s",
row.date, row.city, row.region, row.ops, row.unit, row.task
)
if showEcon then
line = line .. " || " .. (row.econ ~= "" and row.econ or "n/a")
end
line = line .. " || " .. row.bank
if includeThreat then
local threatCell = (row.threat ~= "" and (row.threat .. (row.threatUrg ~= "" and (" ("..row.threatUrg..")") or ""))) or "No reports / n.a."
line = line .. " || " .. threatCell
end
line = line .. " || " .. row.sev
table.insert(out, line)
end
end
table.insert(out, "|}")
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- 21) CULT RELATIONS PROXY FUNCTIONS
-- These keep [[Module:CultRelationsDailyBVR]] separate, but allow pages to call
-- cult relations through this missionary module when desired.
--------------------------------------------------------------------------------
function p.cultRelations(frame)
if not cultRel or type(cultRel.makeTable) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable.''"
end
local ok, result = pcall(cultRel.makeTable, frame)
if ok then
return result
end
return "''Cult-relations display error: " .. tostring(result) .. "''"
end
function p.cultRelationPair(frame)
if not cultRel or type(cultRel.makePair) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose makePair.''"
end
local ok, result = pcall(cultRel.makePair, frame)
if ok then
return result
end
return "''Cult-relation pair display error: " .. tostring(result) .. "''"
end
function p.cultRelationEvents(frame)
if not cultRel or type(cultRel.eventLog) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose eventLog.''"
end
local ok, result = pcall(cultRel.eventLog, frame)
if ok then
return result
end
return "''Cult-relations event-log error: " .. tostring(result) .. "''"
end
function p.cultRelationMatrix(frame)
if not cultRel or type(cultRel.makeMatrix) ~= "function" then
return "''[[Module:CultRelationsDailyBVR]] is unavailable or does not expose makeMatrix.''"
end
local ok, result = pcall(cultRel.makeMatrix, frame)
if ok then
return result
end
return "''Cult-relations matrix error: " .. tostring(result) .. "''"
end
return p