added flammable tiles (closes #1)
This commit is contained in:
parent
a87b4679f4
commit
ea79ba1dfa
5 changed files with 61 additions and 31 deletions
2
Makefile
2
Makefile
|
@ -4,7 +4,7 @@ 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_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)
|
PEACH_RAINBOW_ANIM = $(foreach percent, $(shell seq 100 2 299), build/peach-rainbow$(percent).png)
|
||||||
|
|
||||||
IMAGES = $(foreach name, apple wall, public/assets/$(name)32.png)
|
IMAGES = $(foreach name, apple wall oil, public/assets/$(name)32.png)
|
||||||
TILESETS = $(foreach name, hole, public/assets/$(name)-ts.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, public/assets/$(name)-anim.png)
|
||||||
JSON = $(foreach name, snake levelList config metaConfig, public/assets/$(name).json)
|
JSON = $(foreach name, snake levelList config metaConfig, public/assets/$(name).json)
|
||||||
|
|
BIN
assets/oil.png
Normal file
BIN
assets/oil.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 233 KiB |
|
@ -5,13 +5,13 @@
|
||||||
" iiiiiii i iiiiiii ",
|
" iiiiiii i iiiiiii ",
|
||||||
" i f i ",
|
" i f i ",
|
||||||
" i i ",
|
" i i ",
|
||||||
" i iiiiiiiiiiiii i ",
|
" i IIIiiiiiiiIII i ",
|
||||||
" i i ",
|
" i i ",
|
||||||
" i f 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 ",
|
" I i I ",
|
||||||
" i i i ",
|
" i i i ",
|
||||||
" i i i ",
|
" i i i ",
|
||||||
" i i i ",
|
" i i i ",
|
||||||
|
@ -19,13 +19,13 @@
|
||||||
" i i i ",
|
" i i i ",
|
||||||
" i i i ",
|
" i i i ",
|
||||||
" i i i ",
|
" i i i ",
|
||||||
" i i i ",
|
" 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 f i ",
|
||||||
" i i ",
|
" i i ",
|
||||||
" i iiiiiiiiiiiii i ",
|
" i IIIiiiiiiiIII i ",
|
||||||
" i i ",
|
" i i ",
|
||||||
" i f i ",
|
" i f i ",
|
||||||
" iiiiiii i iiiiiii ",
|
" iiiiiii i iiiiiii ",
|
||||||
|
|
|
@ -5,6 +5,7 @@ const assetSpecs=[
|
||||||
{ name: 'superFruit', filename: 'peach-rainbow-anim.png', type: 'image' },
|
{ name: 'superFruit', filename: 'peach-rainbow-anim.png', type: 'image' },
|
||||||
{ name: 'decayFruit', filename: 'peach-decay-anim.png', type: 'image' },
|
{ name: 'decayFruit', filename: 'peach-decay-anim.png', type: 'image' },
|
||||||
{ name: 'wall', filename: 'wall32.png', type: 'image' },
|
{ name: 'wall', filename: 'wall32.png', type: 'image' },
|
||||||
|
{ name: 'flammable', filename: 'oil32.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: 'fire', filename: 'fire-anim.png', type: 'image' },
|
||||||
{ name: 'snake', filename: 'snake.json', type: 'json' },
|
{ name: 'snake', filename: 'snake.json', type: 'json' },
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const [EMPTY, FOOD, SUPER_FOOD, DECAY_FOOD, WALL, FIRE, HOLE, HOLE_S, SNAKE]=Array(255).keys();
|
const [EMPTY, FOOD, SUPER_FOOD, DECAY_FOOD, WALL, FIRE, FLAMMABLE, FLAMMABLE_S, HOLE, HOLE_S, SNAKE]=Array(255).keys();
|
||||||
|
|
||||||
class SnekGame {
|
class SnekGame {
|
||||||
constructor(settings, canvas, rules) {
|
constructor(settings, canvas, rules) {
|
||||||
|
@ -22,6 +22,7 @@ class SnekGame {
|
||||||
case 'w': return WALL;
|
case 'w': return WALL;
|
||||||
case 'o': return HOLE;
|
case 'o': return HOLE;
|
||||||
case 'i': return FIRE;
|
case 'i': return FIRE;
|
||||||
|
case 'I': return FLAMMABLE;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
@ -65,8 +66,9 @@ class SnekGame {
|
||||||
// add the holes
|
// add the holes
|
||||||
if(settings.holes) settings.holes.forEach(([x, y]) => this.world[x][y]=HOLE);
|
if(settings.holes) settings.holes.forEach(([x, y]) => this.world[x][y]=HOLE);
|
||||||
|
|
||||||
// add the fires
|
// add the fires and flammable tiles
|
||||||
if(settings.fires) settings.fires.forEach(([x, y]) => this.world[x][y]=FIRE);
|
if(settings.fires) settings.fires.forEach(([x, y]) => this.world[x][y]=FIRE);
|
||||||
|
if(settings.flammable) settings.flammable.forEach(([x, y]) => this.world[x][y]=FLAMMABLE);
|
||||||
|
|
||||||
// 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);
|
||||||
|
@ -117,7 +119,7 @@ class SnekGame {
|
||||||
worldWrap: true,
|
worldWrap: true,
|
||||||
winCondition: 'none',
|
winCondition: 'none',
|
||||||
scoreSystem: 'fruit',
|
scoreSystem: 'fruit',
|
||||||
netPlay: false,
|
fireTickSpeed: 10,
|
||||||
autoSizeGrow: false,
|
autoSizeGrow: false,
|
||||||
autoSpeedIncrease: false
|
autoSpeedIncrease: false
|
||||||
}, rules, settings.rules || {});
|
}, rules, settings.rules || {});
|
||||||
|
@ -127,6 +129,19 @@ class SnekGame {
|
||||||
return Date.now()-this.firstStep;
|
return Date.now()-this.firstStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTilesOfType(type) {
|
||||||
|
return this
|
||||||
|
.world
|
||||||
|
.map(
|
||||||
|
(l, x) => l
|
||||||
|
.map(
|
||||||
|
(r, y) => r==type?[x,y]:null
|
||||||
|
).filter(
|
||||||
|
a => a
|
||||||
|
)
|
||||||
|
).flat();
|
||||||
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
const assets=require('assets');
|
const assets=require('assets');
|
||||||
const config=require('config');
|
const config=require('config');
|
||||||
|
@ -170,6 +185,7 @@ class SnekGame {
|
||||||
const wall=assets.get('wall');
|
const wall=assets.get('wall');
|
||||||
const hole=assets.get('hole');
|
const hole=assets.get('hole');
|
||||||
const fire=assets.get('fire');
|
const fire=assets.get('fire');
|
||||||
|
const flammable=assets.get('flammable');
|
||||||
const superFruit=assets.get('superFruit');
|
const superFruit=assets.get('superFruit');
|
||||||
const decayFruit=assets.get('decayFruit');
|
const decayFruit=assets.get('decayFruit');
|
||||||
const putTile=(x, y, tile) => this.ctx.drawImage(
|
const putTile=(x, y, tile) => this.ctx.drawImage(
|
||||||
|
@ -221,6 +237,11 @@ class SnekGame {
|
||||||
// however, the tileset only handles convex shapes
|
// however, the tileset only handles convex shapes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case FLAMMABLE:
|
||||||
|
case FLAMMABLE_S:
|
||||||
|
putTile(x, y, flammable);
|
||||||
|
break;
|
||||||
|
|
||||||
case SUPER_FOOD:
|
case SUPER_FOOD:
|
||||||
putTileAnim(x, y, superFruit);
|
putTileAnim(x, y, superFruit);
|
||||||
break;
|
break;
|
||||||
|
@ -228,12 +249,12 @@ class SnekGame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw our decaying fruits
|
// draw our decaying fruits (they have more information than just XY, so they need to be drawn here
|
||||||
this.decayFood.forEach(([x, y, birth]) =>
|
this.decayFood.forEach(([x, y, birth]) =>
|
||||||
putTileAnimPercent(x, y, decayFruit, (this.playTime-birth)/2000)
|
putTileAnimPercent(x, y, decayFruit, (this.playTime-birth)/2000)
|
||||||
);
|
);
|
||||||
|
|
||||||
// draw our snake
|
// draw our snake (it gets drawn completely differently, so here it goes)
|
||||||
const snake=assets.get('snake');
|
const snake=assets.get('snake');
|
||||||
this.ctx.fillStyle=snake.color;
|
this.ctx.fillStyle=snake.color;
|
||||||
this.ctx.strokeStyle=snake.color;
|
this.ctx.strokeStyle=snake.color;
|
||||||
|
@ -367,6 +388,9 @@ class SnekGame {
|
||||||
case HOLE_S:
|
case HOLE_S:
|
||||||
this.world[tail[0]][tail[1]]=HOLE;
|
this.world[tail[0]][tail[1]]=HOLE;
|
||||||
break;
|
break;
|
||||||
|
case FLAMMABLE_S:
|
||||||
|
this.world[tail[0]][tail[1]]=FLAMMABLE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.world[tail[0]][tail[1]]=EMPTY;
|
this.world[tail[0]][tail[1]]=EMPTY;
|
||||||
}
|
}
|
||||||
|
@ -416,9 +440,8 @@ class SnekGame {
|
||||||
|
|
||||||
// you eat, you grow
|
// you eat, you grow
|
||||||
case FOOD:
|
case FOOD:
|
||||||
// re-grow the snake
|
// re-grow the snake partially (can't hit the tail, but it's there for all other intents and purposes
|
||||||
this.snake.push(tail);
|
this.snake.push(tail);
|
||||||
this.world[tail[0]][tail[1]]=SNAKE;
|
|
||||||
this.length++;
|
this.length++;
|
||||||
|
|
||||||
// remove the fruit from existence
|
// remove the fruit from existence
|
||||||
|
@ -430,21 +453,9 @@ class SnekGame {
|
||||||
// increase score
|
// increase score
|
||||||
this.score++;
|
this.score++;
|
||||||
|
|
||||||
// list empty cells
|
|
||||||
const getEmptyCells=() =>
|
|
||||||
this.world
|
|
||||||
.map(
|
|
||||||
(l, x) => l
|
|
||||||
.map(
|
|
||||||
(r, y) => r==EMPTY?[x,y]:null
|
|
||||||
).filter(
|
|
||||||
a => a
|
|
||||||
)
|
|
||||||
).flat();
|
|
||||||
|
|
||||||
// custom rules
|
// custom rules
|
||||||
if(this.rules.fruitRegrow) {
|
if(this.rules.fruitRegrow) {
|
||||||
const emptyCells=getEmptyCells();
|
const emptyCells=this.getTilesOfType(EMPTY);
|
||||||
|
|
||||||
const cell=emptyCells[Math.floor(Math.random()*emptyCells.length)];
|
const cell=emptyCells[Math.floor(Math.random()*emptyCells.length)];
|
||||||
this.fruits.push(cell);
|
this.fruits.push(cell);
|
||||||
|
@ -453,7 +464,7 @@ class SnekGame {
|
||||||
|
|
||||||
if(this.rules.superFruitGrow) {
|
if(this.rules.superFruitGrow) {
|
||||||
if(Math.random()<.1) { // 10% chance
|
if(Math.random()<.1) { // 10% chance
|
||||||
const emptyCells=getEmptyCells();
|
const emptyCells=this.getTilesOfType(EMPTY);
|
||||||
const cell=emptyCells[Math.floor(Math.random()*emptyCells.length)];
|
const cell=emptyCells[Math.floor(Math.random()*emptyCells.length)];
|
||||||
this.world[cell[0]][cell[1]]=SUPER_FOOD;
|
this.world[cell[0]][cell[1]]=SUPER_FOOD;
|
||||||
}
|
}
|
||||||
|
@ -461,7 +472,7 @@ class SnekGame {
|
||||||
|
|
||||||
if(this.rules.decayingFruitGrow) {
|
if(this.rules.decayingFruitGrow) {
|
||||||
if(Math.random()<.2) { // 20% chance
|
if(Math.random()<.2) { // 20% chance
|
||||||
const emptyCells=getEmptyCells();
|
const emptyCells=this.getTilesOfType(EMPTY);
|
||||||
const cell=emptyCells[Math.floor(Math.random()*emptyCells.length)];
|
const cell=emptyCells[Math.floor(Math.random()*emptyCells.length)];
|
||||||
this.world[cell[0]][cell[1]]=DECAY_FOOD;
|
this.world[cell[0]][cell[1]]=DECAY_FOOD;
|
||||||
this.decayFood.push([cell[0], cell[1], this.playTime]);
|
this.decayFood.push([cell[0], cell[1], this.playTime]);
|
||||||
|
@ -479,6 +490,9 @@ class SnekGame {
|
||||||
case HOLE:
|
case HOLE:
|
||||||
this.world[head[0]][head[1]]=HOLE_S;
|
this.world[head[0]][head[1]]=HOLE_S;
|
||||||
break;
|
break;
|
||||||
|
case FLAMMABLE:
|
||||||
|
this.world[head[0]][head[1]]=FLAMMABLE_S;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.world[head[0]][head[1]]=SNAKE;
|
this.world[head[0]][head[1]]=SNAKE;
|
||||||
}
|
}
|
||||||
|
@ -504,6 +518,21 @@ class SnekGame {
|
||||||
if(this.tickId%this.rules.autoSizeGrowTicks==0) this.snake.push(tail);
|
if(this.tickId%this.rules.autoSizeGrowTicks==0) this.snake.push(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fire tick
|
||||||
|
if(this.tickId%this.rules.fireTickSpeed==0) {
|
||||||
|
const touchingFire=([x, y]) => {
|
||||||
|
const surrounding=[
|
||||||
|
this.world[x][y-1],
|
||||||
|
this.world[x][y+1],
|
||||||
|
(this.world[x-1]||[])[y],
|
||||||
|
(this.world[x+1]||[])[y]
|
||||||
|
];
|
||||||
|
return surrounding.some(tile => tile==FIRE);
|
||||||
|
};
|
||||||
|
if(this.getTilesOfType(FLAMMABLE_S).some(touchingFire)) return this.die();
|
||||||
|
this.getTilesOfType(FLAMMABLE).filter(touchingFire).forEach(([x, y]) => this.world[x][y]=FIRE);
|
||||||
|
}
|
||||||
|
|
||||||
// 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();
|
||||||
|
|
Loading…
Reference in a new issue