Module:WarLeagueUnitUtilizationBVR

From MicrasWiki
Revision as of 22:15, 26 January 2026 by NewZimiaGov (talk | contribs) (Created page with "-- 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 (A...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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