diff --git a/.gitignore b/.gitignore index 718b695..f7e3fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ public/assets/*.json public/css/*.css public/js/*.js public/favicon.ico +build/*.png diff --git a/Makefile b/Makefile index 392dad0..106fa27 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,22 @@ .PHONY: all clean +FIRE_ANIM = $(foreach angle, $(shell seq 0 6 359), build/fire$(angle).png) + IMAGES = $(foreach name, apple wall, public/assets/$(name)32.png) TILESETS = $(foreach name, hole, public/assets/$(name)-ts.png) +ANIMATIONS = $(foreach name, fire, public/assets/$(name)-anim.png) JSON = $(foreach name, snake levelList config, public/assets/$(name).json) ICON = public/assets/icon32.png public/assets/icon256.png public/favicon.ico CSS = public/css/snek.css JS = public/js/snek.js -OUTPUT = $(IMAGES) $(TILESETS) $(JSON) $(ICON) $(CSS) $(JS) +OUTPUT = $(IMAGES) $(TILESETS) $(ANIMATIONS) $(JSON) $(ICON) $(CSS) $(JS) -all: images tilesets json icon css js +all: images tilesets animations json icon css js images: $(IMAGES) tilesets: $(TILESETS) +animations: $(ANIMATIONS) json: $(JSON) icon: $(ICON) css: $(CSS) @@ -34,6 +38,12 @@ public/assets/%256.png: assets/%.jpg public/assets/%-ts.png: assets/%.png convert $^ -scale 32x $@ +public/assets/fire-anim.png: $(FIRE_ANIM) + convert $^ -append -scale 32x $@ + +build/fire%.png: assets/fire.png + convert $^ -distort ScaleRotateTranslate $(shell echo $@ | sed 's/[^0-9]*//g') $@ + public/assets/%.json: assets/%.json cp $^ $@ @@ -44,4 +54,5 @@ public/js/snek.js: $(wildcard src/js/*.js) node mergejs.js $^ > $@ clean: + rm -f build/*.* rm -f $(OUTPUT) diff --git a/assets/fire.png b/assets/fire.png new file mode 100755 index 0000000..6a63286 Binary files /dev/null and b/assets/fire.png differ diff --git a/build/.gitkeep b/build/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/levels/level5.json b/levels/level5.json new file mode 100644 index 0000000..476c308 --- /dev/null +++ b/levels/level5.json @@ -0,0 +1,41 @@ +{ + "world": [ + " i ", + " i ", + " iiiiiii i iiiiiii ", + " i f i ", + " i i ", + " i iiiiiiiiiiiii i ", + " i i ", + " i f i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + "iiif i fiiiiiiiiiiiiiiif i fiii", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i i i ", + " i f i ", + " i i ", + " i iiiiiiiiiiiii i ", + " i i ", + " i f i ", + " iiiiiii i iiiiiii ", + " i ", + " i " + ], + "delay": 200, + "snake": [ + [16, 4], + [15, 4], + [14, 4] + ] +} diff --git a/src/js/assets.js b/src/js/assets.js index 8a9dc67..0abe7f6 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -3,14 +3,16 @@ 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: 'hole', filename: 'hole-ts.png', type: 'image' }, + { name: 'fire', filename: 'fire-anim.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 } + { from: 'hole', type: 'tileset', tiles: ['base', 'ul', 'dr', 'dl', 'ur', 'l', 'r', 'd', 'u'], steps: 3 }, + { from: 'fire', type: 'animation', steps: 6 } ]; const cvs=document.createElement('canvas'); @@ -32,7 +34,7 @@ bar.addReadyListener(() => { }); //XXX purposefully slow down asset loading -const sleep=(ms=1000) => new Promise(ok => setTimeout(ok, ms*Math.random())); +const sleep=(ms=500) => new Promise(ok => setTimeout(ok, ms*Math.random())); const loadAsset=async (asset) => { const response=await fetch('assets/'+asset.filename); @@ -75,8 +77,23 @@ let readyListeners=[]; 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(); + if(tId%(task.tiles.length/task.steps)==0) { + await sleep(100); + bar.update(); + } + } + break; + } + + case 'animation': { + let anim=assets[task.from]=[]; + let frameCount=source.height/source.width; + for(let i=0; i this.world[x][y]=HOLE); + // add the fires + if(settings.fires) settings.fires.forEach(([x, y]) => this.world[x][y]=FIRE); + // add the food settings.food.forEach(([x, y]) => this.world[x][y]=FOOD); this.fruits=[...settings.food]; @@ -122,7 +126,7 @@ class SnekGame { // draw our walls const wall=assets.get('wall'); const hole=assets.get('hole'); - + const fire=assets.get('fire'); const putTile=(x, y, tile) => this.ctx.drawImage( tile, offsetX+cellSize*x, @@ -149,6 +153,10 @@ class SnekGame { putTile(x, y, wall); break; + case FIRE: + putTile(x, y, fire[Math.floor(Date.now()/1000*60)%60]); + break; + case HOLE: case HOLE_S: { putTile(x, y, hole.base); @@ -253,6 +261,7 @@ class SnekGame { switch(this.world[head[0]][head[1]]) { // you hit, you die case WALL: + case FIRE: case SNAKE: case HOLE_S: return this.die();