Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
9f43f63ddf | |||
439f5080de | |||
3465bdad68 | |||
b9a357130b | |||
d2ded2599c | |||
bbe0923523 | |||
c7a5df7923 | |||
10af6cf109 | |||
782f2f6eff | |||
495856ee53 | |||
03fa6c32b0 | |||
c2d9a0a846 | |||
c4baa0ce15 | |||
56ed8b369e | |||
b84c53b111 | |||
8c5ab18f70 | |||
2c28b9205c | |||
7c480c7a2c | |||
64a3dcd0cc | |||
cc78984783 | |||
0c042160ab | |||
825c02532b | |||
9da289ea62 | |||
e93c7fe6f1 | |||
e8cf095cec | |||
401c59d172 | |||
2ee388f7eb | |||
9da6f3990f | |||
eb02467c40 | |||
c52cb48a70 | |||
5e398094f0 | |||
b049787caa | |||
27a30fd0ba | |||
424ce603c1 | |||
1452b28732 | |||
57da262827 | |||
6d735bdfca | |||
8aa3215f5f | |||
8ea643b1bb | |||
612317d752 | |||
0e1f31eaba | |||
17a13eb0c0 | |||
fb755bec7e | |||
aff23993c5 | |||
9b5e993062 | |||
0dc789a168 | |||
f7d6ba99dd | |||
22f4cb851e | |||
7002b5f782 | |||
0cbcfda28b | |||
fae7b38912 | |||
c31cd2b30a | |||
fb3aa86e1d | |||
6db704f2c8 | |||
5bc38b8e65 | |||
924af6745e | |||
cb46be0088 | |||
3a210d1b60 | |||
72633d8c0f | |||
3490dd07ae | |||
e878626d54 | |||
1f096d1ca4 |
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@ -0,0 +1,2 @@
|
||||
projects
|
||||
examples
|
33
.eslintrc.js
Normal file
33
.eslintrc.js
Normal file
@ -0,0 +1,33 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
]
|
||||
}
|
||||
};
|
48
README.md
48
README.md
@ -1,12 +1,48 @@
|
||||
![asdf-games](https://gitea.arnweb.nl/arne/asdf-games/raw/branch/master/res/asdf-logo.png "asdf-games logo")
|
||||
# asdf-games
|
||||
|
||||
Me making games using HTML5 Games: Novice to Ninja by Sitepoint.
|
||||
My attempt at making the framework featured in the book HTML5 Games: Novice to Ninja.
|
||||
[You can find the book here.](https://www.sitepoint.com/premium/books/html5-games-novice-to-ninja)
|
||||
|
||||
The projects here are very similar to those featured in the book, and it will take some time before I get to make my own game.
|
||||
I turned the framework featured in the book into an npm package for use with browserify or Electron.
|
||||
If you are on Github or Gitea now, you can find the npm package [here](https://www.npmjs.com/package/asdf-games).
|
||||
|
||||
## Installation
|
||||
To use asdf framework in your projects, you need to:
|
||||
* Install npm on your device (if you don't have it already)
|
||||
* Run ```npm install asdf-games```
|
||||
* Use the snippet below to start off or check out one of the examples, keep in mind that the examples are not comepletely up to date.
|
||||
* Thanks to my friend [Job](https://jobbel.nl/), this project supports TypeScript typings. Make sure your editor supports them to make your life easier.
|
||||
|
||||
|
||||
## Example usage
|
||||
```javascript
|
||||
// Require asdf
|
||||
const asdf = require("asdf-games");
|
||||
|
||||
// Add whatever classes you need here
|
||||
const [ Game, Sprite, Texture, KeyControls ] = asdf;
|
||||
|
||||
// Game(width, height, disable pixel smoothening)
|
||||
var game = new Game(720, 480, true);
|
||||
|
||||
// Any picture URL used in new Texture() must be relative to the location of the HTML file
|
||||
const playerTexture = new Texture('player.png');
|
||||
|
||||
var player = new Sprite(texture);
|
||||
player.pos = {
|
||||
x: (game.w / 2) - (player.w / 2),
|
||||
y: (game.h / 2) - (player.h / 2)
|
||||
}
|
||||
|
||||
// Add your entities to the game's scene
|
||||
game.scene.add(player);
|
||||
|
||||
game.run(() => {
|
||||
// Game loop
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
To try out asdf library and the projects for yourself, you need to:
|
||||
* TODO
|
||||
|
||||
Releases will be featured on
|
||||
[ARNweb Games](https://arnweb.nl/games/)
|
65
lib/AnimManager.js
Normal file
65
lib/AnimManager.js
Normal file
@ -0,0 +1,65 @@
|
||||
class Anim {
|
||||
constructor(frames, rate) {
|
||||
this.frames = frames;
|
||||
this.rate = rate;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.frame = this.frames[0];
|
||||
this.curFrame = 0;
|
||||
this.curTime = 0;
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
const { rate, frames } = this;
|
||||
if ((this.curTime += dt) > rate) {
|
||||
this.curFrame++;
|
||||
this.frame = frames[this.curFrame % frames.length];
|
||||
this.curTime -= rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnimManager {
|
||||
constructor(e = { x: 0, y: 0 }) {
|
||||
this.anims = {};
|
||||
this.running = false;
|
||||
this.frameSource = e.frame || e;
|
||||
this.current = null;
|
||||
}
|
||||
|
||||
add(name, frames, speed) {
|
||||
this.anims[name] = new Anim(frames, speed);
|
||||
return this.anims[name];
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
const { current, anims, frameSource } = this;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
const anim = anims[current];
|
||||
anim.update(dt);
|
||||
|
||||
// Sync the tileSprite frame
|
||||
frameSource.x = anim.frame.x;
|
||||
frameSource.y = anim.frame.y;
|
||||
}
|
||||
|
||||
play(anim) {
|
||||
const { current, anims } = this;
|
||||
if (anim === current) {
|
||||
return;
|
||||
}
|
||||
this.current = anim;
|
||||
anims[anim].reset();
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AnimManager;
|
||||
|
54
lib/Camera.js
Normal file
54
lib/Camera.js
Normal file
@ -0,0 +1,54 @@
|
||||
const Container = require("./Container");
|
||||
const math = require("./utilities/math");
|
||||
|
||||
class Camera extends Container {
|
||||
constructor(subject, viewport, worldSize = viewport) {
|
||||
super();
|
||||
this.w = viewport.w;
|
||||
this.h = viewport.h;
|
||||
this.worldSize = worldSize;
|
||||
this.setSubject(subject);
|
||||
}
|
||||
|
||||
setSubject(e) {
|
||||
this.subject = e ? e.pos || e : this.pos;
|
||||
this.offset = { x: 0, y: 0 };
|
||||
|
||||
// Center on the entity
|
||||
if (e && e.w) {
|
||||
this.offset.x += e.w / 2;
|
||||
this.offset.y += e.h / 2;
|
||||
}
|
||||
if (e && e.anchor) {
|
||||
this.offset.x -= e.anchor.x;
|
||||
this.offset.y -= e.anchor.y;
|
||||
}
|
||||
this.focus();
|
||||
}
|
||||
|
||||
focus() {
|
||||
const { pos, w, h, worldSize, subject, offset } = this;
|
||||
|
||||
const centeredX = subject.x + offset.x - w / 2;
|
||||
const maxX = worldSize.w - w;
|
||||
const x = -math.clamp(centeredX, 0, maxX);
|
||||
|
||||
const centeredY = subject.y + offset.y - h / 2;
|
||||
const maxY = worldSize.h - h;
|
||||
const y = -math.clamp(centeredY, 0, maxY);
|
||||
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
}
|
||||
|
||||
update(dt, t) {
|
||||
super.update(dt, t);
|
||||
|
||||
if (this.subject) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = Camera;
|
@ -1,30 +1,30 @@
|
||||
class Container {
|
||||
constructor() {
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.children = [];
|
||||
}
|
||||
constructor() {
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
add(child) {
|
||||
this.children.push(child);
|
||||
return child;
|
||||
}
|
||||
add(child) {
|
||||
this.children.push(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
remove(child) {
|
||||
this.children = this.children.filter(c => c !== child);
|
||||
return child;
|
||||
}
|
||||
remove(child) {
|
||||
this.children = this.children.filter(c => c !== child);
|
||||
return child;
|
||||
}
|
||||
|
||||
map(f) {
|
||||
return this.children.map(f);
|
||||
}
|
||||
map(f) {
|
||||
return this.children.map(f);
|
||||
}
|
||||
|
||||
update(dt, t) {
|
||||
this.children = this.children.filter(child => {
|
||||
if (child.update) {
|
||||
child.update(dt, t, this);
|
||||
}
|
||||
return child.dead ? false : true;
|
||||
});
|
||||
}
|
||||
update(dt, t) {
|
||||
this.children = this.children.filter(child => {
|
||||
if (child.update) {
|
||||
child.update(dt, t, this);
|
||||
}
|
||||
return child.dead ? false : true;
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = Container;
|
||||
|
53
lib/Game.js
53
lib/Game.js
@ -1,40 +1,43 @@
|
||||
var Container = require("./Container"),
|
||||
CanvasRenderer = require('./renderer/CanvasRenderer')
|
||||
CanvasRenderer = require("./renderer/CanvasRenderer")
|
||||
;
|
||||
|
||||
const STEP = 1 / 60;
|
||||
const FRAME_MAX = 5 * STEP;
|
||||
|
||||
class Game {
|
||||
constructor(w, h, pixelated, parent = "#board") {
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
this.renderer = new CanvasRenderer(w, h);
|
||||
document.querySelector(parent).appendChild(this.renderer.view);
|
||||
constructor(w, h, pixelated, parent = "#board") {
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
this.renderer = new CanvasRenderer(w, h);
|
||||
document.querySelector(parent).appendChild(this.renderer.view);
|
||||
|
||||
if (pixelated) {
|
||||
this.renderer.setPixelated();
|
||||
}
|
||||
if (pixelated) {
|
||||
this.renderer.setPixelated();
|
||||
}
|
||||
|
||||
this.scene = new Container();
|
||||
}
|
||||
this.scene = new Container();
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
run(gameUpdate = () => { }) {
|
||||
let dt = 0;
|
||||
let last = 0;
|
||||
const loop = ms => {
|
||||
requestAnimationFrame(loop);
|
||||
run(gameUpdate = () => { }) {
|
||||
let dt = 0;
|
||||
let last = 0;
|
||||
const loop = ms => {
|
||||
requestAnimationFrame(loop);
|
||||
|
||||
const t = ms / 1000;
|
||||
dt = Math.min(t - last, FRAME_MAX);
|
||||
last = t;
|
||||
const t = ms / 1000;
|
||||
dt = Math.min(t - last, FRAME_MAX);
|
||||
last = t;
|
||||
|
||||
this.scene.update(dt, t);
|
||||
gameUpdate(dt, t);
|
||||
this.renderer.render(this.scene);
|
||||
};
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
if (!this.paused) {
|
||||
this.scene.update(dt, t);
|
||||
gameUpdate(dt, t);
|
||||
this.renderer.render(this.scene);
|
||||
}
|
||||
};
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Game;
|
9
lib/Line.js
Normal file
9
lib/Line.js
Normal file
@ -0,0 +1,9 @@
|
||||
class Line {
|
||||
constructor(x1, y1, x2, y2, style = "#000000") {
|
||||
this.pos = { x: x1, y: y1 };
|
||||
this.target = { x: x2, y: y2 };
|
||||
this.style = style;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Line;
|
10
lib/Rect.js
Normal file
10
lib/Rect.js
Normal file
@ -0,0 +1,10 @@
|
||||
class Rect {
|
||||
constructor(w, h, style = { fill: "#333" }) {
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
this.style = style;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Rect;
|
@ -1,10 +1,11 @@
|
||||
class Sprite {
|
||||
constructor(texture) {
|
||||
this.texture = texture;
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.anchor = { x: 0, y: 0 };
|
||||
this.scale = { x: 1, y: 1 };
|
||||
this.rotation = 0;
|
||||
}
|
||||
constructor(texture) {
|
||||
this.texture = texture;
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.anchor = { x: 0, y: 0 };
|
||||
this.scale = { x: 1, y: 1 };
|
||||
this.pivot = { x: 0, y: 0 };
|
||||
this.rotation = 0;
|
||||
}
|
||||
}
|
||||
module.exports = Sprite;
|
||||
|
@ -1,39 +1,39 @@
|
||||
class SpriteSheetXML {
|
||||
constructor(url) {
|
||||
this.array = [];
|
||||
this.fetchXMLtoArray(url);
|
||||
}
|
||||
constructor(url) {
|
||||
this.array = [];
|
||||
this.fetchXMLtoArray(url);
|
||||
}
|
||||
|
||||
fetchXMLtoArray(url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, false);
|
||||
xhr.send(null);
|
||||
fetchXMLtoArray(url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", url, false);
|
||||
xhr.send(null);
|
||||
|
||||
if (xhr.status === 200) {
|
||||
var children = xhr.responseXML.children[0].children;
|
||||
for (let index = 0; index < children.length; index++) {
|
||||
const element = children[index];
|
||||
this.array.push({
|
||||
name: element.attributes.name.nodeValue,
|
||||
x: element.attributes.x.nodeValue,
|
||||
y: element.attributes.y.nodeValue,
|
||||
width: element.attributes.width.nodeValue,
|
||||
height: element.attributes.height.nodeValue
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.error('XML file cannot be loaded!')
|
||||
}
|
||||
}
|
||||
if (xhr.status === 200) {
|
||||
var children = xhr.responseXML.children[0].children;
|
||||
for (let index = 0; index < children.length; index++) {
|
||||
const element = children[index];
|
||||
this.array.push({
|
||||
name: element.attributes.name.nodeValue,
|
||||
x: element.attributes.x.nodeValue,
|
||||
y: element.attributes.y.nodeValue,
|
||||
width: element.attributes.width.nodeValue,
|
||||
height: element.attributes.height.nodeValue
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.error("XML file cannot be loaded!");
|
||||
}
|
||||
}
|
||||
|
||||
findIndex(attribute, value) {
|
||||
for (let index = 0; index < this.array.length; index++) {
|
||||
const element = this.array[index];
|
||||
if (element[attribute] == value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
findIndex(attribute, value) {
|
||||
for (let index = 0; index < this.array.length; index++) {
|
||||
const element = this.array[index];
|
||||
if (element[attribute] == value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SpriteSheetXML;
|
29
lib/State.js
Normal file
29
lib/State.js
Normal file
@ -0,0 +1,29 @@
|
||||
class State {
|
||||
constructor(state) {
|
||||
this.set(state);
|
||||
}
|
||||
|
||||
set(state) {
|
||||
this.last = this.state;
|
||||
this.state = state;
|
||||
this.time = 0;
|
||||
this.justSetState = true;
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
this.first = this.justSetState;
|
||||
this.justSetStrate = false;
|
||||
|
||||
this.time += this.first ? 0 : dt;
|
||||
}
|
||||
|
||||
is(state) {
|
||||
return this.state === state;
|
||||
}
|
||||
|
||||
isIn(...states) {
|
||||
return states.some(s => this.is(s));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = State;
|
10
lib/Text.js
10
lib/Text.js
@ -1,8 +1,8 @@
|
||||
class Text {
|
||||
constructor(text = "", style = {}) {
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.text = text;
|
||||
this.style = style;
|
||||
}
|
||||
constructor(text = "", style = {}) {
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.text = text;
|
||||
this.style = style;
|
||||
}
|
||||
}
|
||||
module.exports = Text;
|
||||
|
@ -1,7 +1,7 @@
|
||||
class Texture {
|
||||
constructor(url) {
|
||||
this.img = new Image();
|
||||
this.img.src = url;
|
||||
}
|
||||
constructor(url) {
|
||||
this.img = new Image();
|
||||
this.img.src = url;
|
||||
}
|
||||
}
|
||||
module.exports = Texture;
|
||||
|
@ -1,25 +1,73 @@
|
||||
var Container = require("./Container"),
|
||||
TileSprite = require("./TileSprite")
|
||||
TileSprite = require("./TileSprite")
|
||||
;
|
||||
|
||||
class TileMap extends Container {
|
||||
constructor(tiles, mapW, mapH, tileW, tileH, texture) {
|
||||
super();
|
||||
this.mapW = mapW;
|
||||
this.mapH = mapH;
|
||||
this.tileW = tileW;
|
||||
this.tileH = tileH;
|
||||
this.w = mapW * tileW;
|
||||
this.h = mapH * tileH;
|
||||
constructor(tiles, mapW, mapH, tileW, tileH, texture) {
|
||||
super();
|
||||
this.mapW = mapW;
|
||||
this.mapH = mapH;
|
||||
this.tileW = tileW;
|
||||
this.tileH = tileH;
|
||||
this.w = mapW * tileW;
|
||||
this.h = mapH * tileH;
|
||||
|
||||
this.children = tiles.map((frame, i) => {
|
||||
const s = new TileSprite(texture, tileW, tileH);
|
||||
s.frame = frame;
|
||||
s.pos.x = i % mapW * tileW;
|
||||
s.pos.y = Math.floor(i / mapW) * tileH;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
this.children = tiles.map((frame, i) => {
|
||||
const s = new TileSprite(texture, tileW, tileH);
|
||||
s.frame = frame;
|
||||
s.pos.x = i % mapW * tileW;
|
||||
s.pos.y = Math.floor(i / mapW) * tileH;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
pixelToMapPos(pos) {
|
||||
const { tileW, tileH } = this;
|
||||
return {
|
||||
x: Math.floor(pos.x / tileW),
|
||||
y: Math.floor(pos.y / tileH)
|
||||
};
|
||||
}
|
||||
|
||||
mapToPixelPos(mapPos) {
|
||||
const { tileW, tileH } = this;
|
||||
return {
|
||||
x: mapPos.x * tileW,
|
||||
y: mapPos.y * tileH
|
||||
};
|
||||
}
|
||||
|
||||
tileAtMapPos(mapPos) {
|
||||
return this.children[mapPos.y * this.mapW + mapPos.x];
|
||||
}
|
||||
|
||||
tileAtPixelPos(pos) {
|
||||
return this.tileAtMapPos(this.pixelToMapPos(pos));
|
||||
}
|
||||
|
||||
setFrameAtMapPos(mapPos, frame) {
|
||||
const tile = this.tileAtMapPos(mapPos);
|
||||
tile.frame = frame;
|
||||
return tile;
|
||||
}
|
||||
|
||||
setFrameAtPixelPos(pos, frame) {
|
||||
return this.setFrameAtMapPos(this.pixelToMapPos(pos), frame);
|
||||
}
|
||||
|
||||
tilesAtCorners(bounds, xo = 0, yo = 0) {
|
||||
return [
|
||||
[bounds.x, bounds.y], // Top-left
|
||||
[bounds.x + bounds.w, bounds.y], // Top-right
|
||||
[bounds.x, bounds.y + bounds.h], // Bottom-left
|
||||
[bounds.x + bounds.w, bounds.y + bounds.h] // Bottom-right
|
||||
].map(([x, y]) =>
|
||||
this.tileAtPixelPos({
|
||||
x: x + xo,
|
||||
y: y + yo
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TileMap;
|
@ -1,25 +1,73 @@
|
||||
var Container = require("./Container"),
|
||||
TileSpriteXML = require("./TileSpriteXML")
|
||||
TileSpriteXML = require("./TileSpriteXML")
|
||||
;
|
||||
|
||||
class TileMapXML extends Container {
|
||||
constructor(tiles, mapW, mapH, texture, xml) {
|
||||
super(texture);
|
||||
this.mapW = mapW;
|
||||
this.mapH = mapH;
|
||||
this.tileW = xml.array[tiles[0]].width;
|
||||
this.tileH = xml.array[tiles[0]].height;
|
||||
this.w = mapW * this.tileW;
|
||||
this.h = mapH * this.tileH;
|
||||
constructor(tiles, mapW, mapH, texture, xml) {
|
||||
super(texture);
|
||||
this.mapW = mapW;
|
||||
this.mapH = mapH;
|
||||
this.tileW = xml.array[tiles[0]].width;
|
||||
this.tileH = xml.array[tiles[0]].height;
|
||||
this.w = mapW * this.tileW;
|
||||
this.h = mapH * this.tileH;
|
||||
|
||||
this.children = tiles.map((frame, i) => {
|
||||
const s = new TileSpriteXML(texture, xml, frame);
|
||||
s.frame = frame;
|
||||
s.pos.x = i % mapW * this.tileW;
|
||||
s.pos.y = Math.floor(i / mapW) * this.tileH;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
this.children = tiles.map((frame, i) => {
|
||||
const s = new TileSpriteXML(texture, xml, frame);
|
||||
s.frame = frame;
|
||||
s.pos.x = i % mapW * this.tileW;
|
||||
s.pos.y = Math.floor(i / mapW) * this.tileH;
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
pixelToMapPos(pos) {
|
||||
const { tileW, tileH } = this;
|
||||
return {
|
||||
x: Math.floor(pos.x / tileW),
|
||||
y: Math.floor(pos.y / tileH)
|
||||
};
|
||||
}
|
||||
|
||||
mapToPixelPos(mapPos) {
|
||||
const { tileW, tileH } = this;
|
||||
return {
|
||||
x: mapPos.x * tileW,
|
||||
y: mapPos.y * tileH
|
||||
};
|
||||
}
|
||||
|
||||
tileAtMapPos(mapPos) {
|
||||
return this.children[mapPos.y * this.mapW + mapPos.x];
|
||||
}
|
||||
|
||||
tileAtPixelPos(pos) {
|
||||
return this.tileAtMapPos(this.pixelToMapPos(pos));
|
||||
}
|
||||
|
||||
setFrameAtMapPos(mapPos, frame) {
|
||||
const tile = this.tileAtMapPos(mapPos);
|
||||
tile.frame = frame;
|
||||
return tile;
|
||||
}
|
||||
|
||||
setFrameAtPixelPos(pos, frame) {
|
||||
return this.setFrameAtMapPos(this.pixelToMapPos(pos), frame);
|
||||
}
|
||||
|
||||
tilesAtCorners(bounds, xo = 0, yo = 0) {
|
||||
return [
|
||||
[bounds.x, bounds.y], // Top-left
|
||||
[bounds.x + bounds.w, bounds.y], // Top-right
|
||||
[bounds.x, bounds.y + bounds.h], // Bottom-left
|
||||
[bounds.x + bounds.w, bounds.y + bounds.h] // Bottom-right
|
||||
].map(([x, y]) =>
|
||||
this.tileAtPixelPos({
|
||||
x: x + xo,
|
||||
y: y + yo
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TileMapXML;
|
@ -1,12 +1,26 @@
|
||||
var Sprite = require("./Sprite");
|
||||
var Sprite = require("./Sprite"),
|
||||
AnimManager = require("./AnimManager");
|
||||
|
||||
class TileSprite extends Sprite {
|
||||
constructor(texture, w, h) {
|
||||
super(texture);
|
||||
this.tileW = w;
|
||||
this.tileH = h;
|
||||
this.frame = { x: 0, y: 0 };
|
||||
}
|
||||
constructor(texture, w, h) {
|
||||
super(texture);
|
||||
this.tileW = w;
|
||||
this.tileH = h;
|
||||
this.frame = { x: 0, y: 0 };
|
||||
this.anims = new AnimManager(this);
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
this.anims.update(dt);
|
||||
}
|
||||
|
||||
get w() {
|
||||
return this.tileW * Math.abs(this.scale.x);
|
||||
}
|
||||
|
||||
get h() {
|
||||
return this.tileH * Math.abs(this.scale.y);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TileSprite;
|
@ -1,13 +1,27 @@
|
||||
var Sprite = require("./Sprite");
|
||||
var Sprite = require("./Sprite"),
|
||||
AnimManager = require("./AnimManager");
|
||||
|
||||
class TileSpriteXML extends Sprite {
|
||||
constructor(texture, xml, index) {
|
||||
super(texture);
|
||||
var src = xml.array[index];
|
||||
this.imgPos = { x: src['x'], y: src['y'] };
|
||||
this.width = src['width'];
|
||||
this.height = src['height'];
|
||||
}
|
||||
constructor(texture, xml, index) {
|
||||
super(texture);
|
||||
var src = xml.array[index];
|
||||
this.imgPos = { x: src["x"], y: src["y"] };
|
||||
this.width = src["width"];
|
||||
this.height = src["height"];
|
||||
this.anims = new AnimManager(this);
|
||||
}
|
||||
|
||||
update(dt) {
|
||||
this.anims.update(dt);
|
||||
}
|
||||
|
||||
get w() {
|
||||
return this.width * Math.abs(this.scale.x);
|
||||
}
|
||||
|
||||
get h() {
|
||||
return this.height * Math.abs(this.scale.y);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TileSpriteXML;
|
@ -1,61 +1,78 @@
|
||||
class KeyControls {
|
||||
|
||||
constructor() {
|
||||
this.keys = {};
|
||||
// Bind event handlers
|
||||
document.addEventListener("keydown", e => {
|
||||
if ([37, 38, 39, 40].indexOf(e.which) >= 0) {
|
||||
e.preventDefault();
|
||||
}
|
||||
this.keys[e.which] = true;
|
||||
}, false);
|
||||
document.addEventListener('keyup', e => {
|
||||
this.keys[e.which] = false;
|
||||
}, false);
|
||||
}
|
||||
constructor(listen = true) {
|
||||
this.keys = {};
|
||||
if (listen) {
|
||||
// Bind event handlers
|
||||
document.addEventListener("keydown", e => {
|
||||
if ([37, 38, 39, 40].indexOf(e.which) >= 0) {
|
||||
e.preventDefault();
|
||||
}
|
||||
this.keys[e.which] = true;
|
||||
}, false);
|
||||
document.addEventListener("keyup", e => {
|
||||
this.keys[e.which] = false;
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
get action() {
|
||||
// Spacebar
|
||||
return this.keys[32];
|
||||
}
|
||||
get action() {
|
||||
// Spacebar
|
||||
return this.keys[32];
|
||||
}
|
||||
|
||||
get x() {
|
||||
// Arrow Left or A (WASD)
|
||||
if (this.keys[37] || this.keys[65]) {
|
||||
return -1;
|
||||
}
|
||||
// Arrow Right or D (WASD)
|
||||
if (this.keys[39] || this.keys[68]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
get ctrl() {
|
||||
// Control
|
||||
return this.keys[17];
|
||||
}
|
||||
|
||||
get y() {
|
||||
// Arrow Up or W (WASD)
|
||||
if (this.keys[38] || this.keys[87]) {
|
||||
return -1;
|
||||
}
|
||||
// Arrow Down or S (WASD)
|
||||
if (this.keys[40] || this.keys[83]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
get shift() {
|
||||
// Shift
|
||||
return this.keys[16];
|
||||
}
|
||||
|
||||
get escape() {
|
||||
// Escape
|
||||
return this.keys[27];
|
||||
}
|
||||
|
||||
get x() {
|
||||
// Arrow Left or A (WASD)
|
||||
if (this.keys[37] || this.keys[65]) {
|
||||
return -1;
|
||||
}
|
||||
// Arrow Right or D (WASD)
|
||||
if (this.keys[39] || this.keys[68]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
get y() {
|
||||
// Arrow Up or W (WASD)
|
||||
if (this.keys[38] || this.keys[87]) {
|
||||
return -1;
|
||||
}
|
||||
// Arrow Down or S (WASD)
|
||||
if (this.keys[40] || this.keys[83]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
key(key, value) {
|
||||
if (value !== undefined) {
|
||||
this.keys[key] = value;
|
||||
}
|
||||
return this.keys[key];
|
||||
}
|
||||
key(key, value) {
|
||||
if (value !== undefined) {
|
||||
this.keys[key] = value;
|
||||
}
|
||||
return this.keys[key];
|
||||
}
|
||||
|
||||
reset() {
|
||||
for (let key in this.keys) {
|
||||
this.keys[key] = false;
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
for (let key in this.keys) {
|
||||
this.keys[key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,45 +1,49 @@
|
||||
class MouseControls {
|
||||
constructor(container) {
|
||||
this.el = container || document.body;
|
||||
// State
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.isDown = false;
|
||||
this.pressed = false;
|
||||
this.released = false;
|
||||
// Handlers
|
||||
document.addEventListener('mousemove', this.move.bind(this), false);
|
||||
document.addEventListener('mousedown', this.down.bind(this), false);
|
||||
document.addEventListener('mouseup', this.up.bind(this), false);
|
||||
}
|
||||
constructor(container, listen = true) {
|
||||
this.el = container || document.body;
|
||||
// State
|
||||
this.pos = { x: 0, y: 0 };
|
||||
this.isDown = false;
|
||||
this.pressed = false;
|
||||
this.released = false;
|
||||
|
||||
mousePosFromEvent({ clientX, clientY }) {
|
||||
const { el, pos } = this;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const xr = el.width / el.clientWidth;
|
||||
const yr = el.height / el.clientHeight;
|
||||
pos.x = (clientX - rect.left) * xr;
|
||||
pos.y = (clientY - rect.top) * yr;
|
||||
}
|
||||
if (listen) {
|
||||
// Handlers
|
||||
document.addEventListener("mousemove", this.move.bind(this), false);
|
||||
document.addEventListener("mousedown", this.down.bind(this), false);
|
||||
document.addEventListener("mouseup", this.up.bind(this), false);
|
||||
}
|
||||
|
||||
move(e) {
|
||||
this.mousePosFromEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
down(e) {
|
||||
this.isDown = true;
|
||||
this.pressed = true;
|
||||
this.mousePosFromEvent(e);
|
||||
}
|
||||
mousePosFromEvent({ clientX, clientY }) {
|
||||
const { el, pos } = this;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const xr = el.width / el.clientWidth;
|
||||
const yr = el.height / el.clientHeight;
|
||||
pos.x = (clientX - rect.left) * xr;
|
||||
pos.y = (clientY - rect.top) * yr;
|
||||
}
|
||||
|
||||
up() {
|
||||
this.isDown = false;
|
||||
this.released = true;
|
||||
}
|
||||
move(e) {
|
||||
this.mousePosFromEvent(e);
|
||||
}
|
||||
|
||||
update() {
|
||||
this.released = false;
|
||||
this.pressed = false;
|
||||
}
|
||||
down(e) {
|
||||
this.isDown = true;
|
||||
this.pressed = true;
|
||||
this.mousePosFromEvent(e);
|
||||
}
|
||||
|
||||
up() {
|
||||
this.isDown = false;
|
||||
this.released = true;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.released = false;
|
||||
this.pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MouseControls;
|
||||
|
372
lib/index.d.ts
vendored
372
lib/index.d.ts
vendored
@ -2,6 +2,11 @@ export as namespace asdf;
|
||||
|
||||
type Coordinates = {x: number, y: number};
|
||||
|
||||
class Renderable {
|
||||
visible?: boolean;
|
||||
dead?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* TileSpriteXML class
|
||||
*/
|
||||
@ -87,6 +92,52 @@ export class TileMap extends Container<TileSprite> {
|
||||
* @param texture Texture instance of source image file
|
||||
*/
|
||||
constructor(tiles: Coordinates[], mapW: number, mapH: number, tileW: number, tileH: number, texture: Texture);
|
||||
|
||||
/**
|
||||
* Calculates in which tile a pixel is located.
|
||||
* @param pos The position of the pixel.
|
||||
*/
|
||||
pixelToMapPos(pos: Coordinates): Coordinates
|
||||
|
||||
/**
|
||||
* Calculates the pixel position of a tile in a map.
|
||||
* @param pos The position of the tile in the map.
|
||||
*/
|
||||
mapToPixelPos(pos: Coordinates): Coordinates
|
||||
|
||||
/**
|
||||
* Returns the TileSprite at a given map position.
|
||||
* @param mapPos the tile of a given position.
|
||||
*/
|
||||
tileAtMapPos(mapPos: Coordinates): TileSprite
|
||||
|
||||
/**
|
||||
* Returns the TileSprite at a given pixel position.
|
||||
* @param pos The pixel position of the tile
|
||||
*/
|
||||
tileAtPixelPos(pos: Coordinates): TileSprite
|
||||
|
||||
/**
|
||||
* Changes the frame attribute of the TileSprite at the given mapPos.
|
||||
* @param mapPos the map position of the tile.
|
||||
* @param frame the new value for the frame attribute.
|
||||
*/
|
||||
setFrameAtMapPos(mapPos: Coordinates, frame: Coordinates): TileSprite
|
||||
|
||||
/**
|
||||
* Changes the frame attribute of the TileSprite at the given pixel position.
|
||||
* @param mapPos the pixel position of the tile.
|
||||
* @param frame the new value for the frame attribute.
|
||||
*/
|
||||
setFrameAtPixelPos(pos: Coordinates, frame: Coordinates): TileSprite
|
||||
|
||||
/**
|
||||
* Returns the tiles at the corner of the bounds.
|
||||
* @param bounds a rectangle which defines what the corner are.
|
||||
* @param xo offset to the x values.
|
||||
* @param yo offset to the y values.
|
||||
*/
|
||||
tilesAtCorners(bounds: {x: number, y: number, w: number, h: number}, xo?: number, yo?: number): TileSprite[]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,27 +154,29 @@ export class Texture {
|
||||
constructor(url: string);
|
||||
}
|
||||
|
||||
interface TextStyleOptions {
|
||||
font?: string,
|
||||
fill?: string | CanvasGradient | CanvasPattern,
|
||||
align?: CanvasTextAlign
|
||||
}
|
||||
|
||||
/**
|
||||
* Text class
|
||||
*/
|
||||
export class Text {
|
||||
export class Text extends Renderable {
|
||||
|
||||
pos: Coordinates;
|
||||
text: string;
|
||||
visible: boolean;
|
||||
update?: (dt?: number, t?: number) => void;
|
||||
style: {
|
||||
font?: string,
|
||||
fill?: string | CanvasGradient | CanvasPattern,
|
||||
align?: CanvasTextAlign
|
||||
};
|
||||
style: TextStyleOptions;
|
||||
|
||||
/**
|
||||
* Prints styled text on canvas
|
||||
* @param text Text to print
|
||||
* @param style Styles to apply to text
|
||||
*/
|
||||
constructor(text: string, style: {});
|
||||
constructor(text: string, style: TextStyleOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,15 +224,13 @@ export class SpriteSheetXML {
|
||||
/**
|
||||
* Sprite class
|
||||
*/
|
||||
export class Sprite {
|
||||
export class Sprite extends Renderable {
|
||||
texture: Texture;
|
||||
pos: Coordinates;
|
||||
anchor: Coordinates;
|
||||
scale: Coordinates;
|
||||
pivot: Coordinates;
|
||||
visible: boolean;
|
||||
rotation: number;
|
||||
dead: boolean;
|
||||
|
||||
update?: (dt?: number, t?: number) => void;
|
||||
|
||||
@ -198,7 +249,8 @@ export class Game {
|
||||
h: number;
|
||||
renderer: CanvasRenderer;
|
||||
scene: Container<unknown>;
|
||||
|
||||
paused: boolean;
|
||||
|
||||
/**
|
||||
* Set the games parameters
|
||||
* @param w Width of canvas
|
||||
@ -212,14 +264,14 @@ export class Game {
|
||||
* Start game loop
|
||||
* @param gameUpdate Function to run next to scene updates such as debug logging, etc.
|
||||
*/
|
||||
run(gameUpdate: (dt?: number, t?: number) => void): void;
|
||||
run(gameUpdate?: (dt?: number, t?: number) => void): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container class
|
||||
*/
|
||||
export class Container<T> {
|
||||
pos: {x: number, y: number};
|
||||
pos: Coordinates;
|
||||
children: T[];
|
||||
|
||||
constructor();
|
||||
@ -249,9 +301,9 @@ export class Container<T> {
|
||||
* Updates all children when called
|
||||
* @param dt Delta time
|
||||
* @param t Total time
|
||||
* @returns Returns if the child is dead or not
|
||||
* @returns Returns anything
|
||||
*/
|
||||
update(dt: number, t: number): boolean;
|
||||
update(dt: number, t: number): any;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -321,6 +373,24 @@ export class KeyControls {
|
||||
*/
|
||||
get action(): boolean;
|
||||
|
||||
/**
|
||||
* Returns value of ctrl key.
|
||||
* @returns Key value
|
||||
*/
|
||||
get ctrl(): boolean;
|
||||
|
||||
/**
|
||||
* Returns value of shift key.
|
||||
* @returns Key value
|
||||
*/
|
||||
get shift(): boolean;
|
||||
|
||||
/**
|
||||
* Returns value of escape key.
|
||||
* @returns Key value
|
||||
*/
|
||||
get escape(): boolean;
|
||||
|
||||
/**
|
||||
* Returns -1 on Arrow Left or A
|
||||
*
|
||||
@ -343,10 +413,10 @@ export class KeyControls {
|
||||
* @param value Value to set to key
|
||||
* @return Value of key
|
||||
*/
|
||||
key(key: number, value: boolean): boolean;
|
||||
key(key: number, value?: boolean): boolean;
|
||||
|
||||
/**
|
||||
* Resets default value to all keys
|
||||
* Resets default value (false) to all keys
|
||||
*/
|
||||
reset(): void;
|
||||
}
|
||||
@ -381,7 +451,275 @@ export class CanvasRenderer {
|
||||
render(container: Container<unknown>, clear?: boolean): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Anim represents a single animation.
|
||||
*/
|
||||
declare class Anim {
|
||||
|
||||
curTime: number;
|
||||
frames: Coordinates[];
|
||||
rate: number;
|
||||
curFrame: number;
|
||||
frame: Coordinates;
|
||||
|
||||
/**
|
||||
* Constructor for an animation.
|
||||
* @param frames A collection of coordinates for each frame in the texture file.
|
||||
* @param rate The rate at which the animation plays.
|
||||
*/
|
||||
constructor(frames: Coordinates[], rate: number);
|
||||
|
||||
/**
|
||||
* Resets the animation to the first frame.
|
||||
*/
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* Causes the animation to update based on dt.
|
||||
* @param dt Delta time
|
||||
*/
|
||||
update(dt: number): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The AnimManager class
|
||||
*/
|
||||
export class AnimManager {
|
||||
|
||||
anims: {
|
||||
[name: string]: Anim
|
||||
}
|
||||
running: boolean;
|
||||
frameSource: Coordinates;
|
||||
current: string | null;
|
||||
|
||||
constructor(e: Coordinates);
|
||||
|
||||
/**
|
||||
* Adds an animation to the AnimManager
|
||||
* @param name The name for the animation
|
||||
* @param frames Where each frame is located in the texture
|
||||
* @param speed The speed at which the animation plays.
|
||||
*/
|
||||
add(name: string, frames: Coordinates[], speed: number): Anim;
|
||||
|
||||
/**
|
||||
* Updates the current animation.
|
||||
* @param dt delta time
|
||||
*/
|
||||
update(dt: number): void;
|
||||
|
||||
/**
|
||||
* Starts playing an animation.
|
||||
* @param anim The name of the animation
|
||||
*/
|
||||
play(anim: string): void;
|
||||
|
||||
/**
|
||||
* Stops playing any animation
|
||||
*/
|
||||
stop(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Rect class
|
||||
*/
|
||||
export class Rect {
|
||||
|
||||
pos: Coordinates
|
||||
w: number;
|
||||
h: number;
|
||||
style: {
|
||||
fill: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a rectangle.
|
||||
* @param w The width of the rectangle
|
||||
* @param h The height of the rectangle
|
||||
* @param style The styles of the rectangle
|
||||
*/
|
||||
constructor(w: number, h: number, style: { fill: string })
|
||||
}
|
||||
|
||||
export class Camera extends Container<unknown> {
|
||||
|
||||
w: number;
|
||||
h: number;
|
||||
worldSize: {w: number, h: number};
|
||||
offset: Coordinates;
|
||||
subject: Coordinates;
|
||||
|
||||
/**
|
||||
* Constructs a camera object. This can be fed into the main scene: `scene.add(camera);`
|
||||
* @param subject The entity on which should be focused
|
||||
* @param viewport the size of the viewport - how much should be visible.
|
||||
* @param worldSize the size of the whole map.
|
||||
*/
|
||||
constructor(subject: NumericalEntity | Coordinates, viewport: {w: number, h: number}, worldSize: {w: number, h: number})
|
||||
|
||||
/**
|
||||
* Sets the subject of the camera.
|
||||
* @param e The entity that needs to be set as subject.
|
||||
*/
|
||||
setSubject(e: NumericalEntity | Coordinates): void;
|
||||
|
||||
/**
|
||||
* Moves the camera to the subject
|
||||
*/
|
||||
focus(): void;
|
||||
|
||||
/**
|
||||
* update function for the container.
|
||||
* @param t the elapsed time
|
||||
* @param dt delta time
|
||||
*/
|
||||
update(t: number, dt: number): void;
|
||||
}
|
||||
|
||||
interface SoundOptions {
|
||||
loop?: boolean,
|
||||
volume?: number
|
||||
}
|
||||
|
||||
export class Sound {
|
||||
|
||||
src: string;
|
||||
options: SoundOptions;
|
||||
|
||||
/**
|
||||
* Initiates HTML5 audio element for source audio file with control methods
|
||||
* @param src Source audio file
|
||||
* @param options Play settings
|
||||
*/
|
||||
constructor(src: string, options?: SoundOptions);
|
||||
|
||||
/**
|
||||
* Starts playing the audio file
|
||||
* @param overrides sets options for playing the sound using different setting as defined in `constructor()`
|
||||
*/
|
||||
play(overrides?: SoundOptions): void;
|
||||
|
||||
/**
|
||||
* Stops playing the audio file
|
||||
*/
|
||||
stop(): void;
|
||||
}
|
||||
|
||||
interface NumericalEntityBase {pos: Coordinates}
|
||||
interface NumericalSprite {w: number, h: number}
|
||||
interface NumericalTileSprite {tileW: number, tileH: number}
|
||||
|
||||
type NumericalEntity = NumericalSprite | NumericalTileSprite
|
||||
|
||||
type NumericalEntityWithHitbox = {hitBox: NumericalEntity} & NumericalEntity
|
||||
|
||||
export namespace deadInTracks {
|
||||
/**
|
||||
* This functions checks whether ent walks against a non-walkable object and whether it should move in the x and y position and how much.
|
||||
* The difference with wallslide is that deadInTracks stops the entity entirely when it touches a non-walkable surface.
|
||||
* wallslide will move the entity in x or y if possible.
|
||||
* @param ent The entity that is moving.
|
||||
* @param map The TileMap the entity moves on.
|
||||
* @param x The maximal movement on the x. default is 0
|
||||
* @param y The maximal movement on the y. default is 0
|
||||
* @returns Coordinates of how much the entity walks in x and y.
|
||||
*/
|
||||
export function deadInTracks(ent: NumericalEntity | NumericalEntityWithHitbox, map: TileMap, x?: number, y?: number): Coordinates;
|
||||
}
|
||||
|
||||
export namespace wallslide {
|
||||
/**
|
||||
* This functions checks whether ent walks against a non-walkable object and whether it should move in the x and y position and how much.
|
||||
* The difference with wallslide is that deadInTracks stops the entity entirely when it touches a non-walkable surface.
|
||||
* wallslide will move the entity in x or y if possible.
|
||||
* @param ent The entity that is moving.
|
||||
* @param map The TileMap the entity moves on.
|
||||
* @param x The maximal movement on the x. default is 0
|
||||
* @param y The maximal movement on the y. default is 0
|
||||
* @returns Coordinates of how much the entity walks in x and y.
|
||||
*/
|
||||
export function wallslide(ent: NumericalEntity | NumericalEntityWithHitbox, map: TileMap, x?: number, y?: number): Coordinates;
|
||||
}
|
||||
|
||||
export namespace entity {
|
||||
|
||||
/**
|
||||
* addDebug adds a red border around the hitboxes of an entity.
|
||||
* @param e The entity.
|
||||
*/
|
||||
export function addDebug<T extends Container<unknown> | NumericalEntityWithHitbox>(e: T): T;
|
||||
|
||||
/**
|
||||
* This function checks if an entity hits anything in a container.
|
||||
* @param entity The entity.
|
||||
* @param container The container.
|
||||
* @param hitCallback The callback that is executed when an entity hits something in the container.
|
||||
*/
|
||||
export function hits<T extends NumericalEntityWithHitbox | NumericalEntity>(entity: NumericalEntityWithHitbox | NumericalEntity, container: Container<T>, hitCallback: (e2: T) => any): void;
|
||||
|
||||
/**
|
||||
* This functions calculates whether two entities hit each other.
|
||||
* @param e1 The first entity.
|
||||
* @param e2 The second entity.
|
||||
*/
|
||||
export function hit(e1: NumericalEntityWithHitbox | NumericalEntity, e2: NumericalEntityWithHitbox | NumericalEntity): boolean;
|
||||
|
||||
/**
|
||||
* This function calculates the angle relative to the x-axis between the centers of two entities.
|
||||
* @param a The first entity.
|
||||
* @param b The second entity.
|
||||
* @returns the angle in radians.
|
||||
*/
|
||||
export function angle(a: NumericalEntity, b: NumericalEntity): number;
|
||||
|
||||
/**
|
||||
* This function calculates the full hitbox of an entity.
|
||||
* @param entity The enitity
|
||||
*/
|
||||
export function bounds(entity: NumericalEntityWithHitbox | NumericalEntity): {x: number, y: number, w: number, h: number};
|
||||
|
||||
/**
|
||||
* This function calculates the distance between the centers of two entities.
|
||||
* @param a The first entity.
|
||||
* @param b The second entity.
|
||||
*/
|
||||
export function distance(a: NumericalEntity, b: NumericalEntity): number;
|
||||
|
||||
/**
|
||||
* This function calculates the center of an entity.
|
||||
* @param entity The entity to calculate the center of.
|
||||
*/
|
||||
export function center(entity: NumericalEntity): number;
|
||||
|
||||
}
|
||||
|
||||
export namespace math {
|
||||
|
||||
/**
|
||||
* This function calculates the angle relative from the x-axis between two points.
|
||||
* @param a The first point.
|
||||
* @param b The second point.
|
||||
* @returns The angle in radians.
|
||||
*/
|
||||
export function angle(a: Coordinates, b: Coordinates): number;
|
||||
|
||||
/**
|
||||
* This function calculates if x is between min and max.
|
||||
* @param x A numerical value
|
||||
* @param min A numerical value
|
||||
* @param max A numerical value
|
||||
* @returns x if x is between min and max.
|
||||
*/
|
||||
export function clamp(x: number, min: number, max: number): number;
|
||||
|
||||
/**
|
||||
* Calculates the distance between two points
|
||||
* @param a The first point.
|
||||
* @param b The second point.
|
||||
*/
|
||||
export function distance(a: Coordinates, b: Coordinates): number;
|
||||
|
||||
/**
|
||||
* Returns random integer between min and max
|
||||
* @param min Minimum value
|
||||
|
74
lib/index.js
74
lib/index.js
@ -1,32 +1,50 @@
|
||||
var Container = require("./Container.js"),
|
||||
CanvasRenderer = require("./renderer/CanvasRenderer.js"),
|
||||
Game = require("./Game.js"),
|
||||
math = require("./utilities/math.js"),
|
||||
KeyControls = require("./controls/KeyControls.js"),
|
||||
MouseControls = require("./controls/MouseControls.js"),
|
||||
Sprite = require("./Sprite.js"),
|
||||
TileMap = require("./TileMap.js"),
|
||||
TileMapXML = require("./TileMapXML.js"),
|
||||
TileSprite = require("./TileSprite.js"),
|
||||
TileSpriteXML = require("./TileSpriteXML.js"),
|
||||
Text = require("./Text.js"),
|
||||
Texture = require("./Texture.js"),
|
||||
SpriteSheetXML = require("./SpriteSheetXML.js")
|
||||
var AnimManager = require("./AnimManager.js"),
|
||||
Camera = require("./Camera.js"),
|
||||
Container = require("./Container.js"),
|
||||
CanvasRenderer = require("./renderer/CanvasRenderer.js"),
|
||||
Game = require("./Game.js"),
|
||||
math = require("./utilities/math.js"),
|
||||
entity = require("./utilities/entity.js"),
|
||||
wallslide = require("./movement/wallslide.js"),
|
||||
deadInTracks = require("./movement/deadInTracks.js"),
|
||||
State = require("./State.js"),
|
||||
Sound = require("./sound/Sound.js"),
|
||||
Rect = require("./Rect.js"),
|
||||
Line = require("./Line.js"),
|
||||
KeyControls = require("./controls/KeyControls.js"),
|
||||
MouseControls = require("./controls/MouseControls.js"),
|
||||
Sprite = require("./Sprite.js"),
|
||||
TileMap = require("./TileMap.js"),
|
||||
TileMapXML = require("./TileMapXML.js"),
|
||||
TileSprite = require("./TileSprite.js"),
|
||||
TileSpriteXML = require("./TileSpriteXML.js"),
|
||||
Text = require("./Text.js"),
|
||||
Texture = require("./Texture.js"),
|
||||
SpriteSheetXML = require("./SpriteSheetXML.js")
|
||||
;
|
||||
|
||||
module.exports = {
|
||||
CanvasRenderer,
|
||||
Container,
|
||||
Game,
|
||||
math,
|
||||
KeyControls,
|
||||
MouseControls,
|
||||
Sprite,
|
||||
TileMap,
|
||||
TileMapXML,
|
||||
TileSprite,
|
||||
SpriteSheetXML,
|
||||
TileSpriteXML,
|
||||
Text,
|
||||
Texture
|
||||
AnimManager,
|
||||
CanvasRenderer,
|
||||
Camera,
|
||||
Container,
|
||||
Game,
|
||||
math,
|
||||
entity,
|
||||
wallslide,
|
||||
deadInTracks,
|
||||
State,
|
||||
Sound,
|
||||
Rect,
|
||||
Line,
|
||||
KeyControls,
|
||||
MouseControls,
|
||||
Sprite,
|
||||
TileMap,
|
||||
TileMapXML,
|
||||
TileSprite,
|
||||
SpriteSheetXML,
|
||||
TileSpriteXML,
|
||||
Text,
|
||||
Texture
|
||||
};
|
||||
|
16
lib/movement/deadInTracks.js
Normal file
16
lib/movement/deadInTracks.js
Normal file
@ -0,0 +1,16 @@
|
||||
const entity = require("../utilities/entity");
|
||||
|
||||
function deadInTracks(ent, map, x = 0, y = 0) {
|
||||
const bounds = entity.bounds(ent);
|
||||
const tiles = map.tilesAtCorners(bounds, x, y);
|
||||
const walks = tiles.map(t => t && t.frame.walkable);
|
||||
const blocked = walks.some(w => !w);
|
||||
if (blocked) {
|
||||
x = 0,
|
||||
y = 0;
|
||||
}
|
||||
return { x, y };
|
||||
}
|
||||
module.exports = {
|
||||
deadInTracks
|
||||
};
|
51
lib/movement/wallslide.js
Normal file
51
lib/movement/wallslide.js
Normal file
@ -0,0 +1,51 @@
|
||||
const entity = require("../utilities/entity.js");
|
||||
|
||||
function wallslide(ent, map, x = 0, y = 0) {
|
||||
let tiles;
|
||||
let tileEdge;
|
||||
const bounds = entity.bounds(ent);
|
||||
|
||||
// Final amounts of movement to allow
|
||||
let xo = x;
|
||||
let yo = y;
|
||||
|
||||
// Check vertical movement
|
||||
if (y !== 0) {
|
||||
tiles = map.tilesAtCorners(bounds, 0, yo);
|
||||
const [tl, tr, bl, br] = tiles.map(t => t && t.frame.walkable);
|
||||
|
||||
// Hit your head
|
||||
if (y < 0 && !(tl && tr)) {
|
||||
tileEdge = tiles[0].pos.y + tiles[0].h;
|
||||
yo = tileEdge - bounds.y;
|
||||
}
|
||||
// Hit your feet
|
||||
if (y > 0 && !(bl && br)) {
|
||||
tileEdge = tiles[2].pos.y - 1;
|
||||
yo = tileEdge - (bounds.y + bounds.h);
|
||||
}
|
||||
}
|
||||
|
||||
// Check horizontal movement
|
||||
if (x !== 0) {
|
||||
tiles = map.tilesAtCorners(bounds, xo, yo);
|
||||
const [tl, tr, bl, br] = tiles.map(t => t && t.frame.walkable);
|
||||
|
||||
// Hit left edge
|
||||
if (x < 0 && !(tl && bl)) {
|
||||
tileEdge = tiles[0].pos.x + tiles[0].w;
|
||||
xo = tileEdge - bounds.x;
|
||||
}
|
||||
// Hit right edge
|
||||
if (x > 0 && !(tr && br)) {
|
||||
tileEdge = tiles[1].pos.x - 1;
|
||||
xo = tileEdge - (bounds.x + bounds.w);
|
||||
}
|
||||
}
|
||||
// xo & yo contain the amount we're allowed to move by.
|
||||
return { x: xo, y: yo };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wallslide
|
||||
};
|
@ -1,97 +1,115 @@
|
||||
class CanvasRenderer {
|
||||
|
||||
constructor(w, h) {
|
||||
const canvas = document.createElement("canvas");
|
||||
this.w = canvas.width = w;
|
||||
this.h = canvas.height = h;
|
||||
this.view = canvas;
|
||||
this.ctx = canvas.getContext("2d");
|
||||
this.ctx.textBaseLine = "top";
|
||||
}
|
||||
constructor(w, h) {
|
||||
const canvas = document.createElement("canvas");
|
||||
this.w = canvas.width = w;
|
||||
this.h = canvas.height = h;
|
||||
this.view = canvas;
|
||||
this.ctx = canvas.getContext("2d");
|
||||
this.ctx.textBaseLine = "top";
|
||||
}
|
||||
|
||||
setPixelated() {
|
||||
this.ctx['imageSmoothingEnabled'] = false; /* standard */
|
||||
this.ctx['mozImageSmoothingEnabled'] = false; /* Firefox */
|
||||
this.ctx['oImageSmoothingEnabled'] = false; /* Opera */
|
||||
this.ctx['webkitImageSmoothingEnabled'] = false; /* Safari */
|
||||
this.ctx['msImageSmoothingEnabled'] = false; /* IE */
|
||||
}
|
||||
setPixelated() {
|
||||
this.ctx["imageSmoothingEnabled"] = false; /* standard */
|
||||
this.ctx["mozImageSmoothingEnabled"] = false; /* Firefox */
|
||||
this.ctx["oImageSmoothingEnabled"] = false; /* Opera */
|
||||
this.ctx["webkitImageSmoothingEnabled"] = false; /* Safari */
|
||||
this.ctx["msImageSmoothingEnabled"] = false; /* IE */
|
||||
}
|
||||
|
||||
render(container, clear = true) {
|
||||
const { ctx } = this;
|
||||
function renderRec(container) {
|
||||
// Render container children
|
||||
container.children.forEach(child => {
|
||||
if (child.visible == false) {
|
||||
return;
|
||||
}
|
||||
render(container, clear = true) {
|
||||
const { ctx } = this;
|
||||
function renderRec(container) {
|
||||
// Render container children
|
||||
container.children.forEach(child => {
|
||||
if (child.visible == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.save();
|
||||
ctx.save();
|
||||
|
||||
if (child.pos) {
|
||||
ctx.translate(Math.round(child.pos.x), Math.round(child.pos.y));
|
||||
}
|
||||
if (child.pos) {
|
||||
ctx.translate(Math.round(child.pos.x), Math.round(child.pos.y));
|
||||
}
|
||||
|
||||
if (child.anchor) {
|
||||
ctx.translate(child.anchor.x, child.anchor.y);
|
||||
}
|
||||
if (child.anchor) {
|
||||
ctx.translate(child.anchor.x, child.anchor.y);
|
||||
}
|
||||
|
||||
if (child.scale) {
|
||||
ctx.scale(child.scale.x, child.scale.y);
|
||||
}
|
||||
if (child.scale) {
|
||||
ctx.scale(child.scale.x, child.scale.y);
|
||||
}
|
||||
|
||||
if (child.rotation) {
|
||||
const px = child.pivot ? child.pivot.x : 0;
|
||||
const py = child.pivot ? child.pivot.y : 0;
|
||||
ctx.translate(px, py);
|
||||
ctx.rotate(child.rotation);
|
||||
ctx.translate(-px, -py);
|
||||
}
|
||||
if (child.rotation) {
|
||||
const px = child.pivot ? child.pivot.x : 0;
|
||||
const py = child.pivot ? child.pivot.y : 0;
|
||||
ctx.translate(px, py);
|
||||
ctx.rotate(child.rotation);
|
||||
ctx.translate(-px, -py);
|
||||
}
|
||||
|
||||
if (child.text) {
|
||||
const { font, fill, align } = child.style;
|
||||
if (font) ctx.font = font;
|
||||
if (fill) ctx.fillStyle = fill;
|
||||
if (align) ctx.textAlign = align;
|
||||
ctx.fillText(child.text, 0, 0);
|
||||
}
|
||||
if (child.text) {
|
||||
const { font, fill, align } = child.style;
|
||||
if (font) ctx.font = font;
|
||||
if (fill) ctx.fillStyle = fill;
|
||||
if (align) ctx.textAlign = align;
|
||||
ctx.fillText(child.text, 0, 0);
|
||||
}
|
||||
|
||||
else if (child.texture) {
|
||||
const img = child.texture.img;
|
||||
if (child.tileW && child.tileH) {
|
||||
ctx.drawImage(
|
||||
img,
|
||||
child.frame.x * child.tileW,
|
||||
child.frame.y * child.tileH,
|
||||
child.tileW, child.tileH,
|
||||
0, 0,
|
||||
child.tileW, child.tileH
|
||||
);
|
||||
} else if (child.imgPos && child.width && child.height) {
|
||||
ctx.drawImage(
|
||||
img,
|
||||
child.imgPos.x,
|
||||
child.imgPos.y,
|
||||
child.width, child.height,
|
||||
0, 0,
|
||||
child.width, child.height
|
||||
);
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
}
|
||||
}
|
||||
else if (child.texture) {
|
||||
const img = child.texture.img;
|
||||
if (child.tileW && child.tileH) {
|
||||
ctx.drawImage(
|
||||
img,
|
||||
child.frame.x * child.tileW,
|
||||
child.frame.y * child.tileH,
|
||||
child.tileW, child.tileH,
|
||||
0, 0,
|
||||
child.tileW, child.tileH
|
||||
);
|
||||
} else if (child.imgPos && child.width && child.height) {
|
||||
ctx.drawImage(
|
||||
img,
|
||||
child.imgPos.x,
|
||||
child.imgPos.y,
|
||||
child.width, child.height,
|
||||
0, 0,
|
||||
child.width, child.height
|
||||
);
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
}
|
||||
} else if (child.style && child.w && child.h) {
|
||||
ctx.fillStyle = child.style.fill;
|
||||
ctx.fillRect(0, 0, child.w, child.h);
|
||||
} else if (child.style && child.radius) {
|
||||
var gradient = ctx.createRadialGradient(0, 0, 50, 0, 0, 50);
|
||||
gradient.addColorStop(0, child.style.start);
|
||||
gradient.addColorStop(1, child.style.stop);
|
||||
|
||||
// Handle children with children
|
||||
if (child.children) {
|
||||
renderRec(child);
|
||||
}
|
||||
ctx.restore();
|
||||
})
|
||||
}
|
||||
if (clear) {
|
||||
ctx.clearRect(0, 0, this.w, this.h);
|
||||
}
|
||||
renderRec(container);
|
||||
}
|
||||
ctx.arc(0, 0, child.radius, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = gradient;
|
||||
|
||||
ctx.fill();
|
||||
} else if (child.style && child.target) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0,0);
|
||||
ctx.lineTo(child.target.x, child.target.y);
|
||||
ctx.strokeStyle = child.style;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Handle children with children
|
||||
if (child.children) {
|
||||
renderRec(child);
|
||||
}
|
||||
ctx.restore();
|
||||
});
|
||||
}
|
||||
if (clear) {
|
||||
ctx.clearRect(0, 0, this.w, this.h);
|
||||
}
|
||||
renderRec(container);
|
||||
}
|
||||
}
|
||||
module.exports = CanvasRenderer;
|
||||
|
53
lib/sound/Sound.js
Normal file
53
lib/sound/Sound.js
Normal file
@ -0,0 +1,53 @@
|
||||
class Sound {
|
||||
constructor(src, options = {}) {
|
||||
this.playing = false;
|
||||
this.src = src;
|
||||
this.options = Object.assign({ volume: 1 }, options);
|
||||
|
||||
// Configure audio element
|
||||
const audio = new Audio();
|
||||
audio.src = src;
|
||||
if (options.loop) {
|
||||
audio.loop = true;
|
||||
}
|
||||
audio.addEventListener(
|
||||
"error",
|
||||
() => {
|
||||
throw Error(`Error loading audio: ${src}`);
|
||||
},
|
||||
false
|
||||
);
|
||||
audio.addEventListener(
|
||||
"ended",
|
||||
() => {
|
||||
this.playing = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
this.audio = audio;
|
||||
}
|
||||
|
||||
play(overrides) {
|
||||
const { audio, options } = this;
|
||||
const opts = Object.assign({ time: 0 }, options, overrides);
|
||||
audio.volume = opts.volume;
|
||||
audio.currentTime = opts.time;
|
||||
audio.play();
|
||||
this.playing = true;
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.audio.pause();
|
||||
this.playing = false;
|
||||
}
|
||||
|
||||
get volume() {
|
||||
return this.audio.volume;
|
||||
}
|
||||
|
||||
set volume(volume) {
|
||||
this.options.volume = this.audio.volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Sound;
|
96
lib/utilities/entity.js
Normal file
96
lib/utilities/entity.js
Normal file
@ -0,0 +1,96 @@
|
||||
const math = require("./math");
|
||||
const Rect = require("../Rect");
|
||||
|
||||
function addDebug(e) {
|
||||
e.children = e.children || [];
|
||||
const bb = new Rect(e.w, e.h, { fill: "rgba(255, 0, 0, 0.3)" });
|
||||
e.children.push(bb);
|
||||
if (e.hitBox) {
|
||||
const { x, y, w, h } = e.hitBox;
|
||||
const hb = new Rect(w, h, { fill: "rgba(255, 0, 0, 0.5)" });
|
||||
hb.pos.x = x;
|
||||
hb.pos.y = y;
|
||||
e.children.push(hb);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
function angle(a, b) {
|
||||
return math.angle(center(a), center(b));
|
||||
}
|
||||
|
||||
function bounds(entity) {
|
||||
let pos, w, h, hitBox;
|
||||
// Object.prototype.hasOwnProperty.call is needed because of eslint
|
||||
if (Object.prototype.hasOwnProperty.call(entity, "tileW") && Object.prototype.hasOwnProperty.call(entity, "tileH")) {
|
||||
({pos, w, h} = {pos: entity.pos, w: entity.tileW, h: entity.tileH});
|
||||
} else {
|
||||
({pos, w, h} = entity);
|
||||
}
|
||||
|
||||
if (entity.hitBox) hitBox = entity.hitBox;
|
||||
|
||||
const hit = hitBox || { x: 0, y: 0, w, h };
|
||||
return {
|
||||
x: hit.x + pos.x,
|
||||
y: hit.y + pos.y,
|
||||
w: hit.w - 1,
|
||||
h: hit.h - 1
|
||||
};
|
||||
}
|
||||
|
||||
function center(entity) {
|
||||
let pos, w, h;
|
||||
|
||||
// Object.prototype.hasOwnProperty.call is needed because of eslint
|
||||
if (Object.prototype.hasOwnProperty.call(entity, "tileW") && Object.prototype.hasOwnProperty.call(entity, "tileH")) {
|
||||
({pos, w, h} = {pos: entity.pos, w: entity.tileW, h: entity.tileH});
|
||||
} else {
|
||||
({pos, w, h} = entity);
|
||||
}
|
||||
|
||||
return {
|
||||
x: pos.x + w / 2,
|
||||
y: pos.y + h / 2
|
||||
};
|
||||
}
|
||||
|
||||
function distance(a, b) {
|
||||
return math.distance(center(a), center(b));
|
||||
}
|
||||
|
||||
function hit(e1, e2) {
|
||||
const a = bounds(e1);
|
||||
const b = bounds(e2);
|
||||
return (
|
||||
a.x + a.w >= b.x &&
|
||||
a.x <= b.x + b.w &&
|
||||
a.y + a.h >= b.y &&
|
||||
a.y <= b.y + b.h
|
||||
);
|
||||
}
|
||||
|
||||
function hits(entity, container, hitCallback) {
|
||||
const a = bounds(entity);
|
||||
container.map(e2 => {
|
||||
const b = bounds(e2);
|
||||
if (
|
||||
a.x + a.w >= b.x &&
|
||||
a.x <= b.x + b.w &&
|
||||
a.y + a.h >= b.y &&
|
||||
a.y <= b.y + b.h
|
||||
) {
|
||||
hitCallback(e2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addDebug,
|
||||
angle,
|
||||
bounds,
|
||||
center,
|
||||
distance,
|
||||
hit,
|
||||
hits
|
||||
};
|
@ -1,26 +1,48 @@
|
||||
function angle(a, b) {
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
const angle = Math.atan2(dy, dx);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
function clamp(x, min, max) {
|
||||
return Math.max(min, Math.min(x, max));
|
||||
}
|
||||
|
||||
function distance (a, b) {
|
||||
const dx = a.x - b.x;
|
||||
const dy = a.y - b.y;
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
function rand(min, max) {
|
||||
return Math.floor(randf(min, max));
|
||||
return Math.floor(randf(min, max));
|
||||
}
|
||||
|
||||
function randf(min, max) {
|
||||
if (max == null) {
|
||||
max = min || 1;
|
||||
min = 0;
|
||||
}
|
||||
return Math.random() * (max - min) + min;
|
||||
if (max == null) {
|
||||
max = min || 1;
|
||||
min = 0;
|
||||
}
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
function randOneFrom(items) {
|
||||
return items[rand(items.length)];
|
||||
return items[rand(items.length)];
|
||||
}
|
||||
|
||||
function randOneIn(max = 2) {
|
||||
return rand(0, max) === 0;
|
||||
return rand(0, max) === 0;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
rand,
|
||||
randf,
|
||||
randOneFrom,
|
||||
randOneIn
|
||||
angle,
|
||||
clamp,
|
||||
distance,
|
||||
rand,
|
||||
randf,
|
||||
randOneFrom,
|
||||
randOneIn
|
||||
};
|
1109
package-lock.json
generated
Normal file
1109
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asdf-games",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.6",
|
||||
"description": "Javascript gaming framework, based upon the framework made in the book HTML5 Games: Novice to Ninja by Sitepoint.",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
@ -17,5 +17,11 @@
|
||||
"name": "Arne van Iterson",
|
||||
"url": "https://gitea.arnweb.nl/arne/"
|
||||
},
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"eslint": "^6.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"easystarjs": "^0.4.3"
|
||||
}
|
||||
}
|
||||
|
BIN
res/asdf-logo.png
Normal file
BIN
res/asdf-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
Loading…
Reference in New Issue
Block a user