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/
|
|
@ -1,9 +1,10 @@
|
||||||
const ProgressBar=require('progress');
|
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…
Reference in a new issue