Back to GRAV / RIFT
SOURCE / PINNED RELEASE

Made of little things.

GRAV / RIFT

Release
288aa261ce07…
Author-recorded commit
c549b5a4e17e…
License
Check the original source
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-8a7361981e8

The hash-verified playable HTML, which may contain bundled or minified code. This view does not execute it.

index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
  <meta name="theme-color" content="#0a1113" />
  <title>GRAV / RIFT</title>
  <script type="module" crossorigin>var qt=Object.defineProperty;var Kt=(i,t,e)=>t in i?qt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var g=(i,t,e)=>Kt(i,typeof t!="symbol"?t+"":t,e);function Xt(){const i=window;if(!i.napplet)throw new Error("window.napplet is unavailable -- runtime did not inject the napplet namespace");return i.napplet}function C(i){const e=Xt()[i];if(!e)throw new Error(`window.napplet.${String(i)} is unavailable -- runtime did not inject this domain`);return e}var bt={getItem(i){return C("storage").getItem(i)},setItem(i,t){return C("storage").setItem(i,t)},removeItem(i){return C("storage").removeItem(i)},keys(){return C("storage").keys()},instance:{getItem(i){return C("storage").instance.getItem(i)},setItem(i,t){return C("storage").instance.setItem(i,t)},removeItem(i){return C("storage").instance.removeItem(i)},keys(){return C("storage").instance.keys()}}},Vt={createSession(i){return C("media").createSession(i)},updateSession(i,t){C("media").updateSession(i,t)},destroySession(i){C("media").destroySession(i)},reportState(i,t){C("media").reportState(i,t)},reportCapabilities(i,t){C("media").reportCapabilities(i,t)},sendCommand(i,t,e){C("media").sendCommand(i,t,e)},onCommand(i,t){return C("media").onCommand(i,t)},onState(i,t){return C("media").onState(i,t)},onCapabilities(i,t){return C("media").onCapabilities(i,t)},onControls(i,t){return C("media").onControls(i,t)}};function zt(i,t){return!!(i!=null&&i[t])}function ct(i){return zt(window.napplet,i)}class jt{constructor(t,e,s={}){g(this,"context");g(this,"output");g(this,"session","");g(this,"pending",!1);g(this,"blocked",!1);g(this,"disposed",!1);g(this,"muted",!1);g(this,"volume",1);g(this,"stopped",!1);g(this,"listener");this.media=t,this.title=e,this.options=s}activate(){var t,e;if(!this.media||this.disposed||this.blocked)return!1;try{return this.context||(this.context=((e=(t=this.options).createContext)==null?void 0:e.call(t))??new AudioContext,this.output=this.context.createGain(),this.output.gain.value=0,this.output.connect(this.context.destination),this.context.onstatechange=()=>this.report()),!this.session&&!this.pending&&(this.pending=!0,this.register()),this.context.state==="suspended"&&this.resume(),this.audible}catch{return this.dispose(),!1}}get audible(){var t;return!!this.session&&!this.disposed&&!this.blocked&&!this.muted&&((t=this.context)==null?void 0:t.state)==="running"}setMuted(t){this.muted=t,this.applyGain(),this.report()}applyGain(){this.output&&(this.output.gain.value=this.audible?this.volume:0)}report(){var t,e;if(this.applyGain(),(e=(t=this.options).onChange)==null||e.call(t),!(!this.session||this.disposed))try{this.media.reportState(this.session,{status:this.audible?"playing":this.stopped?"stopped":"paused",volume:this.muted?0:this.volume})}catch{this.dispose()}}async resume(){var t;try{await((t=this.context)==null?void 0:t.resume()),this.report()}catch{this.applyGain()}}async register(){try{const t=await this.media.createSession({owner:"napplet",metadata:{title:this.title,mediaType:"audio"},capabilities:["play","pause","stop","volume"]});if(this.disposed){t.sessionId&&this.media.destroySession(t.sessionId);return}if(t.error||!t.sessionId||t.owner!=="napplet"){t.sessionId&&this.media.destroySession(t.sessionId),this.dispose();return}this.session=t.sessionId,this.listener=this.media.onCommand(this.session,(e,s)=>{var a;this.disposed||(e==="pause"||e==="stop"?(this.blocked=!0,this.stopped=e==="stop",this.applyGain(),(a=this.context)==null||a.suspend().then(()=>this.report()).catch(()=>{})):e==="play"?(this.blocked=!1,this.stopped=!1,this.resume()):e==="volume"&&typeof s=="number"&&Number.isFinite(s)&&(this.volume=Math.max(0,Math.min(1,s))),this.report())}),this.report()}catch{this.dispose()}finally{this.pending=!1}}dispose(){var t;if(!this.disposed){this.disposed=!0,this.applyGain();try{(t=this.listener)==null||t.close()}catch{}if(this.context&&(this.context.onstatechange=null,this.context.close().catch(()=>{})),this.session)try{this.media.destroySession(this.session)}catch{}this.session=""}}}const A=(i,t,e)=>Math.max(t,Math.min(e,i)),k=i=>Math.hypot(i.x,i.y),w=(i,t)=>({x:i.x-t.x,y:i.y-t.y}),X=(i,t)=>i.x*t.x+i.y*t.y,K=(i,t)=>i.x*t.y-i.y*t.x,N=i=>{const t=k(i)||1;return{x:i.x/t,y:i.y/t}};function j(i,t){const e=Math.cos(t),s=Math.sin(t);return{x:i.x*e-i.y*s,y:i.x*s+i.y*e}}function Jt(i){const t=[...new Map(i.map(r=>[`${r.x},${r.y}`,r])).values()].sort((r,h)=>r.x-h.x||r.y-h.y);if(t.length<3)return t;const e=r=>{const h=[];for(const l of r){for(;h.length>=2&&K(w(h.at(-1),h.at(-2)),w(l,h.at(-1)))<=0;)h.pop();h.push(l)}return h},s=e(t),a=e([...t].reverse());return[...s.slice(0,-1),...a.slice(0,-1)]}function _t(i,t,e,s=1){const a=[];for(let r=0;r<e;r++)for(let h=0;h<t;h++)if(!(i[(r*t+h)*4+3]<128))for(const[l,d]of[[0,0],[1,0],[1,1],[0,1]])a.push({x:(h+l-t/2)*s,y:(r+d-e/2)*s});return Jt(a)}let Qt=0;function Zt(i,t,e,s,a,r=1,h=1){const l=Math.max(...e.map(k),1);return{id:Qt++,kind:i,sprite:t,hull:e,x:s,y:a,homeY:a,vx:0,vy:0,angle:0,omega:0,radius:l,mass:r,invMass:1/r,invInertia:1/(r*l*l*.5),hp:30,maxHp:30,held:!1,thrown:0,dead:!1,age:0,scale:h,attached:!1,stress:0,cooldown:0,volley:0,armed:!1}}function Lt(i){const t=Math.sin(i.angle),e=Math.cos(i.angle),s=i.hull.map(a=>a.x*t+a.y*e);return{min:Math.min(...s),max:Math.max(...s)}}function ot(i){return i.hull.map(t=>{const e=j(t,i.angle);return{x:e.x+i.x,y:e.y+i.y}})}function*te(i){const t=new Map;for(const s of i){const a=s.held?"held":s.attached?"rig":s.thrown>0?`volley:${s.volley}`:"loose",r=t.get(a);r?r.push(s):t.set(a,[s])}const e=[...t];for(let s=0;s<e.length;s++)for(let a=s;a<e.length;a++){const[r,h]=e[s],[l,d]=e[a];if(!(s===a&&r!=="loose")&&!(r==="held"&&l==="rig"||r==="rig"&&l==="held"))for(let u=0;u<h.length;u++){const p=h[u];for(let m=s===a?u+1:0;m<d.length;m++){const n=d[m],f=p.radius+n.radius;!p.dead&&!n.dead&&!(p.rigId!==void 0&&p.rigId===n.rigId)&&!((p.launchGrace??0)>0&&n.held||(n.launchGrace??0)>0&&p.held)&&(p.x-n.x)**2+(p.y-n.y)**2<=f**2&&(yield[p,n])}}}}function ee(i,t){if(Math.hypot(i.x-t.x,i.y-t.y)>i.radius+t.radius)return null;const e=ot(i),s=ot(t);let a=1/0,r={x:1,y:0};for(const u of[e,s])for(let p=0;p<u.length;p++){const m=w(u[(p+1)%u.length],u[p]),n=N({x:-m.y,y:m.x}),f=e.map(M=>X(M,n)),b=s.map(M=>X(M,n)),o=Math.min(Math.max(...f),Math.max(...b))-Math.max(Math.min(...f),Math.min(...b));if(o<=0)return null;o<a&&(a=o,r=n)}X(w(t,i),r)<0&&(r={x:-r.x,y:-r.y});const h=(u,p)=>{const m=Math.max(...u.map(f=>X(f,p))),n=u.filter(f=>m-X(f,p)<.5);return{x:n.reduce((f,b)=>f+b.x,0)/n.length,y:n.reduce((f,b)=>f+b.y,0)/n.length}},l=h(e,r),d=h(s,{x:-r.x,y:-r.y});return{normal:r,depth:a,point:{x:(l.x+d.x)/2,y:(l.y+d.y)/2}}}function se(i,t,e){const s=e.normal,a=w(e.point,i),r=w(e.point,t),h=i.attached?.025:i.invMass,l=t.attached?.025:t.invMass,d=i.attached?0:i.invInertia,u=t.attached?0:t.invInertia,p={x:t.vx-t.omega*r.y-i.vx+i.omega*a.y,y:t.vy+t.omega*r.x-i.vy-i.omega*a.x},m=X(p,s),n=Math.max(0,e.depth-.2)*.7/(h+l);if(i.x-=s.x*n*h,i.y-=s.y*n*h,t.x+=s.x*n*l,t.y+=s.y*n*l,m>=0)return 0;const f=-1.32*m/(h+l+K(a,s)**2*d+K(r,s)**2*u),b=(E,S)=>{i.vx-=E.x*S*h,i.vy-=E.y*S*h,i.omega-=K(a,E)*S*d,t.vx+=E.x*S*l,t.vy+=E.y*S*l,t.omega+=K(r,E)*S*u};b(s,f);const o=N({x:p.x-m*s.x,y:p.y-m*s.y}),M=-X(p,o)/(h+l+K(a,o)**2*d+K(r,o)**2*u);return b(o,A(M,-f*.18,f*.18)),-m}function ie(i,t,e){i.age+=t,i.thrown=Math.max(0,i.thrown-t),i.cooldown=Math.max(0,i.cooldown-t),i.launchGrace=Math.max(0,(i.launchGrace??0)-t),i.vy+=e*t;const s=Math.exp(-.16*t),a=i.held?2200:1400,r=i.held?38:24;i.vx=A(i.vx*s,-a,a),i.vy=A(i.vy*s,-a,a),i.omega=A(i.omega*Math.exp(-.18*t),-r,r),i.x+=i.vx*t,i.y+=i.vy*t,i.angle+=i.omega*t}const x={width:4800,height:1e3,ceiling:82*1e3/600,floor:509*1e3/600,startX:360,startY:500,bossX:4160,bossY:509*1e3/600-200,debrisCount:224},O={scale:.85,radius:13,speedX:360,speedY:330,cruise:80},ae={foundry:{name:"THE FOUNDRY",gravity:640,startY:x.floor-160,detail:"↓ GRAVITY ON / TEAR DOWN THE YARD"},orbit:{name:"ORBITAL GRAVEYARD",gravity:0,startY:x.startY,detail:"0 G / FREE-FLOATING SALVAGE"}},tt={dropChance:.35,blastRadius:260,hardImpact:150};function oe(i){return[850,1690,2590,3420].map((t,e)=>{const s=[],a=[],r=(p,m,n,f=0,b=11)=>{const o=i(b,t+p,x.floor-m,2.4*n,n);return o.angle=f,o.mountAngle=f,o.attached=!0,o.mount={x:o.x,y:o.y},o.hp=o.maxHp=65,s.push(o),o},h=r(-52,49,1.75,Math.PI/2),l=r(52,49,1.75,Math.PI/2),d=r(0,105,2.4);a.push([h,d],[l,d]);const u=e%2===0;if(u){const p=r(-44,160,1.75,Math.PI/2),m=r(44,160,1.75,Math.PI/2),n=r(0,215,2.2);a.push([d,p],[d,m],[p,n],[m,n]);const f=r(-29,252,1,0,12),b=r(29,252,1,0,12);a.push([n,f],[n,b])}else for(const p of[-44,0,44]){const m=r(p,140,.85,0,12);a.push([d,m])}return{x:t,name:u?"BOLTED GANTRY":"SALVAGE RACK",parts:s,roots:[h,l],links:a}})}function Pt(i){const t=a=>a.attached&&!a.dead,e=new Set(i.roots.filter(t)),s=[...e];for(let a=0;a<s.length;a++)for(const[r,h]of i.links){const l=r===s[a]?h:h===s[a]?r:null;l&&t(l)&&!e.has(l)&&(e.add(l),s.push(l))}return i.parts.filter(a=>t(a)&&!e.has(a))}class re{constructor(t,e){g(this,"parts",[]);this.root=t,this.name=e,t.rigId=t.id}add(t){if(this.parts.some(s=>s.key===t.key)||t.parent&&!this.parts.some(s=>s.key===t.parent))throw new Error("Rig parts need unique keys and an existing parent");const e={...t,angle:t.angle??0};return e.body.attached=!0,e.body.rigId=this.root.id,this.parts.push(e),e}part(t){return this.parts.find(e=>e.body===t)}connected(t){return this.parts.filter(e=>e.body.attached&&!e.body.dead&&(!t||e.role===t))}get shielded(){return this.connected("shield").length>0}get legs(){return this.connected("leg").filter(t=>t.parent!==void 0).length}get totalLegs(){return this.parts.filter(t=>t.role==="leg"&&t.parent!==void 0).length}get mobility(){return this.totalLegs?this.legs/this.totalLegs:1}get grabbed(){return this.root.held||this.root.thrown>0}pose(t,e,s){const a=this.parts.find(h=>h.key===t);if(!a||!a.body.attached)return;const r=a.parent?this.parts.find(h=>h.key===a.parent).body:this.root;a.offset=j(w(e,r),-r.angle),a.angle=s-r.angle,a.body.x=e.x,a.body.y=e.y,a.body.angle=s}sync(){for(const t of this.connected()){const e=t.parent?this.parts.find(r=>r.key===t.parent).body:this.root,s=j(t.offset,e.angle),a=t.body;a.x=e.x+s.x,a.y=e.y+s.y,a.angle=e.angle+t.angle,a.vx=e.vx-s.y*e.omega,a.vy=e.vy+s.x*e.omega,a.omega=e.omega}}stress(t,e){const s=this.part(t);return!s||!t.attached||t.dead?[]:(t.stress+=e,t.stress>=s.strength?this.detach(t):[])}detach(t){const e=this.part(t);if(!e||!t.attached)return[];const s=new Set([e.key]);for(const r of this.parts)r.parent&&s.has(r.parent)&&s.add(r.key);const a=[];for(const r of this.parts)if(s.has(r.key)&&r.body.attached){const h=r.body;h.attached=!1,h.rigId=void 0,h.stress=0,h.hp=Math.max(30,h.hp),h.cooldown=.15,r.role==="shield"&&(h.kind="cell",h.armed=!1,h.age=0),a.push(h)}return a}releaseAll(){return this.parts.flatMap(t=>this.detach(t.body))}}function Gt(i,t,e,s,a){const r=w(t,i),h=A(k(r),.01,e+s-.01),l=(e*e-s*s+h*h)/(2*h),d=Math.sqrt(Math.max(0,e*e-l*l)),u={x:r.x/(k(r)||1),y:r.y/(k(r)||1)};return{x:i.x+u.x*l+u.y*d*a,y:i.y+u.y*l-u.x*d*a}}function mt(i,t){const e=j(w(t,i),-i.angle),s=i.hull;let a=!0,r=0,h=1/0;for(let l=0;l<s.length;l++){const d=s[l],u=s[(l+1)%s.length],p=w(u,d),m=w(e,d),n=p.x*m.y-p.y*m.x;Math.abs(n)>.001&&(r&&Math.sign(n)!==r&&(a=!1),r=Math.sign(n));const f=A((m.x*p.x+m.y*p.y)/(p.x*p.x+p.y*p.y||1),0,1);h=Math.min(h,Math.hypot(e.x-d.x-p.x*f,e.y-d.y-p.y*f))}return a?0:h}function Dt(i,t){const e=i.kind==="core",s=new re(i,e?"WARDEN":"STRIDER"),a=e?3:4;for(let h=0;h<a;h++){const l=e?105:58,d=e?115:64,u=`leg-${h}`;for(const[p,m]of[["upper",l],["lower",d]]){const n=t("limb",15,i.x,i.y,e?2.5:1.1,m/56);n.hp=n.maxHp=e?100:65,s.add({key:`${u}-${p}`,role:"leg",body:n,parent:p==="lower"?`${u}-upper`:void 0,limb:u,offset:{x:0,y:0},strength:e?100:65})}}const r=t("power",16,i.x,i.y,e?2:1.2,e?.85:.65);return r.hp=r.maxHp=75,s.add({key:"shield-cell",role:"shield",body:r,offset:{x:0,y:e?-115:-61},strength:e?125:85}),s}function Wt(i,t,e){for(const[s,a,r]of[["upper",e.hip,e.knee],["lower",e.knee,e.foot]])i.pose(`leg-${t}-${s}`,{x:(a.x+r.x)/2,y:(a.y+r.y)/2},Math.atan2(r.y-a.y,r.x-a.x))}function ne(i,t){const e=i.root;for(let s=0;s<3;s++){const a={x:-45+s*45,y:32},r={x:-115+s*112+Math.sin(t*.8+s)*12,y:i.grabbed?194:x.floor-6-e.y},h=j(a,e.angle),l=j(r,e.angle),d={x:e.x+h.x,y:e.y+h.y},u={x:e.x+l.x,y:e.y+l.y};Wt(i,s,{hip:d,foot:u,knee:Gt(d,u,105,115,s===0?-1:1)})}i.sync()}function he(i,t){return{body:i,rig:t,gait:0,direction:-1,aim:{x:-1,y:0},reload:1.2,windup:0}}function Et(i,t){const e=i.body;return[-32,-17,17,32].map((s,a)=>{const r=(i.gait+a%2*.5)%1,h=r<.65,l=h?r/.65:(r-.65)/.35,d=(h?.5-l:l-.5)*64*i.direction,u={x:e.x+s+d,y:e.held||e.thrown>0?e.y+103:x.floor-3};let p=0;for(const f of t)!f.dead&&!f.held&&!f.attached&&["scrap","plate","structure"].includes(f.kind)&&Math.abs(f.x-u.x)<f.radius+12&&f.y>x.floor-48&&(p=Math.max(p,A(x.floor-f.y+f.radius*.4,0,25)));u.y-=p+(h?0:Math.sin(l*Math.PI)*32);const m={x:e.x+s*.8,y:e.y+20},n=Gt(m,u,58,64,a<2?-1:1);if(e.held||e.thrown>0)for(const f of[m,n,u]){const b=j(w(f,e),e.angle);f.x=e.x+b.x,f.y=e.y+b.y}return{hip:m,knee:n,foot:u,planted:h}})}function le(i,t){i.rig&&(Et(i,t).forEach((e,s)=>Wt(i.rig,s,e)),i.rig.sync())}function de(i,t,e,s){var u;const a=i.body,r=t.x-a.x;if(a.held||a.thrown>0)return i.windup=0,!1;const h=((u=i.rig)==null?void 0:u.mobility)??1,d=Math.abs(r)>270&&Math.abs(r)<1100?Math.sign(r)*32*h:0;a.vx+=(d-a.vx)*Math.min(1,s*3),h>0&&(a.vy+=((x.floor-40-66*h-a.y)*70-a.vy*15)*s),Math.abs(a.vx)>3&&(i.direction=Math.sign(a.vx)),i.gait+=Math.abs(a.vx)*s*.65/64,a.angle=Math.sin(i.gait*Math.PI*4)*(.025+(1-h)*.14),i.aim=N(w(t,a));for(const[p,m]of Et(i,e).entries())if(m.planted&&(!i.rig||i.rig.connected("leg").some(n=>n.key===`leg-${p}-lower`)))for(const n of e)n.dead||n.held||n.attached||!["scrap","cell","plate","structure"].includes(n.kind)||k(w(n,m.foot))<n.radius+17&&(n.vx+=(a.vx+i.direction*28-n.vx)*Math.min(1,s*4/Math.sqrt(n.mass)),n.vy-=s*20,n.omega+=i.direction*s*2);if(Math.abs(r)>900)return!1;if(i.windup>0){if(i.windup-=s,i.windup<=0)return i.reload=3.6,!0}else i.reload-=s,i.reload<=0&&(i.windup=.7);return!1}const Mt=["VOLLEY","STREAM","CLUSTERS"];class ce{constructor(){g(this,"mode",0);g(this,"cooldown",0);g(this,"fireWas",!1)}setMode(t){this.mode=t,this.cooldown=0}clear(){this.cooldown=0,this.fireWas=!1}tick(t,e,s){this.cooldown=Math.max(0,this.cooldown-t);const a=e&&(this.mode!==0||!this.fireWas);if(this.fireWas=e,!s.length)return[];if(!a||this.cooldown>0)return[];const r=this.mode===0?s.length:this.mode===1?1:5,h=s.slice(0,r);return this.cooldown=this.mode===1?.075:this.mode===2?.38:0,h}}const St=["ORBIT","COMPRESS","RIP TETHER","SURGICAL"];class fe{constructor(t,e="foundry",s=Math.random){g(this,"phase","ready");g(this,"mode",0);g(this,"practice",!1);g(this,"ship",{x:Number(x.startX),y:Number(x.startY),vx:Number(O.cruise),vy:0,hp:100,invulnerable:0});g(this,"bodies",[]);g(this,"particles",[]);g(this,"bullets",[]);g(this,"pops",[]);g(this,"structures",[]);g(this,"blasts",[]);g(this,"explosions",0);g(this,"demolished",0);g(this,"walkers",[]);g(this,"shellsCaught",0);g(this,"shellsReturned",0);g(this,"returnKills",0);g(this,"rigs",[]);g(this,"launcher",new ce);g(this,"focus",null);g(this,"limbsTorn",0);g(this,"cellsExtracted",0);g(this,"liveThrows",0);g(this,"tacticalOpen",!1);g(this,"surgicalShot",!1);g(this,"fireWas",!1);g(this,"input",{x:0,y:0,field:!1,aim:null});g(this,"facing",{x:1,y:0});g(this,"vortexPhase",0);g(this,"camera",0);g(this,"cameraY",0);g(this,"viewWidth",1800);g(this,"viewHeight",1e3);g(this,"time",0);g(this,"visualTime",0);g(this,"charge",0);g(this,"salvage",0);g(this,"score",0);g(this,"level",0);g(this,"kills",0);g(this,"shots",0);g(this,"torn",0);g(this,"shake",0);g(this,"fieldWas",!1);g(this,"debug",!1);g(this,"boss");g(this,"bossAwake",!1);g(this,"bossShot",2);g(this,"supplyTimer",0);g(this,"message","");g(this,"messageTime",0);g(this,"onSound",()=>{});this.sprites=t,this.site=e,this.random=s,this.boss=this.spawn("core",10,x.bossX,x.bossY,14,2.1),this.reset()}get environment(){return ae[this.site]}spawn(t,e,s,a,r=1,h=1){const l=this.sprites[e].hull.map(u=>({x:u.x*h,y:u.y*h})),d=Zt(t,e,l,s,a,r,h);return this.bodies.push(d),d}reset(){this.bodies=[],this.particles=[],this.bullets=[],this.pops=[],this.structures=[],this.blasts=[],this.explosions=0,this.demolished=0,this.rigs=[],this.focus=null,this.launcher.clear(),this.launcher.setMode(0),this.limbsTorn=0,this.cellsExtracted=0,this.liveThrows=0,this.surgicalShot=!1,this.fireWas=!1,this.walkers=[],this.shellsCaught=0,this.shellsReturned=0,this.returnKills=0,this.ship={x:x.startX,y:this.environment.startY,vx:O.cruise,vy:0,hp:100,invulnerable:0},this.camera=0,this.cameraY=0,this.updateCamera(1),this.time=0,this.charge=0,this.salvage=0,this.score=0,this.level=0,this.kills=0,this.shots=0,this.torn=0,this.bossAwake=!1,this.bossShot=2,this.mode=0,this.supplyTimer=0,this.fieldWas=!1,this.messageTime=0,this.facing={x:1,y:0},this.vortexPhase=0,this.clearInput();for(let e=0;e<x.debrisCount;e++){const s=e%32,a=Math.floor(e/32),r=260+s*134+a*11%66,h=x.ceiling+55+(a*97+s*37)%(x.floor-x.ceiling-110),l=e%13===12,d=l?.55:e%17===0?.7:.4+e%3*.08,u=this.spawn(l?"cell":"scrap",l?6:e%6,r,h,.6+e%3*.2,d);u.angle=e*1.8,u.omega=(e%3-1)*.5,this.site==="foundry"&&(u.x=260+e%56*77+Math.floor(e/56)*11,u.y=x.floor-Lt(u).max-Math.floor(e/56)*9,u.omega=0)}this.site==="foundry"&&(this.structures=oe((e,s,a,r,h)=>this.spawn("structure",e,s,a,r,h)));for(const[e,s,a]of[[880,420,"drone"],[1200,x.floor-24,"wheel"],[1470,290,"drone"],[1760,630,"drone"],[2060,x.floor-24,"wheel"],[2320,360,"drone"],[2600,680,"drone"],[2890,x.floor-24,"wheel"],[3130,280,"drone"],[3440,580,"drone"],[3710,x.floor-24,"wheel"]]){const r=this.spawn(a,a==="drone"?8:9,e,s,2.5,1.05);r.hp=r.maxHp=a==="wheel"?48:34,r.cooldown=3+e%3}for(const e of[1340,2460,3530]){const s=this.spawn("walker",13,e,x.floor-106,6,1.6);s.hp=s.maxHp=115;const a=Dt(s,this.spawn.bind(this));this.rigs.push(a),this.walkers.push(he(s,a))}this.boss=this.spawn("core",10,x.bossX,x.bossY,14,2.1),this.boss.hp=this.boss.maxHp=230,this.boss.attached=!0;const t=Dt(this.boss,this.spawn.bind(this));this.rigs.push(t);for(let e=0;e<6;e++){const s={x:e<3?-53:44,y:(e%3-1)*45},a=this.spawn("plate",7,this.boss.x+s.x,this.boss.y+s.y,3,1.35);a.hp=a.maxHp=95,t.add({key:`armor-${e}`,role:"armor",body:a,offset:s,strength:80})}this.poseRigs()}start(t=!1,e=this.site){this.site=e,this.practice=t,this.reset(),this.phase="playing",this.say(this.site==="foundry"?"LIFT THE SCRAP. TEAR DOWN THE YARD.":"RMB TO GATHER. LMB TO HURL.")}updateCamera(t){const e=A(this.ship.x-this.viewWidth*.28,0,Math.max(0,x.width-this.viewWidth)),s=this.viewHeight>=x.height?(x.height-this.viewHeight)/2:A(this.ship.y-this.viewHeight*.5,0,x.height-this.viewHeight),a=Math.min(1,t*3);this.camera+=(e-this.camera)*a,this.cameraY+=(s-this.cameraY)*a}clearInput(){this.input.x=0,this.input.y=0,this.input.field=!1,this.input.fire=!1,this.input.aim=null,this.fieldWas=!1,this.charge=0,this.focus=null,this.surgicalShot=!1,this.fireWas=!1,this.launcher.clear()}pause(){this.phase==="playing"&&(this.phase="paused",this.clearInput())}setMode(t){if(this.mode!==t){if(t===3||this.mode===3)for(const e of this.held)e.held=!1,e.thrown=0;this.mode=t,this.focus=null,this.charge=0,this.fieldWas=!1,this.surgicalShot=!1,this.launcher.clear()}}setRelease(t){this.launcher.setMode(t)}rigFor(t){return this.rigs.find(e=>e.root.id===t.rigId)}poseRigs(){for(const e of this.walkers)e.body.dead||le(e,this.bodies);const t=this.rigFor(this.boss);t&&!this.boss.dead&&ne(t,this.time)}get surgicalRange(){return 570+this.level*50}get hover(){const t=this.input.aim;return t?this.bodies.filter(e=>!e.dead&&k(w(e,this.ship))<this.surgicalRange&&mt(e,t)<18).sort((e,s)=>mt(e,t)-mt(s,t)||(e.attached?-1:0)-(s.attached?-1:0)||k(w(e,t))-k(w(s,t)))[0]??null:null}get focusLabel(){const t=this.focus??this.hover;if(!t)return"POINT AT ONE OBJECT";const e=this.rigFor(t),s=e==null?void 0:e.part(t);return(e==null?void 0:e.root)===t&&e.shielded?"GRAV SHIELD · EXTRACT THE VIOLET CELL":t.attached?`${(s==null?void 0:s.role)==="shield"?"SHIELD CELL":(s==null?void 0:s.role)==="leg"?"LEG JOINT":t.kind.toUpperCase()} · PULL ${Math.min(100,Math.round(t.stress/((s==null?void 0:s.strength)??80)*100))}%`:`${t.kind==="core"?"WARDEN":t.kind==="walker"?"STRIDER":t.kind.toUpperCase()} · ${t.held?"LMB HURL / WHEEL ROTATE":"RMB GRAB"}`}capture(t){var e;t.ordnance&&(t.ordnance.state="captured",t.ordnance.fuse=6,this.shellsCaught++,this.pop(t.x,t.y,"SHELL CAUGHT","#b5f5d5")),t.held=!0,t.armed=!1,t.thrown=0,((e=this.rigFor(t))==null?void 0:e.root)===t&&(t.attached=!1),this.onSound("grab")}detached(t,e){for(const s of t)s.kind==="plate"&&(this.torn++,this.salvage+=25,this.score+=150,this.pop(s.x,s.y,"PLATE RIPPED +25"),this.plates.length<=2&&this.say("CORE EXPOSED — THROW EVERYTHING.")),s.kind==="limb"&&this.limbsTorn++,s.kind==="cell"&&(this.cellsExtracted++,this.pop(s.x,s.y,"GRAV SHIELD DOWN","#df9ef2")),s===e&&this.input.field&&this.capture(s),this.burst(s.x,s.y,"#ebc581",10),this.onSound("hit");t.length&&(this.shake=Math.max(this.shake,3));for(const s of this.rigs)(!s.shielded||s.mobility===0)&&(s.root.attached=!1)}surgical(t){var p;if(!this.input.field){this.charge=0,this.dropCargo(),this.focus=null,this.surgicalShot=!1;return}!this.fieldWas&&!this.surgicalShot&&(this.focus=this.hover);const e=this.focus;if(!e||e.dead||this.surgicalShot)return;if(k(w(e,this.ship))>this.surgicalRange+100){e.held=!1,this.focus=null,this.surgicalShot=!0;return}const s=this.rigFor(e);if((s==null?void 0:s.root)===e&&s.shielded||e.attached&&(s?this.detached(s.stress(e,t*115*(1+this.level*.2)),e):(e.stress+=t*115,e.stress>=80&&this.detach(e)),this.charge=Math.min(1,e.stress/(((p=s==null?void 0:s.part(e))==null?void 0:p.strength)??80)),e.attached))return;e.held||this.capture(e);const a=this.input.aim??this.fieldCenter,r=N(w(a,this.ship)),h=A(k(w(a,this.ship)),65,this.surgicalRange),l={x:this.ship.x+r.x*h,y:this.ship.y+r.y*h},d=200/Math.sqrt(Math.max(1,e.mass/3)),u=2*Math.sqrt(d);e.vx+=((l.x-e.x)*d-e.vx*u)*t,e.vy+=((l.y-e.y)*d-e.vy*u)*t,e.omega*=Math.exp(-t*8),this.charge=Math.min(1,this.charge+t),this.input.fire&&!this.fireWas&&(this.launch([e]),this.focus=null,this.surgicalShot=!0)}get held(){return this.bodies.filter(t=>t.held&&!t.dead)}get plates(){return this.bodies.filter(t=>t.kind==="plate"&&t.attached&&!t.dead)}get range(){return(this.mode===2?255:210)+this.level*28}get vortexSpeed(){return 14+this.level*1.8}get fieldCenter(){const t=this.aim();return{x:this.ship.x+t.x*165,y:this.ship.y+t.y*165}}get upgradeCost(){return 50+this.level*45}upgrade(){return this.level>=3||this.salvage<this.upgradeCost||!["playing","paused"].includes(this.phase)?!1:(this.salvage-=this.upgradeCost,this.level++,this.ship.hp=Math.min(100,this.ship.hp+20),this.say(`FIELD MK.${this.level+1} — RANGE + FIELD POWER INCREASED`),this.onSound("upgrade"),!0)}say(t){this.message=t,this.messageTime=4}aim(){if(this.input.aim){const l=w(this.input.aim,this.ship);return k(l)>1&&(this.facing=N(l)),this.facing}const e=this.site==="foundry"&&!this.bodies.some(l=>l.held&&!l.dead)?this.bodies.filter(l=>!l.dead&&!l.attached&&l.thrown<=0&&["scrap","cell"].includes(l.kind)&&l.x>this.ship.x+60&&k(w(l,this.ship))<400).sort((l,d)=>k(w(l,this.ship))-k(w(d,this.ship)))[0]:void 0,s=this.bodies.filter(l=>{var d;return!l.dead&&((d=l.ordnance)==null?void 0:d.state)==="hostile"&&k(w(l,this.ship))<650}).sort((l,d)=>k(w(l,this.ship))-k(w(d,this.ship)))[0],a=this.bodies.find(l=>!l.dead&&l.held&&l.ordnance),h=(a?this.bodies.find(l=>!l.dead&&l.id===a.ordnance.owner):void 0)??s??e??this.bodies.filter(l=>!l.dead&&(l.kind==="drone"||l.kind==="wheel"||l.kind==="walker"||l.kind==="core"||l.kind==="structure"&&l.attached)&&l.x>this.ship.x-70).sort((l,d)=>k(w(l,this.ship))-k(w(d,this.ship)))[0];return this.facing=h?N(w(h,this.ship)):{x:1,y:0},this.facing}burst(t,e,s,a=20,r=140){for(let h=0;h<a;h++){const l=Math.random()*Math.PI*2,d=r*(.2+Math.random()),u=.25+Math.random()*.6;this.particles.push({x:t,y:e,vx:Math.cos(l)*d,vy:Math.sin(l)*d,life:u,max:u,color:s,size:Math.random()<.3?4:2})}this.particles.length>350&&this.particles.splice(0,this.particles.length-350)}pop(t,e,s,a="#c9ead7"){this.pops.push({x:t,y:e,text:s,life:1.5,color:a})}detach(t){if(!t.attached)return;const e=this.rigFor(t);if(e&&e.root!==t){const s=this.mode===3?this.focus===t:this.input.field&&t.kind==="plate"&&k(w(t,this.fieldCenter))<this.range;this.detached(e.detach(t),s?t:void 0);return}if(t.kind==="structure"){this.unbolt(t);for(const s of this.structures)for(const a of Pt(s))this.unbolt(a);this.pop(t.x,t.y,"SUPPORT BROKEN","#f7bf75"),this.onSound("hit"),this.shake=Math.max(this.shake,5);return}}unbolt(t){t.attached=!1,t.mount=void 0,t.stress=0,this.demolished++,this.salvage+=4,this.score+=20,t.held=this.input.field&&(this.mode===3?this.focus===t:k(w(t,this.fieldCenter))<this.range),this.burst(t.x,t.y,"#eab879",10,180)}explode(t,e){var a;const s=((a=t.ordnance)==null?void 0:a.state)==="returned"?t.ordnance.owner:e;this.explosions++,this.blasts.push({x:t.x,y:t.y,age:0,radius:tt.blastRadius}),this.blasts.length>24&&this.blasts.shift(),this.burst(t.x,t.y,"#fff0b1",60,680),this.burst(t.x,t.y,"#f49148",40,410),this.onSound("boom"),this.shake=Math.max(this.shake,28*A(1-k(w(this.ship,t))/1400,.1,1));for(const r of[...this.bodies]){const h=k(w(r,t));if(r.dead||r===t||h>tt.blastRadius)continue;const l=1-h/tt.blastRadius;if(r.kind==="cell"||r.kind==="shell"){this.destroy(r,!1,s);continue}if(r.attached&&r.kind!=="core"){const p=this.rigFor(r);p?this.detached(p.stress(r,45+l*100)):(r.stress+=45+l*100,r.stress>=80&&this.detach(r))}if(r.kind==="cell"){this.destroy(r,!1,s);continue}const d=h>1?N(w(r,t)):{x:0,y:-1},u=(400+l*850)/Math.sqrt(r.mass);r.vx+=d.x*u,r.vy+=d.y*u,r.omega+=(r.id%2?1:-1)*l*15,!r.attached&&!r.held&&(r.thrown=2.5,r.volley=-t.id-1),this.damage(r,["scrap","structure","plate"].includes(r.kind)?l*22:35+l*110),r.dead&&r.kind==="walker"&&s===r.id&&(this.returnKills++,this.pop(r.x,r.y-55,"RETURN TO SENDER","#b5f5d5"))}k(w(this.ship,t))<190&&this.hurt(18)}destroy(t,e=!1,s){if(t.dead)return;if(t.dead=!0,t.held=!1,t.kind==="structure"&&t.attached){t.attached=!1;for(const l of this.structures)for(const d of Pt(l))this.unbolt(d)}const a=this.rigFor(t);if((a==null?void 0:a.root)===t?this.detached(a.releaseAll()):a&&t.attached&&this.detached(a.detach(t)),t.kind==="core"){this.phase="won",this.clearInput(),this.score+=1500,this.burst(t.x,t.y,"#f4c572",90,400),this.onSound("boom"),this.shake=12,this.plates.forEach(l=>{l.attached=!1,l.vx=(Math.random()-.5)*250});return}const r=t.kind==="drone"||t.kind==="wheel"||t.kind==="walker";if(r){const l=t.kind==="walker"?75:40;this.kills++,this.score+=t.kind==="walker"?450:250,this.salvage+=l,this.pop(t.x,t.y,`SALVAGE +${l}`)}if(this.burst(t.x,t.y,t.kind==="cell"?"#ffd67b":"#a9b8a0",r?30:12,r?210:100),t.kind==="cell"||t.kind==="shell"){this.explode(t,s);return}const h=r&&this.random()<tt.dropChance;if(r||t.scale>.38){const l=t.kind==="walker"?6:r?4:2;for(let d=0;d<l;d++){const u=h&&d===l-1,p=u?6:t.kind==="walker"&&d<3?11:(t.sprite+d)%6,m=this.spawn(u?"cell":"scrap",p,t.x+(d-1)*12,t.y,Math.max(.2,t.mass/l),u?.55:t.scale*(t.kind==="walker"?.38:.55));m.vx=t.vx*.3+(Math.random()-.5)*180,m.vy=t.vy*.3+(Math.random()-.5)*170,m.omega=(Math.random()-.5)*8,m.cooldown=.4}}(e||r)&&this.onSound("hit")}damage(t,e){var s;if(!t.dead){if(t.kind==="core"&&this.plates.length>2){this.pop(t.x,t.y-80,"ARMOR LOCKED","#f3b65b");return}if(t.attached&&((s=this.rigFor(t))==null?void 0:s.root)!==t&&this.rigFor(t)){t.hp-=e,t.hp<=0?this.detach(t):this.detached(this.rigFor(t).stress(t,e*.65));return}if(t.attached&&t.kind==="structure"){t.stress+=e*.65,t.hp-=e,(t.hp<=0||t.stress>=80)&&this.detach(t);return}t.hp-=e,t.hp<=0&&this.destroy(t)}}hurt(t){this.ship.invulnerable>0||this.phase!=="playing"||(this.ship.hp=Math.max(this.practice?25:0,this.ship.hp-t),this.ship.invulnerable=1,this.shake=Math.max(this.shake,5),this.burst(this.ship.x,this.ship.y,"#ed8267",15),this.onSound("hit"),this.ship.hp<=0&&(this.phase="lost",this.clearInput(),this.onSound("boom")))}fireShell(t){const e=t.body,s=t.aim,a=e.radius+18,r=this.spawn("shell",14,e.x+s.x*a,e.y+s.y*a,1.2,.65);r.hp=r.maxHp=30,r.armed=!0,r.ordnance={owner:e.id,state:"hostile",fuse:6};const h=A(k(w(this.ship,r))/320,.7,2.1);r.vx=(this.ship.x+this.ship.vx*.25-r.x)/h,r.vy=(this.ship.y+this.ship.vy*.2-r.y)/h-this.environment.gravity*.25*h*.5,r.omega=-5,this.burst(r.x,r.y,"#ffb178",14,130),this.onSound("throw"),this.shellsCaught===0&&this.say("INCOMING SHELL — HOLD YOUR FIELD TO CATCH.")}release(){this.launch(this.held)}dropCargo(){for(const t of this.held)t.held=!1,t.thrown=0,t.vx=this.ship.vx+(t.vx-this.ship.vx)*.1,t.vy=this.ship.vy+(t.vy-this.ship.vy)*.1,t.omega*=.2,t.ordnance&&(t.ordnance.state="hostile",t.armed=!0)}launch(t){const e=this.aim();t.length&&(t.forEach((s,a)=>{var d;const r=(t.length>1?a/(t.length-1)-.5:0)*(this.mode===0?.52:.12),h=Math.atan2(e.y,e.x)+r,l=(this.mode===1?700+this.charge*600:this.mode===2?1e3:this.mode===3?1050/Math.sqrt(Math.max(1,s.mass/4)):860)+this.level*65;(((d=this.rigFor(s))==null?void 0:d.root)===s||s.kind==="drone"||s.kind==="wheel")&&this.liveThrows++,s.vx=Math.cos(h)*l+this.ship.vx,s.vy=Math.sin(h)*l+this.ship.vy,s.held=!1,s.thrown=3,s.volley=this.shots+1,s.omega+=(a%2?1:-1)*4,s.launchGrace=.3,s.kind==="cell"&&(s.armed=!0),s.ordnance&&(s.armed=!0,s.ordnance.state="returned",s.ordnance.fuse=6,this.shellsReturned++),this.burst(s.x,s.y,"#b5f5d5",3,50)}),this.shots++,this.onSound("throw"),this.shake=2+this.charge*3,this.ship.vx-=e.x*30,this.ship.vy-=e.y*30)}step(t){var d,u,p,m,n,f,b;if(this.visualTime+=t,this.blasts.forEach(o=>o.age+=t),this.blasts=this.blasts.filter(o=>o.age<.95),this.shake=Math.max(0,this.shake-t*22),this.particles.forEach(o=>{o.x+=o.vx*t,o.y+=o.vy*t,o.vy+=80*t,o.life-=t}),this.particles=this.particles.filter(o=>o.life>0),this.pops.forEach(o=>{o.y-=t*24,o.life-=t}),this.pops=this.pops.filter(o=>o.life>0),this.phase!=="playing")return;this.time+=t,this.messageTime=Math.max(0,this.messageTime-t),this.vortexPhase+=this.vortexSpeed*t;const e=this.ship,s=this.input;e.invulnerable=Math.max(0,e.invulnerable-t),this.practice&&(e.hp=Math.min(100,e.hp+t*3));const a=e.x<x.bossX-270?O.cruise:0;if(e.vx+=(s.x*O.speedX+a-e.vx)*Math.min(1,t*5),e.vy+=(s.y*O.speedY-e.vy)*Math.min(1,t*5),e.x=A(e.x+e.vx*t,this.camera+40,x.width-120),e.y=A(e.y+e.vy*t,x.ceiling+O.radius,x.floor-O.radius),this.updateCamera(t),e.x>x.bossX-570&&!this.bossAwake&&(this.bossAwake=!0,this.say("WARDEN / 07 — RIP ITS ARMOR. EXPOSE THE CORE.")),this.bossAwake&&!this.boss.dead&&!this.boss.held&&this.boss.thrown<=0){if(this.boss.attached){const o=((d=this.rigFor(this.boss))==null?void 0:d.mobility)??1;this.boss.y=x.floor-65-135*o+Math.sin(this.time*1.6)*11*o,this.boss.x=x.bossX+Math.sin(this.time*.45)*50*o}else{const o=((u=this.rigFor(this.boss))==null?void 0:u.mobility)??0;o>0&&(this.boss.vy+=((x.floor-65-135*o-this.boss.y)*40-this.boss.vy*12)*t)}if(this.bossShot-=t,this.bossShot<=0){this.bossShot=this.plates.length>2?2.5:1.7;const o=Math.atan2(e.y-this.boss.y,e.x-this.boss.x);for(const M of[-.15,0,.15])this.bullets.push({x:this.boss.x-65,y:this.boss.y,vx:Math.cos(o+M)*190,vy:Math.sin(o+M)*190,life:5})}}const r=this.fieldCenter;if(this.mode===3)this.surgical(t);else if(s.field){this.charge=Math.min(1,this.charge+t*1.1);const o=this.bodies.filter(S=>!S.dead&&S.kind!=="core"&&S.kind!=="wheel"&&S.kind!=="walker"&&!(S.attached&&(S.kind==="limb"||S.kind==="power"))&&!S.held&&S.thrown<=0&&k(w(S,r))<this.range).sort((S,L)=>k(w(S,r))-k(w(L,r))),M=this.aim();let E=!1;for(const S of o)if(S.attached){if(this.mode===2&&E)continue;const L=N(w(S,e));if(L.x*M.x+L.y*M.y<.35)continue;S.stress+=t*(this.mode===2?105:26)*(1+this.level*.2),E=!0,S.stress>=80&&this.detach(S)}else S.kind==="drone"?this.mode===2&&(S.hp-=t*45,S.vx+=(r.x-S.x)*t*5,S.vy+=(r.y-S.y)*t*5,S.hp<=0&&this.destroy(S)):this.capture(S)}this.mode!==3&&this.held.forEach(o=>{const M=this.vortexPhase+o.id*2.3999632297,E=this.mode===0?68+o.id%5*7:this.mode===1?28*(1-this.charge*.55):40,S=Math.cos(M)*E,L=Math.sin(M)*E,I=this.vortexSpeed,P=320*(1+this.level*.18);o.vx+=((r.x+S-o.x)*P+(e.vx-L*I-o.vx)*30-S*I*I)*t,o.vy+=((r.y+L-o.y)*P+(e.vy+S*I-o.vy)*30-L*I*I)*t;const Z=(o.id%2?1:-1)*(20+o.id%7);o.omega+=(Z-o.omega)*Math.min(1,t*16)}),this.mode!==3&&!this.tacticalOpen&&(this.launch(this.launcher.tick(t,!!s.fire,this.held)),s.field||(this.dropCargo(),this.charge=0)),this.fieldWas=s.field,this.fireWas=!!s.fire;for(const o of this.walkers)!o.body.dead&&de(o,e,this.bodies,t)&&this.fireShell(o);for(const o of[...this.bodies]){if(o.dead)continue;if(o.ordnance&&!o.held&&o.ordnance.state!=="captured"&&(o.ordnance.fuse-=t,o.ordnance.fuse<=0)){this.destroy(o);continue}if(o.attached&&(o.cooldown=Math.max(0,o.cooldown-t),o.age+=t),o.kind==="core"&&o.attached||o.attached&&this.rigFor(o))continue;if(o.attached&&o.mount){const I=o.mount.x,P=o.mount.y,Z=s.field&&k(w(o,r))<this.range,F=Z?N(w(r,o)):{x:0,y:0};o.vx+=((I+F.x*o.stress*.12-o.x)*110-o.vx*17)*t,o.vy+=((P+F.y*o.stress*.12-o.y)*110-o.vy*17)*t,o.x+=o.vx*t,o.y+=o.vy*t,o.angle=(o.mountAngle??0)+Math.sin(this.time*21+o.id)*o.stress*.0015,Z||(o.stress=Math.max(0,o.stress-t*9));continue}if((o.kind==="drone"||o.kind==="wheel")&&!o.held&&o.thrown<=0&&Math.abs(o.x-e.x)<this.viewWidth){o.vx+=((o.kind==="drone"?-24:-13)-o.vx)*t*2;const I=o.kind==="drone"?o.homeY+Math.sin(this.time*1.5+o.id)*80:x.floor-24;if(o.vy+=((I-o.y)*9-o.vy*4)*t,o.angle=o.kind==="drone"?Math.sin(this.time*2+o.id)*.1:0,o.cooldown<=0&&Math.abs(o.x-e.x)<650){const P=N(w(e,o));this.bullets.push({x:o.x,y:o.y,vx:P.x*160,vy:P.y*160,life:5}),o.cooldown=3.4}}const M=(o.kind==="drone"||o.kind==="wheel"||o.kind==="walker"&&(((p=this.rigFor(o))==null?void 0:p.mobility)??1)>0||o.kind==="core"&&(((m=this.rigFor(o))==null?void 0:m.mobility)??0)>0)&&o.thrown<=0;ie(o,t,o.held||M?0:this.environment.gravity*(o.kind==="shell"?.25:1)),((n=this.rigFor(o))==null?void 0:n.root)===o&&(o.x<70+o.radius||o.x>x.width-70-o.radius)&&(o.x=A(o.x,70+o.radius,x.width-70-o.radius),o.vx*=-.5);const E=Lt(o),S=x.ceiling-E.min,L=x.floor-E.max;if(o.y>L||o.y<S){const I=Math.abs(o.vy);if(o.y=A(o.y,S,L),(o.kind==="cell"||o.kind==="shell")&&!o.held&&(o.armed||o.age>.5&&I>tt.hardImpact)){this.destroy(o);continue}const P=this.environment.gravity>0&&o.y>=L&&!o.held;o.vy*=P?I<65?0:-.18:-.62,o.vx*=P?Math.exp(-t*8):.91,o.omega*=P?Math.exp(-t*12):.8,o.thrown>0&&I>250&&o.cooldown<=0&&(this.damage(o,I*.06),o.cooldown=.2)}}this.poseRigs();const h=this.bodies.filter(o=>!o.dead&&o.x>this.camera-160&&o.x<this.camera+this.viewWidth+200);for(const[o,M]of te(h)){if(o.dead||M.dead||o.attached&&M.attached||o.held&&M.held||(o.attached||M.attached)&&(o.held||M.held)||((f=o.ordnance)==null?void 0:f.state)==="hostile"&&(o.ordnance.owner===M.id||o.ordnance.owner===M.rigId)||((b=M.ordnance)==null?void 0:b.state)==="hostile"&&(M.ordnance.owner===o.id||M.ordnance.owner===o.rigId)||o.thrown>0&&M.thrown>0&&o.volley===M.volley)continue;const E=ee(o,M);if(!E)continue;const S=Math.max(0,-((M.vx-o.vx)*E.normal.x+(M.vy-o.vy)*E.normal.y));let L=!1;for(const F of[o,M])(F.kind==="cell"||F.kind==="shell")&&!F.held&&(F.armed||F.age>.5&&S>tt.hardImpact)&&(this.destroy(F),L=!0);if(L)continue;const I=o.thrown>0||M.thrown>0,P=se(o,M,E),Z=o.kind==="structure"&&!o.attached||M.kind==="structure"&&!M.attached;if(P>85&&(I||Z&&P>120)&&o.cooldown<=0&&M.cooldown<=0){const F=A(P*.16,12,90);this.damage(o,F*M.mass*.5),this.damage(M,F*o.mass*.5),o.cooldown=.16,M.cooldown=.16,this.burst(E.point.x,E.point.y,"#f3cf83",9,130),this.onSound("hit"),this.shake=Math.max(this.shake,2)}}for(const o of h){if(!o.dead&&!o.held&&o.kind==="shell"&&o.armed&&k(w(o,e))<o.radius+O.radius){this.destroy(o);continue}o.dead||o.held||o.thrown>0||!["drone","wheel","walker","core","plate","structure"].includes(o.kind)||k(w(o,e))<o.radius*.7+O.radius&&this.hurt(16)}const l=this.held;for(const o of this.bullets)o.x+=o.vx*t,o.y+=o.vy*t,o.life-=t,l.find(E=>k(w(E,o))<E.radius*.8+5)?(o.life=0,this.burst(o.x,o.y,"#c3f9dc",5,70)):k(w(o,e))<O.radius+3&&(o.life=0,this.hurt(12));if(this.bullets=this.bullets.filter(o=>o.life>0),this.supplyTimer+=t,this.supplyTimer>5&&this.bodies.filter(o=>!o.dead&&["scrap","cell","plate"].includes(o.kind)&&!o.attached&&k(w(o,e))<350).length<12){this.supplyTimer=0;for(let o=0;o<12;o++)this.spawn("scrap",o%6,e.x+70+o%4*50,this.site==="foundry"?x.floor-16-Math.floor(o/4)*14:A(e.y-110+Math.floor(o/4)*100,x.ceiling+35,x.floor-35),.6+o%3*.2,.4+o%3*.08)}this.bodies=this.bodies.filter(o=>!o.dead&&(o.kind==="core"||o.held||o.thrown>0||o.attached||o.x>this.camera-400&&o.x<x.width+520)),this.rigs=this.rigs.filter(o=>!o.root.dead&&this.bodies.includes(o.root)),this.walkers=this.walkers.filter(o=>!o.body.dead&&this.bodies.includes(o.body))}}class ue{constructor(t,e){g(this,"c");g(this,"width",1e3);g(this,"height",600);g(this,"scale",1);g(this,"reduced",!1);this.canvas=t,this.game=e,this.c=t.getContext("2d")}resize(){const t=this.canvas.getBoundingClientRect(),e=Math.min(2,window.devicePixelRatio||1);t.width<=0||t.height<=0||(this.width=t.width,this.height=t.height,this.canvas.width=Math.round(t.width*e),this.canvas.height=Math.round(t.height*e),this.scale=Math.min(t.height/(t.height<360?700:x.height),t.width/800),this.game.viewWidth=t.width/this.scale,this.game.viewHeight=t.height/this.scale,this.game.updateCamera(1),this.c.setTransform(e,0,0,e,0,0),this.c.imageSmoothingEnabled=!1)}worldPoint(t,e){const s=this.canvas.getBoundingClientRect();return{x:(t-s.left)/this.scale+this.game.camera,y:(e-s.top)/this.scale+this.game.cameraY}}rect(t,e,s,a,r){this.c.fillStyle=r,this.c.fillRect(Math.round(t),Math.round(e),Math.round(s),Math.round(a))}line(t,e,s=1){const a=this.c;a.beginPath(),a.moveTo(t[0],t[1]);for(let r=2;r<t.length;r+=2)a.lineTo(t[r],t[r+1]);a.strokeStyle=e,a.lineWidth=s,a.stroke()}text(t,e,s,a="#72857d",r=11){this.c.fillStyle=a,this.c.font=`${r}px "SFMono-Regular", Consolas, monospace`,this.c.fillText(t,e,s)}draw(){const t=this.c,e=this.game,s=e.viewWidth,a=e.visualTime,r=e.camera;t.clearRect(0,0,this.width,this.height),this.rect(0,0,this.width,this.height,"#0b1417"),t.save(),t.scale(this.scale,this.scale),t.translate(0,-e.cameraY),!this.reduced&&e.shake>0&&t.translate((Math.random()-.5)*e.shake,(Math.random()-.5)*e.shake),t.save(),t.scale(1,x.height/600);const h=Math.max(0,-e.cameraY)*600/x.height,l=Math.max(0,e.cameraY+e.viewHeight-x.height)*600/x.height,d=t.createRadialGradient(s*.58,270,10,s*.58,270,s*.75);d.addColorStop(0,e.site==="foundry"?"#3b382c":"#253a37"),d.addColorStop(.55,"#14272a"),d.addColorStop(1,"#091316"),t.fillStyle=d,t.fillRect(0,-h,s,600+h+l);for(let n=-1;n<s/240+2;n++){const f=n*240-r*.18%240;this.rect(f,115,140,285,"#132326"),this.rect(f+5,119,128,3,"#30433b"),this.rect(f+15,145,8,225,"#233833"),this.rect(f+120,145,5,225,"#213430"),this.line([f+25,350,f+112,185],"#263b35",6);for(let b=0;b<5;b++)this.rect(f+39+b*13,385,5,6,b===2?"#7a7650":"#324942");this.line([f-20,130,f+160,175,f+235,120],"#0c1d21",2)}for(let n=-1;n<s/420+2;n++){const f=n*420-r*.4%420;this.rect(f,0,44,600,"#122125"),this.rect(f+4,0,5,600,"#2b3e3a"),this.line([f+28,25,f+200,130,f+218,130],"#31413a",12),this.line([f+28,25,f+200,130,f+218,130],"#101f23",7),this.rect(f+22,186,30,80,"#263833"),this.rect(f+28,194,14,2,"#526054"),this.rect(f+28,205,14,2,"#526054"),this.rect(f+32,248,7,4,"#bc9557")}this.rect(0,-h,s,82+h,"#0a1215"),this.rect(0,74,s,7,"#34433a"),this.rect(0,81,s,4,"#73806a"),this.rect(0,86,s,6,"#17282a"),this.rect(0,512,s,88+l,"#0b171b"),this.rect(0,509,s,4,"#758275"),this.rect(0,513,s,8,"#2d413d"),this.rect(0,541,s,3,"#3a4c42");for(let n=-1;n<s/100+2;n++){const f=n*100-r%100;this.rect(f,35,70,27,"#172629"),this.rect(f+4,38,60,2,"#33433d"),this.rect(f+9,56,48,2,"#253b36"),this.rect(f+12,72,6,7,"#b59659"),this.rect(f,526,86,8,"#1b2e2d"),this.rect(f+4,528,24,3,"#344b3f"),this.rect(f+50,528,4,3,"#bf9659"),this.rect(f+59,528,4,3,"#bf9659"),this.line([f+90,548,f+90,597],"#1b3130",2);for(let b=0;b<3;b++)this.rect(f+15+b*18,557,11,2,"#294039")}for(let n=-1;n<s/330+2;n++){const f=n*330-r%330;this.rect(f+150,65,50,6,"#5c5943"),this.rect(f+159,71,31,3,"#ecc07a"),t.globalAlpha=.035,t.fillStyle="#ffd987",t.beginPath(),t.moveTo(f+156,78),t.lineTo(f+90,350),t.lineTo(f+260,350),t.lineTo(f+197,78),t.fill(),t.globalAlpha=1,this.text("BAY / "+String((n+Math.floor(r/330)+12)%99).padStart(2,"0"),f+30,471,"#43574d",12),this.rect(f+35,480,70,2,"#324b42")}if(!this.reduced)for(let n=0;n<35;n++){const f=((n*83.17+a*(4+n%3)-r*.3)%(s+40)+s+40)%(s+40),b=100+n*41.37%370;t.globalAlpha=.12+n%4*.04,this.rect(f,b,2,2,"#b7c9a2")}if(t.globalAlpha=1,t.restore(),t.save(),t.translate(-r,0),e.site==="foundry"){this.rect(r,x.floor+7,s,5,"#a1854e");for(let n=Math.floor(r/100)*100;n<r+s+100;n+=100)this.line([n,x.floor+15,n+30,x.floor+30],"#5c5638",8);for(const n of e.structures)if(n.parts.some(f=>f.attached&&!f.dead)){for(const[f,b]of n.links)f.attached&&b.attached&&!f.dead&&!b.dead&&(this.line([f.x,f.y,b.x,b.y],"#b49a6055",2),this.rect((f.x+b.x)/2-3,(f.y+b.y)/2-3,6,6,"#d4ba80"));this.text(n.name,n.x-70,x.floor-300,"#c4a778",12)}}this.drawBoss();for(const n of e.walkers)!n.body.dead&&n.body.x>r-180&&n.body.x<r+s+180&&this.drawWalker(n);this.drawField();for(const n of e.rigs)if(n.shielded&&!n.root.dead){const f=n.root;t.strokeStyle="#d39be03b",t.lineWidth=2,t.setLineDash([8,9]),t.beginPath(),t.ellipse(f.x,f.y,f.radius+26,f.radius+32,0,0,Math.PI*2),t.stroke(),t.setLineDash([])}const u=e.fieldCenter,p=e.held.length>48;for(const n of e.bodies){if(n.dead||n.x<r-100||n.x>r+s+100||n.kind==="core"||n.kind==="walker")continue;t.save(),t.translate(Math.round(n.x),Math.round(n.y)),t.rotate(n.angle),n.kind==="power"?(t.shadowColor="#df9ef2",t.shadowBlur=12):n.kind==="cell"?(t.shadowColor="#ffd477",t.shadowBlur=12):n.held&&!p&&(t.shadowColor="#9fedd1",t.shadowBlur=10);const f=e.sprites[n.sprite],b=f.width*n.scale;if(t.drawImage(f.canvas,-b/2,-b/2,b,b),t.restore(),n.kind==="cell"&&n.armed&&!this.reduced&&(this.line([n.x-12,n.y-12,n.x+12,n.y+12],"#fff1b4",2),this.line([n.x+12,n.y-12,n.x-12,n.y+12],"#fff1b4",2)),n.ordnance){const o=n.ordnance.state==="hostile"?"#ff9471":"#b5f5d5";t.strokeStyle=o,t.lineWidth=2,t.beginPath(),t.arc(n.x,n.y,n.radius+5,0,Math.PI*2),t.stroke(),this.reduced||this.line([n.x-n.vx*.075,n.y-n.vy*.075,n.x,n.y],o,3)}if(n.held&&!this.reduced&&(t.globalAlpha=.45,this.line([n.x-n.vx*.024,n.y-n.vy*.024,n.x,n.y],"#baf7d9",2),t.globalAlpha=1),n.attached&&n.stress>1&&(this.rect(n.x-20,n.y-32,40,3,"#3b493d"),this.rect(n.x-20,n.y-32,n.stress/80*40,3,"#a8efd2"),this.line([u.x,u.y,n.x,n.y],"#b6f1d1",1.5)),(n.kind==="drone"||n.kind==="wheel")&&n.hp<n.maxHp&&(this.rect(n.x-18,n.y-34,36,3,"#583e30"),this.rect(n.x-18,n.y-34,36*n.hp/n.maxHp,3,"#ecb470")),n.thrown>0&&(t.globalAlpha=.4,this.line([n.x,n.y,n.x-n.vx*.027,n.y-n.vy*.027],n.kind==="cell"?"#e8bb62":"#aedecd",2),t.globalAlpha=1),e.debug){const o=ot(n);this.line([...o,o[0]].flatMap(M=>[M.x,M.y]),n.attached?"#eaa064":"#85ebcb")}}for(const n of e.bullets)t.shadowColor="#f3a566",t.shadowBlur=9,this.rect(n.x-5,n.y-3,10,6,"#ee9960"),this.rect(n.x-2,n.y-1,4,2,"#ffe9ad"),t.shadowBlur=0;this.drawShip(),this.drawSelection();for(const n of e.particles)t.globalAlpha=Math.max(0,n.life/n.max),this.rect(n.x,n.y,n.size,n.size,n.color);t.globalAlpha=1,this.drawBlasts();for(const n of e.pops)t.globalAlpha=Math.min(1,n.life),this.text(n.text,n.x-n.text.length*3,n.y,n.color,11);if(t.globalAlpha=1,e.phase==="playing"&&e.input.aim){const n=e.input.aim,f="#e6c18a";this.line([n.x-9,n.y-4,n.x-9,n.y-9,n.x-4,n.y-9],f),this.line([n.x+4,n.y+9,n.x+9,n.y+9,n.x+9,n.y+4],f),this.rect(n.x-1,n.y-1,2,2,f)}if(t.restore(),t.restore(),this.text("SECTOR 07    /    "+e.environment.name,20,55,"#81968a",10),this.text(e.environment.detail,20,73,e.site==="foundry"?"#a88e5f":"#466a5c",9),e.bossAwake&&!e.boss.dead){const n=Math.min(this.width-40,330),f=(this.width-n)/2;this.text("WARDEN / 07",f,101,"#e7ba78",12),this.text(e.plates.length>2?`${e.plates.length} ARMOR PLATES`:"CORE EXPOSED",f+n-125,101,"#9a9274",10),this.rect(f,111,n,4,"#493b2c"),this.rect(f,111,n*e.boss.hp/e.boss.maxHp,4,"#dba560")}const m=t.createLinearGradient(0,0,0,this.height);m.addColorStop(0,"#07101288"),m.addColorStop(.2,"#07101200"),m.addColorStop(.8,"#07101200"),m.addColorStop(1,"#07101288"),t.fillStyle=m,t.fillRect(0,0,this.width,this.height)}drawWalker(t){const e=this.c,s=t.body;for(const h of t.rig?[]:Et(t,this.game.bodies)){const l=[h.hip.x,h.hip.y,h.knee.x,h.knee.y,h.foot.x,h.foot.y];this.line(l,"#101b1c",15),this.line(l,"#936a55",9),this.line(l,"#c59e76",2),this.rect(h.knee.x-5,h.knee.y-5,10,10,"#ccac7c"),this.rect(h.knee.x-2,h.knee.y-2,4,4,"#392f2b"),this.rect(h.foot.x-12,h.foot.y-4,24,7,h.planted?"#d0ad80":"#886956")}e.save(),e.translate(Math.round(s.x),Math.round(s.y)),e.rotate(s.angle);const a=this.game.sprites[s.sprite],r=a.width*s.scale;if(e.drawImage(a.canvas,-r/2,-r/2,r,r),e.restore(),e.save(),e.translate(s.x,s.y-6),e.rotate(Math.atan2(t.aim.y,t.aim.x)),this.rect(-13,-13,27,26,"#312d28"),this.rect(-9,-9,19,18,"#d0a071"),this.rect(10,-9,51,18,"#192423"),this.rect(13,-6,45,12,"#90694f"),this.rect(18,-6,37,3,"#cfa875"),this.rect(53,-11,12,22,"#5f5140"),t.windup>0){const h=1-t.windup/.7;e.shadowColor="#ff9060",e.shadowBlur=this.reduced?0:12+h*20,this.rect(62,-5-h*4,5+h*7,10+h*8,"#ffe2a3"),e.shadowBlur=0}if(e.restore(),this.text(t.windup>0?"SHELL CHARGING":"STRIDER",s.x-35,s.y-104,t.windup>0?"#ffc78c":"#b39b7b",10),this.rect(s.x-35,s.y-92,70,3,"#49382c"),this.rect(s.x-35,s.y-92,70*s.hp/s.maxHp,3,"#e4ad79"),this.game.debug){const h=ot(s);this.line([...h,h[0]].flatMap(l=>[l.x,l.y]),"#eaa064")}}drawSelection(){var d,u;const t=this.game,e=this.c;if(t.mode!==3||t.phase!=="playing")return;const s=t.focus??t.hover;if(!s)return;const a=ot(s),r=((d=t.rigFor(s))==null?void 0:d.root)===s&&((u=t.rigFor(s))!=null&&u.shielded)?"#df9ef2":"#b5f5d5";this.line([...a,a[0]].flatMap(p=>[p.x,p.y]),r,3),t.focus&&t.input.field&&this.line([t.ship.x,t.ship.y,s.x,s.y],r,2);const h=t.focusLabel,l=(h.length*2.8+10)/this.scale;e.save(),e.translate(A(s.x,t.camera+l,t.camera+t.viewWidth-l),Math.max(s.y-s.radius-24,t.cameraY+35/this.scale)),e.scale(1/this.scale,1/this.scale),this.rect(-h.length*2.8-5,-14,h.length*5.6+10,19,"#0b171dee"),this.text(h,-h.length*2.8,0,r,9),e.restore()}drawBlasts(){const t=this.c;for(const e of this.game.blasts){const s=e.age,a=Math.max(0,1-s/.95);if(t.save(),t.translate(e.x,e.y),this.reduced){t.globalAlpha=a*.4,t.fillStyle="#d99b55",t.beginPath(),t.arc(0,0,65,0,Math.PI*2),t.fill(),t.restore();continue}for(let d=0;d<9;d++){const u=d*2.39996,p=25+s*110,m=24+s*48;t.globalAlpha=a*.28,this.rect(Math.cos(u)*p-m/2,Math.sin(u)*p-m/2-s*65,m,m,"#665549")}t.globalCompositeOperation="lighter";const r=Math.min(1,s/.45)*e.radius,h=Math.max(0,1-s/.36),l=t.createRadialGradient(0,0,2,0,0,90+s*180);l.addColorStop(0,`rgba(255,221,134,${h*.8})`),l.addColorStop(.45,`rgba(242,112,38,${a*.35})`),l.addColorStop(1,"#f2772500"),t.globalAlpha=1,t.fillStyle=l,t.fillRect(-280,-280,560,560),s<.6&&(t.globalAlpha=1-s/.6,t.strokeStyle="#ffd285",t.lineWidth=7*(1-s/.6)+1,t.beginPath(),t.arc(0,0,r,0,Math.PI*2),t.stroke());for(let d=0;d<14;d++){const u=d*Math.PI*2/14+e.x*.01,p=35+s*520;t.globalAlpha=a*.75,this.line([Math.cos(u)*p,Math.sin(u)*p,Math.cos(u)*(p+35*a),Math.sin(u)*(p+35*a)],d%2?"#ffc677":"#fff2bd",3)}if(h>0){t.globalAlpha=h;const d=18+Math.sin(Math.min(1,s/.25)*Math.PI)*60;this.rect(-d,-d*.55,d*2,d*1.1,"#ffd074"),this.rect(-d*.55,-d,d*1.1,d*2,"#fff2c1")}t.restore()}}drawField(){const t=this.c,e=this.game;if(e.mode===3||!e.input.field&&!e.held.length)return;const s=e.fieldCenter,a=e.aim(),r=e.mode===0?103:e.mode===1?52-e.charge*24:52,h={x:e.ship.x+a.x*29*O.scale,y:e.ship.y+a.y*29*O.scale};this.line([h.x,h.y,s.x,s.y],"#b8f2ce55",2),t.save(),t.translate(s.x,s.y),t.strokeStyle="#92c9ad33",t.lineWidth=1,t.setLineDash([2,10]),t.beginPath(),t.arc(0,0,e.range,0,Math.PI*2),t.stroke(),t.setLineDash([]);const l=t.createRadialGradient(0,0,4,0,0,r*1.4);l.addColorStop(0,"#c6ffe123"),l.addColorStop(.7,"#87e7bb15"),l.addColorStop(1,"#87e7bb00"),t.fillStyle=l,t.fillRect(-r*1.4,-r*1.4,r*2.8,r*2.8);const d=this.reduced?0:e.vortexPhase;for(let u=0;u<(this.reduced?2:5);u++){t.beginPath();for(let p=0;p<=24;p++){const m=p/24,n=12+m*r,f=d+u*Math.PI*2/5+m*2.2,b=Math.cos(f)*n,o=Math.sin(f)*n;p===0?t.moveTo(b,o):t.lineTo(b,o)}t.strokeStyle=u%2?"#c7ffe1b0":"#79d6b57a",t.lineWidth=u%2?2:1,t.stroke()}if(!this.reduced)for(let u=0;u<12;u++){const p=(e.time*2.4+u/12)%1,m=r+65*(1-p),n=d*.7+u*2.39996;t.globalAlpha=p*.65,this.line([Math.cos(n)*m,Math.sin(n)*m,Math.cos(n+.1)*(m-15),Math.sin(n+.1)*(m-15)],"#c7ffe1",2)}t.globalAlpha=1,this.rect(-2,-2,4,4,"#d7ffe4"),t.restore()}drawShip(){const t=this.c,e=this.game,s=e.ship,a=e.visualTime,r=e.aim();t.save(),t.translate(Math.round(s.x),Math.round(s.y)),t.rotate(Math.atan2(r.y,r.x)),t.scale(O.scale,O.scale),s.invulnerable>0&&Math.floor(a*16)%2&&(t.globalAlpha=.5);const h=12+Math.sin(a*42)*6+Math.abs(s.vx)*.04;this.rect(-34-h,-6,h,12,"#477f6e"),this.rect(-30-h*.65,-4,h*.65,8,"#9ce8c4"),this.rect(-33,-3,8,6,"#e6ffe0"),this.rect(-23,-20,21,7,"#344b47"),this.rect(-19,-22,17,5,"#9eb5a1"),this.rect(-17,14,19,8,"#8da58f"),this.rect(-23,13,16,5,"#364c47"),this.rect(-29,-13,42,27,"#152d30"),this.rect(-27,-10,43,19,"#738f7f"),this.rect(-18,-13,25,5,"#b9c8a0"),this.rect(-13,-8,32,14,"#abc0a4"),this.rect(-6,-7,22,10,"#2a655e"),this.rect(1,-7,14,3,"#a8f7d3"),this.rect(13,-4,8,6,"#83d8b9"),this.rect(-22,-6,8,12,"#263f3d"),this.rect(-21,-3,5,5,"#e0aa58"),this.rect(-12,10,22,4,"#4d7164"),this.rect(18,-12,6,6,"#b9d2a9"),this.rect(18,8,6,6,"#b9d2a9"),this.rect(23,-10,5,4,"#91d5b2"),this.rect(23,8,5,4,"#91d5b2"),e.input.field&&(t.shadowColor="#bdffda",t.shadowBlur=15,this.rect(24,-2,4,4,"#ddffe0"),t.shadowBlur=0),t.restore(),e.phase==="ready"&&(this.text("SALVAGE TUG / M-03",s.x-56,s.y+51,"#a2baa6",10),this.line([s.x,s.y+28,s.x,s.y+37],"#5d8271"))}drawBoss(){const t=this.c,e=this.game,s=e.boss,a=e.visualTime;if(s.dead||s.x-e.camera>e.viewWidth+170)return;t.save(),t.translate(s.x,s.y),t.rotate(s.angle),t.translate(-s.x,-s.y),this.rect(s.x-62,s.y-75,122,16,"#172e31"),this.rect(s.x-45,s.y-81,75,8,"#5f7667"),this.rect(s.x-70,s.y+57,140,13,"#1d3636"),t.save(),t.translate(s.x,s.y),t.shadowColor=e.plates.length>2?"#bd7b31":"#f5c879",t.shadowBlur=25+Math.sin(a*5)*9;const r=64*s.scale;t.drawImage(e.sprites[10].canvas,-r/2,-r/2,r,r),t.restore(),this.rect(s.x-75,s.y-18,14,36,"#3f5d50"),this.rect(s.x-85,s.y-6,18,10,"#b0854b"),this.rect(s.x+35,s.y-97,6,15,"#849275"),this.rect(s.x+34,s.y-100,8,4,Math.sin(a*3)>0?"#f7c769":"#7e623c"),this.text("W / 07",s.x-25,s.y-65,"#d1c399",11),t.restore()}}const pe=2,ge=[["#263d40","#49615f","#81928a","#b6c4a7","#efb663"],["#402f26","#835539","#bd8050","#e7b76f","#f4db9a"],["#273338","#51656a","#839c9e","#cee0d0","#edaa56"]];function ye(i){const t=document.createElement("canvas");t.width=32,t.height=32;const e=t.getContext("2d",{willReadFrequently:!0}),s=ge[i%3],a=(h,l,d,u,p)=>{e.fillStyle=p,e.fillRect(h,l,d,u)};if(i<6){const h=[[[6,11,21,9],[10,8,12,14]],[[9,5,10,23],[6,10,17,9]],[[5,12,24,7],[7,9,7,12]],[[7,7,18,17],[4,13,23,8]],[[9,8,17,17],[6,12,11,9]],[[5,10,22,13],[10,6,12,20]]];for(const[l,d,u,p]of h[i])a(l,d,u,p,s[0]);a(9,11,14,10,s[1]),a(10,10,11,3,s[2]),a(10,14,9,2,s[3]),a(17,18,5,3,s[2]),a(8,12,2,3,s[4]),a(22,13,2,2,s[4]),i%2===0?(a(14,12,3,7,s[0]),a(10,22,10,2,s[2])):(a(19,8,3,12,s[0]),a(11,18,4,4,s[4]))}else if(i===6)a(10,5,12,23,"#342b20"),a(8,9,16,15,"#4e6760"),a(11,8,10,16,"#b76734"),a(12,9,8,14,"#efac50"),a(13,10,3,11,"#fff1b0"),a(10,5,12,4,"#9ea99a"),a(10,24,12,3,"#8c9d91"),a(8,13,3,5,"#253f3e"),a(21,13,3,5,"#253f3e");else if(i===7){a(4,9,24,16,"#243a3b"),a(6,7,19,16,"#617571"),a(7,8,16,5,"#91a199"),a(8,15,17,6,"#374c4a"),a(8,16,14,2,"#d3a555");for(let h=9;h<23;h+=5)a(h,16,2,2,"#4b3c2b");a(7,10,2,2,"#d0d6b5"),a(23,21,2,2,"#b7c2a4"),a(12,22,11,4,"#172a2d")}else if(i===8)a(7,10,18,13,"#794934"),a(10,7,12,18,"#bd8a52"),a(9,11,16,6,"#e3bc79"),a(3,12,5,3,"#637776"),a(24,12,6,3,"#637776"),a(14,12,9,4,"#47281f"),a(18,12,5,3,"#f1714c"),a(11,23,3,4,"#f4c96d"),a(19,23,3,4,"#f4c96d");else if(i===9)a(6,11,21,12,"#515f55"),a(7,8,17,10,"#9c9977"),a(9,8,12,3,"#cbc397"),a(15,5,4,5,"#eabe68"),a(7,20,20,4,"#242e2a"),a(6,21,7,7,"#0c1c20"),a(21,21,7,7,"#0c1c20"),a(8,23,3,3,"#a1ad91"),a(23,23,3,3,"#a1ad91"),a(5,13,5,3,"#f36b51");else if(i===10){a(5,8,22,17,"#152f31"),a(8,5,16,23,"#657f70"),a(10,7,12,19,"#a86a38"),a(12,9,8,15,"#ea9948"),a(13,10,5,12,"#ffe7a0"),a(15,11,3,9,"#fffbd0");for(let h=9;h<24;h+=6)a(6,h,5,2,"#203b3c"),a(21,h,5,2,"#203b3c")}else if(i===11){a(2,11,28,10,"#253b3a"),a(2,10,28,3,"#a19c78"),a(2,19,28,3,"#596f62"),a(4,13,24,6,"#765a39"),a(6,14,20,3,"#b28c51");for(const h of[3,26])a(h,12,3,7,"#41544b"),a(h,13,2,2,"#e5c283")}else if(i===12){a(4,4,24,24,"#243b39"),a(6,6,20,20,"#8b693e"),a(8,8,16,16,"#455e51"),a(7,7,18,3,"#c3a164"),a(7,22,18,3,"#a08552");for(let h=0;h<5;h++)a(8+h*3,10+h*2,4,3,"#ad8a52");a(5,5,3,3,"#e4c886"),a(24,24,3,3,"#d2b87b")}else i===13?(a(3,9,26,16,"#283a37"),a(6,6,20,19,"#744d37"),a(5,9,23,11,"#bd8650"),a(7,7,16,4,"#dbc18a"),a(7,13,18,6,"#5c4736"),a(9,14,13,3,"#edaa64"),a(4,20,24,5,"#465d50"),a(9,23,14,4,"#213934"),a(4,11,5,4,"#ed7855"),a(10,12,3,3,"#ffdab0"),a(21,8,3,3,"#e4c77f"),a(25,13,4,9,"#304a44"),a(26,14,2,4,"#95a991")):i===14?(a(9,6,14,20,"#253b38"),a(6,10,20,12,"#5a4836"),a(9,8,14,16,"#af5e38"),a(11,7,10,18,"#efac58"),a(8,12,16,8,"#d67b40"),a(12,11,8,10,"#fff0b0"),a(14,12,4,7,"#fff8dc"),a(5,13,4,6,"#8b9b80"),a(23,13,4,6,"#8b9b80")):i===15?(a(2,12,28,8,"#233733"),a(3,13,26,5,"#a88d66"),a(5,13,22,2,"#d3c094"),a(2,11,5,10,"#496352"),a(25,11,5,10,"#496352"),a(3,13,3,3,"#ebc486"),a(26,13,3,3,"#ebc486")):(a(8,3,16,26,"#293736"),a(10,5,12,22,"#704f80"),a(12,7,8,18,"#df9ef2"),a(14,8,4,16,"#fff0ed"),a(5,8,5,4,"#b4c29b"),a(22,20,5,4,"#b4c29b"));const r=e.getImageData(0,0,32,32).data;return{canvas:t,hull:_t(r,32,32,pe),width:64,height:64}}function me(){return Array.from({length:17},(i,t)=>ye(t))}function xe(i){const t=document.createElement("canvas");t.width=192,t.height=Math.ceil(i.length/6)*32;const e=t.getContext("2d");return i.forEach((s,a)=>e.drawImage(s.canvas,a%6*32,Math.floor(a/6)*32)),t}class ve{constructor(t,e,s){g(this,"isOpen",!1);g(this,"hovered",null);g(this,"index",0);g(this,"tabHeld",!1);g(this,"abort",new AbortController);g(this,"buttons",[]);this.root=t,this.choose=e,this.changed=s;const a=t.querySelector(".radial-dial");[{field:0},{field:1},{field:2},{field:3},{release:0},{release:1},{release:2}].forEach((h,l)=>{const d=document.createElement("button"),u=(-90+l*360/7)*Math.PI/180;d.className="radial-choice",d.style.setProperty("--x",String(50+Math.cos(u)*36)),d.style.setProperty("--y",String(50+Math.sin(u)*36)),d.dataset[h.field!==void 0?"selectMode":"selectRelease"]=String(h.field??h.release),d.innerHTML=`<small>${h.field!==void 0?"FIELD":"RELEASE"}</small><b>${h.field!==void 0?St[h.field]:Mt[h.release]}</b>`,d.addEventListener("pointerenter",()=>this.highlight(l,h),{signal:this.abort.signal}),d.addEventListener("focus",()=>this.highlight(l,h),{signal:this.abort.signal}),d.addEventListener("click",()=>{this.choose(h),this.highlight(l,h)},{signal:this.abort.signal}),a.append(d),this.buttons.push(d)}),t.querySelector("button[data-close]").addEventListener("click",()=>this.close(!1),{signal:this.abort.signal})}highlight(t,e){this.index=t,this.hovered=e,this.buttons.forEach((s,a)=>s.classList.toggle("hovered",a===t))}open(t=!1){this.isOpen||(this.tabHeld=t,this.isOpen=!0,this.hovered=null,this.root.hidden=!1,this.buttons.forEach(e=>e.classList.remove("hovered")),this.changed(!0))}close(t=!1){this.isOpen&&(t&&this.hovered&&this.choose(this.hovered),this.isOpen=!1,this.tabHeld=!1,this.root.hidden=!0,this.changed(!1))}keyDown(t){if(t.code==="Tab")return t.preventDefault(),t.repeat||(this.isOpen?this.tabHeld||this.close(!0):this.open(!0)),!0;if(!this.isOpen)return!1;if(t.code==="Escape")return t.preventDefault(),this.close(!1),!0;if(t.code==="Enter")return t.preventDefault(),this.close(!0),!0;if(["ArrowRight","ArrowDown","ArrowLeft","ArrowUp"].includes(t.code)){t.preventDefault();const e=(this.index+(t.code==="ArrowRight"||t.code==="ArrowDown"?1:-1)+7)%7;return this.buttons[e].focus(),!0}return!0}keyUp(t){t.code==="Tab"&&this.tabHeld&&(t.preventDefault(),this.close(!0))}update(t,e){this.buttons.forEach(s=>s.setAttribute("aria-pressed",String(s.dataset.selectMode!==void 0?Number(s.dataset.selectMode)===t:Number(s.dataset.selectRelease)===e))),this.root.querySelector("[data-field-name]").textContent=St[t],this.root.querySelector("[data-release-name]").textContent=Mt[e]}destroy(){this.abort.abort()}}const V=new jt(ct("media")?Vt:void 0,"GRAV / RIFT"),y=i=>document.getElementById(i),xt=y("app"),v=y("game"),Nt=me(),c=new fe(Nt),Y=new ue(v,c);y("atlas").append(xe(Nt));const D=new Set;let z=0,H=!1,G,rt,nt=c.phase,ht=ct("storage")?"Host save available · personal best is saved after each run.":"Host save unavailable · personal best lasts for this session.",lt={x:0,y:0},st=!1,_=!1,B=!1,et=null,W=null,Rt=0,vt=0,wt=0,Ot=0,Ht=0;const Ft={grab:0,throw:0,hit:0,boom:0,upgrade:0},gt=new AbortController,T=new ve(y("radial"),i=>{i.field!==void 0&&c.setMode(i.field),i.release!==void 0&&c.setRelease(i.release),Q()},i=>{c.tacticalOpen=i,B=!1,D.delete("KeyF"),c.input.fire=!1,i||(c.fieldWas=_||st||D.has("Space"),it())}),R=(i,t,e)=>i.addEventListener(t,e,{signal:gt.signal});async function kt(i){let t=0;try{return await Promise.race([i,new Promise((e,s)=>{t=window.setTimeout(()=>s(new Error("Save request timed out")),3500)})])}finally{clearTimeout(t)}}async function yt(i,t){if(ct("storage")){try{await kt(bt.setItem(i,t)),ht="Host save connected · flight record saved."}catch{ht="Host save failed · flight record kept for this session. Retry with Save record."}y("storageStatus").textContent=ht}}async function we(){if(ct("storage"))try{const[i,t]=await Promise.all([kt(bt.getItem("grav-rift.best.v1")),kt(bt.getItem("grav-rift.sound.v1"))]),e=Number(i);Number.isFinite(e)&&e>=0&&(z=Math.max(z,Math.min(99999999,Math.floor(e)))),H=t==="true",At()}catch{ht="Host save could not be read · playing with session records."}}function $(){if(H&&(V.activate(),G=V.context,!!G&&!rt)){rt=G.createBuffer(1,Math.floor(G.sampleRate*.7),G.sampleRate);const i=rt.getChannelData(0);for(let t=0;t<i.length;t++)i[t]=Math.random()*2-1}}c.onSound=i=>{if(!H||!G||!V.audible)return;const t=G.currentTime;if(t-Ft[i]<(i==="grab"?.13:i==="hit"?.08:.05)||t-Ht<.015)return;if(Ft[i]=t,Ht=t,i==="boom"&&rt){const r=G.createBufferSource(),h=G.createBiquadFilter(),l=G.createGain();r.buffer=rt,h.type="lowpass",h.frequency.setValueAtTime(2200,t),h.frequency.exponentialRampToValueAtTime(130,t+.65),l.gain.setValueAtTime(.3,t),l.gain.exponentialRampToValueAtTime(.001,t+.7),r.connect(h),h.connect(l),l.connect(V.output),r.start(t),r.stop(t+.7)}const e=G.createOscillator(),s=G.createGain(),a={grab:[270,410,.06],throw:[220,45,.25],hit:[120,35,.13],boom:[70,22,.55],upgrade:[440,880,.25]}[i];e.type=i==="upgrade"||i==="grab"?"sine":"triangle",e.frequency.setValueAtTime(a[0],t),e.frequency.exponentialRampToValueAtTime(a[1],t+a[2]),s.gain.setValueAtTime(i==="boom"?.16:.07,t),s.gain.exponentialRampToValueAtTime(.001,t+a[2]),e.connect(s),s.connect(V.output),e.start(t),e.stop(t+a[2]+.01)};function At(){V.setMuted(!H),y("sound").setAttribute("aria-label",H?"Mute sound":"Enable sound"),y("sound").title=H?"Mute sound":"Enable sound",y("sound").querySelector("span").style.display=H?"none":"",y("soundSetting").checked=H}function U(){T.close(!1),D.clear(),B=!1,lt={x:0,y:0},st=!1,_=!1,et=null,W=null,y("stick").style.transform="",y("fieldButton").classList.remove("held"),c.clearInput(),c.held.forEach(i=>{i.held=!1,i.thrown=0})}function it(){v.focus({preventScroll:!0})}function Ct(i){U(),$(),c.start(i),nt="ready",at(),it()}function ft(){c.pause(),U(),at()}function Tt(){c.phase==="paused"&&(U(),c.phase="playing",at(),it(),$())}function Yt(i){c.setMode(i),Q()}function at(){const i=c.phase;if(xt.classList.toggle("intro-open",i==="ready"),xt.classList.toggle("modal-open",i==="paused"||i==="won"||i==="lost"),xt.dataset.phase=i,y("intro").hidden=i!=="ready",y("modal").hidden=i==="ready"||i==="playing",y("pause").setAttribute("aria-label",i==="paused"?"Resume game":"Pause game"),y("pause").textContent=i==="paused"?"▷":"Ⅱ",y("pause").disabled=i==="ready",i==="paused"||i==="won"||i==="lost"){const t=i!=="paused";y("modalSerial").textContent=t?"FLIGHT RECORDER / SECTOR 07":"FLIGHT COMPUTER / STANDBY",y("modalTitle").textContent=i==="won"?"Reactor neutralized.":i==="lost"?"Tug signal lost.":"Flight paused.",y("modalText").textContent=i==="won"?"The Warden is scrap. Nothing goes to waste.":i==="lost"?"The wreckage is still out there. Take another run.":"Your cargo and flight progress are safe.",y("resume").hidden=t,y("help").hidden=t,y("retry").textContent=t?"DEPLOY AGAIN ↗":"RESTART RUN",y("results").innerHTML=t?`<div><small>SCORE</small><b>${c.score}</b></div><div><small>PLATES RIPPED</small><b>${c.torn}</b></div><div><small>WRECKS</small><b>${c.kills}</b></div>`:"",y("storageStatus").textContent=ht,t&&nt!==i&&(z=Math.max(z,c.score),yt("grav-rift.best.v1",String(z))),nt!==i&&y(t?"retry":"resume").focus({preventScroll:!0})}nt=i,Q()}function Q(){var h,l;const i=Math.ceil(c.ship.hp),t=c.held.length;y("hullValue").textContent=`${i}%`,y("hull").setAttribute("aria-valuenow",String(i)),Array.from(y("hull").children).forEach((d,u)=>d.classList.toggle("empty",u>=Math.ceil(i/10))),y("salvage").textContent=String(c.salvage).padStart(3,"0"),y("fieldLevel").textContent=`MK. ${["I","II","III","IV"][c.level]}`,document.querySelectorAll("[data-mode]").forEach(d=>{const u=Number(d.dataset.mode)===c.mode;d.classList.toggle("active",u),d.setAttribute("aria-pressed",String(u))}),T.update(c.mode,c.launcher.mode),y("releaseName").textContent=Mt[c.launcher.mode],y("payloadCount").textContent=String(t),y("charge").style.width=`${c.charge*100}%`,y("chargeLabel").textContent=c.mode===3?(h=c.focus)!=null&&h.held?"SINGLE OBJECT HELD":"SURGICAL READY":c.held.some(d=>d.kind==="shell")?"SHELL SECURED":c.input.field?c.charge>=1?"FULLY CHARGED":"FIELD ACTIVE":"FIELD IDLE";const e=y("upgrade");e.disabled=c.level>=3||c.salvage<c.upgradeCost||!["playing","paused"].includes(c.phase),y("upgradePrice").textContent=c.level>=3?"MAX":`◇ ${c.upgradeCost}`,e.title=c.level>=3?"Field fully upgraded":`Upgrade: ${c.upgradeCost} salvage. More range, stronger vortex, +20 hull. (U)`,y("best").textContent=`BEST ${String(z).padStart(5,"0")}`,y("runTime").textContent=`${String(Math.floor(c.time/60)).padStart(2,"0")}:${String(Math.floor(c.time%60)).padStart(2,"0")}`,y("flightStatus").textContent=c.phase==="ready"?"SYSTEMS STANDBY":c.phase==="paused"?"FLIGHT PAUSED":c.practice?"PRACTICE / HULL REGEN":c.bossAwake?"REACTOR BAY / HOSTILE":"SALVAGE RUN / LIVE",y("mission").querySelector("b").textContent=c.bossAwake?c.plates.length>2?`Rip armor · ${6-c.plates.length} / 4 plates`:"Core exposed — hurl debris!":"Reach the reactor bay",y("toast").textContent=c.phase==="playing"&&c.messageTime>0?c.message:"",v.dataset.payload=String(t),v.dataset.plates=String(c.plates.length),v.dataset.kills=String(c.kills),v.dataset.shots=String(c.shots),v.dataset.fieldMode=String(c.mode),v.dataset.releaseMode=String(c.launcher.mode),v.dataset.simTime=String(c.time),v.dataset.timeScale=c.tacticalOpen?"0.05":"1",v.dataset.limbsTorn=String(c.limbsTorn),v.dataset.cellsExtracted=String(c.cellsExtracted),v.dataset.liveThrows=String(c.liveThrows),v.dataset.focus=String(((l=c.focus)==null?void 0:l.id)??""),v.dataset.focusLabel=c.mode===3?c.focusLabel:"",v.dataset.rigs=JSON.stringify(c.rigs.map(d=>({id:d.root.id,kind:d.root.kind,x:d.root.x,y:d.root.y,hp:d.root.hp,shielded:d.shielded,legs:d.legs,parts:d.connected().map(u=>({key:u.key,role:u.role,x:u.body.x,y:u.body.y,id:u.body.id}))}))),v.dataset.coreHull=String(Math.ceil(c.boss.hp)),v.dataset.site=c.site,v.dataset.demolished=String(c.demolished),v.dataset.explosions=String(c.explosions),v.dataset.structureParts=String(c.bodies.filter(d=>d.kind==="structure"&&d.attached).length),v.dataset.cellsHeld=String(c.held.filter(d=>d.kind==="cell").length),v.dataset.shellsCaught=String(c.shellsCaught),v.dataset.shellsReturned=String(c.shellsReturned),v.dataset.returnKills=String(c.returnKills),v.dataset.shellsHeld=String(c.held.filter(d=>d.kind==="shell").length),v.dataset.walkersAlive=String(c.walkers.filter(d=>!d.body.dead).length);const s=c.walkers.find(d=>!d.body.dead),a=c.bodies.find(d=>{var u;return!d.dead&&((u=d.ordnance)==null?void 0:u.state)==="hostile"});v.dataset.walkerX=String((s==null?void 0:s.body.x)??""),v.dataset.walkerY=String((s==null?void 0:s.body.y)??""),v.dataset.shellX=String((a==null?void 0:a.x)??""),v.dataset.shellY=String((a==null?void 0:a.y)??"");const r=c.fieldCenter;v.dataset.fieldX=String(r.x),v.dataset.fieldY=String(r.y),v.dataset.shipX=String(c.ship.x),v.dataset.shipY=String(c.ship.y),v.dataset.camera=String(c.camera),v.dataset.cameraY=String(c.cameraY),v.dataset.viewScale=String(Y.scale),v.setAttribute("aria-label",`Salvage combat. ${St[c.mode]} field. Hull ${i} percent. ${t} parts held. ${c.plates.length} armor plates remain.`)}y("selector").addEventListener("click",()=>{c.phase==="playing"&&(T.isOpen?T.close(!1):T.open(),Q())});y("deploy").addEventListener("click",()=>Ct(!1));y("practice").addEventListener("click",()=>Ct(!0));y("site").addEventListener("change",i=>{const t=i.target.value;c.phase==="ready"&&(t==="foundry"||t==="orbit")&&(c.site=t,c.reset(),Q())});y("changeSite").addEventListener("click",()=>{U(),c.phase="ready",c.reset(),at(),y("site").focus()});y("pause").addEventListener("click",()=>c.phase==="paused"?Tt():ft());y("resume").addEventListener("click",Tt);y("retry").addEventListener("click",()=>Ct(c.practice));y("sound").addEventListener("click",()=>{H=!H,At(),$(),yt("grav-rift.sound.v1",String(H))});y("soundSetting").addEventListener("change",()=>{H=y("soundSetting").checked,At(),$(),yt("grav-rift.sound.v1",String(H))});y("upgrade").addEventListener("click",()=>{c.upgrade(),Q(),it()});document.querySelectorAll("[data-mode]").forEach(i=>i.addEventListener("click",()=>{Yt(Number(i.dataset.mode)),it()}));const pt=y("reduced");pt.checked=matchMedia("(prefers-reduced-motion: reduce)").matches;Y.reduced=pt.checked;pt.addEventListener("change",()=>Y.reduced=pt.checked);y("debug").addEventListener("change",i=>c.debug=i.target.checked);const ut=document.createElement("button");ut.className="text-button";ut.textContent="Save record";ut.hidden=!ct("storage");ut.addEventListener("click",()=>void yt("grav-rift.best.v1",String(Math.max(z,c.score))));y("storageStatus").after(ut);R(window,"keydown",i=>{var t;if(!["INPUT","SELECT"].includes((t=i.target)==null?void 0:t.tagName)&&!(c.phase==="playing"&&T.keyDown(i))){if(["Escape","KeyP"].includes(i.code)){i.preventDefault(),i.repeat||(c.phase==="paused"?Tt():ft());return}c.phase==="playing"&&["Space","ArrowUp","ArrowDown","ArrowLeft","ArrowRight","KeyW","KeyA","KeyS","KeyD","Digit1","Digit2","Digit3","Digit4","KeyU","KeyF","KeyR"].includes(i.code)&&(i.preventDefault(),D.add(i.code),i.code==="KeyU"&&!i.repeat&&c.upgrade(),i.code==="KeyR"&&!i.repeat&&c.setRelease((c.launcher.mode+1)%3),i.code.startsWith("Digit")&&Yt(Number(i.code.slice(-1))-1))}});R(window,"keyup",i=>{T.keyUp(i),D.delete(i.code)});R(v,"mousedown",i=>{c.phase!=="playing"||T.isOpen||![0,2].includes(i.button)||(i.preventDefault(),$(),it(),i.button===2?_=!0:B=!0,W={x:i.clientX,y:i.clientY},c.input.aim=Y.worldPoint(i.clientX,i.clientY))});R(window,"mouseup",i=>{i.button===2&&(_=!1),i.button===0&&(B=!1)});R(v,"pointerdown",i=>{i.pointerType!=="touch"||c.phase!=="playing"||T.isOpen||(i.preventDefault(),v.setPointerCapture(i.pointerId),_=!0,W={x:i.clientX,y:i.clientY},$())});R(v,"pointermove",i=>{T.isOpen||(W={x:i.clientX,y:i.clientY})});R(v,"pointerup",i=>{i.pointerType==="touch"&&(_=!1,v.hasPointerCapture(i.pointerId)&&v.releasePointerCapture(i.pointerId))});R(v,"pointercancel",U);v.addEventListener("wheel",i=>{var t;c.mode===3&&((t=c.focus)!=null&&t.held)&&!T.isOpen&&(i.preventDefault(),c.focus.angle+=i.deltaY*.002)},{passive:!1,signal:gt.signal});v.addEventListener("contextmenu",i=>i.preventDefault());const J=y("joystick");R(J,"pointerdown",i=>{c.phase!=="playing"||et!==null||(et=i.pointerId,J.setPointerCapture(i.pointerId),$(),W=null,c.input.aim=null,Bt(i))});function Bt(i){if(i.pointerId!==et)return;const t=J.getBoundingClientRect(),e=i.clientX-t.left-t.width/2,s=i.clientY-t.top-t.height/2,a=Math.max(1,Math.hypot(e,s)/32);lt={x:e/a/32,y:s/a/32},y("stick").style.transform=`translate(${e/a}px,${s/a}px)`}R(J,"pointermove",Bt);const It=i=>{i.pointerId===et&&(et=null,lt={x:0,y:0},y("stick").style.transform="")};R(J,"pointerup",It);R(J,"pointercancel",It);R(J,"lostpointercapture",It);const dt=y("fireButton");R(dt,"pointerdown",i=>{c.phase!=="playing"||T.isOpen||(dt.setPointerCapture(i.pointerId),B=!0,$())});R(dt,"pointerup",()=>B=!1);R(dt,"pointercancel",()=>B=!1);R(dt,"lostpointercapture",()=>B=!1);const q=y("fieldButton");R(q,"pointerdown",i=>{c.phase==="playing"&&(q.setPointerCapture(i.pointerId),st=!0,W=null,c.input.aim=null,q.classList.add("held"),$())});R(q,"pointerup",()=>{st=!1,q.classList.remove("held")});R(q,"pointercancel",U);R(q,"lostpointercapture",()=>{st=!1,q.classList.remove("held")});R(window,"blur",()=>{c.phase==="playing"?ft():U()});document.addEventListener("visibilitychange",()=>{document.hidden&&ft()},{signal:gt.signal});const $t=new ResizeObserver(()=>{Y.resize(),(innerWidth<280||innerHeight<280)&&ft()});$t.observe(y("viewport"));R(window,"orientationchange",()=>{U(),Y.resize()});function Ut(i){const t=Math.min(.05,vt?(i-vt)/1e3:0);for(vt=i,wt+=t*(T.isOpen?.05:1),c.input.x=Math.max(-1,Math.min(1,lt.x+(D.has("KeyD")||D.has("ArrowRight")?1:0)-(D.has("KeyA")||D.has("ArrowLeft")?1:0))),c.input.y=Math.max(-1,Math.min(1,lt.y+(D.has("KeyS")||D.has("ArrowDown")?1:0)-(D.has("KeyW")||D.has("ArrowUp")?1:0))),T.isOpen||(c.input.field=c.phase==="playing"&&(_||st||D.has("Space"))),c.input.fire=c.phase==="playing"&&!T.isOpen&&(B||D.has("KeyF"));wt>=1/120;)W&&!T.isOpen&&(c.input.aim=Y.worldPoint(W.x,W.y)),c.step(1/120),wt-=1/120;W&&!T.isOpen&&(c.input.aim=Y.worldPoint(W.x,W.y)),Y.draw(),c.phase!==nt&&(U(),at()),i-Ot>100&&(Q(),Ot=i),Rt=requestAnimationFrame(Ut)}R(window,"pagehide",()=>{T.destroy(),gt.abort(),$t.disconnect(),cancelAnimationFrame(Rt),V.dispose()});Y.resize();at();we();Rt=requestAnimationFrame(Ut);
