Module:StraitsDaily

From MicrasWiki
Revision as of 16:49, 6 September 2025 by NewZimiaGov (talk | contribs)
Jump to navigationJump to search

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

-- Module: StraitsDaily
-- Purpose: Daily, in-world report of Strait of Haifa enforcement

local p = {}

-------------------------------------------------------------------------------
-- (A) PSSC DATE
-------------------------------------------------------------------------------
local function getPSSCDate()
    local startDate = os.time({year = 1999, month = 8, day = 6})
    local secondsInDay = 86400
    local daysPerYear  = 183
    local now = os.time()
    local totalDaysElapsed = math.floor((now - startDate) / secondsInDay)

    local yearFraction = totalDaysElapsed / daysPerYear
    local psscYear     = math.floor(yearFraction)
    local dayOfYear    = math.floor((yearFraction - psscYear) * daysPerYear) + 1

    local month, dayOfMonth
    if dayOfYear <= 61 then
        month = 1; dayOfMonth = dayOfYear
    elseif dayOfYear <= 122 then
        month = 2; dayOfMonth = dayOfYear - 61
    else
        month = 3; dayOfMonth = dayOfYear - 122
    end

    local dateString = string.format("%d/%d/%d PSSC", dayOfMonth, month, psscYear)
    return { text = dateString, year = psscYear, dayOfYear = dayOfYear }
end

-------------------------------------------------------------------------------
-- (B) STATIC DATA
-------------------------------------------------------------------------------
local sectors = {
    "Strait South Inbound", "Strait South Outbound", "Strait North Inbound", "Strait North Outbound",
    "Archipelago Zone", "Sacred Buffer Approaches", "Environmental Buffer", "Northern Approach TSS", "Southern Approach TSS"
}

local flags = {
    {name="Bassaridian-flag", weight=6, risky=false},
    {name="Moorland", weight=2, risky=false},
    {name="Imperial Federation", weight=5, risky=true},
    {name="Matamoros (IF)", weight=2, risky=true},
    {name="Neutral Merchant", weight=4, risky=false},
    {name="Distant Partner (Euranidom)", weight=3, risky=true},
    {name="Distant Partner (Corumia)", weight=3, risky=true},
    {name="Distant Partner (Southern Keltiania)", weight=3, risky=true},
    {name="Consortium Registry", weight=2, risky=true},
    {name="Other Coastal State", weight=7, risky=false}
}

local vessels = {
    "Handymax bulker", "Product tanker", "General cargo ship", "Container feeder",
    "Ro-Ro ferry", "Research craft", "Declared submarine", "Undeclared submarine"
}

local cargoTypes = {
    {name="Medical reagents (dual-use precursors)", weight=3, risk=0.14, dual=true},
    {name="Industrial oxidizers / nitrates (dual-use)", weight=2, risk=0.16, dual=true},
    {name="Precision machine tools & CNC spares (dual-use)", weight=3, risk=0.15, dual=true},
    {name="Encrypted electronics & comms modules (dual-use)", weight=2, risk=0.17, dual=true},
    {name="Satellite components / sensors (dual-use)", weight=1, risk=0.18, dual=true},
    {name="Industrial fasteners (coded)", weight=2, risk=0.12, dual=true},
    {name="Hydraulic lubricants (dual-use)", weight=2, risk=0.13, dual=true},
    {name="High-frequency alloys (coded)", weight=1, risk=0.14, dual=true},
    {name="Aviation fuels & additives", weight=3, risk=0.12, dual=false},
    {name="Refined petroleum products", weight=4, risk=0.08, dual=false},
    {name="Relief supplies (mixed pallets)", weight=5, risk=0.06, dual=false},
    {name="Agricultural commodities", weight=5, risk=0.04, dual=false},
    {name="Finished consumer goods", weight=6, risk=0.03, dual=false}
}

