Module:GeneralPortOperations

From MicrasWiki
Revision as of 19:53, 25 April 2025 by NewZimiaGov (talk | contribs)
Jump to navigationJump to search

Documentation for this module may be created at Module:GeneralPortOperations/doc

-- Module:GeneralPortDailyLog
local p = {}

-- compatibility shim for unpack
local unpack = table.unpack or unpack

-- 1) Date logic
function p.getCurrentDate()
    local start   = os.time{year=1999, month=8, day=6}
    local elapsed = math.floor((os.time() - start) / 86400)
    local year    = math.floor(elapsed / 183)
    local day     = (elapsed % 183) + 1
    return year, day
end

-- 2) Random helpers
local function randomChoice(tbl)
    if type(tbl) ~= "table" or #tbl == 0 then return nil end
    return tbl[math.random(#tbl)]
end

local function randomN(tbl, n)
    if type(tbl) ~= "table" or n < 1 then return {} end
    local copy, out = {unpack(tbl)}, {}
    for i = 1, math.min(n, #copy) do
        local idx = math.random(#copy)
        table.insert(out, table.remove(copy, idx))
    end
    return out
end

-- 3) Home ports + flags
local cities = {
    "Aegirheim","Skýrophos","Bjornopolis","Norsolyra","Ephyra",
    "Symphonara","Delphica","Vaeringheim","Somniumpolis",
    "Keybir-Aviv","Sufriya","Jogi"
}
local cityFlag = {}
for _, name in ipairs(cities) do
    local norm = name
        :gsub("[%s%-’]", "")
        :gsub("[Áá]", "A"):gsub("[Éé]", "E")
        :gsub("[Íí]", "I"):gsub("[Óó]", "O")
        :gsub("[Úú]", "U"):gsub("[Ýý]", "Y")
    cityFlag[name] = string.format("[[File:%sFlag.png|20px]]", norm)
end
-- override for Skýrophos
cityFlag["Skýrophos"] = "[[File:SkyrophosFlag.png|20px]]"

-- 4) Vessel & cargo types
local vesselTypes = {"Cargo","Military","Ritual","Privateer"}
local cargoTypes  = {"Agricultural","Mining/Manufacturing","Services","Unspecified"}

-- 5) Owner pools
local ritualCults = {
    "Ordo Solis Invicti","Order of Aurora Mystica","Harmony Sanctum",
    "Ignis Aeternum","Celestial Harmony Sect","Guild of Golden Shadows",
    "Azure Sentinel Sect","Reverie Nebulous","Eon Fellowship",
    "Order of the Umbral Oracle","Mystery of the Verdant Embrace",
    "Conclave Illuminara","Sanctum Vitalis","Temple Alabaster",
    "Court of the Ironclad","Accord Concordia","Mystery of the Stygian Veil",
    "Sylvan Fellowship","Mystery of Red Mirth","Rex Catonis",
    "Sanctum Delphica","Ordo Amicitia","Temple Illuminata"
}
local militaryDivs = {
    "Alpazkigz Division","Odiferia Division","Vaeringheim Division",
    "Haifa Division","Jeseri Division","Hatch Ministry Division",
    "New South Jangsong Division","Haifan Bassaridia Division"
}
local agriCompanies = {
    "Gladeseed Farmers' Union","Atterian Cattle Drivers","Noctic Fleet",
    "Jogiani Merchants of Manwine","Keepers of the Grove of Zoe Elm",
    "Plains of Jogi","Ale of the Night Central Brewery",
    "Camel Herders of the Jangsong Province","Anterran Imports and Services",
    "Merchant Guild of the Poppy Goddess","Grand Cave Bee Meadery",
    "Cinammon Plains of Rouge","The Velvet Company","Seamstresses of Rouge",
    "Salurian Temple of Sacred Horehound","Bulhanu Ranchers' Association",
    "Suncliff Fisheries","Ministers of Mistress Mugwort",
    "Blood Vineyards of the Far North",
    "Norsolyrian Wisp Rice Farmers Association",
    "Giid-Lisea Ranching Import Company","Bird Keepers of Saluria",
    "Hunters of the Giant Fanged Penguin","Knights Castle Reindeer Herders",
    "Lindley Whaling Company","Aminian Coffee Company"
}
local miningCompanies = {
    "Baratar Corporation","Roving Wind Farm Corporation",
    "Thalassian Temple Granite Export Cooperative","East Keltian Iron Company",
    "Mylecian Coal Ports","Merchants of the Valley of Diamonds",
    "East Keltian Timber Company","Onceanic Boot Company",
    "Commune of the Garganid Apostles","Shï Collective","Order of the Sphinx",
    "Market Hanavusu","Somniant Stock Fund"
}
local serviceCompanies = {
    "Lake Morovia Blockade Fund", "Hatch Ministry",
    "Maritime Guild of the Cult of Maskmakers","Ergonian Spy Agency",
    "Temple Bank of the Reformed Stripping Path","Couriers of the Lizard Queen",
    "Temple University of Delphica","La Sái Ebile",
    "Hellhound Breeders of Dragevik"
}

-- 6) Company logos mapping
local companyLogo = {}
local function addLogo(name,file) companyLogo[name]=file end
addLogo("Lake Morovia Blockade Fund","BlockadeFund420.png")
addLogo("Gladeseed Farmers' Union","Gladeseed420.png")
addLogo("Atterian Cattle Drivers","Atterian420.png")
addLogo("Noctic Fleet","Noctic420.png")
addLogo("Jogiani Merchants of Manwine","Jogiani420.png")
addLogo("Keepers of the Grove of Zoe Elm","KeepersoftheGrove.png")
addLogo("Plains of Jogi","PlainsofJogi.png")
addLogo("Ale of the Night Central Brewery","AleofNight420.png")
addLogo("Camel Herders of the Jangsong Province","CamelHerders420.png")
addLogo("Thalassian Temple Granite Export Cooperative","ThalassianGranite420.png")
addLogo("East Keltian Iron Company","EKIron420.png")
addLogo("Mylecian Coal Ports","CoalPorts420.png")
addLogo("Merchants of the Valley of Diamonds","ValleyofDiamonds420.png")
addLogo("Baratar Corporation","Baratar420.png")
addLogo("Roving Wind Farm Corporation","RovingWind420.png")
addLogo("Grand Cave Bee Meadery","GrandCave420.png")
addLogo("Merchant Guild of the Poppy Goddess","PoppyGoddess420.png")
addLogo("Cinammon Plains of Rouge","CinammonPlains420.png")
addLogo("The Velvet Company","VelvetHome420.png")
addLogo("Seamstresses of Rouge","Seamstresses420.png")
addLogo("Salurian Temple of Sacred Horehound","SalurianTemple421.png")
addLogo("Bulhanu Ranchers' Association","Bulhanu421.png")
addLogo("Suncliff Fisheries","Suncliff421.png")
addLogo("Ministers of Mistress Mugwort","Mugwort421.png")
addLogo("Blood Vineyards of the Far North","BloodVineyards421.png")
addLogo("Norsolyrian Wisp Rice Farmers Association","Norsolyrian421.png")
addLogo("Giid-Lisea Ranching Import Company","GiidLisea421.png")
addLogo("Bird Keepers of Saluria","SaluriaBK421.png")
addLogo("Knights Castle Reindeer Herders","KnightsCastle421.png")
addLogo("Lindley Whaling Company","LindleyWhalingCompany.png")
addLogo("Aminian Coffee Company","Aminian421.png")
addLogo("Commune of the Garganid Apostles","Garganid421.png")
addLogo("Onceanic Boot Company","OnceanicBoot421.png")
addLogo("Maritime Guild of the Cult of Maskmakers","MaritimeGuild420.png")
addLogo("Ergonian Spy Agency","ErgonianSpy421.png")
addLogo("Temple Bank of the Reformed Stripping Path","TempleBankCrest.png")
addLogo("Couriers of the Lizard Queen","LizardQueen421.png")
addLogo("Temple University of Delphica","TempleUniversity421.png")
addLogo("Shï Collective","ShiCollective421.png")
addLogo("Order of the Sphinx","OrderofSphinx421.png")
addLogo("Market Hanavusu","MarketHanavusu421.png")
addLogo("Somniant Stock Fund","SomniantStockFund.png")
addLogo("Hatch Ministry","HatchMinistry420.png")
addLogo("Anterran Imports and Services","AnterranImports420.png")
addLogo("East Keltian Timber Company","EKTimber420.png")
addLogo("Hellhound Breeders of Dragevik","Dragevik421.png")
addLogo("La Sái Ebile","LaSie421.png")
addLogo("Hunters of the Giant Fanged Penguin","FangedPenguin421.png")
addLogo("Commune of the Garganid Apostles","Garganid421.png")

