(async () => { location.hash=''; const assets=require('assets'); const Popup=require('popup'); const SnekGame=require('snek'); await new Promise(ok => assets.onReady(ok)); const main=document.querySelector('main'); const nav=main.querySelector('nav'); const canvas=main.querySelector('canvas'); const config=assets.get('config'); let currentGame=null; let currentInputs={}; const resizeCanvas=() => { if(document.fullscreenElement) { canvas.width=screen.width; canvas.height=screen.height; } else { canvas.width=main.clientWidth; canvas.height=main.clientHeight; } }; resizeCanvas(); window.addEventListener('resize', resizeCanvas); const levelList=assets.get('levelList'); Object.keys(levelList).forEach(category => { const cat=levelList[category]; const section=nav.appendChild(document.createElement('section')); const h1=section.appendChild(document.createElement('h1')); h1.innerText=category[0].toUpperCase()+category.slice(1)+" Mode"; const p=section.appendChild(document.createElement('p')); p.innerText=cat.desc; const ul=section.appendChild(document.createElement('ul')); cat.levels.forEach((level, i) => { level=''+level; const displayName=cat.levelDisplay .replace(//g, level) .replace(//g, level.toLowerCase()); const fileName=cat.levelFilename .replace(//g, level) .replace(//g, level.toLowerCase()); const li=ul.appendChild(document.createElement('li')); const a=li.appendChild(document.createElement('a')); a.href='#'+category+'/'+fileName; a.innerText=displayName; if(cat.levelDesc) { const span=li.appendChild(document.createElement('span')); span.innerText=cat.levelDesc[i]; } }); }); const handleGamepads=() => { const gp=navigator.getGamepads()[0]; let inputs=currentInputs; if(!gp || !gp.axes) return; const magnitude=Math.hypot(gp.axes[0], gp.axes[1]); const angle=((Math.atan2(gp.axes[0], gp.axes[1])+2*Math.PI)%(2*Math.PI))/Math.PI; if(magnitude>config.gamepad.deadzone) { if(angle>.25 && angle <.75) inputs.right=true; else if(angle>.75 && angle<1.25) inputs.up=true; else if(angle>1.25 && angle<1.75) inputs.left=true; else inputs.down=true; } if(!config.gamepad.buffer) inputs.clearBuffer=true; }; const startGame=async (category, filename) => { //TODO migrate relevant code here }; window.addEventListener('hashchange', async () => { nav.classList.add('hidden'); const [_, category, filename]=location.hash.match(/([a-zA-Z0-9_-]+?)\/(.+)/); const rules=levelList[category].rules || {}; const level=await (async () => { const resp=await fetch('levels/'+filename); return await resp.json(); })(); const snek=new SnekGame(level, canvas, rules); canvas.classList.remove('hidden'); snek.start(); snek.callback=evt => { if(evt=='tick') { if(navigator.getGamepads) handleGamepads(); snek.handleInputs(currentInputs); } else if(evt=='win') { let popup=new Popup("Finished!"); popup.addStrong("You won!"); popup.addContent({ "Time": snek.playTime/1000+'s', "Score": snek.score, "Final length": snek.snake.length }); popup.buttons={ retry: "Retry", next: "Next level", menu: "Main menu" }; popup.display(); //TODO do something with the result } }; currentGame=snek; }); window.addEventListener('keydown', async e => { e.preventDefault(); if(e.key=='f') { if(document.fullscreenElement) await document.exitFullscreen(); else await main.requestFullscreen(); resizeCanvas(); } let inputs=currentInputs; if(e.key=='ArrowUp') inputs.up=true; else if(e.key=='ArrowDown') inputs.down=true; else if(e.key=='ArrowLeft') inputs.left=true; else if(e.key=='ArrowRight') inputs.right=true; if(!config.keyboard.buffer) inputs.clearBuffer=true; }); if(config.touchscreen.mode=='crosspad') { const handleTouch=e => { let x=e.touches[0].clientX-window.innerWidth/2; let y=e.touches[0].clientY-window.innerHeight/2; const angle=((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI; let inputs=currentInputs; if(angle>.25 && angle <.75) inputs.right=true; else if(angle>.75 && angle<1.25) inputs.up=true; else if(angle>1.25 && angle<1.75) inputs.left=true; else inputs.down=true; if(!config.touchscreen.buffer) inputs.clearBuffer=true; }; window.addEventListener('touchstart', handleTouch); window.addEventListener('touchmove', handleTouch); } if(config.touchscreen.mode=='joystick') { let center={x: 0, y: 0}; window.center=center; window.addEventListener('touchstart', e => { center.x=e.touches[0].clientX; center.y=e.touches[0].clientY; }); window.addEventListener('touchmove', e => { let x=e.touches[0].clientX-center.x; let y=e.touches[0].clientY-center.y; const angle=((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI; const magnitude=Math.hypot(x, y); let inputs=currentInputs; if(magnitude>config.touchscreen.deadzone) { if(angle>.25 && angle <.75) inputs.right=true; else if(angle>.75 && angle<1.25) inputs.up=true; else if(angle>1.25 && angle<1.75) inputs.left=true; else inputs.down=true; } if(!config.touchscreen.buffer) inputs.clearBuffer=true; }); } if(config.touchscreen.mode=='swipe') { let center={x: 0, y: 0}; window.center=center; window.addEventListener('touchstart', e => { center.x=e.touches[0].clientX; center.y=e.touches[0].clientY; }); window.addEventListener('touchmove', e => { let x=e.touches[0].clientX-center.x; let y=e.touches[0].clientY-center.y; const angle=((Math.atan2(x, y)+2*Math.PI)%(2*Math.PI))/Math.PI; const magnitude=Math.hypot(x, y); let inputs=currentInputs; if(magnitude>config.touchscreen.deadzone) { if(angle>.25 && angle <.75) inputs.right=true; else if(angle>.75 && angle<1.25) inputs.up=true; else if(angle>1.25 && angle<1.75) inputs.left=true; else inputs.down=true; center.x=e.touches[0].clientX; center.y=e.touches[0].clientY; } if(!config.touchscreen.buffer) inputs.clearBuffer=true; }); } })();