Module:WarLeagueUnitUtilizationBVR
From MicrasWiki
Documentation for this module may be created at Module:WarLeagueUnitUtilizationBVR/doc
-- Module:WarLeagueUnitUtilizationBVR
--
-- Purpose:
-- Displays the embedded War League inventory table and overlays a daily “used” estimate by division,
-- derived from Module:WarLeagueDivisionOpsBVR (formations + daily ops context) PLUS routine activity.
--
-- Guarantees:
--
-- Invoke:
-- {{#invoke:WarLeagueUnitUtilizationBVR|render}}
-- {{#invoke:WarLeagueUnitUtilizationBVR|render|breadth=low}} -- low|standard|high
-- {{#invoke:WarLeagueUnitUtilizationBVR|render|breadth=high}}
-- {{#invoke:WarLeagueUnitUtilizationBVR|render|seed=520154|opsrows=40}}
-- {{#invoke:WarLeagueUnitUtilizationBVR|render|debug=yes|debugrows=8}}
local p = {}
--------------------------------------------------------------------------------
-- 1) FULL EMBEDDED TABLE (embedded exactly as provided)
--------------------------------------------------------------------------------
local RAW_BASE_TABLE = [=[
{| 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
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Personnel & readiness'''
|-
| [[File:ActiveUnit1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Active_Units|Active Units]] || 249,973 || 25,866 || 20,847 || 25,042 || 1,749 || 1,008 || 7,246 || 10,187 || 15,385 || 8,354 || 21,206 || 113,083
|-
| [[File:ReserveSoldier1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Reserve_Units|Reserve Units]] || 305,525 || 31,615 || 25,480 || 30,608 || 2,138 || 1,232 || 8,857 || 12,451 || 18,803 || 10,211 || 25,918 || 138,212
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Small arms & infantry weapons'''
|-
| [[File:KalithrosClass1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Kalithros_Class_Rifle|Kalithros Class Rifle]] || 169,837 || 17,563 || 14,156 || 17,006 || 1,192 || 688 || 4,931 || 6,916 || 10,466 || 5,684 || 14,402 || 76,832
|-
| [[File:DelphicaClass1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Delphica_Class_Grenadier_Rifle|Delphica Class Grenadier Rifle]] || 44,520 || 4,604 || 3,711 || 4,458 || 312 || 180 || 1,293 || 1,813 || 2,744 || 1,490 || 3,775 || 20,140
|-
| [[File:ChrysosClass1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Chrysos_Class_Commando_Rifle|Chrysos Class Commando Rifle]] || 19,821 || 2,050 || 1,652 || 1,985 || 139 || 80 || 575 || 807 || 1,221 || 663 || 1,681 || 8,967
|-
| [[File:Lothaya1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Lothaya_Class_Sniper_Rifle|Lothaya Class Sniper Rifle]] || 6,099 || 631 || 509 || 611 || 43 || 25 || 177 || 248 || 375 || 204 || 517 || 2,556
|-
| [[File:ErythrosPist1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Erythros_Class_Pistol|Erythros Class Pistol]] || 28,540 || 2,952 || 2,379 || 2,858 || 200 || 116 || 828 || 1,162 || 1,758 || 955 || 2,421 || 12,911
|-
| [[File:Bubalus1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Bubalus_XT_Class_Revolver|Bubalus XT Class Revolver]] || 150,285 || 15,541 || 12,526 || 15,049 || 1,055 || 609 || 4,363 || 6,120 || 9,261 || 5,030 || 12,744 || 67,987
|-
| [[File:Cathartes1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Cathartes_Class_RPG|Cathartes Class RPG]] || 6,182 || 640 || 516 || 620 || 43 || 25 || 179 || 251 || 380 || 206 || 525 || 2,797
|-
| [[File:Hapyriya1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Harpyia_Class_Submachine_Gun|Harpyia Class Submachine Gun]] || 22,349 || 2,312 || 1,863 || 2,238 || 157 || 91 || 649 || 910 || 1,377 || 748 || 1,896 || 10,110
|-
| [[File:StrixShotgun1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Strix_Class_Combat_Shotgun|Strix Class Combat Shotgun]] || 35,555 || 3,677 || 2,964 || 3,561 || 249 || 144 || 1,032 || 1,448 || 2,190 || 1,190 || 3,015 || 15,346
|-
| [[File:Regavis1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Regavis_Class_DMR|Regavis Class DMR]] || 43,178 || 4,465 || 3,599 || 4,323 || 303 || 175 || 1,254 || 1,759 || 2,661 || 1,445 || 3,661 || 19,534
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Armored vehicles & ground support'''
|-
| [[File:Makra1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Makra_Class_Battle_Tank|Makra Class Battle Tank]] || 1,290 || 133 || 107 || 129 || 9 || 5 || 37 || 53 || 79 || 43 || 109 || 584
|-
| [[File:LayaClass1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Laya_Class_Heavy_Tank|Laya Class Heavy Tank]] || 444 || 46 || 37 || 45 || 3 || 2 || 13 || 18 || 27 || 15 || 38 || 201
|-
| [[File:Arachne1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Arachne_Class_Light_Tank|Arachne Class Light Tank]] || 1,560 || 162 || 130 || 157 || 11 || 6 || 45 || 63 || 95 || 52 || 133 || 705
|-
| [[File:ThalassaMBT1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Thalassa_Class_Main_Battle_Tank|Thalassa Class Main Battle Tank]] || 765 || 80 || 64 || 77 || 5 || 3 || 22 || 30 || 46 || 25 || 66 || 346
|-
| [[File:Syrinx1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Syrinx_Class_Armored_Infantry|Syrinx Class Armored Infantry]] || 7,067 || 731 || 589 || 708 || 50 || 29 || 205 || 288 || 436 || 237 || 599 || 3,198
|-
| [[File:Bijarian1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Bijarian_Command_Vehicle|Bijarian Command Vehicle]] || 115 || 12 || 10 || 12 || 1 || 0 || 3 || 4 || 6 || 3 || 10 || 52
|-
| [[File:Onceanic1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Onceanic_Recon_Vehicle|Onceanic Recon Vehicle]] || 495 || 52 || 42 || 50 || 3 || 2 || 14 || 20 || 30 || 16 || 43 || 223
|-
| [[File:Ephyra1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Ephyra_Class_Anti-Aircraft_Vehicle|Ephyra Class Anti-Aircraft Vehicle]] || 218 || 23 || 19 || 22 || 1 || 1 || 6 || 8 || 13 || 7 || 19 || 99
|-
| [[File:AmpelosARV1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Ampelos_Class_Armored_Recovery_Vehicle|Ampelos Class Armored Recovery Vehicle]] || 495 || 52 || 42 || 50 || 3 || 2 || 14 || 20 || 30 || 16 || 43 || 223
|-
| [[File:Penthia1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Penthia_Class_Bridge-Laying_Vehicle|Penthia Class Bridge-Laying Vehicle]] || 146 || 16 || 13 || 15 || 1 || 0 || 4 || 5 || 8 || 4 || 13 || 66
|-
| [[File:Ampelostr1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Ampelos_Class_Rail-Laying_Engineering_Vehicle|Ampelos Class Rail-Laying Engineering Vehicle]] || 301 || 32 || 26 || 31 || 2 || 1 || 8 || 12 || 17 || 9 || 26 || 137
|-
| [[File:Corythia1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Corythia_Class_Dedicated_Logistics_and_Transport_Truck|Corythia Class Dedicated Logistics and Transport Truck]] || 301 || 32 || 26 || 31 || 2 || 1 || 8 || 12 || 17 || 9 || 26 || 137
|-
| [[File:Icaria1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Icaria_Class_Mine-Clearing_/Combat_Engineering_Vehicle|Icaria Class Mine-Clearing / Combat Engineering Vehicle]] || 134 || 14 || 11 || 14 || 1 || 0 || 4 || 5 || 8 || 4 || 11 || 61
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Artillery, mortars & coastal fires'''
|-
| [[File:Aithra1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Aithra_Class_Howitzer|Aithra Class Howitzer]] || 321 || 34 || 27 || 33 || 2 || 1 || 9 || 13 || 19 || 10 || 28 || 145
|-
| [[File:Aetheris1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Aetheris_Class_Towed_Howitzer|Aetheris Class Towed Howitzer]] || 941 || 98 || 79 || 95 || 7 || 4 || 27 || 38 || 57 || 31 || 80 || 426
|-
| [[File:Iynas1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Iynas_Class_Field_Gun|Iynas Class Field Gun]] || 753 || 78 || 63 || 76 || 5 || 3 || 22 || 30 || 46 || 25 || 64 || 341
|-
| [[File:Secutor1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#300mm_Secutor_Class_Coastal_Gun|300mm Secutor Class Coastal Gun]] || 516 || 53 || 43 || 52 || 4 || 2 || 15 || 21 || 32 || 17 || 43 || 234
|-
| [[File:Halicarn1226.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#155mm_Halicarn_Class_Self-Propelled_Howitzer|155mm Halicarn Class Self-Propelled Howitzer]] || 373 || 39 || 32 || 38 || 3 || 1 || 10 || 15 || 22 || 12 || 32 || 169
|-
| [[File:Odiferian1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#120mm_Odiferian_Class_Mortar_Carrier|120mm Odiferian Class Mortar Carrier]] || 218 || 23 || 19 || 22 || 1 || 1 || 6 || 8 || 13 || 7 || 19 || 99
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Autonomous systems, prototypes & fixed defenses'''
|-
| [[File:Houndbot231227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Quadwalker_“Horehound”_4170|Quadwalker “Horehound” 4170]] || 495 || 52 || 42 || 50 || 3 || 2 || 14 || 20 || 30 || 16 || 43 || 223
|-
| [[File:Dungbeetle1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Quadwalker_“Dungbeetle”_4172|Quadwalker “Dungbeetle” 4172]] || 361 || 37 || 30 || 36 || 3 || 1 || 10 || 15 || 22 || 12 || 30 || 164
|-
| [[File:KillBot1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Quadwalker_“Killbot”_4200|Quadwalker “Killbot” 4200]] || 734 || 76 || 62 || 74 || 5 || 3 || 21 || 29 || 45 || 24 || 62 || 333
|-
| [[File:Mechaoble1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Quadwalker_“Oble-Lisea”_4189|Quadwalker “Oble-Lisea” 4189]] || 528 || 55 || 44 || 53 || 4 || 2 || 15 || 21 || 32 || 17 || 45 || 239
|-
| [[File:Nexa1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Nexa_Class_Advanced_Prototype|Nexa Class Advanced Prototype]] || 1,095 || 114 || 92 || 110 || 8 || 4 || 31 || 44 || 67 || 36 || 93 || 496
|-
| [[File:GarganiClass1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Gargani_Class_Quadwalker|Gargani Class Quadwalker]] || 683 || 71 || 57 || 69 || 5 || 3 || 19 || 27 || 41 || 22 || 58 || 309
|-
| [[File:AbeisMobile1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis_Class_Advanced_Air_Defense_System_(Mobile_Laser)|Abeis Class Advanced Air Defense System (Mobile Laser)]] || 146 || 16 || 13 || 15 || 1 || 0 || 4 || 5 || 8 || 4 || 13 || 66
|-
| [[File:AbeisStatic1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis_Class_Static_Emplacement_(Fixed_Laser)|Abeis Class Static Emplacement (Fixed Laser)]] || 95 || 11 || 9 || 10 || 0 || 0 || 2 || 3 || 5 || 3 || 9 || 43
|-
| [[File:Fenruvian1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Fenruvian_THAAD_System_Facility|Fenruvian THAAD System Facility]] || 12 || 1 || 1 || 1 || 0 || 0 || 0 || 1 || 1 || 1 || 1 || 5
|-
| [[File:DelphisTower1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Delphis_Class_Coastal_Surveillance_Tower|Delphis Class Coastal Surveillance Tower]] || 28 || 2 || 2 || 2 || 2 || 2 || 2 || 2 || 2 || 2 || 2 || 8
|-
| [[File:AnguillaClass1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Anguilla_Class_Coastal_Missile_Battery|Anguilla Class Coastal Missile Battery]] || 373 || 39 || 32 || 38 || 3 || 1 || 10 || 15 || 22 || 12 || 32 || 169
|-
| [[File:V991227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Model_V-99_Thunderguard_Citadel_Train|Model V-99 Thunderguard Citadel Train]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Naval forces & maritime platforms'''
|-
| [[File:Erigone1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Erigone_Class_Littoral_Orbital_&_Missile_Intercept_Catamaran|Abeis-Erigone Class Littoral Orbital & Missile Intercept Catamaran]] || 12 || 2 || 1 || 2 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 5
|-
| [[File:GenportManf1225.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Empress_Class_Littoral_Battlecruiser|Abeis-Empress Class Littoral Battlecruiser]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Aetherion1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Aetherion Class Multidomain Hover-Corvette|Abeis-Aetherion Class Multidomain Hover-Corvette]]<br> ('''active 1/2/52 [[PSSC]]''') || 4 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 0 || 4
|-
| [[File:KalithrosBarge1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Kalithros-Class_Amphibious_Airstrip_Barge|Kalithros-Class Amphibious Airstrip Barge]] || 12 || 2 || 1 || 2 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 5
|-
| [[File:Aiji1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Aiji_Class_Air-Caravan|Aiji Class Air-Caravan]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Seidolon1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Sideron_Class_Coastal_Battlecruiser|Sideron Class Coastal Battlecruiser]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Eidolon1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Eidolon_Class_Cruiser|Eidolon Class Cruiser]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Acheron1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Acheron_Class_Submersible_Destroyer|Acheron Class Submersible Destroyer]] || 12 || 2 || 1 || 2 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 5
|-
| [[File:Xylanda1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Xylanda_Class_Attack_Submarine|Xylanda Class Attack Submarine]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Bijarian1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Bijarian_Ballistic_Missile_Submarine|Bijarian Ballistic Missile Submarine]] || 12 || 2 || 1 || 2 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 5
|-
| [[File:Vaeringheim1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Vaeringheim_Class_Corvette|Vaeringheim Class Corvette]] || 83 || 9 || 7 || 9 || 0 || 0 || 2 || 3 || 5 || 3 || 7 || 38
|-
| [[File:Sylvapolis1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Sylvapolis_Class_Frigate|Sylvapolis Class Frigate]] || 43 || 5 || 4 || 5 || 0 || 0 || 1 || 1 || 2 || 1 || 4 || 20
|-
| [[File:Atteran1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Atterian_Class_Combat_Ship|Atterian Class Combat Ship]] || 12 || 2 || 1 || 2 || 0 || 0 || 0 || 0 || 0 || 0 || 2 || 5
|-
| [[File:Aetherium1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Aetherium_Class_Patrol_Ship|Aetherium Class Patrol Ship]] || 95 || 11 || 9 || 10 || 0 || 0 || 2 || 3 || 5 || 3 || 9 || 43
|-
| [[File:Velkara1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Velkara_Class_Littoral_Landing_Ship|Velkara Class Littoral Landing Ship]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Saluria1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Saluria_Class_Gunboat|Saluria Class Gunboat]] || 43 || 5 || 4 || 5 || 0 || 0 || 1 || 1 || 2 || 1 || 4 || 20
|-
| [[File:Riceship.jpg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Cetus_Class_Attack_Craft|Cetus Class Attack Craft]] || 64 || 7 || 6 || 7 || 0 || 0 || 1 || 2 || 3 || 2 || 6 || 29
|-
| [[File:Tartarian1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Tartarian_Amphibious_Assault_Craft|Tartarian Amphibious Assault Craft]] || 83 || 9 || 7 || 9 || 0 || 0 || 2 || 3 || 5 || 3 || 7 || 38
|-
| [[File:Ismael1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Ismael_Class_Privateer_Commission|Ismael Class Privateer Commission]] || 50 || 0 || 0 || 0 || 0 || 0 || 0 || 50 || 0 || 0 || 0 || 0
|-
| [[File:Odobenus1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Odobenus_Class_Minelayer|Odobenus Class Minelayer]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Cetomagna1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Cetomagna_Class_Minehunter|Cetomagna Class Minehunter]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
! colspan="14" style="background:#f2f2f2; text-align:left;" | '''Mobile platforms, UAVs & aviation'''
|-
| [[File:Bulhanu1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Bulhanu_Class_High-Speed_Skirmisher_(Hoverbike)|Abeis-Bulhanu Class High-Speed Skirmisher (Hoverbike)]] || 1,290 || 133 || 107 || 129 || 9 || 5 || 37 || 53 || 79 || 43 || 109 || 584
|-
| [[File:Khamsin1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Bulhanu_Khamsin-Class_Desert_Skirmisher|Abeis-Bulhanu Khamsin-Class Desert Skirmisher]] || 1,063 || 110 || 89 || 107 || 7 || 4 || 31 || 43 || 65 || 35 || 90 || 481
|-
| [[File:Virelia1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Bulhanu_Virelia-Class_Urban_Pacifier|Abeis-Bulhanu Virelia-Class Urban Pacifier]] || 671 || 69 || 56 || 67 || 5 || 3 || 19 || 27 || 41 || 22 || 57 || 303
|-
| [[File:Hrimthurs1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Bulhanu_Hrimthurs-Class_Arctic_Hoverbike|Abeis-Bulhanu Hrimthurs-Class Arctic Hoverbike]] || 805 || 84 || 67 || 81 || 5 || 3 || 23 || 33 || 49 || 27 || 69 || 364
|-
| [[File:IsmaelHover1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Ismael_Class_“Cathartes”_Littoral_Hoverbike|Abeis-Ismael Class “Cathartes” Littoral Hoverbike]] || 857 || 89 || 72 || 86 || 6 || 3 || 25 || 35 || 52 || 28 || 73 || 387
|-
| [[File:Strix1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Empress_Class_“Strix-E”_Swarm-Interdictor_Littoral_Hoverbike|Abeis-Empress Class “Strix-E” Swarm-Interdictor Littoral Hoverbike]] || 683 || 71 || 57 || 69 || 5 || 3 || 19 || 27 || 41 || 22 || 58 || 309
|-
| [[File:Minerva1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Minerva_Class_Combat_Littoral_Strike_Aircraft|Abeis-Minerva Class Combat Littoral Strike Aircraft]] || 83 || 9 || 7 || 9 || 0 || 0 || 2 || 3 || 5 || 3 || 7 || 38
|-
| [[File:Boreas1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Abeis-Boreas_Littoral_Dominance_Helicopter|Abeis-Boreas Littoral Dominance Helicopter]] || 134 || 14 || 11 || 14 || 1 || 0 || 4 || 5 || 8 || 4 || 11 || 61
|-
| [[File:AithraFighter1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Aithra_Class_Proximity_Aircraft|Aithra Class Proximity Aircraft]] || 146 || 16 || 13 || 15 || 1 || 0 || 4 || 5 || 8 || 4 || 13 || 66
|-
| [[File:Intercept1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Symphonara_Class_Compound_Intercept|Symphonara Class Compound Intercept]] || 83 || 9 || 7 || 9 || 0 || 0 || 2 || 3 || 5 || 3 || 7 || 38
|-
| [[File:ThalassaCopt1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Thalassa_Class_Attack_Helicopter|Thalassa Class Attack Helicopter]] || 198 || 21 || 17 || 21 || 1 || 1 || 5 || 7 || 11 || 6 || 17 || 90
|-
| [[File:Catonisuav1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Catonis_Class_Unmanned_Aircraft_(UAV)|Catonis Class Unmanned Aircraft (UAV)]] || 95 || 11 || 9 || 10 || 0 || 0 || 2 || 3 || 5 || 3 || 9 || 43
|-
| [[File:Lotos1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Lotos_Class_Tactical_UAV|Lotos Class Tactical UAV]] || 167 || 18 || 14 || 17 || 1 || 1 || 4 || 6 || 10 || 5 || 15 || 75
|-
| [[File:Aurantius1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Aurantius_Class_Multi-Role_UAV|Aurantius Class Multi-Role UAV]] || 167 || 18 || 14 || 17 || 1 || 1 || 4 || 6 || 10 || 5 || 15 || 75
|-
| [[File:SeaBomber1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Chrysos_Class_Sea_Bomber|Chrysos Class Sea Bomber]] || 218 || 23 || 19 || 22 || 1 || 1 || 6 || 8 || 13 || 7 || 19 || 99
|-
| [[File:MisttalonBomber1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Misttalon_Class_Heavy_Bomber|Misttalon Class Heavy Bomber]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Change into a 202512291428.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Orphali_Class_Tanker_Aircraft|Orphali Class Tanker Aircraft]] || 64 || 7 || 6 || 7 || 0 || 0 || 1 || 2 || 3 || 2 || 6 || 29
|-
| [[File:Ventiflor1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Ventiflor_Eye_Class_AEW&C_Aircraft|Ventiflor Eye Class AEW&C Aircraft]] || 31 || 4 || 3 || 3 || 0 || 0 || 1 || 1 || 2 || 1 || 3 || 14
|-
| [[File:Fluxus1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Fluxus_Class_Maritime_Patrol_Aircraft|Fluxus Class Maritime Patrol Aircraft]] || 95 || 11 || 9 || 10 || 0 || 0 || 2 || 3 || 5 || 3 || 9 || 43
|-
| [[File:Hostica1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Hostica_Class_Maritime_Patrol_Aircraft|Hostica Class Maritime Patrol Aircraft]] || 95 || 11 || 9 || 10 || 0 || 0 || 2 || 3 || 5 || 3 || 9 || 43
|-
| [[File:NefelianTransport1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Nefelian_Transport_Plane|Nefelian Transport Plane]] || 115 || 12 || 10 || 12 || 1 || 0 || 3 || 4 || 6 || 3 || 10 || 52
|-
| [[File:Noctiluna1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Noctiluna_Class_Medium_Transport_Helicopter|Noctiluna Class Medium Transport Helicopter]] || 134 || 14 || 11 || 14 || 1 || 0 || 4 || 5 || 8 || 4 || 11 || 61
|-
| [[File:Umbraclaw1227.jpeg|150px|border]] || [[Somniant_Stock_Fund_Military_Hardware#Umbraclaw_Class_Heavy_Transport_Helicopter|Umbraclaw Class Heavy Transport Helicopter]] || 115 || 12 || 10 || 12 || 1 || 0 || 3 || 4 || 6 || 3 || 10 || 52
|}
]=]
--------------------------------------------------------------------------------
-- 2) Column order + mapping
--------------------------------------------------------------------------------
local COL_KEYS = { "VREG","ALPK","ODFR","HAFA","JESR","HMIN","NSJ","HBAS","BNOR","OURN","CKD" }
local COL_TO_DIV = {
VREG="VAE", ALPK="ALP", ODFR="ODF",
HAFA="HFA", JESR="HFA", HBAS="HFA",
HMIN="HAT", NSJ="NSJ", BNOR="BNO", OURN="OUR", CKD="CKD",
}
--------------------------------------------------------------------------------
-- 3) Hatch Ministry always deploys these privateers
--------------------------------------------------------------------------------
local ALWAYS_HATCH_PRIVATEERS = 30
local ISMAEL_UNIT_NAME = "Ismael Class Privateer Commission"
--------------------------------------------------------------------------------
-- 4) Helpers
--------------------------------------------------------------------------------
local function push(t, v) t[#t+1] = v end
local function trim(s) s=tostring(s or ""); return (s:gsub("^%s+",""):gsub("%s+$","")) end
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()
return out:gsub("^,","")
end
local function extractDisplayText(wikilinkCell)
local c=tostring(wikilinkCell or "")
local display=c:match("%[%[[^%]]-%|([^%]]-)%]%]")
if display and display~="" then return display end
display=c:match("%[%[([^%]]-)%]%]")
if display and display~="" then return display end
return trim(c:gsub("[%[%]]",""))
end
local function cellColor(pct)
if pct <= 0.02 then return "#d4edda" end
if pct <= 0.05 then return "#fff3cd" end
if pct <= 0.10 then return "#ffe5cc" end
return "#f8d7da"
end
local function normalizeWikitextTable(text)
local out={}
for line in tostring(text):gmatch("([^\n]*)\n?") do
push(out, line:gsub("^[ \t]+",""))
end
return table.concat(out,"\n")
end
--------------------------------------------------------------------------------
-- 5) Ops parsing helpers
--------------------------------------------------------------------------------
local function sanitizeOpsSegment(seg)
seg=tostring(seg or "")
seg=seg:gsub("<br%s*/?>","\n")
seg=seg:gsub("<[^>]+>","")
seg=seg:gsub("''+","")
seg=seg:gsub("%[%[[^%]|]-|([^%]]-)%]%]","%1")
seg=seg:gsub("%[%[([^%]]-)%]%]","%1")
seg=seg:gsub(" "," ")
seg=seg:gsub("%s+"," ")
return trim(seg)
end
local function parseFormation(seg)
seg = sanitizeOpsSegment(seg)
local divCode = seg:match("%((%u%u%u)%-[%w]+%s*%)%s*$")
if not divCode then return nil,nil end
local echelon = trim(seg:gsub("%(%u%u%u%-%w+%s*%)%s*$",""))
return echelon, divCode
end
local function urgencyScore(urg)
urg=tostring(urg or "")
if urg=="CRITICAL" then return 4 end
if urg=="HIGH" then return 3 end
if urg=="MEDIUM" then return 2 end
if urg=="LOW" then return 1 end
return 0
end
local function parseImpact(impactCell)
local s=tostring(impactCell or "")
local g=s:match("Gnd:%s*([^;]+)") or "OK"
local sea=s:match("Sea:%s*([^;]+)") or "OK"
local air=s:match("Air:%s*([^;]+)") or "OK"
return { ground=trim(g), sea=trim(sea), air=trim(air) }
end
local function impactAllows(imp, domain)
if not imp then return true end
local v = (domain=="sea" and imp.sea) or (domain=="air" and imp.air) or imp.ground
v=tostring(v or "OK"):lower()
if v:find("no%-go",1,true) then return false end
if v:find("closed",1,true) then return false end
return true
end
--------------------------------------------------------------------------------
-- 6) Lua 5.1-safe deterministic picker
--------------------------------------------------------------------------------
local function hash32(s)
s=tostring(s or "")
local h=0
for i=1,#s do
h = (h*131 + s:byte(i)) % 4294967296
end
return h
end
local function pickDeterministic(list, k, salt)
if not list or #list==0 or k<=0 then return {} end
local picked, used = {}, {}
local h = hash32(salt)
for _=1,k do
if #picked >= #list then break end
h = (1103515245*h + 12345) % 4294967296
local idx = (h % #list) + 1
local guard=0
while used[idx] and guard<100 do
h = (1103515245*h + 12345) % 4294967296
idx = (h % #list) + 1
guard=guard+1
end
if not used[idx] then
used[idx]=true
picked[#picked+1]=list[idx]
end
end
return picked
end
--------------------------------------------------------------------------------
-- 7) Baseline TOE templates (your canon set)
--------------------------------------------------------------------------------
local TOE = {
["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,
},
["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,
},
["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,
},
}
--------------------------------------------------------------------------------
-- 8) Coverage enforcement: ensure every unit appears in every TOE template (default 0)
--------------------------------------------------------------------------------
local function collectAllUnitsFromBase(baseText)
local units={}
for line in tostring(baseText):gmatch("[^\n]+") do
if line:match("^|%s*%[%[File:") and line:find("||",1,true) then
local parts = mw.text.split(line:gsub("^|%s*",""), "%s*||%s*")
if #parts == 14 then
local unitName = extractDisplayText(parts[2])
if unitName ~= "" then units[unitName]=true end
end
end
end
return units
end
local function normalizeToeTemplates(allUnits)
for _, toe in pairs(TOE) do
if type(toe)=="table" then
for unitName,_ in pairs(allUnits) do
if toe[unitName]==nil then toe[unitName]=0 end
end
end
end
end
--------------------------------------------------------------------------------
-- 9) Pools + categorization (includes quadwalkers + hover platforms)
--------------------------------------------------------------------------------
local function normName(name)
name=tostring(name or ""):lower()
name=name:gsub("[^a-z0-9%-%s/()]", " "):gsub("%s+"," ")
return trim(name)
end
local function rolesForUnit(unitName)
local n=normName(unitName)
local roles={}
local function add(r) roles[r]=true end
-- Exclude aggregate personnel rows from “breadth rotation” pools
if n=="active units" or n=="reserve units" then add("personnel"); return roles end
-- Small arms
if n:find("rifle",1,true) or n:find("pistol",1,true) or n:find("revolver",1,true)
or n:find("shotgun",1,true) or n:find("submachine",1,true) or n:find("dmr",1,true)
or n:find("sniper",1,true) or n:find("rpg",1,true) then
add("small_arms")
add("all_ground")
end
-- Quadwalkers
if n:find("quadwalker",1,true) then
add("autonomous")
add("all_ground")
end
-- Hover platforms / hovercraft / hoverbikes (explicit)
if n:find("hover",1,true) or n:find("hoverbike",1,true) then
add("hover_mobility")
-- hoverbikes are treated as ground mobility; hover-corvette already gets sea via corvette keyword
add("all_ground")
end
-- Sea hints
if n:find("ship",1,true) or n:find("corvette",1,true) or n:find("frigate",1,true) or n:find("cruiser",1,true)
or n:find("battlecruiser",1,true) or n:find("submarine",1,true) or n:find("submersible",1,true)
or n:find("gunboat",1,true) or n:find("minelayer",1,true) or n:find("minehunter",1,true)
or n:find("landing ship",1,true) or n:find("airstrip barge",1,true) or n:find("privateer",1,true)
or n:find("air%-caravan",1,true) then
add("all_sea")
end
-- Air hints
if n:find("uav",1,true) or n:find("aircraft",1,true) or n:find("helicopter",1,true)
or n:find("bomber",1,true) or n:find("intercept",1,true) or n:find("transport plane",1,true) then
add("all_air")
end
-- Ground vehicles/support (catch-all)
if n:find("tank",1,true) or n:find("armored infantry",1,true) or n:find("recon vehicle",1,true)
or n:find("command vehicle",1,true) or n:find("recovery vehicle",1,true)
or n:find("bridge%-laying",1,true) or n:find("rail%-laying",1,true) or n:find("citadel train",1,true)
or n:find("transport truck",1,true) or n:find("mine%-clearing",1,true) or n:find("engineering",1,true) then
add("all_ground")
end
-- If nothing matched, keep it reachable
local any=false
for _ in pairs(roles) do any=true break end
if not any then add("misc") end
return roles
end
local function buildPools(allUnits)
local pools = { all_any={}, all_ground={}, all_sea={}, all_air={}, misc={}, small_arms={} }
for unitName,_ in pairs(allUnits) do
local n = normName(unitName)
if n ~= "active units" and n ~= "reserve units" then
local roles = rolesForUnit(unitName)
pools.all_any[#pools.all_any+1] = unitName
if roles.all_ground then pools.all_ground[#pools.all_ground+1] = unitName end
if roles.all_sea then pools.all_sea[#pools.all_sea+1] = unitName end
if roles.all_air then pools.all_air[#pools.all_air+1] = unitName end
if roles.misc then pools.misc[#pools.misc+1] = unitName end
if roles.small_arms then pools.small_arms[#pools.small_arms+1] = unitName end
end
end
for _,list in pairs(pools) do table.sort(list) end
return pools
end
--------------------------------------------------------------------------------
-- 10) Category-scaled breadth amounts (big enough to be meaningful)
--------------------------------------------------------------------------------
local function unitCategory(unitName)
local n = normName(unitName)
if n == "active units" or n == "reserve units" then return "personnel" end
if n:find("rifle",1,true) or n:find("pistol",1,true) or n:find("revolver",1,true)
or n:find("shotgun",1,true) or n:find("submachine",1,true) or n:find("dmr",1,true)
or n:find("sniper",1,true) or n:find("rpg",1,true) then
return "small_arms"
end
if n:find("quadwalker",1,true) then return "autonomous" end
if n:find("hover",1,true) or n:find("hoverbike",1,true) then return "hover_mobility" end
if n:find("howitzer",1,true) or n:find("field gun",1,true) or n:find("mortar",1,true)
or n:find("coastal gun",1,true) or n:find("coastal missile",1,true) then
return "fires"
end
if n:find("air defense system",1,true) or n:find("static emplacement",1,true) or n:find("thaad",1,true)
or n:find("surveillance tower",1,true) then
return "fixed_defense"
end
if n:find("tank",1,true) or n:find("armored infantry",1,true) or n:find("recon vehicle",1,true)
or n:find("command vehicle",1,true) or n:find("recovery vehicle",1,true)
or n:find("bridge%-laying",1,true) or n:find("rail%-laying",1,true)
or n:find("transport truck",1,true) or n:find("mine%-clearing",1,true) or n:find("engineering",1,true)
or n:find("citadel train",1,true) then
return "ground_vehicle"
end
if n:find("uav",1,true) or n:find("aircraft",1,true) or n:find("helicopter",1,true)
or n:find("bomber",1,true) or n:find("intercept",1,true) then
return "air"
end
if n:find("ship",1,true) or n:find("corvette",1,true) or n:find("frigate",1,true) or n:find("cruiser",1,true)
or n:find("battlecruiser",1,true) or n:find("submarine",1,true) or n:find("submersible",1,true)
or n:find("gunboat",1,true) or n:find("minelayer",1,true) or n:find("minehunter",1,true)
or n:find("landing ship",1,true) or n:find("airstrip barge",1,true) or n:find("privateer",1,true)
or n:find("air%-caravan",1,true) then
return "sea"
end
return "misc"
end
local function scaleFactor(uScore, bLevel)
local s = 1
if bLevel == 2 then s = 2 end
if bLevel == 3 then s = 4 end
if uScore >= 3 then s = s + 1 end
if uScore >= 4 then s = s + 2 end
return s
end
local function breadthAmountFor(unitName, uScore, bLevel)
local cat = unitCategory(unitName)
local s = scaleFactor(uScore, bLevel)
if cat == "small_arms" then return 150 * s end
if cat == "personnel" then return 500 * s end
if cat == "ground_vehicle" then return math.max(1, math.floor(2 * s)) end
if cat == "autonomous" then return math.max(1, math.floor(2 * s)) end
if cat == "hover_mobility" then return math.max(1, math.floor(3 * s)) end
if cat == "fires" then return math.max(1, math.floor(1 * s)) end
if cat == "fixed_defense" then return math.max(1, math.floor(1 * s)) end
if cat == "air" then return math.max(1, math.floor(1 * s)) end
if cat == "sea" then return math.max(1, math.floor(1 * s)) end
return 1
end
local function breadthLevel(args)
local b=tostring((args and args.breadth) or "standard"):lower()
if b=="low" then return 1 end
if b=="high" then return 3 end
return 2
end
--------------------------------------------------------------------------------
-- 11) Demand accumulation
--------------------------------------------------------------------------------
local function addDemand(util, divCode, unitName, amount)
if not divCode or divCode=="" or not unitName or unitName=="" or not amount or amount<=0 then return end
util[divCode]=util[divCode] or {}
util[divCode][unitName]=(util[divCode][unitName] or 0) + amount
end
--------------------------------------------------------------------------------
-- 12) Ensure small arms never excluded + broad activation for all units
--------------------------------------------------------------------------------
local function applyBreadthActivation(util, pools, divCode, opText, uScore, impact, salt, bLevel)
-- 12.1 Guarantee: some small arms every day
local kSA = (bLevel==1 and 2) or (bLevel==2 and 3) or 5
if uScore >= 3 then kSA = kSA + 1 end
if uScore >= 4 then kSA = kSA + 1 end
if kSA > 6 then kSA = 6 end
local saPicks = pickDeterministic(pools.small_arms, kSA, salt.."|guarantee_small_arms")
for _, u in ipairs(saPicks) do
local base = 150
if bLevel == 2 then base = 250 end
if bLevel == 3 then base = 450 end
if uScore >= 3 then base = base + 150 end
if uScore >= 4 then base = base + 250 end
addDemand(util, divCode, u, base)
end
-- 12.2 Background activation: rotate through broad pools with category-scaled amounts
local baseK = (bLevel==1 and 2) or (bLevel==2 and 4) or 7
if uScore>=3 then baseK = baseK + 2 end
if uScore>=4 then baseK = baseK + 3 end
if baseK > 12 then baseK = 12 end
-- Ground
local gPicks = pickDeterministic(pools.all_ground, baseK, salt.."|bg_ground")
for _, u in ipairs(gPicks) do
addDemand(util, divCode, u, breadthAmountFor(u, uScore, bLevel))
end
-- Sea
if impactAllows(impact,"sea") then
local kSea = math.max(2, math.floor(baseK/2))
local sPicks = pickDeterministic(pools.all_sea, kSea, salt.."|bg_sea")
for _, u in ipairs(sPicks) do
addDemand(util, divCode, u, breadthAmountFor(u, uScore, bLevel))
end
end
-- Air
if impactAllows(impact,"air") then
local kAir = math.max(2, math.floor(baseK/2))
local aPicks = pickDeterministic(pools.all_air, kAir, salt.."|bg_air")
for _, u in ipairs(aPicks) do
addDemand(util, divCode, u, breadthAmountFor(u, uScore, bLevel))
end
end
-- Misc (ensures odd items stay reachable)
local mPicks = pickDeterministic(pools.misc, (bLevel>=2 and 2 or 1), salt.."|bg_misc")
for _, u in ipairs(mPicks) do
addDemand(util, divCode, u, breadthAmountFor(u, uScore, bLevel))
end
end
--------------------------------------------------------------------------------
-- 13) Hatch privateer commissions: div override for this unit in overlay
--------------------------------------------------------------------------------
local function divOverrideForUnit(unitName, colKey, defaultDiv)
if unitName == ISMAEL_UNIT_NAME then
-- The 50 commissions live under NSJ in the stock table; Hatch deploys 30 daily.
-- Treat that column's availability as HAT for this unit only.
if colKey == "NSJ" then return "HAT" end
end
return defaultDiv
end
--------------------------------------------------------------------------------
-- 14) Compute utilization from DailyOps (TOE + breadth activation + hatch privateers)
--------------------------------------------------------------------------------
local function computeUtilization(args, pools)
local util, debugRows = {}, {}
local ok, ops = pcall(require, "Module:WarLeagueDivisionOpsBVR")
if not ok or not ops or type(ops.makeTable) ~= "function" then
return util, debugRows, false
end
local rows = tonumber(args.opsrows) or tonumber(args.rows) or 36
local seed = args.seed
local bLevel = breadthLevel(args)
local tbl = ops.makeTable({ args = {
rows = rows,
seed = seed,
showlegend = "no",
flagsize = args.flagsize or "20",
division = args.division or "",
}})
-- prevent multiplying breadth activation many times per row/div
local activated = {}
local function activateOnce(divCode, opText, uScore, impact, salt)
activated[divCode] = activated[divCode] or {}
if activated[divCode][salt] then return end
activated[divCode][salt] = true
applyBreadthActivation(util, pools, divCode, opText, uScore, impact, salt, bLevel)
end
local function handleCell(forceCell, opText, uScore, impact, saltBase)
forceCell=tostring(forceCell or "—")
if forceCell=="—" or trim(forceCell)=="" then return end
forceCell = forceCell:gsub("<br%s*/?>","\n")
for _,seg in ipairs(mw.text.split(forceCell,"\n")) do
seg=trim(seg)
if seg~="" and seg~="—" then
local echelon, divCode = parseFormation(seg)
if echelon and divCode then
-- baseline TOE demand
local toe = TOE[echelon]
if toe then
for unitName, amount in pairs(toe) do
if amount and amount>0 then addDemand(util, divCode, unitName, amount) end
end
end
-- breadth activation once per div per ops-row domain
activateOnce(divCode, opText, uScore, impact, saltBase.."|"..divCode)
end
end
end
end
for line in tostring(tbl):gmatch("[^\n]+") do
if line:match("^|%s") and line:find("||",1,true) then
local parts = mw.text.split(line:gsub("^|%s*",""), "%s*||%s*")
-- expected: date, division, city, daily event, urgency, ops alert, ops impact, operation, ground, naval, air, ...
if #parts >= 11 then
local dateCell = parts[1] or ""
local urg = parts[5] or ""
local uScore = urgencyScore(urg)
local impact = parseImpact(parts[7] or "")
local opText = parts[8] or ""
local ground = parts[9] or "—"
local naval = parts[10] or "—"
local air = parts[11] or "—"
local salt = tostring(seed or "").."|"..tostring(dateCell).."|"..tostring(parts[2] or "").."|"..tostring(parts[3] or "").."|"..tostring(opText)
push(debugRows, {
date=dateCell, division=parts[2] or "", city=parts[3] or "",
urgency=urg, impact=parts[7] or "", op=opText,
ground=ground, naval=naval, air=air
})
if impactAllows(impact,"ground") then handleCell(ground, opText, uScore, impact, salt.."|G") end
if impactAllows(impact,"sea") then handleCell(naval, opText, uScore, impact, salt.."|N") end
if impactAllows(impact,"air") then handleCell(air, opText, uScore, impact, salt.."|A") end
end
end
end
-- Always-on Hatch Ministry privateer deployment (capped by availability in overlay)
addDemand(util, "HAT", ISMAEL_UNIT_NAME, ALWAYS_HATCH_PRIVATEERS)
return util, debugRows, true
end
--------------------------------------------------------------------------------
-- 15) Overlay highlights (NEVER exceeding availability)
--------------------------------------------------------------------------------
local function overlayTable(baseText, util)
local out={}
local inMain=false
for line in tostring(baseText):gmatch("([^\n]*)\n?") do
if line:match("^%{%|") then
inMain=true
push(out,line)
elseif inMain and line:match("^|%}%s*$") then
inMain=false
push(out,line)
elseif inMain and line:match("^|%s*%[%[File:") and line:find("||",1,true) then
local parts = mw.text.split(line:gsub("^|%s*",""), "%s*||%s*")
if #parts == 14 then
local unitName = extractDisplayText(parts[2])
local divCols, availByIdx = {}, {}
for i=4,14 do
local colKey = COL_KEYS[i-3]
local defaultDiv = COL_TO_DIV[colKey]
local divCode = divOverrideForUnit(unitName, colKey, defaultDiv)
local avail = toNumber(parts[i]) or 0
availByIdx[i]=avail
if divCode then
divCols[divCode]=divCols[divCode] or {}
divCols[divCode][#divCols[divCode]+1] = { idx=i, avail=avail }
end
end
local usedByIdx = {}
for divCode, cols in pairs(divCols) do
local demand = (util[divCode] and util[divCode][unitName]) or 0
if demand > 0 then
local totalAvail = 0
for _,c in ipairs(cols) do totalAvail = totalAvail + (c.avail or 0) end
local usedTotal = math.min(demand, totalAvail)
if totalAvail>0 and usedTotal>0 then
local assigned=0
for _,c in ipairs(cols) do
local share = math.floor(usedTotal * c.avail / totalAvail)
if share > c.avail then share = c.avail end
usedByIdx[c.idx] = share
assigned = assigned + share
end
local rem = usedTotal - assigned
if rem > 0 then
table.sort(cols, function(x,y)
local cx=(x.avail or 0) - (usedByIdx[x.idx] or 0)
local cy=(y.avail or 0) - (usedByIdx[y.idx] or 0)
return cx > cy
end)
local safety=0
while rem>0 and safety<2000 do
safety=safety+1
local progressed=false
for _,c in ipairs(cols) do
if rem<=0 then break end
local cap=(c.avail or 0) - (usedByIdx[c.idx] or 0)
if cap>0 then
usedByIdx[c.idx]=(usedByIdx[c.idx] or 0)+1
rem=rem-1
progressed=true
end
end
if not progressed then break end
end
end
end
end
end
for i=4,14 do
local avail=availByIdx[i] or 0
local used=usedByIdx[i] or 0
if used>avail then used=avail end
if avail>0 and used>0 then
local pct=used/avail; if pct>1 then pct=1 end
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
push(out, "| " .. table.concat(parts, " || "))
else
push(out,line)
end
else
push(out,line)
end
end
push(out,"")
push(out,"<div style=\"margin-top:0.5em; font-size:90%;\">")
push(out,"<b>Daily overview:</b> usage is a capped, per-day estimate derived from War League daily ops plus routine training/maintenance/unpublished activity.")
push(out,"<br/><b>Hard rule:</b> used never exceeds availability in any cell or across a shared ops-division group.")
push(out,"<br/><b>Hatch Ministry:</b> always attempts to deploy 30 <i>Ismael Class Privateer</i> commissions (capped to what exists).")
push(out,"<br/><b>Small arms:</b> always includes a daily mix (never excluded).")
push(out,"</div>")
return table.concat(out,"\n")
end
--------------------------------------------------------------------------------
-- 16) Public entrypoint
--------------------------------------------------------------------------------
function p.render(frame)
local args = (frame and frame.args) or {}
local base = normalizeWikitextTable(RAW_BASE_TABLE)
-- ensure TOE coverage for all inventory rows
local allUnits = collectAllUnitsFromBase(base)
normalizeToeTemplates(allUnits)
-- build pools from the embedded table so all units are reachable
local pools = buildPools(allUnits)
-- compute utilization from ops + breadth + hatch privateers
local util, debugRows, ok = computeUtilization(args, pools)
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
local rendered = overlayTable(base, util)
-- optional debug
if tostring(args.debug or ""):lower() == "yes" then
local max = tonumber(args.debugrows) or 8
local dbg={}
push(dbg,"")
push(dbg,"<div style=\"margin-top:0.75em; font-size:90%;\">")
push(dbg,"<b>Debug (proof of source):</b> Parsed rows from <i>Module:WarLeagueDivisionOpsBVR</i> (Operation/Urgency/Impact + force cells).")
push(dbg,"<ul>")
for i=1, math.min(max, #debugRows) do
local r=debugRows[i]
push(dbg, string.format(
"<li><b>%s</b> | %s | %s | <b>%s</b><br/>Impact: %s<br/>Op: %s<br/>G: %s<br/>N: %s<br/>A: %s</li>",
tostring(r.date or ""), tostring(r.division or ""), tostring(r.city or ""),
tostring(r.urgency or ""), tostring(r.impact or ""), tostring(r.op or ""),
tostring(r.ground or ""), tostring(r.naval or ""), tostring(r.air or "")
))
end
push(dbg,"</ul></div>")
rendered = rendered .. table.concat(dbg,"\n")
end
return rendered
end
return p