addLogo("Alpazkigz Division",        "AlpazkigzDivisionFlag.png")
addLogo("Odiferia Division",         "OdiferiaFlag.png")
addLogo("Vaeringheim Division",      "VaeringheimDivisionFlag.png")
addLogo("Haifa Division",            "HafaanFlag.png")
addLogo("Jeseri Division",           "JeseriFlag.png")
addLogo("Hatch Ministry Division",   "HatchMinistryFlag.png")
addLogo("New South Jangsong Division","NewSouthJangsongFlag.png")
addLogo("Haifan Bassaridia Division", "HaifaDivisionFlag.png")

addLogo("Order of Aurora Mystica",        "Auroramystica.png")
addLogo("Harmony Sanctum",         "Harmonysanctum.png")
addLogo("Ignis Aeternum",      "Ignis Aeternum.png")
addLogo("Celestial Harmony Sect",            "HarmonySect.png")
addLogo("Guild of Golden Shadows",           "GoldenShadows.png")
addLogo("Azure Sentinel Sect",   "AzureSentinel.png")
addLogo("Reverie Nebulous","RevNeb.png")
addLogo("Eon Fellowship", "EonFel.png")
addLogo("Order of the Umbral Oracle", "Umbral.png")
addLogo("Mystery of the Verdant Embrace", "Verdant.png")
addLogo("Conclave Illuminara", "Conclave.png")
addLogo("Sanctum Vitalis", "Vitalis.png")
addLogo("Temple Alabaster", "Alabaster.png")
addLogo("Court of the Ironclad", "Ironclad.png")
addLogo("Accord Concordia", "Concordia.png")
addLogo("Mystery of the Stygian Veil", "StygianVe.png")
addLogo("Sylvan Fellowship", "Sylvan.png")
addLogo("Mystery of Red Mirth", "MirthFlag.png")
addLogo("Rex Catonis", "Catonis.png")

