; ; /* ============================================================================ RED ASCENDANCY STRUCTURES ---------------------------------------------------------------------------- A separate industrial architecture kit for the Legion/Red Ascendancy. These are not recoloured Nova buildings: every family uses squat armored plinths, oversized mechanisms, exposed heat hardware and red command-view livery. The maps at the bottom deliberately mirror BLD_MDL so old saves keep their simulation type while receiving the correct faction silhouette at render. ============================================================================ */ const LEG_ARM=C(188,178,172), LEG_ARM_D=C(84,73,75); const LEG_PANEL=C(146,136,133), LEG_EDGE=C(215,207,200); const LEG_MACH=C(112,116,124), LEG_TRIM=C(232,218,207); const LEG_RED=C(245,91,62), LEG_HOT=C(255,151,72); const LEG_PAD=C(98,92,90), LEG_BORE=C(12,10,11); COL_MAT.set(LEG_ARM,MAT.TWR_ARMOR); COL_MAT.set(LEG_ARM_D,MAT.TWR_COAT); COL_MAT.set(LEG_PANEL,MAT.TWR_ARMOR); COL_MAT.set(LEG_EDGE,MAT.TWR_MACH); COL_MAT.set(LEG_MACH,MAT.TWR_MACH); COL_MAT.set(LEG_TRIM,MAT.TRIM); COL_MAT.set(LEG_RED,MAT.TWR_ARMOR); COL_MAT.set(LEG_HOT,MAT.TWR_GLOW); COL_MAT.set(LEG_PAD,MAT.TWR_PAD); COL_MAT.set(LEG_BORE,MAT.TWR_BORE); COL_TEAM.add(LEG_RED); function legPad(m,r,h){ r=r||18; h=h||3; m.cyl(0,0,0,r,r-1.8,h,8,LEG_PAD); m.cyl(0,h,0,r-3,r-4.4,2.6,8,LEG_ARM_D); m.ring(0,h+2.62,0,r-5.0,r-3.7,16,LEG_EDGE); for(let k=0;k<8;k++){ const a=k/8*TAU; const x=Math.cos(a)*(r-2.2),z=Math.sin(a)*(r-2.2); m.bevelBox(x,h+0.4,z,3.8,1.5,3.8,.35,k%2?LEG_MACH:LEG_PANEL,a); if(!(k&1)) m.box(x,h+1.92,z,2.2,.20,.55,k===0?LEG_RED:LEG_HOT,a); } return m; } function legHeatStack(m,x,y,z,h,r){ r=r||2.1; m.cyl(x,y,z,r*1.35,r*1.1,h*.30,8,LEG_MACH); m.cyl(x,y+h*.26,z,r,r*.82,h*.66,8,LEG_ARM_D); m.ring(x,y+h*.28,z,r*1.02,r*1.24,10,LEG_EDGE); m.tube(x,y+h*.88,z,r*.9,r*.48,h*.14,8,LEG_BORE); m.box(x,y+h*.43,z+r*.92,r*.36,h*.32,r*.28,LEG_HOT); } function legCore(m,x,y,z,r){ m.cyl(x,y,z,r*1.55,r*1.30,1.6,10,LEG_MACH); m.sphere(x,y+1.5,z,r,10,LEG_HOT,.92,false); m.ring(x,y+1.7,z,r*1.32,r*1.62,18,LEG_RED); } function legGun(m,len,r,z,tier){ const t=tier||1, L=len+(t-1)*2.2; m.bevelBox(-2,0,z||0,9+t*1.1,5+t*.4,8+t*.6,.8,LEG_ARM); m.bevelBox(-2,4.4,z||0,7+t,1.2,5.8,.3,LEG_PANEL); m.box(-2,5.62,z||0,4.4+t*.4,.22,2.2,LEG_RED); for(const s of [-1,1]){ m.bevelBox(-2,1.5,(z||0)+s*(4.15+t*.28),5.6+t*.5,2.3,.58,.22,LEG_PANEL); m.box(-2,3.66,(z||0)+s*(4.47+t*.28),3.2,.18,.62,s>0?LEG_RED:LEG_HOT); } tubeX(m,1.4,2.2,z||0,L,r+(t-1)*.12,(r+(t-1)*.12)*.48,12,LEG_BORE); for(let k=0;k<2+t;k++){ const x=3+k*(L/(2+t)); ringX(m,x,2.2,z||0,r*1.42,r*1.75,12,k===1+t?LEG_HOT:LEG_MACH); } m.box(-5.4,1.1,z||0,2.4,3.5,6.4,LEG_MACH); return m; } function mdlLegTurretBase(tier){ const m=MB(),t=tier||1; legPad(m,15+t,3.2); m.cyl(0,5.6,0,8.2+t*.7,7.2+t*.6,7.2+t,12,LEG_ARM); m.ring(0,11.0+t,0,6.9+t*.55,7.7+t*.58,16,LEG_PANEL); m.cyl(0,12.4+t,0,6.2+t*.45,5.2+t*.4,2.2,12,LEG_MACH); for(const s of [-1,1]){ m.bevelBox(-3,7.0,s*(8.4+t*.5),8+t,4.2,2.5,.45,LEG_PANEL); m.box(-3,10.85,s*(9.68+t*.5),4+t*.4,.22,.55,s>0?LEG_RED:LEG_HOT); } return m.build(); } function mdlLegTurretGun(tier){ const m=MB(); legGun(m,12,1.25,0,tier); return m.build(); } function mdlLegBunkerBase(tier){ const m=MB(),t=tier||1; legPad(m,20+t,3.4); m.bevelBox(0,5.4,0,26+t*2,9+t,22+t*1.5,2.0,LEG_ARM); m.bevelBox(4,13+t,0,16+t*1.4,2.0,15,.45,LEG_PANEL); m.box(4,15.05+t,0,9+t,.24,4.5,LEG_RED); for(const s of [-1,1]){ m.box(-7,6.3,s*11.4,8,5,3,LEG_MACH); legHeatStack(m,-9,10,s*8,8+t,1.6); } return m.build(); } function mdlLegBunkerGun(tier){ const m=MB(),t=tier||1; m.bevelBox(-2,0,0,11+t,5+t*.5,12+t,.9,LEG_ARM); for(const s of [-1,1]) legGun(m,11+t*1.5,.9,s*(2.1+t*.2),t); return m.build(); } function mdlLegBastionBase(tier){ const m=MB(),t=tier||1; legPad(m,22+t*1.2,4); m.cyl(0,7.5,0,13+t,11+t*.8,13+t,12,LEG_ARM_D); m.cyl(0,19+t,0,10+t*.8,9+t*.7,3,12,LEG_MACH); for(let k=0;k<4;k++){ const a=k/4*TAU,x=Math.cos(a)*15,z=Math.sin(a)*15; m.bevelBox(x,5,z,7,5,8,.6,k%2?LEG_ARM:LEG_PANEL,a); m.box(x,9.8,z,3.5,.22,4,k===0?LEG_RED:LEG_HOT,a); } return m.build(); } function mdlLegBastionGun(tier){ const m=MB(),t=tier||1; m.bevelBox(-3,0,0,15+t*1.5,7+t*.8,14+t,1.2,LEG_ARM); for(const s of [-1,1]){ m.bevelBox(-4,2.1,s*(7.1+t*.5),8+t,3.0,.65,.24,LEG_PANEL); m.box(-4,4.92,s*(7.45+t*.5),4+t*.3,.2,.7,s>0?LEG_RED:LEG_HOT); } tubeX(m,2,3.0,0,17+t*2.5,2.0+t*.2,1.05+t*.12,14,LEG_BORE); for(let k=0;k<4;k++) ringX(m,5+k*(4+t*.3),3,0,2.5+t*.22,2.9+t*.22,14,k===3?LEG_HOT:LEG_MACH); for(const s of [-1,1]) m.box(-5,1.2,s*6.4,7,5.5,3.0,LEG_RED); return m.build(); } /* The Ascendancy rail battery used to borrow the concussion bastion body and gun. That saved geometry, but erased the role at phone scale: a recoil-fed penetrator read as another artillery cannon. The dedicated family below is deliberately long and low, with separated rails and exposed capacitor banks. Tier growth adds hardware instead of simply inflating the silhouette. */ function mdlLegRailBase(tier){ const m=MB(),t=tier||1; legPad(m,18+t*1.25,3.6); m.bevelBox(-1,5.2,0,34+t*2.2,6.5,24+t*1.7,1.3,LEG_ARM_D); m.bevelBox(-5,11.5,0,22+t*1.6,3.2,17+t,0.6,LEG_ARM); m.cyl(0,14.7,0,8.0+t*.45,7.2+t*.38,2.2,14,LEG_MACH); m.ring(0,16.92,0,5.8+t*.32,7.0+t*.38,18,LEG_EDGE); for(const s of [-1,1]){ const z=s*(10.1+t*.72); m.bevelBox(-5,7.1,z,13+t,4.4,3.1,.55,LEG_PANEL); for(let k=0;k0?LEG_RED:LEG_HOT); } m.box(-8,2.4,0,3.2,5.4,7.2,LEG_MACH); return m.build(); } function mdlLegAA(tier){ const m=MB(),t=tier||1; legPad(m,15+t,3); m.cyl(0,5,0,8+t*.35,7+t*.25,7+t,12,LEG_ARM); m.cyl(0,12+t,0,5.4,5.4,2,12,LEG_MACH); legCore(m,0,14+t,0,2.1); if(t>=3) m.box(0,13+t,0,1.0,.18,6.4,LEG_RED); return m.build(); } function mdlLegAAGun(tier){ const m=MB(),t=tier||1; m.bevelBox(-2,0,0,10+t*.4,4.5+t*.25,11+t*.35,.8,LEG_ARM); for(const s of [-1,1]) m.bevelBox(-2,1.1,s*5.55,6.2,2.2,.55,.18,LEG_PANEL); for(const s of [-1,1]) for(const q of [-1,1]) tubeX(m,1.5,2+q*1.1,s*2.6,11+t,.62,.30,10,LEG_BORE); m.box(-4,3.8,0,3,2,7,LEG_RED); if(t>=3) m.box(-4,5.4,0,2.2,.18,5.2,LEG_HOT); return m.build(); } function mdlLegMiningBase(tier){ return mdlLegTurretBase((tier||1)+1); } function mdlLegMiningGun(tier){ const m=MB(),t=tier||1; m.bevelBox(-2,0,0,12+t,6+t*.5,10+t,.9,LEG_ARM); for(const s of [-1,1]) m.bevelBox(-3,1.5,s*(5.15+t*.5),7+t,2.8,.58,.2,LEG_PANEL); tubeX(m,2,2.8,0,14+t*2,1.7+t*.15,.72+t*.08,14,LEG_BORE); for(let k=0;k<4+t;k++) ringX(m,4+k*2.5,2.8,0,2.0+t*.1,2.35+t*.1,14,k%2?LEG_HOT:LEG_RED); return m.build(); } function mdlLegHellfireBase(tier){ const m=MB(),t=tier||1; legPad(m,18+t,3.5); m.cyl(0,6,0,10+t,8.5+t,10,12,LEG_ARM); m.cyl(0,16,0,7.2+t*.3,6.6+t*.25,1.8,14,LEG_ARM_D); m.ring(0,17.82,0,4.8+t*.2,6.2+t*.25,16,LEG_EDGE); for(const s of [-1,1]){ m.bevelBox(-4,10,s*(10.7+t*.25),5,5,3,.4,LEG_PANEL); m.box(-4,14.8,s*(12.22+t*.25),2.6,.20,.6,s>0?LEG_RED:LEG_HOT); } return m.build(); } function mdlLegHellfireTur(tier){ const m=MB(),t=tier||1; m.cyl(0,0,0,6.7+t*.25,6.2+t*.2,2.0,14,LEG_MACH); m.bevelBox(-1,2,0,13+t,5+t*.5,12+t,1,LEG_ARM); m.bevelBox(-2,6.3+t*.4,0,8+t*.6,1.2,8+t*.5,.25,LEG_PANEL); for(const s of [-1,1]) for(const q of [-1,0,1]) tubeX(m,2,4.5+q*1.45,s*2.9,12+t*1.5,.6,.28,9,LEG_BORE); for(const s of [-1,1]) m.box(-4,6.6,s*(6.05+t*.5),2.8,.20,.55,s>0?LEG_RED:LEG_HOT); return m.build(); } function mdlLegArc(tier){ const m=MB(),t=tier||1; legPad(m,17+t,3.4); m.cyl(0,6,0,10+t,7+t*.7,8+t,10,LEG_ARM_D); for(let k=0;k<4+t;k++){ const a=k/(4+t)*TAU,rr=8+t*.6; m.wedge(Math.cos(a)*rr,8,Math.sin(a)*rr,4,12+t*2,3,LEG_ARM,a+Math.PI/2); } m.cyl(0,13+t,0,4+t*.4,2.2,9+t*2,10,LEG_MACH); legCore(m,0,23+t*3,0,3+t*.35); return m.build(); } function mdlLegBarrier(tier){ const m=MB(),t=tier||1; legPad(m,17+t,3.4); m.cyl(0,6,0,10+t,7+t,9+t,10,LEG_ARM); for(let k=0;k<6;k++){const a=k/6*TAU;m.wedge(Math.cos(a)*(11+t),5,Math.sin(a)*(11+t),4,16+t*2,4,LEG_ARM_D,a);} legCore(m,0,16+t,0,4+t*.5); m.ring(0,17+t,0,7+t,8+t,24,LEG_HOT); return m.build(); } function mdlLegUplink(tier){ const m=MB(),t=tier||1; legPad(m,15+t,3); m.box(0,5,0,13+t,9+t,12+t,LEG_ARM); m.bevelBox(0,14+t,0,10+t,2,9,.35,LEG_PANEL); for(const s of [-1,1]) m.box(0,10,s*(6.05+t*.5),6,.25,.45,s>0?LEG_RED:LEG_HOT); m.cyl(0,16+t,0,1.3,1.0,12+t*3,8,LEG_MACH); m.ring(0,24+t*3,0,6+t,8+t,24,LEG_TRIM); legCore(m,0,24+t*3,0,1.8+t*.3); return m.build(); } function mdlLegMissileBase(tier){ const m=MB(),t=tier||1; legPad(m,22+t,4); m.bevelBox(0,7,0,27+t*2,8,23+t,1.6,LEG_ARM); for(const s of [-1,1]){ m.bevelBox(-8,10,s*(11.65+t*.5),8,4,.62,.22,LEG_PANEL); m.box(-8,13.75,s*(12+t*.5),4,.2,.68,s>0?LEG_RED:LEG_HOT); } m.cyl(0,15,0,9.2+t*.35,8.5+t*.3,2.0,16,LEG_ARM_D); m.ring(0,17.02,0,6.3+t*.25,8.0+t*.3,18,LEG_EDGE); return m.build(); } function mdlLegMissileTur(tier){ const m=MB(),t=tier||1; m.cyl(0,0,0,8.6+t*.3,8.0+t*.25,2.0,16,LEG_MACH); m.bevelBox(-2,2,0,17+t*1.4,8+t*.5,17+t,1.2,LEG_PANEL); const rows=2+t,cols=2+t; for(let r=0;r0?LEG_RED:LEG_HOT); } m.cyl(2,14.5,0,11.5+t*.35,10.3+t*.3,3.1,16,LEG_PANEL); m.ring(2,17.62,0,7.6+t*.25,9.8+t*.3,20,LEG_EDGE); for(let k=0;k<2+t;k++){ const a=k/(2+t)*TAU,x=-13,z=Math.sin(a)*9; m.cyl(x,7.5,z,1.5,1.25,6.0,9,LEG_MACH); m.ring(x,13.52,z,.85,1.28,10,k===t?LEG_HOT:LEG_RED); } return m.build(); } function mdlLegNovaTur(tier){ const m=MB(),t=tier||1; m.cyl(0,0,0,10.2+t*.28,9.4+t*.25,2.3,16,LEG_MACH); m.bevelBox(-2,2.3,0,18+t,8.0+t*.45,17+t*.55,1.25,LEG_ARM); const cells=t===1?[-3.2,3.2]:t===2?[-4.1,0,4.1]:[-5.1,-1.7,1.7,5.1]; for(let i=0;i0?LEG_RED:LEG_HOT); } }else if(kind==='pgen'||kind==='geo'){ m.cyl(0,6,0,12,9,15,12,LEG_ARM); m.cyl(0,21,0,7,4,6,12,LEG_MACH); legCore(m,0,27,0,3.5); for(let k=0;k<4;k++){const a=k/4*TAU;legHeatStack(m,Math.cos(a)*12,5,Math.sin(a)*12,12,1.8);} }else if(kind==='silo'){ for(const s of [-1,1]){m.cyl(s*7,5,0,6,5,25,10,LEG_ARM);m.cyl(s*7,30,0,5,2,5,10,LEG_RED);} legHeatStack(m,0,5,-8,20,2); }else{ m.bevelBox(-2,5,0,25,14,23,1.5,LEG_ARM); m.bevelBox(-2,19,0,21,3,18,.5,LEG_PANEL); m.box(-2,22.05,0,12,.24,4.5,LEG_RED); legHeatStack(m,11,5,8,22,2.5); legHeatStack(m,11,5,-8,18,2.2); } return m.build(); } function mdlLegFactory(kind){ const m=MB(),large=kind==='hq'||kind==='tgate',w=large?72:kind==='airfield'?82:56,d=large?60:kind==='airfield'?38:46; m.bevelBox(0,0,0,w,5,d,2,LEG_PAD); m.bevelBox(-5,5,0,w-12,large?26:19,d-10,2.4,LEG_ARM); m.bevelBox(-5,large?31:24,0,w-22,3,d-18,.55,LEG_PANEL); m.box(-5,large?34.05:27.05,0,w*.32,.24,d*.18,LEG_RED); for(const s of [-1,1]){m.box(-w*.34,7,s*(d*.40),w*.18,large?22:15,4,LEG_MACH);legHeatStack(m,w*.30,5,s*(d*.32),large?26:19,2.2);} if(kind==='airfield'||kind==='harbor') m.box(w*.24,5,0,w*.38,3,d*.64,LEG_MACH); if(kind==='techlab'){ legCore(m,4,26,0,5); m.ring(4,28,0,9,11,28,LEG_HOT); } if(kind==='tgate'){ m.ring(5,33,0,16,20,30,LEG_HOT); m.ring(5,33,0,21,23,30,LEG_MACH); } if(kind==='hq'){ m.bevelBox(4,34,0,31,14,30,2,LEG_ARM); m.bevelBox(4,48,0,24,4,22,.6,LEG_PANEL); m.box(4,52.05,0,13,.25,5.5,LEG_RED); } return m.build(); } function mdlLegWall(){ const m=MB();m.bevelBox(0,0,0,34,8,16,1.5,LEG_PAD);m.bevelBox(0,8,0,30,11,12,1.4,LEG_ARM); m.bevelBox(0,10.5,-6.1,15,5,.5,.16,LEG_ARM_D); m.bevelBox(0,19,0,24,3,8,.45,LEG_PANEL);m.box(0,22.05,0,12,.22,2.2,LEG_RED); for(const s of [-1,1]){ m.box(s*11,11,s*6.05,4,5,.3,LEG_EDGE); m.box(s*11,15.4,s*6.24,1.6,.22,.36,LEG_HOT); } return m.build(); } function mdlLegGate(){ const m=MB();m.bevelBox(0,0,0,42,5,18,1.4,LEG_PAD); for(const s of [-1,1]){ m.bevelBox(s*16,5,0,9,22,14,1.4,LEG_ARM);m.bevelBox(s*16,27,0,7,3,10,.4,LEG_PANEL); m.box(s*16,30.05,0,3.4,.22,3,s>0?LEG_RED:LEG_HOT); } m.bevelBox(0,5,0,22,13,5,.35,LEG_ARM_D);m.box(0,17.8,0,11,.22,5.2,LEG_EDGE);return m.build(); } function mdlLegSeafortBase(tier){ const m=MB(),t=tier||1; m.bevelBox(0,0,0,42,4.2,36,1.6,LEG_PAD); m.bevelBox(0,4.2,0,38,2.0,32,.55,LEG_PANEL); for(const s of [-1,1]){ m.bevelBox(-4,0,s*16.8,30,3.4,5.2,.7,LEG_ARM); m.box(-4,3.42,s*16.95,16,.22,1.6,s>0?LEG_RED:LEG_HOT); legHeatStack(m,-14,4.2,s*12.5,9+t,1.7); } m.cyl(0,6.2,0,11+t*.3,9.5+t*.25,8.5,12,LEG_ARM_D); m.cyl(0,14.5,0,8.4+t*.25,7.6+t*.2,2.0,14,LEG_MACH); m.ring(0,16.52,0,5.8+t*.2,7.4+t*.22,16,LEG_EDGE); return m.build(); } function mdlLegSeafortGun(tier){ const m=MB(),t=tier||1; m.bevelBox(-2,0,0,13+t,5.6+t*.4,13+t,.9,LEG_ARM); tubeX(m,2,2.6,0,14+t*1.6,1.85+t*.12,.92+t*.08,12,LEG_BORE); for(let k=0;k<3;k++) ringX(m,5+k*3.2,2.6,0,2.2+t*.1,2.55+t*.1,12,k===2?LEG_HOT:LEG_MACH); for(const s of [-1,1]){ m.bevelBox(-3,1.4,s*(6.4+t*.3),7,2.4,.55,.2,LEG_PANEL); tubeX(m,1.2,4.8,s*3.0,11+t,0.62,0.30,9,LEG_BORE); m.box(-3,3.72,s*(6.7+t*.3),3.6,.2,.62,s>0?LEG_RED:LEG_HOT); } m.box(-5.4,4.6,0,2.8,.22,6.4,LEG_RED); return m.build(); } function mdlLegStormBase(tier){ const m=MB(),t=tier||1; legPad(m,20+t*.4,3.8); m.bevelBox(0,6.4,0,34+t,7.2,28+t,1.4,LEG_ARM_D); for(const s of [-1,1]){ m.bevelBox(-2,10,s*(12.6+t*.3),16,5.2,4.0,.55,LEG_PANEL); for(const x of [-6,2,10]){ m.cyl(x,14.8,s*(12.6+t*.3),1.35,1.1,4.2+t*.6,8,LEG_MACH); m.tube(x,18.9+t*.6,s*(12.6+t*.3),1.15,.55,.45,8,LEG_BORE); m.ring(x,19.5+t*.6,s*(12.6+t*.3),.7,1.1,10,x===2?LEG_HOT:LEG_RED); } } m.cyl(0,13.4,0,8.8+t*.25,8.0+t*.2,2.2,14,LEG_MACH); m.ring(0,15.62,0,6.2+t*.2,8.0+t*.22,16,LEG_EDGE); return m.build(); } function mdlLegStormTur(tier){ const m=MB(),t=tier||1; m.cyl(0,0,0,8.2+t*.2,7.6+t*.18,2.0,14,LEG_MACH); m.bevelBox(-2,2,0,16+t,7.2+t*.4,16+t,.95,LEG_ARM); for(let r=0;r<4;r++) for(let c=0;c<4;c++){ const y=4.2+(r-1.5)*2.05,z=(c-1.5)*3.15; m.bevelBox(3.6,y-1.05,z,3.4,2.15,2.4,.28,LEG_PANEL); tubeX(m,5.2,y,z,2.2,1.12,.62,8,LEG_BORE); } m.box(-7.2,5.4,0,3.2,5.6,10.5,LEG_ARM_D); m.box(-7.2,10.9,0,1.4,.22,6.4,LEG_RED); return m.build(); } function mdlLegNest(){ /* Nest is Brood-owned at runtime (bldFactionKey forces horde). This Legion fallback used to alias HQ — a 72-wide keep for a hatchery slot. A sealed quarantine redoubt keeps the catalogue readable as Dominion iron without growing tissue or cloning the command keep. */ const m=MB(); legPad(m,20,3.4); m.bevelBox(0,5.4,0,26,9,22,1.8,LEG_ARM); m.bevelBox(0,14.4,0,20,1.8,16,.45,LEG_PANEL); m.box(0,16.25,0,10,.22,4.2,LEG_RED); m.cyl(0,16.2,0,6.4,5.2,5.5,12,LEG_MACH); m.cyl(0,21.7,0,4.6,3.0,2.0,12,LEG_PANEL); m.tube(0,23.7,0,3.0,1.4,1.2,10,LEG_BORE); m.ring(0,24.9,0,1.8,3.2,14,LEG_HOT); for(const s of [-1,1]){ m.bevelBox(s*11.2,6.2,0,5.6,7.0,8.0,.6,LEG_PANEL); m.box(s*11.2,13.15,0,3.2,.22,2.0,s>0?LEG_RED:LEG_HOT); legHeatStack(m,s*11.2,13.4,s*6.0,8,1.5); } return m.build(); } const BLD_MDL_LEGION={ mex:()=>mdlLegEconomy('mex'),pgen:()=>mdlLegEconomy('pgen'),geo:()=>mdlLegEconomy('geo'),silo:()=>mdlLegEconomy('silo'),fab:()=>mdlLegEconomy('fab'), fac:()=>mdlLegFactory('fac'),tgate:()=>mdlLegFactory('tgate'),harbor:()=>mdlLegFactory('harbor'),airfield:()=>mdlLegFactory('airfield'), techlab:()=>mdlLegFactory('techlab'),hq:()=>mdlLegFactory('hq'),nest:()=>mdlLegNest(), turret:mdlLegTurretBase,bunker:mdlLegBunkerBase,bastion:mdlLegBastionBase,aatower:mdlLegAA, minelaser:mdlLegMiningBase,missilebastion:mdlLegMissileBase,hellstorm:mdlLegHellfireBase,arc:mdlLegArc, sgen:mdlLegBarrier,uplink:mdlLegUplink,nova:mdlLegNovaBase,plasma:mdlLegPlasma, rail:mdlLegRailBase,wall:mdlLegWall,gate:mdlLegGate, seafort:mdlLegSeafortBase,stormcaller:mdlLegStormBase, }; const BLD_TUR_MDL_LEGION={ turret:mdlLegTurretGun,bunker:mdlLegBunkerGun,bastion:mdlLegBastionGun, aatower:mdlLegAAGun,minelaser:mdlLegMiningGun,rail:mdlLegRailTur, hellstorm:mdlLegHellfireTur,nova:mdlLegNovaTur,missilebastion:mdlLegMissileTur, seafort:mdlLegSeafortGun,stormcaller:mdlLegStormTur, }; const BLD_TUR_H_LEGION={ turret:14,bunker:17,bastion:22,aatower:12,minelaser:15,rail:17, hellstorm:17.8,nova:17.7,missilebastion:17,seafort:16.5,stormcaller:15.6, }; const BLD_TUR_S_LEGION={ turret:1.13,bunker:1.10,bastion:1.11,aatower:1.12,minelaser:1.10,rail:1.0, hellstorm:1.0,nova:1.0,missilebastion:1.0,seafort:1.08,stormcaller:1.05, }; /* Stage D1 — isolated Legion landmark packs. These are per-building semantic bakes, not a faction-wide colour wash: the shared battle material still owns lighting/AO/microdetail, while each landmark chooses its own armor, machine and heat vocabulary. `maps:null` deliberately records that no bespoke authored UV BaseAO/NRE/mask export exists yet. Keeping the selector here, next to the actual builders, makes a later authored-map replacement local and avoids changing the generic building renderer. */ const DOM_LEGION_STRUCTURE_PACKS=Object.freeze({ hq:Object.freeze({ id:'legion-hq-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), fac:Object.freeze({ id:'legion-fac-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), techlab:Object.freeze({ id:'legion-techlab-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), pgen:Object.freeze({ id:'legion-pgen-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_SIEGE, [MAT.TWR_MACH]:MAT.LEGION_RIVET,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), mex:Object.freeze({ id:'legion-mex-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), geo:Object.freeze({ id:'legion-geo-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), airfield:Object.freeze({ id:'legion-airfield-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), rail:Object.freeze({ id:'legion-rail-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), uplink:Object.freeze({ id:'legion-uplink-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), turret:Object.freeze({ id:'legion-turret-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), bunker:Object.freeze({ id:'legion-bunker-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), bastion:Object.freeze({ id:'legion-bastion-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), aatower:Object.freeze({ id:'legion-aatower-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), minelaser:Object.freeze({ id:'legion-minelaser-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), missilebastion:Object.freeze({ id:'legion-missilebastion-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), hellstorm:Object.freeze({ id:'legion-hellstorm-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), seafort:Object.freeze({ id:'legion-seafort-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), stormcaller:Object.freeze({ id:'legion-stormcaller-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), arc:Object.freeze({ id:'legion-arc-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), sgen:Object.freeze({ id:'legion-sgen-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), plasma:Object.freeze({ id:'legion-plasma-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), wall:Object.freeze({ id:'legion-wall-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), gate:Object.freeze({ id:'legion-gate-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), nest:Object.freeze({ id:'legion-nest-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), harbor:Object.freeze({ id:'legion-harbor-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), tgate:Object.freeze({ id:'legion-tgate-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), silo:Object.freeze({ id:'legion-silo-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), fab:Object.freeze({ id:'legion-fab-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_RIVET,[MAT.TWR_COAT]:MAT.LEGION_CAST, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }), nova:Object.freeze({ id:'legion-nova-v2',source:'semantic-bake', maps:null, surfaces:Object.freeze({ [MAT.TWR_ARMOR]:MAT.LEGION_CAST,[MAT.TWR_COAT]:MAT.LEGION_RIVET, [MAT.TWR_MACH]:MAT.LEGION_SIEGE,[MAT.TWR_GLOW]:MAT.LEGION_THERMITE, [MAT.TWR_PAD]:MAT.LEGION_SIEGE,[MAT.TRIM]:MAT.LEGION_RIVET }) }) }); function domLegionStructureSurfacePass(geo,pack){ if(!geo||!geo.v||!pack)return geo; const v=geo.v; for(let o=11;o({base:()=>BLD_MDL_LEGION[k](t),tur:()=>BLD_TUR_MDL_LEGION[k](t)})); for(const k of ['hellstorm','nova','missilebastion','seafort','stormcaller']) BLD_TIER_MDL_LEGION[k]=[1,2,3].map(t=>({base:()=>BLD_MDL_LEGION[k](t),tur:()=>BLD_TUR_MDL_LEGION[k](t)})); for(const k of ['arc','sgen','uplink','plasma']) BLD_TIER_MDL_LEGION[k]=[1,2,3].map(t=>({base:()=>BLD_MDL_LEGION[k](t)}));