Added everything from chapter 4 and set up ESlint

This commit is contained in:
Arne van Iterson 2020-03-21 17:22:28 +01:00
parent 3490dd07ae
commit 72633d8c0f
23 changed files with 1727 additions and 416 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
projects
examples

33
.eslintrc.js Normal file
View 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"
]
}
};

63
lib/AnimManager.js Normal file
View File

@ -0,0 +1,63 @@
class Anim {
constructor(frames, rate) {
this.frames = frames;
this.rate = rate;
this.reset();
}
update(dt) {
const { rate, frames } = this;
if ((this.curTime += dt) > rate) {
this.curFrame++;
this.frame = frames[this.curFrame % frames.length];
this.curTime -= rate;
}
}
reset() {
this.frame = this.frames[0];
this.curFrame = 0;
this.curTime = 0;
}
}
class AnimManager {
constructor(e) {
this.anims = {};
this.running = false;
this.frameSource = e.frame || e;
this.currrent = 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);
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
View 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;

View File

@ -1,30 +1,30 @@
class Container { class Container {
constructor() { constructor() {
this.pos = { x: 0, y: 0 }; this.pos = { x: 0, y: 0 };
this.children = []; this.children = [];
} }
add(child) { add(child) {
this.children.push(child); this.children.push(child);
return child; return child;
} }
remove(child) { remove(child) {
this.children = this.children.filter(c => c !== child); this.children = this.children.filter(c => c !== child);
return child; return child;
} }
map(f) { map(f) {
return this.children.map(f); return this.children.map(f);
} }
update(dt, t) { update(dt, t) {
this.children = this.children.filter(child => { this.children = this.children.filter(child => {
if (child.update) { if (child.update) {
child.update(dt, t, this); child.update(dt, t, this);
} }
return child.dead ? false : true; return child.dead ? false : true;
}); });
} }
} }
module.exports = Container; module.exports = Container;

View File

@ -1,40 +1,40 @@
var Container = require("./Container"), var Container = require("./Container"),
CanvasRenderer = require('./renderer/CanvasRenderer') CanvasRenderer = require("./renderer/CanvasRenderer")
; ;
const STEP = 1 / 60; const STEP = 1 / 60;
const FRAME_MAX = 5 * STEP; const FRAME_MAX = 5 * STEP;
class Game { class Game {
constructor(w, h, pixelated, parent = "#board") { constructor(w, h, pixelated, parent = "#board") {
this.w = w; this.w = w;
this.h = h; this.h = h;
this.renderer = new CanvasRenderer(w, h); this.renderer = new CanvasRenderer(w, h);
document.querySelector(parent).appendChild(this.renderer.view); document.querySelector(parent).appendChild(this.renderer.view);
if (pixelated) { if (pixelated) {
this.renderer.setPixelated(); this.renderer.setPixelated();
} }
this.scene = new Container(); this.scene = new Container();
} }
run(gameUpdate = () => { }) { run(gameUpdate = () => { }) {
let dt = 0; let dt = 0;
let last = 0; let last = 0;
const loop = ms => { const loop = ms => {
requestAnimationFrame(loop); requestAnimationFrame(loop);
const t = ms / 1000; const t = ms / 1000;
dt = Math.min(t - last, FRAME_MAX); dt = Math.min(t - last, FRAME_MAX);
last = t; last = t;
this.scene.update(dt, t); this.scene.update(dt, t);
gameUpdate(dt, t); gameUpdate(dt, t);
this.renderer.render(this.scene); this.renderer.render(this.scene);
}; };
requestAnimationFrame(loop); requestAnimationFrame(loop);
} }
} }
module.exports = Game; module.exports = Game;

View File

@ -1,10 +1,10 @@
class Rect { class Rect {
constructor(w, h, style = { fill: "#333" }) { constructor(w, h, style = { fill: "#333" }) {
this.pos = { x: 0, y: 0 }; this.pos = { x: 0, y: 0 };
this.w = w; this.w = w;
this.h = h; this.h = h;
this.style = style; this.style = style;
} }
} }
module.exports = Rect; module.exports = Rect;

View File

@ -1,10 +1,10 @@
class Sprite { class Sprite {
constructor(texture) { constructor(texture) {
this.texture = texture; this.texture = texture;
this.pos = { x: 0, y: 0 }; this.pos = { x: 0, y: 0 };
this.anchor = { x: 0, y: 0 }; this.anchor = { x: 0, y: 0 };
this.scale = { x: 1, y: 1 }; this.scale = { x: 1, y: 1 };
this.rotation = 0; this.rotation = 0;
} }
} }
module.exports = Sprite; module.exports = Sprite;

View File

@ -1,39 +1,39 @@
class SpriteSheetXML { class SpriteSheetXML {
constructor(url) { constructor(url) {
this.array = []; this.array = [];
this.fetchXMLtoArray(url); this.fetchXMLtoArray(url);
} }
fetchXMLtoArray(url) { fetchXMLtoArray(url) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('GET', url, false); xhr.open("GET", url, false);
xhr.send(null); xhr.send(null);
if (xhr.status === 200) { if (xhr.status === 200) {
var children = xhr.responseXML.children[0].children; var children = xhr.responseXML.children[0].children;
for (let index = 0; index < children.length; index++) { for (let index = 0; index < children.length; index++) {
const element = children[index]; const element = children[index];
this.array.push({ this.array.push({
name: element.attributes.name.nodeValue, name: element.attributes.name.nodeValue,
x: element.attributes.x.nodeValue, x: element.attributes.x.nodeValue,
y: element.attributes.y.nodeValue, y: element.attributes.y.nodeValue,
width: element.attributes.width.nodeValue, width: element.attributes.width.nodeValue,
height: element.attributes.height.nodeValue height: element.attributes.height.nodeValue
}); });
} }
} else { } else {
console.error('XML file cannot be loaded!') console.error("XML file cannot be loaded!");
} }
} }
findIndex(attribute, value) { findIndex(attribute, value) {
for (let index = 0; index < this.array.length; index++) { for (let index = 0; index < this.array.length; index++) {
const element = this.array[index]; const element = this.array[index];
if (element[attribute] == value) { if (element[attribute] == value) {
return index; return index;
} }
} }
} }
} }
module.exports = SpriteSheetXML; module.exports = SpriteSheetXML;

