From e70f1a73dcdaddbf38c2747471dcb3cf5e72e6a0 Mon Sep 17 00:00:00 2001 From: Codinget Date: Wed, 25 Mar 2020 18:29:28 +0100 Subject: [PATCH] fixed engine and added level2 --- assets/levelList.json | 14 +++++- levels/level2.json | 23 +++++++++ mergejs.js | 4 +- src/js/main.js | 30 ++++++++---- src/js/snek.js | 106 ++++++++++++++++++++++++++++++----------- src/less/mainMenu.less | 16 +++++++ src/less/popup.less | 29 +++++++++++ src/less/snek.less | 8 ++-- 8 files changed, 185 insertions(+), 45 deletions(-) create mode 100644 levels/level2.json create mode 100644 src/less/popup.less diff --git a/assets/levelList.json b/assets/levelList.json index 6ac6f44..4692bd8 100644 --- a/assets/levelList.json +++ b/assets/levelList.json @@ -1,11 +1,12 @@ { "speedrun": { + "desc": "Get all the fruits as fast as possible without touching the walls", "rules": { "fruitRegrow": false, "speedIncrease": false, "worldWrap": false, "winCondition": "fruit", - "scoreSystem": "time" + "scoreSystem": "speedrun" }, "levelFilename": "level.json", "levelDisplay": "Level ", @@ -14,9 +15,11 @@ ] }, "arcade": { + "desc": "Have fun just like in the good ol' days, walls wrap around, fruits respawn and speed increases", "rules": { "fruitRegrow": true, "speedIncrease": true, + "speedMultiplier": 0.9, "worldWrap": true }, "levelFilename": "arcade-.json", @@ -24,7 +27,14 @@ "levels": [ "Arcade", "Timed", - "Survival" + "Survival", + "Versus" + ], + "levelDesc": [ + "The old classic, try to get as high as a score as you can", + "Get a score as high as you can in 30 seconds", + "Survive for as long as you can in an increasingly difficult game", + "Fight against an opponent online" ] } } diff --git a/levels/level2.json b/levels/level2.json new file mode 100644 index 0000000..eabcc7c --- /dev/null +++ b/levels/level2.json @@ -0,0 +1,23 @@ +{ + "world": [ + " ", + " f ", + " fw w wf ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " w ", + " f " + ], + "snake": [ + [6,6], + [6,7], + [6,8], + [6,9] + ], + "delay": 100 +} diff --git a/mergejs.js b/mergejs.js index 6692b72..2bb6bc6 100644 --- a/mergejs.js +++ b/mergejs.js @@ -4,7 +4,7 @@ const requireFn=` const require=function require(name) { if(require.cache[name]) return require.cache[name]; if(!require.source[name]) throw new Error("Cannot require "+name+": not found"); - require.cache[name]=require.source[name]() || true; + require.cache[name]=require.source[name]({}) || true; return require.cache[name]; }; require.cache=Object.create(null); @@ -19,7 +19,7 @@ process.argv .forEach(([modFile, modName]) => { const modSource=fs.readFileSync(modFile, 'utf8'); outputCode.push(` -require.source['${modName}']=(a => a.bind(a)) (function ${modName}() { +require.source['${modName}']=(a => a.bind(a)) (function ${modName}(module) { 'use strict'; ${modSource} }); diff --git a/src/js/main.js b/src/js/main.js index 1e871bf..35e82a4 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -25,14 +25,18 @@ const levelList=assets.get('levelList'); Object.keys(levelList).forEach(category => { - const nav=document.querySelector('nav'); + 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')); - levelList[category].levels.forEach(level => { + cat.levels.forEach((level, i) => { level=''+level; - const cat=levelList[category]; const displayName=cat.levelDisplay .replace(//g, level) .replace(//g, level.toLowerCase()); @@ -43,13 +47,16 @@ 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; - console.log(gp); if(!gp || !gp.axes) return; const magnitude=Math.hypot(gp.axes[0], gp.axes[1]); @@ -71,23 +78,26 @@ const resp=await fetch('levels/'+filename); return await resp.json(); })(); - console.log(rules, level); const SnekGame=require('snek'); const snek=new SnekGame(level, canvas, rules); canvas.classList.remove('hidden'); snek.start(); - snek.callback=() => { - if(navigator.getGamepads) handleGamepads(); - snek.handleInputs(currentInputs); + snek.callback=evt => { + if(evt=='tick') { + if(navigator.getGamepads) handleGamepads(); + snek.handleInputs(currentInputs); + } }; currentGame=snek; + //XXX + window.snek=snek; }); window.addEventListener('keydown', async e => { - if(e.key=='f' && !canvas.classList.contains('hidden')) { + if(e.key=='f') { if(document.fullscreenElement) await document.exitFullscreen(); - else await canvas.requestFullscreen(); + else await main.requestFullscreen(); resizeCanvas(); } diff --git a/src/js/snek.js b/src/js/snek.js index 66b342a..0a46102 100644 --- a/src/js/snek.js +++ b/src/js/snek.js @@ -6,20 +6,61 @@ const ifNaN=(v, r) => isNaN(v)?r:v; class SnekGame { constructor(settings, canvas, rules) { - // build the world - this.dimensions=[...settings.dimensions]; - this.world=Array(settings.dimensions[0]); - for(let i=0; i { + switch(settings.world[y][x]) { + case ' ': return EMPTY; + case 'f': return FOOD; + case 'w': return WALL; + } + })(); + } + } + // + // extract the dimensions + this.dimensions=[this.world.length, this.world[0].length]; + + // extract the fruits + this.fruits=[]; + this.world + .forEach((l, x) => l.forEach( + (c, y) => { + if(c==FOOD) this.fruits.push([x, y]); + } + )); + } else { // dimension and objects + + // get the dimensions + this.dimensions=[...settings.dimensions]; + + // build an empty world + this.world=Array(settings.dimensions[0]); + for(let i=0; i this.world[x][y]=WALL); + + // add the food + settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); + this.fruits=[...settings.food]; } - console.log(this); - settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); - settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); + + // add the snake to the world settings.snake.forEach(([x, y]) => this.world[x][y]=SNAKE); - // setup the delay - this.delay=settings.delay; // get the head and initial direction this.head=[...settings.snake[0]]; @@ -28,9 +69,8 @@ class SnekGame { ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) ]; - // get the snake and the fruits themselves + // store the snake this.snake=[...settings.snake]; - this.fruits=[...settings.food]; // get our canvas, like, if we want to actually draw this.canvas=canvas; @@ -43,8 +83,13 @@ class SnekGame { speedIncrease: true, worldWrap: true, winCondition: 'none', - scoreSystem: 'fruit' - }, rules, settings); + scoreSystem: 'fruit', + netPlay: false + }, rules, settings.rules || {}); + } + + get playTime() { + return Date.now()-this.firstStep; } draw() { @@ -121,7 +166,7 @@ class SnekGame { this.ctx.drawImage( fruit, offsetX+cellSize*x+(1-fruitScale)*cellSize/2, - offsetY+cellSize*x+(1-fruitScale)*cellSize/2, + offsetY+cellSize*y+(1-fruitScale)*cellSize/2, cellSize*fruitScale, cellSize*fruitScale ); @@ -163,11 +208,8 @@ class SnekGame { // remove the fruit from existence this.world[head[0]][head[1]]=SNAKE; - this.fruits.splice( - this.fruits.find( - ([x, y]) => x==head[0] && y==head[1] - ), - 1 + this.fruits=this.fruits.filter( + ([x, y]) => !(x==head[0] && y==head[1]) ); // custom rules @@ -192,14 +234,22 @@ class SnekGame { this.snake.unshift(head); // victory condition - if(!this.fruits.length) return this.win(); + if(this.rules.winCondition=='fruit') { + if(!this.fruits.length) return this.win(); + } + if(this.rules.winCondition=='time') { + if(this.playTime>=this.rules.gameDuration) return this.win(); + } + if(this.rules.winCondition=='score') { + if(this.score>=this.rules.scoreObjective) return this.win(); + } } tick() { if(!this.playing) return; if(!this.lastStep) this.lastStep=this.firstStep; this.draw(); - if(this.callback) this.callback(); + if(this.callback) this.callback('tick'); if(this.lastStep+this.delay