diff --git a/Makefile b/Makefile index 392dad0..341a0a3 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,30 @@ .PHONY: all clean -IMAGES = $(foreach name, apple wall, public/assets/$(name)32.png) -TILESETS = $(foreach name, hole, public/assets/$(name)-ts.png) -JSON = $(foreach name, snake levelList config, public/assets/$(name).json) ICON = public/assets/icon32.png public/assets/icon256.png public/favicon.ico +APPLE = public/assets/apple32.png +WALL = public/assets/wall32.png + +SNAKE = public/assets/snake.json +LEVEL_LIST = public/assets/levelList.json +CONFIG = public/assets/config.json + CSS = public/css/snek.css + JS = public/js/snek.js -OUTPUT = $(IMAGES) $(TILESETS) $(JSON) $(ICON) $(CSS) $(JS) +OUTPUT = $(ICON) $(APPLE) $(WALL) $(SNAKE) $(LEVEL_LIST) $(CONFIG) $(CSS) $(JS) -all: images tilesets json icon css js +all: icon apple wall snake levelList config css js +icon: $(ICON) +apple: $(APPLE) +wall: $(WALL) + +snake: $(SNAKE) +levelList: $(LEVEL_LIST) +config: $(CONFIG) -images: $(IMAGES) -tilesets: $(TILESETS) -json: $(JSON) -icon: $(ICON) css: $(CSS) + js: $(JS) public/favicon.ico: assets/icon.jpg @@ -31,9 +40,6 @@ public/assets/%32.png: assets/%.jpg public/assets/%256.png: assets/%.jpg convert $^ -resize 256x $@ -public/assets/%-ts.png: assets/%.png - convert $^ -scale 32x $@ - public/assets/%.json: assets/%.json cp $^ $@ diff --git a/assets/hole.png b/assets/hole.png deleted file mode 100755 index a8278e0..0000000 Binary files a/assets/hole.png and /dev/null differ diff --git a/assets/hole.xcf b/assets/hole.xcf deleted file mode 100755 index 17e3b02..0000000 Binary files a/assets/hole.xcf and /dev/null differ diff --git a/levels/level4.json b/levels/level4.json deleted file mode 100644 index 151751d..0000000 --- a/levels/level4.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "world": [ - " o ", - " o ", - " ooooo o ooooo ", - " f ", - " o o ", - " o ooooooooooooo o ", - " o o ", - " o f o ", - " o o o ", - " o o o ", - " o o o ", - " o o o ", - " o o o ", - " o o o ", - " o o ", - "ooof o foooooo oooooof o fooo", - " o o ", - " o o o ", - " o o o ", - " o o o ", - " o o o ", - " o o o ", - " o o o ", - " o f o ", - " o o ", - " o ooooooooooooo o ", - " o o ", - " f ", - " ooooo o ooooo ", - " o ", - " o " - ], - "delay": 100, - "snake": [ - [16, 4] - ] -} diff --git a/src/js/assets.js b/src/js/assets.js index 8a9dc67..16d15e7 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -3,23 +3,18 @@ const ProgressBar=require('progress'); const assetSpecs=[ { name: 'fruit', filename: 'apple32.png', type: 'image' }, { name: 'wall', filename: 'wall32.png', type: 'image' }, - { name: 'hole', filename: 'hole-ts.png', type: 'image' }, { name: 'snake', filename: 'snake.json', type: 'json' }, { name: 'levelList', filename: 'levelList.json', type: 'json' }, { name: 'config', filename: 'config.json', type: 'json' } ]; -const tasks=[ - { from: 'hole', type: 'tileset', tiles: ['base', 'ul', 'dr', 'dl', 'ur', 'l', 'r', 'd', 'u'], steps: 9 } -]; - const cvs=document.createElement('canvas'); cvs.width=400; cvs.height=50; cvs.classList.add('progressBar'); cvs.classList.add('hiddenBottom'); -const bar=new ProgressBar(assetSpecs.length*2+tasks.reduce((a, t) => a+t.steps, 0)); +const bar=new ProgressBar(assetSpecs.length*2); bar.addUpdateListener(() => bar.draw(cvs)); bar.draw(cvs); @@ -32,11 +27,11 @@ bar.addReadyListener(() => { }); //XXX purposefully slow down asset loading -const sleep=(ms=1000) => new Promise(ok => setTimeout(ok, ms*Math.random())); +const sleep=(ms) => new Promise(ok => setTimeout(ok, ms)); const loadAsset=async (asset) => { const response=await fetch('assets/'+asset.filename); - await sleep(); + await sleep(1000*Math.random()); bar.update(); let result; switch(asset.type) { @@ -48,7 +43,7 @@ const loadAsset=async (asset) => { result=await createImageBitmap(await response.blob()); break; } - await sleep(); + await sleep(1000*Math.random()); bar.update(); return [asset.name, result]; }; @@ -57,37 +52,18 @@ let assets=Object.create(null); let ready=false; let readyListeners=[]; -(async () => { - let arr=await Promise - .all( - assetSpecs.map(a => loadAsset(a)) - ); - - arr.forEach(([name, value]) => { - assets[name]=value; +Promise + .all( + assetSpecs.map(a => loadAsset(a)) + ).then(arr => { + arr.forEach(([name, value]) => { + assets[name]=value; + }); + ready=true; + readyListeners.forEach(fn => fn.bind(fn)()); + readyListeners=null; }); - for(let task of tasks) { - const source=assets[task.from]; - switch(task.type) { - case 'tileset': { - let asset=assets[task.from]=Object.create(null); - for(let tId in task.tiles) { - const tName=task.tiles[tId]; - asset[tName]=await createImageBitmap(source, 0, source.width*tId, source.width, source.width); - await sleep(100); - bar.update(); - } - break; - } - } - } - - ready=true; - readyListeners.forEach(fn => fn.bind(fn)()); - readyListeners=null; -})(); - const onReady=(fn) => { if(ready) fn.bind(fn)(); else readyListeners.push(fn); diff --git a/src/js/snek.js b/src/js/snek.js index fde0d3c..271a33e 100644 --- a/src/js/snek.js +++ b/src/js/snek.js @@ -1,4 +1,6 @@ -const [EMPTY, FOOD, WALL, HOLE, HOLE_S, SNAKE]=Array(6).keys(); +const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys(); + +const ifNaN=(v, r) => isNaN(v)?r:v; class SnekGame { constructor(settings, canvas, rules) { @@ -18,12 +20,11 @@ class SnekGame { case ' ': return EMPTY; case 'f': return FOOD; case 'w': return WALL; - case 'o': return HOLE; } })(); } } - + // // extract the dimensions this.dimensions=[this.world.length, this.world[0].length]; @@ -50,9 +51,6 @@ class SnekGame { // add the walls if(settings.walls) settings.walls.forEach(([x, y]) => this.world[x][y]=WALL); - // add the holes - if(settings.holes) settings.holes.forEach(([x, y]) => this.world[x][y]=HOLE); - // add the food settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); this.fruits=[...settings.food]; @@ -64,13 +62,9 @@ class SnekGame { // get the head and initial direction this.head=[...settings.snake[0]]; - if(settings.snake.length>=2) this.direction=[ - settings.snake[0][0]-settings.snake[1][0], - settings.snake[0][1]-settings.snake[1][1] - ]; - else this.direction=[ - 1, - 0 + this.direction=[ + ifNaN(settings.snake[0][0]-settings.snake[1][0], 1), + ifNaN(settings.snake[0][1]-settings.snake[1][1], 0) ]; this.lastDirection=this.direction @@ -121,46 +115,18 @@ class SnekGame { // draw our walls const wall=assets.get('wall'); - const hole=assets.get('hole'); - - const putTile=(x, y, tile) => this.ctx.drawImage( - tile, - offsetX+cellSize*x, - offsetY+cellSize*y, - cellSize, - cellSize - ); - const checkAdj=(x, y) => { - let adj={}; - adj.u=this.world[x][y-1]; - adj.d=this.world[x][y+1]; - adj.l=(this.world[x-1] || [])[y]; - adj.r=(this.world[x+1] || [])[y]; - adj.ul=(this.world[x-1] || [])[y-1]; - adj.ur=(this.world[x+1] || [])[y-1]; - adj.dl=(this.world[x-1] || [])[y+1]; - adj.dr=(this.world[x+1] || [])[y+1]; - return adj; - }; for(let x=0; x adj[k]==HOLE || adj[k]==HOLE_S) - .forEach(k => putTile(x, y, hole[k])); - break; - // technically, this works for all shapes - // however, the tileset only handles convex shapes - } } } } @@ -232,13 +198,7 @@ class SnekGame { // get our tail out of the way const tail=this.snake.pop(); - switch(this.world[tail[0]][tail[1]]) { - case HOLE_S: - this.world[tail[0]][tail[1]]=HOLE; - break; - default: - this.world[tail[0]][tail[1]]=EMPTY; - } + this.world[tail[0]][tail[1]]=EMPTY; // check for out of world conditions if(head[0]<0 || head[0]>=this.dimensions[0] || head[1]<0 || head[1]>=this.dimensions[1]) { @@ -254,21 +214,8 @@ class SnekGame { // you hit, you die case WALL: case SNAKE: - case HOLE_S: return this.die(); - // if either 3 consecutive segments or the whole snake is on a hole, you die - case HOLE: - if( - this.snake.length==0 || - this.snake.length==1 && - this.world[this.snake[0][0]][this.snake[0][1]]==HOLE_S || - this.snake.length>=2 && - this.world[this.snake[0][0]][this.snake[0][1]]==HOLE_S && - this.world[this.snake[1][0]][this.snake[1][1]]==HOLE_S - ) return this.die(); - break; - // you eat, you don't die case FOOD: // re-grow the snake @@ -304,13 +251,7 @@ class SnekGame { } // move our head forward - switch(this.world[head[0]][head[1]]) { - case HOLE: - this.world[head[0]][head[1]]=HOLE_S; - break; - default: - this.world[head[0]][head[1]]=SNAKE; - } + this.world[head[0]][head[1]]=SNAKE; this.snake.unshift(head); // automatic speed increase @@ -360,7 +301,6 @@ class SnekGame { } handleInputs(inputs) { - // change direction if the input is valid const trySet=(dir) => { if(!dir.every((e, i) => e==this.lastDirection[i] || e==-this.lastDirection[i])) { this.direction=dir; @@ -368,7 +308,6 @@ class SnekGame { } } - // reduce buffer duration Object .keys(inputs) .forEach(k => { @@ -379,13 +318,11 @@ class SnekGame { else inputs[k]=v; }); - // try all inputs in order and unbuffer them if valid 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; - // buffering might be disabled if(inputs.clearBuffer) { Object .keys(inputs)