Module:WarLeagueUnitUtilizationBVR
From MicrasWiki
Documentation for this module may be created at Module:WarLeagueUnitUtilizationBVR/doc
-- Module:WarLeagueUnitUtilizationBVR
-- Renders the existing Unit Numbers table (kept as raw wikitext) and overlays
-- daily utilization highlights by parsing Module:WarLeagueDivisionOpsBVR output.
local p = {}
--------------------------------------------------------------------------------
-- 1) PASTE YOUR EXISTING TABLE HERE (exactly as-is)
--------------------------------------------------------------------------------
local RAW_BASE_TABLE = [=[
====Unit Numbers (Activated 32/3/52 PSSC)====
{| class="wikitable sortable" style="width:100%;"
|+ '''Unit Numbers (Activated 32/3/52 PSSC)'''
! rowspan="2" | Image
! rowspan="2" | Unit
! rowspan="2" | Total
! colspan="6" | Regional investors
! colspan="5" | Service divisions / national allocations
|-
! [[File:VaeringheimDivisionFlag.png|25px]] Vaeringheim Regional Investor
! [[File:AlpazkigzDivisionFlag.png|25px]] Alpazkigz Regional Investor
! [[File:OdiferiaFlag.png|25px]] Odiferia Regional Investor
! [[File:HafaanFlag.png|25px]] Hafaan Regional Investor
! [[File:JeseriFlag.png|25px]] Jeseri Regional Investor
! [[File:HatchMinistryFlag.png|25px]] Hatch Ministry Regional Investor
! [[File:NewSouthJangsongFlag.png|25px]] New South Jangsong Division
! [[File:HaifaDivisionFlag.png|25px]] Haifan Bassaridia Division
! [[File:BassaridianNormarkFlagB.png|25px]] Bassaridian Normark
! [[File:OurianaFlag.png|25px]] Ouriana Division
! [[File:CouncilofKingsFlag.png|25px]] Council of Kings Division
<!-- KEEP PASTING THE REST OF YOUR TABLE HERE, UNCHANGED -->
|} -- end table
]=]
--------------------------------------------------------------------------------
-- 2) COLUMN ORDER (must match your table)
--------------------------------------------------------------------------------
local COL_KEYS = {
"VREG","ALPK","ODFR","HAFA","JESR","HMIN",
"NSJ","HBAS","BNOR","OURN","CKD"
}
-- Map your Unit Numbers columns -> WarLeague division codes used in the ops module.
-- Tweak if you want Hafaan/Jeresi investors to map differently.
local COL_TO_DIV = {
VREG = "VAE",
ALPK = "ALP",
ODFR = "ODF",
HAFA = "HFA",
JESR = "HFA", -- adjust if you add a distinct Jeseri ops division later
HMIN = "HAT",
NSJ = "NSJ",
HBAS = "HFA",
BNOR = "BNO",
OURN = "OUR",
CKD = "CKD",
}
--------------------------------------------------------------------------------
-- 3) TO&E / “DRAW” TEMPLATES (edit these to match your canon)
-- Keys MUST match the display text in the Unit column of your base table.
--------------------------------------------------------------------------------
local TOE = {
-- Ground echelons from Module:WarLeagueDivisionOpsBVR
["Turmae"] = {
["Active Units"] = 120,
["Kalithros Class Rifle"] = 120,
["Bubalus XT Class Revolver"] = 20,
["Syrinx Class Armored Infantry"] = 2,
["Onceanic Recon Vehicle"] = 1,
["Corythia Class Dedicated Logistics and Transport Truck"] = 2,
},
["Contubernia"] = {
["Active Units"] = 60,
["Kalithros Class Rifle"] = 60,
["Bubalus XT Class Revolver"] = 10,
["Syrinx Class Armored Infantry"] = 1,
["Corythia Class Dedicated Logistics and Transport Truck"] = 1,
},
["Ordinis"] = {
["Active Units"] = 240,
["Kalithros Class Rifle"] = 240,
["Delphica Class Grenadier Rifle"] = 24,
["Bubalus XT Class Revolver"] = 40,
["Syrinx Class Armored Infantry"] = 6,
["Onceanic Recon Vehicle"] = 2,
["Corythia Class Dedicated Logistics and Transport Truck"] = 6,
},
["Manipuli"] = {
["Active Units"] = 900,
["Kalithros Class Rifle"] = 900,
["Delphica Class Grenadier Rifle"] = 120,
["Chrysos Class Commando Rifle"] = 60,
["Lothaya Class Sniper Rifle"] = 18,
["Bubalus XT Class Revolver"] = 120,
["Cathartes Class RPG"] = 18,
["Syrinx Class Armored Infantry"] = 24,
["Makra Class Battle Tank"] = 2,
["Arachne Class Light Tank"] = 4,
["Ephyra Class Anti-Aircraft Vehicle"] = 2,
["Corythia Class Dedicated Logistics and Transport Truck"] = 30,
["Ampelos Class Armored Recovery Vehicle"] = 2,
},
["Centuriae"] = {
["Active Units"] = 2400,
["Kalithros Class Rifle"] = 2400,
["Delphica Class Grenadier Rifle"] = 360,
["Chrysos Class Commando Rifle"] = 120,
["Lothaya Class Sniper Rifle"] = 36,
["Bubalus XT Class Revolver"] = 240,
["Cathartes Class RPG"] = 36,
["Syrinx Class Armored Infantry"] = 80,
["Makra Class Battle Tank"] = 8,
["Thalassa Class Main Battle Tank"] = 4,
["Arachne Class Light Tank"] = 16,
["Aithra Class Howitzer"] = 6,
["Aetheris Class Towed Howitzer"] = 12,
["Corythia Class Dedicated Logistics and Transport Truck"] = 90,
["Ampelos Class Armored Recovery Vehicle"] = 6,
["Icaria Class Mine-Clearing / Combat Engineering Vehicle"] = 4,
},
["Cohortes"] = {
["Active Units"] = 8000,
["Kalithros Class Rifle"] = 8000,
["Delphica Class Grenadier Rifle"] = 1200,
["Chrysos Class Commando Rifle"] = 400,
["Lothaya Class Sniper Rifle"] = 120,
["Bubalus XT Class Revolver"] = 900,
["Cathartes Class RPG"] = 120,
["Syrinx Class Armored Infantry"] = 240,
["Makra Class Battle Tank"] = 24,
["Thalassa Class Main Battle Tank"] = 12,
["Arachne Class Light Tank"] = 48,
["Aithra Class Howitzer"] = 18,
["Aetheris Class Towed Howitzer"] = 36,
["Iynas Class Field Gun"] = 12,
["Corythia Class Dedicated Logistics and Transport Truck"] = 300,
["Ampelos Class Armored Recovery Vehicle"] = 18,
["Penthia Class Bridge-Laying Vehicle"] = 4,
["Icaria Class Mine-Clearing / Combat Engineering Vehicle"] = 12,
},
-- Naval echelons from Module:WarLeagueDivisionOpsBVR
["Birlik-i Gemi (Patrol Element)"] = {
["Aetherium Class Patrol Ship"] = 1,
["Cetus Class Attack Craft"] = 2,
},
["Tabur-i Derya (Task Unit)"] = {
["Vaeringheim Class Corvette"] = 2,
["Aetherium Class Patrol Ship"] = 2,
["Cetus Class Attack Craft"] = 6,
["Odobenus Class Minelayer"] = 1,
},
["Ordu (Task Group)"] = {
["Sylvapolis Class Frigate"] = 2,
["Vaeringheim Class Corvette"] = 4,
["Aetherium Class Patrol Ship"] = 6,
["Cetus Class Attack Craft"] = 12,
["Cetomagna Class Minehunter"] = 1,
["Odobenus Class Minelayer"] = 1,
},
-- Air echelons from Module:WarLeagueDivisionOpsBVR
["Zeygi (Element)"] = {
["Catonis Class Unmanned Aircraft (UAV)"] = 1,
["Lotos Class Tactical UAV"] = 1,
},
["Ptisis (Flight)"] = {
["Thalassa Class Attack Helicopter"] = 1,
["Noctiluna Class Medium Transport Helicopter"] = 1,
["Catonis Class Unmanned Aircraft (UAV)"] = 2,
},
["Moira (Squadron)"] = {
["Abeis-Boreas Littoral Dominance Helicopter"] = 2,
["Thalassa Class Attack Helicopter"] = 4,
["Noctiluna Class Medium Transport Helicopter"] = 2,
["Catonis Class Unmanned Aircraft (UAV)"] = 6,
["Aurantius Class Multi-Role UAV"] = 2,
},
}
--------------------------------------------------------------------------------
-- 4) Helpers
--------------------------------------------------------------------------------
local function toNumber(s)
s = tostring(s or "")
local n = s:gsub("[^0-9]", "")
if n == "" then return nil end
return tonumber(n)
end
local function fmt(n)
if not n then return "" end
local s = tostring(math.floor(n))
local out = s:reverse():gsub("(%d%d%d)", "%1,"):reverse()
out = out:gsub("^,", "")
return out
end
local function extractDisplayText(wikilinkCell)
-- Gets the display part of [[...|DISPLAY]]; falls back to stripped text.
local c = tostring(wikilinkCell or "")
local display = c:match("%[%[[^%]]-%|([^%]]-)%]%]")
if display and display ~= "" then return display end
-- if [[Page]] style
display = c:match("%[%[([^%]]-)%]%]")
if display and display ~= "" then return display end
return c:gsub("[%[%]]", ""):gsub("^%s+", ""):gsub("%s+$", "")
end
local function cellColor(pct)
-- 0 = no highlight handled elsewhere
if pct <= 0.02 then return "#d4edda" end -- green
if pct <= 0.05 then return "#fff3cd" end -- yellow
if pct <= 0.10 then return "#ffe5cc" end -- orange
return "#f8d7da" -- red
end
--------------------------------------------------------------------------------
-- 5) Parse today’s ops table, compute utilization by division code + unit name
--------------------------------------------------------------------------------
local function computeUtilization(args)
local util = {} -- util[DIVCODE][UNITNAME] = usedCount
local function bump(div, unitName, amount)
if not div or div == "" or not unitName or unitName == "" or not amount or amount <= 0 then return end
util[div] = util[div] or {}
util[div][unitName] = (util[div][unitName] or 0) + amount
end
local ok, ops = pcall(require, "Module:WarLeagueDivisionOpsBVR")
if not ok or not ops or type(ops.makeTable) ~= "function" then
return util, false
end
local seed = args.seed
local rows = tonumber(args.opsrows) or tonumber(args.rows) or 36
local tbl = ops.makeTable({ args = {
rows = rows,
seed = seed,
showlegend = "no",
flagsize = args.flagsize or "20",
}})
for line in tostring(tbl):gmatch("[^\n]+") do
-- data lines look like: | date || division || ... || Ground || Naval || Air || ...
if line:match("^|%s") and line:find("||", 1, true) then
local parts = mw.text.split(line:gsub("^|%s*", ""), "%s*||%s*")
if #parts >= 11 then
local ground = parts[9] or "—"
local naval = parts[10] or "—"
local air = parts[11] or "—"
local function handle(forceCell)
forceCell = tostring(forceCell or "—")
if forceCell == "—" or forceCell == "" then return end
-- Example formats in ops module:
-- "Manipuli (ODF-01)" "Birlik-i Gemi (Patrol Element) (ODF-N07)" "Ptisis (Flight) (ODF-A12)"
local divCode = forceCell:match("%((%u%u%u)%-") -- grabs ODF from (ODF-01)
local echelon = forceCell:match("^%s*(.-)%s*%(") or forceCell
local toe = TOE[echelon]
if toe and divCode then
for unitName, amount in pairs(toe) do
bump(divCode, unitName, amount)
end
end
end
handle(ground)
handle(naval)
handle(air)
end
end
end
return util, true
end
--------------------------------------------------------------------------------
-- 6) Overlay-highlight the base table
--------------------------------------------------------------------------------
local function overlayTable(baseText, util, args)
local out = {}
local inMainTable = false
for line in tostring(baseText):gmatch("[^\n]*\n?") do
if line == "" then break end
-- Track when we are inside the big table
if line:match("^%{%|") then inMainTable = true end
if inMainTable and line:match("^|%}%s*$") then
inMainTable = false
table.insert(out, line)
elseif inMainTable and line:match("^|%s*%[%[File:") and line:find("||", 1, true) then
-- This is a data row with 14 columns:
-- image || unit || total || 11 columns...
local parts = mw.text.split(line:gsub("^|%s*", ""), "%s*||%s*")
if #parts == 14 then
local unitCell = parts[2]
local unitName = extractDisplayText(unitCell)
-- decorate each of the 11 investor/service columns
for i = 4, 14 do
local colKey = COL_KEYS[i - 3] -- 4->1, 14->11
local divCode = COL_TO_DIV[colKey]
local available = toNumber(parts[i])
local used = (divCode and util[divCode] and util[divCode][unitName]) or 0
if available and used and used > 0 then
local pct = (available > 0) and (used / available) or 1
local bg = cellColor(pct)
local usedText = string.format("<br/><span style='font-size:85%;'><b>used:</b> %s</span>", fmt(used))
parts[i] = string.format("style='background:%s; text-align:center;' | %s%s", bg, parts[i], usedText)
end
end
table.insert(out, "| " .. table.concat(parts, " || "))
else
table.insert(out, line)
end
else
table.insert(out, line)
end
end
-- legend
table.insert(out, "")
table.insert(out, "<div style='margin-top:0.5em; font-size:90%;'>")
table.insert(out, "<b>Utilization highlight:</b> cells tint when that division/investor drew from that row today.")
table.insert(out, "<br/><span style='background:#d4edda;padding:2px 6px;border:1px solid #c3e6cb;'>Low</span> ")
table.insert(out, "<span style='background:#fff3cd;padding:2px 6px;border:1px solid #ffeeba;'>Moderate</span> ")
table.insert(out, "<span style='background:#ffe5cc;padding:2px 6px;border:1px solid #ffddaa;'>High</span> ")
table.insert(out, "<span style='background:#f8d7da;padding:2px 6px;border:1px solid #f5c6cb;'>Very high</span>")
table.insert(out, "<br/>Numbers are driven by the TO&E templates in this module (edit TOE[...] to match canon).")
table.insert(out, "</div>")
return table.concat(out, "")
end
--------------------------------------------------------------------------------
-- 7) Public entrypoint
--------------------------------------------------------------------------------
function p.render(frame)
local args = (frame and frame.args) or {}
local util, ok = computeUtilization(args)
local base = RAW_BASE_TABLE
if not ok then
return base .. "\n<div style='margin-top:0.5em; font-size:90%;'><b>Note:</b> Ops module unavailable; showing base table only.</div>"
end
return overlayTable(base, util, args)
end
return p