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 71: | Line 71: | ||
{label="Oportian observer", weight=4}, | {label="Oportian observer", weight=4}, | ||
{label="Nouvelle Alexandrie observer", weight=4}, | {label="Nouvelle Alexandrie observer", weight=4}, | ||
{label="Imperial Federation observer", weight=4}, | |||
{label="Straits Convention monitor (civilian)", weight=2}, | {label="Straits Convention monitor (civilian)", weight=2}, | ||
{label="No observer (weather delay)", weight=1}, | {label="No observer (weather delay)", weight=1}, | ||
Revision as of 04:00, 22 September 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 }
end
-------------------------------------------------------------------------------
-- (B) STATIC DATA
-------------------------------------------------------------------------------
-- Units (fixed, as announced 13/1/51 PSSC)
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 (historic New Zimia = NZ, Bayen/Gamesman = GM)
local districts = {
{name="Lyvian Zones", kind="NZ"},
{name="Lucinarian Zones", kind="NZ"},
{name="Brentonian Zones", kind="NZ"},
{name="Ossyani Zones", kind="NZ"},
{name="Sinclarian Zones", kind="GM"},
{name="Nortonian Zones", kind="GM"},
{name="Wallisian Zones", kind="GM"},
{name="Alindan Zones", kind="GM"},
{name="Aijiac Zones", kind="GM"},
{name="Braxian Zones", kind="GM"},
}
-- Activities (mandated only)
local activities = {
{name="relief"}, -- medical/aid distribution
{name="education"}, -- instruction, literacy, catechesis
{name="reconciliation"}, -- mediation, community dialogues
}
-- 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)
if activityName == "relief" then
local patients = math.random(18, 64)
local parcels = math.random(40, 140)
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(24, 90)
return string.format("classes: %d; learners served: %d", classes, students)
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
-- Determine 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)
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)
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 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
local u = pick(units)
local d = pick(districts)
local act = pick(activities)
local outputs = outputsFor(act.name)
local obsLine = observerLine()
local hceLine = string.format("%s; %s", hceRef(dateInfo, u.code), pick(kitNotes))
local guards = guardrailsStatus()
-- Expanded White-Lane 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, u.headcount, u.code)
-- Apply color-coded row style
local style = rowColor(hceLine, guards, obsLine)
table.insert(out, "|-" .. style)
-- Build transparency line and case ID
local caseId = string.format("MD-%03d.%03d-%02d", dateInfo.year, dateInfo.dayOfYear, i)
local triggers = buildTriggers(u, d, act, obsLine, hceLine, guards, wl)
-- Merge White-Lane note + transparency into the final column
local guardrailsNotes = guards
if guardrailsNotes ~= "" then
guardrailsNotes = table.concat({
guardrailsNotes,
wlNote,
"Case: "..caseId,
"Triggers: "..triggers
}, " | ")
else
guardrailsNotes = table.concat({
wlNote,
"Case: "..caseId,
"Triggers: "..triggers
}, " | ")
end
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