-- 7) Privateer flags + helper
local privateers = {
    {first="Arion",    last="Theron"},
    {first="Cassius",  last="Valerus"},
    {first="Nefra",    last="Sekeri",   nick="Sekhem"},
    {first="Lysander", last="Pallas"},
    {first="Marwan",   last="Othman"},
    {first="Octavia",  last="Marcellus"},
    {first="Amenhotep",last="Ankhu"},
    {first="Thalia",   last="Chrysos"},
    {first="Demetrius",last="Gravis"},
    {first="Selim",    last="Bey"},
    {first="Julius",   last="Drusus"},
    {first="Leyla",    last="Han"},
    {first="Marcus",   last="Flavianus"},
    {first="Fatima",   last="Pasha"},
    {first="Cornelia", last="Urbanus"},
    {first="Eudora",   last="Merit"},
    {first="Perseus",  last="Phoebus"},
    {first="Khepri",   last="Sobek"},
    {first="Lucius",   last="Aurelian"},
    {first="Aisha",    last="Dinar"},
    {first="Gaius",    last="Cassianus"},
    {first="Selene",   last="Argus"},
    {first="Arsinoe",  last="Menkaure"},
    {first="Omar",     last="Zahir"},
    {first="Dorian",   last="Grimm"},
    {first="Quintus",  last="Nero"},
    {first="Zainab",   last="Sultan"},
    {first="Cassian",  last="Corinth"},
    {first="Valeria",  last="Maximus",  nick="Ferox"},
    {first="Thorne",   last="Noctis",   nick="Mainomenos"}
}

local function getRandomPrivateer()
    local c = randomChoice(privateers)
    local flag = string.format("[[File:%s.png|20px]] ", c.last)
    if c.nick and math.random() < 0.20 then
        return string.format("%sCaptain %s ‘%s’ %s", flag, c.first, c.nick, c.last)
    else
        return string.format("%sCaptain %s %s", flag, c.first, c.last)
    end
end



-- 8) Determine Owner/Operator
local function getOwner(vt, ct)
    if vt == "Ritual" then
        return randomChoice(ritualCults)
    elseif vt == "Military" then
        return randomChoice(militaryDivs)
    elseif vt == "Privateer" then
        return getRandomPrivateer()
    elseif vt == "Cargo" then
        if ct == "Agricultural" then
            return randomChoice(agriCompanies)
        elseif ct == "Mining/Manufacturing" then
            return randomChoice(miningCompanies)
        elseif ct == "Services" then
            return randomChoice(serviceCompanies)
        end
    end
    return "Independent Consortium"
