SIMON Master Console Rebuild
True command center for SIMON + WEB360
This rebuild gives you a cleaner production spine: interactive tree, live viewer, guarded editor, dynamic graphics, architecture map, and note storage. It is designed to sit beside your existing
console.php
and become the stronger replacement path.
Dashboard
Tree
Viewer
System Map
File Registry
Notes
Files
15,680
Folders
433
Scanned Size
3.89 GB
PHP Files
988
Editable Text Files
12,938
File viewer
guarded to /htdocs
/downloads/defender-2.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" /> <title>SIMON Arcade • Gaylord Neon Defender</title> <meta name="theme-color" content="#070b16" /> <style> :root{ --bg:#070b16; --bg2:#050812; --glass:rgba(255,255,255,.06); --glass2:rgba(255,255,255,.035); --edge:rgba(255,255,255,.16); --txt:#eaf2ff; --mut:#a9b6d9; --cyan:#6ae0ff; --ind:#7b62ff; --pink:#ff4fd6; --green:#44d18a; --warn:#ffcc4d; --bad:#ff4f6d; --shadow:0 18px 60px rgba(0,0,0,.55); } *{box-sizing:border-box} html,body{height:100%} body{ margin:0; overflow:hidden; color:var(--txt); font-family: ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,"Apple Color Emoji","Segoe UI Emoji"; background: radial-gradient(1200px 700px at 50% -10%, #142457, var(--bg) 62%), radial-gradient(900px 520px at 20% 110%, #2a0f4a, transparent 55%), radial-gradient(900px 520px at 110% 80%, #063d4d, transparent 55%), linear-gradient(180deg, var(--bg2), var(--bg)); } canvas{position:fixed; inset:0; width:100vw; height:100vh; display:block} .topbar{ position:fixed; left:14px; right:14px; top:14px; display:flex; align-items:center; justify-content:space-between; gap:12px; z-index:20; pointer-events:none; } .brand,.hud{ pointer-events:auto; display:flex; align-items:center; gap:12px; padding:10px 12px; border-radius:999px; background:linear-gradient(180deg,var(--glass),var(--glass2)); border:1px solid var(--edge); box-shadow:var(--shadow); backdrop-filter: blur(10px); } .logo{width:34px; height:34px; filter: drop-shadow(0 0 12px rgba(106,224,255,.22))} .brandTxt{display:flex; flex-direction:column; line-height:1.05} .brandTxt b{font-size:12.5px; letter-spacing:.18em} .brandTxt span{font-size:11px; color:var(--mut); letter-spacing:.08em} .pill{ display:flex; align-items:baseline; gap:8px; padding:6px 10px; border-radius:999px; background:rgba(0,0,0,.18); border:1px solid rgba(255,255,255,.10); } .pill label{font-size:10px; color:var(--mut); letter-spacing:.12em} .pill strong{font-size:13px} .pill small{font-size:11px; color:var(--mut)} .btn{ cursor:pointer; user-select:none; padding:8px 12px; border-radius:999px; border:1px solid rgba(255,255,255,.18); background:linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.04)); color:var(--txt); font-weight:900; letter-spacing:.06em; font-size:11px; touch-action: manipulation; } .btn:hover{filter:brightness(1.12)} .btn:active{transform:translateY(1px)} .btn.good{border-color:rgba(68,209,138,.45)} .btn.bad{border-color:rgba(255,79,109,.45)} .btn.cyan{border-color:rgba(106,224,255,.45)} .btn.pink{border-color:rgba(255,79,214,.45)} .overlay{ position:fixed; inset:0; z-index:30; display:flex; align-items:center; justify-content:center; padding:22px; background: radial-gradient(900px 520px at 50% 0%, rgba(20,36,87,.35), rgba(7,11,22,.88)); backdrop-filter: blur(6px); } .panel{ width:min(1020px, 100%); border-radius: 26px; border:1px solid var(--edge); background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,.03)); box-shadow: var(--shadow); padding:18px; } .grid{display:grid; grid-template-columns: 1.08fr .92fr; gap:14px} @media (max-width: 900px){ .grid{grid-template-columns:1fr} } h1{margin:0 0 10px; font-size:20px; letter-spacing:.14em} .sub{color:var(--mut); font-size:12.5px; line-height:1.5} .rules{margin-top:12px; display:grid; grid-template-columns:1fr 1fr; gap:10px} .card{border-radius:14px; border:1px solid rgba(255,255,255,.12); background: rgba(0,0,0,.16); padding:10px} .card b{display:block; font-size:12px; letter-spacing:.08em} .card span{display:block; font-size:12px; color:var(--mut); margin-top:4px; line-height:1.35} .row{display:flex; justify-content:space-between; gap:12px; padding:10px 0; border-bottom:1px solid rgba(255,255,255,.08)} .row:last-child{border-bottom:none} select{ background: rgba(0,0,0,.22); color: var(--txt); border:1px solid rgba(255,255,255,.15); border-radius: 12px; padding:8px 10px; outline:none; } .toast{ position:fixed; left:50%; bottom:18px; transform: translateX(-50%); z-index:40; padding:10px 12px; border-radius:999px; border:1px solid rgba(255,255,255,.16); background: rgba(0,0,0,.35); backdrop-filter: blur(8px); color:var(--txt); font-size:12px; letter-spacing:.06em; opacity:0; pointer-events:none; transition: opacity .25s ease, transform .25s ease; } .toast.on{ opacity:1; transform: translateX(-50%) translateY(-4px); } </style> </head> <body> <canvas id="cv"></canvas> <div class="topbar"> <div class="brand"> <svg class="logo" viewBox="0 0 64 64" aria-hidden="true"> <defs> <radialGradient id="g1" cx="35%" cy="25%" r="75%"> <stop offset="0%" stop-color="#6ae0ff"/> <stop offset="45%" stop-color="#7b62ff"/> <stop offset="100%" stop-color="#ff4fd6"/> </radialGradient> </defs> <circle cx="32" cy="32" r="28" fill="url(#g1)" opacity=".95"/> <circle cx="32" cy="32" r="18" fill="rgba(0,0,0,.18)" /> <path d="M18 34c5-10 23-20 28-10 5 10-8 20-18 20-6 0-12-4-10-10z" fill="rgba(255,255,255,.28)"/> <path d="M20 30c8-10 28-12 28 2" stroke="rgba(255,255,255,.45)" stroke-width="2" fill="none" stroke-linecap="round"/> </svg> <div class="brandTxt"> <b>GAYLORD SINCLAIR</b> <span>SIMON Arcade • Neon Defender</span> </div> </div> <div class="hud"> <div class="pill"><label>WAVE</label><strong id="wave">1</strong></div> <div class="pill"><label>SCORE</label><strong id="score">0</strong></div> <div class="pill"><label>BEST</label><strong id="best">0</strong></div> <div class="pill"><label>CORE</label><strong id="core">100</strong><small>%</small></div> <div class="pill"><label>SHIELD</label><strong id="shield">100</strong><small>%</small></div> <div class="pill"><label>BLAST</label><strong id="blast">READY</strong></div> <button class="btn cyan" id="pause">PAUSE</button> <button class="btn pink" id="menuBtn">MENU</button> </div> </div> <div class="overlay" id="menu" style="display:flex"> <div class="panel"> <div class="grid"> <div> <h1>GAYLORD NEON DEFENDER</h1> <div class="sub"> Orbit shields to intercept incoming glitch swarms. Trigger a <b>shockwave</b> for clutch clears. 🛡️⚡ </div> <div class="rules"> <div class="card"><b style="color:var(--cyan)">AIM</b><span>Move mouse / touch to steer shield arc.</span></div> <div class="card"><b style="color:var(--warn)">BLAST</b><span><b>Space</b> or Click/Tap to shockwave.</span></div> <div class="card"><b style="color:var(--pink)">POWERUPS</b><span>Grab orbs: heal, shield, slow, multi.</span></div> <div class="card"><b style="color:var(--green)">BOSS</b><span>Every 5 waves: heavy core breach.</span></div> </div> </div> <div> <div class="row"> <div class="sub"><b>Difficulty</b><br><span style="color:var(--mut)">enemy speed + damage</span></div> <select id="diff"> <option value="chill">Chill</option> <option value="arcade" selected>Arcade</option> <option value="hard">Hard</option> </select> </div> <div class="row"> <button class="btn good" id="play">PLAY</button> <button class="btn bad" id="reset">RESET BEST</button> </div> <div class="sub" style="margin-top:10px">Tip: keep shield between swarm and core—don’t chase.</div> </div> </div> </div> </div> <div class="toast" id="toast">Ready</div> <script> (() => { "use strict"; const clamp=(v,a,b)=>Math.max(a,Math.min(b,v)); const rand=(a,b)=>a+Math.random()*(b-a); // Canvas const cv=document.getElementById("cv"); const ctx=cv.getContext("2d",{alpha:true}); let W=0,H=0,DPR=1; function resize(){ DPR=Math.min(2,devicePixelRatio||1); W=Math.floor(innerWidth); H=Math.floor(innerHeight); cv.width=Math.floor(W*DPR); cv.height=Math.floor(H*DPR); cv.style.width=W+"px"; cv.style.height=H+"px"; ctx.setTransform(DPR,0,0,DPR,0,0); } addEventListener("resize",resize,{passive:true}); resize(); const $=id=>document.getElementById(id); const menu=$("menu"), playBtn=$("play"), pauseBtn=$("pause"), menuBtn=$("menuBtn"), resetBtn=$("reset"); const diffSel=$("diff"), toast=$("toast"); const elWave=$("wave"), elScore=$("score"), elBest=$("best"), elCore=$("core"), elShield=$("shield"), elBlast=$("blast"); const BEST_KEY="gs_neon_defender_best"; let best=Number(localStorage.getItem(BEST_KEY)||"0"); elBest.textContent=String(best); function showToast(msg){ toast.textContent=msg; toast.classList.add("on"); clearTimeout(showToast._t); showToast._t=setTimeout(()=>toast.classList.remove("on"), 900); } // Background particles const bg=[]; function initBG(){ bg.length=0; const n=220; for(let i=0;i<n;i++){ bg.push({x:Math.random()*W,y:Math.random()*H,r:rand(0.8,2.4),vx:rand(-0.10,0.10),vy:rand(0.02,0.22),a:rand(0.10,0.45),h:rand(180,320)}); } } initBG(); addEventListener("resize",initBG,{passive:true}); // FX particles const fx=[]; function burst(x,y,n=18,h=rand(180,320)){ for(let i=0;i<n;i++){ const a=rand(0,Math.PI*2), s=rand(0.8,6.0); fx.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,r:rand(1,3),a:1,drag:rand(0.90,0.985),h:h+rand(-15,15)}); } } // Audio (tiny WebAudio beeps) let AC=null, master=null, audioReady=false, muted=false; function ensureAudio(){ if(audioReady) return; try{ AC = new (window.AudioContext||window.webkitAudioContext)(); master = AC.createGain(); master.gain.value = 0.20; master.connect(AC.destination); audioReady=true; }catch(err){ audioReady=false; } } function beep(freq=880, dur=0.06){ if(!audioReady || muted) return; const t=AC.currentTime; const o=AC.createOscillator(); const g=AC.createGain(); o.type="sine"; o.frequency.setValueAtTime(freq,t); g.gain.setValueAtTime(0.0001,t); g.gain.exponentialRampToValueAtTime(0.35,t+0.010); g.gain.exponentialRampToValueAtTime(0.0001,t+dur); o.connect(g); g.connect(master); o.start(t); o.stop(t+dur+0.02); } // Difficulty tuning const DIFF={ chill:{spd:0.85, dmg:0.75, spawn:0.95}, arcade:{spd:1.00, dmg:1.00, spawn:1.00}, hard:{ spd:1.18, dmg:1.20, spawn:1.08}, }; // World let running=false, paused=false; let wave=1, score=0; let core=100, shield=100; // Center "core" function center(){ return {x:W*0.5, y:H*0.56}; } function coreRadius(){ return Math.min(W,H)*0.075; } // Shield orbit + aim let aimX=W*0.5, aimY=H*0.2; let shieldAng=0; // current orbit angle let shieldAngVel=0; let shieldArc=0.85; // radians coverage let shieldR=0; // orbit radius let shieldHP=100; // Blast (shockwave) let blastCD=0; let shock=0; // expanding ring radius when active // Enemies + powerups const enemies=[]; // {x,y,vx,vy,r,spd,hp,type} const orbs=[]; // {x,y,vx,vy,r,type,ttl} const debris=[]; // {x,y,vx,vy,a,r,h} function resetRun(){ enemies.length=0; orbs.length=0; fx.length=0; debris.length=0; wave=1; score=0; core=100; shield=100; shieldHP=100; blastCD=0; shock=0; shieldAng=0; shieldAngVel=0; showToast("DEFEND THE CORE"); updateHUD(); } function updateHUD(){ elWave.textContent=String(wave); elScore.textContent=String(Math.round(score)); elBest.textContent=String(best); elCore.textContent=String(Math.round(core)); elShield.textContent=String(Math.round(shieldHP)); elBlast.textContent = blastCD<=0 ? "READY" : Math.ceil(blastCD).toString(); } // Input function setAim(x,y){ aimX=x; aimY=y; } addEventListener("pointermove",(e)=>setAim(e.clientX,e.clientY),{passive:true}); addEventListener("pointerdown",(e)=>{ ensureAudio(); if(AC && AC.state==="suspended") AC.resume().catch(()=>{}); if(menu.style.display!=="none") return; triggerBlast(); },{passive:true}); addEventListener("keydown",(e)=>{ if(e.key===" "){ e.preventDefault(); ensureAudio(); if(AC && AC.state==="suspended") AC.resume().catch(()=>{}); triggerBlast(); } if(e.key==="p"||e.key==="P"){ if(running){ paused=!paused; showToast(paused?"Paused":"Go!"); } } if(e.key==="Escape"){ menu.style.display=(menu.style.display==="none")?"flex":"none"; if(menu.style.display!=="none") paused=true; } },{passive:false}); // Buttons playBtn.addEventListener("click",()=>{ menu.style.display="none"; running=true; paused=false; resetRun(); }); pauseBtn.addEventListener("click",()=>{ if(running){ paused=!paused; showToast(paused?"Paused":"Go!"); }}); menuBtn.addEventListener("click",()=>{ menu.style.display="flex"; paused=true; }); resetBtn.addEventListener("click",()=>{ best=0; localStorage.setItem(BEST_KEY,"0"); elBest.textContent="0"; showToast("Best cleared"); }); // Spawn system let spawnT=0; function spawnEnemy(kind="drone"){ const cfg=DIFF[diffSel.value]||DIFF.arcade; const c=center(); const R=Math.min(W,H)*0.52; const a=rand(0,Math.PI*2); const x=c.x + Math.cos(a)*R; const y=c.y + Math.sin(a)*R; let r=10, hp=1, spd=rand(70,120)*cfg.spd, type=kind; if(kind==="brute"){ r=16; hp=3; spd=rand(55,85)*cfg.spd; } if(kind==="boss"){ r=28; hp=18; spd=rand(35,55)*cfg.spd; } enemies.push({x,y,vx:0,vy:0,r,hp,spd,type}); } function maybeBossWave(){ return (wave%5===0); } function startWave(w){ const cfg=DIFF[diffSel.value]||DIFF.arcade; // wave spawn pattern const base = 10 + Math.floor(w*2.2); const brutes = Math.floor(w/3); for(let i=0;i<base;i++) spawnEnemy("drone"); for(let i=0;i<brutes;i++) spawnEnemy("brute"); if(maybeBossWave()){ spawnEnemy("boss"); showToast("BOSS WAVE!"); beep(220,0.12); } else { showToast("WAVE " + w); } spawnT = 1.2 / cfg.spawn; } // Orbs function dropOrb(x,y){ const roll=Math.random(); let type="heal"; if(roll<0.35) type="heal"; else if(roll<0.62) type="shield"; else if(roll<0.82) type="slow"; else type="multi"; orbs.push({x,y,vx:rand(-30,30),vy:rand(-40,40),r:10,type,ttl:10}); } function triggerBlast(){ if(!running || paused) return; if(blastCD>0) return; shock=1; // start blastCD=6.5; showToast("SHOCKWAVE!"); beep(980,0.06); beep(620,0.08); } function gameOver(){ running=false; showToast("CORE BREACHED"); const final=Math.round(score); if(final>best){ best=final; localStorage.setItem(BEST_KEY,String(best)); elBest.textContent=String(best); showToast("NEW BEST!"); } setTimeout(()=>{ menu.style.display="flex"; paused=true; }, 800); } // Draw helpers function roundRect(c,x,y,w,h,r){ r=Math.min(r,w/2,h/2); c.beginPath(); c.moveTo(x+r,y); c.arcTo(x+w,y,x+w,y+h,r); c.arcTo(x+w,y+h,x,y+h,r); c.arcTo(x,y+h,x,y,r); c.arcTo(x,y,x+w,y,r); c.closePath(); } // Loop let last=performance.now(); let waveStarted=false; function step(now){ const dt=clamp((now-last)/1000, 0, 0.033); last=now; // background ctx.fillStyle="rgba(7,11,22,0.18)"; ctx.fillRect(0,0,W,H); for(const s of bg){ s.x+=s.vx; s.y+=s.vy; if(s.y>H+20){ s.y=-20; s.x=Math.random()*W; } if(s.x<-20) s.x=W+20; if(s.x>W+20) s.x=-20; ctx.beginPath(); ctx.fillStyle=`hsla(${s.h},100%,70%,${s.a})`; ctx.arc(s.x,s.y,s.r,0,Math.PI*2); ctx.fill(); } // layout orbit radius shieldR = Math.min(W,H)*0.205; // draw frame even if paused if(!running || paused){ drawWorld(0); requestAnimationFrame(step); return; } const cfg=DIFF[diffSel.value]||DIFF.arcade; if(!waveStarted){ enemies.length=0; orbs.length=0; startWave(wave); waveStarted=true; } // timers if(blastCD>0) blastCD=Math.max(0, blastCD-dt); // shield aim -> target angle const c=center(); const targetAng = Math.atan2(aimY-c.y, aimX-c.x); // smooth rotate shortest path let d = targetAng - shieldAng; while(d> Math.PI) d -= Math.PI*2; while(d<-Math.PI) d += Math.PI*2; shieldAngVel += d * dt * 9.0; shieldAngVel *= Math.pow(0.0008, dt); shieldAng += shieldAngVel; // keep within -pi..pi range if(shieldAng>Math.PI) shieldAng-=Math.PI*2; if(shieldAng<-Math.PI) shieldAng+=Math.PI*2; // shockwave expand + apply if(shock>0){ const maxR=Math.min(W,H)*0.55; const rNow = shock * maxR; shock += dt * 2.2; // apply to enemies within ring band for(let i=enemies.length-1;i>=0;i--){ const e=enemies[i]; const dist=Math.hypot(e.x-c.x, e.y-c.y); if(dist < rNow && dist > rNow-60){ e.hp -= (e.type==="boss") ? 1 : 999; burst(e.x,e.y, 20, 280); score += (e.type==="boss") ? 60 : 25; if(e.hp<=0){ if(Math.random()<0.25) dropOrb(e.x,e.y); enemies.splice(i,1); beep(860,0.045); } } } if(rNow>=maxR) shock=0; } // enemy AI -> move to core for(let i=enemies.length-1;i>=0;i--){ const e=enemies[i]; const dx=c.x-e.x, dy=c.y-e.y; const dist=Math.hypot(dx,dy)||1; const ux=dx/dist, uy=dy/dist; let sp = e.spd; // slow orb effect not global; but we do a light slow while orb active using score pulse trick (simple) // (kept deterministic; optional) e.vx = ux*sp; e.vy = uy*sp; e.x += e.vx*dt; e.y += e.vy*dt; // shield collision: arc segment on orbit ring const angToE = Math.atan2(e.y-c.y, e.x-c.x); let da = angToE - shieldAng; while(da> Math.PI) da -= Math.PI*2; while(da<-Math.PI) da += Math.PI*2; const onArc = Math.abs(da) < shieldArc*0.5; const onRing = Math.abs(dist - shieldR) < (e.r + 10); if(onArc && onRing && shieldHP>0){ // deflect + damage e.hp -= (e.type==="boss") ? 1 : 999; shieldHP = Math.max(0, shieldHP - (6 * cfg.dmg) * (e.type==="boss" ? 2.5 : 1)); burst(e.x,e.y, 16, 200); score += (e.type==="boss") ? 50 : 20; beep(920,0.04); // knockback e.x -= ux*26; e.y -= uy*26; if(e.hp<=0){ if(Math.random()<0.22) dropOrb(e.x,e.y); enemies.splice(i,1); burst(e.x,e.y, 26, 48); } } // core collision if(dist < coreRadius() + e.r){ const hit = (e.type==="boss") ? 14 : (e.type==="brute" ? 8 : 5); core = Math.max(0, core - hit*cfg.dmg); burst(e.x,e.y, 28, 350); beep(180,0.08); enemies.splice(i,1); if(core<=0){ gameOver(); } } } // orb update + collect for(let i=orbs.length-1;i>=0;i--){ const o=orbs[i]; o.ttl -= dt; o.vx *= Math.pow(0.02, dt); o.vy *= Math.pow(0.02, dt); o.x += o.vx*dt; o.y += o.vy*dt; const dist=Math.hypot(o.x-c.x, o.y-c.y); if(dist < shieldR + 26 && dist > shieldR - 26){ // collect if on orbit band if(o.type==="heal"){ core = clamp(core + 12, 0, 100); showToast("CORE HEAL +"); beep(700,0.06); } else if(o.type==="shield"){ shieldHP = clamp(shieldHP + 30, 0, 100); showToast("SHIELD +"); beep(820,0.06); } else if(o.type==="slow"){ // apply immediate: reduce enemy speeds briefly by chopping their spd for(const e of enemies) e.spd *= 0.72; showToast("SLOW FIELD"); beep(520,0.08); } else if(o.type==="multi"){ score += 300; showToast("BONUS +300"); beep(980,0.05); } burst(o.x,o.y, 26, 280); orbs.splice(i,1); continue; } if(o.ttl<=0) orbs.splice(i,1); } // regen shield slowly shieldHP = clamp(shieldHP + dt*2.2, 0, 100); // wave complete? if(enemies.length===0){ wave++; waveStarted=false; score += 250; showToast("WAVE CLEAR +250"); beep(980,0.05); beep(1240,0.06); } // fx update for(let i=fx.length-1;i>=0;i--){ const q=fx[i]; q.vx*=q.drag; q.vy*=q.drag; q.x += q.vx*2.0; q.y += q.vy*2.0; q.a -= 0.020; if(q.a<=0) fx.splice(i,1); } // passive scoring score += dt * 4; updateHUD(); drawWorld(dt); requestAnimationFrame(step); } function drawWorld(dt){ const c=center(); const cr=coreRadius(); // core glow ctx.save(); ctx.globalCompositeOperation="lighter"; const g=ctx.createRadialGradient(c.x,c.y,cr*0.15,c.x,c.y,cr*3.2); g.addColorStop(0,"rgba(255,255,255,.08)"); g.addColorStop(.35,"rgba(123,98,255,.18)"); g.addColorStop(.65,"rgba(106,224,255,.10)"); g.addColorStop(1,"rgba(255,79,214,0)"); ctx.fillStyle=g; ctx.beginPath(); ctx.arc(c.x,c.y,cr*3.2,0,Math.PI*2); ctx.fill(); // core disk ctx.globalCompositeOperation="source-over"; ctx.fillStyle="rgba(0,0,0,.35)"; ctx.beginPath(); ctx.arc(c.x,c.y,cr,0,Math.PI*2); ctx.fill(); // core ring health ctx.globalCompositeOperation="lighter"; ctx.lineWidth=5; ctx.strokeStyle="rgba(68,209,138,.30)"; ctx.beginPath(); ctx.arc(c.x,c.y,cr*1.25,0,Math.PI*2); ctx.stroke(); ctx.strokeStyle="rgba(255,204,77,.55)"; ctx.beginPath(); ctx.arc(c.x,c.y,cr*1.25,-Math.PI/2, -Math.PI/2 + (Math.PI*2)*(core/100)); ctx.stroke(); // orbit ring ctx.strokeStyle="rgba(106,224,255,.14)"; ctx.lineWidth=3; ctx.beginPath(); ctx.arc(c.x,c.y,shieldR,0,Math.PI*2); ctx.stroke(); // shield arc (segment) const a0 = shieldAng - shieldArc*0.5; const a1 = shieldAng + shieldArc*0.5; ctx.lineWidth=10; ctx.strokeStyle="rgba(106,224,255,.45)"; ctx.beginPath(); ctx.arc(c.x,c.y,shieldR,a0,a1); ctx.stroke(); ctx.lineWidth=6; ctx.strokeStyle="rgba(0,0,0,.35)"; ctx.beginPath(); ctx.arc(c.x,c.y,shieldR,a0,a1); ctx.stroke(); // shockwave if(shock>0){ const maxR=Math.min(W,H)*0.55; const rNow = shock * maxR; ctx.globalCompositeOperation="lighter"; ctx.lineWidth=3; ctx.strokeStyle="rgba(255,79,214,.22)"; ctx.beginPath(); ctx.arc(c.x,c.y,rNow,0,Math.PI*2); ctx.stroke(); ctx.strokeStyle="rgba(255,204,77,.16)"; ctx.beginPath(); ctx.arc(c.x,c.y,rNow-22,0,Math.PI*2); ctx.stroke(); } // enemies for(const e of enemies){ ctx.globalCompositeOperation="lighter"; const eg=ctx.createRadialGradient(e.x,e.y,2,e.x,e.y,e.r*3.0); eg.addColorStop(0,"rgba(255,255,255,.06)"); eg.addColorStop(.35, e.type==="boss" ? "rgba(255,204,77,.18)" : "rgba(255,79,109,.18)"); eg.addColorStop(1,"rgba(255,79,214,0)"); ctx.fillStyle=eg; ctx.beginPath(); ctx.arc(e.x,e.y,e.r*3.0,0,Math.PI*2); ctx.fill(); ctx.globalCompositeOperation="source-over"; ctx.fillStyle="rgba(0,0,0,.35)"; ctx.beginPath(); ctx.arc(e.x,e.y,e.r,0,Math.PI*2); ctx.fill(); ctx.globalCompositeOperation="lighter"; ctx.lineWidth=2; ctx.strokeStyle= e.type==="boss" ? "rgba(255,204,77,.60)" : "rgba(255,79,109,.60)"; ctx.beginPath(); ctx.arc(e.x,e.y,e.r*1.08,0,Math.PI*2); ctx.stroke(); // boss hp ring if(e.type==="boss"){ ctx.lineWidth=3; ctx.strokeStyle="rgba(255,204,77,.22)"; ctx.beginPath(); ctx.arc(e.x,e.y,e.r*1.55,0,Math.PI*2); ctx.stroke(); const frac=clamp(e.hp/18,0,1); ctx.strokeStyle="rgba(106,224,255,.45)"; ctx.beginPath(); ctx.arc(e.x,e.y,e.r*1.55,-Math.PI/2,-Math.PI/2+Math.PI*2*frac); ctx.stroke(); } } // orbs for(const o of orbs){ ctx.globalCompositeOperation="lighter"; const hue = (o.type==="heal")?140:(o.type==="shield")?200:(o.type==="slow")?280:48; const og=ctx.createRadialGradient(o.x,o.y,2,o.x,o.y,o.r*3.0); og.addColorStop(0,"rgba(255,255,255,.06)"); og.addColorStop(.35,`hsla(${hue},100%,70%,.20)`); og.addColorStop(1,"rgba(255,79,214,0)"); ctx.fillStyle=og; ctx.beginPath(); ctx.arc(o.x,o.y,o.r*3.0,0,Math.PI*2); ctx.fill(); ctx.globalCompositeOperation="source-over"; ctx.fillStyle="rgba(0,0,0,.30)"; ctx.beginPath(); ctx.arc(o.x,o.y,o.r,0,Math.PI*2); ctx.fill(); ctx.globalCompositeOperation="lighter"; ctx.strokeStyle=`hsla(${hue},100%,70%,.60)`; ctx.lineWidth=2; ctx.beginPath(); ctx.arc(o.x,o.y,o.r*1.10,0,Math.PI*2); ctx.stroke(); } // fx particles for(const q of fx){ ctx.globalCompositeOperation="lighter"; ctx.fillStyle=`hsla(${q.h},100%,70%,${q.a*0.55})`; ctx.beginPath(); ctx.arc(q.x,q.y,q.r,0,Math.PI*2); ctx.fill(); } // hint ctx.globalCompositeOperation="source-over"; ctx.fillStyle="rgba(234,242,255,.70)"; ctx.font="900 12px ui-sans-serif,system-ui"; ctx.textAlign="center"; ctx.fillText("MOVE = AIM SHIELD • SPACE / CLICK = SHOCKWAVE", c.x, c.y + shieldR + 42); ctx.restore(); } // init menu.style.display="flex"; paused=true; showToast("Ready"); updateHUD(); requestAnimationFrame(step); // persist best on unload (safe) addEventListener("visibilitychange",()=>{ if(document.hidden){ const final=Math.round(score); if(final>best){ best=final; localStorage.setItem(BEST_KEY,String(best)); } } }); })(); </script> </body> </html>
Save file
Quick jump
open a path
Open