refactored game to separate tile management, game code and render code (closes #39)
parent
45e7a0d323
commit
3e8eb69329
@ -0,0 +1,329 @@ |
|||||||
|
const assets=require('assets'); |
||||||
|
const config=require('config'); |
||||||
|
const {tiles: T}=require('tiles'); |
||||||
|
|
||||||
|
// declare our tiles
|
||||||
|
let snake, |
||||||
|
wall, hole, |
||||||
|
fire, flammable, |
||||||
|
fruit, superFruit, decayFruit, |
||||||
|
portalA, portalB, portalC, portalD, |
||||||
|
key, door, |
||||||
|
switchTile, spikes; |
||||||
|
|
||||||
|
// load our tiles
|
||||||
|
assets.onReady(() => { |
||||||
|
wall=assets.get('wall'); |
||||||
|
hole=assets.get('hole'); |
||||||
|
fire=assets.get('fire'); |
||||||
|
flammable=assets.get('flammable'); |
||||||
|
superFruit=assets.get('superFruit'); |
||||||
|
decayFruit=assets.get('decayFruit'); |
||||||
|
portalA=assets.get('portalA'); |
||||||
|
portalB=assets.get('portalB'); |
||||||
|
portalC=assets.get('portalC'); |
||||||
|
portalD=assets.get('portalD'); |
||||||
|
key=assets.get('key'); |
||||||
|
door=assets.get('door'); |
||||||
|
switchTile=assets.get('switch'); |
||||||
|
spikes=assets.get('spikes'); |
||||||
|
snake=assets.get('snake'); |
||||||
|
fruit=assets.get('fruit'); |
||||||
|
}); |
||||||
|
|
||||||
|
const draw=(game, canvas=game.canvas, ctx=canvas.getContext('2d')) => { |
||||||
|
// clear the canvas, because it's easier than having to deal with everything
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||||
|
|
||||||
|
// get the cell size and offset
|
||||||
|
const cellSize=Math.min( |
||||||
|
canvas.width/game.dimensions[0], |
||||||
|
canvas.height/game.dimensions[1] |
||||||
|
); |
||||||
|
const offsetX=(canvas.width-cellSize*game.dimensions[0])/2; |
||||||
|
const offsetY=(canvas.height-cellSize*game.dimensions[1])/2; |
||||||
|
|
||||||
|
// tile draw functions
|
||||||
|
const putTile=(x, y, tile) => ctx.drawImage( |
||||||
|
tile, |
||||||
|
offsetX+cellSize*x, |
||||||
|
offsetY+cellSize*y, |
||||||
|
cellSize, |
||||||
|
cellSize |
||||||
|
); |
||||||
|
const putTileAnim=(x, y, tile) => putTile(x, y, tile[ |
||||||
|
Math.floor(Date.now()/1000*60+x+y)%tile.length |
||||||
|
]); |
||||||
|
const putTileAnimPercent=(x, y, tile, percent) => putTile(x, y, tile[ |
||||||
|
Math.min(Math.round(percent*tile.length), tile.length-1) |
||||||
|
]); |
||||||
|
|
||||||
|
// adjascence check
|
||||||
|
const checkAdj=(x, y) => { |
||||||
|
let adj={}; |
||||||
|
adj.u=game.world[x][y-1]; |
||||||
|
adj.d=game.world[x][y+1]; |
||||||
|
adj.l=(game.world[x-1] || [])[y]; |
||||||
|
adj.r=(game.world[x+1] || [])[y]; |
||||||
|
adj.ul=(game.world[x-1] || [])[y-1]; |
||||||
|
adj.ur=(game.world[x+1] || [])[y-1]; |
||||||
|
adj.dl=(game.world[x-1] || [])[y+1]; |
||||||
|
adj.dr=(game.world[x+1] || [])[y+1]; |
||||||
|
return adj; |
||||||
|
}; |
||||||
|
|
||||||
|
// draw a grid/checkerboard if requested
|
||||||
|
if(config.getS('appearance.grid')=='grid') { |
||||||
|
ctx.strokeStyle='rgba(0, 0, 0, 50%)'; |
||||||
|
ctx.lineCap='square'; |
||||||
|
ctx.lineWidth=1; |
||||||
|
ctx.beginPath(); |
||||||
|
for(let x=1; x<game.dimensions[0]; x++) { |
||||||
|
ctx.moveTo(offsetX+x*cellSize, offsetY); |
||||||
|
ctx.lineTo(offsetX+x*cellSize, canvas.height-offsetY); |
||||||
|
} |
||||||
|
for(let y=1; y<game.dimensions[1]; y++) { |
||||||
|
ctx.moveTo(offsetX, offsetY+y*cellSize); |
||||||
|
ctx.lineTo(canvas.width-offsetX, offsetY+y*cellSize); |
||||||
|
} |
||||||
|
ctx.stroke(); |
||||||
|
} else if(config.getS('appearance.grid')=='checkerboard') { |
||||||
|
ctx.fillStyle='rgba(0, 0, 0, 10%)'; |
||||||
|
for(let x=0; x<game.dimensions[0]; x++) { |
||||||
|
for(let y=(x+1)%2; y<game.dimensions[1]; y+=2) { |
||||||
|
ctx.fillRect(offsetX+x*cellSize, offsetY+y*cellSize, cellSize, cellSize); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// draw our tiles
|
||||||
|
for(let x=0; x<game.dimensions[0]; x++) { |
||||||
|
for(let y=0; y<game.dimensions[1]; y++) { |
||||||
|
switch(game.world[x][y]) { |
||||||
|
case T.WALL: |
||||||
|
putTile(x, y, wall); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.FIRE: |
||||||
|
putTileAnim(x, y, fire); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.HOLE: |
||||||
|
case T.HOLE_S: { |
||||||
|
putTile(x, y, hole.base); |
||||||
|
let adj=checkAdj(x, y); |
||||||
|
Object |
||||||
|
.keys(adj) |
||||||
|
.filter(k => adj[k]==T.HOLE || adj[k]==T.HOLE_S) |
||||||
|
.forEach(k => putTile(x, y, hole[k])); |
||||||
|
} break; |
||||||
|
|
||||||
|
case T.FLAMMABLE: |
||||||
|
case T.FLAMMABLE_S: |
||||||
|
putTile(x, y, flammable); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.SUPER_FOOD: |
||||||
|
putTileAnim(x, y, superFruit); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.PORTAL_A: |
||||||
|
case T.PORTAL_A_S: |
||||||
|
putTileAnim(x, y, portalA); |
||||||
|
break; |
||||||
|
case T.PORTAL_B: |
||||||
|
case T.PORTAL_B_S: |
||||||
|
putTileAnim(x, y, portalB); |
||||||
|
break; |
||||||
|
case T.PORTAL_C: |
||||||
|
case T.PORTAL_C_S: |
||||||
|
putTileAnim(x, y, portalC); |
||||||
|
break; |
||||||
|
case T.PORTAL_D: |
||||||
|
case T.PORTAL_D_S: |
||||||
|
putTileAnim(x, y, portalD); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.KEY: |
||||||
|
putTile(x, y, key); |
||||||
|
break; |
||||||
|
case T.DOOR: |
||||||
|
putTile(x, y, door); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.SWITCH_ON: |
||||||
|
case T.SWITCH_ON_S: |
||||||
|
putTile(x, y, switchTile.on); |
||||||
|
break; |
||||||
|
case T.SWITCH_OFF: |
||||||
|
case T.SWITCH_OFF_S: |
||||||
|
putTile(x, y, switchTile.off); |
||||||
|
break; |
||||||
|
|
||||||
|
case T.SPIKES_ON: |
||||||
|
putTile(x, y, spikes.on); |
||||||
|
break; |
||||||
|
case T.SPIKES_OFF: |
||||||
|
case T.SPIKES_OFF_S: |
||||||
|
putTile(x, y, spikes.off); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// draw our decaying fruits (they have more information than just XY, so they need to be drawn here
|
||||||
|
game.decayFood.forEach(([x, y, birth]) => |
||||||
|
putTileAnimPercent(x, y, decayFruit, (game.playTime-birth)/2000) |
||||||
|
); |
||||||
|
|
||||||
|
// draw the lines between portals
|
||||||
|
if(Object.keys(game.portals).length) { |
||||||
|
ctx.strokeStyle='rgba(128, 128, 128, 20%)'; |
||||||
|
ctx.lineCap='round'; |
||||||
|
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); |
||||||
|
ctx.beginPath(); |
||||||
|
ctx.moveTo( |
||||||
|
offsetX+cellSize*(xa+1/2)+dx, |
||||||
|
offsetY+cellSize*(ya+1/2)+dy |
||||||
|
); |
||||||
|
ctx.lineTo( |
||||||
|
offsetX+cellSize*(xb+1/2)+dx, |
||||||
|
offsetY+cellSize*(yb+1/2)+dy |
||||||
|
); |
||||||
|
ctx.stroke(); |
||||||
|
} |
||||||
|
}; |
||||||
|
if(game.portals.a && game.portals.b) drawTunnel(game.portals.a, game.portals.b); |
||||||
|
if(game.portals.c && game.portals.d) drawTunnel(game.portals.c, game.portals.d); |
||||||
|
} |
||||||
|
|
||||||
|
// draw our snake (it gets drawn completely differently, so here it goes)
|
||||||
|
{ |
||||||
|
ctx.fillStyle=snake.color; |
||||||
|
ctx.strokeStyle=snake.color; |
||||||
|
ctx.lineCap=snake.cap; |
||||||
|
ctx.lineJoin=snake.join; |
||||||
|
ctx.lineWidth=cellSize*snake.tailSize; |
||||||
|
|
||||||
|
ctx.beginPath(); |
||||||
|
ctx.ellipse( |
||||||
|
offsetX+cellSize*(game.snake[0][0]+1/2), |
||||||
|
offsetY+cellSize*(game.snake[0][1]+1/2), |
||||||
|
cellSize/2*snake.headSize, |
||||||
|
cellSize/2*snake.headSize, |
||||||
|
0, |
||||||
|
0, |
||||||
|
Math.PI*2 |
||||||
|
); |
||||||
|
ctx.fill(); |
||||||
|
|
||||||
|
ctx.beginPath(); |
||||||
|
game.snake.forEach(([x, y], i, a) => { |
||||||
|
ctx.lineTo( |
||||||
|
offsetX+cellSize*(x+1/2), |
||||||
|
offsetY+cellSize*(y+1/2) |
||||||
|
); |
||||||
|
if(i!=0 && Math.hypot(x-a[i-1][0], y-a[i-1][1])>1) { |
||||||
|
ctx.lineWidth=cellSize*snake.tailWrapSize; |
||||||
|
} else { |
||||||
|
ctx.lineWidth=cellSize*snake.tailSize; |
||||||
|
} |
||||||
|
ctx.stroke(); |
||||||
|
ctx.beginPath() |
||||||
|
ctx.moveTo( |
||||||
|
offsetX+cellSize*(x+1/2), |
||||||
|
offsetY+cellSize*(y+1/2) |
||||||
|
); |
||||||
|
}); |
||||||
|
ctx.stroke(); |
||||||
|
} |
||||||
|
|
||||||
|
// our fruit has a nice animation to it between .8 and 1.2 scale
|
||||||
|
{ |
||||||
|
const ms=Date.now(); |
||||||
|
const fruitScale=Math.sin(ms/400*Math.PI)*.2+1 |
||||||
|
game.fruits.forEach(([x, y]) => { |
||||||
|
ctx.drawImage( |
||||||
|
fruit, |
||||||
|
offsetX+cellSize*x+(1-fruitScale)*cellSize/2, |
||||||
|
offsetY+cellSize*y+(1-fruitScale)*cellSize/2, |
||||||
|
cellSize*fruitScale, |
||||||
|
cellSize*fruitScale |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// show the timer
|
||||||
|
if(game.rules.winCondition=='time') { |
||||||
|
if(config.getS('appearance.timer')=='border' || config.getS('appearance.timer')=='both') { |
||||||
|
let remaining=(game.rules.gameDuration-game.playTime)/game.rules.gameDuration; |
||||||
|
const w=game.dimensions[0]*cellSize; |
||||||
|
const h=game.dimensions[1]*cellSize; |
||||||
|
const p=w*2+h*2; |
||||||
|
|
||||||
|
const wp=w/p; |
||||||
|
const hp=h/p; |
||||||
|
|
||||||
|
const pdst=(st, ed, frac) => |
||||||
|
(ed-st)*frac+st; |
||||||
|
|
||||||
|
ctx.strokeStyle='#930a16'; |
||||||
|
ctx.lineJoin='miter'; |
||||||
|
ctx.lineCap='round'; |
||||||
|
ctx.lineWidth=5; |
||||||
|
ctx.beginPath(); |
||||||
|
ctx.moveTo(canvas.width/2, offsetY+2); |
||||||
|
|
||||||
|
let sp=Math.min(wp/2, remaining); |
||||||
|
remaining-=sp; |
||||||
|
ctx.lineTo(pdst(canvas.width/2, w+offsetX-2, sp/wp*2), offsetY+2); |
||||||
|
if(remaining) { |
||||||
|
sp=Math.min(hp, remaining); |
||||||
|
remaining-=sp; |
||||||
|
ctx.lineTo(w+offsetX-2, pdst(offsetY+2, offsetY+h-2, sp/hp)); |
||||||
|
} |
||||||
|
if(remaining) { |
||||||
|
sp=Math.min(wp, remaining); |
||||||
|
remaining-=sp; |
||||||
|
ctx.lineTo(pdst(w+offsetX-2, offsetX+2, sp/wp), offsetY+h-2); |
||||||
|
} |
||||||
|
if(remaining) { |
||||||
|
sp=Math.min(hp, remaining); |
||||||
|
remaining-=sp; |
||||||
|
ctx.lineTo(offsetX+2, pdst(offsetY+h-2, offsetY+2, sp/hp)); |
||||||
|
} |
||||||
|
if(remaining) { |
||||||
|
ctx.lineTo(pdst(offsetX+2, canvas.width/2, remaining/wp*2), offsetY+2); |
||||||
|
} |
||||||
|
ctx.stroke(); |
||||||
|
} |
||||||
|
if(config.getS('appearance.timer')=='number' || config.getS('appearance.timer')=='both') { |
||||||
|
let remaining=''+Math.ceil((game.rules.gameDuration-game.playTime)/1000); |
||||||
|
while(remaining.length<(''+game.rules.gameDuration/1000).length) remaining='0'+remaining; |
||||||
|
|
||||||
|
ctx.fillStyle='#930a16'; |
||||||
|
ctx.textAlign='center'; |
||||||
|
ctx.textBaseline='middle'; |
||||||
|
ctx.font='4rem "Fira Code"'; |
||||||
|
ctx.fillText(remaining, canvas.width/2, canvas.height/2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// draw the border around our game area
|
||||||
|
{ |
||||||
|
ctx.fillStyle='black'; |
||||||
|
ctx.fillRect(0, 0, canvas.width, offsetY); |
||||||
|
ctx.fillRect(0, 0, offsetX, canvas.height); |
||||||
|
ctx.fillRect(offsetX+cellSize*game.dimensions[0], 0, offsetX, canvas.height); |
||||||
|
ctx.fillRect(0, offsetY+cellSize*game.dimensions[1], canvas.width, offsetY); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return module.exports={ |
||||||
|
draw |
||||||
|
}; |
@ -0,0 +1,221 @@ |
|||||||
|
const [ |
||||||
|
EMPTY, SNAKE, |
||||||
|
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, |
||||||
|
KEY, DOOR, |
||||||
|
SWITCH_ON, SWITCH_ON_S, SWITCH_OFF, SWITCH_OFF_S, SPIKES_OFF, SPIKES_OFF_S, SPIKES_ON |
||||||
|
]=Array(255).keys(); |
||||||
|
|
||||||
|
const tiles={ |
||||||
|
EMPTY, SNAKE, |
||||||
|
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, |
||||||
|
KEY, DOOR, |
||||||
|
SWITCH_ON, SWITCH_ON_S, SWITCH_OFF, SWITCH_OFF_S, SPIKES_OFF, SPIKES_OFF_S, SPIKES_ON |
||||||
|
}; |
||||||
|
|
||||||
|
const tileNames=(() => { |
||||||
|
let tileNames=[]; |
||||||
|
Object.keys(tiles).forEach(key => tileNames[tiles[key]]=key); |
||||||
|
return tileNames; |
||||||
|
})(); |
||||||
|
|
||||||
|
const getName=t => |
||||||
|
tileNames[t] || `Unknown tile ${t}`; |
||||||
|
|
||||||
|
const forChar=c => { |
||||||
|
switch(c) { |
||||||
|
case ' ': return EMPTY; |
||||||
|
case 'f': return FOOD; |
||||||
|
case 'F': return SUPER_FOOD; |
||||||
|
case 'd': return DECAY_FOOD; |
||||||
|
case 'w': return WALL; |
||||||
|
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; |
||||||
|
case 'k': return KEY; |
||||||
|
case 'K': return DOOR; |
||||||
|
case 's': return SWITCH_OFF; |
||||||
|
case 'S': return SPIKES_ON; |
||||||
|
case 't': return SPIKES_OFF; |
||||||
|
} |
||||||
|
throw TypeError(`'${c}' doesn't correspond to any tile`); |
||||||
|
}; |
||||||
|
|
||||||
|
const charFor=t => { |
||||||
|
switch(t) { |
||||||
|
case EMPTY: return ' '; |
||||||
|
case FOOD: return 'f'; |
||||||
|
case SUPER_FOOD: return 'F'; |
||||||
|
case DECAY_FOOD: return 'd'; |
||||||
|
case WALL: return 'w'; |
||||||
|
case HOLE: return 'o'; |
||||||
|
case FIRE: return 'i'; |
||||||
|
case FLAMMABLE: return 'I'; |
||||||
|
case PORTAL_A: return 'A'; |
||||||
|
case PORTAL_B: return 'B'; |
||||||
|
case PORTAL_C: return 'C'; |
||||||
|
case PORTAL_D: return 'D'; |
||||||
|
case KEY: return 'k'; |
||||||
|
case DOOR: return 'K'; |
||||||
|
case SWITCH_OFF: return 's'; |
||||||
|
case SPIKES_ON: return 'S'; |
||||||
|
case SPIKES_OFF: return 't'; |
||||||
|
} |
||||||
|
throw TypeError(`'${getName(t)}' doesn't have a corresponding character'`); |
||||||
|
}; |
||||||
|
|
||||||
|
const snakeVersion=t => { |
||||||
|
switch(t) { |
||||||
|
case EMPTY: return SNAKE; |
||||||
|
case HOLE: return HOLE_S; |
||||||
|
case FLAMMABLE: return FLAMMABLE_S; |
||||||
|
case PORTAL_A: return PORTAL_A_S; |
||||||
|
case PORTAL_B: return PORTAL_B_S; |
||||||
|
case PORTAL_C: return PORTAL_C_S; |
||||||
|
case PORTAL_D: return PORTAL_D_S; |
||||||
|
case SWITCH_OFF: return SWITCH_OFF_S; |
||||||
|
case SWITCH_ON: return SWITCH_ON_S; |
||||||
|
case SPIKES_OFF: return SPIKES_OFF_S; |
||||||
|
} |
||||||
|
throw TypeError(`'${getName(t)}' doesn't have a snake version'`); |
||||||
|
}; |
||||||
|
|
||||||
|
const nonSnakeVersion=t => { |
||||||
|
switch(t) { |
||||||
|
case SNAKE: return EMPTY; |
||||||
|
case HOLE_S: return HOLE; |
||||||
|
case FLAMMABLE_S: return FLAMMABLE; |
||||||
|
case PORTAL_A_S: return PORTAL_A; |
||||||
|
case PORTAL_B_S: return PORTAL_B; |
||||||
|
case PORTAL_C_S: return PORTAL_C; |
||||||
|
case PORTAL_D_S: return PORTAL_D; |
||||||
|
case SWITCH_OFF_S: return SWITCH_OFF; |
||||||
|
case SWITCH_ON_S: return SWITCH_ON; |
||||||
|
case SPIKES_OFF_S: return SPIKES_OFF; |
||||||
|
} |
||||||
|
throw TypeError(`'${getName(t)}' doesn't have a non-snake version'`); |
||||||
|
}; |
||||||
|
|
||||||
|
const isSnakeVersion=t => { |
||||||
|
switch(t) { |
||||||
|
case SNAKE: |
||||||
|
case HOLE_S: |
||||||
|
case FLAMMABLE_S: |
||||||
|
case PORTAL_A_S: case PORTAL_B_S: case PORTAL_C_S: case PORTAL_D_S: |
||||||
|
case SWITCH_OFF_S: case SWITCH_ON_S: |
||||||
|
case SPIKES_OFF_S: |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
const isNonSnakeVersion=t => { |
||||||
|
switch(t) { |
||||||
|
case EMPTY: |
||||||
|
case HOLE: |
||||||
|
case FLAMMABLE: |
||||||
|
case PORTAL_A: case PORTAL_B: case PORTAL_C: case PORTAL_D: |
||||||
|
case SWITCH_OFF: case SWITCH_ON: |
||||||
|
case SPIKES_OFF: |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
const isSafe=t => { |
||||||
|
switch(t) { |
||||||
|
case EMPTY: |
||||||
|
case HOLE: |
||||||
|
case FLAMMABLE: |
||||||
|
case PORTAL_A: case PORTAL_B: case PORTAL_C: case PORTAL_D: |
||||||
|
case SWITCH_OFF: case SWITCH_ON: |
||||||
|
case SPIKES_OFF: |
||||||
|
case FOOD: case SUPER_FOOD: case DECAY_FOOD: |
||||||
|
case KEY: |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
const isWall=t => { |
||||||
|
switch(t) { |
||||||
|
case WALL: |
||||||
|
case FIRE: |
||||||
|
case DOOR: |
||||||
|
case SPIKES_ON: |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
const getType=t => { |
||||||
|
if(isSnakeVersion(t)) return 'snake'; |
||||||
|
if(isWall(t)) return 'wall'; |
||||||
|
|
||||||
|
switch(t) { |
||||||
|
case EMPTY: |
||||||
|
case FLAMMABLE: |
||||||
|
case SPIKES_OFF: |
||||||
|
return 'empty'; |
||||||
|
|
||||||
|
case FOOD: |
||||||
|
return 'food'; |
||||||
|
|
||||||
|
case SUPER_FOOD: |
||||||
|
case DECAY_FOOD: |
||||||
|
return 'bonus'; |
||||||
|
|
||||||
|
case HOLE: |
||||||
|
return 'hole'; |
||||||
|
|
||||||
|
case PORTAL_A: |
||||||
|
case PORTAL_B: |
||||||
|
case PORTAL_C: |
||||||
|
case PORTAL_D: |
||||||
|
return 'portal'; |
||||||
|
|
||||||
|
case KEY: |
||||||
|
return 'key'; |
||||||
|
|
||||||
|
case SWITCH_ON: |
||||||
|
case SWITCH_OFF: |
||||||
|
return 'switch'; |
||||||
|
} |
||||||
|
|
||||||
|
throw TypeError(`'${getName(t)}' isn't a valid tile`); |
||||||
|
}; |
||||||
|
|
||||||
|
const isPortal=t => { |
||||||
|
switch(t) { |
||||||
|
case PORTAL_A: |
||||||
|
case PORTAL_B: |
||||||
|
case PORTAL_C: |
||||||
|
case PORTAL_D: |
||||||
|
case PORTAL_A_S: |
||||||
|
case PORTAL_B_S: |
||||||
|
case PORTAL_C_S: |
||||||
|
case PORTAL_D_S: |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
return module.exports={ |
||||||
|
tiles, |
||||||
|
tileNames, getName, |
||||||
|
forChar, charFor, |
||||||
|
snakeVersion, nonSnakeVersion, isSnakeVersion, isNonSnakeVersion, |
||||||
|
isSafe, isWall, isPortal, |
||||||
|
getType |
||||||
|
}; |
Loading…
Reference in new issue