Module:MissionaryDeploymentCorum: Difference between revisions
From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs) No edit summary |
NewZimiaGov (talk | contribs) No edit summary |
||
| Line 26: | Line 26: | ||
local dateString = string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear) | local dateString = string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear) | ||
return { text = dateString, year = psscYear, dayOfYear = dayOfYear } | return { text = dateString, year = psscYear, dayOfYear = dayOfYear, month = month, dom = dayOfMonth } | ||
end | |||
-- Health surge activates on/after 36/2/52 PSSC and remains active thereafter | |||
local function surgeActive(di) | |||
-- 36/2 => dayOfYear = 61 + 36 = 97 | |||
if di.year > 52 then return true end | |||
if di.year < 52 then return false end | |||
return di.dayOfYear >= 97 | |||
end | end | ||
| Line 32: | Line 40: | ||
-- (B) STATIC DATA | -- (B) STATIC DATA | ||
------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ||
-- Units (fixed | -- Units (fixed baseline; headcount will display +50% under surge) | ||
local units = { | local units = { | ||
{order="Celestial Harmony Sect", unit="Kleisthenes", headcount=25, code="CHS-K1"}, | {order="Celestial Harmony Sect", unit="Kleisthenes", headcount=25, code="CHS-K1"}, | ||
| Line 42: | Line 50: | ||
} | } | ||
-- Districts ( | -- Districts (NZ = New Zimia heritage; GM = Bayen/Gamesman) | ||
-- Give Nortonian & Braxian higher weight under respiratory-risk focus | |||
local districts = { | local districts = { | ||
{name="Lyvian Zones", kind="NZ"}, | {name="Lyvian Zones", kind="NZ", weight=1}, | ||
{name="Lucinarian Zones", kind="NZ"}, | {name="Lucinarian Zones", kind="NZ", weight=1}, | ||
{name="Brentonian Zones", kind="NZ"}, | {name="Brentonian Zones", kind="NZ", weight=1}, | ||
{name="Ossyani Zones", kind="NZ"}, | {name="Ossyani Zones", kind="NZ", weight=1}, | ||
{name="Sinclarian Zones", kind="GM"}, | {name="Sinclarian Zones", kind="GM", weight=1}, | ||
{name="Nortonian Zones", kind="GM"}, | {name="Nortonian Zones", kind="GM", weight=3}, -- focus | ||
{name="Wallisian Zones", kind="GM"}, | {name="Wallisian Zones", kind="GM", weight=1}, | ||
{name="Alindan Zones", kind="GM"}, | {name="Alindan Zones", kind="GM", weight=1}, | ||
{name="Aijiac Zones", kind="GM"}, | {name="Aijiac Zones", kind="GM", weight=1}, | ||
{name="Braxian Zones", kind="GM"}, | {name="Braxian Zones", kind="GM", weight=3}, -- focus | ||
} | } | ||
-- Activities ( | -- Activities (add nutrition) | ||
local activities = { | local activities = { | ||
{name="relief"}, -- medical/aid distribution | {name="relief"}, -- medical/aid distribution | ||
{name="education"}, -- instruction, literacy, catechesis | {name="education"}, -- instruction, literacy, catechesis | ||
{name="reconciliation"}, -- mediation, community dialogues | {name="reconciliation"}, -- mediation, community dialogues | ||
{name="nutrition"}, -- fortified rations, screenings, supplements | |||
} | } | ||
| Line 114: | Line 124: | ||
end | end | ||
local function outputsFor(activityName) | local function outputsFor(activityName, districtName) | ||
if activityName == "relief" then | if activityName == "relief" then | ||
local patients = math.random( | local patients = math.random(22, 72) | ||
local parcels = math.random( | local parcels = math.random(60, 180) | ||
-- Modestly higher effort if in the two focus zones | |||
if districtName == "Nortonian Zones" or districtName == "Braxian Zones" then | |||
patients = math.floor(patients * 1.15) | |||
parcels = math.floor(parcels * 1.15) | |||
end | |||
return string.format("patients seen: %d; relief parcels: %d", patients, parcels) | return string.format("patients seen: %d; relief parcels: %d", patients, parcels) | ||
elseif activityName == "education" then | elseif activityName == "education" then | ||
local classes = math.random(1, 3) | local classes = math.random(1, 3) | ||
local students = math.random( | local students = math.random(28, 110) | ||
return string.format("classes: %d; learners served: %d", classes, students) | return string.format("classes: %d; learners served: %d", classes, students) | ||
elseif activityName == "nutrition" then | |||
local rations = math.random(120, 320) | |||
local screened = math.random(35, 120) | |||
local supplements = math.random(80, 220) | |||
if districtName == "Nortonian Zones" or districtName == "Braxian Zones" then | |||
rations = math.floor(rations * 1.20) | |||
screened = math.floor(screened * 1.20) | |||
supplements = math.floor(supplements* 1.20) | |||
end | |||
return string.format("fortified rations: %d; health screenings: %d; supplements issued: %d", | |||
rations, screened, supplements) | |||
else -- reconciliation | else -- reconciliation | ||
local circles = math.random(1, 4) | local circles = math.random(1, 4) | ||
| Line 154: | Line 180: | ||
end | end | ||
-- | -- Row color based on compliance outcomes | ||
local function rowColor(hceLine, guards, obsLine) | local function rowColor(hceLine, guards, obsLine) | ||
if guards:match("deferred") or hceLine:match("pre%-mission") then | if guards:match("deferred") or hceLine:match("pre%-mission") then | ||
| Line 185: | Line 211: | ||
-- Build a compact transparency trigger line per row | -- Build a compact transparency trigger line per row | ||
local function buildTriggers(u, d, act, obsLine, hceLine, guards, wl) | local function buildTriggers(u, d, act, obsLine, hceLine, guards, wl, di, surge) | ||
local t = {} | local t = {} | ||
-- observer presence / delay | -- observer presence / delay | ||
| Line 212: | Line 238: | ||
table.insert(t, "district "..(d.kind or "NZ")) | table.insert(t, "district "..(d.kind or "NZ")) | ||
table.insert(t, "activity "..act.name) | table.insert(t, "activity "..act.name) | ||
-- surge flags | |||
if surge then | |||
table.insert(t, "health surge: active") | |||
if d.name == "Nortonian Zones" or d.name == "Braxian Zones" then | |||
table.insert(t, "focus: "..d.name) | |||
end | |||
end | |||
return table.concat(t, "; ") | return table.concat(t, "; ") | ||
end | end | ||
| Line 224: | Line 257: | ||
local rows = tonumber(frame and frame.args and frame.args.rows) or 12 | local rows = tonumber(frame and frame.args and frame.args.rows) or 12 | ||
local showLegend = (frame and frame.args and tostring(frame.args.showlegend) or "yes"):lower() ~= "no" | local showLegend = (frame and frame.args and tostring(frame.args.showlegend) or "yes"):lower() ~= "no" | ||
local healthSurge = surgeActive(dateInfo) | |||
local out = {} | local out = {} | ||
| Line 230: | Line 264: | ||
for i = 1, rows do | for i = 1, rows do | ||
-- Prefer weighted districts (Braxian/Nortonian focus) | |||
local d = pickWeighted(districts) | |||
-- Bias activities in focus zones toward relief/nutrition | |||
local act | |||
if (d.name == "Nortonian Zones" or d.name == "Braxian Zones") and math.random() < 0.65 then | |||
act = ({ {name="relief"}, {name="nutrition"} })[math.random(2)] | |||
else | |||
act = pick(activities) | |||
end | |||
local u = pick(units) | local u = pick(units) | ||
local | -- Display +50% headcount during surge | ||
local displayedHeadcount = u.headcount | |||
local outputs = outputsFor(act.name) | if healthSurge then | ||
displayedHeadcount = math.floor(u.headcount * 1.5 + 0.5) | |||
end | |||
local outputs = outputsFor(act.name, d.name) | |||
local obsLine = observerLine() | local obsLine = observerLine() | ||
local hceLine = string.format("%s; %s", hceRef(dateInfo, u.code), pick(kitNotes)) | local hceLine = string.format("%s; %s", hceRef(dateInfo, u.code), pick(kitNotes)) | ||
local guards = guardrailsStatus() | local guards = guardrailsStatus() | ||
-- | -- WL annotation | ||
local wl = pickWLCorridorForDistrict(d) | local wl = pickWLCorridorForDistrict(d) | ||
local gfId = geofenceProfileId(d, dateInfo) | local gfId = geofenceProfileId(d, dateInfo) | ||
| Line 250: | Line 297: | ||
end | end | ||
local unitStr = string.format("%s — %s (%d) [%s]", u.order, u.unit, | local unitStr = string.format("%s — %s (%d) [%s]", u.order, u.unit, displayedHeadcount, u.code) | ||
-- | -- Color | ||
local style = rowColor(hceLine, guards, obsLine) | local style = rowColor(hceLine, guards, obsLine) | ||
table.insert(out, "|-" .. style) | table.insert(out, "|-" .. style) | ||
-- | -- Case & triggers | ||
local caseId = string.format("MD-%03d.%03d-%02d", dateInfo.year, dateInfo.dayOfYear, i) | local caseId = string.format("MD-%03d.%03d-%02d", dateInfo.year, dateInfo.dayOfYear, i) | ||
local triggers = buildTriggers(u, d, act, obsLine, hceLine, guards, wl) | local triggers = buildTriggers(u, d, act, obsLine, hceLine, guards, wl, dateInfo, healthSurge) | ||
-- | -- Notes (append surge note when active) | ||
local | local surgeNote = healthSurge and "Public health surge: respiratory risk mitigation & nutrition support" or nil | ||
local parts = { guards, wlNote, "Case: "..caseId, "Triggers: "..triggers } | |||
if surgeNote then table.insert(parts, surgeNote) end | |||
local guardrailsNotes = table.concat(parts, " | ") | |||
table.insert(out, string.format("| %s || %s || %s || %s || %s || %s || %s || %s", | table.insert(out, string.format("| %s || %s || %s || %s || %s || %s || %s || %s", | ||
Revision as of 00:27, 1 October 2025
Documentation for this module may be created at Module:MissionaryDeploymentCorum/doc
-- Module: MissionaryDaily
-- Purpose: Daily, in-world report of missionary activities (pre-CRH), aligned with guardrails + WL expansion + transparency [[Baratar Scandal of 52 PSSC]]
local p = {}
-------------------------------------------------------------------------------
-- (A) PSSC DATE
-------------------------------------------------------------------------------
local function getPSSCDate()
local startDate = os.time({year = 1999, month = 8, day = 6})
local secondsInDay = 86400
local daysPerYear = 183
local now = os.time()
local totalDays = math.floor((now - startDate) / secondsInDay)
local yearFraction = totalDays / daysPerYear
local psscYear = math.floor(yearFraction)
local dayOfYear = math.floor((yearFraction - psscYear) * daysPerYear) + 1
local month, dayOfMonth
if dayOfYear <= 61 then
month = 1; dayOfMonth = dayOfYear
elseif dayOfYear <= 122 then
month = 2; dayOfMonth = dayOfYear - 61
else
month = 3; dayOfMonth = dayOfYear - 122
end
local dateString = string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear)
return { text = dateString, year = psscYear, dayOfYear = dayOfYear, month = month, dom = dayOfMonth }
end
-- Health surge activates on/after 36/2/52 PSSC and remains active thereafter
local function surgeActive(di)
-- 36/2 => dayOfYear = 61 + 36 = 97
if di.year > 52 then return true end
if di.year < 52 then return false end
return di.dayOfYear >= 97
end
-------------------------------------------------------------------------------
-- (B) STATIC DATA
-------------------------------------------------------------------------------
-- Units (fixed baseline; headcount will display +50% under surge)
local units = {
{order="Celestial Harmony Sect", unit="Kleisthenes", headcount=25, code="CHS-K1"},
{order="Reverie Nebulous", unit="Kleisthenes", headcount=25, code="REN-K1"},
{order="Temple Alabaster", unit="Kleisthenes", headcount=25, code="ALB-K1"},
{order="Sanctum Delphica", unit="Kleisthenes", headcount=25, code="DEL-K1"},
{order="Order of the Umbral Oracle", unit="Dodekade", headcount=12, code="UMO-D1"},
{order="Order of the Umbral Oracle", unit="Hetairos", headcount=6, code="UMO-H1"},
}
-- Districts (NZ = New Zimia heritage; GM = Bayen/Gamesman)
-- Give Nortonian & Braxian higher weight under respiratory-risk focus
local districts = {
{name="Lyvian Zones", kind="NZ", weight=1},
{name="Lucinarian Zones", kind="NZ", weight=1},
{name="Brentonian Zones", kind="NZ", weight=1},
{name="Ossyani Zones", kind="NZ", weight=1},
{name="Sinclarian Zones", kind="GM", weight=1},
{name="Nortonian Zones", kind="GM", weight=3}, -- focus
{name="Wallisian Zones", kind="GM", weight=1},
{name="Alindan Zones", kind="GM", weight=1},
{name="Aijiac Zones", kind="GM", weight=1},
{name="Braxian Zones", kind="GM", weight=3}, -- focus
}
-- Activities (add nutrition)
local activities = {
{name="relief"}, -- medical/aid distribution
{name="education"}, -- instruction, literacy, catechesis
{name="reconciliation"}, -- mediation, community dialogues
{name="nutrition"}, -- fortified rations, screenings, supplements
}
-- Observers (neutral/independent)
local observerPool = {
{label="Neutral temple auditor", weight=4},
{label="Independent port compliance inspector", weight=3},
{label="Community elder (witness)", weight=3},
{label="Straits Convention monitor (civilian)", weight=2},
{label="Oportian observer", weight=4},
{label="Nouvelle Alexandrie observer", weight=4},
{label="Imperial Federation observer", weight=4},
{label="Straits Convention monitor (civilian)", weight=2},
{label="No observer (weather delay)", weight=1},
}
-- Compliance notes (HCE & kit checks)
local kitNotes = {
"HCE log filed; roster posted; kit verified (no encryption/UAV/sensors)",
"Kit verified; spot-check passed; HCE reference filed",
"HCE log filed; minor inventory variance (expiry) corrected same-day",
}
-- Expanded White-Lane corridors (kind-aware)
local wlCorridors = {
-- New Zimian (NZ)
{code="WL-NZ-1", name="New Zimian Humanitarian Belt", kind="NZ", window="0700–1400 (expanded)"},
{code="WL-NZ-2", name="East Zimia Service Loop", kind="NZ", window="1200–1900 (expanded)"},
-- Gamesman/Bayen (GM)
{code="WL-GM-1", name="Bayen Relief Spur", kind="GM", window="0900–1600 (expanded)"},
{code="WL-GM-2", name="Lake Norton South Chain", kind="GM", window="1300–2000 (expanded)"},
-- Mixed coastal shuttle usable by either
{code="WL-SEA-1", name="Corum Coastal Shuttle", kind="Mixed", window="0600–1800 (expanded)"},
}
-------------------------------------------------------------------------------
-- (C) HELPERS
-------------------------------------------------------------------------------
local function pick(list, seedRoll)
local idx = (seedRoll and ((seedRoll % #list) + 1)) or math.random(#list)
return list[idx]
end
local function pickWeighted(list)
local total = 0
for _, item in ipairs(list) do total = total + (item.weight or 1) end
local roll, cumul = math.random(total), 0
for _, item in ipairs(list) do
cumul = cumul + (item.weight or 1)
if roll <= cumul then return item end
end
return list[#list]
end
local function outputsFor(activityName, districtName)
if activityName == "relief" then
local patients = math.random(22, 72)
local parcels = math.random(60, 180)
-- Modestly higher effort if in the two focus zones
if districtName == "Nortonian Zones" or districtName == "Braxian Zones" then
patients = math.floor(patients * 1.15)
parcels = math.floor(parcels * 1.15)
end
return string.format("patients seen: %d; relief parcels: %d", patients, parcels)
elseif activityName == "education" then
local classes = math.random(1, 3)
local students = math.random(28, 110)
return string.format("classes: %d; learners served: %d", classes, students)
elseif activityName == "nutrition" then
local rations = math.random(120, 320)
local screened = math.random(35, 120)
local supplements = math.random(80, 220)
if districtName == "Nortonian Zones" or districtName == "Braxian Zones" then
rations = math.floor(rations * 1.20)
screened = math.floor(screened * 1.20)
supplements = math.floor(supplements* 1.20)
end
return string.format("fortified rations: %d; health screenings: %d; supplements issued: %d",
rations, screened, supplements)
else -- reconciliation
local circles = math.random(1, 4)
local agreements = math.random(0, math.min(circles, 3))
return string.format("mediation circles: %d; agreements reached: %d", circles, agreements)
end
end
local function observerLine()
local obs = pickWeighted(observerPool)
if obs.label == "No observer (weather delay)" then
return "Observer: n/a (weather delay); affidavits to follow"
else
return "Observer present: " .. obs.label
end
end
local function hceRef(dateInfo, unitCode)
return string.format("HCE-%03d.%03d-%s", dateInfo.year, dateInfo.dayOfYear, unitCode)
end
local function guardrailsStatus()
local roll = math.random()
if roll < 0.08 then
return "Geofence: route adjusted (+1.6 km buffer); Separation: ✓; No-contact log: signed"
elseif roll < 0.95 then
return "Geofence: ✓; Separation: ✓; No-contact log: signed"
else
return "Mission deferred (no-go buffer triggered); rescheduled next window"
end
end
-- Row color based on compliance outcomes
local function rowColor(hceLine, guards, obsLine)
if guards:match("deferred") or hceLine:match("pre%-mission") then
return ' style="background-color:#f8d7da;"' -- red
end
if hceLine:match("variance") or guards:match("adjusted") or obsLine:match("weather delay") then
return ' style="background-color:#fff3cd;"' -- yellow
end
return ' style="background-color:#d4edda;"' -- green
end
-- Pick a White-Lane corridor suitable for a given district (by kind)
local function pickWLCorridorForDistrict(district)
local kind = district.kind
local pool = {}
for _, wl in ipairs(wlCorridors) do
if wl.kind == kind or wl.kind == "Mixed" then
table.insert(pool, wl)
end
end
if #pool == 0 then pool = wlCorridors end
return pool[math.random(#pool)]
end
-- Generate a simple geofence profile ID by kind/day
local function geofenceProfileId(district, dateInfo)
local k = district.kind or "NZ"
return string.format("GF-%s-%03d", k, dateInfo.dayOfYear)
end
-- Build a compact transparency trigger line per row
local function buildTriggers(u, d, act, obsLine, hceLine, guards, wl, di, surge)
local t = {}
-- observer presence / delay
if obsLine:match("Observer present") then
local who = obsLine:gsub("Observer present: ","")
table.insert(t, "observer: " .. who)
elseif obsLine:match("weather delay") then
table.insert(t, "observer: weather delay")
else
table.insert(t, "observer: n/a")
end
-- kit status
if hceLine:match("minor inventory variance") then
table.insert(t, "kit: variance")
elseif hceLine:match("pre%-mission") then
table.insert(t, "kit: pre-mission only")
else
table.insert(t, "kit: verified")
end
-- guardrails
if guards:match("adjusted") then table.insert(t, "geofence: adjusted")
elseif guards:match("deferred") then table.insert(t, "geofence: deferred")
else table.insert(t, "geofence: OK") end
-- lane/district/activity
table.insert(t, "WL "..wl.code)
table.insert(t, "district "..(d.kind or "NZ"))
table.insert(t, "activity "..act.name)
-- surge flags
if surge then
table.insert(t, "health surge: active")
if d.name == "Nortonian Zones" or d.name == "Braxian Zones" then
table.insert(t, "focus: "..d.name)
end
end
return table.concat(t, "; ")
end
-------------------------------------------------------------------------------
-- (D) MAIN TABLE
-------------------------------------------------------------------------------
function p.makeTable(frame)
local dateInfo = getPSSCDate()
math.randomseed(dateInfo.year * 1000 + dateInfo.dayOfYear)
local rows = tonumber(frame and frame.args and frame.args.rows) or 12
local showLegend = (frame and frame.args and tostring(frame.args.showlegend) or "yes"):lower() ~= "no"
local healthSurge = surgeActive(dateInfo)
local out = {}
table.insert(out, "{| class='wikitable sortable'")
table.insert(out, "! Date !! District !! Order / Unit (Headcount) !! Activity !! Outputs !! Observer Verification !! HCE / Kit Compliance !! Guardrails / Notes")
for i = 1, rows do
-- Prefer weighted districts (Braxian/Nortonian focus)
local d = pickWeighted(districts)
-- Bias activities in focus zones toward relief/nutrition
local act
if (d.name == "Nortonian Zones" or d.name == "Braxian Zones") and math.random() < 0.65 then
act = ({ {name="relief"}, {name="nutrition"} })[math.random(2)]
else
act = pick(activities)
end
local u = pick(units)
-- Display +50% headcount during surge
local displayedHeadcount = u.headcount
if healthSurge then
displayedHeadcount = math.floor(u.headcount * 1.5 + 0.5)
end
local outputs = outputsFor(act.name, d.name)
local obsLine = observerLine()
local hceLine = string.format("%s; %s", hceRef(dateInfo, u.code), pick(kitNotes))
local guards = guardrailsStatus()
-- WL annotation
local wl = pickWLCorridorForDistrict(d)
local gfId = geofenceProfileId(d, dateInfo)
local wlNote = string.format("WL %s (%s) | window %s | Geofence Profile: %s",
wl.code, wl.name, wl.window, gfId)
if guards:match("deferred") then
outputs = "n/a (deferred)"
obsLine = "Observer: n/a (deferred)"
hceLine = hceRef(dateInfo, u.code) .. "; pre-mission check only"
end
local unitStr = string.format("%s — %s (%d) [%s]", u.order, u.unit, displayedHeadcount, u.code)
-- Color
local style = rowColor(hceLine, guards, obsLine)
table.insert(out, "|-" .. style)
-- Case & triggers
local caseId = string.format("MD-%03d.%03d-%02d", dateInfo.year, dateInfo.dayOfYear, i)
local triggers = buildTriggers(u, d, act, obsLine, hceLine, guards, wl, dateInfo, healthSurge)
-- Notes (append surge note when active)
local surgeNote = healthSurge and "Public health surge: respiratory risk mitigation & nutrition support" or nil
local parts = { guards, wlNote, "Case: "..caseId, "Triggers: "..triggers }
if surgeNote then table.insert(parts, surgeNote) end
local guardrailsNotes = table.concat(parts, " | ")
table.insert(out, string.format("| %s || %s || %s || %s || %s || %s || %s || %s",
dateInfo.text, d.name, unitStr, act.name, outputs, obsLine, hceLine, guardrailsNotes))
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> = Compliant ")
table.insert(out, "<span style='background:#fff3cd;padding:2px 6px;border:1px solid #ffeeba;'>Yellow</span> = Variance/Adjustment/Weather ")
table.insert(out, "<span style='background:#f8d7da;padding:2px 6px;border:1px solid #f5c6cb;'>Red</span> = Deferred/Pre-mission only")
table.insert(out, "</div>")
end
return table.concat(out, "\n")
end
return p