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