end

-- 9) Build the table
function p.generateShipTable()
    math.randomseed(os.time())
    local y, d = p.getCurrentDate()

    -- pools
    local poolMap = {
        Agricultural = agriCompanies,
        ["Mining/Manufacturing"] = miningCompanies,
        Services = serviceCompanies
    }
    local allPools = {}
    for _, list in pairs(poolMap) do
        for _, name in ipairs(list) do
            table.insert(allPools, name)
        end
    end

    local ships = {}
    for i = 1, math.random(50, 80) do
        local hr, mn = math.random(0, 23), math.random(0, 59)
        local vt, ct = randomChoice(vesselTypes), randomChoice(cargoTypes)
        local owner = getOwner(vt, ct) or "Independent Consortium"
        local ownerLogo = companyLogo[owner] and ("[[File:" .. companyLogo[owner] .. "|20px]] ") or ""
        local home = randomChoice(cities)

        -- select companies
        local count = math.random(3, 4)
        local picks = {}
        if ct == "Unspecified" then
            picks = randomN(allPools, count)
        else
            picks = randomN(poolMap[ct] or {}, count)
        end

        local compCell = "<ul>"
        for _, nm in ipairs(picks) do
            local logo = companyLogo[nm] and ("[[File:" .. companyLogo[nm] .. "|20px]] ") or ""
            compCell = compCell .. string.format("<li>%s%s</li>", logo, nm)
        end
        compCell = compCell .. "</ul>"

        local errList = {
            "Cargo Mislabeling","Improper Documentation",
            "Surveillance Anomaly","Inspection Failure",
            "Unregistered Ritual Cargo"
        }
        local err = math.random() < 0.2 and randomChoice(errList) or "None"

        table.insert(ships, {
            date           = string.format("Day %d, Y.%d", d, y),
            time           = string.format("%02d:%02d", hr, mn),
            sort           = hr * 60 + mn,
            vid            = "VM-" .. math.random(10000, 99999),
            home           = cityFlag[home] .. " " .. home,
            typ            = vt,
            ownerCell      = ownerLogo .. owner,
            cargotype      = ct,
            cargoCompanies = compCell,
            berth          = math.random(1, 124),
            teus           = math.random(100, 1000),
            tonnage        = string.format("%.2f", math.random() * 19000 + 1000),
            queue          = string.format("%.2f", math.random() * 5.5 + 0.5),
            turnaround     = string.format("%.2f", math.random() * 32 + 4),
            error          = err
        })
    end

    table.sort(ships, function(a, b) return a.sort < b.sort end)

    local out = {
        '{| class="wikitable sortable"',
        '! Date !! Time !! Vessel ID !! Home City !! Type !! Owner/Operator !! Cargo Type !! Companies',
        '!! Berth !! TEUs !! Tonnage (t) !! Queue (hrs) !! Turnaround (hrs) !! Error'
    }
    for _, s in ipairs(ships) do
        local row = (s.error ~= "None") and '|- bgcolor="#ffcccc"' or '|-'
        table.insert(out, row)
        table.insert(out, table.concat({
            '|', s.date, '||', s.time, '||', s.vid, '||', s.home, '||', s.typ,
            '||', s.ownerCell, '||', s.cargotype, '||', s.cargoCompanies,
            '||', s.berth, '||', s.teus, '||', s.tonnage, '||', s.queue,
            '||', s.turnaround, '||', s.error
        }, ' '))
    end
    table.insert(out, '|}')
    table.insert(out, "'''Error Key:'''")
    table.insert(out, "* '''Cargo Mislabeling:''' Manifest ≠ actual goods")
    table.insert(out, "* '''Improper Documentation:''' Missing or outdated papers")
    table.insert(out, "* '''Surveillance Anomaly:''' Unexpected heat/motion")
    table.insert(out, "* '''Inspection Failure:''' Customs/hatch failure")
    table.insert(out, "* '''Unregistered Ritual Cargo:''' Not pre-cleared by Temple Bank")

    return table.concat(out, "\n")
end

function p.dailyReport()
    local y, d = p.getCurrentDate()
    return string.format(
        "'''General Port of Lake Morovia: Operational Log – Day %d, Year %d PSSC'''",
        d, y
    ) .. "\n" .. p.generateShipTable()
end

return p