base game working
This commit is contained in:
		
							parent
							
								
									daef55781e
								
							
						
					
					
						commit
						beb9598f69
					
				
					 10 changed files with 270 additions and 30 deletions
				
			
		
							
								
								
									
										20
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
					@ -5,38 +5,40 @@ APPLE = public/assets/apple32.png
 | 
				
			||||||
WALL = public/assets/wall32.png
 | 
					WALL = public/assets/wall32.png
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SNAKE = public/assets/snake.json
 | 
					SNAKE = public/assets/snake.json
 | 
				
			||||||
 | 
					LEVEL_LIST = public/assets/levelList.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CSS = public/css/snek.css
 | 
					CSS = public/css/snek.css
 | 
				
			||||||
 | 
					
 | 
				
			||||||
JS = public/js/snek.js
 | 
					JS = public/js/snek.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OUTPUT = $(ICON) $(APPLE) $(WALL) $(SNAKE) $(CSS) $(JS)
 | 
					OUTPUT = $(ICON) $(APPLE) $(WALL) $(SNAKE) $(LEVEL_LIST) $(CSS) $(JS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
all: icon apple wall snake css js
 | 
					all: icon apple wall snake levelList css js
 | 
				
			||||||
icon: $(ICON) 
 | 
					icon: $(ICON) 
 | 
				
			||||||
apple: $(APPLE)
 | 
					apple: $(APPLE)
 | 
				
			||||||
wall: $(WALL)
 | 
					wall: $(WALL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
snake: $(SNAKE)
 | 
					snake: $(SNAKE)
 | 
				
			||||||
 | 
					levelList: $(LEVEL_LIST)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
css: $(CSS)
 | 
					css: $(CSS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
js: $(JS)
 | 
					js: $(JS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public/assets/icon32.png: assets/icon.jpg
 | 
					 | 
				
			||||||
	convert $^ -resize 32x $@
 | 
					 | 
				
			||||||
public/assets/icon256.png: assets/icon.jpg
 | 
					 | 
				
			||||||
	convert $^ -resize 256x $@
 | 
					 | 
				
			||||||
public/favicon.ico: assets/icon.jpg
 | 
					public/favicon.ico: assets/icon.jpg
 | 
				
			||||||
	convert $^ -resize 32x $@
 | 
						convert $^ -resize 32x $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public/assets/apple32.png: assets/apple.png
 | 
					public/assets/%32.png: assets/%.png
 | 
				
			||||||
	convert $^ -resize 32x $@
 | 
						convert $^ -resize 32x $@
 | 
				
			||||||
 | 
					public/assets/%256.png: assets/%.png
 | 
				
			||||||
 | 
						convert $^ -resize 256x $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public/assets/wall32.png: assets/wall.png
 | 
					public/assets/%32.png: assets/%.jpg
 | 
				
			||||||
	convert $^ -resize 32x $@
 | 
						convert $^ -resize 32x $@
 | 
				
			||||||
 | 
					public/assets/%256.png: assets/%.jpg
 | 
				
			||||||
 | 
						convert $^ -resize 256x $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public/assets/snake.json: assets/snake.json
 | 
					public/assets/%.json: assets/%.json
 | 
				
			||||||
	cp $^ $@
 | 
						cp $^ $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public/css/snek.css: src/less/snek.less $(wildcard src/less/*.less)
 | 
					public/css/snek.css: src/less/snek.less $(wildcard src/less/*.less)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										30
									
								
								assets/levelList.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								assets/levelList.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"speedrun": {
 | 
				
			||||||
 | 
							"rules": {
 | 
				
			||||||
 | 
								"fruitRegrow": false,
 | 
				
			||||||
 | 
								"speedIncrease": false,
 | 
				
			||||||
 | 
								"worldWrap": false,
 | 
				
			||||||
 | 
								"winCondition": "fruit",
 | 
				
			||||||
 | 
								"scoreSystem": "time"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"levelFilename": "level<n>.json",
 | 
				
			||||||
 | 
							"levelDisplay": "Level <n>",
 | 
				
			||||||
 | 
							"levels": [
 | 
				
			||||||
 | 
								1, 2, 3, 4, 5
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"arcade": {
 | 
				
			||||||
 | 
							"rules": {
 | 
				
			||||||
 | 
								"fruitRegrow": true,
 | 
				
			||||||
 | 
								"speedIncrease": true,
 | 
				
			||||||
 | 
								"worldWrap": true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"levelFilename": "arcade-<l>.json",
 | 
				
			||||||
 | 
							"levelDisplay": "<n>",
 | 
				
			||||||
 | 
							"levels": [
 | 
				
			||||||
 | 
								"Arcade",
 | 
				
			||||||
 | 
								"Timed",
 | 
				
			||||||
 | 
								"Survival"
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								levels/level1.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								levels/level1.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "dimensions": [80, 40],
 | 
				
			||||||
 | 
					    "delay": 200,
 | 
				
			||||||
 | 
					    "walls": [
 | 
				
			||||||
 | 
					        [5,5], [5,6], [5,7], [5,8], [70, 35], [71, 35], [72, 35]
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "food": [
 | 
				
			||||||
 | 
					        [10,10]
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "snake": [
 | 
				
			||||||
 | 
					      [60,20],
 | 
				
			||||||
 | 
					      [60,19],
 | 
				
			||||||
 | 
					      [60,18]
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,9 @@
 | 
				
			||||||
		<link rel="favicon" href="favicon.ico">
 | 
							<link rel="favicon" href="favicon.ico">
 | 
				
			||||||
		<link rel="stylesheet" href="css/snek.css">
 | 
							<link rel="stylesheet" href="css/snek.css">
 | 
				
			||||||
		<script src="js/snek.js"></script>
 | 
							<script src="js/snek.js"></script>
 | 
				
			||||||
		<script>window.addEventListener('load', () => require('assets'));</script>
 | 
							<script>
 | 
				
			||||||
 | 
								window.addEventListener('load', () => require('main'));
 | 
				
			||||||
 | 
							</script>
 | 
				
			||||||
	</head>
 | 
						</head>
 | 
				
			||||||
	<body>
 | 
						<body>
 | 
				
			||||||
		<header>
 | 
							<header>
 | 
				
			||||||
| 
						 | 
					@ -15,7 +17,8 @@
 | 
				
			||||||
			<h2>A simple Snake</h2>
 | 
								<h2>A simple Snake</h2>
 | 
				
			||||||
		</header>
 | 
							</header>
 | 
				
			||||||
		<main>
 | 
							<main>
 | 
				
			||||||
			<img src="assets/apple32.png">
 | 
								<nav></nav>
 | 
				
			||||||
 | 
								<canvas class="hidden"></canvas>
 | 
				
			||||||
		</main>
 | 
							</main>
 | 
				
			||||||
		<footer>
 | 
							<footer>
 | 
				
			||||||
			<img src="assets/icon32.png">
 | 
								<img src="assets/icon32.png">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								public/levels
									
										
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								public/levels
									
										
									
									
									
										Symbolic link
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					../levels/
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,8 @@ const ProgressBar=require('progress');
 | 
				
			||||||
const assetSpecs=[
 | 
					const assetSpecs=[
 | 
				
			||||||
	{ name: 'fruit',     filename: 'apple32.png',    type: 'image' },
 | 
						{ name: 'fruit',     filename: 'apple32.png',    type: 'image' },
 | 
				
			||||||
	{ name: 'wall',      filename: 'wall32.png',     type: 'image' },
 | 
						{ name: 'wall',      filename: 'wall32.png',     type: 'image' },
 | 
				
			||||||
	{ name: 'snake', filename: 'snake.json',  type: 'json'  }
 | 
						{ name: 'snake',     filename: 'snake.json',     type: 'json'  },
 | 
				
			||||||
 | 
						{ name: 'levelList', filename: 'levelList.json', type: 'json'  }
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cvs=document.createElement('canvas');
 | 
					const cvs=document.createElement('canvas');
 | 
				
			||||||
| 
						 | 
					@ -64,7 +65,7 @@ Promise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onReady=(fn) => {
 | 
					const onReady=(fn) => {
 | 
				
			||||||
	if(ready) fn.bind(fn)();
 | 
						if(ready) fn.bind(fn)();
 | 
				
			||||||
	else readyListeners.push(ready);
 | 
						else readyListeners.push(fn);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const get=(name) => {
 | 
					const get=(name) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										101
									
								
								src/js/main.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/js/main.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,101 @@
 | 
				
			||||||
 | 
					(async () => {
 | 
				
			||||||
 | 
						location.hash='';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const assets=require('assets');
 | 
				
			||||||
 | 
						await new Promise(ok => assets.onReady(ok));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const main=document.querySelector('main');
 | 
				
			||||||
 | 
						const nav=main.querySelector('nav');
 | 
				
			||||||
 | 
						const canvas=main.querySelector('canvas');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let currentGame=null;
 | 
				
			||||||
 | 
						let currentInputs={};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const resizeCanvas=() => {
 | 
				
			||||||
 | 
							if(document.fullscreenElement) {
 | 
				
			||||||
 | 
								canvas.width=screen.width;
 | 
				
			||||||
 | 
								canvas.height=screen.height;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								canvas.width=main.clientWidth;
 | 
				
			||||||
 | 
								canvas.height=main.clientHeight;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						resizeCanvas();
 | 
				
			||||||
 | 
						window.addEventListener('resize', resizeCanvas);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const levelList=assets.get('levelList');
 | 
				
			||||||
 | 
						Object.keys(levelList).forEach(category => {
 | 
				
			||||||
 | 
							const nav=document.querySelector('nav');
 | 
				
			||||||
 | 
							const section=nav.appendChild(document.createElement('section'));
 | 
				
			||||||
 | 
							const h1=section.appendChild(document.createElement('h1'));
 | 
				
			||||||
 | 
							h1.innerText=category[0].toUpperCase()+category.slice(1)+" Mode";
 | 
				
			||||||
 | 
							const ul=section.appendChild(document.createElement('ul'));
 | 
				
			||||||
 | 
							levelList[category].levels.forEach(level => {
 | 
				
			||||||
 | 
								level=''+level;
 | 
				
			||||||
 | 
								const cat=levelList[category];
 | 
				
			||||||
 | 
								const displayName=cat.levelDisplay
 | 
				
			||||||
 | 
									.replace(/<n>/g, level)
 | 
				
			||||||
 | 
									.replace(/<l>/g, level.toLowerCase());
 | 
				
			||||||
 | 
								const fileName=cat.levelFilename
 | 
				
			||||||
 | 
									.replace(/<n>/g, level)
 | 
				
			||||||
 | 
									.replace(/<l>/g, level.toLowerCase());
 | 
				
			||||||
 | 
								const li=ul.appendChild(document.createElement('li'));
 | 
				
			||||||
 | 
								const a=li.appendChild(document.createElement('a'));
 | 
				
			||||||
 | 
								a.href='#'+category+'/'+fileName;
 | 
				
			||||||
 | 
								a.innerText=displayName;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const handleGamepads=() => {
 | 
				
			||||||
 | 
							const gp=navigator.getGamepads()[0];
 | 
				
			||||||
 | 
							let inputs=currentInputs;
 | 
				
			||||||
 | 
							console.log(gp);
 | 
				
			||||||
 | 
							if(!gp || !gp.axes) return;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							const magnitude=Math.hypot(gp.axes[0], gp.axes[1]);
 | 
				
			||||||
 | 
							const angle=((Math.atan2(gp.axes[0], gp.axes[1])+2*Math.PI)%(2*Math.PI))/Math.PI;
 | 
				
			||||||
 | 
							if(magnitude>.5) {
 | 
				
			||||||
 | 
								if(angle>.25 && angle <.75) inputs.right=true;
 | 
				
			||||||
 | 
								else if(angle>.75 && angle<1.25) inputs.up=true;
 | 
				
			||||||
 | 
								else if(angle>1.25 && angle<1.75) inputs.left=true;
 | 
				
			||||||
 | 
								else inputs.down=true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						window.addEventListener('hashchange', async () => {
 | 
				
			||||||
 | 
							nav.classList.add('hidden');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const [_, category, filename]=location.hash.match(/([a-zA-Z0-9_-]+?)\/(.+)/);
 | 
				
			||||||
 | 
							const rules=levelList[category].rules || {};
 | 
				
			||||||
 | 
							const level=await (async () => {
 | 
				
			||||||
 | 
								const resp=await fetch('levels/'+filename);
 | 
				
			||||||
 | 
								return await resp.json();
 | 
				
			||||||
 | 
							})();
 | 
				
			||||||
 | 
							console.log(rules, level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const SnekGame=require('snek');
 | 
				
			||||||
 | 
							const snek=new SnekGame(level, canvas, rules);
 | 
				
			||||||
 | 
							canvas.classList.remove('hidden');
 | 
				
			||||||
 | 
							snek.start();
 | 
				
			||||||
 | 
							snek.callback=() => {
 | 
				
			||||||
 | 
								if(navigator.getGamepads) handleGamepads();
 | 
				
			||||||
 | 
								snek.handleInputs(currentInputs);
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							currentGame=snek;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						window.addEventListener('keydown', async e => {
 | 
				
			||||||
 | 
							if(e.key=='f' && !canvas.classList.contains('hidden')) {
 | 
				
			||||||
 | 
								if(document.fullscreenElement) await document.exitFullscreen();
 | 
				
			||||||
 | 
								else await canvas.requestFullscreen();
 | 
				
			||||||
 | 
								resizeCanvas();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let inputs=currentInputs;
 | 
				
			||||||
 | 
							if(e.key=='ArrowUp') inputs.up=true;
 | 
				
			||||||
 | 
							else if(e.key=='ArrowDown') inputs.down=true;
 | 
				
			||||||
 | 
							else if(e.key=='ArrowLeft') inputs.left=true;
 | 
				
			||||||
 | 
							else if(e.key=='ArrowRight') inputs.right=true;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
| 
						 | 
					@ -5,11 +5,15 @@ const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys();
 | 
				
			||||||
const ifNaN=(v, r) => isNaN(v)?r:v;
 | 
					const ifNaN=(v, r) => isNaN(v)?r:v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SnekGame {
 | 
					class SnekGame {
 | 
				
			||||||
	constructor(settings, canvas) {
 | 
						constructor(settings, canvas, rules) {
 | 
				
			||||||
		// build the world
 | 
							// build the world
 | 
				
			||||||
		this.dimensions=[...settings.dimensions];
 | 
							this.dimensions=[...settings.dimensions];
 | 
				
			||||||
		this.world=Array(settings.dimensions[0])
 | 
							this.world=Array(settings.dimensions[0]);
 | 
				
			||||||
			.forEach((_, i, a) => a[i]=Array(settings.dimensions[1]).fill(EMPTY));
 | 
							for(let i=0; i<settings.dimensions[0]; i++) {
 | 
				
			||||||
 | 
								this.world[i]=Array(settings.dimensions[1]);
 | 
				
			||||||
 | 
								this.world[i].fill(EMPTY);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							console.log(this);
 | 
				
			||||||
		settings.walls.forEach(([x, y]) => this.world[x][y]=WALL);
 | 
							settings.walls.forEach(([x, y]) => this.world[x][y]=WALL);
 | 
				
			||||||
		settings.food.forEach(([x, y]) => this.world[x][y]=FOOD);
 | 
							settings.food.forEach(([x, y]) => this.world[x][y]=FOOD);
 | 
				
			||||||
		settings.snake.forEach(([x, y]) => this.world[x][y]=SNAKE);
 | 
							settings.snake.forEach(([x, y]) => this.world[x][y]=SNAKE);
 | 
				
			||||||
| 
						 | 
					@ -20,8 +24,8 @@ class SnekGame {
 | 
				
			||||||
		// get the head and initial direction
 | 
							// get the head and initial direction
 | 
				
			||||||
		this.head=[...settings.snake[0]];
 | 
							this.head=[...settings.snake[0]];
 | 
				
			||||||
		this.direction=[
 | 
							this.direction=[
 | 
				
			||||||
			ifNaN(settings.snake[1][0]-settings.snake[0][0], 1),
 | 
								ifNaN(settings.snake[0][0]-settings.snake[1][0], 1),
 | 
				
			||||||
			ifNaN(settings.snake[1][1]-settings.snake[0][1], 0)
 | 
								ifNaN(settings.snake[0][1]-settings.snake[1][1], 0)
 | 
				
			||||||
		];
 | 
							];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// get the snake and the fruits themselves
 | 
							// get the snake and the fruits themselves
 | 
				
			||||||
| 
						 | 
					@ -32,24 +36,40 @@ class SnekGame {
 | 
				
			||||||
		this.canvas=canvas;
 | 
							this.canvas=canvas;
 | 
				
			||||||
		this.ctx=canvas.getContext('2d');
 | 
							this.ctx=canvas.getContext('2d');
 | 
				
			||||||
		//TODO this.gl=canvas.getContext('webgl');
 | 
							//TODO this.gl=canvas.getContext('webgl');
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							// load the custom rules
 | 
				
			||||||
 | 
							this.rules=Object.assign({
 | 
				
			||||||
 | 
								fruitRegrow: true,
 | 
				
			||||||
 | 
								speedIncrease: true,
 | 
				
			||||||
 | 
								worldWrap: true,
 | 
				
			||||||
 | 
								winCondition: 'none',
 | 
				
			||||||
 | 
								scoreSystem: 'fruit'
 | 
				
			||||||
 | 
							}, rules, settings);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	draw() {
 | 
						draw() {
 | 
				
			||||||
		// clear the canvas, because it's easier than having to deal with everything
 | 
							// clear the canvas, because it's easier than having to deal with everything
 | 
				
			||||||
		this.ctx.clearRect(0, 0, this.canvas.with, this.canvas.height);
 | 
							this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// get the cell size and offset
 | 
							// get the cell size and offset
 | 
				
			||||||
		const cellSize=Math.min(
 | 
							const cellSize=Math.min(
 | 
				
			||||||
			this.canvas.with/this.dimensions[0],
 | 
								this.canvas.width/this.dimensions[0],
 | 
				
			||||||
			this.canvas.height/this.dimensions[1]
 | 
								this.canvas.height/this.dimensions[1]
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		const offsetX=(this.canvas.width-cellSize*this.dimensions[0])/2;
 | 
							const offsetX=(this.canvas.width-cellSize*this.dimensions[0])/2;
 | 
				
			||||||
		const offsetY=(this.canvas.height-cellSize*this.dimensions[1])/2;
 | 
							const offsetY=(this.canvas.height-cellSize*this.dimensions[1])/2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// draw the border around our game area
 | 
				
			||||||
 | 
							this.ctx.fillStyle='black';
 | 
				
			||||||
 | 
							this.ctx.fillRect(0, 0, this.canvas.width, offsetY);
 | 
				
			||||||
 | 
							this.ctx.fillRect(0, 0, offsetX, this.canvas.height);
 | 
				
			||||||
 | 
							this.ctx.fillRect(offsetX+cellSize*this.dimensions[0], 0, offsetX, this.canvas.height);
 | 
				
			||||||
 | 
							this.ctx.fillRect(0, offsetY+cellSize*this.dimensions[1], this.canvas.width, offsetY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// draw our walls
 | 
							// draw our walls
 | 
				
			||||||
		const wall=assets.get('wall');
 | 
							const wall=assets.get('wall');
 | 
				
			||||||
		for(let x=0; x<this.dimensions[0]; x++) {
 | 
							for(let x=0; x<this.dimensions[0]; x++) {
 | 
				
			||||||
			for(let y=0; x<this.dimensions[1]; y++) {
 | 
								for(let y=0; y<this.dimensions[1]; y++) {
 | 
				
			||||||
				switch(this.world[x][y]) {
 | 
									switch(this.world[x][y]) {
 | 
				
			||||||
					case WALL:
 | 
										case WALL:
 | 
				
			||||||
						this.ctx.drawImage(
 | 
											this.ctx.drawImage(
 | 
				
			||||||
| 
						 | 
					@ -95,7 +115,7 @@ class SnekGame {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// our fruit has a nice animation to it between .8 and 1.2 scale
 | 
							// our fruit has a nice animation to it between .8 and 1.2 scale
 | 
				
			||||||
		const ms=Date.now();
 | 
							const ms=Date.now();
 | 
				
			||||||
		const fruitScale=Math.sin(ms/1000*Math.PI)*.2+1
 | 
							const fruitScale=Math.sin(ms/400*Math.PI)*.2+1
 | 
				
			||||||
		const fruit=assets.get('fruit');
 | 
							const fruit=assets.get('fruit');
 | 
				
			||||||
		this.fruits.forEach(([x, y]) => {
 | 
							this.fruits.forEach(([x, y]) => {
 | 
				
			||||||
			this.ctx.drawImage(
 | 
								this.ctx.drawImage(
 | 
				
			||||||
| 
						 | 
					@ -119,6 +139,16 @@ class SnekGame {
 | 
				
			||||||
		const tail=this.snake.pop();
 | 
							const tail=this.snake.pop();
 | 
				
			||||||
		this.world[tail[0]][tail[1]]=EMPTY;
 | 
							this.world[tail[0]][tail[1]]=EMPTY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// check for out of world conditions
 | 
				
			||||||
 | 
							if(head[0]<0 || head[0]>=this.dimensions[0] || head[1]<0 || head[1]>=this.dimensions[1]) {
 | 
				
			||||||
 | 
								if(this.rules.worldWrap) {
 | 
				
			||||||
 | 
									head[0]=(head[0]+this.dimensions[0])%this.dimensions[0];
 | 
				
			||||||
 | 
									head[1]=(head[1]+this.dimensions[1])%this.dimensions[1];
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return this.die();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch(this.world[head[0]][head[1]]) {
 | 
							switch(this.world[head[0]][head[1]]) {
 | 
				
			||||||
			// you hit, you die
 | 
								// you hit, you die
 | 
				
			||||||
			case WALL:
 | 
								case WALL:
 | 
				
			||||||
| 
						 | 
					@ -126,7 +156,7 @@ class SnekGame {
 | 
				
			||||||
				return this.die();
 | 
									return this.die();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// you eat, you don't die
 | 
								// you eat, you don't die
 | 
				
			||||||
			case FRUIT:
 | 
								case FOOD:
 | 
				
			||||||
				// re-grow the snake
 | 
									// re-grow the snake
 | 
				
			||||||
				this.snake.push(tail);
 | 
									this.snake.push(tail);
 | 
				
			||||||
				this.world[tail[0]][tail[1]]=SNAKE;
 | 
									this.world[tail[0]][tail[1]]=SNAKE;
 | 
				
			||||||
| 
						 | 
					@ -141,7 +171,7 @@ class SnekGame {
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// custom rules
 | 
									// custom rules
 | 
				
			||||||
				if(this.rules.regrowFruits) {
 | 
									if(this.rules.fruitRegrow) {
 | 
				
			||||||
					const emptyCells=this.world
 | 
										const emptyCells=this.world
 | 
				
			||||||
						.map(
 | 
											.map(
 | 
				
			||||||
							(l, x) => l
 | 
												(l, x) => l
 | 
				
			||||||
| 
						 | 
					@ -153,7 +183,7 @@ class SnekGame {
 | 
				
			||||||
						).flat();
 | 
											).flat();
 | 
				
			||||||
					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);
 | 
				
			||||||
					this.world[cell[0]][cell[1]]=FRUIT;
 | 
										this.world[cell[0]][cell[1]]=FOOD;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,12 +196,14 @@ class SnekGame {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tick() {
 | 
						tick() {
 | 
				
			||||||
 | 
							if(!this.playing) return;
 | 
				
			||||||
		if(!this.lastStep) this.lastStep=this.firstStep;
 | 
							if(!this.lastStep) this.lastStep=this.firstStep;
 | 
				
			||||||
		if(this.lastStep+delay<Date.now()) {
 | 
							this.draw();
 | 
				
			||||||
			this.lastStep+=delay;
 | 
							if(this.callback) this.callback();
 | 
				
			||||||
 | 
							if(this.lastStep+this.delay<Date.now()) {
 | 
				
			||||||
 | 
								this.lastStep+=this.delay;
 | 
				
			||||||
			this.step();
 | 
								this.step();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this.draw();
 | 
					 | 
				
			||||||
		requestAnimationFrame(() => this.tick());
 | 
							requestAnimationFrame(() => this.tick());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,8 +220,26 @@ class SnekGame {
 | 
				
			||||||
		console.log("You bad lol");
 | 
							console.log("You bad lol");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						handleInputs(inputs) {
 | 
				
			||||||
 | 
							const trySet=(dir) => {
 | 
				
			||||||
 | 
								if(!(this.direction[0]==-dir[0] && this.direction[1]==-dir[1])) this.direction=dir;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if(inputs.left) {
 | 
				
			||||||
 | 
								trySet([-1, 0]);
 | 
				
			||||||
 | 
							} else if(inputs.right) {
 | 
				
			||||||
 | 
								trySet([ 1, 0]);
 | 
				
			||||||
 | 
							} else if(inputs.up) {
 | 
				
			||||||
 | 
								trySet([ 0,-1]);
 | 
				
			||||||
 | 
							} else if(inputs.down) {
 | 
				
			||||||
 | 
								trySet([ 0, 1]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Object.keys(inputs).forEach(k => delete inputs[k]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	start() {
 | 
						start() {
 | 
				
			||||||
		this.firstStep=Date.now();
 | 
							this.firstStep=Date.now();
 | 
				
			||||||
 | 
							this.playing=true;
 | 
				
			||||||
		requestAnimationFrame(() => this.tick());
 | 
							requestAnimationFrame(() => this.tick());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								src/less/mainMenu.less
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/less/mainMenu.less
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					nav {
 | 
				
			||||||
 | 
						font-size: 1.6rem;
 | 
				
			||||||
 | 
						display: flex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						section {
 | 
				
			||||||
 | 
							flex: 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ul {
 | 
				
			||||||
 | 
							padding-left: 2rem;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						li {
 | 
				
			||||||
 | 
							list-style-type: disc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,10 @@ h1, h2, h3, h4, h5, h6, strong, a {
 | 
				
			||||||
	font-weight: bold;
 | 
						font-weight: bold;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					h1, h2, h3, h4, h5, h6 {
 | 
				
			||||||
 | 
						margin-bottom: .1em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a {
 | 
					a {
 | 
				
			||||||
	text-decoration: inherit;
 | 
						text-decoration: inherit;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -68,6 +72,15 @@ header, footer, main {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
main {
 | 
					main {
 | 
				
			||||||
	flex: 1;
 | 
						flex: 1;
 | 
				
			||||||
 | 
						position: relative;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						canvas {
 | 
				
			||||||
 | 
							position: absolute;
 | 
				
			||||||
 | 
							top: 0;
 | 
				
			||||||
 | 
							left: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							background: @bg;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
h1 {
 | 
					h1 {
 | 
				
			||||||
| 
						 | 
					@ -80,5 +93,13 @@ p {
 | 
				
			||||||
	font-size: 1.6rem;
 | 
						font-size: 1.6rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hidden {
 | 
				
			||||||
 | 
						display: none !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setup the progress bar
 | 
					// setup the progress bar
 | 
				
			||||||
@import 'progressBar';
 | 
					@import 'progressBar';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setup the main menu
 | 
				
			||||||
 | 
					@import 'mainMenu';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue