Module:SouthernStraitCorridorOps: Difference between revisions
From MicrasWiki
Jump to navigationJump to search
NewZimiaGov (talk | contribs) No edit summary |
NewZimiaGov (talk | contribs) No edit summary |
||
| Line 2: | Line 2: | ||
-- Module:SouthernStraitCorridorOps | -- Module:SouthernStraitCorridorOps | ||
-- Southern Strait-only daily operations wrapper. | -- Southern Strait-only daily operations wrapper. | ||
-- Shows only Haifan Bassaridian | -- Shows only Southern Strait / Haifan Bassaridian corridor entries from: | ||
-- 1) WarLeagueDivisionOpsBVR | -- 1) WarLeagueDivisionOpsBVR | ||
-- 2) HatchMinistryDailyOps | -- 2) HatchMinistryDailyOps | ||
-- 3) MissionaryDailyOpsBVR | -- 3) MissionaryDailyOpsBVR | ||
-- | -- | ||
-- | -- Adds row-level columns: | ||
-- 4) | -- 4) Locality focus | ||
-- 5) | -- 5) Trade agreement | ||
-- 6) Rebellion alert | |||
-- 6) | |||
-- | -- | ||
-- Economic design note: | -- Economic design note: | ||
| Line 217: | Line 216: | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
local function | local function rowLocalityData(args, channel, rowNumber) | ||
if not southernTradeCities or #southernTradeCities == 0 then | if not southernTradeCities or #southernTradeCities == 0 then | ||
return | return nil | ||
end | end | ||
| Line 228: | Line 227: | ||
local offset = hashString(seed) % #southernTradeCities | local offset = hashString(seed) % #southernTradeCities | ||
local idx = ((tonumber(rowNumber) or 1) - 1 + offset) % #southernTradeCities + 1 | local idx = ((tonumber(rowNumber) or 1) - 1 + offset) % #southernTradeCities + 1 | ||
return | return southernTradeCities[idx] | ||
end | end | ||
local function | local function rowLocalityText(loc) | ||
if not loc then | |||
return "Southern Strait corridor" | |||
if not | |||
return | |||
end | end | ||
return loc.city .. "<br><small>" .. loc.region .. "</small>" | |||
end | end | ||
| Line 335: | Line 296: | ||
local tradeOutcomes = { | local tradeOutcomes = { | ||
" | "Cleared for General Port ledger review.", | ||
"Provisional | "Provisional admission recommended.", | ||
"Conditional trade window approved | "Conditional trade window approved.", | ||
"Company charter accepted for corridor listing | "Company charter accepted for corridor listing.", | ||
"Trade agreement filed | "Trade agreement filed through ordinary registry channels." | ||
} | } | ||
local function tradeAgreementCell(args, channel, rowNumber, loc) | |||
if not loc then | |||
return "—" | |||
end | |||
local chance = tonumber(args.tradechance or args.tradeChance) or 8 | local chance = tonumber(args.tradechance or args.tradeChance) or 8 | ||
if chance < 0 then chance = 0 end | if chance < 0 then chance = 0 end | ||
if chance > 100 then chance = 100 end | if chance > 100 then chance = 100 end | ||
local year = tonumber(args.year or args.psscyear) or 0 | local year = tonumber(args.year or args.psscyear) or 0 | ||
local day = tonumber(args.day or args.dayofyear) or 0 | local day = tonumber(args.day or args.dayofyear) or 0 | ||
local seed = tostring(args.seed or "") .. ":SS-TRADE:" .. tostring(year) .. ":" .. tostring(day) | local seed = tostring(args.seed or "") .. | ||
":SS-TRADE-COL:" .. tostring(channel or "") .. | |||
":" .. tostring(rowNumber or 0) .. | |||
":" .. tostring(loc.code or "GEN") .. | |||
":" .. tostring(year) .. | |||
":" .. tostring(day) | |||
local roll = pctFromSeed(seed, "roll") + 1 | local roll = pctFromSeed(seed, "roll") + 1 | ||
if roll > chance then | if roll > chance then | ||
if | if yn(args.showTradeNoEvent or args.showtradenoevent, false) then | ||
return " | return "No sign-off" | ||
end | end | ||
return | return "—" | ||
end | end | ||
local cult = pick(tradeSigningCults, seed, "cult") | local cult = pick(tradeSigningCults, seed, "cult") | ||
local outcome = pick(tradeOutcomes, seed, "outcome") | local outcome = pick(tradeOutcomes, seed, "outcome") | ||
if | if not cult then | ||
return "—" | |||
end | |||
local seq = (pctFromSeed(seed, "seq") % 20) + 1 | local seq = (pctFromSeed(seed, "seq") % 20) + 1 | ||
| Line 374: | Line 339: | ||
if year > 0 and day > 0 then | if year > 0 and day > 0 then | ||
tradeId = string.format("GP-SS-%03d.%03d-%s-%02d", year, day, | tradeId = string.format("GP-SS-%03d.%03d-%s-%02d", year, day, loc.code or "GEN", seq) | ||
else | else | ||
tradeId = string.format("GP-SS-%s-%02d", | tradeId = string.format("GP-SS-%s-%02d", loc.code or "GEN", seq) | ||
end | end | ||
local signCode = string.format("%s-TA%02d", cult.code or "CUL", seq) | local signCode = string.format("%s-TA%02d", cult.code or "CUL", seq) | ||
return | return "'''Trade sign-off'''<br>" .. | ||
"<small>" .. tradeId .. "</small><br>" .. | |||
flagLabel(cult.flag, cult.label) .. "<br>" .. | |||
"<small>Code: '''" .. signCode .. "'''<br>" .. outcome .. "</small>" | |||
end | end | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- | -- Rebellion / corridor unrest row column | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| Line 413: | Line 359: | ||
{ | { | ||
label = "Confederacy-adjacent courier screen", | label = "Confederacy-adjacent courier screen", | ||
description = " | description = "False-route couriers testing inspection gaps." | ||
}, | }, | ||
{ | { | ||
label = "Former Maritime Markets broker faction", | label = "Former Maritime Markets broker faction", | ||
description = "Old broker-house retainers resisting General Port | description = "Old broker-house retainers resisting General Port paperwork." | ||
}, | }, | ||
{ | { | ||
label = "Anti-registry dock syndicate", | label = "Anti-registry dock syndicate", | ||
description = " | description = "Warehouse intermediaries opposing voucher and ledger discipline." | ||
}, | }, | ||
{ | { | ||
label = "False-proclamation circle", | label = "False-proclamation circle", | ||
description = "Pamphlet | description = "Pamphlet readers circulating forged civic orders." | ||
}, | }, | ||
{ | { | ||
label = "Route-barricade committee", | label = "Route-barricade committee", | ||
description = " | description = "Agitators obstructing lawful inspection lanes." | ||
}, | }, | ||
{ | { | ||
label = "Dispossessed-sympathetic quay cell", | label = "Dispossessed-sympathetic quay cell", | ||
description = " | description = "Waterfront cell copying Confederacy-style courier habits." | ||
} | } | ||
} | } | ||
local rebellionTriggers = { | local rebellionTriggers = { | ||
"Forged levy notice | "Forged levy notice.", | ||
"Masked speakers | "Masked speakers redirected traffic.", | ||
" | "Missing seals and altered registry slips.", | ||
" | "Crowd pressure over bonded cargo.", | ||
" | "Tampered signal schedule.", | ||
" | "Conflicting route orders.", | ||
" | "Coordinated paper strike.", | ||
" | "Anti-Bassaridian chant movement.", | ||
" | "Repeated courier handoffs.", | ||
" | "Matched false manifests." | ||
} | } | ||
local rebellionActions = { | local rebellionActions = { | ||
" | "CKD cordon and evidence hold.", | ||
"Temple | "Temple rumor-control desk opened.", | ||
" | "Ledgers and copied seals sealed.", | ||
"Privateer | "Privateer stand-off posture.", | ||
" | "Pass papers reissued.", | ||
" | "Lawful cargo separated.", | ||
"Missionary | "Missionary mediation opened.", | ||
" | "Corrected signal notice issued.", | ||
"General Port | "Temporary General Port audit lane opened.", | ||
" | "Incident classified as contained unrest." | ||
} | } | ||
| Line 484: | Line 417: | ||
} | } | ||
local function | local function rebellionAlertCell(args, channel, rowNumber, loc) | ||
if not loc then | |||
return "—" | |||
end | |||
local chance = tonumber(args.rebellionchance or args.rebellionChance or args.unrestchance or args.unrestChance) or 6 | local chance = tonumber(args.rebellionchance or args.rebellionChance or args.unrestchance or args.unrestChance) or 6 | ||
if chance < 0 then chance = 0 end | if chance < 0 then chance = 0 end | ||
if chance > 100 then chance = 100 end | if chance > 100 then chance = 100 end | ||
local year = tonumber(args.year or args.psscyear) or 0 | local year = tonumber(args.year or args.psscyear) or 0 | ||
local day = tonumber(args.day or args.dayofyear) or 0 | local day = tonumber(args.day or args.dayofyear) or 0 | ||
local seed = tostring(args.seed or "") .. ":SS-REBELLION:" .. tostring(year) .. ":" .. tostring(day) | local seed = tostring(args.seed or "") .. | ||
":SS-REBELLION-COL:" .. tostring(channel or "") .. | |||
":" .. tostring(rowNumber or 0) .. | |||
":" .. tostring(loc.code or "GEN") .. | |||
":" .. tostring(year) .. | |||
":" .. tostring(day) | |||
local roll = pctFromSeed(seed, "roll") + 1 | local roll = pctFromSeed(seed, "roll") + 1 | ||
if roll > chance then | if roll > chance then | ||
if | if yn(args.showRebellionNoEvent or args.showrebellionnoevent or args.showUnrestNoEvent or args.showunrestnoevent, false) then | ||
return " | return "No unrest" | ||
end | end | ||
return | return "—" | ||
end | end | ||
local profile = pick(rebellionProfiles, seed, "profile") | local profile = pick(rebellionProfiles, seed, "profile") | ||
local trigger = pick(rebellionTriggers, seed, "trigger") | local trigger = pick(rebellionTriggers, seed, "trigger") | ||
local action = pick(rebellionActions, seed, "action") | local action = pick(rebellionActions, seed, "action") | ||
local risk = pick(rebellionRisks, seed, "risk") | local risk = pick(rebellionRisks, seed, "risk") | ||
if | if not profile then | ||
return "—" | |||
end | |||
local seq = (pctFromSeed(seed, "seq") % 20) + 1 | local seq = (pctFromSeed(seed, "seq") % 20) + 1 | ||
| Line 516: | Line 456: | ||
if year > 0 and day > 0 then | if year > 0 and day > 0 then | ||
eventId = string.format("SS-RB-%03d.%03d-%s-%02d", year, day, | eventId = string.format("SS-RB-%03d.%03d-%s-%02d", year, day, loc.code or "GEN", seq) | ||
else | else | ||
eventId = string.format("SS-RB-%s-%02d", | eventId = string.format("SS-RB-%s-%02d", loc.code or "GEN", seq) | ||
end | |||
return "'''Unrest alert'''<br>" .. | |||
"<small>" .. eventId .. "</small><br>" .. | |||
"'''Profile:''' " .. profile.label .. "<br>" .. | |||
"<small>" .. profile.description .. "<br>" .. | |||
"'''Trigger:''' " .. trigger .. "<br>" .. | |||
"'''Risk:''' " .. risk .. "<br>" .. | |||
"'''Response:''' " .. action .. "</small>" | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Row-column insertion helper | |||
-------------------------------------------------------------------------------- | |||
local function addCorridorColumns(body, args, channel) | |||
body = tostring(body or "") | |||
if not body:find("{|", 1, true) then | |||
return body | |||
end | |||
local showLocalityColumn = yn(args.rowlocalities or args.localities or args.showlocalities, true) | |||
local showTradeColumn = yn(args.tradecolumn or args.tradecolumns or args.showtradecolumn, true) | |||
local showRebellionColumn = yn(args.rebellioncolumn or args.rebellioncolumns or args.unrestcolumn or args.showrebellioncolumn, true) | |||
if not showLocalityColumn and not showTradeColumn and not showRebellionColumn then | |||
return body | |||
end | |||
local lines = mw.text.split(body, "\n", true) | |||
local out = {} | |||
local inTable = false | |||
local headerInserted = false | |||
local rowNumber = 0 | |||
for _, line in ipairs(lines) do | |||
if line:match("^%s*{|") then | |||
inTable = true | |||
headerInserted = false | |||
table.insert(out, line) | |||
elseif inTable and not headerInserted and line:match("^%s*!") then | |||
if showLocalityColumn then | |||
table.insert(out, "! Locality focus") | |||
end | |||
if showTradeColumn then | |||
table.insert(out, "! Trade agreement") | |||
end | |||
if showRebellionColumn then | |||
table.insert(out, "! Rebellion alert") | |||
end | |||
headerInserted = true | |||
table.insert(out, line) | |||
elseif inTable and line:match("^%s*|%-") then | |||
rowNumber = rowNumber + 1 | |||
local loc = rowLocalityData(args, channel, rowNumber) | |||
table.insert(out, line) | |||
if showLocalityColumn then | |||
table.insert(out, "| " .. rowLocalityText(loc)) | |||
end | |||
if showTradeColumn then | |||
table.insert(out, "| " .. tradeAgreementCell(args, channel, rowNumber, loc)) | |||
end | |||
if showRebellionColumn then | |||
table.insert(out, "| " .. rebellionAlertCell(args, channel, rowNumber, loc)) | |||
end | |||
elseif inTable and line:match("^%s*|}") then | |||
inTable = false | |||
table.insert(out, line) | |||
else | |||
table.insert(out, line) | |||
end | |||
end | end | ||
return table.concat( | return table.concat(out, "\n") | ||
end | end | ||
| Line 555: | Line 555: | ||
local showLegend = yn(args.legend or args.showlegend, false) | local showLegend = yn(args.legend or args.showlegend, false) | ||
local collapseSections = yn(args.collapsible, true) | local collapseSections = yn(args.collapsible, true) | ||
local startCollapsed = yn(args.collapsed, true) | local startCollapsed = yn(args.collapsed, true) | ||
local includeWar | local includeWar = yn(args.war or args.warleague, true) | ||
local includeHatch | local includeHatch = yn(args.hatch or args.hatchministry, true) | ||
local includeMission | local includeMission = yn(args.missionary or args.templebank, true) | ||
local year = args.year or args.psscyear | local year = args.year or args.psscyear | ||
| Line 588: | Line 580: | ||
)) | )) | ||
table.insert(out, "") | table.insert(out, "") | ||
table.insert(out, "This display combines Council of Kings deployments, Hatch Ministry privateer activity, Temple Bank missionary operations, | table.insert(out, "This display combines Council of Kings deployments, Hatch Ministry privateer activity, Temple Bank missionary operations, row-level Southern Strait locality focus columns, row-level trade-agreement sign-offs, and row-level rebellion-type unrest alerts while excluding unrelated regional rows.") | ||
end | end | ||
| Line 599: | Line 591: | ||
year = year, | year = year, | ||
day = day, | day = day, | ||
rows = args.warrows or args.rows or "24", | rows = args.warrows or args.rows or "24", | ||
scanrows = args.warscanrows or args.scanrows or "160", | scanrows = args.warscanrows or args.scanrows or "160", | ||
showlegend = showLegend and "yes" or "no", | showlegend = showLegend and "yes" or "no", | ||
flagsize = args.flagsize or "20", | flagsize = args.flagsize or "20", | ||
| Line 620: | Line 607: | ||
local ok, body = safeCall("Module:WarLeagueDivisionOpsBVR", "makeTable", warArgs) | local ok, body = safeCall("Module:WarLeagueDivisionOpsBVR", "makeTable", warArgs) | ||
body = addCorridorColumns(body, args, "war") | |||
table.insert(out, hr()) | table.insert(out, hr()) | ||
| Line 646: | Line 630: | ||
local ok, body = safeCall("Module:HatchMinistryDailyOps", "dailyReport", hatchArgs) | local ok, body = safeCall("Module:HatchMinistryDailyOps", "dailyReport", hatchArgs) | ||
body = addCorridorColumns(body, args, "hatch") | |||
table.insert(out, hr()) | table.insert(out, hr()) | ||
| Line 676: | Line 657: | ||
local ok, body = safeCall("Module:MissionaryDailyOpsBVR", "makeTable", missionaryArgs) | local ok, body = safeCall("Module:MissionaryDailyOpsBVR", "makeTable", missionaryArgs) | ||
body = addCorridorColumns(body, args, "missionary") | |||
table.insert(out, hr()) | table.insert(out, hr()) | ||
table.insert(out, section("Temple Bank Southern Strait missionary operations", body, collapseSections, startCollapsed)) | table.insert(out, section("Temple Bank Southern Strait missionary operations", body, collapseSections, startCollapsed)) | ||
end | end | ||
| Line 719: | Line 673: | ||
"* '''Temple Bank Southern Strait missionary operations''' show Haifan Bassaridian missionary rows only.", | "* '''Temple Bank Southern Strait missionary operations''' show Haifan Bassaridian missionary rows only.", | ||
"* '''Locality focus''' columns assign one extended Southern Strait, Eastern Mykonos Desert, or eastern Snowholme foothill settlement to each generated row when row-level localities are enabled.", | "* '''Locality focus''' columns assign one extended Southern Strait, Eastern Mykonos Desert, or eastern Snowholme foothill settlement to each generated row when row-level localities are enabled.", | ||
"* ''' | "* '''Trade agreement''' columns show rare row-level cult sign-offs allowing locality companies to enter the appropriate [[General Port of Lake Morovia]] ledger review process.", | ||
"* ''' | "* '''Rebellion alert''' columns show rare row-level contained unrest events such as forged proclamations, broker-cell activity, route barricades, false orders, or Dispossessed-adjacent courier behavior.", | ||
"* ''' | "* Use '''tradechance=100''' and '''rebellionchance=100''' for testing; return to low values such as '''tradechance=8''' and '''rebellionchance=6''' for ordinary use." | ||
}, "\n") | }, "\n") | ||
| Line 745: | Line 699: | ||
args.hatch = "no" | args.hatch = "no" | ||
args.missionary = "no" | args.missionary = "no" | ||
args.war = "yes" | args.war = "yes" | ||
args.title = args.title or "War League Southern Strait Daily Operations" | args.title = args.title or "War League Southern Strait Daily Operations" | ||
| Line 756: | Line 708: | ||
args.war = "no" | args.war = "no" | ||
args.missionary = "no" | args.missionary = "no" | ||
args.hatch = "yes" | args.hatch = "yes" | ||
args.title = args.title or "Hatch Ministry Southern Strait Daily Operations" | args.title = args.title or "Hatch Ministry Southern Strait Daily Operations" | ||
| Line 767: | Line 717: | ||
args.war = "no" | args.war = "no" | ||
args.hatch = "no" | args.hatch = "no" | ||
args.missionary = "yes" | args.missionary = "yes" | ||
args.title = args.title or "Temple Bank Southern Strait Missionary Daily Operations" | args.title = args.title or "Temple Bank Southern Strait Missionary Daily Operations" | ||
Revision as of 20:32, 4 June 2026
Documentation for this module may be created at Module:SouthernStraitCorridorOps/doc
-- Module:SouthernStraitCorridorOps
-- Southern Strait-only daily operations wrapper.
-- Shows only Southern Strait / Haifan Bassaridian corridor entries from:
-- 1) WarLeagueDivisionOpsBVR
-- 2) HatchMinistryDailyOps
-- 3) MissionaryDailyOpsBVR
--
-- Adds row-level columns:
-- 4) Locality focus
-- 5) Trade agreement
-- 6) Rebellion alert
--
-- Economic design note:
-- Southern Strait and Eastern Mykonos localities are written as grounded
-- successor/satellite economies influenced by the historic port economies of
-- Blore Heath, Jogi, and Mylecia, without copying those ports directly.
local p = {}
--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
local function yn(value, default)
local v = tostring(value or ""):lower()
if v == "" then return default end
if v == "no" or v == "n" or v == "0" or v == "false" then return false end
if v == "yes" or v == "y" or v == "1" or v == "true" then return true end
return default
end
local function makeFrame(args)
return { args = args or {} }
end
local function safeRequire(moduleName)
local ok, mod = pcall(require, moduleName)
if ok and mod then
return mod, nil
end
return nil, "Could not load " .. tostring(moduleName)
end
local function safeCall(moduleName, funcName, args)
local mod, err = safeRequire(moduleName)
if not mod then
return false, "''" .. err .. ".''"
end
if type(mod[funcName]) ~= "function" then
return false, "''" .. tostring(moduleName) .. " has no public function named " .. tostring(funcName) .. ".''"
end
local ok, result = pcall(function()
return mod[funcName](makeFrame(args))
end)
if not ok then
return false, "''Error while invoking " .. tostring(moduleName) .. "." .. tostring(funcName) .. ": " .. tostring(result) .. "''"
end
return true, tostring(result or "")
end
local function doyToMonthDom(doy)
doy = tonumber(doy) or 1
if doy < 1 then doy = 1 end
if doy > 183 then doy = 183 end
if doy <= 61 then
return 1, doy
elseif doy <= 122 then
return 2, doy - 61
else
return 3, doy - 122
end
end
local function dateLabel(args)
local y = tonumber(args.year or args.psscyear)
local d = tonumber(args.day or args.dayofyear)
if y and d then
local m, dom = doyToMonthDom(d)
return string.format("%d/%d/%d PSSC", dom, m, y)
end
return "current PSSC date"
end
local function section(title, body, collapsible, collapsed)
local out = {}
local safeTitle = tostring(title or "Operations")
local content = tostring(body or "''No output.''")
if not collapsible then
table.insert(out, "===" .. safeTitle .. "===")
table.insert(out, content)
return table.concat(out, "\n")
end
local collapseClass = collapsed and "mw-collapsible mw-collapsed" or "mw-collapsible"
table.insert(out, '<div class="' .. collapseClass .. '" style="border:1px solid #a2a9b1; margin:0.6em 0; background:#f8f9fa;">')
table.insert(out, '<div style="font-weight:bold; padding:0.35em 0.6em; background:#eaecf0;">' .. safeTitle .. '</div>')
table.insert(out, '<div style="padding:0.5em 0.6em;">')
table.insert(out, content)
table.insert(out, '</div>')
table.insert(out, '</div>')
return table.concat(out, "\n")
end
local function hr()
return "\n----\n"
end
local function hashString(s)
s = tostring(s or "")
local h = 0
for i = 1, #s do
h = (h * 31 + string.byte(s, i)) % 1000003
end
return h
end
local function pick(list, seed, salt)
if not list or #list == 0 then return nil end
local idx = (hashString(tostring(seed or "") .. ":" .. tostring(salt or "")) % #list) + 1
return list[idx]
end
local function pctFromSeed(seed, salt)
return hashString(tostring(seed or "") .. ":" .. tostring(salt or "")) % 100
end
local function flagLabel(file, label)
if file and tostring(file) ~= "" then
return "{{flagicon image|" .. file .. "|200px}} " .. tostring(label or "")
end
return tostring(label or "")
end
--------------------------------------------------------------------------------
-- Extended Southern Strait / Eastern Mykonos / Snowholme locality list
--------------------------------------------------------------------------------
local southernTradeCities = {
{
code = "MIR",
city = "[[Mirovia]]",
region = "[[Eastern Mykonos]] Desert",
category = "Agriculture",
company = "[[Mirovian Cistern Growers' Syndicate]]",
resource = "cistern-grown melons, dryland cucumbers, preserved desert fruit, seed oil, packed caravan rations, and water-accounted produce consignments",
lineage = "Desert provisioning economy influenced by [[Jogi]]'s food-market role and [[Mylecia]]'s caravan-facing trade networks; adapted to cistern agriculture, water claims, and dry-route supply rather than beer, grain, or ritual crops"
},
{
code = "SAR",
city = "[[Saruq Thyrsa]]",
region = "Southern Strait coast",
category = "Agriculture",
company = "[[Saruq Thyrsa Reedcutters' Guild]]",
resource = "coastal reed, cane fiber, packing straw, ferry mats, cargo-padding material, rope backing, warehouse thatch, and crate-lining bundles",
lineage = "Harbor-materials economy influenced by [[Blore Heath]]'s shipping brokerage and [[Jogi]]'s quay-market supply system; focused on practical cargo handling, ferry shelters, and packing materials rather than piracy or cult goods"
},
{
code = "AYS",
city = "[[Ayllu Nysara]]",
region = "Eastern foothills of the [[Snowholme Range]]",
category = "Agriculture",
company = "[[Nysaran Terrace Farmers' Ayllu]]",
resource = "terrace beans, cold-weather tubers, sheep wool, highland fodder, pack-animal feed, preserved root stores, and mule-train provisions",
lineage = "Foothill supply economy influenced only indirectly by the Maritime Markets; tied more closely to Snowholme terrace farming, upland food storage, wool traffic, and pass-road provisioning"
},
{
code = "THQ",
city = "[[Thal Qencha]]",
region = "Eastern foothills of the [[Snowholme Range]]",
category = "Agriculture",
company = "[[Thal Qencha Springkeepers' Cooperative]]",
resource = "spring-fed watercress, trout ponds, smoked fish, cold-storage greens, inspected mountain food shipments, packed ice-box produce, and road-market fish consignments",
lineage = "Foothill water-and-food economy tied to Snowholme springs and pass roads; comparable to older Maritime Markets food distribution only in its role as a regulated supplier to travelers, markets, and route offices"
},
{
code = "RUK",
city = "[[Rumi-Kefaya]]",
region = "[[Eastern Mykonos]] Desert",
category = "Mining / Manufacturing / Energy",
company = "[[Rumi-Kefaya Stone and Lime Works]]",
resource = "basalt, limestone, paving stone, railway ballast, drainage tile, lime mortar, culvert blocks, harbor repair stone, quay coping, and storm-route masonry",
lineage = "Infrastructure economy influenced by [[Mylecia]]'s coal-and-works tradition and Southern Strait coastal preservation activity; focused on roads, quays, culverts, retaining walls, and storm-damage repair rather than extractive wealth"
},
{
code = "HAY",
city = "[[Hayr Chaskal]]",
region = "Southern Strait coast",
category = "Mining / Manufacturing / Energy",
company = "[[Hayr Chaskal Glass and Signal Works]]",
resource = "beacon glass, telegraph insulators, lighthouse lenses, lamp glass, storm panes, signal mirrors, buoy lenses, and weatherproof signal housings",
lineage = "Signal-and-navigation economy influenced by [[Blore Heath]]'s shipping control and [[Mylecia]]'s coastal works; supports ferry warnings, lighthouse maintenance, telegraph lines, and Hatch Ministry watch posts without copying older pirate or brokerage firms"
},
{
code = "QSE",
city = "[[Qhapaq Selen]]",
region = "Eastern foothills of the [[Snowholme Range]]",
category = "Services",
company = "[[Qhapaq Selen Route Registry Office]]",
resource = "night-road registry, caravan lodging, pass permits, route insurance, guide licensing, cargo-route papers, missing-traveler filings, and bonded transit records",
lineage = "Foothill route-service economy influenced by [[Mylecia]]'s caravan-facing commerce and [[Jogi]]'s intelligence-and-paperwork traditions; adapted to Snowholme pass roads, guide houses, lodging records, and insurance claims"
}
}
--------------------------------------------------------------------------------
-- Row-level locality focus helper
--------------------------------------------------------------------------------
local function rowLocalityData(args, channel, rowNumber)
if not southernTradeCities or #southernTradeCities == 0 then
return nil
end
local year = tonumber(args.year or args.psscyear) or 0
local day = tonumber(args.day or args.dayofyear) or 0
local seed = tostring(args.seed or "") .. ":SS-ROW-LOCALITY:" .. tostring(channel or "") .. ":" .. tostring(year) .. ":" .. tostring(day)
local offset = hashString(seed) % #southernTradeCities
local idx = ((tonumber(rowNumber) or 1) - 1 + offset) % #southernTradeCities + 1
return southernTradeCities[idx]
end
local function rowLocalityText(loc)
if not loc then
return "Southern Strait corridor"
end
return loc.city .. "<br><small>" .. loc.region .. "</small>"
end
--------------------------------------------------------------------------------
-- Cult sign-off list for trade agreements
--------------------------------------------------------------------------------
local tradeSigningCults = {
{
code = "UMB",
flag = "UmbOracle.png",
label = "[[Noctis (Reformed Stripping Path)#Order of the Umbral Oracle|Order of the Umbral Oracle]]"
},
{
code = "GGS",
flag = "GoldenShadows.png",
label = "[[Chrysos (Reformed Stripping Path)#Guild of Golden Shadows|Guild of Golden Shadows]]"
},
{
code = "AZS",
flag = "AzureSentinel.png",
label = "[[Thalassa (Reformed Stripping Path)#Azure Sentinel Sect|Azure Sentinel Sect]]"
},
{
code = "HAR",
flag = "Harmonysanctum.png",
label = "[[Micras (Reformed Stripping Path)#Harmony Sanctum|Harmony Sanctum]]"
},
{
code = "VIT",
flag = "Vitalis.png",
label = "[[Plateau (Reformed Stripping Path)#Sanctum Vitalis|Sanctum Vitalis]]"
},
{
code = "AUR",
flag = "Auroramystica.png",
label = "[[Eos (Reformed Stripping Path)#Order of Aurora Mystica|Order of Aurora Mystica]]"
},
{
code = "DEL",
flag = "SanctumDel.png",
label = "[[Sisera (Reformed Stripping Path)#Sanctum Delphica|Sanctum Delphica]]"
},
{
code = "CAT",
flag = "Catonis.png",
label = "[[Cato (Reformed Stripping Path)#Rex Catonis|Rex Catonis]]"
},
{
code = "CON",
flag = "Concordia.png",
label = "[[Lukedu (Reformed Stripping Path)#Accord Concordia|Accord Concordia]]"
},
{
code = "STY",
flag = "StygianVe.png",
label = "[[Styx (Reformed Stripping Path)#Mystery of the Stygian Veil|Mystery of the Stygian Veil]]"
}
}
local tradeOutcomes = {
"Cleared for General Port ledger review.",
"Provisional admission recommended.",
"Conditional trade window approved.",
"Company charter accepted for corridor listing.",
"Trade agreement filed through ordinary registry channels."
}
local function tradeAgreementCell(args, channel, rowNumber, loc)
if not loc then
return "—"
end
local chance = tonumber(args.tradechance or args.tradeChance) or 8
if chance < 0 then chance = 0 end
if chance > 100 then chance = 100 end
local year = tonumber(args.year or args.psscyear) or 0
local day = tonumber(args.day or args.dayofyear) or 0
local seed = tostring(args.seed or "") ..
":SS-TRADE-COL:" .. tostring(channel or "") ..
":" .. tostring(rowNumber or 0) ..
":" .. tostring(loc.code or "GEN") ..
":" .. tostring(year) ..
":" .. tostring(day)
local roll = pctFromSeed(seed, "roll") + 1
if roll > chance then
if yn(args.showTradeNoEvent or args.showtradenoevent, false) then
return "No sign-off"
end
return "—"
end
local cult = pick(tradeSigningCults, seed, "cult")
local outcome = pick(tradeOutcomes, seed, "outcome")
if not cult then
return "—"
end
local seq = (pctFromSeed(seed, "seq") % 20) + 1
local tradeId
if year > 0 and day > 0 then
tradeId = string.format("GP-SS-%03d.%03d-%s-%02d", year, day, loc.code or "GEN", seq)
else
tradeId = string.format("GP-SS-%s-%02d", loc.code or "GEN", seq)
end
local signCode = string.format("%s-TA%02d", cult.code or "CUL", seq)
return "'''Trade sign-off'''<br>" ..
"<small>" .. tradeId .. "</small><br>" ..
flagLabel(cult.flag, cult.label) .. "<br>" ..
"<small>Code: '''" .. signCode .. "'''<br>" .. outcome .. "</small>"
end
--------------------------------------------------------------------------------
-- Rebellion / corridor unrest row column
--------------------------------------------------------------------------------
local rebellionProfiles = {
{
label = "Confederacy-adjacent courier screen",
description = "False-route couriers testing inspection gaps."
},
{
label = "Former Maritime Markets broker faction",
description = "Old broker-house retainers resisting General Port paperwork."
},
{
label = "Anti-registry dock syndicate",
description = "Warehouse intermediaries opposing voucher and ledger discipline."
},
{
label = "False-proclamation circle",
description = "Pamphlet readers circulating forged civic orders."
},
{
label = "Route-barricade committee",
description = "Agitators obstructing lawful inspection lanes."
},
{
label = "Dispossessed-sympathetic quay cell",
description = "Waterfront cell copying Confederacy-style courier habits."
}
}
local rebellionTriggers = {
"Forged levy notice.",
"Masked speakers redirected traffic.",
"Missing seals and altered registry slips.",
"Crowd pressure over bonded cargo.",
"Tampered signal schedule.",
"Conflicting route orders.",
"Coordinated paper strike.",
"Anti-Bassaridian chant movement.",
"Repeated courier handoffs.",
"Matched false manifests."
}
local rebellionActions = {
"CKD cordon and evidence hold.",
"Temple rumor-control desk opened.",
"Ledgers and copied seals sealed.",
"Privateer stand-off posture.",
"Pass papers reissued.",
"Lawful cargo separated.",
"Missionary mediation opened.",
"Corrected signal notice issued.",
"Temporary General Port audit lane opened.",
"Incident classified as contained unrest."
}
local rebellionRisks = {
"Low",
"Moderate",
"High",
"Contained",
"Escalating"
}
local function rebellionAlertCell(args, channel, rowNumber, loc)
if not loc then
return "—"
end
local chance = tonumber(args.rebellionchance or args.rebellionChance or args.unrestchance or args.unrestChance) or 6
if chance < 0 then chance = 0 end
if chance > 100 then chance = 100 end
local year = tonumber(args.year or args.psscyear) or 0
local day = tonumber(args.day or args.dayofyear) or 0
local seed = tostring(args.seed or "") ..
":SS-REBELLION-COL:" .. tostring(channel or "") ..
":" .. tostring(rowNumber or 0) ..
":" .. tostring(loc.code or "GEN") ..
":" .. tostring(year) ..
":" .. tostring(day)
local roll = pctFromSeed(seed, "roll") + 1
if roll > chance then
if yn(args.showRebellionNoEvent or args.showrebellionnoevent or args.showUnrestNoEvent or args.showunrestnoevent, false) then
return "No unrest"
end
return "—"
end
local profile = pick(rebellionProfiles, seed, "profile")
local trigger = pick(rebellionTriggers, seed, "trigger")
local action = pick(rebellionActions, seed, "action")
local risk = pick(rebellionRisks, seed, "risk")
if not profile then
return "—"
end
local seq = (pctFromSeed(seed, "seq") % 20) + 1
local eventId
if year > 0 and day > 0 then
eventId = string.format("SS-RB-%03d.%03d-%s-%02d", year, day, loc.code or "GEN", seq)
else
eventId = string.format("SS-RB-%s-%02d", loc.code or "GEN", seq)
end
return "'''Unrest alert'''<br>" ..
"<small>" .. eventId .. "</small><br>" ..
"'''Profile:''' " .. profile.label .. "<br>" ..
"<small>" .. profile.description .. "<br>" ..
"'''Trigger:''' " .. trigger .. "<br>" ..
"'''Risk:''' " .. risk .. "<br>" ..
"'''Response:''' " .. action .. "</small>"
end
--------------------------------------------------------------------------------
-- Row-column insertion helper
--------------------------------------------------------------------------------
local function addCorridorColumns(body, args, channel)
body = tostring(body or "")
if not body:find("{|", 1, true) then
return body
end
local showLocalityColumn = yn(args.rowlocalities or args.localities or args.showlocalities, true)
local showTradeColumn = yn(args.tradecolumn or args.tradecolumns or args.showtradecolumn, true)
local showRebellionColumn = yn(args.rebellioncolumn or args.rebellioncolumns or args.unrestcolumn or args.showrebellioncolumn, true)
if not showLocalityColumn and not showTradeColumn and not showRebellionColumn then
return body
end
local lines = mw.text.split(body, "\n", true)
local out = {}
local inTable = false
local headerInserted = false
local rowNumber = 0
for _, line in ipairs(lines) do
if line:match("^%s*{|") then
inTable = true
headerInserted = false
table.insert(out, line)
elseif inTable and not headerInserted and line:match("^%s*!") then
if showLocalityColumn then
table.insert(out, "! Locality focus")
end
if showTradeColumn then
table.insert(out, "! Trade agreement")
end
if showRebellionColumn then
table.insert(out, "! Rebellion alert")
end
headerInserted = true
table.insert(out, line)
elseif inTable and line:match("^%s*|%-") then
rowNumber = rowNumber + 1
local loc = rowLocalityData(args, channel, rowNumber)
table.insert(out, line)
if showLocalityColumn then
table.insert(out, "| " .. rowLocalityText(loc))
end
if showTradeColumn then
table.insert(out, "| " .. tradeAgreementCell(args, channel, rowNumber, loc))
end
if showRebellionColumn then
table.insert(out, "| " .. rebellionAlertCell(args, channel, rowNumber, loc))
end
elseif inTable and line:match("^%s*|}") then
inTable = false
table.insert(out, line)
else
table.insert(out, line)
end
end
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- Main Southern Strait-only view
--------------------------------------------------------------------------------
function p.makeTable(frame)
local args = (frame and frame.args) or {}
local showIntro = yn(args.intro, false)
local showTitle = yn(args.showtitle or args.titleline, false)
local showLegend = yn(args.legend or args.showlegend, false)
local collapseSections = yn(args.collapsible, true)
local startCollapsed = yn(args.collapsed, true)
local includeWar = yn(args.war or args.warleague, true)
local includeHatch = yn(args.hatch or args.hatchministry, true)
local includeMission = yn(args.missionary or args.templebank, true)
local year = args.year or args.psscyear
local day = args.day or args.dayofyear
local title = args.title or "Southern Strait Corridor Daily Operations"
local label = dateLabel(args)
local out = {}
if showTitle then
table.insert(out, "==" .. title .. "==")
end
if showIntro then
table.insert(out, string.format(
"''Southern Strait-only operational display for %s, limited to Haifan Bassaridian / former Maritime Markets corridor entries.''",
label
))
table.insert(out, "")
table.insert(out, "This display combines Council of Kings deployments, Hatch Ministry privateer activity, Temple Bank missionary operations, row-level Southern Strait locality focus columns, row-level trade-agreement sign-offs, and row-level rebellion-type unrest alerts while excluding unrelated regional rows.")
end
----------------------------------------------------------------------------
-- War League: Southern Strait-only
----------------------------------------------------------------------------
if includeWar then
local warArgs = {
year = year,
day = day,
rows = args.warrows or args.rows or "24",
scanrows = args.warscanrows or args.scanrows or "160",
showlegend = showLegend and "yes" or "no",
flagsize = args.flagsize or "20",
zone = "SouthernStrait",
-- Southern Strait Corridor wrapper shows only Council of Kings
-- Division rows for War League deployments.
division = "Council of Kings Division",
}
if args.seed and tostring(args.seed) ~= "" then
warArgs.seed = args.seed
end
local ok, body = safeCall("Module:WarLeagueDivisionOpsBVR", "makeTable", warArgs)
body = addCorridorColumns(body, args, "war")
table.insert(out, hr())
table.insert(out, section("Council of Kings Southern Strait deployments", body, collapseSections, startCollapsed))
end
----------------------------------------------------------------------------
-- Hatch Ministry: Southern Strait-only
----------------------------------------------------------------------------
if includeHatch then
local hatchArgs = {
year = year,
day = day,
legend = showLegend and "yes" or "no",
zone = "SouthernStrait",
}
if args.captain and tostring(args.captain) ~= "" then
hatchArgs.captain = args.captain
end
local ok, body = safeCall("Module:HatchMinistryDailyOps", "dailyReport", hatchArgs)
body = addCorridorColumns(body, args, "hatch")
table.insert(out, hr())
table.insert(out, section("Hatch Ministry Southern Strait privateer operations", body, collapseSections, startCollapsed))
end
----------------------------------------------------------------------------
-- Temple Bank / Missionary Operations: Haifan only
----------------------------------------------------------------------------
if includeMission then
local missionaryArgs = {
year = year,
day = day,
region = "Haifan",
showlegend = showLegend and "yes" or "no",
includeThreat = args.includeThreat or args.includethreat or "no",
showEcon = args.showEcon or args.showecon or "no",
}
if args.missionarylimit and tostring(args.missionarylimit) ~= "" then
missionaryArgs.limit = args.missionarylimit
elseif args.limit and tostring(args.limit) ~= "" then
missionaryArgs.limit = args.limit
end
local ok, body = safeCall("Module:MissionaryDailyOpsBVR", "makeTable", missionaryArgs)
body = addCorridorColumns(body, args, "missionary")
table.insert(out, hr())
table.insert(out, section("Temple Bank Southern Strait missionary operations", body, collapseSections, startCollapsed))
end
----------------------------------------------------------------------------
-- Combined legend
----------------------------------------------------------------------------
if showLegend then
local legendBody = table.concat({
"* '''Council of Kings Southern Strait deployments''' show only Council of Kings Division War League rows for the Southern Strait corridor.",
"* '''Hatch Ministry Southern Strait privateer operations''' show Haifan home-port privateers only.",
"* '''Temple Bank Southern Strait missionary operations''' show Haifan Bassaridian missionary rows only.",
"* '''Locality focus''' columns assign one extended Southern Strait, Eastern Mykonos Desert, or eastern Snowholme foothill settlement to each generated row when row-level localities are enabled.",
"* '''Trade agreement''' columns show rare row-level cult sign-offs allowing locality companies to enter the appropriate [[General Port of Lake Morovia]] ledger review process.",
"* '''Rebellion alert''' columns show rare row-level contained unrest events such as forged proclamations, broker-cell activity, route barricades, false orders, or Dispossessed-adjacent courier behavior.",
"* Use '''tradechance=100''' and '''rebellionchance=100''' for testing; return to low values such as '''tradechance=8''' and '''rebellionchance=6''' for ordinary use."
}, "\n")
table.insert(out, hr())
table.insert(out, section("Combined legend", legendBody, collapseSections, startCollapsed))
end
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- Shortcut views
--------------------------------------------------------------------------------
function p.southernStrait(frame)
local args = (frame and frame.args) or {}
args.title = args.title or "Southern Strait Corridor Daily Operations"
return p.makeTable({ args = args })
end
function p.warOnly(frame)
local args = (frame and frame.args) or {}
args.hatch = "no"
args.missionary = "no"
args.war = "yes"
args.title = args.title or "War League Southern Strait Daily Operations"
return p.makeTable({ args = args })
end
function p.hatchOnly(frame)
local args = (frame and frame.args) or {}
args.war = "no"
args.missionary = "no"
args.hatch = "yes"
args.title = args.title or "Hatch Ministry Southern Strait Daily Operations"
return p.makeTable({ args = args })
end
function p.missionaryOnly(frame)
local args = (frame and frame.args) or {}
args.war = "no"
args.hatch = "no"
args.missionary = "yes"
args.title = args.title or "Temple Bank Southern Strait Missionary Daily Operations"
return p.makeTable({ args = args })
end
return p