From a27b8fcd9e958ab646c84354cad919760520d06f Mon Sep 17 00:00:00 2001 From: Nathan DECHER Date: Tue, 14 Apr 2020 14:32:45 +0200 Subject: [PATCH] added portals (closes #4) and first level of puzzle mode --- Makefile | 31 +++++++- api.js | 2 +- assets/levelList.json | 20 +++++ assets/portal.png | Bin 0 -> 40020 bytes levels/puzzle1.json | 21 ++++++ src/js/assets.js | 10 ++- src/js/snek.js | 172 ++++++++++++++++++++++++++++++++++++------ 7 files changed, 230 insertions(+), 26 deletions(-) create mode 100755 assets/portal.png create mode 100644 levels/puzzle1.json diff --git a/Makefile b/Makefile index b6b42b1..76f3c67 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,14 @@ FIRE_ANIM = $(foreach angle, $(shell seq 0 6 359), build/fire$(angle).png) PEACH_DECAY_ANIM = $(foreach percent, $(shell seq 99 -1 0), build/peach-decay$(percent).png) PEACH_RAINBOW_ANIM = $(foreach percent, $(shell seq 100 2 299), build/peach-rainbow$(percent).png) +PORTAL_A_ANIM = $(foreach angle, $(shell seq 0 6 359), build/portal-a$(angle).png) +PORTAL_B_ANIM = $(foreach angle, $(shell seq 0 6 359), build/portal-b$(angle).png) +PORTAL_C_ANIM = $(foreach angle, $(shell seq 0 6 359), build/portal-c$(angle).png) +PORTAL_D_ANIM = $(foreach angle, $(shell seq 0 6 359), build/portal-d$(angle).png) IMAGES = $(foreach name, apple wall oil, public/assets/$(name)32.png) TILESETS = $(foreach name, hole, public/assets/$(name)-ts.png) -ANIMATIONS = $(foreach name, fire peach-decay peach-rainbow, public/assets/$(name)-anim.png) +ANIMATIONS = $(foreach name, fire peach-decay peach-rainbow portal-a portal-b portal-c portal-d, public/assets/$(name)-anim.png) JSON = $(foreach name, snake levelList config metaConfig, public/assets/$(name).json) ICON = public/assets/icon32.png public/assets/icon256.png public/favicon.ico CSS = public/css/snek.css @@ -58,6 +62,31 @@ public/assets/peach-rainbow-anim.png: $(PEACH_RAINBOW_ANIM) build/peach-rainbow%.png: assets/peach.png convert $^ -modulate 100,100,$(shell echo $@ | sed 's/[^0-9]*//g') -resize 32x $@ +build/portal-b.png: assets/portal.png + convert $^ -modulate 100,100,200 $@ +build/portal-c.png: assets/portal.png + convert $^ -modulate 100,100,150 $@ +build/portal-d.png: assets/portal.png + convert $^ -modulate 100,100,50 $@ + +build/portal-a%.png: assets/portal.png + convert $^ -distort ScaleRotateTranslate $(shell echo $@ | sed 's/[^0-9]*//g') -resize 32x $@ +build/portal-b%.png: build/portal-b.png + convert $^ -distort ScaleRotateTranslate $(shell echo $@ | sed 's/[^0-9]*//g') -resize 32x $@ +build/portal-c%.png: build/portal-c.png + convert $^ -distort ScaleRotateTranslate $(shell echo $@ | sed 's/[^0-9]*//g') -resize 32x $@ +build/portal-d%.png: build/portal-d.png + convert $^ -distort ScaleRotateTranslate $(shell echo $@ | sed 's/[^0-9]*//g') -resize 32x $@ + +public/assets/portal-a-anim.png: $(PORTAL_A_ANIM) + convert $^ -append $@ +public/assets/portal-b-anim.png: $(PORTAL_B_ANIM) + convert $^ -append $@ +public/assets/portal-c-anim.png: $(PORTAL_C_ANIM) + convert $^ -append $@ +public/assets/portal-d-anim.png: $(PORTAL_D_ANIM) + convert $^ -append $@ + public/assets/%.json: assets/%.json cp $^ $@ diff --git a/api.js b/api.js index 9c3a8b2..d837747 100644 --- a/api.js +++ b/api.js @@ -78,7 +78,7 @@ api.post('/leaderboards/:category/:id', (req, res) => { err: 'Invalid time' }); const speed=req.body.speed; - if((typeof speed)!='number' || speed%1 || speed<1) return res.status(400).json({ + if((typeof speed)!='number' || speed%1 || speed<0) return res.status(400).json({ ok: false, err: 'Invalid speed' }); diff --git a/assets/levelList.json b/assets/levelList.json index 1e960a1..1bedb1b 100644 --- a/assets/levelList.json +++ b/assets/levelList.json @@ -41,5 +41,25 @@ "Get a score as high as you can in 30 seconds", "Survive for as long as you can in an increasingly difficult game" ] + }, + "puzzle": { + "desc": "Time doesn't flow in these puzzles. Try getting the fruits in as little moves as possible", + "rules": { + "fruitRegrow": false, + "timeFlow": false, + "speedIncrease": false, + "worldWrap": false, + "winCondition": "fruit", + "scoreSystem": "moves", + "moveCount": 50, + "uploadOnDeath": false, + "leaderboardsSort": "score" + }, + "levelFilename": "puzzle.json", + "levelDisplay": "Level ", + "levels": [ + 1 + ], + "nextLevel": true } } diff --git a/assets/portal.png b/assets/portal.png new file mode 100755 index 0000000000000000000000000000000000000000..75693244ab50280dc310a8a8b02ebf48c32434da GIT binary patch literal 40020 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

t^w!>?eV98ZLB~k~{ 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 DGd=N( literal 0 HcmV?d00001 diff --git a/levels/puzzle1.json b/levels/puzzle1.json new file mode 100644 index 0000000..4f34363 --- /dev/null +++ b/levels/puzzle1.json @@ -0,0 +1,21 @@ +{ + "dimensions": [15, 10], + "walls": [ + [5, 0], [5, 1], [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], + [10, 0], [10, 1], [10, 2], [10, 3], [10, 4], [10, 5], [10, 6], [10, 7], [10, 8], [10, 9] + ], + "food": [ + [4, 5], + [8, 7], + [14, 9] + ], + "snake": [ + [0, 0] + ], + "portals": { + "a": [4, 9], + "b": [6, 0], + "c": [9, 9], + "d": [11, 0] + } +} diff --git a/src/js/assets.js b/src/js/assets.js index f289e4b..028a592 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -8,6 +8,10 @@ const assetSpecs=[ { name: 'flammable', filename: 'oil32.png', type: 'image' }, { name: 'hole', filename: 'hole-ts.png', type: 'image' }, { name: 'fire', filename: 'fire-anim.png', type: 'image' }, + { name: 'portalA', filename: 'portal-a-anim.png', type: 'image' }, + { name: 'portalB', filename: 'portal-b-anim.png', type: 'image' }, + { name: 'portalC', filename: 'portal-c-anim.png', type: 'image' }, + { name: 'portalD', filename: 'portal-d-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' }, @@ -16,7 +20,11 @@ const assetSpecs=[ const tasks=[ { from: 'hole', type: 'tileset', steps: 3, tiles: ['base', 'ul', 'dr', 'dl', 'ur', 'l', 'r', 'd', 'u'] }, - { from: 'fire', type: 'animation', steps: 6 }, + { from: 'fire', type: 'animation', steps: 3 }, + { from: 'portalA', type: 'animation', steps: 3 }, + { from: 'portalB', type: 'animation', steps: 3 }, + { from: 'portalC', type: 'animation', steps: 3 }, + { from: 'portalD', type: 'animation', steps: 3 }, { from: 'superFruit', type: 'animation', steps: 5 }, { from: 'decayFruit', type: 'animation', steps: 5 } ]; diff --git a/src/js/snek.js b/src/js/snek.js index d2c5a8d..1806b51 100644 --- a/src/js/snek.js +++ b/src/js/snek.js @@ -1,9 +1,12 @@ -const [EMPTY, FOOD, SUPER_FOOD, DECAY_FOOD, WALL, FIRE, FLAMMABLE, FLAMMABLE_S, HOLE, HOLE_S, SNAKE]=Array(255).keys(); +const [EMPTY, FOOD, SUPER_FOOD, DECAY_FOOD, WALL, FIRE, FLAMMABLE, FLAMMABLE_S, HOLE, HOLE_S, PORTAL_A, PORTAL_A_S, PORTAL_B, PORTAL_B_S, PORTAL_C, PORTAL_C_S, PORTAL_D, PORTAL_D_S, SNAKE]=Array(255).keys(); class SnekGame { constructor(settings, canvas, rules) { // setup the delay - this.delay=settings.delay; + this.delay=settings.delay || Infinity; + + // score starts at 0 + this.score=0; // world is given in the level if(settings.world) { // explicitly @@ -23,6 +26,10 @@ class SnekGame { case 'o': return HOLE; case 'i': return FIRE; case 'I': return FLAMMABLE; + case 'A': return PORTAL_A; + case 'B': return PORTAL_B; + case 'C': return PORTAL_C; + case 'D': return PORTAL_D; } })(); } @@ -32,22 +39,21 @@ class SnekGame { 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]); - } - )); + this.fruits=this.getTilesOfType(FOOD); // extract the decaying fruits - this.decayFood=[]; - this.world - .forEach((l, x) => l.forEach( - (c, y) => { - if(c==DECAY_FOOD) this.decaying.push([x, y, 0]); - } - )); + this.decayFood=this.getTilesOfType(DECAY_FOOD); + + // extract the portals + this.portals={}; + this.world.forEach((l, x) => + l.forEach((c, y) => { + if(c==PORTAL_A) this.portals.a=[x, y]; + if(c==PORTAL_B) this.portals.b=[x, y]; + if(c==PORTAL_C) this.portals.c=[x, y]; + if(c==PORTAL_D) this.portals.d=[x, y]; + }) + ); } else { // dimension and objects // get the dimensions @@ -84,6 +90,17 @@ class SnekGame { } else { this.decayFood=[]; } + + // add the portals + if(settings.portals) { + if(settings.portals.a) this.world[settings.portals.a[0]][settings.portals.a[1]]=PORTAL_A; + if(settings.portals.b) this.world[settings.portals.b[0]][settings.portals.b[1]]=PORTAL_B; + if(settings.portals.c) this.world[settings.portals.c[0]][settings.portals.c[1]]=PORTAL_C; + if(settings.portals.d) this.world[settings.portals.d[0]][settings.portals.d[1]]=PORTAL_D; + this.portals={...settings.portals}; + } else { + this.portals={}; + } } // add the snake to the world @@ -121,8 +138,20 @@ class SnekGame { scoreSystem: 'fruit', fireTickSpeed: 10, autoSizeGrow: false, - autoSpeedIncrease: false + autoSpeedIncrease: false, + timeFlow: true }, rules, settings.rules || {}); + + // reset direction if time doesn't flow + if(!this.rules.timeFlow) { + this.lastDirection=[0, 0]; + this.direction=[0, 0]; + } + + // set score if move-based + if(this.rules.scoreSystem=='moves') { + this.score=this.rules.moveCount; + } } get playTime() { @@ -192,6 +221,10 @@ class SnekGame { const flammable=assets.get('flammable'); const superFruit=assets.get('superFruit'); const decayFruit=assets.get('decayFruit'); + const portalA=assets.get('portalA'); + const portalB=assets.get('portalB'); + const portalC=assets.get('portalC'); + const portalD=assets.get('portalD'); const putTile=(x, y, tile) => this.ctx.drawImage( tile, offsetX+cellSize*x, @@ -249,6 +282,23 @@ class SnekGame { case SUPER_FOOD: putTileAnim(x, y, superFruit); break; + + case PORTAL_A: + case PORTAL_A_S: + putTileAnim(x, y, portalA); + break; + case PORTAL_B: + case PORTAL_B_S: + putTileAnim(x, y, portalB); + break; + case PORTAL_C: + case PORTAL_C_S: + putTileAnim(x, y, portalC); + break; + case PORTAL_D: + case PORTAL_D_S: + putTileAnim(x, y, portalD); + break; } } } @@ -258,6 +308,32 @@ class SnekGame { putTileAnimPercent(x, y, decayFruit, (this.playTime-birth)/2000) ); + // draw the lines between portals + if(Object.keys(this.portals).length) { + this.ctx.strokeStyle='rgba(128, 128, 128, 20%)'; + this.ctx.lineCap='round'; + this.ctx.lineWidth=cellSize*.15; + const drawTunnel=([xa, ya], [xb, yb]) => { + const angle=(Math.floor(Date.now()/10)%360)*(Math.PI/180); + for(let i=0; i<=1; i++) { + const dx=cellSize/3*Math.cos(angle+i*Math.PI); + const dy=cellSize/3*Math.sin(angle+i*Math.PI); + this.ctx.beginPath(); + this.ctx.moveTo( + offsetX+cellSize*(xa+1/2)+dx, + offsetY+cellSize*(ya+1/2)+dy + ); + this.ctx.lineTo( + offsetX+cellSize*(xb+1/2)+dx, + offsetY+cellSize*(yb+1/2)+dy + ); + this.ctx.stroke(); + } + }; + if(this.portals.a && this.portals.b) drawTunnel(this.portals.a, this.portals.b); + if(this.portals.c && this.portals.d) drawTunnel(this.portals.c, this.portals.d); + } + // draw our snake (it gets drawn completely differently, so here it goes) const snake=assets.get('snake'); this.ctx.fillStyle=snake.color; @@ -381,10 +457,21 @@ class SnekGame { this.lastDirection=this.direction; // compute our new head - const head=[ - this.snake[0][0]+this.direction[0], - this.snake[0][1]+this.direction[1] - ]; + let head; + if(!this.portaled && [PORTAL_A_S, PORTAL_B_S, PORTAL_C_S, PORTAL_D_S].includes(this.world[this.snake[0][0]][this.snake[0][1]])) { + const tile=this.world[this.snake[0][0]][this.snake[0][1]]; + if(tile==PORTAL_A_S) head=this.portals.b; + if(tile==PORTAL_B_S) head=this.portals.a; + if(tile==PORTAL_C_S) head=this.portals.d; + if(tile==PORTAL_D_S) head=this.portals.c; + this.portaled=true; + } else { + head=[ + this.snake[0][0]+this.direction[0], + this.snake[0][1]+this.direction[1] + ]; + this.portaled=false; + } // get our tail out of the way const tail=this.snake.pop(); @@ -395,6 +482,18 @@ class SnekGame { case FLAMMABLE_S: this.world[tail[0]][tail[1]]=FLAMMABLE; break; + case PORTAL_A_S: + this.world[tail[0]][tail[1]]=PORTAL_A; + break; + case PORTAL_B_S: + this.world[tail[0]][tail[1]]=PORTAL_B; + break; + case PORTAL_C_S: + this.world[tail[0]][tail[1]]=PORTAL_C; + break; + case PORTAL_D_S: + this.world[tail[0]][tail[1]]=PORTAL_D; + break; default: this.world[tail[0]][tail[1]]=EMPTY; } @@ -416,6 +515,10 @@ class SnekGame { case SNAKE: case HOLE_S: case FLAMMABLE_S: + case PORTAL_A_S: + case PORTAL_B_S: + case PORTAL_C_S: + case PORTAL_D_S: return this.die("achieved every dog's dream", "ate their own tail"); // if either 3 consecutive segments or the whole snake is on a hole, you die @@ -498,6 +601,18 @@ class SnekGame { case FLAMMABLE: this.world[head[0]][head[1]]=FLAMMABLE_S; break; + case PORTAL_A: + this.world[head[0]][head[1]]=PORTAL_A_S; + break; + case PORTAL_B: + this.world[head[0]][head[1]]=PORTAL_B_S; + break; + case PORTAL_C: + this.world[head[0]][head[1]]=PORTAL_C_S; + break; + case PORTAL_D: + this.world[head[0]][head[1]]=PORTAL_D_S; + break; default: this.world[head[0]][head[1]]=SNAKE; } @@ -538,6 +653,12 @@ class SnekGame { this.getTilesOfType(FLAMMABLE).filter(touchingFire).forEach(([x, y]) => this.world[x][y]=FIRE); } + // THE WORLD! + if(!this.rules.timeFlow) { + this.lastDirection=[0, 0]; + this.direction=[0, 0]; + } + // victory condition if(this.rules.winCondition=='fruit') { if(!this.fruits.length) return this.win(); @@ -548,6 +669,9 @@ class SnekGame { if(this.rules.winCondition=='score') { if(this.score>=this.rules.scoreObjective) return this.win(); } + if(this.rules.scoreSystem=='moves') { + if(this.score) this.score--; + } } tick() { @@ -555,10 +679,13 @@ class SnekGame { if(!this.lastStep) this.lastStep=this.firstStep; this.draw(); if(this.callback) this.callback('tick'); - if(this.lastStep+this.delay this.tick()); } @@ -615,7 +742,6 @@ class SnekGame { this.firstStep=Date.now(); this.tickId=0; this.playing=true; - this.score=0; requestAnimationFrame(() => this.tick()); } }