local cargoTags = {
    {name="standard", weight=6, risk=0.00},
    {name="expedited", weight=3, risk=0.05},
    {name="emergency", weight=2, risk=0.07},
    {name="perishable", weight=2, risk=0.06}
}

local partnerProfiles = {
    {code="P-07", weight=4, risk=0.00},
    {code="P-12", weight=3, risk=0.03},
    {code="P-17", weight=3, risk=0.12},
    {code="P-22", weight=3, risk=0.14},
    {code="P-31", weight=3, risk=0.05},
    {code="P-38", weight=2, risk=0.10},
    {code="P-44", weight=4, risk=0.02},
    {code="P-53", weight=3, risk=0.04}
}

-------------------------------------------------------------------------------
-- (C) MAIN GENERATION
-------------------------------------------------------------------------------
function p.makeTable(frame)
    local dateInfo = getPSSCDate()
    math.randomseed(dateInfo.year*1000 + dateInfo.dayOfYear)
    local rows = tonumber(frame and frame.args and frame.args.rows) or 18

    local function pickWeighted(list)
        if type(list) ~= "table" then return nil end
        local total = 0
        for _, item in ipairs(list) do total = total + (item.weight or 1) end
        local roll, cumul = math.random(total), 0
        for _, item in ipairs(list) do
            cumul = cumul + (item.weight or 1)
            if roll <= cumul then return item end
        end
        return list[#list]
    end

    local function pick(list) return list[math.random(#list)] end

    local function baseProb(flag, sector, cargo, tag, partner)
        local p = 0.28
        if flag.risky then p = p + 0.06 end
        if sector == "Sacred Buffer Approaches" or sector == "Environmental Buffer" then p = p + 0.08 end
        p = p + (cargo and cargo.risk or 0)
        if cargo and cargo.dual then p = p + 0.03 end
        p = p + (tag and tag.risk or 0)
        p = p + (partner and partner.risk or 0)
        return math.min(p, 0.72)
    end

    local out = {}
    table.insert(out, "{| class='wikitable sortable'")
    table.insert(out, "! Date !! Sector !! Vessel/Flag !! Action !! Legal Basis !! Outcome !! Notes")

    for _ = 1, rows do
        local sector = pick(sectors)
        local flag = pickWeighted(flags)
        local vessel = pick(vessels)
        local cargo = pickWeighted(cargoTypes)
        local tag = pickWeighted(cargoTags)
        local partner = pickWeighted(partnerProfiles)

        local enforce = baseProb(flag, sector, cargo, tag, partner)
        local outcome, action, legal, notes

        if math.random() > enforce then
            outcome = "No reports"
            action = "Routine transit"
            legal = "N/A"
            notes = string.format("Cargo: %s [%s]; Partner: %s — Compliant passage", cargo.name, tag.name, partner.code)
        else
            action = "Cargo manifest reconciliation audit"
            legal = "SC §R1–R3"
            outcome = "Administrative notice issued; monitoring"
            notes = string.format("Cargo: %s [%s]; Partner: %s", cargo.name, tag.name, partner.code)

            local riskPush = 0
            if flag.name:match("Imperial Federation") or flag.name:match("Matamoros") then riskPush = riskPush + 1 end
            if cargo.dual or cargo.risk >= 0.12 then riskPush = riskPush + 1 end
            if tag.name ~= "standard" then riskPush = riskPush + 1 end
            if partner.risk >= 0.09 then riskPush = riskPush + 1 end

            if riskPush >= 2 and math.random() < 0.65 then
                outcome = "Random audit hold; clearance pending procedural update"
                if flag.name == "Imperial Federation" or flag.name == "Matamoros (IF)" then
                    outcome = outcome .. " [Extended regulatory review]"
                end
            end
        end

        table.insert(out, "|-")
        table.insert(out, string.format("| %s || %s || %s (%s) || %s || %s || %s || %s",
            dateInfo.text, sector, vessel, flag.name, action, legal, outcome, notes))
    end

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

return p