</script>
  <style>:root{--void:#0a1113;--panel:#101d1f;--line:#2c3c36;--mint:#a5ead3;--amber:#f3b65b;--text:#dce6d6;--muted:#859c8f;color-scheme:dark;font:14px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;--napplet-text-selection:none}*{box-sizing:border-box}html,body,#app{height:100%;margin:0;background:var(--void);color:var(--text)}body{overflow:hidden}button,input{font:inherit}button{cursor:pointer;color:inherit}button:disabled{cursor:default;opacity:.45}button:focus-visible,summary:focus-visible,canvas:focus-visible{outline:2px solid var(--mint);outline-offset:-3px}button{touch-action:manipulation}button,canvas{user-select:none;-webkit-user-select:none}button{border-radius:0}button:hover:not(:disabled){filter:brightness(1.18)}[hidden]{display:none!important}small,.serial,.stat-label,.flight-status,.toast,.brief-steps,.intro-note,.mission,.keyline,.mode,.payload,.upgrade,.storage-status,kbd{font-family:SFMono-Regular,Consolas,Liberation Mono,monospace}#app{display:grid;grid-template-rows:78px minmax(0,1fr)88px 30px;position:relative;padding:0 max(0px,env(safe-area-inset-right)) 0 max(0px,env(safe-area-inset-left))}.hud{display:flex;align-items:center;gap:26px;padding:0 28px;background:#0e191b;border-bottom:1px solid #304238;z-index:2}.brand{display:flex;align-items:center;gap:11px;min-width:176px}.brand-mark{font-size:41px;line-height:1;color:var(--amber);font-weight:300}.brand b{font-size:20px;letter-spacing:2px;font-weight:850}.brand b span{color:var(--amber);margin:0 2px}.brand small{display:block;font-size:8px;letter-spacing:2.5px;color:#91a392;margin-top:2px}.hull-block{max-width:240px;width:22%;margin-left:auto}.stat-label{font-size:9px;letter-spacing:1.25px;color:#a1b3a3;display:flex;justify-content:space-between;gap:12px}.stat-label span{color:#dce6d6;letter-spacing:0}.hull-meter{display:flex;gap:4px;margin-top:9px}.hull-meter i{height:6px;background:#a9c3a1;flex:1;transition:background .2s}.hull-meter i.empty{background:#283a33}.score-block{padding-left:24px;border-left:1px solid var(--line);min-width:94px}.score-block small{display:block;font-size:8px;letter-spacing:2px;color:#a4b29b}.score-block strong{display:block;font:23px/1.3 SFMono-Regular,Consolas,monospace;font-weight:400;color:#e1c992}.diamond{font-size:18px;color:var(--amber)}.icon-button{position:relative;background:transparent;border:1px solid #34473b;width:38px;height:38px;font-size:20px;flex-shrink:0}.sound-slash{position:absolute;top:7px;left:11px;color:#b3c1ac}.viewport{position:relative;overflow:hidden;min-height:0;background:#122226}#game{display:block;width:100%;height:100%;touch-action:none;outline:none}.flight-status{position:absolute;left:28px;top:18px;font-size:9px;letter-spacing:1.3px;display:flex;align-items:center;gap:9px;color:#b5c5ac}.live-dot{width:5px;height:5px;background:#a9c5a2;box-shadow:0 0 6px #a9c5a244}#runTime{padding-left:15px;color:#778f80}.toast{position:absolute;top:50px;left:50%;transform:translate(-50%);padding:7px 12px;background:#132a25e8;border-left:2px solid var(--mint);font-size:10px;letter-spacing:.7px;max-width:calc(100% - 24px);width:max-content;color:#d0efcb;pointer-events:none}.toast:empty{display:none}.screen{position:absolute;top:0;right:0;bottom:0;left:0;z-index:4}.intro-screen{background:linear-gradient(90deg,#0c171af7,#0c171adb 32%,#0c171a30 74%);display:flex;align-items:center;padding:24px 7%}.intro-panel{width:520px;max-width:100%}.site-picker{display:flex;align-items:center;gap:12px;max-width:390px;margin:0 0 14px;color:var(--amber);font:9px SFMono-Regular,Consolas,monospace;letter-spacing:1px}.site-picker span{flex:0 0 54px}.site-picker select{min-width:0;width:100%;min-height:44px;padding:8px;border:1px solid #71846c;border-radius:0;color:#d6e3c5;background:#182b29;font:10px SFMono-Regular,Consolas,monospace}.site-picker select:focus-visible{outline:2px solid var(--amber);outline-offset:3px}.serial{display:block;color:var(--amber);font-size:9px;letter-spacing:1.8px;margin-bottom:18px}h1{font-size:clamp(32px,4.4vw,64px);line-height:.99;letter-spacing:-2px;font-weight:900;margin:0 0 22px}h1 em{font-style:normal;color:#b7d3b1}.intro-panel>p{font-size:15px;line-height:1.65;color:#a2b6a8;margin:0 0 24px}.brief-steps{display:flex;gap:18px;font-size:9px;color:#a9b9a6;margin-bottom:25px}.brief-steps span{max-width:110px}.brief-steps b{display:block;font-size:10px;color:#e0b675;margin-bottom:6px}.primary{background:var(--amber);color:#1c281f;border:1px solid #f1c680;padding:15px 20px;font:700 11px/1.4 SFMono-Regular,Consolas,monospace;letter-spacing:1.5px;display:flex;align-items:center;justify-content:space-between;gap:40px;min-width:230px;min-height:48px}.primary span{font-size:21px;line-height:14px}.text-button{background:none;border:none;padding:13px 0;font-size:10px;letter-spacing:.4px;color:#a9bbad}.text-button span{color:var(--amber);margin-left:12px}.intro-note{border-top:1px solid #34463a;margin-top:12px;padding-top:12px;max-width:340px;display:flex;justify-content:space-between;font-size:8px;letter-spacing:1px;color:#6f8b7e}.intro-callout{position:absolute;right:7%;bottom:18%;color:#a1b8a2}.intro-callout>span{font:8px monospace;letter-spacing:2px;color:#768f7d}.intro-callout strong{display:block;font-size:13px;line-height:1.5;letter-spacing:1px;margin-top:9px}.callout-line{height:1px;background:#688973;width:120px;margin-top:20px}.mission{position:absolute;left:28px;bottom:25px;pointer-events:none}.mission>span{font-size:8px;letter-spacing:2px;color:#789a85;display:block;margin-bottom:5px}.mission b{font-size:12px;font-weight:400;display:block;color:#c8d6b7}.mission small{font-size:9px;color:#718e7c;display:block;margin-top:3px}.intro-open .mission{display:none}.dock{display:flex;align-items:center;padding:12px 28px;gap:22px;background:#101c1e;border-top:1px solid #3a4a3c;z-index:2}.field-label{min-width:93px}.field-label small{display:block;font-size:8px;letter-spacing:1.1px;color:#8f9f8b}.field-label b{font:18px monospace;letter-spacing:2px;display:block;margin-top:4px}.modes{display:flex;gap:6px;flex:1;max-width:600px}.mode{min-height:54px;flex:1;background:transparent;border:1px solid #34473c;display:flex;align-items:center;gap:10px;text-align:left;padding:8px 12px;color:#94a895;position:relative}.mode.active{background:#24382c;border-color:#a5b58b;color:#d8e5be;box-shadow:inset 0 -2px #b7c899}.mode-icon{font-size:24px;line-height:1;font-family:monospace}.mode b{display:block;font-size:10px;font-weight:500;letter-spacing:.7px;white-space:nowrap}.mode small{display:block;font-size:7px;letter-spacing:.6px;margin-top:4px;color:#819781;white-space:nowrap}.mode.active small{color:#a7b798}.mode kbd{font-size:8px;position:absolute;right:5px;top:3px;color:#627a65}.payload{width:125px;margin-left:auto}.payload small{font-size:8px;letter-spacing:1px;display:flex;justify-content:space-between;gap:8px;white-space:nowrap;color:#a2b293}.charge-track{height:4px;background:#2e4538;margin:9px 0 5px}.charge-track i{display:block;height:4px;width:0;background:#c8e7b7}.payload>span{font-size:7px;letter-spacing:1px;color:#7c967e}.upgrade{min-width:102px;min-height:52px;background:transparent;border:1px solid #7b7351;display:flex;align-items:center;justify-content:center;gap:11px;color:#e0bf7b}.upgrade>span{font-size:22px}.upgrade b{font-size:8px;font-weight:400;letter-spacing:.8px}.upgrade small{display:block;font-size:10px;margin-top:5px}.keyline{display:flex;align-items:center;justify-content:space-between;padding:0 28px;font-size:8px;letter-spacing:.6px;color:#718774;background:#0c1719;gap:12px}.keyline kbd{color:#a5b096;font-size:8px}.keyline i{font-style:normal;margin:0 10px;color:#425a46}.modal-screen{background:#081211d9;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);display:grid;place-items:center;padding:16px;overflow:auto}.modal-panel{width:510px;max-width:100%;border:1px solid #49624c;background:#14221f;padding:26px;max-height:100%;overflow:auto}.modal-panel .serial{margin-bottom:12px}h2{font-size:32px;line-height:1.1;margin:0 0 12px;letter-spacing:-1px}.modal-panel>p{font-size:12px;color:#a7b6a2}.help p{font-size:10px;line-height:1.6;color:#9baf9d}.help kbd{font-size:9px;color:#d7dabb}.help p:last-child{margin-bottom:20px}.touch-help{display:none}.modal-actions{display:flex;gap:8px;flex-wrap:wrap}.modal-actions .primary{flex:1;min-width:160px;font-size:9px;padding:12px;gap:15px}.secondary{background:transparent;border:1px solid #698164;color:#bfcbad;padding:12px;font:9px monospace;min-height:44px}.settings-row{display:flex;gap:18px;flex-wrap:wrap;margin-top:20px;font-size:10px;color:#b4c2ac}.settings-row label{display:flex;align-items:center;gap:6px;cursor:pointer;min-height:25px}input{accent-color:var(--amber)}.storage-status{font-size:9px!important;line-height:1.5;margin-bottom:0;color:#7f9c86!important}details{border-top:1px solid #354c3b;margin-top:12px;padding-top:12px;font-size:10px;color:#99b29a}summary{cursor:pointer;padding:6px 0}details canvas{width:100%;image-rendering:pixelated;background:#091513;margin-top:12px}details p{font-size:9px}.results{display:flex;gap:28px;margin:20px 0;color:#e3c189}.results b{font-size:25px;display:block;font-weight:400}.results small{font-size:8px;letter-spacing:1px;color:#8ca085}.touch-controls{display:none;position:absolute;inset:auto 18px 17px;align-items:end;justify-content:space-between;pointer-events:none;z-index:3}.joystick{height:104px;width:104px;border:1px solid #8dab7766;border-radius:50%;background:#10221d66;position:relative;touch-action:none;pointer-events:auto}.joystick:before,.joystick:after{content:"";position:absolute;background:#708c653b}.joystick:before{top:50%;left:10%;width:80%;height:1px}.joystick:after{left:50%;top:10%;height:80%;width:1px}#stick{width:40px;height:40px;border-radius:50%;background:#6383636b;border:1px solid #a2bc8d;position:absolute;left:31px;top:31px}.joystick>span{position:absolute;bottom:-13px;left:0;right:0;text-align:center;font:7px monospace;letter-spacing:1px;color:#9cae88}.field-button{width:104px;height:104px;border-radius:50%;border:1px solid #b8bb83;background:#293e29c7;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#d9e9b7;touch-action:none;pointer-events:auto}.field-button>span{font-size:30px;line-height:32px}.field-button b{font:10px monospace;letter-spacing:2px}.field-button small{font-size:6px;margin-top:4px}.field-button.held{background:#496b3f;box-shadow:0 0 24px #abda8055}.minimum-notice{display:none}@media(min-width:1400px){.hud,.dock{padding-left:40px;padding-right:40px}.dock{gap:32px}.mode{padding:10px 18px}.modes{max-width:700px}.mode b{font-size:11px}.mode small{font-size:8px}}@media(max-width:900px){#app{grid-template-rows:66px minmax(0,1fr)76px 26px}.hud{padding:0 18px;gap:16px}.brand{min-width:145px;gap:8px}.brand b{font-size:17px}.brand-mark{font-size:32px}.brand small{font-size:7px;letter-spacing:2px}.dock{padding:10px 16px;gap:12px}.field-label{display:none}.mode{padding:8px;gap:8px}.payload{width:105px}.upgrade{min-width:84px}.intro-callout{display:none}.keyline{padding:0 18px}.brief-steps{gap:14px}.intro-panel{max-width:480px}.intro-screen{padding-left:7%}}@media(max-width:650px){.brand small{display:none}.brand{min-width:auto}.brand-mark{font-size:27px}.brand b{font-size:14px;letter-spacing:1px}.hud{gap:12px;padding:0 12px}.hull-block{width:auto;flex:1;max-width:155px}.stat-label{font-size:7px;letter-spacing:.5px}.score-block{padding-left:12px;min-width:61px}.score-block small{font-size:7px}.score-block strong{font-size:18px}.icon-button{width:30px;height:36px;font-size:16px}.sound-slash{top:9px;left:10px}.dock{padding:8px 10px;gap:7px}.payload{display:none}.mode{min-height:51px;gap:5px;padding:6px}.mode-icon{font-size:21px}.mode b{font-size:8px;letter-spacing:.2px}.mode small{font-size:6px;letter-spacing:0}.upgrade{min-width:58px;gap:5px;padding:4px}.upgrade b{font-size:7px}.upgrade>span{font-size:16px}.upgrade small{font-size:9px}.keyline>span:first-child{font-size:7px;letter-spacing:0}.keyline i{margin:0 3px}.keyline #best{font-size:7px;white-space:nowrap}.mission{left:17px}.intro-screen{padding:24px}.intro-panel h1{font-size:43px}.intro-panel>p{font-size:13px;margin-bottom:18px}.serial{font-size:8px;letter-spacing:1px}.flight-status{left:16px}.intro-note{font-size:7px}.modal-panel{padding:20px}}@media(pointer:coarse),(max-width:520px){.touch-controls{display:flex}.intro-open .touch-controls,.modal-open .touch-controls{display:none}.touch-help{display:block}.mission{bottom:145px}.keyline>span:first-child{font-size:0}.keyline>span:first-child:after{content:"STICK: FLY · FIELD: HOLD · FIRE: HURL";font:7px monospace;letter-spacing:.2px}.keyline>span:first-child>*{display:none}.icon-button{width:44px;height:44px}.settings-row label{min-height:40px}.modes button{min-height:48px}.mode kbd{display:none}}@media(max-width:400px){#app{grid-template-rows:58px minmax(0,1fr)68px 24px}.hud{gap:8px}.brand-mark{display:none}.brand b{font-size:12px;letter-spacing:.4px}.hull-block{max-width:108px}.stat-label{font-size:6px;letter-spacing:0}.hull-meter{gap:2px;margin-top:6px}.hull-meter i{height:5px}.score-block{min-width:46px;padding-left:8px}.score-block strong{font-size:15px}.score-block small{font-size:6px}.icon-button{width:36px;height:44px}.hud #sound{display:none}.mode{display:flex;justify-content:center;padding:5px 3px;gap:3px}.mode-icon{font-size:16px}.mode b{font-size:7px}.mode small{font-size:5px}.modes{gap:4px}.dock{padding:7px 6px;gap:5px}.upgrade{min-width:48px}.upgrade>span{display:none}.intro-screen{padding:20px}.intro-panel h1{font-size:37px;letter-spacing:-1.4px;margin-bottom:17px}.intro-panel>p{font-size:12px;margin-bottom:17px}.brief-steps{font-size:8px;margin-bottom:18px}.brief-steps b{margin-bottom:3px}.serial{margin-bottom:14px;font-size:7px}.primary{min-width:205px;padding:12px 15px}.intro-note{margin-top:5px;padding-top:9px}.flight-status{font-size:7px;top:14px}.joystick,.field-button{width:88px;height:88px}#stick{left:23px;top:23px}.field-button small{font-size:5px}.mission b{font-size:10px}.mission small{font-size:8px}.mission{bottom:122px}.toast{font-size:8px;top:37px}.modal-panel{padding:16px}h2{font-size:27px}}@media(max-height:500px)and (min-width:520px){#app{grid-template-rows:50px minmax(0,1fr)60px}.keyline{display:none}.hud{gap:18px}.brand small{display:none}.hull-meter{margin-top:4px}.brand-mark{font-size:28px}.score-block strong{font-size:18px}.dock{padding-top:4px;padding-bottom:4px}.mode,.upgrade{min-height:46px}.intro-screen{padding:18px 6%}.intro-panel{max-width:650px}.intro-panel h1{font-size:32px;margin-bottom:8px}.intro-panel>p{font-size:11px;margin:0 0 10px}.intro-panel>p br{display:none}.serial{margin-bottom:8px;font-size:7px}.brief-steps,.intro-note{display:none}.primary{display:inline-flex;min-width:210px;min-height:44px;padding:10px 15px}.text-button{margin-left:15px}.flight-status{top:10px;font-size:7px}.mission{display:none}.toast{top:30px;font-size:8px}.touch-controls{bottom:15px;left:15px;right:15px}.joystick,.field-button{width:82px;height:82px}#stick{left:20px;top:20px}.field-button>span{font-size:23px;line-height:25px}.field-button small{font-size:5px}.modal-panel{padding:16px}.modal-panel .help{display:none}.settings-row{margin-top:8px}.results{margin:10px 0}}@media(max-width:279px),(max-height:279px){.minimum-notice{position:absolute;top:0;right:0;bottom:0;left:0;background:#101e1b;z-index:20;display:flex;flex-direction:column;justify-content:center;padding:18px;gap:9px}.minimum-notice b{font-size:18px;color:#dfe7c9}.minimum-notice span{font-size:11px;line-height:1.6;color:#a4bba1}.hud,.viewport,.dock,.keyline{visibility:hidden}}@media(prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto!important}}#app{min-width:0;overflow:hidden}.hud,.dock,.viewport{min-width:0}@media(max-width:400px){.hud #pause{width:44px}.hull-block{min-width:0}}@media(max-width:520px){.mode small{display:none}.mode b{font-size:9px}.mode-icon{font-size:20px}.mode{flex-direction:column;gap:3px}.upgrade b{font-size:8px}.upgrade small{font-size:10px}.flight-status{font-size:8px}.mission>span{font-size:9px}.mission b{font-size:12px}.mission small{font-size:10px}.field-button small{font-size:7px}.field-button b{font-size:11px}.joystick>span{font-size:8px}}@media(max-height:720px){.intro-screen .brief-steps,.intro-screen .intro-note{display:none}.intro-panel h1,.intro-panel>p{margin-bottom:12px}.intro-panel .serial{margin-bottom:12px}}@media(max-height:500px)and (min-width:520px){.site-picker{max-width:360px;margin-bottom:8px}.intro-screen{padding-top:12px;padding-bottom:12px}}.selector-button{min-height:48px;min-width:96px;padding:7px 10px;border:1px solid #708679;background:#172b29;color:var(--mint);font:10px monospace}.selector-button small{display:block;font-size:7px;letter-spacing:.6px;color:#8eac9c;margin-bottom:5px}.selector-button b{letter-spacing:1px}.radial-overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:9;display:flex;align-items:center;justify-content:center;flex-direction:column;background:#071211b0;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.radial-dial{position:relative;width:min(440px,calc(100vw - 12px),calc(100dvh - 80px));aspect-ratio:1;border:1px solid #94c5ad35;border-radius:50%;background:radial-gradient(circle,transparent 32%,#6b9e8b15 33%,transparent 34%,transparent 71%,#8cdabf18 72%,transparent 73%)}.radial-dial:before{content:"";position:absolute;top:18%;right:18%;bottom:18%;left:18%;border:1px dashed #90ad8660;border-radius:50%;pointer-events:none}.radial-center{position:absolute;top:35%;right:22%;bottom:35%;left:22%;display:flex;align-items:center;justify-content:center;flex-direction:column;text-align:center;font-family:monospace;pointer-events:none}.radial-center small{font-size:8px;color:#90c9b7;letter-spacing:.7px;white-space:nowrap}.radial-center b{font-size:16px;margin:8px 0 3px;color:#e5efd8}.radial-center span{font-size:9px;color:var(--amber);letter-spacing:2px}.radial-choice{position:absolute;left:calc(var(--x)*1%);top:calc(var(--y)*1%);transform:translate(-50%,-50%);width:96px;min-height:49px;padding:8px 4px;background:#152725;border:1px solid #536d5b;box-shadow:0 4px 20px #0006;text-align:center;font:10px monospace}.radial-choice small{display:block;font-size:7px;letter-spacing:1px;color:#879e92;margin-bottom:4px}.radial-choice b{font-weight:500}.radial-choice[data-select-release]{border-color:#826743;color:#edc68a}.radial-choice[aria-pressed=true]{background:#345541;border-color:#aee1bf;box-shadow:inset 0 -3px #b0e4bd}.radial-choice[data-select-release][aria-pressed=true]{background:#4d4229;border-color:#eac580;box-shadow:inset 0 -3px #eac580}.radial-choice.hovered{outline:2px solid #e2f2cc;outline-offset:3px}.radial-overlay>p{font:9px monospace;color:#b4cbbb;margin:4px 12px 10px;text-align:center}.radial-overlay>.secondary{min-height:36px;padding:6px 18px}.fire-button{pointer-events:auto;touch-action:none;min-height:48px;min-width:62px;border:1px solid #dcad6f;background:#473c28d9;color:#f6dbad;font:9px monospace;align-self:end;margin-bottom:12px}@media(max-width:1150px){.dock{gap:10px}.mode-icon{display:none}.mode{padding:8px}.mode small{font-size:6px}.selector-button{min-width:85px;padding:6px}}@media(max-width:650px){#app{grid-template-rows:58px minmax(0,1fr)112px 24px}.dock{display:grid;grid-template-columns:minmax(0,1fr)80px;grid-template-rows:48px 44px;gap:5px;padding:6px}.modes{grid-column:1/-1;max-width:none}.mode{justify-content:center;min-width:0;min-height:44px;padding:5px}.mode b{font-size:8px}.mode small{display:none}.selector-button{min-height:44px;display:flex;align-items:center;justify-content:center;gap:15px}.selector-button small{margin:0}.upgrade{min-height:44px}.upgrade small{margin:2px 0 0}.radial-choice{width:77px;min-height:44px;font-size:8px}.radial-choice small{font-size:6px}.radial-center b{font-size:11px}.radial-center small{font-size:6px}}@media(max-height:450px)and (min-width:651px){.mode small{display:none}.mode b{font-size:8px}.selector-button{min-width:78px}.payload{width:80px}.radial-choice{width:78px;font-size:8px;min-height:44px}.radial-center b{font-size:11px}.radial-center small{font-size:6px}}@media(max-width:279px),(max-height:279px){.radial-overlay{display:none}}
</style>
  <meta name="napplet-type" content="physics-type">
  <meta name="napplet-config-schema" content="{&quot;type&quot;:&quot;object&quot;,&quot;properties&quot;:{}}">
</head>
<body>
<main id="app">
  <header class="hud">
    <div class="brand"><span class="brand-mark" aria-hidden="true">⊙</span><div><b>GRAV<span>/</span>RIFT</b><small>SALVAGE DIVISION</small></div></div>
    <div class="hull-block"><div class="stat-label">HULL INTEGRITY <span id="hullValue">100%</span></div><div id="hull" class="hull-meter" role="meter" aria-label="Hull integrity" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div></div>
    <div class="score-block"><small>SALVAGE</small><strong><span class="diamond">◇</span> <span id="salvage">000</span></strong></div>
    <button id="sound" class="icon-button" aria-label="Enable sound" title="Sound">♪<span class="sound-slash">/</span></button>
    <button id="pause" class="icon-button" aria-label="Pause game" title="Pause (P)">Ⅱ</button>
  </header>
  <section id="viewport" class="viewport" aria-label="Salvage combat playfield">
    <canvas id="game" tabindex="0" aria-label="Physics salvage game. Move with WASD or arrows. Hold right mouse or Space to gather debris; release to drop. Left mouse or F fires. Keys 1–4 change gravity mode."></canvas>
    <div class="flight-status"><span class="live-dot"></span><span id="flightStatus">SYSTEMS STANDBY</span><span id="runTime">00:00</span></div>
    <div id="toast" class="toast" role="status" aria-live="polite"></div>
    <div id="intro" class="screen intro-screen">
      <div class="intro-panel">
        <span class="serial">EXPERIMENTAL FLIGHT PROGRAM / 001</span>
        <h1>EVERYTHING<br>IS <em>AMMUNITION.</em></h1>
        <p>No guns. Just gravity.<br>Turn the wreckage into your weapon.</p>
        <div class="brief-steps"><span><b>01</b> Gather scrap</span><span><b>02</b> Hurl it back</span><span><b>03</b> Rip the core open</span></div>
        <label class="site-picker" for="site"><span>DROP SITE</span><select id="site"><option value="foundry">FOUNDRY · GRAVITY ON</option><option value="orbit">ORBITAL GRAVEYARD · ZERO G</option></select></label>
        <button id="deploy" class="primary">DEPLOY TUG <span>↗</span></button>
        <button id="practice" class="text-button">Enter practice range <span>→</span></button>
        <div class="intro-note">A PHYSICS COMBAT PROTOTYPE <span>v0.1</span></div>
      </div>
      <div class="intro-callout"><span>FIELD GENERATOR</span><strong>ZERO ROUNDS.<br>UNLIMITED POSSIBILITIES.</strong><div class="callout-line"></div></div>
    </div>
    <div id="modal" class="screen modal-screen" hidden>
      <section class="modal-panel" aria-labelledby="modalTitle">
        <span id="modalSerial" class="serial">FLIGHT COMPUTER</span><h2 id="modalTitle">Flight paused</h2><p id="modalText"></p>
        <div id="results" class="results"></div>
        <div id="help" class="help"><p><kbd>TAB</kbd> Hold for slow-motion selector · <kbd>R</kbd> Cycle release pattern</p><p><kbd>LMB / F</kbd> Fire while gathering · <kbd>4</kbd> Surgical: hold RMB on one object, drag, LMB hurl, release RMB to drop, wheel to rotate.</p><p>Pull violet shield cells to lift living machines. Rip leg joints to cripple them. Volley fires everything; Stream feeds one piece at a time; Clusters fires small groups.</p><p><kbd>W A S D</kbd> / <kbd>↑ ↓ ← →</kbd> Fly</p><p><kbd>RMB / SPACE</kbd> Hold to gather · release to drop. Hold both mouse buttons to gather and fire together.</p><p><kbd>1 2 3 4</kbd> Change mode · <kbd>U</kbd> Upgrade</p><p>Catch a Strider's glowing shell in your field, aim back at its cannon, then fire with LMB or F. Rip supports to collapse structures. Strip four Warden plates, then hit the core. Amber cells arm when thrown and explode on contact; hard impacts and nearby blasts can set off loose cells.</p><p class="touch-help">Touch: left stick flies; hold FIELD to gather, press FIRE to shoot; release FIELD to drop. Aim intercepts incoming shells, finds floor scrap when empty, then targets a threat.</p></div>
        <div class="modal-actions"><button id="resume" class="primary">RESUME FLIGHT <span>→</span></button><button id="retry" class="secondary">RESTART RUN</button></div>
        <button id="changeSite" class="text-button">CHOOSE ANOTHER SITE <span>→</span></button>
        <div class="settings-row"><label><input id="soundSetting" type="checkbox"> Sound</label><label><input id="reduced" type="checkbox"> Reduced effects</label><label><input id="debug" type="checkbox"> Show collision hulls</label></div>
        <details id="atlasDetails"><summary>Inspect debris atlas</summary><div id="atlas"></div><p>Original pixel sprites → alpha silhouette → convex collision hull.</p></details>
        <p id="storageStatus" class="storage-status"></p>
      </section>
    </div>
    <div id="touchControls" class="touch-controls"><div id="joystick" class="joystick" role="group" aria-label="Drag to steer"><div id="stick"></div><span>MOVE</span></div><button id="fireButton" class="fire-button">FIRE</button><button id="fieldButton" class="field-button" aria-label="Hold to gather debris, release to drop"><span>◎</span><b>HOLD</b><small>RELEASE TO DROP</small></button></div>
    <div id="mission" class="mission"><span>OBJECTIVE</span><b>Reach the reactor bay</b><small>Strip the Warden. Rupture its core.</small></div>
  </section>
  <footer class="dock">
    <div class="field-label"><small>GRAVITY FIELD</small><b id="fieldLevel">MK. I</b></div>
    <div class="modes" role="group" aria-label="Gravity field mode">
      <button class="mode active" data-mode="0" aria-pressed="true"><span class="mode-icon">◎</span><span><b>ORBIT</b><small>SPIN & SHIELD</small></span><kbd>1</kbd></button>
      <button class="mode" data-mode="1" aria-pressed="false"><span class="mode-icon">✳</span><span><b>COMPRESS</b><small>CHARGE & BLAST</small></span><kbd>2</kbd></button>
      <button class="mode" data-mode="2" aria-pressed="false"><span class="mode-icon">⌁</span><span><b>RIP TETHER</b><small>TEAR & RECLAIM</small></span><kbd>3</kbd></button>
    <button class="mode" data-mode="3" aria-pressed="false"><span class="mode-icon">⌖</span><span><b>SURGICAL</b><small>GRAB ONE / DISMANTLE</small></span><kbd>4</kbd></button></div>
    <button id="selector" class="selector-button" aria-label="Open gravity selector" title="Hold Tab for slow motion"><small>TAB · SELECTOR</small><b id="releaseName">VOLLEY</b></button>
    <div class="payload"><small>PAYLOAD <span id="payloadCount">0</span></small><div class="charge-track"><i id="charge"></i></div><span id="chargeLabel">FIELD IDLE</span></div>
    <button id="upgrade" class="upgrade" disabled aria-label="Upgrade gravity field"><span>↑</span><b>UPGRADE<small id="upgradePrice">◇ 50</small></b></button>
  </footer>
  <div class="keyline"><span><kbd>WASD</kbd> MOVE <i>·</i> AIM WITH POINTER <i>·</i> <kbd>RMB</kbd> GATHER / DROP <i>·</i> <kbd>LMB</kbd> FIRE</span><span id="best">BEST 00000</span></div>
  <div id="radial" class="radial-overlay" role="dialog" aria-label="Gravity field and release selector" hidden><div class="radial-dial"><div class="radial-center"><small>TACTICAL / 05% TIME</small><b data-field-name>ORBIT</b><span data-release-name>VOLLEY</span></div></div><p>Hover → release Tab · Click to set both · Esc cancels</p><button class="secondary" data-close>RETURN TO FLIGHT</button></div>
  <div class="minimum-notice"><b>More room to fly.</b><span>Resize to at least 280 × 280.<br>Your flight is paused and safe.</span></div>
</main>
</body>
</html>