generated from michael/webpack-base
941 lines
20 KiB
JavaScript
941 lines
20 KiB
JavaScript
var HOLD_TO_MOVE = false;
|
|
var PATH_DIAGONAL = true;
|
|
|
|
//Contstants
|
|
|
|
var tau = 6.283185307179586476925286766559;
|
|
var pi = 3.1415926535897932384626433832795;
|
|
var tau_over_4 = 1.57079632679;
|
|
var tau_over_8 = 0.785398163;
|
|
var sqrt_2 = 1.41421356237;
|
|
var sqrt_2_over_2 = 0.70710678118;
|
|
var dtime = 17;
|
|
var last_timestamp = 0;
|
|
|
|
//Key Constants
|
|
var K_W = 87;
|
|
var K_A = 65;
|
|
var K_S = 83;
|
|
var K_D = 68;
|
|
var K_UPARROW = 38;
|
|
var K_DOWNARROW = 40;
|
|
var K_LEFTARROW = 37;
|
|
var K_RIGHTARROW = 39;
|
|
|
|
//Prototypes
|
|
Array.prototype.max = function() {
|
|
return Math.max.apply(null, this);
|
|
};
|
|
|
|
Array.prototype.min = function() {
|
|
return Math.min.apply(null, this);
|
|
};
|
|
|
|
//Useful Functions
|
|
|
|
//Create a unique id
|
|
var p_id = 1;
|
|
function get_id() {
|
|
return p_id++;
|
|
}
|
|
|
|
//Make a Vec2 Faster
|
|
function vec2(x, y) {
|
|
return new Vec2(x, y);
|
|
}
|
|
|
|
function clamp(a, min, max) {
|
|
return Math.min(Math.max(a, min), max);
|
|
}
|
|
|
|
//Dampens a number based on a dt. The threshold is the minimum value before it just returns 0
|
|
function dampen(x, m, dt, threshold) {
|
|
var ret = Math.pow(m, dt) * x;
|
|
return (Math.abs(ret) > threshold ? ret : 0);
|
|
}
|
|
|
|
//Logs all arguments using the first one as the seperator
|
|
function logAll(sep) {
|
|
var s = "";
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
s += arguments[i] + sep;
|
|
}
|
|
console.log(s);
|
|
}
|
|
|
|
//Returns current time in milliseconds from 1970
|
|
function time() {
|
|
return (new Date()).getTime();
|
|
}
|
|
|
|
//Returns a random number between plus or minus x
|
|
function variance(x) {
|
|
return (Math.random() - 0.5) * 2 * x
|
|
}
|
|
|
|
//Returns a random number in [0, max)
|
|
function rand(max) {
|
|
return max * Math.random();
|
|
}
|
|
|
|
function randSelect(arr) {
|
|
var index = Math.floor(rand(arr.length));
|
|
if (index == arr.length) index = arr.length - 1;
|
|
return arr[index];
|
|
}
|
|
|
|
//Relative Position Calculation
|
|
|
|
function screenPos(pos) {
|
|
return pos.minus(cameraPos);
|
|
}
|
|
|
|
function worldPos(screenPos) {
|
|
return screenPos.plus(cameraPos);
|
|
}
|
|
|
|
//Draw Functions
|
|
|
|
function setFillStyle(style) {
|
|
ctx.fillStyle = style;
|
|
}
|
|
|
|
function setStrokeStyle(style) {
|
|
ctx.strokeStyle = style;
|
|
}
|
|
|
|
function setLineWidth(width) {
|
|
ctx.lineWidth = width;
|
|
}
|
|
|
|
/* Possible Values
|
|
* butt
|
|
* round
|
|
* square
|
|
*/
|
|
function setLineCap(lineCap) {
|
|
ctx.lineCap = lineCap;
|
|
}
|
|
|
|
function setFont(font) {
|
|
ctx.font = font;
|
|
}
|
|
|
|
/* Possible Values
|
|
* start (default)
|
|
* end
|
|
* center
|
|
* left
|
|
* right
|
|
*/
|
|
function setTextAlign(textalign) {
|
|
ctx.textAlign = textalign;
|
|
}
|
|
|
|
/* Possible Values
|
|
* alphabetic (default)
|
|
* top
|
|
* hanging
|
|
* middle
|
|
* ideographic
|
|
* bottom
|
|
*/
|
|
function setTextBaseline(textbaseline) {
|
|
ctx.textBaseline = textbaseline;
|
|
}
|
|
|
|
function measureText(text) {
|
|
return ctx.measureText(text);
|
|
}
|
|
|
|
function fillCircle(pos, r) {
|
|
ctx.beginPath();
|
|
ctx.arc(pos.x - cameraPos.x, pos.y - cameraPos.y, r, 0, tau, false);
|
|
ctx.fill();
|
|
}
|
|
|
|
function fillRect(x, y, w, h) {
|
|
ctx.fillRect(x - cameraPos.x, y - cameraPos.y, w, h);
|
|
}
|
|
|
|
function fillImage(x, y, w, h, img) {
|
|
ctx.drawImage(img, x - cameraPos.x, y - cameraPos.y, w, h);
|
|
}
|
|
|
|
//Fills an image rotated **With Center as Origin**
|
|
function fillImageRotated(x, y, w, h, img, angle) {
|
|
ctx.save();
|
|
ctx.translate(x - cameraPos.x, y - cameraPos.y);
|
|
ctx.rotate(angle);
|
|
ctx.drawImage(img, -w / 2, -h / 2, w, h);
|
|
ctx.restore();
|
|
}
|
|
|
|
//Fills an animated image rotated **With Center as Origin**
|
|
//Works with vertically animated images
|
|
function fillAnimatedImageRotated(x, y, w, h, img, angle, frame, wpf, hpf) {
|
|
ctx.save();
|
|
ctx.translate(x, y);
|
|
ctx.rotate(angle);
|
|
ctx.drawImage(img, 0, frame * hpf, wpf, hpf, -w / 2, -h / 2, w, h);
|
|
ctx.restore();
|
|
}
|
|
|
|
function fillTileRect(x, y) {
|
|
fillRect(x * tileSize.x, y * tileSize.y, tileSize.x + tileSizeDrawDelta, tileSize.y * tileSizeDrawDelta);
|
|
}
|
|
|
|
function fillRectRel(x, y, w, h) {
|
|
ctx.fillRect(x, y, w, h);
|
|
}
|
|
|
|
function fillText(text, x, y) {
|
|
ctx.fillText(text, x - cameraPos.x, y - cameraPos.y);
|
|
}
|
|
|
|
function fillTextRel(text, x, y) {
|
|
ctx.fillText(text, x, y);
|
|
}
|
|
|
|
function drawLine(x1, y1, x2, y2) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x1 - cameraPos.x, y1 - cameraPos.y);
|
|
ctx.lineTo(x2 - cameraPos.x, y2 - cameraPos.y);
|
|
ctx.stroke();
|
|
}
|
|
|
|
//Draws an arrow from p1 to p2 with a 90 degree point of length n
|
|
function drawArrow(x1, y1, x2, y2, n = 10) {
|
|
drawLine(x1, y1, x2, y2);
|
|
var v = vec2(x1 - x2, y1 - y2);
|
|
var nub = Vec2.normalize(v.rotate(tau_over_8)).times(n);
|
|
drawLine(x2, y2, x2 + nub.x, y2 + nub.y);
|
|
nub = nub.rotate(-tau_over_4);
|
|
drawLine(x2, y2, x2 + nub.x, y2 + nub.y);
|
|
}
|
|
|
|
function clearCanvas() {
|
|
ctx.fillRect(0, 0, c.width, c.height);
|
|
}
|
|
|
|
//Other Draw Functions
|
|
function drawStatusBar(x, y, w, h, across, bgColor, fgColor) {
|
|
setFillStyle(bgColor);
|
|
fillRect(x, y, w, h);
|
|
setFillStyle(fgColor);
|
|
fillRect(x, y, across * w, h);
|
|
}
|
|
|
|
function fillRoundedRect(x, y, w, h, radius) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x + radius, y);
|
|
//top
|
|
ctx.lineTo(x + w - radius, y);
|
|
//top right
|
|
ctx.quadraticCurveTo(x + w, y, x + w, y + radius);
|
|
//right
|
|
ctx.lineTo(x + w, y + h - radius);
|
|
//bottom right
|
|
ctx.quadraticCurveTo(x + w, y + h, x + w - radius, y + h);
|
|
//bottom
|
|
ctx.lineTo(x + radius, y + h);
|
|
//bottom left
|
|
ctx.quadraticCurveTo(x, y + h, x, y + h - radius);
|
|
//left
|
|
ctx.lineTo(x, y + radius);
|
|
//top left
|
|
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
|
|
//Returns text in array of lines with measureText x of width or less
|
|
//Text is split by word (seperator is spaces)
|
|
function getTextLines(text, width) {
|
|
var ret = [];
|
|
var lineStart = 0;
|
|
var prev = 0;
|
|
var iter = 0;
|
|
while (true) {
|
|
iter = text.indexOf(" ", iter + 1);
|
|
if (iter == -1) {
|
|
ret.push(text.slice(lineStart, text.length));
|
|
return ret;
|
|
}
|
|
var len = measureText(text.slice(lineStart, iter)).width;
|
|
if (len > width) {
|
|
ret.push(text.slice(lineStart, prev));
|
|
lineStart = iter = prev + 1;
|
|
} else {
|
|
prev = iter;
|
|
}
|
|
}
|
|
}
|
|
|
|
//fills a text box with fixed width
|
|
//s = the fixed size
|
|
//px, py = padding in x / y
|
|
//lh = line height
|
|
//ls = line spacing
|
|
//radius = rounded radius (optional)
|
|
//text top left aligned.
|
|
function fillTextBox(text, fgColor, bgColor, x, y, px, py, w, lh, ls, alpha, radius) {
|
|
ctx.save();
|
|
ctx.globalAlpha = alpha;
|
|
var textLines = text.split("\n");
|
|
var drawnTextLines = [];
|
|
for (var i = 0; i < textLines.length; i++) {
|
|
drawnTextLines = drawnTextLines.concat(getTextLines(textLines[i], w - py * 2));
|
|
}
|
|
var totalHeight = drawnTextLines.length * (lh + ls) - ls + py * 2;
|
|
setFillStyle(bgColor);
|
|
if (typeof(radius) == "number") {
|
|
fillRoundedRect(x, y, w, totalHeight, radius);
|
|
} else {
|
|
fillRect(x, y, w, totalHeight);
|
|
}
|
|
setFillStyle(fgColor);
|
|
setTextAlign("left");
|
|
setTextBaseline("top");
|
|
var offsetY = y + py;
|
|
for (var i = 0; i < drawnTextLines.length; i++) {
|
|
fillText(drawnTextLines[i], x + px, offsetY);
|
|
offsetY += lh + ls;
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
//Drawable Classes
|
|
|
|
class Slider {
|
|
constructor(pos, width, lineWidth, lineColor, radius, circleColor, value, minVal, maxVal) {
|
|
this.pos = pos.copy();
|
|
this.width = width;
|
|
this.lineWidth = lineWidth;
|
|
this.lineColor = lineColor;
|
|
this.radius = radius;
|
|
this.circleColor = circleColor;
|
|
this.value = value;
|
|
this.minVal = minVal;
|
|
this.maxVal = maxVal;
|
|
this.transitioning = false;
|
|
}
|
|
|
|
update(t) {
|
|
if (mousePressed) {
|
|
if (pointInRect(mousePos, this.pos.minus(vec2(0, this.radius)), vec2(this.width, this.radius * 2))) {
|
|
this.transitioning = true;
|
|
}
|
|
}
|
|
if (!mouseDown) {
|
|
this.transitioning = false;
|
|
}
|
|
if (this.transitioning) {
|
|
this.value = (((clamp(mousePos.x, this.pos.x, this.pos.x + this.width) - this.pos.x) / this.width) * (this.maxVal - this.minVal) + this.minVal);
|
|
}
|
|
}
|
|
|
|
draw(t) {
|
|
//Draw Line
|
|
setLineWidth(this.lineWidth);
|
|
setLineCap("round");
|
|
setStrokeStyle(this.lineColor);
|
|
drawLine(this.pos.x, this.pos.y, this.pos.x + this.width, this.pos.y);
|
|
setFillStyle(this.circleColor);
|
|
fillCircle(vec2(this.pos.x + (this.width * (this.value - this.minVal) / (this.maxVal - this.minVal)), this.pos.y), this.radius);
|
|
}
|
|
}
|
|
|
|
//Converts a world to a grid for pathing
|
|
|
|
//False = Blocked, True = Open
|
|
function gridFromWorld(world) {
|
|
grid = [];
|
|
world.forEach((o, i) => {
|
|
grid.push([]);
|
|
o.forEach((l) => {
|
|
if (l == "#") {
|
|
grid[i].push(false);
|
|
} else {
|
|
grid[i].push(true);
|
|
}
|
|
});
|
|
});
|
|
return grid;
|
|
}
|
|
|
|
//Grid Position Calculation
|
|
|
|
//Center of Tile
|
|
function gridPosToWorldPos(pos) {
|
|
return vec2(pos.x * tileSize.x + tileSize.x / 2, pos.y * tileSize.y + tileSize.y / 2);
|
|
}
|
|
|
|
//Top Left Of Tile
|
|
function gridPosToWorldPosTL(pos) {
|
|
return vec2(pos.x * tileSize.x, pos.y * tileSize.y);
|
|
}
|
|
|
|
function worldPosToGridPos(pos) {
|
|
return vec2(
|
|
Math.floor(pos.x / tileSize.x),
|
|
Math.floor(pos.y / tileSize.y)
|
|
);
|
|
}
|
|
|
|
//Path Finding
|
|
|
|
//Returns the value of a grid at the specified position
|
|
function gridAt(grid, pos) {
|
|
if (pos.y < 0 || pos.x < 0 || pos.y >= grid.length || pos.x >= grid[pos.y].length) {
|
|
return false;
|
|
}
|
|
return grid[pos.y][pos.x];
|
|
}
|
|
|
|
//Returns neighbors of a position node
|
|
function getNeighbors(grid, pos) {
|
|
ret = [];
|
|
if (gridAt(grid, pos.plus(vec2(1, 0)))) {
|
|
ret.push(pos.plus(vec2(1, 0)));
|
|
}
|
|
if (gridAt(grid, pos.plus(vec2(-1, 0)))) {
|
|
ret.push(pos.plus(vec2(-1, 0)));
|
|
}
|
|
if (gridAt(grid, pos.plus(vec2(0, 1)))) {
|
|
ret.push(pos.plus(vec2(0, 1)));
|
|
}
|
|
if (gridAt(grid, pos.plus(vec2(0, -1)))) {
|
|
ret.push(pos.plus(vec2(0, -1)));
|
|
}
|
|
if (PATH_DIAGONAL) {
|
|
//TODO
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function heuristic(a, b) {
|
|
return Vec2.distance(a, b);
|
|
}
|
|
|
|
function createPath(cameFrom, a, start) {
|
|
ret = [a];
|
|
while (!a.equals(start)) {
|
|
a = cameFrom[a.h()];
|
|
ret.unshift(a);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function getPath(grid, start, goal) {
|
|
//Returns empty list on failure otherwise a list of grid coords
|
|
gScore = {};
|
|
gScore[start.h()] = 0;
|
|
|
|
fScore = {};
|
|
fScore[start.h()] = heuristic(start, goal);
|
|
|
|
openSet = [start];
|
|
closedSet = [];
|
|
|
|
cameFrom = {};
|
|
|
|
success = false;
|
|
|
|
while (openSet.length != 0) {
|
|
openf = openSet.map((o) => { return fScore[o.h()]; });
|
|
var ci = openf.indexOf(openf.min());
|
|
var c = openSet[ci].copy();
|
|
if (c.equals(goal)) {
|
|
//return finished path
|
|
return createPath(cameFrom, c, start);
|
|
}
|
|
openSet.splice(ci, 1);
|
|
closedSet.push(c);
|
|
//Possible Speedup: Also Make closedSets set grid values to
|
|
//blocked once they are in the closedSet
|
|
var neighbors = getNeighbors(grid, c);
|
|
for (var i = 0; i < neighbors.length; i++) {
|
|
var o = neighbors[i];
|
|
if (o.inArray(closedSet)) {
|
|
//Path already evaluated
|
|
continue;
|
|
}
|
|
if (!o.inArray(openSet)) {
|
|
openSet.push(o);
|
|
}
|
|
var tgScore = gScore[c.h()] + Vec2.distance(c, o);
|
|
var cgScore = gScore[o.h()];
|
|
if (typeof(cgScore) !== "undefined" && tgScore > cgScore) {
|
|
//Not a better path
|
|
continue;
|
|
}
|
|
cameFrom[o.h()] = c;
|
|
gScore[o.h()] = tgScore;
|
|
fScore[o.h()] = tgScore + heuristic(o, goal);
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
function getWorldPath(start, goal) {
|
|
var path = getPath(worldGrid, worldPosToGridPos(start), worldPosToGridPos(goal));
|
|
path = path.map((o) => { return gridPosToWorldPos(o); });
|
|
if (path.length > 0) {
|
|
path.push(goal);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
//Collision Detection
|
|
|
|
function pointInRect(pos, rPos, rSize) {
|
|
return (
|
|
pos.x >= rPos.x &&
|
|
pos.y >= rPos.y &&
|
|
pos.x <= rPos.x + rSize.x &&
|
|
pos.y <= rPos.y + rSize.y
|
|
);
|
|
}
|
|
|
|
function rectInRect(r1Pos, r1Size, r2Pos, r2Size) {
|
|
return (
|
|
pointInRect(r2Pos, r1Pos, r1Size) ||
|
|
pointInRect(r2Pos.plusv(r2Size.x, 0), r1Pos, r1Size) ||
|
|
pointInRect(r2Pos.plusv(0, r2Size.y), r1Pos, r1Size) ||
|
|
pointInRect(r2Pos.plus(r2Size), r1Pos, r1Size) ||
|
|
|
|
pointInRect(r1Pos, r2Pos, r2Size) ||
|
|
pointInRect(r1Pos.plusv(r1Size.x, 0), r2Pos, r2Size) ||
|
|
pointInRect(r1Pos.plusv(0, r1Size.y), r2Pos, r2Size) ||
|
|
pointInRect(r1Pos.plus(r1Size), r2Pos, r2Size)
|
|
);
|
|
}
|
|
|
|
function circleInRect(cPos, cRadius, rPos, rSize) {
|
|
return (
|
|
//Rect Points In Circle
|
|
pointInCircle(rPos, cPos, cRadius) ||
|
|
pointInCircle(rPos.plusv(rSize.x, 0), cPos, cRadius) ||
|
|
pointInCircle(rPos.plusv(0, rSize.y), cPos, cRadius) ||
|
|
pointInCircle(rPos.plus(rSize), cPos, cRadius) ||
|
|
//Circle Points In Rect
|
|
pointInRect(cPos.minusv(cRadius, 0), rPos, rSize) ||
|
|
pointInRect(cPos.minusv(-cRadius, 0), rPos, rSize) ||
|
|
pointInRect(cPos.minusv(0, cRadius), rPos, rSize) ||
|
|
pointInRect(cPos.minusv(0, -cRadius), rPos, rSize)
|
|
);
|
|
}
|
|
|
|
function lineFunc(x1, y1, x2, y2) {
|
|
return (x, y) => { return (y2 - y1) * x + (x1 - x2) * y + (x2 * y1 - x1 * y2); };
|
|
}
|
|
|
|
function collideLineRect(lx1, ly1, lx2, ly2, rx1, ry1, rx2, ry2) {
|
|
//Projected No Intersection
|
|
|
|
if (lx1 > rx1 && lx1 > rx2 && lx2 > rx1 && lx2 > rx2) {
|
|
return false;
|
|
}
|
|
if (lx1 < rx1 && lx1 < rx2 && lx2 < rx1 && lx2 < rx2) {
|
|
return false;
|
|
}
|
|
if (ly1 > ry1 && ly1 > ry2 && ly2 > ry1 && ly2 > ry2) {
|
|
return false;
|
|
}
|
|
if (ly1 < ry1 && ly1 < ry2 && ly2 < ry1 && ly2 < ry2) {
|
|
return false;
|
|
}
|
|
|
|
//All points same side of line
|
|
var f = lineFunc(lx1, lx2, ly1, ly2);
|
|
p = 0;
|
|
n = 0;
|
|
var a = f(rx1, ry1);
|
|
if (a > 0) {
|
|
p += 1;
|
|
} else if (a < 0) {
|
|
n += 1;
|
|
} else {
|
|
return true;
|
|
}
|
|
a = f(rx1, ry2);
|
|
if (a > 0) {
|
|
p += 1;
|
|
} else if (a < 0) {
|
|
n += 1;
|
|
} else {
|
|
return true;
|
|
}
|
|
a = f(rx2, ry1);
|
|
if (a > 0) {
|
|
p += 1;
|
|
} else if (a < 0) {
|
|
n += 1;
|
|
} else {
|
|
return true;
|
|
}
|
|
a = f(rx2, ry2);
|
|
if (a > 0) {
|
|
p += 1;
|
|
} else if (a < 0) {
|
|
n += 1;
|
|
} else {
|
|
return true;
|
|
}
|
|
if (p == 4 || n == 4) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function isClear(grid, start, end, radius) {
|
|
//Returns whether the path is clear of world tiles from the start to the end
|
|
var rad = typeof(radius) === "undefined" ? 0 : radius;
|
|
var startGrid = worldPosToGridPos(start);
|
|
var endGrid = worldPosToGridPos(end);
|
|
var sx = 0;//Math.max(Math.min(startGrid.x, endGrid.x), 0);
|
|
var ex = grid[0].length - 1;//Math.min(Math.max(startGrid.x, endGrid.x), grid[0].length - 1);
|
|
var sy = 0;//Math.max(Math.min(startGrid.y, endGrid.y), 0);
|
|
var ey = grid.length - 1;//Math.min(Math.max(startGrid.y, endGrid.y), grid.length - 1);
|
|
var dr = null;
|
|
var s1 = null;
|
|
var s2 = null;
|
|
var e1 = null;
|
|
var e2 = null;
|
|
if (rad != 0) {
|
|
var d = end.minus(start);
|
|
d = Vec2.normalize(d).times(rad);
|
|
dr = vec2(-d.y, d.x);
|
|
s1 = start.plus(dr);
|
|
s2 = start.minus(dr);
|
|
e1 = end.plus(dr);
|
|
e2 = end.minus(dr);
|
|
logAll(", ", dr.str(), start.str(), s1.str(), s2.str(), end.str(), e1.str(), e2.str());
|
|
}
|
|
for (var x = sx; x <= ex; x++) {
|
|
for (var y = sy; y <= ey; y++) {
|
|
if (!gridAt(grid, vec2(x, y))) {
|
|
var a = gridPosToWorldPosTL(vec2(x, y));
|
|
var b = gridPosToWorldPosTL(vec2(x + 1, y + 1));
|
|
if (collideLineRect(start.x, start.y, end.x, end.y, a.x, a.y, b.x, b.y)) {
|
|
return false;
|
|
}
|
|
if (rad != 0) {
|
|
if (collideLineRect(s1.x, s1.y, e1.x, e1.y, a.x, a.y, b.x, b.y)) {
|
|
return false;
|
|
}
|
|
if (collideLineRect(s2.x, s2.y, e2.x, e2.y, a.x, a.y, b.x, b.y)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function circleOnScreen(pos, radius) {
|
|
return (
|
|
pos.x - cameraPos.x + radius >= 0 &&
|
|
pos.y - cameraPos.y + radius >= 0 &&
|
|
pos.x - cameraPos.x - radius <= c.width &&
|
|
pos.y - cameraPos.y - radius <= c.height
|
|
);
|
|
}
|
|
|
|
function squareOnScreen(pos, size) {
|
|
return (
|
|
pos.x - cameraPos.x + size.x >= 0 &&
|
|
pos.y - cameraPos.y + size.x >= 0 &&
|
|
pos.x - cameraPos.x <= c.width &&
|
|
pos.y - cameraPos.y <= c.height
|
|
);
|
|
}
|
|
|
|
function circleInCircle(apos, arad, bpos, brad) {
|
|
return circleCollision(apos, arad, bpos, brad);
|
|
}
|
|
|
|
function circleCollision(apos, arad, bpos, brad) {
|
|
var r = arad + brad;
|
|
var r2 = r * r;
|
|
return apos.distanceToSqrd(bpos) < r2;
|
|
}
|
|
|
|
//Circle collision for 2 objects with a pos and radius variable
|
|
function collision(a, b) {
|
|
var r = (a.radius + b.radius);
|
|
var r2 = r * r;
|
|
return a.pos.distanceToSqrd(b.pos) < r2;
|
|
}
|
|
|
|
//Vector with 2 dimensions
|
|
|
|
class Vec2 {
|
|
constructor(x, y) {
|
|
if (typeof(x) == 'undefined') x = 0;
|
|
if (typeof(y) == 'undefined') y = 0;
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
copy() {
|
|
return vec2(this.x, this.y);
|
|
}
|
|
|
|
setTo(other) {
|
|
this.x = other.x;
|
|
this.y = other.y;
|
|
}
|
|
|
|
setAngle(radians) {
|
|
var mag = this.length();
|
|
this.x = mag * Math.cos(radians)
|
|
this.y = mag * Math.sin(radians);
|
|
}
|
|
|
|
str(decimals) {
|
|
return "< " + this.x.toFixed(decimals) + ", " + this.y.toFixed(decimals) + " >";
|
|
}
|
|
|
|
h() {
|
|
return this.x + "!" + this.y
|
|
}
|
|
|
|
isZero() {
|
|
return this.x == 0 && this.y == 0;
|
|
}
|
|
|
|
equals(other) {
|
|
return this.x == other.x && this.y == other.y;
|
|
}
|
|
|
|
plus(other) {
|
|
return vec2(this.x + other.x, this.y + other.y);
|
|
}
|
|
|
|
modx(n) {
|
|
var v = vec2(this.x % n, this.y);
|
|
if (v.x < 0) {
|
|
v.x += n;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
mody(n) {
|
|
var v = vec2(this.x, this.y % n);
|
|
if (v.y < 0) {
|
|
v.y += n;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
minus(other) {
|
|
return vec2(this.x - other.x, this.y - other.y);
|
|
}
|
|
|
|
rotate(radians) {
|
|
//Get mag and angle, add degrees to angle, return from mag and angle
|
|
var angle = Vec2.angle(this) + radians;
|
|
var mag = this.length();
|
|
return vec2(mag * Math.cos(angle), mag * Math.sin(angle));
|
|
}
|
|
|
|
times(scalar) {
|
|
if (typeof(scalar) !== "number") {
|
|
console.log("WARNING: Trying to multiply vector by non-number");
|
|
return this;
|
|
}
|
|
return vec2(this.x * scalar, this.y * scalar);
|
|
}
|
|
|
|
inArray(arr) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
if (this.equals(arr[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
distanceTo(other) {
|
|
return Vec2.distance(this, other);
|
|
}
|
|
|
|
distanceToSqrd(other) {
|
|
return Vec2.distanceSqrd(this, other);
|
|
}
|
|
|
|
length() {
|
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
}
|
|
|
|
lengthSqrd() {
|
|
return this.x * this.x + this.y * this.y;
|
|
}
|
|
|
|
static distance(a, b) {
|
|
var m = a.minus(b);
|
|
return Math.sqrt(m.x * m.x + m.y * m.y);
|
|
}
|
|
|
|
static distanceSqrd(a, b) {
|
|
var m = a.minus(b);
|
|
return m.x * m.x + m.y * m.y;
|
|
}
|
|
|
|
static length(v) {
|
|
return Math.sqrt(v.x * v.x + v.y * v.y);
|
|
}
|
|
|
|
static normalize(v) {
|
|
var d = Vec2.length(v);
|
|
if (d == 0) {
|
|
return vec2(sqrt_2_over_2, sqrt_2_over_2);
|
|
}
|
|
return vec2(v.x / d, v.y / d);
|
|
}
|
|
|
|
static jiggle(v, maxdist) {
|
|
//Changes the components of v by random amounts up to +/- maxdist
|
|
//This is a square jiggle.
|
|
return vec2(v.x + variance(maxdist), v.y + variance(maxdist));
|
|
}
|
|
|
|
static angle(v) {
|
|
if (v.x == 0) {
|
|
if (v.y == 0) {
|
|
return 0;
|
|
} else if (v.y > 0) {
|
|
return tau_over_4;
|
|
} else {
|
|
return -tau_over_4;
|
|
}
|
|
} else if (v.x > 0) {
|
|
return Math.atan(v.y / v.x);
|
|
} else {
|
|
return Math.atan(v.y / v.x) + pi;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Canvas Stuff
|
|
var c;
|
|
var ctx;
|
|
|
|
//Engine Stuff
|
|
|
|
//Key Maps - true = down, false = up
|
|
//Stored as string -> bool map
|
|
var keys = {}
|
|
var keysPressed = {}
|
|
|
|
var mousePos = vec2();
|
|
var mouseDown = false;
|
|
var mousePressed = null;
|
|
var mouseMiddleDown = false;
|
|
var mouseMiddlePressed = null;
|
|
var mouseRightDown = false;
|
|
var mouseRightPressed = null;
|
|
|
|
var cameraPos = vec2();
|
|
|
|
//Input
|
|
|
|
window.onkeydown = keyDown;
|
|
window.onkeyup = keyUp;
|
|
window.onmousemove = mouseMove;
|
|
window.onmousedown = md;
|
|
window.onmouseup = mu;
|
|
window.onload = load;
|
|
|
|
window.oncontextmenu = function(e) { e.preventDefault(); };
|
|
|
|
function load() {
|
|
initCanvas();
|
|
initGame();
|
|
startLoop();
|
|
}
|
|
|
|
function keyDown(e) {
|
|
keys[e.keyCode + ""] = true;
|
|
keysPressed[e.keyCode + ""] = true;
|
|
}
|
|
|
|
function keyUp(e) {
|
|
keys[e.keyCode + ""] = false;
|
|
keysPressed[e.keyCode + ""] = false;
|
|
}
|
|
|
|
function mouseMove(e) {
|
|
mousePos.x = e.clientX;
|
|
mousePos.y = e.clientY;
|
|
}
|
|
|
|
//Mouse Down
|
|
function md(e) {
|
|
if (e.button == 0) {
|
|
mouseDown = true;
|
|
mousePressed = true;
|
|
} else if (e.button == 1) {
|
|
mouseMiddleDown = true;
|
|
mouseMiddlePressed = true;
|
|
} else if (e.button == 2) {
|
|
mouseRightDown = true;
|
|
mouseRightPressed = true;
|
|
}
|
|
//otherwise another mouse button
|
|
}
|
|
|
|
//Mouse Up
|
|
function mu(e) {
|
|
if (e.button == 0) {
|
|
mouseDown = false;
|
|
mousePressed = false;
|
|
} else if (e.button == 1) {
|
|
mouseMiddleDown = false;
|
|
mouseMiddlePressed = false;
|
|
} else if (e.button == 2) {
|
|
mouseRightDown = false;
|
|
mouseRightPressed = false;
|
|
}
|
|
//otherwise another mouse button
|
|
}
|
|
|
|
function initCanvas() {
|
|
c = document.getElementById("game");
|
|
c.width = document.body.clientWidth;
|
|
c.height = document.body.clientHeight;
|
|
|
|
ctx = c.getContext("2d");
|
|
}
|
|
|
|
function initGame() {
|
|
console.log("Base Game Init");
|
|
}
|
|
|
|
function update(timestamp) {
|
|
}
|
|
|
|
function draw(t) {
|
|
ctx.fillStyle = "#ffffff";
|
|
clearCanvas();
|
|
}
|
|
|
|
function startLoop() {
|
|
window.requestAnimationFrame(loop);
|
|
}
|
|
|
|
function loop(timestamp) {
|
|
dtime = timestamp - last_timestamp;
|
|
last_timestamp = timestamp;
|
|
update(timestamp, dtime);
|
|
draw(timestamp, dtime);
|
|
keysPressed = {};
|
|
mousePressed = null;
|
|
mouseMiddlePressed = null;
|
|
mouseRightPressed = null;
|
|
window.requestAnimationFrame(loop);
|
|
}
|