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 11: | Line 11: | ||
-- 5) Row-level locality focus column for extended Southern Strait, | -- 5) Row-level locality focus column for extended Southern Strait, | ||
-- Eastern Mykonos Desert, and eastern Snowholme foothill settlements | -- Eastern Mykonos Desert, and eastern Snowholme foothill settlements | ||
-- 6) Standalone locality / General Port status table | |||
-- | -- | ||
-- Economic design note: | -- Economic design note: | ||
| Line 154: | Line 155: | ||
company = "[[Mirovian Cistern Growers' Syndicate]]", | company = "[[Mirovian Cistern Growers' Syndicate]]", | ||
resource = "cistern-grown melons, dryland cucumbers, preserved desert fruit, seed oil, packed caravan rations, and water-accounted produce consignments", | resource = "cistern-grown melons, dryland cucumbers, preserved desert fruit, seed oil, packed caravan rations, and water-accounted produce consignments", | ||
terrain = "Oasis-edge desert town built around lined cisterns, wind-pumped wells, reflective irrigation basins, dry-route caravan stations, and water-accounted produce claims.", | |||
role = "Serves as a desert provisioning point for caravan routes, ferry-market suppliers, road-transfer depots, inspection parties, and export-control checks operating between the Southern Strait and the interior desert routes.", | |||
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" | 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" | ||
}, | }, | ||
| Line 163: | Line 166: | ||
company = "[[Saruq Thyrsa Reedcutters' Guild]]", | 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", | resource = "coastal reed, cane fiber, packing straw, ferry mats, cargo-padding material, rope backing, warehouse thatch, and crate-lining bundles", | ||
terrain = "Brackish canal-edge and marsh-inlet settlement located near ferry shelters, reed beds, cargo landings, warehouse yards, and older coastal market routes.", | |||
role = "Supplies practical harbor materials for ferry shelters, baskets, rope backing, cargo padding, crate linings, matting, and cheap construction; also serves as a frequent inspection point because reed, cane, and packing bundles can conceal documents, vouchers, courier packets, or small contraband.", | |||
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" | 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" | ||
}, | }, | ||
| Line 172: | Line 177: | ||
company = "[[Nysaran Terrace Farmers' Ayllu]]", | 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", | resource = "terrace beans, cold-weather tubers, sheep wool, highland fodder, pack-animal feed, preserved root stores, and mule-train provisions", | ||
terrain = "Wechuan-influenced terrace settlement with shared irrigation channels, hillside fields, wool sheds, upland granaries, mule-train yards, and seasonal labor routes.", | |||
role = "Links Snowholme foothill agriculture to Southern Strait supply routes, missionary kitchens, caravan stations, upland checkpoints, rail passengers, pack traffic, and food-storage networks.", | |||
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" | 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" | ||
}, | }, | ||
| Line 181: | Line 188: | ||
company = "[[Thal Qencha Springkeepers' Cooperative]]", | 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", | resource = "spring-fed watercress, trout ponds, smoked fish, cold-storage greens, inspected mountain food shipments, packed ice-box produce, and road-market fish consignments", | ||
terrain = "Spring-fed foothill town centered on cold ponds, watercress beds, storage houses, smoking sheds, spring channels, road markets, and regulated food movement.", | |||
role = "Functions as a water-rights, food-inspection, and cold-chain locality supporting pilgrims, traders, health lodges, caravan kitchens, storm-drainage work, mountain-road markets, and regulated food shipments.", | |||
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" | 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" | ||
}, | }, | ||
| Line 190: | Line 199: | ||
company = "[[Rumi-Kefaya Stone and Lime Works]]", | 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", | resource = "basalt, limestone, paving stone, railway ballast, drainage tile, lime mortar, culvert blocks, harbor repair stone, quay coping, and storm-route masonry", | ||
terrain = "Quarry town on desert stone beds and dry road approaches, with steam-powered cutting yards, lime kilns, drainage works, culvert yards, and small tram or cartage links.", | |||
role = "Provides practical construction material for roadbeds, rail sidings, harbor paving, culverts, retaining walls, inspection posts, shrine buildings, quay repairs, drainage routes, and storm-damaged corridor approaches.", | |||
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" | 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" | ||
}, | }, | ||
| Line 199: | Line 210: | ||
company = "[[Hayr Chaskal Glass and Signal Works]]", | 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", | resource = "beacon glass, telegraph insulators, lighthouse lenses, lamp glass, storm panes, signal mirrors, buoy lenses, and weatherproof signal housings", | ||
terrain = "Signal-coast town with access to coastal sand, quartz, mica-bearing material, lighthouse workshops, telegraph maintenance yards, signal towers, and storm-warning stations.", | |||
role = "Supports the communications and navigation side of the corridor posture, including ferry warnings, lighthouse maintenance, storm-delay routing, telegraph lines, Hatch Ministry watch posts, buoy repair, signal mirrors, and coastal inspection stations.", | |||
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" | 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" | ||
}, | }, | ||
| Line 208: | Line 221: | ||
company = "[[Qhapaq Selen Route Registry Office]]", | 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", | resource = "night-road registry, caravan lodging, pass permits, route insurance, guide licensing, cargo-route papers, missing-traveler filings, and bonded transit records", | ||
terrain = "Foothill route town located along night-road approaches, caravan lodging lanes, pass offices, guide houses, bonded transit desks, and pilgrimage documentation offices.", | |||
role = "Serves as the principal route-administration locality among the extended settlements, handling guide disputes, missing-traveler claims, forged pass papers, lodging records, Temple Bank documentation, caravan insurance, bonded transit files, and corridor movement 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" | 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" | ||
} | } | ||
| Line 235: | Line 250: | ||
body = tostring(body or "") | body = tostring(body or "") | ||
if not body:find("{|", 1, true) then | if not body:find("{|", 1, true) then | ||
return body | return body | ||
| Line 254: | Line 268: | ||
elseif inTable and not headerInserted and line:match("^%s*!") then | elseif inTable and not headerInserted and line:match("^%s*!") then | ||
table.insert(out, "! Locality focus") | table.insert(out, "! Locality focus") | ||
headerInserted = true | headerInserted = true | ||
| Line 342: | Line 355: | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
-- | -- Trade-agreement record helper | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
local function | local function tradeAgreementRecordForDate(args, year, day) | ||
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 | ||
year = tonumber(year) or 0 | |||
day = tonumber(day) or 0 | |||
local seed = tostring(args.seed or "") .. ":SS-TRADE:" .. tostring(year) .. ":" .. tostring(day) | local seed = tostring(args.seed or "") .. ":SS-TRADE:" .. tostring(year) .. ":" .. tostring(day) | ||
local roll = pctFromSeed(seed, "roll") + 1 | |||
if roll > chance then | if roll > chance then | ||
return nil | return nil | ||
end | end | ||
| Line 379: | Line 388: | ||
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 { | |||
year = year, | |||
day = day, | |||
date = dateLabel({ year = year, day = day }), | |||
city = city, | |||
cult = cult, | |||
outcome = outcome, | |||
tradeId = tradeId, | |||
signCode = signCode | |||
} | |||
end | |||
local function admissionMap(args) | |||
local map = {} | |||
local toYear = tonumber(args.year or args.psscyear) or 0 | |||
local toDay = tonumber(args.day or args.dayofyear) or 0 | |||
local fromYear = tonumber(args.fromyear or args.startyear or args.admissionfromyear) or toYear | |||
local fromDay = tonumber(args.fromday or args.startday or args.admissionfromday) or 1 | |||
if toYear <= 0 or toDay <= 0 then | |||
return map | |||
end | |||
if fromYear <= 0 then fromYear = toYear end | |||
if fromDay < 1 then fromDay = 1 end | |||
if fromDay > 183 then fromDay = 183 end | |||
if toDay < 1 then toDay = 1 end | |||
if toDay > 183 then toDay = 183 end | |||
for y = fromYear, toYear do | |||
local dStart = 1 | |||
local dEnd = 183 | |||
if y == fromYear then dStart = fromDay end | |||
if y == toYear then dEnd = toDay end | |||
for d = dStart, dEnd do | |||
local rec = tradeAgreementRecordForDate(args, y, d) | |||
if rec and rec.city and rec.city.code then | |||
local code = rec.city.code | |||
if not map[code] then | |||
map[code] = rec | |||
end | |||
end | |||
end | |||
end | |||
return map | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Rare Southern Strait trade-agreement / General Port admission events | |||
-------------------------------------------------------------------------------- | |||
local function tradeAgreementEvent(args) | |||
local showNoEvent = yn(args.showTradeNoEvent or args.showtradenoevent, false) | |||
local year = tonumber(args.year or args.psscyear) or 0 | |||
local day = tonumber(args.day or args.dayofyear) or 0 | |||
local rec = tradeAgreementRecordForDate(args, year, day) | |||
if not rec then | |||
if showNoEvent then | |||
return "''No new Southern Strait trade-agreement sign-off recorded for " .. dateLabel(args) .. ".''" | |||
end | |||
return nil | |||
end | |||
local city = rec.city | |||
local cult = rec.cult | |||
return table.concat({ | return table.concat({ | ||
| Line 392: | Line 475: | ||
"! Outcome", | "! Outcome", | ||
"|-", | "|-", | ||
"| " .. | "| " .. rec.date, | ||
"| " .. tradeId, | "| " .. rec.tradeId, | ||
"| " .. city.city .. "<br><small>" .. city.region .. "</small>", | "| " .. city.city .. "<br><small>" .. city.region .. "</small>", | ||
"| " .. city.company, | "| " .. city.company, | ||
| Line 399: | Line 482: | ||
"| " .. city.resource, | "| " .. city.resource, | ||
"| " .. (city.lineage or "Local economy tied to Southern Strait corridor trade and General Port admission review."), | "| " .. (city.lineage or "Local economy tied to Southern Strait corridor trade and General Port admission review."), | ||
"| " .. flagLabel(cult.flag, cult.label) .. "<br><small>Sign-off code: '''" .. signCode .. "'''</small>", | "| " .. flagLabel(cult.flag, cult.label) .. "<br><small>Sign-off code: '''" .. rec.signCode .. "'''</small>", | ||
"| " .. outcome, | "| " .. rec.outcome, | ||
"|}" | "|}" | ||
}, "\n") | }, "\n") | ||
| Line 416: | Line 499: | ||
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) | ||
| Line 427: | Line 507: | ||
local includeTrade = yn(args.trade or args.tradeagreements or args.tradeagreement, true) | local includeTrade = yn(args.trade or args.tradeagreements or args.tradeagreement, true) | ||
local showRowLocalities = yn(args.rowlocalities or args.localities or args.showlocalities, true) | local showRowLocalities = yn(args.rowlocalities or args.localities or args.showlocalities, true) | ||
| Line 459: | Line 538: | ||
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 570: | Line 644: | ||
"* '''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.", | ||
"* '''Southern Strait trade-agreement sign-offs''' are rare or uncommon events allowing companies in Southern Strait, Eastern Mykonos Desert, and eastern Snowholme foothill settlements to be admitted to the appropriate [[General Port of Lake Morovia]] ledger after cult sign-off.", | "* '''Southern Strait trade-agreement sign-offs''' are rare or uncommon events allowing companies in Southern Strait, Eastern Mykonos Desert, and eastern Snowholme foothill settlements to be admitted to the appropriate [[General Port of Lake Morovia]] ledger after cult sign-off.", | ||
"* '''Economic lineage''' notes describe how the newer locality economies are influenced by the historic port economies of [[Blore Heath]], [[Jogi]], and [[Mylecia]] without directly copying their companies, commodities, or institutions." | "* '''Economic lineage''' notes describe how the newer locality economies are influenced by the historic port economies of [[Blore Heath]], [[Jogi]], and [[Mylecia]] without directly copying their companies, commodities, or institutions.", | ||
"* '''Standalone locality tables''' use generated trade-agreement records to mark companies as Independent or General Port of Lake Morovia." | |||
}, "\n") | }, "\n") | ||
| Line 618: | Line 693: | ||
args.title = args.title or "Temple Bank Southern Strait Missionary Daily Operations" | args.title = args.title or "Temple Bank Southern Strait Missionary Daily Operations" | ||
return p.makeTable({ args = args }) | return p.makeTable({ args = args }) | ||
end | |||
-------------------------------------------------------------------------------- | |||
-- Standalone locality / General Port status table | |||
-------------------------------------------------------------------------------- | |||
function p.localityTable(frame) | |||
local args = (frame and frame.args) or {} | |||
local admitted = admissionMap(args) | |||
local out = {} | |||
table.insert(out, "{| class=\"wikitable sortable collapsible\" border=\"1;\" width=\"100%\"") | |||
table.insert(out, "|- bgcolor=\"#e6e6ff\"") | |||
table.insert(out, "! Locality") | |||
table.insert(out, "! Geographic zone") | |||
table.insert(out, "! Terrain and setting") | |||
table.insert(out, "! Primary resource focus") | |||
table.insert(out, "! Principal company or institution") | |||
table.insert(out, "! Status") | |||
table.insert(out, "! General Port category") | |||
table.insert(out, "! Admission record") | |||
table.insert(out, "! Corridor role") | |||
for _, city in ipairs(southernTradeCities) do | |||
local rec = admitted[city.code] | |||
local isPort = rec ~= nil | |||
local rowColor = isPort and "#ddffdd" or "#ffdddd" | |||
local companyTag = isPort and "'''(General Port)'''" or "'''(Independent)'''" | |||
local statusText = isPort and "[[General Port of Lake Morovia]]" or "Independent" | |||
local admissionText | |||
if isPort then | |||
admissionText = rec.date .. "<br>Trade-ID: '''" .. rec.tradeId .. "'''<br>Sign-off: '''" .. rec.signCode .. "'''" | |||
else | |||
admissionText = "No General Port admission event recorded by " .. dateLabel(args) .. "." | |||
end | |||
table.insert(out, "|- bgcolor=\"" .. rowColor .. "\"") | |||
table.insert(out, "| '''" .. city.city .. "'''") | |||
table.insert(out, "| " .. city.region) | |||
table.insert(out, "| " .. (city.terrain or "Locality focus within the extended Southern Strait, Eastern Mykonos Desert, or eastern Snowholme foothill corridor.")) | |||
table.insert(out, "| " .. city.resource) | |||
table.insert(out, "| " .. city.company .. " " .. companyTag) | |||
table.insert(out, "| " .. statusText) | |||
table.insert(out, "| " .. city.category) | |||
table.insert(out, "| " .. admissionText) | |||
table.insert(out, "| " .. (city.role or city.lineage or "Secondary locality focus used for inspection, escort, registry, trade-agreement, infrastructure, or route-security activity.")) | |||
end | |||
table.insert(out, "|}") | |||
return table.concat(out, "\n") | |||
end | end | ||
return p | return p | ||
Revision as of 21:48, 2 June 2026
Documentation for this module may be created at Module:SouthernStraitCorridorOps/doc
-- Module:SouthernStraitCorridorOps
-- Southern Strait-only daily operations wrapper.
-- Shows only Haifan Bassaridian / Southern Strait entries from:
-- 1) WarLeagueDivisionOpsBVR
-- 2) HatchMinistryDailyOps
-- 3) MissionaryDailyOpsBVR
--
-- Also adds:
-- 4) Rare Southern Strait trade-agreement / General Port admission events
-- 5) Row-level locality focus column for extended Southern Strait,
-- Eastern Mykonos Desert, and eastern Snowholme foothill settlements
-- 6) Standalone locality / General Port status table
--
-- 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",
terrain = "Oasis-edge desert town built around lined cisterns, wind-pumped wells, reflective irrigation basins, dry-route caravan stations, and water-accounted produce claims.",
role = "Serves as a desert provisioning point for caravan routes, ferry-market suppliers, road-transfer depots, inspection parties, and export-control checks operating between the Southern Strait and the interior desert routes.",
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",
terrain = "Brackish canal-edge and marsh-inlet settlement located near ferry shelters, reed beds, cargo landings, warehouse yards, and older coastal market routes.",
role = "Supplies practical harbor materials for ferry shelters, baskets, rope backing, cargo padding, crate linings, matting, and cheap construction; also serves as a frequent inspection point because reed, cane, and packing bundles can conceal documents, vouchers, courier packets, or small contraband.",
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",
terrain = "Wechuan-influenced terrace settlement with shared irrigation channels, hillside fields, wool sheds, upland granaries, mule-train yards, and seasonal labor routes.",
role = "Links Snowholme foothill agriculture to Southern Strait supply routes, missionary kitchens, caravan stations, upland checkpoints, rail passengers, pack traffic, and food-storage networks.",
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",
terrain = "Spring-fed foothill town centered on cold ponds, watercress beds, storage houses, smoking sheds, spring channels, road markets, and regulated food movement.",
role = "Functions as a water-rights, food-inspection, and cold-chain locality supporting pilgrims, traders, health lodges, caravan kitchens, storm-drainage work, mountain-road markets, and regulated food shipments.",
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",
terrain = "Quarry town on desert stone beds and dry road approaches, with steam-powered cutting yards, lime kilns, drainage works, culvert yards, and small tram or cartage links.",
role = "Provides practical construction material for roadbeds, rail sidings, harbor paving, culverts, retaining walls, inspection posts, shrine buildings, quay repairs, drainage routes, and storm-damaged corridor approaches.",
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",
terrain = "Signal-coast town with access to coastal sand, quartz, mica-bearing material, lighthouse workshops, telegraph maintenance yards, signal towers, and storm-warning stations.",
role = "Supports the communications and navigation side of the corridor posture, including ferry warnings, lighthouse maintenance, storm-delay routing, telegraph lines, Hatch Ministry watch posts, buoy repair, signal mirrors, and coastal inspection stations.",
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",
terrain = "Foothill route town located along night-road approaches, caravan lodging lanes, pass offices, guide houses, bonded transit desks, and pilgrimage documentation offices.",
role = "Serves as the principal route-administration locality among the extended settlements, handling guide disputes, missing-traveler claims, forged pass papers, lodging records, Temple Bank documentation, caravan insurance, bonded transit files, and corridor movement 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 rowLocality(args, channel, rowNumber)
if not southernTradeCities or #southernTradeCities == 0 then
return "Southern Strait corridor"
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
local loc = southernTradeCities[idx]
return loc.city .. "<br><small>" .. loc.region .. "</small>"
end
local function addRowLocalities(body, args, channel)
body = tostring(body or "")
if not body:find("{|", 1, true) 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
table.insert(out, "! Locality focus")
headerInserted = true
table.insert(out, line)
elseif inTable and line:match("^%s*|%-") then
rowNumber = rowNumber + 1
table.insert(out, line)
table.insert(out, "| " .. rowLocality(args, channel, rowNumber))
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
--------------------------------------------------------------------------------
-- 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 = {
"Trade agreement countersigned; company cleared for addition to the appropriate [[General Port of Lake Morovia]] ledger pending ordinary page update.",
"Provisional General Port admission recommended; company papers transmitted to the Merchant General's audit desk for category placement.",
"Conditional trade window approved; first-cycle output, route papers, and stipend-voucher handling placed under Port review.",
"Company charter accepted for corridor listing; local cult sign-off attached to the General Port admission file.",
"Trade agreement filed without public ceremony; company admitted through ordinary registry channels to avoid inflaming corridor tensions."
}
--------------------------------------------------------------------------------
-- Trade-agreement record helper
--------------------------------------------------------------------------------
local function tradeAgreementRecordForDate(args, year, day)
local chance = tonumber(args.tradechance or args.tradeChance) or 8
if chance < 0 then chance = 0 end
if chance > 100 then chance = 100 end
year = tonumber(year) or 0
day = tonumber(day) or 0
local seed = tostring(args.seed or "") .. ":SS-TRADE:" .. tostring(year) .. ":" .. tostring(day)
local roll = pctFromSeed(seed, "roll") + 1
if roll > chance then
return nil
end
local city = pick(southernTradeCities, seed, "city")
local cult = pick(tradeSigningCults, seed, "cult")
local outcome = pick(tradeOutcomes, seed, "outcome")
if not city or not cult then return nil 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, city.code or "GEN", seq)
else
tradeId = string.format("GP-SS-%s-%02d", city.code or "GEN", seq)
end
local signCode = string.format("%s-TA%02d", cult.code or "CUL", seq)
return {
year = year,
day = day,
date = dateLabel({ year = year, day = day }),
city = city,
cult = cult,
outcome = outcome,
tradeId = tradeId,
signCode = signCode
}
end
local function admissionMap(args)
local map = {}
local toYear = tonumber(args.year or args.psscyear) or 0
local toDay = tonumber(args.day or args.dayofyear) or 0
local fromYear = tonumber(args.fromyear or args.startyear or args.admissionfromyear) or toYear
local fromDay = tonumber(args.fromday or args.startday or args.admissionfromday) or 1
if toYear <= 0 or toDay <= 0 then
return map
end
if fromYear <= 0 then fromYear = toYear end
if fromDay < 1 then fromDay = 1 end
if fromDay > 183 then fromDay = 183 end
if toDay < 1 then toDay = 1 end
if toDay > 183 then toDay = 183 end
for y = fromYear, toYear do
local dStart = 1
local dEnd = 183
if y == fromYear then dStart = fromDay end
if y == toYear then dEnd = toDay end
for d = dStart, dEnd do
local rec = tradeAgreementRecordForDate(args, y, d)
if rec and rec.city and rec.city.code then
local code = rec.city.code
if not map[code] then
map[code] = rec
end
end
end
end
return map
end
--------------------------------------------------------------------------------
-- Rare Southern Strait trade-agreement / General Port admission events
--------------------------------------------------------------------------------
local function tradeAgreementEvent(args)
local showNoEvent = yn(args.showTradeNoEvent or args.showtradenoevent, false)
local year = tonumber(args.year or args.psscyear) or 0
local day = tonumber(args.day or args.dayofyear) or 0
local rec = tradeAgreementRecordForDate(args, year, day)
if not rec then
if showNoEvent then
return "''No new Southern Strait trade-agreement sign-off recorded for " .. dateLabel(args) .. ".''"
end
return nil
end
local city = rec.city
local cult = rec.cult
return table.concat({
"{| class=\"wikitable sortable\"",
"! Date",
"! Trade-ID",
"! Settlement",
"! Company",
"! General Port category",
"! Resource focus",
"! Economic lineage",
"! Cult sign-off",
"! Outcome",
"|-",
"| " .. rec.date,
"| " .. rec.tradeId,
"| " .. city.city .. "<br><small>" .. city.region .. "</small>",
"| " .. city.company,
"| " .. city.category,
"| " .. city.resource,
"| " .. (city.lineage or "Local economy tied to Southern Strait corridor trade and General Port admission review."),
"| " .. flagLabel(cult.flag, cult.label) .. "<br><small>Sign-off code: '''" .. rec.signCode .. "'''</small>",
"| " .. rec.outcome,
"|}"
}, "\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 includeTrade = yn(args.trade or args.tradeagreements or args.tradeagreement, true)
local showRowLocalities = yn(args.rowlocalities or args.localities or args.showlocalities, 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 War League deployments, Hatch Ministry privateer activity, Temple Bank missionary operations, rare General Port trade-agreement sign-offs, and row-level Southern Strait locality focus columns 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",
}
if args.division and tostring(args.division) ~= "" then
warArgs.division = args.division
elseif args.wardivision and tostring(args.wardivision) ~= "" then
warArgs.division = args.wardivision
end
if args.seed and tostring(args.seed) ~= "" then
warArgs.seed = args.seed
end
local ok, body = safeCall("Module:WarLeagueDivisionOpsBVR", "makeTable", warArgs)
if showRowLocalities then
body = addRowLocalities(body, args, "war")
end
table.insert(out, hr())
table.insert(out, section("War League 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)
if showRowLocalities then
body = addRowLocalities(body, args, "hatch")
end
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)
if showRowLocalities then
body = addRowLocalities(body, args, "missionary")
end
table.insert(out, hr())
table.insert(out, section("Temple Bank Southern Strait missionary operations", body, collapseSections, startCollapsed))
end
----------------------------------------------------------------------------
-- Rare trade-agreement / General Port admission events
----------------------------------------------------------------------------
if includeTrade then
local tradeBody = tradeAgreementEvent(args)
if tradeBody and tostring(tradeBody) ~= "" then
table.insert(out, hr())
table.insert(out, section("Southern Strait trade-agreement sign-offs", tradeBody, collapseSections, startCollapsed))
end
end
----------------------------------------------------------------------------
-- Combined legend
----------------------------------------------------------------------------
if showLegend then
local legendBody = table.concat({
"* '''War League Southern Strait deployments''' show Haifan Bassaridian / former Maritime Markets security rows only.",
"* '''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.",
"* '''Southern Strait trade-agreement sign-offs''' are rare or uncommon events allowing companies in Southern Strait, Eastern Mykonos Desert, and eastern Snowholme foothill settlements to be admitted to the appropriate [[General Port of Lake Morovia]] ledger after cult sign-off.",
"* '''Economic lineage''' notes describe how the newer locality economies are influenced by the historic port economies of [[Blore Heath]], [[Jogi]], and [[Mylecia]] without directly copying their companies, commodities, or institutions.",
"* '''Standalone locality tables''' use generated trade-agreement records to mark companies as Independent or General Port of Lake Morovia."
}, "\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.trade = "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.trade = "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.trade = "no"
args.missionary = "yes"
args.title = args.title or "Temple Bank Southern Strait Missionary Daily Operations"
return p.makeTable({ args = args })
end
--------------------------------------------------------------------------------
-- Standalone locality / General Port status table
--------------------------------------------------------------------------------
function p.localityTable(frame)
local args = (frame and frame.args) or {}
local admitted = admissionMap(args)
local out = {}
table.insert(out, "{| class=\"wikitable sortable collapsible\" border=\"1;\" width=\"100%\"")
table.insert(out, "|- bgcolor=\"#e6e6ff\"")
table.insert(out, "! Locality")
table.insert(out, "! Geographic zone")
table.insert(out, "! Terrain and setting")
table.insert(out, "! Primary resource focus")
table.insert(out, "! Principal company or institution")
table.insert(out, "! Status")
table.insert(out, "! General Port category")
table.insert(out, "! Admission record")
table.insert(out, "! Corridor role")
for _, city in ipairs(southernTradeCities) do
local rec = admitted[city.code]
local isPort = rec ~= nil
local rowColor = isPort and "#ddffdd" or "#ffdddd"
local companyTag = isPort and "'''(General Port)'''" or "'''(Independent)'''"
local statusText = isPort and "[[General Port of Lake Morovia]]" or "Independent"
local admissionText
if isPort then
admissionText = rec.date .. "<br>Trade-ID: '''" .. rec.tradeId .. "'''<br>Sign-off: '''" .. rec.signCode .. "'''"
else
admissionText = "No General Port admission event recorded by " .. dateLabel(args) .. "."
end
table.insert(out, "|- bgcolor=\"" .. rowColor .. "\"")
table.insert(out, "| '''" .. city.city .. "'''")
table.insert(out, "| " .. city.region)
table.insert(out, "| " .. (city.terrain or "Locality focus within the extended Southern Strait, Eastern Mykonos Desert, or eastern Snowholme foothill corridor."))
table.insert(out, "| " .. city.resource)
table.insert(out, "| " .. city.company .. " " .. companyTag)
table.insert(out, "| " .. statusText)
table.insert(out, "| " .. city.category)
table.insert(out, "| " .. admissionText)
table.insert(out, "| " .. (city.role or city.lineage or "Secondary locality focus used for inspection, escort, registry, trade-agreement, infrastructure, or route-security activity."))
end
table.insert(out, "|}")
return table.concat(out, "\n")
end
return p