View File

@ -1,8 +1,8 @@
class Text { class Text {
constructor(text = "", style = {}) { constructor(text = "", style = {}) {
this.pos = { x: 0, y: 0 }; this.pos = { x: 0, y: 0 };
this.text = text; this.text = text;
this.style = style; this.style = style;
} }
} }
module.exports = Text; module.exports = Text;

View File

@ -1,7 +1,7 @@
class Texture { class Texture {
constructor(url) { constructor(url) {
this.img = new Image(); this.img = new Image();
this.img.src = url; this.img.src = url;
} }
} }
module.exports = Texture; module.exports = Texture;

View File

@ -1,25 +1,59 @@
var Container = require("./Container"), var Container = require("./Container"),
TileSprite = require("./TileSprite") TileSprite = require("./TileSprite")
; ;
class TileMap extends Container { class TileMap extends Container {
constructor(tiles, mapW, mapH, tileW, tileH, texture) { constructor(tiles, mapW, mapH, tileW, tileH, texture) {
super(); super();
this.mapW = mapW; this.mapW = mapW;
this.mapH = mapH; this.mapH = mapH;
this.tileW = tileW; this.tileW = tileW;
this.tileH = tileH; this.tileH = tileH;
this.w = mapW * tileW; this.w = mapW * tileW;
this.h = mapH * tileH; this.h = mapH * tileH;
this.children = tiles.map((frame, i) => { this.children = tiles.map((frame, i) => {
const s = new TileSprite(texture, tileW, tileH); const s = new TileSprite(texture, tileW, tileH);
s.frame = frame; s.frame = frame;
s.pos.x = i % mapW * tileW; s.pos.x = i % mapW * tileW;
s.pos.y = Math.floor(i / mapW) * tileH; s.pos.y = Math.floor(i / mapW) * tileH;
return s; 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);
}
} }
module.exports = TileMap; module.exports = TileMap;

View File

@ -1,25 +1,25 @@
var Container = require("./Container"), var Container = require("./Container"),
TileSpriteXML = require("./TileSpriteXML") TileSpriteXML = require("./TileSpriteXML")
; ;
class TileMapXML extends Container { class TileMapXML extends Container {
constructor(tiles, mapW, mapH, texture, xml) { constructor(tiles, mapW, mapH, texture, xml) {
super(texture); super(texture);
this.mapW = mapW; this.mapW = mapW;
this.mapH = mapH; this.mapH = mapH;
this.tileW = xml.array[tiles[0]].width; this.tileW = xml.array[tiles[0]].width;
this.tileH = xml.array[tiles[0]].height; this.tileH = xml.array[tiles[0]].height;
this.w = mapW * this.tileW; this.w = mapW * this.tileW;
this.h = mapH * this.tileH; this.h = mapH * this.tileH;
this.children = tiles.map((frame, i) => { this.children = tiles.map((frame, i) => {
const s = new TileSpriteXML(texture, xml, frame); const s = new TileSpriteXML(texture, xml, frame);
s.frame = frame; s.frame = frame;
s.pos.x = i % mapW * this.tileW; s.pos.x = i % mapW * this.tileW;
s.pos.y = Math.floor(i / mapW) * this.tileH; s.pos.y = Math.floor(i / mapW) * this.tileH;
return s; return s;
}); });
} }
} }
module.exports = TileMapXML; module.exports = TileMapXML;

View File

@ -1,12 +1,12 @@
var Sprite = require("./Sprite"); var Sprite = require("./Sprite");
class TileSprite extends Sprite { class TileSprite extends Sprite {
constructor(texture, w, h) { constructor(texture, w, h) {
super(texture); super(texture);
this.tileW = w; this.tileW = w;
this.tileH = h; this.tileH = h;
this.frame = { x: 0, y: 0 }; this.frame = { x: 0, y: 0 };
} }
} }
module.exports = TileSprite; module.exports = TileSprite;

View File

@ -1,13 +1,13 @@
var Sprite = require("./Sprite"); var Sprite = require("./Sprite");
class TileSpriteXML extends Sprite { class TileSpriteXML extends Sprite {
constructor(texture, xml, index) { constructor(texture, xml, index) {
super(texture); super(texture);
var src = xml.array[index]; var src = xml.array[index];
this.imgPos = { x: src['x'], y: src['y'] }; this.imgPos = { x: src["x"], y: src["y"] };
this.width = src['width']; this.width = src["width"];
this.height = src['height']; this.height = src["height"];
} }
} }
module.exports = TileSpriteXML; module.exports = TileSpriteXML;

View File

@ -1,61 +1,61 @@
class KeyControls { class KeyControls {
constructor() { constructor() {
this.keys = {}; this.keys = {};
// Bind event handlers // Bind event handlers
document.addEventListener("keydown", e => { document.addEventListener("keydown", e => {
if ([37, 38, 39, 40].indexOf(e.which) >= 0) { if ([37, 38, 39, 40].indexOf(e.which) >= 0) {
e.preventDefault(); e.preventDefault();
} }
this.keys[e.which] = true; this.keys[e.which] = true;
}, false); }, false);
document.addEventListener('keyup', e => { document.addEventListener("keyup", e => {
this.keys[e.which] = false; this.keys[e.which] = false;
}, false); }, false);
} }
get action() { get action() {
// Spacebar // Spacebar
return this.keys[32]; return this.keys[32];
} }
get x() { get x() {
// Arrow Left or A (WASD) // Arrow Left or A (WASD)
if (this.keys[37] || this.keys[65]) { if (this.keys[37] || this.keys[65]) {
return -1; return -1;
} }
// Arrow Right or D (WASD) // Arrow Right or D (WASD)
if (this.keys[39] || this.keys[68]) { if (this.keys[39] || this.keys[68]) {
return 1; return 1;
} }
return 0; return 0;
} }
get y() { get y() {
// Arrow Up or W (WASD) // Arrow Up or W (WASD)
if (this.keys[38] || this.keys[87]) { if (this.keys[38] || this.keys[87]) {
return -1; return -1;
} }
// Arrow Down or S (WASD) // Arrow Down or S (WASD)
if (this.keys[40] || this.keys[83]) { if (this.keys[40] || this.keys[83]) {
return 1; return 1;
} }
return 0; return 0;
} }
key(key, value) { key(key, value) {
if (value !== undefined) { if (value !== undefined) {
this.keys[key] = value; this.keys[key] = value;
} }
return this.keys[key]; return this.keys[key];
} }
reset() { reset() {
for (let key in this.keys) { for (let key in this.keys) {
this.keys[key] = false; this.keys[key] = false;
} }
} }
} }

View File

@ -1,45 +1,45 @@
class MouseControls { class MouseControls {
constructor(container) { constructor(container) {
this.el = container || document.body; this.el = container || document.body;
// State // State
this.pos = { x: 0, y: 0 }; this.pos = { x: 0, y: 0 };
this.isDown = false; this.isDown = false;
this.pressed = false; this.pressed = false;
this.released = false; this.released = false;
// Handlers // Handlers
document.addEventListener('mousemove', this.move.bind(this), false); document.addEventListener("mousemove", this.move.bind(this), false);
document.addEventListener('mousedown', this.down.bind(this), false); document.addEventListener("mousedown", this.down.bind(this), false);
document.addEventListener('mouseup', this.up.bind(this), false); document.addEventListener("mouseup", this.up.bind(this), false);
} }
mousePosFromEvent({ clientX, clientY }) { mousePosFromEvent({ clientX, clientY }) {
const { el, pos } = this; const { el, pos } = this;
const rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
const xr = el.width / el.clientWidth; const xr = el.width / el.clientWidth;
const yr = el.height / el.clientHeight; const yr = el.height / el.clientHeight;
pos.x = (clientX - rect.left) * xr; pos.x = (clientX - rect.left) * xr;
pos.y = (clientY - rect.top) * yr; pos.y = (clientY - rect.top) * yr;
} }
move(e) { move(e) {
this.mousePosFromEvent(e); this.mousePosFromEvent(e);
} }
down(e) { down(e) {
this.isDown = true; this.isDown = true;
this.pressed = true; this.pressed = true;
this.mousePosFromEvent(e); this.mousePosFromEvent(e);
} }
up() { up() {
this.isDown = false; this.isDown = false;
this.released = true; this.released = true;
} }
update() { update() {
this.released = false; this.released = false;
this.pressed = false; this.pressed = false;
} }
} }
module.exports = MouseControls; module.exports = MouseControls;

View File

@ -1,36 +1,40 @@
var Container = require("./Container.js"), var AnimManager = require("./AnimManager.js"),
CanvasRenderer = require("./renderer/CanvasRenderer.js"), Camera = require("./Camera.js"),
Game = require("./Game.js"), Container = require("./Container.js"),
math = require("./utilities/math.js"), CanvasRenderer = require("./renderer/CanvasRenderer.js"),
entity = require("./utilities/entity.js"), Game = require("./Game.js"),
Rect = require("./Rect.js"), math = require("./utilities/math.js"),
KeyControls = require("./controls/KeyControls.js"), entity = require("./utilities/entity.js"),
MouseControls = require("./controls/MouseControls.js"), Rect = require("./Rect.js"),
Sprite = require("./Sprite.js"), KeyControls = require("./controls/KeyControls.js"),
TileMap = require("./TileMap.js"), MouseControls = require("./controls/MouseControls.js"),
TileMapXML = require("./TileMapXML.js"), Sprite = require("./Sprite.js"),
TileSprite = require("./TileSprite.js"), TileMap = require("./TileMap.js"),
TileSpriteXML = require("./TileSpriteXML.js"), TileMapXML = require("./TileMapXML.js"),
Text = require("./Text.js"), TileSprite = require("./TileSprite.js"),
Texture = require("./Texture.js"), TileSpriteXML = require("./TileSpriteXML.js"),
SpriteSheetXML = require("./SpriteSheetXML.js") Text = require("./Text.js"),
Texture = require("./Texture.js"),
SpriteSheetXML = require("./SpriteSheetXML.js")
; ;
module.exports = { module.exports = {
CanvasRenderer, AnimManager,
Container, CanvasRenderer,
Game, Camera,
math, Container,
entity, Game,
Rect, math,
KeyControls, entity,
MouseControls, Rect,
Sprite, KeyControls,
TileMap, MouseControls,
TileMapXML, Sprite,
TileSprite, TileMap,
SpriteSheetXML, TileMapXML,
TileSpriteXML, TileSprite,
Text, SpriteSheetXML,
Texture TileSpriteXML,
Text,
Texture
}; };

View File

@ -1,100 +1,100 @@
class CanvasRenderer { class CanvasRenderer {
constructor(w, h) { constructor(w, h) {
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
this.w = canvas.width = w; this.w = canvas.width = w;
this.h = canvas.height = h; this.h = canvas.height = h;
this.view = canvas; this.view = canvas;
this.ctx = canvas.getContext("2d"); this.ctx = canvas.getContext("2d");
this.ctx.textBaseLine = "top"; this.ctx.textBaseLine = "top";
} }
setPixelated() { setPixelated() {
this.ctx['imageSmoothingEnabled'] = false; /* standard */ this.ctx["imageSmoothingEnabled"] = false; /* standard */
this.ctx['mozImageSmoothingEnabled'] = false; /* Firefox */ this.ctx["mozImageSmoothingEnabled"] = false; /* Firefox */
this.ctx['oImageSmoothingEnabled'] = false; /* Opera */ this.ctx["oImageSmoothingEnabled"] = false; /* Opera */
this.ctx['webkitImageSmoothingEnabled'] = false; /* Safari */ this.ctx["webkitImageSmoothingEnabled"] = false; /* Safari */
this.ctx['msImageSmoothingEnabled'] = false; /* IE */ this.ctx["msImageSmoothingEnabled"] = false; /* IE */
} }
render(container, clear = true) { render(container, clear = true) {
const { ctx } = this; const { ctx } = this;
function renderRec(container) { function renderRec(container) {
// Render container children // Render container children
container.children.forEach(child => { container.children.forEach(child => {
if (child.visible == false) { if (child.visible == false) {
return; return;
} }
ctx.save(); ctx.save();
if (child.pos) { if (child.pos) {
ctx.translate(Math.round(child.pos.x), Math.round(child.pos.y)); ctx.translate(Math.round(child.pos.x), Math.round(child.pos.y));
} }
if (child.anchor) { if (child.anchor) {
ctx.translate(child.anchor.x, child.anchor.y); ctx.translate(child.anchor.x, child.anchor.y);
} }
if (child.scale) { if (child.scale) {
ctx.scale(child.scale.x, child.scale.y); ctx.scale(child.scale.x, child.scale.y);
} }
if (child.rotation) { if (child.rotation) {
const px = child.pivot ? child.pivot.x : 0; const px = child.pivot ? child.pivot.x : 0;
const py = child.pivot ? child.pivot.y : 0; const py = child.pivot ? child.pivot.y : 0;
ctx.translate(px, py); ctx.translate(px, py);
ctx.rotate(child.rotation); ctx.rotate(child.rotation);
ctx.translate(-px, -py); ctx.translate(-px, -py);
} }
if (child.text) { if (child.text) {
const { font, fill, align } = child.style; const { font, fill, align } = child.style;
if (font) ctx.font = font; if (font) ctx.font = font;
if (fill) ctx.fillStyle = fill; if (fill) ctx.fillStyle = fill;
if (align) ctx.textAlign = align; if (align) ctx.textAlign = align;
ctx.fillText(child.text, 0, 0); ctx.fillText(child.text, 0, 0);
} }
else if (child.texture) { else if (child.texture) {
const img = child.texture.img; const img = child.texture.img;
if (child.tileW && child.tileH) { if (child.tileW && child.tileH) {
ctx.drawImage( ctx.drawImage(
img, img,
child.frame.x * child.tileW, child.frame.x * child.tileW,
child.frame.y * child.tileH, child.frame.y * child.tileH,
child.tileW, child.tileH, child.tileW, child.tileH,
0, 0, 0, 0,
child.tileW, child.tileH child.tileW, child.tileH
); );
} else if (child.imgPos && child.width && child.height) { } else if (child.imgPos && child.width && child.height) {
ctx.drawImage( ctx.drawImage(
img, img,
child.imgPos.x, child.imgPos.x,
child.imgPos.y, child.imgPos.y,
child.width, child.height, child.width, child.height,
0, 0, 0, 0,
child.width, child.height child.width, child.height
); );
} else if (child.style && child.w && child.h) { } else if (child.style && child.w && child.h) {
ctx.fillStyle = child.style.fill; ctx.fillStyle = child.style.fill;
ctx.fillRect(0, 0, child.w, child.h) ctx.fillRect(0, 0, child.w, child.h);
} else { } else {
ctx.drawImage(img, 0, 0); ctx.drawImage(img, 0, 0);
} }
} }
// Handle children with children // Handle children with children
if (child.children) { if (child.children) {
renderRec(child); renderRec(child);
} }
ctx.restore(); ctx.restore();
}) });
} }
if (clear) { if (clear) {
ctx.clearRect(0, 0, this.w, this.h); ctx.clearRect(0, 0, this.w, this.h);
} }
renderRec(container); renderRec(container);
} }
} }
module.exports = CanvasRenderer; module.exports = CanvasRenderer;

View File

@ -1,79 +1,79 @@
const math = require('./math'); const math = require("./math");
const Rect = require('../Rect'); const Rect = require("../Rect");
function addDebug(e) { function addDebug(e) {
e.children = e.children || []; e.children = e.children || [];
const bb = new Rect(e.w, e.h, { fill: "rgba(255, 0, 0, 0.3)" }); const bb = new Rect(e.w, e.h, { fill: "rgba(255, 0, 0, 0.3)" });
e.children.push(bb); e.children.push(bb);
if (e.hitBox) { if (e.hitBox) {
const { x, y, w, h } = e.hitBox; const { x, y, w, h } = e.hitBox;
const hb = new Rect(w, h, { fill: "rgba(255, 0, 0, 0.5)" }); const hb = new Rect(w, h, { fill: "rgba(255, 0, 0, 0.5)" });
hb.pos.x = x; hb.pos.x = x;
hb.pos.y = y; hb.pos.y = y;
e.children.push(hb); e.children.push(hb);
} }
return e; return e;
} }
function angle(a, b) { function angle(a, b) {
return math.angle(center(a), center(b)); return math.angle(center(a), center(b));
} }
function bounds(entity) { function bounds(entity) {
const { w, h, pos, hitBox } = entity; const { w, h, pos, hitBox } = entity;
const hit = hitBox || { x: 0, y: 0, w, h }; const hit = hitBox || { x: 0, y: 0, w, h };
return { return {
x: hit.x + pos.x, x: hit.x + pos.x,
y: hit.y + pos.y, y: hit.y + pos.y,
w: hit.w - 1, w: hit.w - 1,
h: hit.h - 1 h: hit.h - 1
}; };
} }
function center(entity) { function center(entity) {
const { pos, w, h } = entity; const { pos, w, h } = entity;
return { return {
x: pos.x + w / 2, x: pos.x + w / 2,
y: pos.y + h / 2 y: pos.y + h / 2
}; };
} }
function distance(a, b) { function distance(a, b) {
return math.distance(center(a), center(b)); return math.distance(center(a), center(b));
} }
function hit(e1, e2) { function hit(e1, e2) {
const a = bounds(e1); const a = bounds(e1);
const b = bounds(e2); const b = bounds(e2);
return ( return (
a.x + a.w >= b.x && a.x + a.w >= b.x &&
a.x <= b.x + b.w && a.x <= b.x + b.w &&
a.y + a.h >= b.y && a.y + a.h >= b.y &&
a.y <= b.y + b.h a.y <= b.y + b.h
); );
} }
function hits(entity, container, hitCallback) { function hits(entity, container, hitCallback) {
const a = bounds(entity); const a = bounds(entity);
container.map(e2 => { container.map(e2 => {
const b = bounds(e2); const b = bounds(e2);
if ( if (
a.x + a.w >= b.x && a.x + a.w >= b.x &&
a.x <= b.x + b.w && a.x <= b.x + b.w &&
a.y + a.h >= b.y && a.y + a.h >= b.y &&
a.y <= b.y + b.h a.y <= b.y + b.h
) { ) {
hitCallback(e2); hitCallback(e2);
} }
}); });
} }
module.exports = { module.exports = {
addDebug, addDebug,
angle, angle,
bounds, bounds,
center, center,
distance, distance,
hit, hit,
hits hits
}; };

View File

@ -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) { function rand(min, max) {
return Math.floor(randf(min, max)); return Math.floor(randf(min, max));
} }
function randf(min, max) { function randf(min, max) {
if (max == null) { if (max == null) {
max = min || 1; max = min || 1;
min = 0; min = 0;
} }
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;
} }
function randOneFrom(items) { function randOneFrom(items) {
return items[rand(items.length)]; return items[rand(items.length)];
} }
function randOneIn(max = 2) { function randOneIn(max = 2) {
return rand(0, max) === 0; return rand(0, max) === 0;
} }
module.exports = { module.exports = {
rand, angle,
randf, clamp,
randOneFrom, distance,
randOneIn rand,
randf,
randOneFrom,
randOneIn
}; };

1096
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,5 +17,8 @@
"name": "Arne van Iterson", "name": "Arne van Iterson",
"url": "https://gitea.arnweb.nl/arne/" "url": "https://gitea.arnweb.nl/arne/"
}, },
"license": "ISC" "license": "ISC",
"devDependencies": {
"eslint": "^6.8.0"
}
} }