PageSourceSearch

https://www.z-add.co.uk/build/assets/MissileDefence-PY2bSKB3.js

js z-add.co.uk collected 2026-10-02 01:10:06 UTC 13,894 bytes, 1 lines download raw bytes

1import{b as h,j as l}from"./client-DVJRcboz.js";import{T as he}from"./ToolPage-BLm8B96_.js";import{M as ge,s as W,C as ye}from"./ToolUi-C1GsOxnf.js";import"./SeoHead-DdgVrFEj.js";import"./CopyButton-CSJM6nv1.js";const p=800,S=520,f=470,_=46,ve=34,A=56,oe=32,we=[40,400,760],be=[112,192,272,528,608,688],K=640,Se=54,Te=38,k=44,xe=170,Ee=.2,Re=120,Ie=32,Q=90,Ae=230,se=1e4,Pe=25,Ne=5,_e=100,te=[{id:"easy",label:"Easy",description:"Slow incoming missiles, plenty of ammunition, and splitting warheads stay away until wave 4.",ammo:12,baseSpeed:44,speedPerWave:6,maxConcurrent:4,mirvFromWave:4,spawnGap:1.15},{id:"medium",label:"Medium",description:"A steadier stream, less ammunition to spend, and warheads start splitting from wave 3.",ammo:10,baseSpeed:56,speedPerWave:8,maxConcurrent:5,mirvFromWave:3,spawnGap:.95},{id:"hard",label:"Hard",description:"Fast, dense salvoes on a tight ammunition budget, splitting from wave 2 — every shot has to count.",ammo:8,baseSpeed:70,speedPerWave:10,maxConcurrent:7,mirvFromWave:2,spawnGap:.72}];function F(e){return Math.min(6,Math.floor((e-1)/2)+1)}function V(e){const i={difficulty:e,wave:0,score:0,nextBonusAt:se,cities:be.map(n=>({x:n,alive:!0})),batteries:we.map(n=>({x:n,ammo:e.ammo,alive:!0})),enemies:[],friendlies:[],explosions:[],spawnQueue:[],spawnTimer:0,phase:"playing",phaseTimer:0,bonus:null,shotsFired:0,missilesDestroyed:0};return le(i,1),i}function le(e,i,n=Math.random){const o=e.difficulty;e.wave=i,e.phase="playing",e.bonus=null,e.enemies=[],e.friendlies=[],e.explosions=[],e.batteries.forEach(c=>{c.alive=!0,c.ammo=o.ammo});const s=Math.min(Ie,10+i*2),r=o.baseSpeed+(i-1)*o.speedPerWave,t=i>=o.mirvFromWave;e.spawnQueue=Array.from({length:s},()=>({speed:r*(.85+n()*.3),splitAt:t&&n()<.22?Q+n()*(Ae-Q):null})),e.spawnTimer=.6}function je(e,i){const n=[...e.cities.filter(o=>o.alive).map(o=>o.x),...e.batteries.filter(o=>o.alive).map(o=>o.x)];return n.length?n[Math.floor(i()*n.length)]:p/2}function ae(e,i,n,o,s,r){const c=je(e,r)-i,d=f-n,g=Math.hypot(c,d)||1;e.enemies.push({x:i,y:n,originX:i,originY:n,vx:c/g*o,vy:d/g*o,speed:o,splitAt:s})}function B(e,i,n,o,s){e.explosions.push({x:i,y:n,r:0,maxR:o,hold:Ee,phase:"grow",damaging:s})}function Le(e,i){e.cities.forEach(n=>{n.alive&&Math.abs(n.x-i)<k+_/2&&(n.alive=!1)}),e.batteries.forEach(n=>{n.alive&&Math.abs(n.x-i)<k+A/2&&(n.alive=!1,n.ammo=0)})}function Me(e,i,n){if(e.phase!=="playing"||n>=f-6)return!1;let o=null,s=1/0;for(const m of e.batteries){if(!m.alive||m.ammo<=0)continue;const w=Math.abs(m.x-i);w<s&&(s=w,o=m)}if(!o)return!1;o.ammo--,e.shotsFired++;const r=o.x,t=f-oe,c=i-r,d=n-t,g=Math.hypot(c,d)||1;return e.friendlies.push({x:r,y:t,originX:r,originY:t,targetX:i,targetY:n,vx:c/g*K,vy:d/g*K}),!0}function J(e,i){for(const n of e.explosions)n.phase==="grow"?(n.r+=xe*i,n.r>=n.maxR&&(n.r=n.maxR,n.phase="hold")):n.phase==="hold"?(n.hold-=i,n.hold<=0&&(n.phase="shrink")):n.r-=Re*i;e.explosions=e.explosions.filter(n=>!(n.phase==="shrink"&&n.r<=0))}function Ce(e,i){const n=[];for(const o of e.friendlies){const s=Math.hypot(o.targetX-o.x,o.targetY-o.y);o.x+=o.vx*i,o.y+=o.vy*i;const r=Math.hypot(o.targetX-o.x,o.targetY-o.y);r<=4||r>s?B(e,o.targetX,o.targetY,Se,!1):n.push(o)}e.friendlies=n}function We(e,i,n){const o=e.enemies;e.enemies=[];for(const s of o){if(s.x+=s.vx*i,s.y+=s.vy*i,s.splitAt!==null&&s.y>=s.splitAt){const t=2+Math.floor(n()*2);for(let c=0;c<t;c++)ae(e,s.x,s.y,s.speed,null,n);continue}if(s.y>=f){B(e,s.x,f,k,!0),Le(e,s.x);continue}if(e.explosions.some(t=>t.r>0&&Math.hypot(t.x-s.x,t.y-s.y)<=t.r)){B(e,s.x,s.y,Te,!1),e.score+=Pe*F(e.wave),e.missilesDestroyed++;continue}e.enemies.push(s)}}function De(e,i,n){if(!e.spawnQueue.length||(e.spawnTimer-=i,e.spawnTimer>0)||e.enemies.length>=e.difficulty.maxConcurrent)return;const o=e.spawnQueue.shift();ae(e,30+n()*(p-60),0,o.speed,o.splitAt,n),e.spawnTimer=e.difficulty.spawnGap*(.6+n()*.8)}function q(e){for(;e.score>=e.nextBonusAt;){e.nextBonusAt+=se;const i=e.cities.find(n=>!n.alive);i&&(i.alive=!0)}}function Ye(e,i,n=Math.random){if(e.phase==="gameOver")return e;if(e.phase==="waveOver")return e.phaseTimer-=i,J(e,i),e.phaseTimer<=0&&le(e,e.wave+1,n),e;if(De(e,i,n),Ce(e,i),J(e,i),We(e,i,n),q(e),!e.cities.some(o=>o.alive))return e.phase="gameOver",e;if(!e.spawnQueue.length&&!e.enemies.length&&!e.friendlies.length){const o=F(e.wave),s=e.batteries.reduce((d,g)=>d+(g.alive?g.ammo:0),0),r=e.cities.filter(d=>d.alive).length,t=s*Ne*o,c=r*_e*o;e.score+=t+c,e.bonus={ammoLeft:s,citiesLeft:r,ammoPoints:t,cityPoints:c,multiplier:o},e.phase="waveOver",e.phaseTimer=2.6,q(e)}return e}const Oe=te.map(e=>[e.id,e.label]),D="wormhole:missile-defence:stats",z="wormhole:missile-defence:prefs",Y=()=>({best:0,bestWave:0,gamesPlayed:0}),ke=()=>({fullWidth:!1});function Z(e,i){try{const n=JSON.parse(localStorage.getItem(e));return n&&typeof n=="object"?n:i()}catch{return i()}}function O(e,i){try{localStorage.setItem(e,JSON.stringify(i))}catch{}}const ee=[[.5,.85,1,.65],[.72,1,.55,.8],[1,.6,.82,.9]];function Be(e,i,n){const o=i.x-_/2;if(!i.alive){e.fillStyle="#3f3f46",e.beginPath(),e.moveTo(o,f),e.lineTo(o+8,f-7),e.lineTo(o+18,f-3),e.lineTo(o+27,f-9),e.lineTo(o+37,f-4),e.lineTo(o+_,f),e.closePath(),e.fill();return}const s=ee[n%ee.length],r=_/s.length,t=r-3;
1s.forEach((c,d)=>{const g=Math.round(c*ve),m=Math.round(o+d*r);e.fillStyle="#5eead4",e.fillRect(m,f-g,t,g),e.fillStyle="rgba(8,15,30,0.6)";for(let w=f-g+4;w<=f-7;w+=6)e.fillRect(m+2,w,t-4,2)})}function Fe(e,i){const n=i.x-A/2,o=f-oe;if(!i.alive){e.fillStyle="#3f3f46",e.fillRect(n+6,f-8,A-12,8);return}e.fillStyle="#a5b4fc",e.beginPath(),e.moveTo(n,f),e.lineTo(n+10,o+8),e.lineTo(n+A-10,o+8),e.lineTo(n+A,f),e.closePath(),e.fill(),e.fillRect(i.x-3,o-4,6,14),e.font="bold 13px ui-monospace, monospace",e.textAlign="center",e.fillStyle=i.ammo===0?"#f87171":i.ammo<=3?"#fbbf24":"#e0e7ff",e.fillText(String(i.ammo),i.x,o-10)}function ne(e,i,n,o){e.strokeStyle=n,e.lineWidth=1.5,e.beginPath(),e.moveTo(i.originX,i.originY),e.lineTo(i.x,i.y),e.stroke(),e.fillStyle=o,e.beginPath(),e.arc(i.x,i.y,2.6,0,Math.PI*2),e.fill()}const ie=["#fef9c3","#fca5a5","#fdba74","#fde047","#fb7185"];function Ge(e,i,n,o,s){const r=e.createLinearGradient(0,0,0,f);if(r.addColorStop(0,"#05060f"),r.addColorStop(1,"#131a33"),e.fillStyle=r,e.fillRect(0,0,p,f),e.fillStyle="#1e293b",e.fillRect(0,f,p,S-f),e.fillStyle="#334155",e.fillRect(0,f,p,3),i.cities.forEach((t,c)=>Be(e,t,c)),i.batteries.forEach(t=>Fe(e,t)),i.enemies.forEach(t=>ne(e,t,"rgba(248,113,113,0.75)","#fecaca")),i.friendlies.forEach(t=>ne(e,t,"rgba(103,232,249,0.8)","#ecfeff")),i.explosions.forEach(t=>{if(t.r<=0)return;const c=ie[Math.floor(s/60)%ie.length];e.fillStyle=c,e.globalAlpha=.75,e.beginPath(),e.arc(t.x,t.y,t.r,0,Math.PI*2),e.fill(),e.globalAlpha=1,e.strokeStyle="#fff7ed",e.lineWidth=1.5,e.stroke()}),n&&i.phase==="playing"&&!o&&(e.strokeStyle="#67e8f9",e.lineWidth=1.5,e.beginPath(),e.moveTo(n.x-10,n.y),e.lineTo(n.x-3,n.y),e.moveTo(n.x+3,n.y),e.lineTo(n.x+10,n.y),e.moveTo(n.x,n.y-10),e.lineTo(n.x,n.y-3),e.moveTo(n.x,n.y+3),e.lineTo(n.x,n.y+10),e.stroke()),e.textAlign="center",i.phase==="waveOver"&&i.bonus){const t=i.bonus;e.fillStyle="rgba(2,6,23,0.72)",e.fillRect(0,0,p,f),e.fillStyle="#e0e7ff",e.font="bold 26px ui-monospace, monospace",e.fillText(`WAVE ${i.wave} CLEARED`,p/2,150),e.font="16px ui-monospace, monospace",e.fillStyle="#a5b4fc",e.fillText(`${t.ammoLeft} missiles unused  ×${t.multiplier}  =  ${t.ammoPoints}`,p/2,200),e.fillText(`${t.citiesLeft} cities standing  ×${t.multiplier}  =  ${t.cityPoints}`,p/2,228)}i.phase==="gameOver"&&(e.fillStyle="rgba(2,6,23,0.75)",e.fillRect(0,0,p,S),e.fillStyle="#fca5a5",e.font="bold 30px ui-monospace, monospace",e.fillText("ALL CITIES LOST",p/2,190),e.fillStyle="#e0e7ff",e.font="17px ui-monospace, monospace",e.fillText(`Wave ${i.wave}  ·  ${i.score.toLocaleString("en-GB")} points`,p/2,228)),o&&i.phase!=="gameOver"&&(e.fillStyle="rgba(2,6,23,0.7)",e.fillRect(0,0,p,S),e.fillStyle="#e0e7ff",e.font="bold 28px ui-monospace, monospace",e.fillText("PAUSED",p/2,S/2))}function Qe({section:e,item:i}){const[n,o]=h.useState("medium"),[s,r]=h.useState(!1),[t,c]=h.useState(()=>Z(D,()=>({}))),[d,g]=h.useState(()=>Z(z,ke)),[m,w]=h.useState({score:0,wave:1,cities:6,ammo:0,phase:"playing"}),j=h.useRef(null),T=h.useRef(null),x=h.useRef(null),P=h.useRef(!1),L=h.useRef(!1),G=h.useRef(m),E=te.find(a=>a.id===n),N=t[n]??Y();h.useEffect(()=>{P.current=s},[s]),T.current===null&&(T.current=V(E));function M(){T.current=V(E),x.current=null,L.current=!1,r(!1)}h.useEffect(()=>{M()},[n]);function re(a){c(u=>{const y={...Y(),...u[n]};y.gamesPlayed++,a.score>y.best&&(y.best=a.score),a.wave>y.bestWave&&(y.bestWave=a.wave);const R={...u,[n]:y};return O(D,R),R})}function H(a){const u=j.current.getBoundingClientRect();return{x:(a.clientX-u.left)/u.width*p,y:(a.clientY-u.top)/u.height*S}}function fe(a){x.current=H(a)}function ce(a){a.preventDefault();const u=H(a);x.current=u,!P.current&&Me(T.current,u.x,u.y)}h.useEffect(()=>{function a(u){u.code==="Space"&&(u.preventDefault(),r(y=>!y))}return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[]),h.useEffect(()=>{const a=j.current.getContext("2d");let u,y=performance.now();function R(C){const de=Math.min(.05,(C-y)/1e3);y=C;const v=T.current;P.current||Ye(v,de),v.phase==="gameOver"&&!L.current&&(L.current=!0,re(v));const $=v.batteries.reduce((b,X)=>b+(X.alive?X.ammo:0),0),U=v.cities.filter(b=>b.alive).length,I=G.current;if(I.score!==v.score||I.wave!==v.wave||I.cities!==U||I.ammo!==$||I.phase!==v.phase){const b={score:v.score,wave:v.wave,cities:U,ammo:$,phase:v.phase};G.current=b,w(b)}
1Ge(a,v,x.current,P.current,C),u=requestAnimationFrame(R)}return u=requestAnimationFrame(R),()=>cancelAnimationFrame(u)},[n]);function ue(a){g(u=>{const y={...u,[a]:!u[a]};return O(z,y),y})}function me(){const a={...t,[n]:Y()};c(a),O(D,a)}const pe=m.phase==="gameOver"?`All cities lost on wave ${m.wave} — ${m.score.toLocaleString("en-GB")} points.`:s?"Paused — press space or the button below to resume.":`Wave ${m.wave} · ×${F(m.wave)} score multiplier`;return l.jsxs(he,{section:e,item:i,fullWidth:d.fullWidth,children:[l.jsxs("div",{className:"flex flex-wrap items-start gap-6",children:[l.jsxs("div",{children:[l.jsx("span",{className:"block text-sm font-medium text-gray-700 mb-1",children:"Difficulty"}),l.jsx(ge,{value:n,onChange:o,options:Oe,label:"Difficulty"}),l.jsx("p",{className:"mt-1 text-xs text-gray-500 max-w-sm",children:E.description})]}),l.jsx("button",{type:"button",onClick:M,className:W,children:"New game"}),l.jsx("button",{type:"button",onClick:()=>r(a=>!a),className:W,children:s?"Resume":"Pause"})]}),l.jsx(ye,{id:"md-full-width",checked:d.fullWidth,onChange:()=>ue("fullWidth"),children:"Full width battlefield"}),l.jsxs("div",{className:"flex flex-col items-center gap-3",children:[l.jsxs("div",{className:"flex flex-wrap items-center justify-center gap-x-8 gap-y-1 font-mono text-lg tabular-nums text-gray-900",children:[l.jsxs("span",{children:["Score ",l.jsx("span",{className:"font-bold",children:m.score.toLocaleString("en-GB")})]}),l.jsxs("span",{children:["Cities ",l.jsx("span",{className:"font-bold",children:m.cities}),"/6"]}),l.jsxs("span",{children:["Missiles ",l.jsx("span",{className:"font-bold",children:m.ammo})]}),l.jsxs("span",{className:"text-gray-500 text-base",children:["Best ",N.best.toLocaleString("en-GB")]})]}),l.jsx("p",{role:"status","aria-live":"polite",className:"h-6 text-sm font-medium text-gray-700",children:pe}),l.jsxs("div",{className:`relative w-full ${d.fullWidth?"":"max-w-3xl"}`,children:[l.jsx("canvas",{ref:j,width:p,height:S,onPointerMove:fe,onPointerDown:ce,onPointerLeave:()=>{x.current=null},className:"w-full h-auto rounded-md border border-gray-800 cursor-crosshair touch-none"}),m.phase==="gameOver"&&l.jsx("div",{className:"absolute inset-x-0 top-[62%] flex items-center justify-center",children:l.jsx("button",{type:"button",onClick:M,className:"px-4 py-2 rounded-md bg-indigo-600 text-white text-sm font-medium hover:bg-indigo-700",children:"Play again"})})]})]}),l.jsxs("div",{className:"border-t border-gray-200 pt-4",children:[l.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[l.jsxs("p",{className:"text-sm text-gray-700",children:["At ",l.jsx("span",{className:"font-medium",children:E.label}),":"," ",l.jsxs("span",{className:"font-medium text-green-700",children:[N.best.toLocaleString("en-GB")," best score"]}),","," ",l.jsxs("span",{className:"font-medium text-gray-600",children:["wave ",N.bestWave," furthest"]}),","," ",l.jsxs("span",{className:"font-medium text-gray-600",children:[N.gamesPlayed," played"]})]}),l.jsx("button",{type:"button",onClick:me,className:W,children:"Reset stats"})]}),l.jsxs("p",{className:"mt-2 text-xs text-gray-500",children:["Click or tap anywhere in the sky to detonate a counter-missile there — it launches from whichever surviving battery is nearest and has ammunition, and the blast keeps growing for a moment, so aim ",l.jsx("em",{children:"ahead"})," of an incoming warhead rather than at it. Anything caught in a blast explodes in turn, so a well-placed shot can take a whole cluster with it. Space bar pauses. Each battery reloads between waves and destroyed batteries are rebuilt, but cities are gone for good — except that every 10,000 points rebuilds one. From wave ",E.mirvFromWave," some warheads split into several on the way down. The waves never stop coming; the game is how long you last and how much you score. Your best is kept in this browser only, per difficulty."]}),l.jsx("p",{className:"mt-2 text-xs text-gray-400",children:"An original implementation of a classic arcade genre, written for this site. Not affiliated with, endorsed by, or derived from any commercial game — no third-party artwork, audio, code or trade marks are used."})]})]})}export{Qe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.