|
|
@ -1,5 +1,3 @@ |
|
|
|
const assets=require('assets'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys(); |
|
|
|
const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys(); |
|
|
|
|
|
|
|
|
|
|
|
const ifNaN=(v, r) => isNaN(v)?r:v; |
|
|
|
const ifNaN=(v, r) => isNaN(v)?r:v; |
|
|
@ -51,7 +49,7 @@ class SnekGame { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// add the walls
|
|
|
|
// 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
|
|
|
|
// add the food
|
|
|
|
settings.food.forEach(([x, y]) => this.world[x][y]=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][0]-settings.snake[1][0], 1), |
|
|
|
ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) |
|
|
|
ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
this.lastDirection=this.direction |
|
|
|
|
|
|
|
|
|
|
|
// store the snake
|
|
|
|
// store the snake
|
|
|
|
this.snake=[...settings.snake]; |
|
|
|
this.snake=[...settings.snake]; |
|
|
@ -75,7 +74,6 @@ class SnekGame { |
|
|
|
// get our canvas, like, if we want to actually draw
|
|
|
|
// get our canvas, like, if we want to actually draw
|
|
|
|
this.canvas=canvas; |
|
|
|
this.canvas=canvas; |
|
|
|
this.ctx=canvas.getContext('2d'); |
|
|
|
this.ctx=canvas.getContext('2d'); |
|
|
|
//TODO this.gl=canvas.getContext('webgl');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// load the custom rules
|
|
|
|
// load the custom rules
|
|
|
|
this.rules=Object.assign({ |
|
|
|
this.rules=Object.assign({ |
|
|
@ -93,6 +91,8 @@ class SnekGame { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
draw() { |
|
|
|
draw() { |
|
|
|
|
|
|
|
const assets=require('assets'); |
|
|
|
|
|
|
|
|
|
|
|
// clear the canvas, because it's easier than having to deal with everything
|
|
|
|
// clear the canvas, because it's easier than having to deal with everything
|
|
|
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); |
|
|
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); |
|
|
|
|
|
|
|
|
|
|
@ -150,11 +150,22 @@ class SnekGame { |
|
|
|
this.ctx.fill(); |
|
|
|
this.ctx.fill(); |
|
|
|
|
|
|
|
|
|
|
|
this.ctx.beginPath(); |
|
|
|
this.ctx.beginPath(); |
|
|
|
this.snake.forEach(([x, y], i) => { |
|
|
|
this.snake.forEach(([x, y], i, a) => { |
|
|
|
this.ctx.lineTo( |
|
|
|
this.ctx.lineTo( |
|
|
|
offsetX+cellSize*(x+1/2), |
|
|
|
offsetX+cellSize*(x+1/2), |
|
|
|
offsetY+cellSize*(y+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(); |
|
|
|
this.ctx.stroke(); |
|
|
|
|
|
|
|
|
|
|
@ -174,6 +185,9 @@ class SnekGame { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
step() { |
|
|
|
step() { |
|
|
|
|
|
|
|
this.tickId++; |
|
|
|
|
|
|
|
this.lastDirection=this.direction; |
|
|
|
|
|
|
|
|
|
|
|
// compute our new head
|
|
|
|
// compute our new head
|
|
|
|
const head=[ |
|
|
|
const head=[ |
|
|
|
this.snake[0][0]+this.direction[0], |
|
|
|
this.snake[0][0]+this.direction[0], |
|
|
@ -227,12 +241,27 @@ class SnekGame { |
|
|
|
this.fruits.push(cell); |
|
|
|
this.fruits.push(cell); |
|
|
|
this.world[cell[0]][cell[1]]=FOOD; |
|
|
|
this.world[cell[0]][cell[1]]=FOOD; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(this.rules.speedIncrease) { |
|
|
|
|
|
|
|
this.delay*=this.rules.speedMultiplier; |
|
|
|
|
|
|
|
if(this.delay<this.rules.speedCap) this.delay=this.rules.speedCap; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// move our head forward
|
|
|
|
// move our head forward
|
|
|
|
this.world[head[0]][head[1]]=SNAKE; |
|
|
|
this.world[head[0]][head[1]]=SNAKE; |
|
|
|
this.snake.unshift(head); |
|
|
|
this.snake.unshift(head); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// automatic speed increase
|
|
|
|
|
|
|
|
if(this.rules.autoSpeedIncrease) { |
|
|
|
|
|
|
|
if(this.delay>50 && 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
|
|
|
|
// victory condition
|
|
|
|
if(this.rules.winCondition=='fruit') { |
|
|
|
if(this.rules.winCondition=='fruit') { |
|
|
|
if(!this.fruits.length) return this.win(); |
|
|
|
if(!this.fruits.length) return this.win(); |
|
|
@ -271,23 +300,20 @@ class SnekGame { |
|
|
|
|
|
|
|
|
|
|
|
handleInputs(inputs) { |
|
|
|
handleInputs(inputs) { |
|
|
|
const trySet=(dir) => { |
|
|
|
const trySet=(dir) => { |
|
|
|
if(!(this.direction[0]==-dir[0] && this.direction[1]==-dir[1])) this.direction=dir; |
|
|
|
if(!dir.every((e, i) => e==this.lastDirection[i] || e==-this.lastDirection[i])) { |
|
|
|
|
|
|
|
this.direction=dir; |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
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(inputs.left && trySet([-1, 0])) return delete inputs.left; |
|
|
|
Object.keys(inputs).forEach(k => delete inputs[k]); |
|
|
|
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() { |
|
|
|
start() { |
|
|
|
this.firstStep=Date.now(); |
|
|
|
this.firstStep=Date.now(); |
|
|
|
|
|
|
|
this.tickId=0; |
|
|
|
this.playing=true; |
|
|
|
this.playing=true; |
|
|
|
requestAnimationFrame(() => this.tick()); |
|
|
|
requestAnimationFrame(() => this.tick()); |
|
|
|
} |
|
|
|
} |
|
|
|