From 6ddcc39c270b82baf175596cabed5b33c4862924 Mon Sep 17 00:00:00 2001 From: Codinget Date: Wed, 25 Mar 2020 19:29:55 +0100 Subject: [PATCH] upgraded engine with input buffering and added arcade & survival --- assets/levelList.json | 1 + assets/snake.json | 3 +- levels/arcade-arcade.json | 12 ++++++++ levels/arcade-survival.json | 16 ++++++++++ src/js/snek.js | 60 ++++++++++++++++++++++++++----------- 5 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 levels/arcade-arcade.json create mode 100644 levels/arcade-survival.json diff --git a/assets/levelList.json b/assets/levelList.json index 4692bd8..14f63e9 100644 --- a/assets/levelList.json +++ b/assets/levelList.json @@ -20,6 +20,7 @@ "fruitRegrow": true, "speedIncrease": true, "speedMultiplier": 0.9, + "speedCap": 50, "worldWrap": true }, "levelFilename": "arcade-.json", diff --git a/assets/snake.json b/assets/snake.json index 77ced44..ca4a1c7 100644 --- a/assets/snake.json +++ b/assets/snake.json @@ -3,5 +3,6 @@ "join": "round", "cap": "round", "headSize": 0.8, - "tailSize": 0.4 + "tailSize": 0.4, + "tailWrapSize": 0.1 } diff --git a/levels/arcade-arcade.json b/levels/arcade-arcade.json new file mode 100644 index 0000000..26a509e --- /dev/null +++ b/levels/arcade-arcade.json @@ -0,0 +1,12 @@ +{ + "dimensions": [32, 32], + "delay": 200, + "food": [ + [16, 16] + ], + "snake": [ + [16, 12], + [16, 11], + [16, 10] + ] +} diff --git a/levels/arcade-survival.json b/levels/arcade-survival.json new file mode 100644 index 0000000..7c2f485 --- /dev/null +++ b/levels/arcade-survival.json @@ -0,0 +1,16 @@ +{ + "dimensions": [32, 32], + "delay": 200, + "food": [], + "snake": [ + [16, 12], + [16, 11], + [16, 10] + ], + "rules": { + "autoSpeedIncrease": true, + "autoSpeadIncreaseTicks": 10, + "autoSizeGrow": true, + "autoSizeGrowTicks": 100 + } +} diff --git a/src/js/snek.js b/src/js/snek.js index 0a46102..792ef45 100644 --- a/src/js/snek.js +++ b/src/js/snek.js @@ -1,5 +1,3 @@ -const assets=require('assets'); - const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys(); const ifNaN=(v, r) => isNaN(v)?r:v; @@ -51,7 +49,7 @@ class SnekGame { } // add the walls - settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); + if(settings.walls) settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); // add the food settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); @@ -68,6 +66,7 @@ class SnekGame { ifNaN(settings.snake[0][0]-settings.snake[1][0], 1), ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) ]; + this.lastDirection=this.direction // store the snake this.snake=[...settings.snake]; @@ -75,7 +74,6 @@ class SnekGame { // get our canvas, like, if we want to actually draw this.canvas=canvas; this.ctx=canvas.getContext('2d'); - //TODO this.gl=canvas.getContext('webgl'); // load the custom rules this.rules=Object.assign({ @@ -93,6 +91,8 @@ class SnekGame { } draw() { + const assets=require('assets'); + // clear the canvas, because it's easier than having to deal with everything this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); @@ -150,11 +150,22 @@ class SnekGame { this.ctx.fill(); this.ctx.beginPath(); - this.snake.forEach(([x, y], i) => { + this.snake.forEach(([x, y], i, a) => { this.ctx.lineTo( offsetX+cellSize*(x+1/2), offsetY+cellSize*(y+1/2) ); + if(i!=0 && Math.hypot(x-a[i-1][0], y-a[i-1][1])>1) { + this.ctx.lineWidth=cellSize*snake.tailWrapSize; + } else { + this.ctx.lineWidth=cellSize*snake.tailSize; + } + this.ctx.stroke(); + this.ctx.beginPath() + this.ctx.moveTo( + offsetX+cellSize*(x+1/2), + offsetY+cellSize*(y+1/2) + ); }); this.ctx.stroke(); @@ -174,6 +185,9 @@ class SnekGame { } step() { + this.tickId++; + this.lastDirection=this.direction; + // compute our new head const head=[ this.snake[0][0]+this.direction[0], @@ -227,12 +241,27 @@ class SnekGame { this.fruits.push(cell); this.world[cell[0]][cell[1]]=FOOD; } + + if(this.rules.speedIncrease) { + this.delay*=this.rules.speedMultiplier; + if(this.delay50 && this.tickId%this.rules.autoSpeadIncreaseTicks==0) this.delay--; + } + + // automatic size grow + if(this.rules.autoSizeGrow) { + if(this.tickId%this.rules.autoSizeGrowTicks==0) this.snake.push(tail); + } + // victory condition if(this.rules.winCondition=='fruit') { if(!this.fruits.length) return this.win(); @@ -271,23 +300,20 @@ class SnekGame { handleInputs(inputs) { const trySet=(dir) => { - if(!(this.direction[0]==-dir[0] && this.direction[1]==-dir[1])) this.direction=dir; - } - if(inputs.left) { - trySet([-1, 0]); - } else if(inputs.right) { - trySet([ 1, 0]); - } else if(inputs.up) { - trySet([ 0,-1]); - } else if(inputs.down) { - trySet([ 0, 1]); + if(!dir.every((e, i) => e==this.lastDirection[i] || e==-this.lastDirection[i])) { + this.direction=dir; + return true; + } } - - Object.keys(inputs).forEach(k => delete inputs[k]); + if(inputs.left && trySet([-1, 0])) return delete inputs.left; + else if(inputs.right && trySet([ 1, 0])) return delete inputs.right; + else if(inputs.up && trySet([ 0,-1])) return delete inputs.up; + else if(inputs.down && trySet([ 0, 1])) return delete inputs.down; } start() { this.firstStep=Date.now(); + this.tickId=0; this.playing=true; requestAnimationFrame(() => this.tick()); }