rename files

This commit is contained in:
ntr 2019-05-25 15:30:26 +10:00
parent 760106e0e7
commit 6a20e72a64
66 changed files with 0 additions and 9546 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
package-lock.json
node_modules/
dist/
.cache/
assets/molecules

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,444 +0,0 @@
{
"textures": [
{
"format": "RGBA8888",
"size": {
"w": 1024,
"h": 1024
},
"scale": 1,
"frames":[
{
"filename":"sprite0",
"frame":{
"x":655,
"y":100,
"w":110,
"h":97
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":110,
"h":97
},
"sourceSize":{
"w":110,
"h":97
}
},
{
"filename":"sprite1",
"frame":{
"x":180,
"y":105,
"w":140,
"h":88
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":88
},
"sourceSize":{
"w":140,
"h":88
}
},
{
"filename":"sprite2",
"frame":{
"x":20,
"y":106,
"w":140,
"h":86
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":86
},
"sourceSize":{
"w":140,
"h":86
}
},
{
"filename":"sprite3",
"frame":{
"x":475,
"y":106,
"w":150,
"h":86
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":150,
"h":86
},
"sourceSize":{
"w":150,
"h":86
}
},
{
"filename":"sprite4",
"frame":{
"x":330,
"y":112,
"w":140,
"h":74
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":74
},
"sourceSize":{
"w":140,
"h":74
}
},
{
"filename":"sprite5",
"frame":{
"x":198,
"y":274,
"w":104,
"h":110
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":104,
"h":110
},
"sourceSize":{
"w":104,
"h":110
}
},
{
"filename":"sprite6",
"frame":{
"x":342,
"y":279,
"w":116,
"h":100
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":116,
"h":100
},
"sourceSize":{
"w":116,
"h":100
}
},
{
"filename":"sprite7",
"frame":{
"x":480,
"y":283,
"w":140,
"h":92
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":92
},
"sourceSize":{
"w":140,
"h":92
}
},
{
"filename":"sprite8",
"frame":{
"x":24,
"y":284,
"w":132,
"h":90
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":132,
"h":90
},
"sourceSize":{
"w":132,
"h":90
}
},
{
"filename":"sprite9",
"frame":{
"x":645,
"y":287,
"w":130,
"h":84
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":130,
"h":84
},
"sourceSize":{
"w":130,
"h":84
}
},
{
"filename":"sprite10",
"frame":{
"x":514,
"y":434,
"w":74,
"h":110
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":74,
"h":110
},
"sourceSize":{
"w":74,
"h":110
}
},
{
"filename":"sprite11",
"frame":{
"x":222,
"y":439,
"w":56,
"h":100
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":56,
"h":100
},
"sourceSize":{
"w":56,
"h":100
}
},
{
"filename":"sprite12",
"frame":{
"x":667,
"y":439,
"w":86,
"h":100
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":86,
"h":100
},
"sourceSize":{
"w":86,
"h":100
}
},
{
"filename":"sprite13",
"frame":{
"x":20,
"y":451,
"w":140,
"h":74
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":74
},
"sourceSize":{
"w":140,
"h":74
}
},
{
"filename":"sprite14",
"frame":{
"x":335,
"y":453,
"w":130,
"h":72
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":130,
"h":72
},
"sourceSize":{
"w":130,
"h":72
}
},
{
"filename":"sprite15",
"frame":{
"x":493,
"y":619,
"w":114,
"h":100
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":114,
"h":100
},
"sourceSize":{
"w":114,
"h":100
}
},
{
"filename":"sprite16",
"frame":{
"x":180,
"y":626,
"w":140,
"h":86
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":86
},
"sourceSize":{
"w":140,
"h":86
}
},
{
"filename":"sprite17",
"frame":{
"x":640,
"y":632,
"w":140,
"h":94
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":140,
"h":94
},
"sourceSize":{
"w":140,
"h":94
}
},
{
"filename":"sprite18",
"frame":{
"x":329,
"y":635,
"w":141,
"h":88
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":141,
"h":88
},
"sourceSize":{
"w":141,
"h":88
}
},
{
"filename":"sprite19",
"frame":{
"x":25,
"y":641,
"w":130,
"h":76
},
"rotated":false,
"trimmed":false,
"spriteSourceSize":{
"x":0,
"y":0,
"w":130,
"h":76
},
"sourceSize":{
"w":130,
"h":76
}
}
],
"meta":{
"app":"https://www.leshylabs.com/apps/sstool/",
"version":"Leshy SpriteSheet Tool v0.8.4",
"image":"spritesheet.png",
"size":{
"w":800,
"h":800
},
"scale":1
}
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
width="13.8889in" height="13.8889in"
viewBox="0 0 1000 1000">
<path id="Unnamed"
fill="none" stroke="black" stroke-width="1"
d="M 250.00,-1.00
C 250.00,-1.00 0.00,300.00 0.00,300.00
0.00,300.00 1000.00,300.00 1000.00,300.00
1000.00,300.00 750.00,2.00 750.00,2.00M 500.00,300.00
C 500.00,300.00 500.00,700.00 500.00,700.00M 250.00,1000.00
C 250.00,1000.00 0.00,700.00 0.00,700.00
0.00,700.00 999.00,700.00 1000.00,700.00
1001.00,700.00 750.00,1000.00 750.00,1000.00" />
</svg>

Before

Width:  |  Height:  |  Size: 811 B

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
width="13.8889in" height="13.8889in"
viewBox="0 0 1000 1000">
<path id="c2"
fill="none" stroke="black" stroke-width="1"
d="M 250.00,-1.00
C 250.00,-1.00 250.00,300.00 250.00,300.00
250.00,300.00 750.00,299.00 750.00,299.00
750.00,299.00 750.00,2.00 750.00,2.00M 500.00,100.00
C 500.00,100.00 500.00,300.00 500.00,300.00
500.00,300.00 500.00,700.00 500.00,700.00M 250.00,1000.00
C 250.00,1000.00 500.00,700.00 500.00,700.00
500.00,700.00 750.00,1000.00 750.00,1000.00M 750.00,700.00
C 750.00,700.00 500.00,400.00 500.00,400.00
500.00,400.00 249.00,699.00 249.00,699.00" />
</svg>

Before

Width:  |  Height:  |  Size: 938 B

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
width="13.8889in" height="13.8889in"
viewBox="0 0 1000 1000">
<path id="c3"
fill="none" stroke="black" stroke-width="1"
d="M 250.00,-1.00
C 250.00,-1.00 250.00,300.00 250.00,300.00
250.00,300.00 750.00,299.00 750.00,299.00
750.00,299.00 750.00,2.00 750.00,2.00M 500.00,300.00
C 500.00,300.00 500.00,700.00 500.00,700.00M 250.00,1000.00
C 250.00,1000.00 250.00,700.00 250.00,700.00
250.00,700.00 500.00,700.00 500.00,700.00
500.00,700.00 750.00,700.00 750.00,700.00
750.00,700.00 750.00,1000.00 750.00,1000.00M 250.00,500.00
C 250.00,500.00 750.00,500.00 750.00,500.00" />
</svg>

Before

Width:  |  Height:  |  Size: 938 B

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -1,24 +0,0 @@
/*@font-face {
font-family: 'nowayregular';
src: url('./assets/fonts/noway-regular-webfont.eot');
src: url('./assets/fonts/noway-regular-webfont.eot?#iefix') format('embedded-opentype'),
url('./assets/fonts/noway-regular-webfont.woff2') format('woff2'),
url('./assets/fonts/noway-regular-webfont.woff') format('woff'),
url('./assets/fonts/noway-regular-webfont.ttf') format('truetype'),
url('./assets/fonts/noway-regular-webfont.svg#nowayregular') format('svg');
font-weight: normal;
font-style: normal;
}
*/
body {
background-color: #181818;
}
canvas{
display:block;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

View File

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>constructs.gg - mnml pvp atbs</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="./node_modules/izitoast/dist/css/iziToast.min.css"></script>
<link href="https://fonts.googleapis.com/css?family=Jura" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
<script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
</head>
</head>
<body>
</body>
<script src="./index.js"></script>
</html>

View File

@ -1,4 +0,0 @@
require('./constructs.css');
// kick it off
require('./src/main');

View File

@ -1,189 +0,0 @@
// http://mrl.nyu.edu/~perlin/noise/
const ImprovedNoise = function () {
const p = [151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10,
23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87,
174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211,
133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208,
89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5,
202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119,
248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205,
93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180];
for (let i = 0; i < 256; i++) {
p[256 + i] = p[i];
}
function fade(t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
function lerp(t, a, b) {
return a + t * (b - a);
}
function grad(hash, x, y, z) {
const h = hash & 15;
const u = h < 8 ? x : y; const
v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
return {
noise(x, y, z) {
const floorX = Math.floor(x); const floorY = Math.floor(y); const
floorZ = Math.floor(z);
const X = floorX & 255; const Y = floorY & 255; const
Z = floorZ & 255;
x -= floorX;
y -= floorY;
z -= floorZ;
const xMinus1 = x - 1; const yMinus1 = y - 1; const
zMinus1 = z - 1;
const u = fade(x); const v = fade(y); const
w = fade(z);
const A = p[X] + Y; const AA = p[A] + Z; const AB = p[A + 1] + Z; const B = p[X + 1] + Y; const BA = p[B] + Z; const
BB = p[B + 1] + Z;
return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),
grad(p[BA], xMinus1, y, z)),
lerp(u, grad(p[AB], x, yMinus1, z),
grad(p[BB], xMinus1, yMinus1, z))),
lerp(v, lerp(u, grad(p[AA + 1], x, y, zMinus1),
grad(p[BA + 1], xMinus1, y, z - 1)),
lerp(u, grad(p[AB + 1], x, yMinus1, zMinus1),
grad(p[BB + 1], xMinus1, yMinus1, zMinus1))));
},
};
};
const currentRandom = Math.random;
// Pseudo-random generator
function Marsaglia(i1, i2) {
// from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
let z = i1 || 362436069; let
w = i2 || 521288629;
const nextInt = function () {
z = (36969 * (z & 65535) + (z >>> 16)) & 0xFFFFFFFF;
w = (18000 * (w & 65535) + (w >>> 16)) & 0xFFFFFFFF;
return (((z & 0xFFFF) << 16) | (w & 0xFFFF)) & 0xFFFFFFFF;
};
this.nextDouble = function () {
const i = nextInt() / 4294967296;
return i < 0 ? 1 + i : i;
};
this.nextInt = nextInt;
}
Marsaglia.createRandomized = function () {
const now = new Date();
return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
};
// Noise functions and helpers
function PerlinNoise(seed) {
const rnd = seed !== undefined ? new Marsaglia(seed) : Marsaglia.createRandomized();
let i; let
j;
// http://www.noisemachine.com/talk1/17b.html
// http://mrl.nyu.edu/~perlin/noise/
// generate permutation
const p = new Array(512);
for (i = 0; i < 256; ++i) { p[i] = i; }
for (i = 0; i < 256; ++i) { const t = p[j = rnd.nextInt() & 0xFF]; p[j] = p[i]; p[i] = t; }
// copy to avoid taking mod in p[0];
for (i = 0; i < 256; ++i) { p[i + 256] = p[i]; }
function grad3d(i, x, y, z) {
const h = i & 15; // convert into 12 gradient directions
const u = h < 8 ? x : y;
const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
}
function grad2d(i, x, y) {
const v = (i & 1) === 0 ? x : y;
return (i & 2) === 0 ? -v : v;
}
function grad1d(i, x) {
return (i & 1) === 0 ? -x : x;
}
function lerp(t, a, b) { return a + t * (b - a); }
this.noise3d = function (x, y, z) {
const X = Math.floor(x) & 255; const Y = Math.floor(y) & 255; const
Z = Math.floor(z) & 255;
x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
const fx = (3 - 2 * x) * x * x; const fy = (3 - 2 * y) * y * y; const
fz = (3 - 2 * z) * z * z;
const p0 = p[X] + Y; const p00 = p[p0] + Z; const p01 = p[p0 + 1] + Z; const p1 = p[X + 1] + Y; const p10 = p[p1] + Z; const
p11 = p[p1 + 1] + Z;
return lerp(fz,
lerp(fy, lerp(fx, grad3d(p[p00], x, y, z), grad3d(p[p10], x - 1, y, z)),
lerp(fx, grad3d(p[p01], x, y - 1, z), grad3d(p[p11], x - 1, y - 1, z))),
lerp(fy, lerp(fx, grad3d(p[p00 + 1], x, y, z - 1), grad3d(p[p10 + 1], x - 1, y, z - 1)),
lerp(fx, grad3d(p[p01 + 1], x, y - 1, z - 1), grad3d(p[p11 + 1], x - 1, y - 1, z - 1))));
};
this.noise2d = function (x, y) {
const X = Math.floor(x) & 255; const
Y = Math.floor(y) & 255;
x -= Math.floor(x); y -= Math.floor(y);
const fx = (3 - 2 * x) * x * x; const
fy = (3 - 2 * y) * y * y;
const p0 = p[X] + Y; const
p1 = p[X + 1] + Y;
return lerp(fy,
lerp(fx, grad2d(p[p0], x, y), grad2d(p[p1], x - 1, y)),
lerp(fx, grad2d(p[p0 + 1], x, y - 1), grad2d(p[p1 + 1], x - 1, y - 1)));
};
this.noise1d = function (x) {
const X = Math.floor(x) & 255;
x -= Math.floor(x);
const fx = (3 - 2 * x) * x * x;
return lerp(fx, grad1d(p[X], x), grad1d(p[X + 1], x - 1));
};
}
// these are lifted from Processing.js
// processing defaults
const noiseProfile = {
generator: undefined, octaves: 4, fallout: 0.5, seed: undefined,
};
module.exports = function noise(x, y, z) {
if (noiseProfile.generator === undefined) {
// caching
noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
}
const generator = noiseProfile.generator;
let effect = 1; let k = 1; let
sum = 0;
for (let i = 0; i < noiseProfile.octaves; ++i) {
effect *= noiseProfile.fallout;
switch (arguments.length) {
case 1:
sum += effect * (1 + generator.noise1d(k * x)) / 2; break;
case 2:
sum += effect * (1 + generator.noise2d(k * x, k * y)) / 2; break;
case 3:
sum += effect * (1 + generator.noise3d(k * x, k * y, k * z)) / 2; break;
}
k *= 2;
}
return sum;
};

View File

@ -1,220 +0,0 @@
const noise = require('./fizzy-noise');
function fizzyText(message) {
const that = this;
// These are the variables that we manipulate with gui-dat.
// Notice they're all defined with "this". That makes them public.
// Otherwise, gui-dat can't see them.
this.growthSpeed = 0.8; // how fast do particles change size?
this.minSize = 1;
this.maxSize = 4; // how big can they get?
this.noiseStrength = 10; // how turbulent is the flow?
this.speed = 0.4; // how fast do particles move?
this.displayOutline = false; // should we draw the message as a stroke?
this.framesRendered = 0;
// //////////////////////////////////////////////////////////////
const _this = this;
const width = 550;
const height = 200;
const textAscent = 101;
const textOffsetLeft = 80;
const noiseScale = 300;
const frameTime = 30;
const colors = ['#000000', '#1A1A1A', '#163C50', '#205A79', '#2A78A2'];
// This is the context we use to get a bitmap of text using
// the getImageData function.
const r = document.createElement('canvas');
const s = r.getContext('2d');
// This is the context we actually use to draw.
const c = document.createElement('canvas');
const g = c.getContext('2d');
r.setAttribute('width', width);
c.setAttribute('width', width);
r.setAttribute('height', height);
c.setAttribute('height', height);
// Add our demo to the HTML
document.getElementById('fizzytext').appendChild(c);
// Stores bitmap image
let pixels = [];
// Stores a list of particles
const particles = [];
// Set g.font to the same font as the bitmap canvas, incase we
// want to draw some outlines.
s.font = g.font = '800 82px monospace, monospace';
// Instantiate some particles
for (let i = 0; i < 1000; i++) {
particles.push(new Particle(Math.random() * width, Math.random() * height));
}
// This function creates a bitmap of pixels based on your message
// It's called every time we change the message property.
const createBitmap = function (msg) {
s.fillStyle = '#fff';
s.fillRect(0, 0, width, height);
s.fillStyle = '#222';
s.fillText(msg, textOffsetLeft, textAscent);
// Pull reference
const imageData = s.getImageData(0, 0, width, height);
pixels = imageData.data;
};
// Called once per frame, updates the animation.
const render = function () {
that.framesRendered++;
g.clearRect(0, 0, width, height);
if (_this.displayOutline) {
g.globalCompositeOperation = 'source-over';
g.strokeStyle = '#000';
g.lineWidth = 0.5;
g.strokeText(message, textOffsetLeft, textAscent);
}
g.globalCompositeOperation = 'darker';
for (let i = 0; i < particles.length; i++) {
g.fillStyle = colors[i % colors.length];
particles[i].render();
}
};
// Returns x, y coordinates for a given index in the pixel array.
const getPosition = function (i) {
return {
x: (i - (width * 4) * Math.floor(i / (width * 4))) / 4,
y: Math.floor(i / (width * 4)),
};
};
// Returns a color for a given pixel in the pixel array.
const getColor = function (x, y) {
const base = (Math.floor(y) * width + Math.floor(x)) * 4;
const c = {
r: pixels[base + 0],
g: pixels[base + 1],
b: pixels[base + 2],
a: pixels[base + 3],
};
return `rgb(${c.r},${c.g},${c.b})`;
};
this.message = message;
createBitmap(message);
var loop = function () {
requestAnimationFrame(loop);
render();
};
// This calls the render function every 30 milliseconds.
loop();
// This class is responsible for drawing and moving those little
// colored dots.
function Particle(x, y, c) {
// Position
this.x = x;
this.y = y;
// Size of particle
this.r = 1;
// This velocity is used by the explode function.
this.vx = 0;
this.vy = 0;
this.constrain = function constrainFn(v, o1, o2) {
if (v < o1) v = o1;
else if (v > o2) v = o2;
return v;
};
// Called every frame
this.render = function renderFrame() {
// What color is the pixel we're sitting on top of?
const c = getColor(this.x, this.y);
// Where should we move?
const angle = noise(this.x / noiseScale, this.y / noiseScale) * _this.noiseStrength;
// var angle = -Math.PI/2;
// Are we within the boundaries of the image?
const onScreen = this.x > 0 && this.x < width && this.y > 0 && this.y < height;
const isBlack = c !== 'rgb(255,255,255)' && onScreen;
// If we're on top of a black pixel, grow.
// If not, shrink.
if (isBlack) {
this.r += _this.growthSpeed;
} else {
this.r -= _this.growthSpeed;
}
// This velocity is used by the explode function.
this.vx *= 0.5;
this.vy *= 0.5;
// Change our position based on the flow field and our
// explode velocity.
this.x += Math.cos(angle) * _this.speed + this.vx;
this.y += -Math.sin(angle) * _this.speed + this.vy;
if (this.r > _this.maxSize) {
this.r = _this.maxSize;
} else if (this.r < 0) {
this.r = 0;
this.x = Math.random() * width;
this.y = Math.random() * height;
return false;
}
// this.r = 3;
// debugger
// console.log(DAT.GUI.constrain(this.r, 0, _this.maxSize));
// this.r = this.constrain(this.r, _this.minSize, _this.maxSize);
// If we're tiny, keep moving around until we find a black
// pixel.
if (this.r <= 0) {
this.x = Math.random() * width;
this.y = Math.random() * height;
return false; // Don't draw!
}
// If we're off the screen, go over to other side
if (this.x < 0) this.x = width;
if (this.x > width) this.x = 0;
if (this.y < 0) this.y = height;
if (this.y > height) this.y = 0;
// Draw the circle.
g.beginPath();
// g.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
g.rect(this.x, this.y, this.r, this.r);
g.fill();
return true;
};
}
}
module.exports = fizzyText;

View File

@ -1,33 +0,0 @@
{
"name": "constructs-client",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "parcel index.html --port 40080 --no-hmr --no-source-maps",
"build": "rm -rf dist && parcel build --no-source-maps index.html",
"lint": "eslint --fix src/",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "UNLICENSED",
"dependencies": {
"async": "^2.6.1",
"borc": "^2.0.3",
"docco": "^0.7.0",
"izitoast": "^1.4.0",
"jdenticon": "^2.1.0",
"key": "^0.1.11",
"keymaster": "^1.6.2",
"lodash": "^4.17.11",
"parcel": "^1.11.0",
"phaser": "^3.16.1",
"redux": "^4.0.0"
},
"devDependencies": {
"eslint": "^5.6.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.14.0",
"jest": "^18.0.0"
}
}

View File

@ -1,219 +0,0 @@
const toast = require('izitoast');
function registerEvents(registry, events, tutorial) {
function setConstructs(constructs) {
registry.set('constructs', constructs);
tutorial('homepage');
}
function setConstructList(constructs) {
registry.set('constructList', constructs);
}
function setWs(ws) {
registry.set('ws', ws);
}
function setGame(game) {
if (game.phase === 'Skill') tutorial('skillPhase');
if (game.phase === 'Target') tutorial('targetPhase');
if (game.resolved.length) tutorial('resolutionPhase');
if (game.phase === 'Finish') tutorial('finishPhase');
return registry.set('game', game);
}
function setAccount(account) {
registry.set('account', account);
registry.set('home', true);
events.emit('ACCOUNT', account);
}
function setActiveSkill(skill) {
registry.set('activeSkill', skill);
}
function setMenu() {
registry.set('menu', true);
}
function setVbox(items) {
registry.set('vbox', items);
}
function setScores(scores) {
registry.set('scores', scores);
}
function setPlayerList(list) {
registry.set('playerList', list);
registry.set('homeInstances', true);
}
function setPlayer(player) {
registry.set('player', player);
if (!registry.get('inMenu')) {
setMenu();
}
}
function setZone(zone) {
registry.set('zone', zone);
}
function setGameList(gameList) {
registry.set('gameList', gameList);
}
function setConstructStatusUpdate(id, skill, target) {
registry.set('constructStatusUpdate', { id, skill, target });
}
events.on('SET_PLAYER', setPlayer);
events.on('SEND_SKILL', function skillActive(gameId, constructId, targetConstructId, skill) {
const ws = registry.get('ws');
ws.sendGameSkill(gameId, constructId, targetConstructId, skill);
setConstructStatusUpdate(constructId, skill, targetConstructId);
});
events.on('CONSTRUCT_ACTIVE', function constructActiveCb(construct) {
const constructs = registry.get('constructs');
for (let i = 0; i < constructs.length; i += 1) {
if (constructs[i].id === construct.id) constructs[i].active = !constructs[i].active;
}
return setConstructs(constructs);
});
const errMessages = {
select_constructs: 'Select your constructs before battle using the numbered buttons next to the construct avatar',
complete_nodes: 'You need to complete the previously connected nodes first',
max_skills: 'Your construct can only learn a maximum of 4 skills',
};
function errorPrompt(type) {
const message = errMessages[type];
const OK_BUTTON = '<button type="submit">OK</button>';
toast.info({
theme: 'dark',
color: 'black',
timeout: false,
drag: false,
position: 'center',
maxWidth: window.innerWidth / 2,
close: false,
buttons: [
[OK_BUTTON, (instance, thisToast) => instance.hide({ transitionOut: 'fadeOut' }, thisToast)],
],
message,
});
}
function loginPrompt() {
const USER_INPUT = '<input className="input" type="email" placeholder="username" />';
const PASSWORD_INPUT = '<input className="input" type="password" placeholder="password" />';
const LOGIN_BUTTON = '<button type="submit">Login</button>';
const REGISTER_BUTTON = '<button type="submit">Register</button>';
const DEMO_BUTTON = '<button type="submit">Demo</button>';
const ws = registry.get('ws');
function submitLogin(instance, thisToast, button, e, inputs) {
const USERNAME = inputs[0].value;
const PASSWORD = inputs[1].value;
ws.sendAccountLogin(USERNAME, PASSWORD);
}
function submitRegister(instance, thisToast, button, e, inputs) {
const USERNAME = inputs[0].value;
const PASSWORD = inputs[1].value;
ws.sendAccountCreate(USERNAME, PASSWORD);
}
function submitDemo() {
ws.sendAccountDemo();
}
const existing = document.querySelector('#login'); // Selector of your toast
if (existing) toast.hide({}, existing, 'reconnect');
toast.question({
id: 'login',
theme: 'dark',
color: 'black',
timeout: false,
// overlay: true,
drag: false,
close: false,
title: 'LOGIN',
position: 'center',
inputs: [
[USER_INPUT, 'change', () => true, true], // true to focus
[PASSWORD_INPUT, 'change', () => true],
],
buttons: [
[LOGIN_BUTTON, submitLogin], // true to focus
[REGISTER_BUTTON, submitRegister], // true to focus
[DEMO_BUTTON, submitDemo], // true to focus
],
});
events.once('ACCOUNT', function closeLoginCb() {
const prompt = document.querySelector('#login'); // Selector of your toast
if (prompt) toast.hide({ transitionOut: 'fadeOut' }, prompt, 'event');
});
}
events.on('CONSTRUCT_SPAWN', function spawnPrompt() {
const NAME_INPUT = '<input className="input" type="email" placeholder="name" />';
const SPAWN_BUTTON = '<button type="submit">SPAWN</button>';
const ws = registry.get('ws');
function submitSpawn(instance, thisToast, button, e, inputs) {
const NAME = inputs[0].value;
ws.sendConstructSpawn(NAME);
instance.hide({ transitionOut: 'fadeOut' }, thisToast, 'button');
}
toast.question({
theme: 'dark',
color: 'black',
timeout: false,
// overlay: true,
drag: false,
close: true,
title: 'SPAWN CONSTRUCT',
position: 'center',
inputs: [
[NAME_INPUT, 'change', null, true], // true to focus
],
buttons: [
[SPAWN_BUTTON, submitSpawn], // true to focus
],
});
});
tutorial('welcome');
return {
errorPrompt,
loginPrompt,
setAccount,
setActiveSkill,
setConstructs,
setConstructList,
setGame,
setMenu,
setPlayer,
setPlayerList,
setVbox,
setWs,
setGameList,
setZone,
setScores,
};
}
module.exports = registerEvents;

View File

@ -1,17 +0,0 @@
const renderConstructs = require('./scenes/constructs');
const createSocket = require('./socket');
const registerEvents = require('./events');
const createTutorial = require('./tutorial');
document.fonts.load('10pt "Jura"').then(() => {
const game = renderConstructs();
const tutorial = createTutorial();
const events = registerEvents(game.registry, game.events, tutorial);
const ws = createSocket(events);
// events.setWs(ws);
// events.setGameList([]);
ws.connect();
});

View File

@ -1,13 +0,0 @@
const genAvatar = (name) => {
let hash = 0;
if (name.length === 0) return hash;
// Probs don't need to hash using the whole string
for (let i = 0; i < name.length; i += 1) {
const chr = name.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash = hash & 10000; // We have avatars named 0-19
}
return `sprite${hash}`;
};
module.exports = genAvatar;

View File

@ -1,166 +0,0 @@
const Phaser = require('phaser');
const CHART = `
#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
float rand(float n){return fract(sin(n) * 43758.5453123 * time * 0.00001);}
float noise(float p){
float fl = floor(p);
float fc = fract(p);
// return mix(rand(fl), rand(fl + 1.0), p);
return mix(rand(fl), rand(fl + 1.0), p);
}
float getLine(vec2 p, float y){
float margin = 0.;
vec2 pos = p;
float a = time * 100. + y * 31.;
vec2 lineCenter = vec2(0.5, y);
pos -= lineCenter;
pos *- mat2(cos(a), -sin(a), sin(a), cos(a));
pos += lineCenter;
float marginb = 0.005;
float b = 0.004;
float t = y + (noise((pos.x + y) * 100.) - 0.5) * 0.02;
float f = (smoothstep(t - b, t, pos.y) - smoothstep(t, t + b, pos.y));
f *= smoothstep(margin - marginb, margin, pos.x) - smoothstep(1. - margin, 1. - margin + marginb, pos.x);
f *= 0.8;
float light = 0.5 + 0.5 * sin(time * .2);
vec2 point = vec2(margin + light * (1. - margin * 2.), t);
f += .008 / distance(pos, point);
return f;
}
void main( void ) {
vec2 p = gl_FragCoord.xy / resolution.xy;
float f = 0.;
for(int i = 0; i < 10; i++){
f += getLine(p, 0.1 + (0.8) / 10. * float(i));
}
vec3 color = vec3(0., .4, .6) * f;
gl_FragColor = vec4(color, 1.);
}`;
const STARS = `
//--- hatsuyuki ---
// by Catzpaw 2016
#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable
uniform float time;
uniform vec2 resolution;
float hash(float x){
return fract(sin(x*133.3)*12.13);
}
void main(void){
vec2 uv=(gl_FragCoord.xy*2.-resolution.xy)/min(resolution.x,resolution.y);
vec3 c=vec3(.2,.2,.2);
float a=4.4;
float si=sin(a),co=cos(a);
uv*=mat2(co,-si,si,co);
uv*=length(uv+vec2(0,1.9))*.5+1.;
float v=1.-sin(hash(floor(uv.x*200.))*2.);
float b=clamp(abs(sin(5.*time*v+uv.y*(5./(2.+v))))-.95,0.,1.)*20.;
c*=v*b;
gl_FragColor = vec4(c,2);
}
`;
const PLASMA = `
precision mediump float;
uniform sampler2D uMainSampler;
uniform vec2 resolution;
uniform float time;
varying vec2 outTexCoord;
varying vec4 outTint;
#define MAX_ITER 4
void main( void )
{
vec2 v_texCoord = gl_FragCoord.xy / resolution;
vec2 p = v_texCoord * 8.0 - vec2(20.0);
vec2 i = p;
float c = 1.0;
float inten = .05;
for (int n = 0; n < MAX_ITER; n++)
{
float t = time * (1.0 - (3.0 / float(n+1)));
i = p + vec2(cos(t - i.x) + sin(t + i.y),
sin(t - i.y) + cos(t + i.x));
c += 1.0/length(vec2(p.x / (sin(i.x+t)/inten),
p.y / (cos(i.y+t)/inten)));
}
c /= float(MAX_ITER);
c = 1.5 - sqrt(c);
vec4 texColor = vec4(0.01, 0.01, 0.01, 1.0);
texColor.rgb *= (1.0 / (1.0 - (c + 0.05)));
gl_FragColor = texColor;
}
`;
const CustomPipeline = new Phaser.Class({
Extends: Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline,
initialize: function CustomPipeline (game) {
Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline.call(this, {
game,
renderer: game.renderer,
fragShader: STARS,
});
},
});
class Background extends Phaser.Scene {
constructor() {
super({ key: 'Background', active: true });
this.bgTime = 10.0;
}
create() {
const game = this.game;
this.customPipeline = game.renderer.addPipeline('Custom', new CustomPipeline(game));
this.customPipeline.setFloat2('resolution', 1600, 1000);
const sprite = this.add.sprite(800, 500);
sprite.setPipeline('Custom');
sprite.displayWidth = 1600 * window.devicePixelRatio;
sprite.displayHeight = 1000 * window.devicePixelRatio;
}
update() {
this.customPipeline.setFloat1('time', this.bgTime);
this.bgTime += 0.005;
}
}
module.exports = Background;

View File

@ -1,250 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { COMBAT }, DELAYS } = require('./constants');
const randomColour = () => {
const colours = ['green', 'blue', 'red', 'white', 'yellow'];
return colours[Math.floor(Math.random() * 5)];
};
const animationParams = (sourceAlly) => {
const spawnLocation = sourceAlly ? COMBAT.width() * 0.35 : COMBAT.width() * 0.65;
const speed = sourceAlly ? 250 : -250;
const img = randomColour();
const angleMin = sourceAlly ? 320 : 180;
const angleMax = sourceAlly ? 360 : 220;
return { spawnLocation, speed, img, angleMin, angleMax };
};
const randomAttack = () => {
const animations = ['wall', 'spit', 'gravBlast', 'gravBomb', 'chargeBall'];
return animations[Math.floor(Math.random() * 5)];
};
class CombatSkills extends Phaser.GameObjects.Group {
constructor(scene) {
super(scene);
this.scene = scene;
}
getSkill(type, sourceAlly, targetAlly, castLocation) {
const genericHeal = ['Heal', 'Triage', 'TriageTick', 'DecayTick'];
const genericBlock = ['Block', 'Parry', 'Evasion', 'Shield'];
if (genericHeal.includes(type)) {
this.genericHeal(targetAlly, castLocation);
} else if (genericBlock.includes(type)) {
this.genericBlock(sourceAlly);
} else {
this[randomAttack()](sourceAlly);
}
}
genericHeal(sourceAlly, castLocation) {
// const { sourceX, sourceY } = getConstructPosition(sourcePos, 0);
const lifespan = DELAYS.ANIMATION_DURATION;
const colour = randomColour();
const particles = this.scene.add.particles(colour);
const x = sourceAlly ? COMBAT.width() * 0.3 : COMBAT.width() * 0.7;
const emitter2 = particles.createEmitter({
x: castLocation.x,
y: castLocation.y,
moveToX: x,
moveToY: COMBAT.height() * 0.2,
speed: 500,
lifespan: lifespan / 3,
scale: { start: 0.5, end: 1 },
quantity: 3,
_frequency: 20,
blendMode: 'ADD',
emitZone: { source: new Phaser.Geom.Rectangle(-200, -100, 400, 200) },
});
const emitter = particles.createEmitter({
x,
y: COMBAT.height() * 0.2,
angle: { min: 250, max: 290 },
speed: 250,
gravityY: 1000,
quantity: 4,
scale: { start: 0.1, end: 1 },
blendMode: 'ADD',
lifespan,
active: false,
});
this.add(particles);
this.scene.time.delayedCall(lifespan / 3, () => { emitter2.stop(); }, [], this);
this.scene.time.delayedCall(lifespan / 3, () => { emitter.active = true; }, [], this);
this.scene.time.delayedCall(lifespan, () => { emitter.stop(); }, [], this);
}
genericBlock(sourceAlly) {
const lifespan = DELAYS.ANIMATION_DURATION;
const colour = randomColour();
const x = sourceAlly ? COMBAT.width() * 0.3 : COMBAT.width() * 0.7;
const emitter1 = this.scene.add.particles(colour).createEmitter({
x,
y: COMBAT.height() * 0.4,
scale: { start: 0.75, end: 0.25 },
blendMode: 'ADD',
emitZone: {
source: new Phaser.Geom.Rectangle(-100, -100, 200, 200),
type: 'edge',
quantity: 24,
yoyo: true,
},
});
const emitter2 = this.scene.add.particles(colour).createEmitter({
x,
y: COMBAT.height() * 0.4,
blendMode: 'SCREEN',
scale: { start: 0.2, end: 0 },
speed: { min: -100, max: 100 },
quantity: 50,
active: false,
emitZone: {
source: new Phaser.Geom.Rectangle(-100, -100, 200, 200),
type: 'edge',
quantity: 50,
},
});
this.scene.time.delayedCall(lifespan / 2, () => { emitter1.stop(); }, [], this);
this.scene.time.delayedCall(lifespan / 2, () => { emitter2.active = true; }, [], this);
this.scene.time.delayedCall(lifespan, () => { emitter2.stop(); }, [], this);
}
wall(sourceAlly) {
const lifespan = DELAYS.ANIMATION_DURATION;
const { spawnLocation, speed, img } = animationParams(sourceAlly);
const particles = this.scene.add.particles(img);
const emitter = particles.createEmitter({
x: spawnLocation,
y: { min: COMBAT.height() * 0.2, max: COMBAT.height() * 0.5 },
speedX: { min: speed, max: speed * 2 },
scale: { start: 0.4, end: 0 },
quantity: 4,
blendMode: 'ADD',
lifespan,
});
this.add(particles);
this.scene.time.delayedCall(1000, () => { emitter.stop(); }, [], this.scene);
}
spit(sourceAlly) {
const lifespan = DELAYS.ANIMATION_DURATION;
const { spawnLocation, speed, img, angleMin, angleMax } = animationParams(sourceAlly);
const particles = this.scene.add.particles(img);
const emitter = particles.createEmitter({
x: spawnLocation,
y: COMBAT.height() * 0.35,
angle: { min: angleMin, max: angleMax },
speed: speed * 2,
scale: { start: 0.4, end: 1 },
gravityY: 250,
quantity: 4,
blendMode: 'ADD',
lifespan,
});
this.add(particles);
this.scene.time.delayedCall(lifespan, () => { emitter.stop(); }, [], this);
}
gravBomb(sourceAlly) {
const lifespan = DELAYS.ANIMATION_DURATION;
const { spawnLocation, img } = animationParams(!sourceAlly);
const particles = this.scene.add.particles(img);
const well = particles.createGravityWell({
x: spawnLocation,
y: COMBAT.height() * 0.25,
power: 4,
gravity: 500,
});
this.emitter = particles.createEmitter({
x: spawnLocation,
y: COMBAT.height() * 0.25,
speed: 1000,
scale: { start: 0.7, end: 1 },
blendMode: 'ADD',
lifespan,
});
this.add(particles);
this.scene.time.delayedCall(lifespan, () => { this.emitter.stop(); well.active = false; }, [], this.scene);
}
gravBlast(sourceAlly) {
const lifespan = DELAYS.ANIMATION_DURATION;
const WELL_END = lifespan / 2;
const img = randomColour();
const spawnLocation = sourceAlly ? COMBAT.width() * 0.35 : COMBAT.width() * 0.65;
const isEnemyLocation = sourceAlly ? COMBAT.width() * 0.7 : COMBAT.width() * 0.3;
const particles = this.scene.add.particles(img);
const bounds = sourceAlly
? { x: COMBAT.width() * 0.3, y: COMBAT.height() * 0.2, w: COMBAT.width() * 0.5, h: COMBAT.height() * 0.2 }
: { x: 0.2 * COMBAT.width(), y: COMBAT.height() * 0.2, w: COMBAT.width() * 0.5, h: COMBAT.height() * 0.2 };
const well = particles.createGravityWell({
x: spawnLocation,
y: COMBAT.height() * 0.35,
power: 4,
gravity: 500,
});
const emitter = particles.createEmitter({
x: spawnLocation,
y: COMBAT.height() * 0.35,
speed: 1000,
scale: { start: 0.7, end: 1 },
blendMode: 'ADD',
bounds,
lifespan,
});
this.add(particles);
this.scene.time.delayedCall(WELL_END, () => { emitter.stop(); well.x = isEnemyLocation; }, [], this.scene);
this.scene.time.delayedCall(lifespan, () => { well.active = false; }, [], this.scene);
}
chargeBall(sourceAlly) {
const lifespan = DELAYS.ANIMATION_DURATION;
const CHARGE_LIFESPAN = lifespan / 3;
const { img, spawnLocation } = animationParams(sourceAlly);
const targetLocation = sourceAlly ? 0.7 * COMBAT.width() : 0.25 * COMBAT.width();
const particles = this.scene.add.particles(img);
const emitter = particles.createEmitter({
x: 0,
y: 0,
moveToX: spawnLocation,
moveToY: COMBAT.height() * 0.1,
scale: 0.75,
quantity: 4,
_frequency: 20,
blendMode: 'ADD',
emitZone: { source: new Phaser.Geom.Rectangle(0, 0, COMBAT.width(), COMBAT.height()) },
lifespan: CHARGE_LIFESPAN,
});
const emitter2 = particles.createEmitter({
radial: false,
x: { min: spawnLocation, max: targetLocation, steps: 90 },
y: { min: COMBAT.height() * 0.1, max: COMBAT.height() * 0.4, steps: 90 },
quantity: 4,
gravityY: 0,
scale: { start: 2, end: 0.1, ease: 'Power3' },
blendMode: 'ADD',
active: false,
lifespan: CHARGE_LIFESPAN,
});
this.add(particles);
this.scene.time.delayedCall(CHARGE_LIFESPAN, () => { emitter.stop(); }, [], this.scene);
this.scene.time.delayedCall(CHARGE_LIFESPAN * 2, () => { emitter2.active = true; }, [], this.scene);
this.scene.time.delayedCall(lifespan, () => { emitter2.stop(); }, [], this.scene);
}
cleanup() {
this.children.entries.forEach(obj => obj.destroy());
}
}
module.exports = CombatSkills;

View File

@ -1,239 +0,0 @@
const Phaser = require('phaser');
const genAvatar = require('./avatar');
const StatBar = require('./elements/combat.statbar');
const { DELAYS, TEXT, POSITIONS: { COMBAT } } = require('./constants');
const CONSTRUCT_MARGIN = COMBAT.constructMargin();
const TEXT_MARGIN = COMBAT.textMargin();
const constructAvatarText = (team, iter) => {
const nameX = COMBAT.width() * team;
const nameY = COMBAT.y() + CONSTRUCT_MARGIN * iter + COMBAT.height() * 0.07;
const statusX = COMBAT.width() * team;
const statusY = COMBAT.y() + TEXT_MARGIN * 6 + CONSTRUCT_MARGIN * iter + COMBAT.height() * 0.07;
return { statusX, statusY, nameX, nameY };
};
const constructEffects = (team, iter) => {
const constructEffectsX = team ? COMBAT.width() - COMBAT.width() / 6.5 : COMBAT.width() / 6.5;
const constructEffectsY = TEXT_MARGIN * 2 + CONSTRUCT_MARGIN * iter;
return { constructEffectsX, constructEffectsY };
};
const constructPosition = (team, iter) => {
const constructAvatarX = team ? COMBAT.width() - COMBAT.width() / 6 : COMBAT.width() / 6;
const constructAvatarY = TEXT_MARGIN * 5 + CONSTRUCT_MARGIN * iter;
return { constructAvatarX, constructAvatarY };
};
class Effects extends Phaser.GameObjects.Group {
constructor(scene, team, iter) {
super(scene);
this.scene = scene;
const { constructEffectsX, constructEffectsY } = constructEffects(team, iter);
this.x = constructEffectsX; this.y = constructEffectsY;
this.effectCount = 0;
}
addEffect(effect) {
const y = this.y + this.effectCount * TEXT_MARGIN;
const text = `${effect.effect} for ${effect.duration} turn`;
const e = this.scene.add.text(this.x, y, text, TEXT.NORMAL);
e.effect = effect.effect;
this.add(e);
this.effectCount += 1;
}
removeEffect(effect) {
this.children.entries.forEach((e) => {
if (e.effect === effect) e.destroy();
});
}
update(effects) {
this.effectCount = 0;
this.children.entries.forEach(e => e.destroy());
effects.forEach((effect) => {
this.addEffect(effect);
});
return true;
}
}
class ConstructImage extends Phaser.GameObjects.Image {
constructor(scene, team, iter, construct) {
// Get coords
const { constructAvatarX, constructAvatarY } = constructPosition(team, iter);
const { statusX, statusY, nameX, nameY } = constructAvatarText(team, iter);
// Construct display
// const avatar = team ? 'magmar' : 'alk';
super(scene, constructAvatarX, constructAvatarY, 'aztec', genAvatar(construct.name));
this.setScale(0.5);
if (!team) this.flipX = true;
// Save position and construct details
this.scene = scene;
this.iter = iter;
this.team = team;
this.construct = construct;
this.state = 'deselect';
// Add construct name
scene.add.text(nameX, nameY, construct.name, TEXT.NORMAL).setOrigin(team, 0);
// Add construct stat bars
this.health = scene.add.existing(new StatBar(scene, this, 'HP'));
this.red_shield = scene.add.existing(new StatBar(scene, this, 'Red Shield'));
this.blue_shield = scene.add.existing(new StatBar(scene, this, 'Blue Shield'));
// this.evasion = scene.add.existing(new StatBar(scene, this, 'Evasion'));
this.effects = scene.add.existing(new Effects(scene, team, iter));
this.statusText = scene.add.text(statusX, statusY, '', TEXT.NORMAL);
}
select() {
this.setTint('0x00bb00');
this.state = 'select';
}
setKo() {
this.state = 'ko';
this.setTint('0x9d9ea0');
}
deselect() {
if (this.state !== 'ko') {
this.clearTint();
this.state = 'deselect';
}
}
clearStatus() {
this.statusText.text = '';
}
reduceDefense(amount, type) {
if (type === 'PhysDmg') {
this.red_shield.takeDamage(amount);
} else if (type === 'BlueDmg') {
this.blue_shield.takeDamage(amount);
}
}
takeDamage(props) {
const { amount, mitigation, category } = props;
if (mitigation) this.reduceDefense(mitigation, category);
this.setTint(0xff0000);
this.health.takeDamage(amount);
this.scene.time.delayedCall(DELAYS.DAMAGE_TICK, () => {
if (this.state !== 'ko') this.clearTint();
});
}
takeHealing(amount) {
this.setTint(0x00bb00);
this.health.takeDamage(amount * -1);
this.scene.time.delayedCall(DELAYS.DAMAGE_TICK, () => {
if (this.state !== 'ko') this.clearTint();
});
}
}
class CombatConstructs extends Phaser.Scene {
constructor() {
super({ key: 'CombatConstructs' });
}
create(game) {
this.constructs = this.add.group();
this.phase = game.phase;
this.account = this.registry.get('account');
this.drawConstructs(game);
this.registry.events.on('changedata', this.updateData, this);
this.registry.set('constructStatusUpdate', false);
this.teams = game.teams.length;
}
updateData(parent, key, data) {
if (key === 'game' && data) {
if (data.teams.length !== this.teams) this.scene.restart(data);
const isAnimating = this.phase === 'animating';
this.game = data;
if (isAnimating) return false;
}
if (key === 'gamePhase' && data) {
const shouldUpdate = data !== this.phase;
this.phase = data;
if (shouldUpdate) {
this.constructs.children.entries.forEach(c => c.clearStatus());
this.drawConstructs(this.game);
}
}
if (key === 'constructStatusUpdate' && data) {
this.updateConstructStatus(data);
}
return true;
}
drawConstructs(game) {
const renderConstruct = (construct, iter, team) => {
// Add Image Avatar Class
const constructObj = new ConstructImage(this, team, iter, construct);
this.add.existing(constructObj);
this.constructs.add(constructObj);
return constructObj;
};
const renderTeam = (construct, iter, team) => {
const constructObj = this.constructs.children.entries
.find(c => c.construct.id === construct.id)
|| renderConstruct(construct, iter, team);
constructObj.health.val = construct.hp.value;
constructObj.red_shield.val = construct.red_shield.value;
constructObj.blue_shield.val = construct.red_shield.value;
constructObj.health.drawStatBar();
constructObj.red_shield.drawStatBar();
constructObj.blue_shield.drawStatBar();
constructObj.effects.update(construct.effects);
};
const allyTeam = game.teams.find(t => t.id === this.account.id);
// in future there will be more than one
const [enemyTeam] = game.teams.filter(t => t.id !== this.account.id);
allyTeam.constructs.forEach((construct, i) => renderTeam(construct, i, 0));
if (!enemyTeam) return false;
enemyTeam.constructs.forEach((construct, i) => renderTeam(construct, i, 1));
return true;
}
selectConstruct(constructId) {
this.constructs.children.entries.forEach(c => c.deselect());
if (constructId) this.constructs.children.entries.find(c => c.construct.id === constructId).select();
}
updateConstructStatus(status) {
const sourceConstruct = this.constructs.children.entries
.find(c => c.construct.id === status.id);
const targetConstruct = this.constructs.children.entries
.find(c => c.construct.id === status.target);
if (this.phase === 'Skill') {
sourceConstruct.statusText.text = `${status.skill} on ${targetConstruct.construct.name}`;
}
}
cleanUp() {
this.registry.events.off('changedata', this.updateData);
this.scene.remove();
}
}
module.exports = CombatConstructs;

View File

@ -1,89 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { COMBAT } } = require('./constants');
const CONSTRUCT_MARGIN = COMBAT.constructMargin();
const BOX_HEIGHT = CONSTRUCT_MARGIN * 0.8;
const BOX_WIDTH = COMBAT.width() * 0.2;
class ConstructHitBox extends Phaser.GameObjects.Rectangle {
constructor(scene, iter, team, cback) {
const y = COMBAT.y() + COMBAT.height() * 0.05 + CONSTRUCT_MARGIN * iter;
super(scene, (COMBAT.width() - BOX_WIDTH) * team, y, BOX_WIDTH, BOX_HEIGHT, 0x222222);
this.setOrigin(0);
this.clickHandler = () => cback();
}
select() {
this.setFillStyle(0x003300);
}
deselect() {
this.setFillStyle(0x222222);
}
}
class CombatHitBox extends Phaser.Scene {
constructor() {
super({ key: 'CombatHitBox' });
}
create(phase) {
this.phase = phase;
this.registry.events.off('changedata', this.updateData);
this.registry.events.on('changedata', this.updateData, this);
if (phase === 'animating') return true;
this.selectHitBox(phase);
return true;
}
updateData(parent, key, data) {
if (key === 'game' && data) {
// In the case that we hit skill phase but teams change we restart
if (data.teams.length !== this.teams) this.scene.restart(data.phase);
}
if (key === 'gamePhase' && data) {
const shouldUpdate = data !== this.phase;
if (shouldUpdate) this.scene.restart(data);
return false;
}
return true;
}
selectHitBox(phase) {
const game = this.registry.get('game');
this.teams = game.teams.length;
if (phase === 'Skill') return this.skillHitBox(game);
return false;
}
skillHitBox(game) {
const account = this.registry.get('account');
const group = this.scene.get('CombatConstructs').constructs;
const skillScene = this.scene.get('CombatSkills');
game.teams.forEach((t) => {
t.constructs.forEach((c) => {
const cback = () => {
const { activeSkill } = skillScene;
if (activeSkill) {
this.scene.get('CombatSkills').clearConstructActive(activeSkill.construct.id);
activeSkill.activate();
skillScene.activeSkill = null;
this.game.events.emit('SEND_SKILL', game.id, activeSkill.construct.id, c.id, activeSkill.skill.skill);
}
};
const constructSpawn = group.children.entries.find(s => s.construct.id === c.id);
const team = c.account === account.id ? 0 : 1;
if (constructSpawn) this.add.existing(new ConstructHitBox(this, constructSpawn.iter, team, cback));
});
});
this.scene.moveBelow('Combat');
}
cleanUp() {
this.registry.events.off('changedata', this.updateData);
this.scene.remove();
}
}
module.exports = CombatHitBox;

View File

@ -1,138 +0,0 @@
const Phaser = require('phaser');
const { throttle } = require('lodash');
const { TEXT, POSITIONS: { COMBAT } } = require('./constants');
const CombatLog = require('./combat.log');
const CombatConstructs = require('./combat.constructs');
const CombatSkills = require('./combat.skills');
const CombatHitBox = require('./combat.hitbox');
const renderResolutions = require('./combat.render.resolutions');
class Combat extends Phaser.Scene {
constructor() {
super({ key: 'Combat' });
}
preload() {
this.load.image('proj', 'https://labs.phaser.io/assets/sprites/bullet.png');
this.load.image('blue', 'https://labs.phaser.io/assets/particles/blue.png');
this.load.image('green', 'https://labs.phaser.io/assets/particles/green.png');
this.load.image('red', 'https://labs.phaser.io/assets/particles/red.png');
this.load.image('white', 'https://labs.phaser.io/assets/particles/white.png');
this.load.image('yellow', 'https://labs.phaser.io/assets/particles/yellow.png');
}
create() {
console.log('creating game');
this.registry.events.off('changedata', this.updateData);
this.registry.events.on('changedata', this.updateData, this);
this.addLeaveGame();
this.registry.set('gamePhase', false);
this.registry.set('inGame', true);
this.registry.set('gameAnimating', false);
this.account = this.registry.get('account');
this.fetchGame = throttle(() => {
const game = this.registry.get('game');
if (game) {
const ws = this.registry.get('ws');
return ws.sendGameState(game.id);
}
return false;
}, 500);
return true;
}
startGame(game) {
this.scene.manager.add('CombatConstructs', CombatConstructs, true, game);
this.scene.manager.add('CombatLog', CombatLog, true, game);
this.renderedResolves = game.resolved.length; // In case you rejoin mid way
this.scene.manager.add('CombatSkills', CombatSkills, true, game.phase);
this.scene.manager.add('CombatHitBox', CombatHitBox, true, game.phase);
this.registry.set('gamePhase', game.phase);
this.phase = game.phase;
return true;
}
update() {
this.fetchGame();
return true;
}
updateData(parent, key, data) {
if (key === 'game') {
if (!data) return false;
const startGame = this.registry.get('gamePhase') === false;
if (startGame) { this.startGame(data); return true; }
this.checkAnimation(data);
// Game over?
// if (data.phase === 'Finish') {
// this.time.delayedCall(10000, () => {
// this.endGame();
// });
// }
}
return true;
}
checkAnimation(game) {
// Check constructs are loaded and whether game is animating
const cantAnimate = this.registry.get('gamePhase') === 'animating';
if (cantAnimate) return false;
if (game.resolved.length !== this.renderedResolves) {
const newResolutions = game.resolved.slice(this.renderedResolves);
renderResolutions(this, game, newResolutions);
this.renderedResolves = game.resolved.length;
return true;
}
if (this.phase !== game.phase) {
this.phase = game.phase;
this.registry.set('gamePhase', game.phase);
}
if (this.registry.get('gameLog') !== game.log.length) {
this.registry.set('gameLog', game.log.length);
}
return true;
}
addLeaveGame() {
const leaveGame = () => this.cleanUp();
this.input.keyboard.on('keydown_BACKSPACE', leaveGame, 0, this);
const LEAVE_HEIGHT = COMBAT.height() / 6;
const LEAVE_WIDTH = COMBAT.width() / 5;
const LEAVE_X = COMBAT.width() * 0.8;
const LEAVE_Y = COMBAT.height() * 0.9;
const menu = this.add
.rectangle(LEAVE_X, LEAVE_Y, LEAVE_WIDTH, LEAVE_HEIGHT, 0x440000)
.setInteractive()
.setOrigin(0)
.on('pointerdown', leaveGame);
this.add
.text(menu.getCenter().x, menu.getCenter().y, 'Menu', TEXT.HEADER)
.setOrigin(0.5, 0.5);
}
cleanUp() {
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
this.registry.set('inGame', null);
this.registry.set('menu', true);
this.registry.set('game', null);
const ACTIVE_SCENES = ['CombatLog', 'CombatConstructs', 'CombatSkills', 'CombatHitBox'];
ACTIVE_SCENES.forEach((sKey) => {
if (this.scene.get(sKey)) this.scene.get(sKey).cleanUp();
});
this.scene.remove();
return true;
}
}
module.exports = Combat;

View File

@ -1,50 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { COMBAT }, TEXT } = require('./constants');
class CombatLog extends Phaser.Scene {
constructor() {
super({ key: 'CombatLog' });
}
create(game) {
this.registry.events.on('changedata', this.updateData, this);
this.cameras.main.setViewport(COMBAT.LOG.x(), COMBAT.LOG.y(), COMBAT.LOG.width(), COMBAT.LOG.height());
this.log = this.add.text(0, 0, '', TEXT.NORMAL);
this.logIndex = game.log.length;
this.logData = game.log;
this.log.setWordWrapWidth(COMBAT.LOG.width());
this.log.setText(Array.from(game.log).reverse());
}
updateData(parent, key, data) {
const UPDATE_KEYS = ['game', 'gameLog'];
if (UPDATE_KEYS.includes(key) && data) {
if (key === 'game') {
this.logData = data.log;
}
if (key === 'gameLog') {
this.logIndex = data;
}
this.updateLog();
}
return true;
}
updateLog() {
// shallow copy because reverse mutates
if (this.logData.length > this.logIndex + 1
&& Array.from(this.logData)[this.logIndex].slice(-2) === 'KO') {
this.logIndex += 1;
this.registry.set('gameLog', this.logIndex);
}
// }
this.log.setText(Array.from(this.logData).slice(0, this.logIndex).reverse());
}
cleanUp() {
this.registry.events.off('changedata', this.updateData);
this.scene.remove();
}
}
module.exports = CombatLog;

View File

@ -1,135 +0,0 @@
const { eachSeries } = require('async');
const CombatAnimations = require('./combat.animations');
const {
DELAYS: { ANIMATION_DURATION, MOVE_CREEP, DAMAGE_TICK },
POSITIONS: { COMBAT },
} = require('./constants');
function findResolutionConstructs(scene, group, resolution, game) {
const sourceSpawn = group.children.entries.find(c => c.construct.id === resolution.source.id);
/* const sourceConstruct = game.teams.find(t => t.constructs.find(c => c.id === resolution.source_construct_id))
.constructs.find(c => c.id === resolution.source_construct_id);
const targetConstruct = game.teams.find(t => t.constructs.find(c => c.id === resolution.target_construct_id))
.constructs.find(c => c.id === resolution.target_construct_id);
*/
const targetSpawn = group.children.entries.find(c => c.construct.id === resolution.target.id);
return { sourceSpawn, targetSpawn };
}
function calculateTweenParams(sourceSpawn, targetSpawn, account, skill) {
const tweenParams = (targets, centreSpot) => {
const enemy = targets.construct.account !== account.id;
let x = centreSpot ? COMBAT.width() * 0.3 : targets.x;
x = (enemy && centreSpot) ? x + COMBAT.width() * 0.4 : x;
const y = centreSpot ? COMBAT.height() * 13.25 / 35 : targets.y;
const ease = 'Power1';
const duration = MOVE_CREEP;
return {
targets, x, y, ease, duration,
};
};
let moveSourceBattle = false;
let moveSourceOrig = false;
const targetOnlySkill = ['DecayTick'];
if (!(targetOnlySkill.includes(skill))) {
if (sourceSpawn.construct.account !== targetSpawn.construct.account) {
moveSourceBattle = tweenParams(sourceSpawn, true);
moveSourceOrig = tweenParams(sourceSpawn, false);
}
}
const moveTargetBattle = tweenParams(targetSpawn, true);
const moveTargetOrig = tweenParams(targetSpawn, false);
return {
moveSourceBattle, moveSourceOrig, moveTargetBattle, moveTargetOrig,
};
}
function animatePhase(scene, game, resolution, cb) {
// return early for disabled skills
if (resolution.length === 0) return cb();
if (resolution.event[0] === 'Disable'
|| resolution.event[0] === 'TargetKo'
|| resolution.event === 'Ko') return cb();
const group = scene.scene.get('CombatConstructs').constructs;
const animations = new CombatAnimations(scene);
const account = scene.registry.get('account');
// Find constructs, targets
const { sourceSpawn, targetSpawn } = findResolutionConstructs(scene, group, resolution, game);
const {
moveSourceBattle, moveSourceOrig, moveTargetBattle, moveTargetOrig,
} = calculateTweenParams(sourceSpawn, targetSpawn, account, resolution.event[1].skill);
const castParams = () => {
const x = (sourceSpawn === targetSpawn) ? moveTargetBattle.x : sourceSpawn.x;
const y = (sourceSpawn === targetSpawn) ? moveTargetBattle.y : sourceSpawn.y;
return { x, y };
};
const castLocation = castParams();
// Move constructs into position
if (moveSourceBattle) scene.tweens.add(moveSourceBattle);
scene.tweens.add(moveTargetBattle);
return scene.time.delayedCall(MOVE_CREEP, () => {
const sourceAlly = sourceSpawn.construct.account === account.id;
const targetAlly = targetSpawn.construct.account === account.id;
// animate animation
animations.getSkill(resolution.event[1].skill, sourceAlly, targetAlly, castLocation);
// Target construct takes damage
scene.time.delayedCall(ANIMATION_DURATION, () => {
console.log(resolution);
if (resolution.event[0] === 'Damage') {
targetSpawn.takeDamage(resolution.event[1]);
scene.registry.set('gameLog', scene.registry.get('gameLog') + 1);
}
if (resolution.event[0] === 'Healing') {
targetSpawn.takeHealing(resolution.event[1]);
scene.registry.set('gameLog', scene.registry.get('gameLog') + 1);
}
if (resolution.event[0] === 'Effect') {
targetSpawn.effects.addEffect(resolution.event[1]);
console.log('target has new effect', resolution.event[1].effect);
}
if (resolution.event[0] === 'Removal') {
targetSpawn.effects.removeEffect(resolution.event[1].effect);
console.log('target effect removed', resolution.event[1].effect);
}
if (moveSourceOrig) scene.tweens.add(moveSourceOrig);
scene.tweens.add(moveTargetOrig);
// all done
scene.time.delayedCall(MOVE_CREEP, () => {
animations.destroy(true);
return cb();
});
});
});
}
function renderResolutions(scene, game, resolutions) {
scene.registry.set('gamePhase', 'animating');
scene.registry.set('gameLog', scene.registry.get('gameLog') + 1);
eachSeries(
resolutions,
(resolution, cb) => animatePhase(scene, game, resolution, cb),
(err) => {
if (err) return console.error(err);
scene.registry.set('gamePhase', 'Skill');
return true;
}
);
return true;
}
module.exports = renderResolutions;

View File

@ -1,235 +0,0 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { COMBAT } } = require('./constants');
const CONSTRUCT_KEY_MAP = ['keydown_ONE', 'keydown_TWO', 'keydown_THREE'];
const SKILL_KEY_MAP = ['keydown_Q', 'keydown_W', 'keydown_E', 'keydown_R'];
const TARGET_KEY_MAP = ['keydown_SEVEN', 'keydown_EIGHT', 'keydown_NINE', 'keydown_ZERO'];
const CONSTRUCT_MARGIN = COMBAT.constructMargin();
const TEXT_MARGIN = COMBAT.textMargin();
const SKILL_WIDTH = COMBAT.width() / 10;
const SKILL_HEIGHT = COMBAT.height() / 30;
const skillPosition = (constructIter, skillIter) => {
const skillTextX = COMBAT.width() / 3.8;
const skillTextY = (TEXT_MARGIN * skillIter) * 1.5 + CONSTRUCT_MARGIN * constructIter + COMBAT.y() + COMBAT.height() * 0.07;
return [skillTextX, skillTextY];
};
const skillCheckHitBox = (scenePlugin, pointer) => {
const { list } = scenePlugin.get('CombatHitBox').children;
for (let i = 0; i < list.length; i += 1) {
if (Phaser.Geom.Rectangle.ContainsPoint(list[i].getBounds(),
pointer.position)) return list[i];
}
// If we didn't find a hitbox deselect them all
for (let i = 0; i < list.length; i += 1) list[i].deselect();
return false;
};
class ConstructSkill extends Phaser.GameObjects.Container {
constructor(scene, x, y, skill, construct) {
// Avatar will be a property of construct
super(scene, x, y);
const CD_TEXT = skill.cd ? `(${skill.cd}T)` : '';
const SKILL_TEXT = `${skill.skill} ${CD_TEXT}`;
this.origX = x; this.origY = y;
this.skillBox = scene.add.rectangle(0, 0, SKILL_WIDTH, SKILL_HEIGHT, 0x222222);
this.skillText = scene.add.text(0, 0, SKILL_TEXT, TEXT.NORMAL).setOrigin(0.5, 0.5);
this.add(this.skillBox);
this.add(this.skillText);
this.state = 'deselect';
this.construct = construct;
this.skill = skill;
this.scene = scene;
this.setSize(SKILL_WIDTH, SKILL_HEIGHT);
this.setInteractive();
}
clickHandler() {
if (this.scene.phase === 'Skill') this.scene.activeSkill = this;
this.select();
}
select() {
this.scene.children.list.forEach((skill) => {
if (skill.state === 'select') skill.deselect();
});
this.skillBox.setFillStyle(0x004bfe);
this.state = 'select';
}
activate() {
this.scene.children.list.forEach((skill) => {
if (skill.state === 'select') skill.deselect();
});
this.skillBox.setFillStyle(0xff0000);
this.state = 'activate';
}
deselect() {
this.skillBox.setFillStyle(0x222222);
this.state = 'deselect';
}
}
class CombatSkills extends Phaser.Scene {
constructor() {
super({ key: 'CombatSkills' });
}
create(phase) {
this.phase = phase;
this.registry.events.off('changedata', this.updateData);
this.registry.events.on('changedata', this.updateData, this);
this.account = this.registry.get('account');
this.input.on('dragstart', (pointer, box) => {
box.clickHandler();
});
this.input.on('drag', (pointer, box, dragX, dragY) => {
const hitBox = skillCheckHitBox(this.scene, pointer);
if (hitBox) hitBox.select();
box.setPosition(dragX, dragY);
});
this.input.on('dragend', (pointer, box) => {
box.deselect();
const hitBox = skillCheckHitBox(this.scene, pointer);
if (hitBox) {
hitBox.clickHandler();
}
box.setPosition(box.origX, box.origY);
});
if (phase === 'animating') return true;
// can't set this.game cause of phaser class named the same
const game = this.registry.get('game');
this.renderSkills(game, phase);
return true;
}
updateData(parent, key, data) {
if (key === 'gamePhase' && data) {
const shouldUpdate = data !== this.phase;
if (shouldUpdate) {
this.scene.get('CombatConstructs').selectConstruct(null);
return this.scene.restart(data);
}
return false;
}
return true;
}
renderSkills(game, phase) {
if (phase === 'Skill') return this.renderSkillPhase(game);
return false;
}
renderSkillPhase(game) {
const { account } = this;
const { keyboard } = this.input;
const { events } = this.game;
const addSkill = (i, j, skill, construct) => {
const skillTextPos = skillPosition(i, j);
const skillObj = new ConstructSkill(this, skillTextPos[0], skillTextPos[1], skill, construct);
if (skill.cd) {
skillObj.skillBox.setFillStyle(0x9d9ea0);
} else {
this.input.setDraggable(skillObj);
}
this.add.existing(skillObj);
return skillObj;
};
const team = game.teams.find(t => t.id === account.id);
const enemyTeam = game.teams.find(t => t.id !== account.id);
team.constructs.forEach((construct) => {
// return early if KOd
if (construct.hp.value === 0) return true;
// find the construct position
const { iter } = this.scene.get('CombatConstructs').constructs.children.entries.find(c => c.construct.id === construct.id);
// draw the skills
const skillButtons = construct.skills.map((skill, j) => addSkill(iter, j, skill, construct));
const bindConstructKeys = () => this.mapSkillKeys(skillButtons, game.id, construct.id, team.id, enemyTeam.id, iter);
// reset everything
keyboard.on('keydown_ESC', bindConstructKeys, this);
events.on('SEND_SKILL', bindConstructKeys, this);
bindConstructKeys();
return true;
});
return true;
}
// FIXME
// needs to send constructId not team
mapSkillKeys(skillButtons, gameId, constructId, alliesId, enemyId, i) {
const { keyboard } = this.input;
keyboard.removeListener(CONSTRUCT_KEY_MAP[i]);
keyboard.on(CONSTRUCT_KEY_MAP[i], () => {
SKILL_KEY_MAP.forEach(k => keyboard.removeListener(k));
this.scene.get('CombatConstructs').selectConstruct(constructId);
skillButtons.forEach((button, j) => {
keyboard.on(SKILL_KEY_MAP[j], () => {
this.activeSkill = button;
button.select();
// clear existing keys
CONSTRUCT_KEY_MAP.forEach(k => keyboard.removeListener(k));
TARGET_KEY_MAP.forEach(k => keyboard.removeListener(k));
CONSTRUCT_KEY_MAP.forEach(k => keyboard.on(k, () => {
this.clearConstructActive(constructId);
button.activate();
this.activeSkill = null;
this.game.events.emit('SEND_SKILL', gameId, constructId, alliesId, button.skill.skill);
}));
TARGET_KEY_MAP.forEach(k => keyboard.on(k, () => {
this.clearConstructActive(constructId);
button.activate();
this.activeSkill = null;
this.game.events.emit('SEND_SKILL', gameId, constructId, enemyId, button.skill.skill);
}));
}, this);
});
}, this);
return true;
}
clearConstructActive(constructId) {
this.scene.scene.children.list.forEach((s) => {
if (s.construct.id === constructId && s.state === 'activate') s.deselect();
});
}
clearKeys() {
TARGET_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
CONSTRUCT_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
SKILL_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
}
cleanUp() {
this.registry.events.off('changedata', this.updateData);
this.scene.remove();
}
}
module.exports = CombatSkills;

View File

@ -1,388 +0,0 @@
// POSITIONING FNS
// floors prevent subpixel rendering which looks trash
const CANVAS_WIDTH = () => Math.floor(window.innerHeight * 1.6);
const CANVAS_HEIGHT = () => Math.floor(window.innerHeight);
const headerWidth = () => CANVAS_WIDTH();
const headerHeight = () => Math.floor(CANVAS_HEIGHT() * 0.05);
const menuConstructListWidth = () => Math.floor(CANVAS_WIDTH() * 0.3);
const menuConstructListHeight = () => Math.floor(CANVAS_HEIGHT() - headerHeight());
const menuConstructListX = () => Math.floor(CANVAS_WIDTH() * 0.3);
const menuConstructListY = () => headerHeight();
const itemListWidth = () => Math.floor(CANVAS_WIDTH() * 0.5);
const itemListHeight = () => Math.floor(CANVAS_HEIGHT() * 0.95);
const itemListX = () => 0;
const itemListY = () => headerHeight();
const itemBlockWidth = () => Math.floor(itemListWidth() * 0.12);
const itemBlockHeight = () => Math.floor(itemListHeight() * 0.04);
const menuNavigationWidth = () => Math.floor(CANVAS_WIDTH() * 0.5);
const menuNavigationHeight = () => Math.floor(CANVAS_HEIGHT() * 0.3);
const menuNavigationX = () => Math.floor(CANVAS_WIDTH() * 0.5);
const menuNavigationY = () => Math.floor(CANVAS_HEIGHT() * 0.7);
const menuMainWidth = () => Math.floor(CANVAS_WIDTH() * 0.4);
const menuMainHeight = () => Math.floor(CANVAS_HEIGHT() * 0.5);
const menuMainX = () => Math.floor(CANVAS_WIDTH() * 0.65);
const menuMainY = () => headerHeight();
const homeMainWidth = () => Math.floor(CANVAS_WIDTH() * 0.6);
const homeMainHeight = () => Math.floor(CANVAS_HEIGHT() * 0.5);
const homeMainX = () => Math.floor(CANVAS_WIDTH() * 0.4);
const homeMainY = () => headerHeight();
const combatWidth = () => CANVAS_WIDTH();
const combatHeight = () => CANVAS_HEIGHT() - headerHeight();
const combatY = () => headerHeight();
const combatX = () => 0;
const combatConstructMargin = () => Math.floor((CANVAS_HEIGHT() - headerHeight()) / 4.5);
const combatTextMargin = () => Math.floor((CANVAS_HEIGHT() - headerHeight()) / 35);
const statsWidth = () => Math.floor(CANVAS_WIDTH() - menuConstructListWidth());
const statsHeight = () => CANVAS_HEIGHT() - headerHeight();
const statsY = () => headerHeight();
const statsX = () => menuConstructListWidth();
const statsKnownX = () => Math.floor(statsX() + statsWidth() / 4);
const statsLearnableX = () => Math.floor(statsX() + statsWidth() / 2);
const statsTextMargin = () => 24;
const statsLearnableMargin = () => 12;
const logWidth = () => Math.floor(combatWidth() * 0.5);
const logHeight = () => Math.floor(combatHeight() * 0.3);
const logY = () => Math.floor(headerHeight() + (combatHeight() * 0.7));
const logX = () => Math.floor(combatWidth() * 0.2);
module.exports = {
TEXT: {
NORMAL: { fontFamily: 'Jura', fontSize: 18, color: '#ffffff' },
LEARNABLE: { fontFamily: 'Jura', fontSize: 18, color: '#ffffff' },
HEADER: { fontFamily: 'Jura', fontSize: 24, color: '#ffffff', fontStyle: 'bold' },
HOVER: { fontFamily: 'Jura', fontSize: 16, color: '#ffffff', backgroundColor: '#222222' },
},
POSITIONS: {
HEADER: {
width: headerWidth,
height: headerHeight,
},
CONSTRUCT_LIST: {
x: menuConstructListX,
y: menuConstructListY,
width: menuConstructListWidth,
height: menuConstructListHeight,
},
MENU_MAIN: {
x: menuMainX,
y: menuMainY,
width: menuMainWidth,
height: menuMainHeight,
},
HOME_MAIN: {
x: homeMainX,
y: homeMainY,
width: homeMainWidth,
height: homeMainHeight,
},
NAVIGATION: {
x: menuNavigationX,
y: menuNavigationY,
width: menuNavigationWidth,
height: menuNavigationHeight,
},
ITEM_LIST: {
x: itemListX,
y: itemListY,
width: itemListWidth,
height: itemListHeight,
itemWidth: itemBlockWidth,
itemHeight: itemBlockHeight,
},
STATS: {
x: statsX,
y: statsY,
width: statsWidth,
height: statsHeight,
knownX: statsKnownX,
learnableX: statsLearnableX,
textMargin: statsTextMargin,
learnableMargin: statsLearnableMargin,
},
COMBAT: {
x: combatX,
y: combatY,
width: combatWidth,
height: combatHeight,
constructMargin: combatConstructMargin,
textMargin: combatTextMargin,
LOG: {
x: logX,
y: logY,
width: logWidth,
height: logHeight,
},
},
},
COLOURS: {
BLUE: 0x004bfe,
CYAN: 0x27e7c0,
PURPLE: 0x61008c,
YELLOW: 0xfdfe02,
ORANGE: 0xff9215,
PINK: 0xe766b6,
GRAY: 0x9d9ea0,
LBLUE: 0x87c6f2,
GREEN: 0x166c4f,
BROWN: 0x583108,
BLACK: 0x000000,
RED: 0xff0000,
WHITE: 0xffffff,
SELECT: 0x003300,
},
DELAYS: {
MOVE_CREEP: 500,
DAMAGE_TICK: 500,
ANIMATION_DURATION: 1000,
// wall: [500],
// spit: [300, 500],
// gravBomb: [300, 500],
// gravBlast: [300, 500],
// chargeBall: [300, 500],
},
ITEMS: {
SKILLS: {
Amplify: {
description: 'increase the magic damage dealt by a construct',
colours: '1 Green 1 Blue',
},
Attack: {
description: 'a fast attack with red damage',
upgrades: 'combine with 2 red / blue / green - red + blue attack not implemented',
},
Banish: {
description: 'target construct is prevented from casting any skills and taking any damage',
colours: '1 Red 1 Green',
},
Blast: {
description: 'blast the target with magic damage',
colours: '2 Blue',
},
Block: {
description: 'decreases incoming red damage for 1T',
upgrades: 'combine with 2 red / blue / green',
},
Buff: {
description: 'increase target construct speed',
upgrades: 'combine with 2 red / blue / green',
},
Clutch: {
description: '??????',
colours: '1 Red 1 Green',
},
Corrupt: {
description: 'Inflicts a dot to attacker while active',
colours: '2 Blue',
},
Curse: {
description: 'target construct takes increased magic damage',
colours: '2 Blue',
},
Debuff: {
description: 'reduce target construct speed',
upgrades: 'combine with 2 red / blue / green',
},
Decay: {
description: 'afflict a construct with a blue damage based damage over time debuff',
colours: '1 Green 1 Blue',
},
Empower: {
description: 'increase the red damage dealt by a construct',
colours: '2 Red',
},
Haste: {
description: 'magical skill that increases speed of target construct',
colours: '1 Red 1 Blue',
},
Heal: {
description: 'heal a construct with blue damage',
colours: '2 Green',
},
Hex: {
description: 'magical bsed skill that prevents target construct from using any skills',
colours: '1 Red 1 Blue',
},
Hostility: {
description: 'magical bsed skill that prevents target construct from using any skills',
colours: '2 Blue',
},
Invert: {
description: 'reverse ???',
colours: '1 Red 1 Blue',
},
Parry: {
description: 'prevents all red damage for 1T',
colours: '2 Red',
},
Purge: {
description: 'remove magical buffs from target construct',
colours: '2 Green',
},
Purify: {
description: 'remove magical debuffs from target construct',
colours: '1 Red 1 Green',
},
Recharge: {
description: 'restore something',
colours: '1 Red 1 Blue',
},
Reflect: {
description: 'reflect damage back to attacker',
colours: '2 Green',
},
Riposte: {
description: '???',
},
Ruin: {
description: 'Stun the entire enemy team',
colours: '2 Blue',
},
Shield: {
description: 'grants immunity to magical skills to target construct',
colours: '1 Green 1 Blue',
},
Silence: {
description: 'prevent target construct from casting magical skills',
colours: '1 Green 1 Blue',
},
Siphon: {
description: 'siphon hp from target construct with a blue damage based debuff',
colours: '1 Green 1 Blue',
},
Slay: {
description: '????',
},
Slow: {
description: 'magical skill that reduces speed of target construct',
colours: '1 Red 1 Green',
},
Snare: {
description: 'prevents red skills from being used for 2T',
colours: '2 Red',
},
Strangle: {
description: 'Stun the enemy and inflict physical damage over 3T',
colours: '2 Red',
},
Strike: {
description: 'Fast attacking red skill',
colours: '2 Red',
},
Stun: {
description: 'red skill hat prevents target construct from using any skills',
upgrades: 'combine with 2 red / blue / green',
},
Taunt: {
description: 'Enemy skills will prioritise constructs with this skill active',
colours: '1 Red 1 Green',
},
Throw: {
description: 'stuns and makes the target take increased red damage',
colours: '2 Green',
},
Triage: {
description: 'grants a blue damage based healing over time buff',
colours: '2 Green',
},
},
SPECS: {
Damage: {
description: 'Increase red / green / blue power stats construct',
upgrades: 'combine with 2 red / blue / green',
},
Hp: {
description: 'Increases health of construct',
upgrades: 'combine with 2 red / blue / green',
},
Speed: {
description: 'Increases speed of construct',
upgrades: 'combine with 2 red / blue / green',
},
},
COLOURS: {
Red: {
description: 'Used to create offensive type combos - fast and chaotic',
},
Green: {
description: 'Used to create defensive / healing type combos',
},
Blue: {
description: 'Used to create offensive type combos - slow and reliable',
},
},
},
};

View File

@ -1,77 +0,0 @@
const Phaser = require('phaser');
const Header = require('./header');
const Home = require('./home');
const Menu = require('./menu');
const Combat = require('./combat');
// const Background = require('./background');
function renderConstructs() {
const config = {
type: Phaser.CANVAS,
// backgroundColor: '#181818',
resolution: window.devicePixelRatio,
scale: {
mode: Phaser.Scale.FIT,
width: Math.floor(window.innerHeight * 1.6),
height: Math.floor(window.innerHeight),
max: {
width: Math.floor(window.innerHeight * 1.6),
height: Math.floor(window.innerHeight),
},
},
antialias: true,
physics: {
default: 'arcade',
arcade: {
debug: false,
gravity: { y: 0 },
},
},
scene: [
// Background,
Header,
],
};
const game = new Phaser.Game(config);
function changeData(parent, key, data) {
// Don't load other scenes if you're not logged in
if (!game.registry.get('account')) return false;
if (key === 'home') {
if (data) return game.scene.add('Home', Home, true);
}
if (key === 'menu') {
if (!data || game.registry.get('inMenu')) return false;
if (data) return game.scene.add('Menu', Menu, true);
}
if (key === 'game') {
if (!data || game.registry.get('inGame')) return false;
return game.scene.add('Combat', Combat, true);
}
return true;
}
game.registry.events.on('changedata', changeData);
game.registry.events.on('setdata', changeData);
window.addEventListener('mouseup', () => game.registry.set('pan', false));
window.addEventListener('mousedown', () => game.registry.set('pan', true));
window.addEventListener('resize', () => {
game.scale.displaySize.maxWidth = window.innerHeight * 1.6;
game.scale.displaySize.maxHeight = window.innerHeight;
});
return game;
}
module.exports = renderConstructs;

View File

@ -1,132 +0,0 @@
const Phaser = require('phaser');
const { TEXT } = require('.././constants');
const BOX = `
#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable
#ifdef GL_ES
precision mediump float;
#endif
#extension GL_OES_standard_derivatives : enable
uniform float time;
uniform vec2 resolution;
uniform vec2 dimensions;
uniform vec2 offset;
uniform vec3 colour;
uniform sampler2D uMainSampler;
varying vec2 outTexCoord;
varying vec4 outTint;
float sdBox( in vec2 p, in vec2 b )
{
vec2 d = abs(p)-b;
return length(max(d,vec2(0))) + min(max(d.x,d.y),0.0);
}
void main()
{
vec2 p = (gl_FragCoord.xy / resolution.xy);
p.x -= offset.x / resolution.x + dimensions.x / (2.0 * resolution.x);
p.y -= -1.0 * offset.y / resolution.y + ((resolution.y - dimensions.y) / (2.0 * resolution.y)) + 0.5;
vec2 dim = 0.5 * dimensions / resolution.xy;
float d = sdBox(p, dim);
float tb = abs(sin(time)) + 0.9;
vec3 col = tb * colour - sign(d) * vec3(0.1);
col *= 0.0 + exp(-100.0 * abs(d));
col += colour * 0.8;
vec4 texel = texture2D(uMainSampler, outTexCoord);
texel *= vec4(outTint.rgb * outTint.a, outTint.a);
gl_FragColor = vec4(col, 1.0);
}
`;
class CustomPipeline extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline {
constructor(game) {
super({ game, renderer: game.renderer, fragShader: BOX });
}
}
function rgbToHex(rgb) {
const getHex = (c) => {
const hex = Math.floor(c * 255).toString(16);
return hex.length === 1 ? `0${hex}` : hex;
};
return `0x${getHex(rgb[0])}${getHex(rgb[1])}${getHex(rgb[2])}`;
}
class BoxEffect extends Phaser.GameObjects.Graphics {
constructor(scene, x, y, width, height, colour, tag) {
super(scene);
this.tag = tag;
this.customPipeline = scene.game.renderer.addPipeline(tag, new CustomPipeline(scene.game));
this.customPipeline.setFloat2('resolution', 1600, 1000);
this.customPipeline.setFloat2('offset', x, y);
this.customPipeline.setFloat2('dimensions', width, height);
this.customPipeline.setFloat3('colour', colour[0], colour[1], colour[2]);
this.bgTime = 10.0;
const radius = height / 2;
this.fillStyle(rgbToHex(colour), 1.0);
this.fillRect(x + radius, y, width - radius * 2, height);
this.fillCircle(x + radius, y + radius, radius);
this.fillCircle(x + width - radius, y + radius, radius);
this.on('destroy', () => {
scene.game.renderer.removePipeline(tag);
});
}
activate() {
this.setPipeline(this.tag);
}
deactivate() {
this.resetPipeline();
}
update() {
this.bgTime += 0.05;
this.customPipeline.setFloat1('time', this.bgTime);
}
}
class Button extends Phaser.GameObjects.Group {
constructor(scene, props) {
const {
x, y, width, height, colour, glTag, bText, callback,
} = props;
super(scene, { classType: BoxEffect, runChildUpdate: true });
const leaveGame = scene.add
.rectangle(x, y, width, height, 0xaaaaaa, 0xffffff)
.setInteractive()
.setOrigin(0);
const effect = scene.add.existing(new BoxEffect(scene, x, y, width, height, colour, glTag));
this.add(effect);
leaveGame
.on('pointerdown', callback)
.on('pointerover', () => effect.activate())
.on('pointerout', () => effect.deactivate());
this.buttonText = scene.add.text(leaveGame.getCenter().x, leaveGame.getCenter().y, bText, TEXT.HEADER).setOrigin(0.5, 0.5);
}
}
module.exports = Button;

View File

@ -1,86 +0,0 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { COMBAT }, COLOURS } = require('.././constants');
const CONSTRUCT_MARGIN = COMBAT.constructMargin();
const TEXT_MARGIN = COMBAT.textMargin();
const statBarDimensions = (team, iter, margin) => {
const statBarWidth = COMBAT.width() * 0.07;
const statBarHeight = TEXT_MARGIN / 1.5;
const statBarX = (COMBAT.width() - statBarWidth) * team;
const statBarY = COMBAT.y() + TEXT_MARGIN * (margin + 1) + CONSTRUCT_MARGIN * iter + COMBAT.height() * 0.07;
return { statBarX, statBarY, statBarWidth, statBarHeight };
};
const statTextCoord = (team, iter, margin) => {
const statTextX = team ? COMBAT.width() - COMBAT.width() * 0.075 : COMBAT.width() * 0.075;
const statTextY = COMBAT.y() + TEXT_MARGIN * (margin + 1) + CONSTRUCT_MARGIN * iter + COMBAT.height() * 0.07;
return { statTextX, statTextY };
};
class StatBar extends Phaser.GameObjects.Graphics {
constructor(scene, construct, type) {
super(scene);
this.constructObj = construct;
this.type = type;
if (type === 'HP') {
this.val = this.constructObj.construct.hp.value;
this.max = this.constructObj.construct.hp.max;
this.margin = 0;
} else if (type === 'Red Shield') {
this.val = this.constructObj.construct.red_shield.value;
this.max = this.constructObj.construct.red_shield.max;
this.margin = 1;
} else if (type === 'Blue Shield') {
this.val = this.constructObj.construct.blue_shield.value;
this.max = this.constructObj.construct.blue_shield.max;
this.margin = 2;
} else if (type === 'Evasion') {
this.val = this.constructObj.construct.evasion.value;
this.max = this.constructObj.construct.evasion.max;
this.margin = 3;
}
const { statTextX, statTextY } = statTextCoord(construct.team, construct.iter, this.margin);
this.statText = scene.add.text(statTextX, statTextY, '', TEXT.NORMAL).setOrigin(construct.team, 0);
this.drawStatBar();
}
drawStatBar() {
this.clear();
const {
statBarX, statBarY, statBarWidth, statBarHeight,
} = statBarDimensions(this.constructObj.team, this.constructObj.iter, this.margin);
this.statText.text = `${this.val.toString()} / ${this.max.toString()} ${this.type}`;
// Draw Black Border
this.fillStyle(COLOURS.BLACK);
this.fillRect(statBarX, statBarY, statBarWidth, statBarHeight);
// White fill
this.fillStyle(COLOURS.WHITE);
this.fillRect(statBarX + 2, statBarY + 2, statBarWidth - 4, statBarHeight - 4);
// Fill the health bar
const statPercentage = this.val / this.max;
if (statPercentage < 0.3) {
this.fillStyle(COLOURS.RED);
} else if (statPercentage < 0.65) {
this.fillStyle(COLOURS.YELLOW);
} else {
this.fillStyle(0x00ff00); // str8 up green
}
const statWidth = statBarWidth * statPercentage;
this.fillRect(statBarX + 2, statBarY + 2, statWidth, statBarHeight - 4);
}
takeDamage(value) {
if (value > 0) {
this.val = (value >= this.val) ? 0 : this.val -= value;
} else {
this.val = (this.val - value > this.max) ? this.max : this.val -= value;
}
if (this.val === 0 && this.type === 'HP') this.constructObj.setKo();
this.drawStatBar();
}
}
module.exports = StatBar;

View File

@ -1,56 +0,0 @@
const Phaser = require('phaser');
const {
TEXT,
COLOURS,
} = require('../constants');
function FindColour(item) {
// Future add skills and use a constants lookup file ??
switch (item) {
case 'Green': return 0x396E26;
case 'Red': return 0x622433;
case 'Blue': return 0x223158;
// case 'Green': return 0x043003;
// case 'Red': return 0x5C0202;
// case 'Blue': return 0x040345;
default: return 0x222222;
}
}
class Item extends Phaser.GameObjects.Container {
constructor(scene, item, index, x, y, width, height) {
super(scene, x, y);
this.scene = scene;
this.item = item;
this.index = index;
this.origX = x;
this.origY = y;
this.width = width;
this.height = height;
this.colour = FindColour(item);
this.box = scene.add
.rectangle(0, 0, width, height, this.colour);
this.text = scene.add
// .text(0, 0, `${action} x${count}`, TEXT.NORMAL)
.text(0, 0, `${item}`, TEXT.NORMAL)
.setOrigin(0.5, 0.5);
this.add(this.box);
this.add(this.text);
this.setSize(width, height);
this.setInteractive();
}
changeOrigin(x, y) {
this.origX = x + this.width / 2;
this.origY = y + this.height / 2;
}
}
module.exports = Item;

View File

@ -1,73 +0,0 @@
const Phaser = require('phaser');
class LineBox extends Phaser.GameObjects.Graphics {
constructor(scene, x, y, width, height, colour, speed) {
super(scene);
this.colour = colour;
this.x0 = x; this.x1 = x + width;
this.y0 = y; this.y1 = y + height;
const margin = Math.abs(Math.floor((this.x1 - this.x0) / 4));
this.lineCoord = [this.x0 + margin, this.x1 - margin, this.y0, this.y0, 0];
this.speed = speed;
}
update() {
this.clear();
let vertX = this.x1;
let horizY = this.y0;
const genLine = () => {
switch (this.lineCoord[4]) {
case 0:
if (this.lineCoord[1] < this.x1) return [1, 1, 0, 0, 0];
return [0, 0, 0, 0, 1];
case 1:
if (this.lineCoord[0] < this.x1) return [1, 0, 0, 1, 1];
return [0, 0, 0, 0, 2];
case 2:
if (this.lineCoord[3] < this.y1) return [0, 0, 1, 1, 2];
return [0, 0, 0, 0, 3];
case 3:
horizY = this.y1;
if (this.lineCoord[2] < this.y1) return [-1, 0, 1, 0, 3];
return [0, 0, 0, 0, 4];
case 4:
horizY = this.y1;
if (this.lineCoord[0] > this.x0) return [-1, -1, 0, 0, 4];
return [0, 0, 0, 0, 5];
case 5:
horizY = this.y1;
vertX = this.x0;
if (this.lineCoord[1] > this.x0) return [0, -1, -1, 0, 5];
return [0, 0, 0, 0, 6];
case 6:
vertX = this.x0;
if (this.lineCoord[2] >= this.y0) return [0, 0, -1, -1, 6];
return [0, 0, 0, 0, 7];
case 7:
vertX = this.x0;
if (this.lineCoord[3] >= this.y0) return [0, 1, 0, -1, 7];
return [0, 0, 0, 0, 0];
default: return false;
}
};
for (let i = 0; i < this.speed; i += 1) {
const delta = genLine();
this.lineCoord = this.lineCoord.map((x, j) => {
if (j < 4) return (x + delta[j]);
return delta[j];
});
}
this.lineStyle(5, this.colour, 1);
this.lineBetween(this.lineCoord[0], horizY, this.lineCoord[1], horizY);
this.lineBetween(vertX, this.lineCoord[2], vertX, this.lineCoord[3]);
}
}
class LineGroup extends Phaser.GameObjects.Group {
constructor(scene) {
super(scene, { classType: LineBox, runChildUpdate: true });
}
}
module.exports = { LineGroup, LineBox }

View File

@ -1,53 +0,0 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { MENU_MAIN } } = require('./constants');
const X = MENU_MAIN.x();
const Y = MENU_MAIN.y();
const WIDTH = MENU_MAIN.width();
const HEIGHT = MENU_MAIN.height();
const ROW_SIZE = 50;
const LOBBY_WIDTH = WIDTH / 2;
class GameList extends Phaser.Scene {
constructor() {
super({ key: 'GameList' });
}
create(gameList) {
this.cameras.main.setViewport(X, Y, WIDTH, HEIGHT);
const ws = this.registry.get('ws');
this.add.text(WIDTH / 3, 0, 'PVP open games', TEXT.HEADER);
const gameRow = (game, i) => {
const GAME_X = WIDTH / 6;
const GAME_Y = 1.3 * ROW_SIZE * (i + 1);
const gameBox = this.add
.rectangle(GAME_X, GAME_Y, LOBBY_WIDTH, ROW_SIZE, 0x222222)
.setInteractive()
.setOrigin(0);
const TITLE = `${game.teams[0].constructs.map(c => c.name).join(', ')} - ${game.team_size}v${game.team_size}`;
this.add
.text(gameBox.getCenter().x, gameBox.getCenter().y, TITLE, TEXT.NORMAL)
.setOrigin(0.5, 0.5);
gameBox.on('pointerdown', () => {
const constructs = this.registry.get('constructs');
const team = constructs.filter(c => c.active).map(c => c.id);
ws.sendGameJoin(game.id, team);
});
};
gameList.forEach(gameRow);
return true;
}
cleanUp() {
this.scene.remove();
}
}
module.exports = GameList;

View File

@ -1,27 +0,0 @@
const Phaser = require('phaser');
const fs = require('fs');
const { TEXT } = require('./constants');
const aztecAtlas = require('../../assets/aztec.atlas.json');
class Header extends Phaser.Scene {
constructor() {
super({ key: 'Header', active: true });
}
preload() {
const aztecImg = new Image();
aztecImg.src = `data:image/png;base64,${new Buffer.from(fs.readFileSync('./assets/aztec.clean.png')).toString('base64')}`;
aztecImg.onload = () => {
this.textures.addAtlas('aztec', aztecImg, aztecAtlas);
};
}
create() {
this.add.text(0, 0, 'constructs.gg', TEXT.HEADER);
}
}
module.exports = Header;

View File

@ -1,142 +0,0 @@
const Phaser = require('phaser');
const { remove } = require('lodash');
const { TEXT, COLOURS, POSITIONS: { CONSTRUCT_LIST } } = require('./constants');
const genAvatar = require('./avatar');
const { LineGroup, LineBox } = require('./elements/outline.rotate');
const ROW_HEIGHT = CONSTRUCT_LIST.height() * 0.1;
const ROW_WIDTH = CONSTRUCT_LIST.width();
const menuY = CONSTRUCT_LIST.height() * 0.8;
const KEY_MAP = [
'keydown-ONE',
'keydown-TWO',
'keydown-THREE',
];
const NULL_UUID = '00000000-0000-0000-0000-000000000000';
class HomeConstructList extends Phaser.Scene {
constructor() {
super({ key: 'HomeConstructs' });
}
updateData(parent, key, data) {
if (key === 'constructList') {
KEY_MAP.forEach(k => this.input.keyboard.removeListener(k));
this.scene.restart();
}
}
create() {
// this.cameras.main.setViewport(CONSTRUCT_LIST.x(), CONSTRUCT_LIST.y(), CONSTRUCT_LIST.width(), CONSTRUCT_LIST.height());
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
const constructs = this.registry.get('constructList');
const lineGroup = this.add.existing(new LineGroup(this));
if (!constructs) return true;
const ws = this.registry.get('ws');
this.activeConstructs = [];
// We only display 3 constructs others can be viewed in construct list (soon TM)
for (let i = 0; i < constructs.length; i += 1) {
const construct = constructs[i];
const BOX_WIDTH = Math.floor(ROW_WIDTH / 5);
const ROW_X = BOX_WIDTH * 2 * (i % 3);
const ROW_Y = CONSTRUCT_LIST.y() + (Math.floor(i / 3)) * ROW_HEIGHT * 1.5;
const ACTIVE_FILL = 0.2;
const FILL = Object.values(COLOURS)[i];
// Selection of constructs
// Construct avatar and interaction box
const cReady = this.add
.rectangle(ROW_X, ROW_Y + ROW_HEIGHT * 0.2, BOX_WIDTH * 2, ROW_HEIGHT, FILL)
.setInteractive()
.setOrigin(0);
cReady.setAlpha(0.2);
cReady.on('pointerdown', () => {
lineGroup.clear(true, true);
if (this.activeConstructs.includes(cReady)) {
remove(this.activeConstructs, n => n === cReady);
cReady.setAlpha(0.2);
} else {
this.activeConstructs.push(cReady);
cReady.setAlpha(0.75);
lineGroup.add(this.add.existing(
new LineBox(this, cReady.x, cReady.y, cReady.width, cReady.height, cReady.fillColor, 3)
));
}
});
cReady.itemSelect = () => {
cReady.setFillStyle(COLOURS.SELECT);
};
cReady.itemDeselect = () => {
cReady.setFillStyle(FILL, ACTIVE_FILL);
};
cReady.construct = construct;
this.add.image(
cReady.getCenter().x,
cReady.getCenter().y,
'aztec',
genAvatar(construct.name)
);
this.add.text(ROW_X + BOX_WIDTH, ROW_Y, construct.name, TEXT.HEADER)
.setOrigin(0.5, 0.5);
}
// Add Spawn Construct Option
const spawn = this.add
.rectangle(ROW_WIDTH * 0.05, menuY, ROW_WIDTH * 0.2, ROW_HEIGHT * 0.5, 0x888888)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => {
this.game.events.emit('CONSTRUCT_SPAWN');
});
this.add
.text(spawn.getCenter().x, spawn.getCenter().y, '+', TEXT.HEADER)
.setOrigin(0.5, 0.5);
const joinNormal = this.add
.rectangle(ROW_WIDTH * 0.3, menuY, ROW_WIDTH * 0.4, ROW_HEIGHT * 0.5, 0x888888)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => {
const playerConstructs = [];
this.activeConstructs.forEach(obj => playerConstructs.push(obj.construct.id));
ws.sendPlayerConstructsSet(NULL_UUID, playerConstructs);
});
this.add
.text(joinNormal.getCenter().x, joinNormal.getCenter().y, 'Join Normal', TEXT.HEADER)
.setOrigin(0.5, 0.5);
const joinInstance = this.add
.rectangle(ROW_WIDTH * 0.8, menuY, ROW_WIDTH * 0.4, ROW_HEIGHT * 0.5, 0x888888)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => {
const playerConstructs = [];
this.activeConstructs.forEach(obj => playerConstructs.push(obj.construct.id));
ws.sendInstanceJoin(playerConstructs);
});
this.add
.text(joinInstance.getCenter().x, joinInstance.getCenter().y, 'New Instance', TEXT.HEADER)
.setOrigin(0.5, 0.5);
return this;
}
cleanUp() {
KEY_MAP.forEach(k => this.input.keyboard.removeListener(k));
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
this.scene.remove();
}
}
module.exports = HomeConstructList;

View File

@ -1,48 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { HOME_MAIN }, TEXT } = require('./constants');
const X = HOME_MAIN.x();
const Y = HOME_MAIN.y();
const WIDTH = HOME_MAIN.width();
const HEIGHT = HOME_MAIN.height();
const NULL_UUID = '00000000-0000-0000-0000-000000000000';
class HomeRankings extends Phaser.Scene {
constructor() {
super({ key: 'HomeInstances' });
}
create() {
this.add.text(X, Y, 'Instances Scene', TEXT.HEADER);
const playerList = this.registry.get('playerList');
const addInstance = (player, i) => {
const joinNormal = this.add
.rectangle(X, Y + HEIGHT * 0.15 * (i + 1), WIDTH * 0.5, HEIGHT * 0.1, 0x888888)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => {
if (player.ready) {
this.registry.set('player', player);
this.registry.get('ws').sendInstanceReady(player.instance);
} else {
this.game.events.emit('SET_PLAYER', player);
this.registry.get('ws').sendInstanceScores(player.instance);
}
});
const name = player.instance === NULL_UUID ? 'Normal Mode' : `${player.instance.substring(0, 5)}`;
const disp = player.ready ? name.concat(' - in progress') : name;
this.add
.text(joinNormal.getCenter().x, joinNormal.getCenter().y, disp, TEXT.NORMAL)
.setOrigin(0.5, 0.5);
};
playerList.forEach(addInstance);
}
cleanUp() {
this.scene.remove();
}
}
module.exports = HomeRankings;

View File

@ -1,91 +0,0 @@
const Phaser = require('phaser');
const HomeConstructs = require('./home.constructs');
const HomeNavigation = require('./home.navigation');
const HomeRankings = require('./home.rankings');
const HomeNews = require('./home.news');
const HomeShop = require('./home.shop');
const HomeInstances = require('./home.instances');
const FIXED_SCENES = [
'HomeConstructs',
'HomeNavigation',
];
const VAR_SCENES = [
'HomeRankings',
'HomeNews',
'HomeShop',
'HomeInstances',
];
class Home extends Phaser.Scene {
constructor() {
super({ key: 'Home', active: true });
}
create() {
this.registry.get('ws').sendAccountInstances();
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
this.scene.manager.add('HomeConstructs', HomeConstructs, true);
this.scene.manager.add('HomeNavigation', HomeNavigation, true);
return true;
}
updateData(parent, key, data) {
if (!data) return false;
// Controls which scene shows in the main top right section
switch (key) {
case 'game': return this.cleanUp();
case 'menu': return this.cleanUp();
case 'homeInstances': return this.newMainScene('HomeInstances', HomeInstances, data);
case 'homeRankings': return this.newMainScene('HomeRankings', HomeRankings, data);
case 'homeNews': return this.newMainScene('HomeNews', HomeNews, data);
case 'homeShop': return this.newMainScene('HomeShop', HomeShop, data);
default: return false;
}
}
newMainScene(key, scene, data) {
let addScene = true;
// List of scenes which could be occupying the main section of the menu
VAR_SCENES.forEach((sKey) => {
if (this.scene.manager.keys[sKey]) {
if (key === sKey) {
// If there is new data for the current scene restart
this.scene.manager.keys[sKey].scene.restart(data);
addScene = false;
} else {
// Delete the old scene
this.scene.manager.keys[sKey].cleanUp();
}
}
});
if (addScene) this.scene.manager.add(key, scene, true, data);
}
cleanUp() {
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
// Delete scenes which could be showing before switching to battle scene
const removeScenes = (sKey) => {
if (this.scene.get(sKey)) this.scene.get(sKey).cleanUp();
};
FIXED_SCENES.forEach(removeScenes);
VAR_SCENES.forEach(removeScenes);
this.scene.remove();
}
}
module.exports = Home;

View File

@ -1,61 +0,0 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { NAVIGATION } } = require('./constants');
const X = NAVIGATION.x();
const Y = NAVIGATION.y();
const WIDTH = NAVIGATION.width();
const HEIGHT = NAVIGATION.height();
const BTN_WIDTH = Math.floor(WIDTH / 6);
const BTN_HEIGHT = Math.floor(HEIGHT / 3);
class HomeNavigation extends Phaser.Scene {
constructor() {
super({ key: 'HomeNavigation' });
}
create() {
/* const ranks = this.add
.rectangle(X, Y, BTN_WIDTH, BTN_HEIGHT, 0x222222)
.setInteractive()
.setOrigin(0);
this.add
.text(ranks.getCenter().x, ranks.getCenter().y, 'Rankings', TEXT.HEADER)
.setOrigin(0.5, 0.5);
ranks.on('pointerdown', () => this.registry.set('homeRankings', true));
const news = this.add
.rectangle(X + BTN_WIDTH * 1.5, Y, BTN_WIDTH, BTN_HEIGHT, 0x222222)
.setInteractive()
.setOrigin(0);
this.add
.text(news.getCenter().x, news.getCenter().y, 'News', TEXT.HEADER)
.setOrigin(0.5, 0.5);
news.on('pointerdown', () => this.registry.set('homeNews', true));
const shop = this.add
.rectangle(X + BTN_WIDTH * 3, Y, BTN_WIDTH, BTN_HEIGHT, 0x222222)
.setInteractive()
.setOrigin(0);
this.add
.text(shop.getCenter().x, shop.getCenter().y, 'Shop', TEXT.HEADER)
.setOrigin(0.5, 0.5);
shop.on('pointerdown', () => this.registry.set('homeShop', true));
const instances = this.add
.rectangle(X + BTN_WIDTH * 4.5, Y, BTN_WIDTH, BTN_HEIGHT, 0x222222)
.setInteractive()
.setOrigin(0);
this.add
.text(instances.getCenter().x, instances.getCenter().y, 'Instances', TEXT.HEADER)
.setOrigin(0.5, 0.5);
instances.on('pointerdown', () => this.registry.set('homeInstances', true));*/
}
cleanUp() {
this.scene.remove();
}
}
module.exports = HomeNavigation;

View File

@ -1,24 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { HOME_MAIN }, TEXT } = require('./constants');
const X = HOME_MAIN.x();
const Y = HOME_MAIN.y();
const WIDTH = HOME_MAIN.width();
const HEIGHT = HOME_MAIN.height();
class HomeNews extends Phaser.Scene {
constructor() {
super({ key: 'HomeNews' });
}
create() {
this.add.text(X, Y, 'News Scene', TEXT.HEADER);
this.add.text(X, Y + HEIGHT * 0.1, 'some other stuff here', TEXT.NORMAL);
}
cleanUp() {
this.scene.remove();
}
}
module.exports = HomeNews;

View File

@ -1,24 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { HOME_MAIN }, TEXT } = require('./constants');
const X = HOME_MAIN.x();
const Y = HOME_MAIN.y();
const WIDTH = HOME_MAIN.width();
const HEIGHT = HOME_MAIN.height();
class HomeRankings extends Phaser.Scene {
constructor() {
super({ key: 'HomeRankings' });
}
create() {
this.add.text(X, Y, 'Rankings Scene', TEXT.HEADER);
this.add.text(X, Y + HEIGHT * 0.1, 'some stuff here', TEXT.NORMAL);
}
cleanUp() {
this.scene.remove();
}
}
module.exports = HomeRankings;

View File

@ -1,24 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { HOME_MAIN }, TEXT } = require('./constants');
const X = HOME_MAIN.x();
const Y = HOME_MAIN.y();
const WIDTH = HOME_MAIN.width();
const HEIGHT = HOME_MAIN.height();
class HomeShop extends Phaser.Scene {
constructor() {
super({ key: 'HomeShop' });
}
create() {
this.add.text(X, Y, 'Shop Scene', TEXT.HEADER);
this.add.text(X, Y + HEIGHT * 0.1, 'rulul', TEXT.NORMAL);
}
cleanUp() {
this.scene.remove();
}
}
module.exports = HomeShop;

View File

@ -1,78 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { MENU_MAIN }, TEXT, ITEMS: { SKILLS, SPECS, COLOURS } } = require('./constants');
const X = MENU_MAIN.x();
const Y = MENU_MAIN.y();
const WIDTH = MENU_MAIN.width();
const HEIGHT = MENU_MAIN.height();
const UNEQUIP_Y = Y + Math.floor(HEIGHT * 0.7);
const UNEQUIP_WIDTH = Math.floor(WIDTH * 0.4);
const UNEQUIP_HEIGHT = Math.floor(HEIGHT * 0.15);
class ItemInfo extends Phaser.Scene {
constructor() {
super({ key: 'ItemInfo' });
}
create(props) {
const { item, construct } = props;
if (!item) return false;
// Default item text
let title = item;
let description = 'Description missing';
let upgrades = '';
let colours = '';
// Replace item text with constants if it exists
if (SKILLS[item]) {
title = `Skill Item - ${item}`;
({ description } = SKILLS[item]);
if (SKILLS[item].upgrades) ({ upgrades } = SKILLS[item]);
if (SKILLS[item].colours) ({ colours } = SKILLS[item]);
} else if (SPECS[item]) {
title = `Spec Item - ${item}`;
({ description } = SPECS[item]);
if (SPECS[item].upgrades) ({ upgrades } = SPECS[item]);
} else if (COLOURS[item]) {
title = `Colour Item - ${item}`;
({ description } = COLOURS[item]);
if (COLOURS[item].upgrades) ({ upgrades } = COLOURS[item]);
}
// Add text objects
this.add.text(X, Y, title, TEXT.HEADER);
this.add
.text(X, Y + HEIGHT * 0.1, description, TEXT.NORMAL)
.setWordWrapWidth(WIDTH * 0.75);
this.add
.text(X, Y + HEIGHT * 0.25, upgrades, TEXT.NORMAL)
.setWordWrapWidth(WIDTH * 0.75);
if (colours !== '') {
this.add
.text(X, Y + HEIGHT * 0.35, `Adds ${colours} to construct`, TEXT.NORMAL)
.setWordWrapWidth(WIDTH * 0.75);
}
if (!construct) return true;
const ws = this.registry.get('ws');
const { vbox } = this.registry.get('player');
const unEquip = this.add.rectangle(X, UNEQUIP_Y, UNEQUIP_WIDTH, UNEQUIP_HEIGHT, 0x222222)
.setOrigin(0, 0)
.setInteractive()
.on('pointerdown', () => {
ws.sendVboxUnequip(vbox.instance, construct.id, item);
this.registry.set('constructStats', construct);
});
this.add.text(unEquip.getCenter().x, unEquip.getCenter().y, 'unequip', TEXT.HEADER)
.setOrigin(0.5, 0.5);
return true;
}
cleanUp() {
this.scene.remove();
}
}
module.exports = ItemInfo;

View File

@ -1,323 +0,0 @@
const Phaser = require('phaser');
const Item = require('./elements/item');
const {
TEXT,
POSITIONS: { ITEM_LIST },
} = require('./constants');
const X = ITEM_LIST.x();
const Y = ITEM_LIST.y();
const WIDTH = ITEM_LIST.width();
const HEIGHT = ITEM_LIST.height();
const ITEM_WIDTH = ITEM_LIST.itemWidth();
const ITEM_HEIGHT = ITEM_LIST.itemHeight();
const BOX_X = X + ITEM_WIDTH * 0.5;
const BOX_Y = Y + ITEM_HEIGHT + ITEM_HEIGHT * 2;
const BOX_ROWS = 6;
const BOX_COLUMNS = 3;
const INV_X = X + ITEM_WIDTH * 0.5;
const INV_Y = Y + ITEM_HEIGHT * 12;
const INV_ROWS = 3;
const INV_COLUMNS = 3;
const COMB_X = X + ITEM_WIDTH * 0.5;
const COMB_Y = Y + ITEM_HEIGHT * 19;
const COMB_ROWS = 1;
const COMB_COLUMNS = 3;
const drawVbox = (graphics) => {
const boxDrawX = BOX_X - ITEM_WIDTH * 0.05;
const boxDrawY = BOX_Y - ITEM_HEIGHT * 0.05;
graphics.strokeRect(boxDrawX, boxDrawY, ITEM_WIDTH * 1.1 * BOX_COLUMNS, ITEM_HEIGHT * 1.1 * BOX_ROWS);
for (let i = 0; i < (BOX_COLUMNS - 1); i += 1) {
const x = boxDrawX + (i + 1) * ITEM_WIDTH * 1.1;
graphics.lineBetween(x, boxDrawY, x, boxDrawY + ITEM_HEIGHT * 1.1 * BOX_ROWS);
}
for (let i = 0; i < (BOX_ROWS - 1); i += 1) {
const y = boxDrawY + (i + 1) * ITEM_HEIGHT * 1.1;
graphics.lineBetween(boxDrawX, y, boxDrawX + ITEM_WIDTH * 1.1 * BOX_COLUMNS, y);
}
};
const drawInventory = (graphics) => {
const invDrawX = INV_X - ITEM_WIDTH * 0.05;
const invDrawY = INV_Y - ITEM_HEIGHT * 0.05;
graphics.strokeRect(invDrawX, invDrawY, ITEM_WIDTH * 1.1 * INV_COLUMNS, ITEM_HEIGHT * 1.1 * INV_ROWS);
for (let i = 0; i < (INV_COLUMNS - 1); i += 1) {
const x = invDrawX + (i + 1) * ITEM_WIDTH * 1.1;
graphics.lineBetween(x, invDrawY, x, invDrawY + ITEM_HEIGHT * 1.1 * INV_ROWS);
}
for (let i = 0; i < (INV_ROWS - 1); i += 1) {
const y = invDrawY + (i + 1) * ITEM_HEIGHT * 1.1;
graphics.lineBetween(invDrawX, y, invDrawX + ITEM_WIDTH * 1.1 * INV_COLUMNS, y);
}
};
const drawCombiner = (graphics) => {
const combDrawX = COMB_X - ITEM_WIDTH * 0.05;
const combDrawY = COMB_Y - ITEM_HEIGHT * 0.05;
graphics.strokeRect(combDrawX, combDrawY, ITEM_WIDTH * 1.1 * COMB_COLUMNS, ITEM_HEIGHT * 1.1 * COMB_ROWS);
for (let i = 0; i < (COMB_COLUMNS - 1); i += 1) {
const x = combDrawX + (i + 1) * ITEM_WIDTH * 1.1;
graphics.lineBetween(x, combDrawY, x, combDrawY + ITEM_HEIGHT * 1.1 * COMB_ROWS);
}
for (let i = 0; i <= (COMB_ROWS - 1); i += 1) {
const y = combDrawY + (i + 1) * ITEM_HEIGHT * 1.1;
graphics.lineBetween(combDrawX, y, combDrawX + ITEM_WIDTH * 1.1 * COMB_COLUMNS, y);
}
};
class CombinerHitBox extends Phaser.GameObjects.Rectangle {
constructor(scene, x, y, i) {
super(scene, x, y, ITEM_WIDTH, ITEM_HEIGHT, 0x000000);
this.setOrigin(0);
this.x = x;
this.y = y;
this.slot = i;
this.item = false;
this.itemSelect = () => this.setFillStyle(0x222222);
this.itemDeselect = () => this.setFillStyle(0x000000);
}
allocate(item) {
if (this.item) this.deallocate();
item.setPosition(this.x + item.width / 2, this.y + item.height / 2);
this.item = item;
}
deallocate() {
if (this.item) this.item.setPosition(this.item.origX, this.item.origY);
this.item = false;
}
}
class DeleteHitBox extends Phaser.GameObjects.Rectangle {
constructor(scene, x, y) {
super(scene, x, y, ITEM_WIDTH * 3.4, ITEM_HEIGHT * 1.25, 0x444444);
this.setOrigin(0);
this.itemSelect = () => this.setFillStyle(0xff0000);
this.itemDeselect = () => this.setFillStyle(0x444444);
}
}
const itemCheckHitbox = (scene, pointer) => {
const { list } = scene.scene.get('MenuConstructList').children;
const hitboxes = list.filter(c => c.construct)
.concat(scene.children.list.filter(c => c instanceof CombinerHitBox || c instanceof DeleteHitBox));
let found;
for (let i = 0; i < hitboxes.length; i += 1) {
if (Phaser.Geom.Rectangle.ContainsPoint(hitboxes[i].getBounds(), pointer.position)) {
found = hitboxes[i];
} else {
hitboxes[i].itemDeselect();
}
}
return found;
};
class ItemList extends Phaser.Scene {
constructor() {
super({ key: 'ItemList', active: true });
}
updateData(parent, key, data) {
if (key === 'player' || key === 'scores') {
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
this.scene.restart();
}
}
create() {
const player = this.registry.get('player');
const scores = this.registry.get('scores') || [];
if (!player) return false;
const { vbox } = player;
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
if (!vbox.bound) return false;
if (!this.combinerItems || vbox.bound.length < this.registry.get('boundLength')) {
this.combinerItems = [-1, -1, -1];
}
this.registry.set('boundLength', vbox.bound.length);
const ws = this.registry.get('ws');
// Static Elements
const graphics = this.add.graphics();
graphics.lineStyle(5, 0x808080, 1.0);
drawCombiner(graphics);
drawInventory(graphics);
drawVbox(graphics);
this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 0.5, `vBox - ${vbox.bits}b`, TEXT.HEADER);
this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 11, 'inventory', TEXT.HEADER);
this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 18, 'iCombinator', TEXT.HEADER);
this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 23, `Wins: ${player.score.wins}`, TEXT.HEADER);
this.add.text(X + ITEM_WIDTH * 0.5, Y + ITEM_HEIGHT * 24, `Losses: ${player.score.losses}`, TEXT.HEADER);
const discard = this.add
.rectangle(X + ITEM_WIDTH * 0.4, Y + ITEM_HEIGHT * 1.5, ITEM_WIDTH * 3.4, ITEM_HEIGHT * 1.25, 0x444444)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => this.registry.get('ws').sendVboxDiscard(vbox.instance));
this.add.text(discard.getCenter().x, discard.getCenter().y, 'discard - 5b', TEXT.HEADER)
.setOrigin(0.5, 0.5);
const combine = this.add
.rectangle(X + ITEM_WIDTH * 0.4, Y + ITEM_HEIGHT * 20.25, ITEM_WIDTH * 3.4, ITEM_HEIGHT * 1.25, 0x444444)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => {
ws.sendVboxCombine(vbox.instance, this.combinerItems);
this.combinerItems = [-1, -1, -1];
this.children.list.filter(obj => obj instanceof CombinerHitBox).forEach(cBox => cBox.deallocate());
});
this.add.text(combine.getCenter().x, combine.getCenter().y, 'combine', TEXT.HEADER)
.setOrigin(0.5, 0.5);
for (let i = 0; i < 3; i += 1) {
const ITEM_X = ITEM_WIDTH * 1.1 * (i % COMB_COLUMNS) + COMB_X;
const ITEM_Y = ITEM_HEIGHT * 1.1 * Math.floor(i / COMB_COLUMNS) + COMB_Y;
this.add.existing(new CombinerHitBox(this, ITEM_X, ITEM_Y, i));
}
const del = this.add.existing(new DeleteHitBox(this, X + ITEM_WIDTH * 0.4, Y + ITEM_HEIGHT * 15.5));
this.add.text(del.getCenter().x, del.getCenter().y, 'drop', TEXT.HEADER)
.setOrigin(0.5, 0.5);
// Generate Items
vbox.bound.forEach((item, i) => {
const ITEM_X = ITEM_WIDTH * 1.1 * (i % INV_COLUMNS) + INV_X + ITEM_WIDTH * 0.5;
const ITEM_Y = ITEM_HEIGHT * 1.1 * Math.floor(i / INV_COLUMNS) + INV_Y + ITEM_HEIGHT * 0.5;
const clickFn = () => this.registry.set('itemInfo', { item });
const itemBox = new Item(this, item, i, ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT);
itemBox.on('pointerdown', clickFn);
this.input.setDraggable(itemBox);
this.add.existing(itemBox);
});
vbox.free.forEach((type, i) => {
type.forEach((item, j) => {
const ITEM_X = ITEM_WIDTH * 1.1 * i + BOX_X + ITEM_WIDTH * 0.5;
const ITEM_Y = ITEM_HEIGHT * 1.1 * j + BOX_Y + ITEM_HEIGHT * 0.5;
const clickFn = () => {
this.registry.set('itemInfo', { item });
ws.sendVboxAccept(vbox.instance, i, j);
};
const itemBox = new Item(this, item, i, ITEM_X, ITEM_Y, ITEM_WIDTH, ITEM_HEIGHT);
itemBox.on('pointerdown', clickFn);
this.add.existing(itemBox);
});
});
// Restore previous combiner item slots
this.combinerItems.forEach((index, i) => {
if (index === -1) return false;
const item = this.children.list.filter(obj => obj instanceof Item).find(it => it.index === index);
const hitBox = this.children.list.filter(obj => obj instanceof CombinerHitBox).find(hb => hb.slot === i);
if (item) hitBox.allocate(item);
return true;
});
// allocation functions
const allocate = (item, hitBox) => {
hitBox.allocate(item);
this.combinerItems[hitBox.slot] = item.index;
this.registry.set('combinerItems', this.combinerItems);
};
const deallocate = (item) => {
const clearIndex = this.combinerItems.indexOf(item.index);
if (clearIndex !== -1) {
this.children.list.filter(obj => obj instanceof CombinerHitBox)
.forEach((cBox) => {
if (cBox.item === item) cBox.deallocate();
});
this.combinerItems[clearIndex] = -1;
this.registry.set('combinerItems', this.combinerItems);
}
item.setPosition(item.origX, item.origY);
};
const findUnallocated = () => {
for (let i = 0; i <= 2; i += 1) {
if (this.combinerItems[i] === -1) {
return this.children.list.filter(obj => obj instanceof CombinerHitBox).find(hb => hb.slot === i);
}
} return false;
};
// this.add.text(ITEM_WIDTH * 11, ITEM_HEIGHT * 1.1, 'Scoreboard', TEXT.HEADER);
// scores.forEach(([name, score], i) => {
// const SCORE_X = ITEM_WIDTH * 11;
// const SCORE_Y = ITEM_HEIGHT * 1.1 * (i + 2);
// this.add.text(SCORE_X, SCORE_Y, `${score.wins} - ${score.losses} | ${name}`, TEXT.NORMAL);
// });
// Add Handlers
this.input.on('dragstart', (pointer, item) => {
if (!(item instanceof Item)) return false;
return true;
});
this.input.on('drag', (pointer, item, dragX, dragY) => {
if (!(item instanceof Item)) return false;
item.setPosition(dragX, dragY);
const hitBox = itemCheckHitbox(this, pointer);
if (hitBox) hitBox.itemSelect();
return true;
});
this.input.on('dragend', (pointer, item) => {
if (!(item instanceof Item)) return false;
// Check first for hitbox interaction
const hitBox = itemCheckHitbox(this, pointer);
if (hitBox) {
// hitbox can only be the combinerhitbox, deletehitbox or construct avatar
hitBox.itemDeselect();
if (hitBox instanceof CombinerHitBox) {
if (hitBox.item === item) deallocate(item);
else allocate(item, hitBox);
} else if (hitBox instanceof DeleteHitBox) {
ws.sendVboxReclaim(vbox.instance, item.index);
} else {
ws.sendVboxApply(vbox.instance, hitBox.construct.id, item.index);
deallocate(item);
} return true;
}
// If not interacting with hitbox and didn't move much try to allocate the item
if (Math.hypot(item.x - item.origX, item.y - item.origY) < Math.hypot(item.width, item.height)) {
// Check theres a free combiner slot
const cBox = findUnallocated();
if (cBox) {
allocate(item, cBox);
return true;
}
}
// If the item hasn't been allocated above reset to natural location
// Check if item needs to be deallocated
// Scene will restart if there is vbox change
deallocate(item);
return true;
});
return this;
}
cleanUp() {
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
this.scene.remove();
}
}
module.exports = ItemList;

View File

@ -1,137 +0,0 @@
const Phaser = require('phaser');
const { TEXT, COLOURS, POSITIONS: { CONSTRUCT_LIST } } = require('./constants');
const genAvatar = require('./avatar');
const Item = require('./elements/item');
const BOX_WIDTH = Math.floor(CONSTRUCT_LIST.width());
const BOX_HEIGHT = Math.floor(CONSTRUCT_LIST.height() / 3.34);
const TEXT_MARGIN = 24;
const KEY_MAP = [
'keydown-ONE',
'keydown-TWO',
'keydown-THREE',
];
class MenuConstructList extends Phaser.Scene {
constructor() {
super({ key: 'MenuConstructList' });
}
create() {
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
const player = this.registry.get('player');
if (player) this.drawConstructs(player.constructs);
}
updateData(parent, key, data) {
if (key === 'player') {
this.drawConstructs(data.constructs);
}
}
drawConstructs(constructs) {
if (!constructs) return true;
if (this.constructGroup) this.constructGroup.destroy(true);
this.constructGroup = this.add.group();
const addConstruct = (construct, i) => {
const ROW_X = CONSTRUCT_LIST.x();
const ROW_Y = CONSTRUCT_LIST.y() + BOX_HEIGHT * i * 1.1;
const ACTIVE_FILL = 0.2;
const FILL = Object.values(COLOURS)[i];
// Selection of constructs
const selectFn = () => {
this.registry.set('constructStats', construct);
};
// Construct interaction box for adding items
const constructInteract = this.add
.rectangle(ROW_X, ROW_Y, BOX_WIDTH, BOX_HEIGHT, FILL, ACTIVE_FILL)
.setInteractive()
.setOrigin(0)
.on('pointerdown', selectFn);
constructInteract.itemSelect = () => {
constructInteract.setFillStyle(COLOURS.SELECT);
};
constructInteract.itemDeselect = () => {
constructInteract.setFillStyle(FILL, ACTIVE_FILL);
};
constructInteract.construct = construct;
// Construct Avatar
const { name } = construct;
const constructImage = this.add.image(
constructInteract.getCenter().x - constructInteract.width / 4,
constructInteract.getCenter().y,
'aztec',
genAvatar(name)
);
// Add text info
const yConstructTextAlgin = Math.floor(constructInteract.y + TEXT_MARGIN / 2);
const xConstructNameAlign = Math.floor(constructInteract.x + constructInteract.width * 1 / 10);
const constructInfoText = this.add.text(xConstructNameAlign, yConstructTextAlgin, name, TEXT.HEADER);
const colourText = (c, j) => {
// Placeholder for when gems are implemented
const gemText = this.add.text(constructInteract.x + constructInteract.width * (j + 3) / 6, yConstructTextAlgin, `${c[0]} - ${c[1]}`, TEXT.HEADER);
this.constructGroup.add(gemText);
};
const { red, green, blue } = construct.colours;
const CONSTRUCT_COLOURS = [
['R', red],
['G', green],
['B', blue],
];
CONSTRUCT_COLOURS.forEach(colourText);
this.constructGroup.addMultiple([constructInteract, constructImage, constructInfoText]);
const constructSkill = (stat, j) => {
const SKILL_WIDTH = Math.floor(BOX_WIDTH / 2);
const SKILL_HEIGHT = Math.floor(BOX_HEIGHT / 8);
const SKILL_X = constructInteract.getCenter().x + constructInteract.width / 4;
const SKILL_Y = constructInteract.y + SKILL_HEIGHT * 1.15 * (j + 1.6);
const itemObj = new Item(this, stat.skill, j, SKILL_X, SKILL_Y, SKILL_WIDTH, SKILL_HEIGHT);
this.add.existing(itemObj);
itemObj.setInteractive();
const itemInfo = { item: stat.skill, construct };
itemObj.on('pointerdown', () => this.registry.set('itemInfo', itemInfo));
this.constructGroup.add(itemObj);
};
construct.skills.forEach(constructSkill);
const constructSpec = (spec, j) => {
const SKILL_WIDTH = Math.floor(BOX_WIDTH * 0.15);
const SKILL_HEIGHT = Math.floor(BOX_HEIGHT * 0.2);
const SKILL_X = Math.floor(constructInteract.x + BOX_WIDTH * (0.6 + j) * 0.175);
const SKILL_Y = Math.floor(constructInteract.y + BOX_HEIGHT * 0.875);
const itemObj = new Item(this, spec, j, SKILL_X, SKILL_Y, SKILL_WIDTH, SKILL_HEIGHT);
itemObj.setInteractive();
const itemInfo = { item: spec, construct };
itemObj.on('pointerdown', () => this.registry.set('itemInfo', itemInfo));
this.add.existing(itemObj);
this.constructGroup.add(itemObj);
};
construct.specs.forEach(constructSpec);
};
constructs.forEach(addConstruct);
return true;
}
cleanUp() {
KEY_MAP.forEach(k => this.input.keyboard.removeListener(k));
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
this.scene.remove();
}
}
module.exports = MenuConstructList;

View File

@ -1,97 +0,0 @@
const Phaser = require('phaser');
// Scenes constantly showing
const MenuConstructList = require('./menu.constructs.list');
const MenuNavigation = require('./menu.navigation');
const ItemList = require('./item.list');
// Scenes which change depending on menu context
const Zones = require('./zones');
const GameList = require('./game.list');
const StatSheet = require('./statsheet');
const ItemInfo = require('./item.info');
const FIXED_MENU_SCENES = [
'MenuConstructList',
'MenuNavigation',
'ItemList',
];
const MAIN_MENU_SCENES = [
'Zones',
'GameList',
'StatSheet',
'ItemInfo',
];
class Menu extends Phaser.Scene {
constructor() {
super({ key: 'Menu', active: true });
}
create() {
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
// Request the latest player state when we load the scene
const player = this.registry.get('player');
this.registry.get('ws').sendPlayerState(player.instance);
// When we load the menu request the latest items
// Item list will restart when the data comes in
this.scene.manager.add('MenuConstructList', MenuConstructList, true);
this.scene.manager.add('MenuNavigation', MenuNavigation, true);
this.scene.manager.add('ItemList', ItemList, true);
this.registry.set('inMenu', true);
return true;
}
updateData(parent, key, data) {
if (!data) return false;
// Controls which scene shows in the main top right section
switch (key) {
case 'game': return this.cleanUp();
case 'home': return this.cleanUp();
case 'zone': return this.newMainScene('Zones', Zones, data);
case 'gameList': return this.newMainScene('GameList', GameList, data);
case 'constructStats': return this.newMainScene('StatSheet', StatSheet, data);
case 'itemInfo': return this.newMainScene('ItemInfo', ItemInfo, data);
default: return false;
}
}
newMainScene(key, scene, data) {
let addScene = true;
// List of scenes which could be occupying the main section of the menu
MAIN_MENU_SCENES.forEach((sKey) => {
if (this.scene.manager.keys[sKey]) {
if (key === sKey) {
// If there is new data for the current scene restart
this.scene.manager.keys[sKey].scene.restart(data);
addScene = false;
} else {
// Delete the old scene
this.scene.manager.keys[sKey].cleanUp();
}
}
});
if (addScene) this.scene.manager.add(key, scene, true, data);
}
cleanUp() {
this.registry.events.off('changedata', this.updateData, this);
this.registry.events.off('setdata', this.updateData, this);
// Delete scenes which could be showing before switching to battle scene
const removeScenes = (sKey) => {
if (this.scene.get(sKey)) this.scene.get(sKey).cleanUp();
};
FIXED_MENU_SCENES.forEach(removeScenes);
MAIN_MENU_SCENES.forEach(removeScenes);
this.registry.set('inMenu', false);
this.combinerItems = this.registry.set('combinerItems', null);
this.scene.remove();
}
}
module.exports = Menu;

View File

@ -1,50 +0,0 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { NAVIGATION } } = require('./constants');
const X = NAVIGATION.x();
const Y = NAVIGATION.y();
const WIDTH = NAVIGATION.width();
const HEIGHT = NAVIGATION.height();
const BTN_WIDTH = Math.floor(WIDTH / 4);
const BTN_HEIGHT = Math.floor(HEIGHT / 2);
class MenuNavigation extends Phaser.Scene {
constructor() {
super({ key: 'MenuNavigation' });
}
create() {
const ws = this.registry.get('ws');
const player = this.registry.get('player');
this.cameras.main.setViewport(X, Y, WIDTH, HEIGHT);
const ready = this.add
.rectangle(BTN_WIDTH * 3, 0, BTN_WIDTH, BTN_HEIGHT, 0x222222)
.setInteractive()
.setOrigin(0);
this.add
.text(ready.getCenter().x, ready.getCenter().y, 'Ready', TEXT.HEADER)
.setOrigin(0.5, 0.5);
ready.on('pointerdown', () => {
ws.sendInstanceReady(player.instance);
});
const menu = this.add
.rectangle(BTN_WIDTH * 3, BTN_HEIGHT, BTN_WIDTH, BTN_HEIGHT, 0x440000)
.setInteractive()
.setOrigin(0)
.on('pointerdown', () => this.registry.set('home', true));
this.add
.text(menu.getCenter().x, menu.getCenter().y, 'Menu', TEXT.HEADER)
.setOrigin(0.5, 0.5);
}
cleanUp() {
this.scene.remove();
}
}
module.exports = MenuNavigation;

View File

@ -1,143 +0,0 @@
const Phaser = require('phaser');
const { POSITIONS: { MENU_MAIN }, TEXT, SKILLS } = require('./constants');
const Item = require('./elements/item');
const X = MENU_MAIN.x();
const Y = MENU_MAIN.y();
const WIDTH = MENU_MAIN.width();
const HEIGHT = MENU_MAIN.height();
const TEXT_MARGIN = 24;
const SKILL_WIDTH = Math.floor(WIDTH / 10);
class DeleteHitBox extends Phaser.GameObjects.Rectangle {
constructor(scene, x, y) {
super(scene, x, y, SKILL_WIDTH, SKILL_WIDTH, 0x222222);
this.setOrigin(0);
this.itemSelect = () => this.setFillStyle(0xff0000);
this.itemDeselect = () => this.setFillStyle(0x222222);
}
}
const itemCheckHitbox = (scene, pointer) => {
const { list } = scene.scene.get('MenuConstructList').children;
const hitboxes = list.filter(c => c.construct)
.concat(scene.children.list.filter(c => c instanceof DeleteHitBox));
let found;
for (let i = 0; i < hitboxes.length; i += 1) {
if (Phaser.Geom.Rectangle.ContainsPoint(hitboxes[i].getBounds(), pointer.position)) {
found = hitboxes[i];
} else {
hitboxes[i].itemDeselect();
}
}
return found;
};
class StatSheet extends Phaser.Scene {
constructor() {
super({ key: 'StatSheet' });
}
updateData(parent, key, data) {
if (key === 'player') {
console.log('grep');
console.log(data);
const construct = data.constructs.find(c => c.id === this.construct.id);
this.scene.restart(construct);
}
}
create(construct) {
this.registry.events.on('changedata', this.updateData, this);
// const ws = this.registry.get('ws');
const player = this.registry.get('player');
if (!player) return false;
// const { vbox } = player;
this.construct = construct;
/*
const del = this.add.existing(new DeleteHitBox(this, X + WIDTH * 0.7, Y + HEIGHT * 0.6));
this.add.text(del.getCenter().x, del.getCenter().y, 'unequip', TEXT.HEADER)
.setOrigin(0.5, 0.5);
*/
this.add.text(X, Y, construct.name, TEXT.HEADER);
const constructStat = (stat, i) => {
const STAT_X = X;
const STAT_Y = Y + (i + 2) * TEXT_MARGIN;
this.add.text(STAT_X, STAT_Y, `${stat.stat}: ${stat.base} -> ${stat.value}`, TEXT.NORMAL);
};
const CONSTRUCT_STATS = [
construct.hp,
construct.red_shield,
construct.blue_shield,
construct.evasion,
construct.red_damage,
construct.blue_damage,
construct.speed,
];
CONSTRUCT_STATS.forEach(constructStat);
/*
const knownSkill = (skill, i) => {
const SKILL_X = X + WIDTH * 0.4 + WIDTH * 0.125 * i;
const SKILL_Y = Y + HEIGHT * 0.15;
const itemObj = new Item(this, skill.skill, i, SKILL_X, SKILL_Y, SKILL_WIDTH, Math.floor(SKILL_WIDTH / 2));
// itemObj.on('pointerdown', () => );
this.input.setDraggable(itemObj);
this.add.existing(itemObj);
};
construct.skills.forEach(knownSkill);
this.add.text(X + WIDTH * 0.35, Y, 'Skills', TEXT.HEADER);
this.add.text(X + WIDTH * 0.35, Y + HEIGHT * 0.25, 'Specs', TEXT.HEADER);
const knownSpec = (spec, i) => {
const SKILL_X = X + WIDTH * 0.4 + WIDTH * 0.125 * i;
const SKILL_Y = Y + HEIGHT * 0.4;
const itemObj = new Item(this, spec, i, SKILL_X, SKILL_Y, SKILL_WIDTH, Math.floor(SKILL_WIDTH / 2));
// itemObj.on('pointerdown', () => );
this.input.setDraggable(itemObj);
this.add.existing(itemObj);
};
construct.specs.forEach(knownSpec);
this.input.on('drag', (pointer, item, dragX, dragY) => {
if (!(item instanceof Item)) return false;
item.setPosition(dragX, dragY);
const hitBox = itemCheckHitbox(this, pointer);
if (hitBox) hitBox.itemSelect();
return true;
});
this.input.on('dragend', (pointer, item) => {
if (!(item instanceof Item)) return false;
item.setPosition(item.origX, item.origY);
const hitBox = itemCheckHitbox(this, pointer);
if (hitBox) {
hitBox.itemDeselect();
// add socket function for unlearn here
ws.sendVboxUnequip(vbox.instance, construct.id, item.item);
}
return true;
});
*/
return this;
}
cleanUp() {
this.registry.events.off('changedata', this.updateData, this);
this.scene.remove();
}
}
module.exports = StatSheet;

View File

@ -1,41 +0,0 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { MENU_MAIN } } = require('./constants');
const X = MENU_MAIN.x();
const Y = MENU_MAIN.y();
const WIDTH = MENU_MAIN.width();
const HEIGHT = MENU_MAIN.height();
class ZoneControls extends Phaser.Scene {
constructor() {
super({ key: 'ZoneControls' });
}
create(zoneId) {
this.cameras.main.setViewport(X, Y, WIDTH, HEIGHT);
const menu = this.add
.rectangle(Math.floor(WIDTH * 0.7), Math.floor(HEIGHT * 0.8), Math.floor(WIDTH * 0.2), Math.floor(HEIGHT * 0.2), 0x888888)
.setInteractive()
.setOrigin(0);
this.add
.text(menu.getCenter().x, menu.getCenter().y, 'Clear', TEXT.HEADER)
.setOrigin(0.5, 0.5);
menu.on('pointerdown', () => {
this.registry.get('ws').sendZoneClose(zoneId);
this.scene.get('Zones').cleanUp();
});
return true;
}
cleanUp() {
this.scene.remove();
}
}
module.exports = ZoneControls;

View File

@ -1,31 +0,0 @@
const Phaser = require('phaser');
class ZoneNode extends Phaser.GameObjects.Sprite {
constructor(scene, x, y, id, success, tag) {
super(scene, x, y);
this.setTexture('eye');
this.scene = scene;
this.success = success;
// this.text = (text.indexOf(',') > -1) ? text.split(',') : text;
this.id = id;
this.setPosition(x, y);
const nodeNoDigits = tag.replace(/[0-9]/g, '');
switch (nodeNoDigits) {
case 'BOSS':
this.setScale(0.25);
break;
case 'MINIBOSS':
this.setScale(0.1);
break;
default:
this.setScale(0.05);
}
if (this.success) {
this.setTint(0xff0000);
}
this.text = tag;
}
}
module.exports = ZoneNode;

View File

@ -1,129 +0,0 @@
const Phaser = require('phaser');
const Node = require('./zone.node');
const ZoneControls = require('./zone.controls');
const { POSITIONS: { MENU_MAIN }, TEXT } = require('./constants');
const X = MENU_MAIN.x();
const Y = MENU_MAIN.y();
const WIDTH = MENU_MAIN.width();
const HEIGHT = MENU_MAIN.height();
// Mouse click hold to move, Q + E to zoom in and out
// Press 'A' to reset allocated passive nodes
class Zones extends Phaser.Scene {
constructor() {
super({ key: 'Zones' });
}
preload() {
this.load.image('eye', 'https://labs.phaser.io/assets/particles/green-orb.png');
}
create(zone) {
if (!zone) return false;
if (!this.scene.get('ZoneControls')) this.scene.manager.add('ZoneControls', ZoneControls, true, zone.id);
this.graphics = this.add.graphics();
this.cameras.main.setViewport(X, Y, WIDTH, HEIGHT);
this.addNodes(zone.graph.nodes);
this.drawEdges(zone.graph.edges);
this.addCameraControl();
this.addEvents(zone.id);
return this;
}
addNodes(nodeData) {
this.nodes = [];
nodeData.forEach((n, i) => {
this.nodes[i] = this.add.existing(
new Node(this, n.x * 50 + 200, n.y * 50 + 200, i, n.success, n.tag)
).setInteractive();
});
}
addCameraControl() {
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl({
camera: this.cameras.main,
zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
acceleration: 0.005,
drag: 0.0005,
maxSpeed: 0.001,
});
}
addEvents(zoneId) {
this.input.on('pointerover', (pointer, gameObjects) => {
if (gameObjects[0] instanceof Node) {
this.displayNodeText(gameObjects[0], pointer);
}
});
this.input.on('pointerout', (pointer, gameObjects) => {
if (gameObjects[0] instanceof Node) {
this.nodeText.destroy();
}
});
this.input.on('pointerup', (pointer, gameObjects) => {
if (Math.abs(pointer.upX - pointer.downX) < 5
&& Math.abs(pointer.upY - pointer.downY) < 5) {
// Check cursor hasn't significantly moved during point allocation
// If panning and mouse release is on node it won't allocate
if (gameObjects[0] instanceof Node) {
const team = this.registry.get('constructs').filter(c => c.active).map(c => c.id);
if (gameObjects[0].success) return false;
this.registry.get('ws').sendZoneJoin(zoneId, gameObjects[0].id, team);
}
}
return true;
});
this.input.on('pointermove', (pointer) => {
const zoomFactor = 2 / this.cameras.main.zoom;
if (this.registry.get('pan')) {
const points = pointer.getInterpolatedPosition(2);
this.cameras.main.scrollX -= zoomFactor * (points[1].x - points[0].x);
this.cameras.main.scrollY -= zoomFactor * (points[1].y - points[0].y);
}
}, this);
}
drawEdges(edgeData) {
this.graphics.clear();
edgeData.forEach((e) => {
const nodeA = this.nodes[e[0]];
const nodeB = this.nodes[e[1]];
if (nodeA.success && nodeB.success) {
this.graphics.lineStyle(10, 0xfff00f, 0.2);
} else {
this.graphics.lineStyle(2, 0xffffff, 0.2);
}
this.graphics.lineBetween(nodeA.x, nodeA.y, nodeB.x, nodeB.y);
return true;
});
}
displayNodeText(node, pointer) {
if (this.nodeText) this.nodeText.destroy();
this.nodeText = this.add.text(node.x, node.y, node.text, TEXT.HOVER).setPadding(32);
this.nodeText.setAlpha(0.8);
this.nodeText.setOrigin(pointer.x >= WIDTH * 0.65 ? 1 : 0,
pointer.y >= HEIGHT * 0.25 ? 1 : 0);
this.nodeText.setWordWrapWidth(450);
this.nodeText.setScale(1 / this.cameras.main.zoom);
}
camPan(bool) {
this.pan = bool;
}
update(delta) {
this.controls.update(delta);
}
cleanUp() {
if (this.scene.get('ZoneControls')) this.scene.get('ZoneControls').cleanUp();
this.scene.remove();
}
}
module.exports = Zones;

View File

@ -1,335 +0,0 @@
const toast = require('izitoast');
const cbor = require('borc');
const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://constructs.gg/ws' : 'ws://localhost:40000';
function errorToast(err) {
console.error(err);
return toast.error({
title: 'BEEP BOOP',
message: err,
position: 'topRight',
});
}
function createSocket(events) {
let ws;
// handle account auth within the socket itself
// https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html
let account = null;
// -------------
// Outgoing
// -------------
function send(msg) {
console.log('outgoing msg', msg);
msg.token = account && account.token;
ws.send(cbor.encode(msg));
}
function sendAccountLogin(name, password) {
send({ method: 'account_login', params: { name, password } });
}
function sendAccountCreate(name, password) {
send({ method: 'account_create', params: { name, password } });
}
function sendAccountDemo() {
send({ method: 'account_demo', params: {} });
}
function sendAccountConstructs() {
send({ method: 'account_constructs', params: {} });
}
function sendAccountInstances() {
send({ method: 'account_instances', params: {} });
}
function sendAccountZone() {
send({ method: 'account_zone', params: {} });
}
function sendConstructSpawn(name) {
send({ method: 'construct_spawn', params: { name } });
}
function sendConstructLearn(id, skill) {
send({ method: 'construct_learn', params: { id, skill } });
}
function sendConstructForget(id, skill) {
send({ method: 'construct_forget', params: { id, skill } });
}
function sendGameState(id) {
send({ method: 'game_state', params: { id } });
}
function sendGamePve(constructIds, mode) {
send({ method: 'game_pve', params: { construct_ids: constructIds, mode } });
}
function sendGamePvp(constructIds) {
send({ method: 'game_pvp', params: { construct_ids: constructIds } });
}
function sendGameJoin(gameId, constructIds) {
send({ method: 'game_join', params: { game_id: gameId, construct_ids: constructIds } });
}
function sendSpecForget(id, spec) {
send({ method: 'construct_unspec', params: { id, spec } });
}
function sendPlayerConstructsSet(instanceId, constructIds) {
send({ method: 'player_constructs_set', params: { instance_id: instanceId, construct_ids: constructIds } });
}
function sendPlayerState(instanceId) {
send({ method: 'player_state', params: { instance_id: instanceId } });
}
function sendVboxAccept(instanceId, group, index) {
send({ method: 'player_vbox_accept', params: { instance_id: instanceId, group, index } });
}
function sendVboxApply(instanceId, constructId, index) {
send({ method: 'player_vbox_apply', params: { instance_id: instanceId, construct_id: constructId, index } });
}
function sendVboxUnequip(instanceId, constructId, target) {
send({ method: 'player_vbox_unequip', params: { instance_id: instanceId, construct_id: constructId, target } });
}
function sendVboxDiscard(instanceId) {
send({ method: 'player_vbox_discard', params: { instance_id: instanceId } });
}
function sendVboxCombine(instanceId, indices) {
send({ method: 'player_vbox_combine', params: { instance_id: instanceId, indices } });
}
function sendVboxReclaim(instanceId, index) {
send({ method: 'player_vbox_reclaim', params: { instance_id: instanceId, index } });
}
function sendGameSkill(gameId, constructId, targetConstructId, skill) {
send({
method: 'game_skill',
params: {
game_id: gameId, construct_id: constructId, target_construct_id: targetConstructId, skill,
},
});
events.setActiveSkill(null);
}
function sendGameTarget(gameId, constructId, skillId) {
send({ method: 'game_target', params: { game_id: gameId, construct_id: constructId, skill_id: skillId } });
events.setActiveSkill(null);
}
function sendZoneCreate() {
send({ method: 'zone_create', params: {} });
}
function sendZoneJoin(zoneId, nodeId, constructIds) {
send({ method: 'zone_join', params: { zone_id: zoneId, node_id: nodeId, construct_ids: constructIds } });
}
function sendZoneClose(zoneId) {
send({ method: 'zone_close', params: { zone_id: zoneId } });
}
function sendInstanceJoin(constructs) {
send({ method: 'instance_join', params: { construct_ids: constructs, pve: true } });
}
function sendInstanceReady(instanceId) {
send({ method: 'instance_ready', params: { instance_id: instanceId } });
}
function sendInstanceScores(instanceId) {
send({ method: 'instance_scores', params: { instance_id: instanceId } });
}
// -------------
// Incoming
// -------------
function accountLogin(res) {
const [struct, login] = res;
account = login;
events.setAccount(login);
sendAccountConstructs();
}
function accountInstanceList(res) {
const [struct, instanceList] = res;
events.setInstanceList(instanceList);
}
function accountConstructs(response) {
const [structName, constructs] = response;
events.setConstructList(constructs);
}
function gameState(response) {
const [structName, game] = response;
events.setGame(game);
}
function constructSpawn(response) {
const [structName, construct] = response;
}
function gamePve(response) {
const [structName, game] = response;
}
function zoneState(response) {
const [structName, zone] = response;
events.setZone(zone);
}
function playerState(response) {
const [structName, player] = response;
events.setPlayer(player);
}
function instanceScores(response) {
const [structName, scores] = response;
events.setScores(scores);
}
// -------------
// Setup
// -------------
// when the server sends a reply it will have one of these message types
// this object wraps the reply types to a function
const handlers = {
construct_spawn: constructSpawn,
construct_forget: () => true,
construct_learn: () => true,
game_pve: gamePve,
game_state: gameState,
account_login: accountLogin,
account_create: accountLogin,
account_constructs: accountConstructs,
account_instances: accountInstanceList,
instance_scores: instanceScores,
zone_create: res => console.log(res),
zone_state: zoneState,
zone_close: res => console.log(res),
player_state: playerState,
};
function errHandler(error) {
switch (error) {
case 'no active zone': return sendZoneCreate();
case 'no constructs selected': return events.errorPrompt('select_constructs');
case 'node requirements not met': return events.errorPrompt('complete_nodes');
case 'construct at max skills (4)': return events.errorPrompt('max_skills');
default: return errorToast(error);
}
}
// decodes the cbor and
// calls the handlers defined above based on message type
function onMessage(event) {
// decode binary msg from server
const blob = new Uint8Array(event.data);
const res = cbor.decode(blob);
const { method, params } = res;
console.log(res);
// check for error and split into response type and data
if (res.err) return errHandler(res.err);
if (!handlers[method]) return errorToast(`${method} handler missing`);
return handlers[method](params);
}
function connect() {
ws = new WebSocket(SOCKET_URL);
ws.binaryType = 'arraybuffer';
// Connection opened
ws.addEventListener('open', () => {
toast.info({
message: 'connected',
position: 'topRight',
});
if (!account) events.loginPrompt();
if (process.env.NODE_ENV !== 'production') {
// send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } });
}
return true;
});
// Listen for messages
ws.addEventListener('message', onMessage);
ws.addEventListener('error', (event) => {
console.error('WebSocket error', event);
// account = null;
// return setTimeout(connect, 5000);
});
ws.addEventListener('close', (event) => {
console.error('WebSocket closed', event);
toast.warning({
message: 'disconnected',
position: 'topRight',
});
return setTimeout(connect, 5000);
});
return ws;
}
return {
sendAccountLogin,
sendAccountCreate,
sendAccountDemo,
sendAccountConstructs,
sendAccountInstances,
sendAccountZone,
sendGameState,
sendGamePve,
sendGamePvp,
sendGameJoin,
sendGameSkill,
sendGameTarget,
sendConstructSpawn,
sendConstructLearn,
sendConstructForget,
sendSpecForget,
sendZoneCreate,
sendZoneJoin,
sendZoneClose,
sendInstanceJoin,
sendInstanceReady,
sendInstanceScores,
sendPlayerConstructsSet,
sendPlayerState,
sendVboxAccept,
sendVboxApply,
sendVboxReclaim,
sendVboxCombine,
sendVboxDiscard,
sendVboxUnequip,
connect,
};
}
module.exports = createSocket;

View File

@ -1,135 +0,0 @@
const toast = require('izitoast');
const OK_BUTTON = '<button type="submit">OK</button>';
const NO_MORE_BUTTON = '<button type="submit">Enough already...</button>';
function noMore(instance, thisToast) {
window.localStorage.setItem('tutorial', 'none');
return instance.hide({ transitionOut: 'fadeOut' }, thisToast);
}
const WELCOME_MESSAGE = `
Welcome to constructs.gg
Enter a username and password and press register to sign up,
or just press DEMO to quick start.
`;
const HOMEPAGE_MESSAGE = `
This homepage shows your constructs, joinable online games, PVE options and your items.\n
If you have no constructs yet, press SPAWN and give your construct a name to create one.
Once you have made a construct, click on them to visit their stat page and teach them some SKILLS.
The stat page also has descriptions of each skill and their effects.
constructs have 3 basic stats: hp, red damage and magic damage.
Toggle whether a construct is selected for your team by clicking the coloured stripes next to the construct or press 1,2,3.
Once you have a team ready press the New PVE Game button to start playing.
`;
const SKILL_PHASE_MESSAGE = `
A constructs battle has two main phases. This first phase is called the SKILL PHASE.
Your constructs are positioned on the left, your opponent's are on the right.
In the centre are your constructs' SKILLS, grayed out SKILLS are currently ON COOLDOWN.
A skill's cooldown reduces on every turn that construct does not use a skill with a cooldown.
For the moment, drag ATTACK onto the opponent team to have your constructs attack them with red damage.
`;
// const TARGET_PHASE_MESSAGE = `
// This phase is the TARGET PHASE.
// In constructs you do not directly attack your opponent's constructs, you attack the opponent as a team
// and you and your opponent choose which construct is the TARGET of each ability.
// Drag the incoming ATTACKS from the right hand side onto your own constructs.
// It's wise to spread the damage around!
// `;
const RESOLUTION_PHASE_MESSAGE = `
The second phase is called the RESOLUTION PHASE.
This phase happens automatically, every skill is RESOLVED in order of its SPEED.
This is important because attacks only RESOLVE while their caster is still able to use the skill,
a fast skill that does a small amount of damage may KO an opponent construct, causing any SKILLS
they have used to no longer RESOLVE!
Another example of this is the skill STUN. STUN causes an opponent construct to be unable to use any
abilities for TWO TURNS including the turn it resolves on. In other words it lasts for the rest of the turn it resolves on
and the whole next turn.
Try it now!
`;
const FINISH_PHASE_MESSAGE = `
gg! The game has now concluded, if you were the winner you have been awarded with a STAT REROLL ITEM.
You can use this to reroll a stat on a construct which may be lacking.
A good metric is that if a stat is more than 1/2 of its STAMINA that's a good roll.
Now that you have learned the basics, press BACKSPACE to return to the main menu
and experiment with some combinations of SKILLS or replace the ones your constructs know in the STAT PAGE.
glhf!
`;
const STEPS = [
'init',
'welcome',
'homepage',
'skillPhase',
// 'targetPhase',
'resolutionPhase',
'finishPhase',
'none',
];
function showTutorial(message, step, nextStep) {
const existing = document.querySelector(`#${step}`); // Selector of your toast
if (existing) return false;
toast.info({
id: step,
theme: 'dark',
color: 'black',
timeout: false,
drag: false,
title: 'TUTORIAL',
position: 'bottomCenter',
maxWidth: window.innerWidth / 2,
close: false,
buttons: [
[NO_MORE_BUTTON, noMore],
[OK_BUTTON, (instance, thisToast) => {
const thisStep = STEPS.indexOf(step);
const storageStep = STEPS.indexOf(window.localStorage.getItem('tutorial'));
if (thisStep >= storageStep) {
window.localStorage.setItem('tutorial', nextStep);
}
return instance.hide({ transitionOut: 'fadeOut' }, thisToast);
}],
],
message,
});
return true;
}
function tutorial() {
function show(step) {
const currentStage = window.localStorage.getItem('tutorial');
if (currentStage === 'none') {
return false;
}
const thisStep = STEPS.indexOf(step);
const storageStep = STEPS.indexOf(window.localStorage.getItem('tutorial'));
if (thisStep < storageStep) return false;
if (step === 'welcome') return showTutorial(WELCOME_MESSAGE, 'welcome', 'homepage');
if (step === 'homepage') return showTutorial(HOMEPAGE_MESSAGE, 'homepage', 'skillPhase');
if (step === 'skillPhase') return showTutorial(SKILL_PHASE_MESSAGE, 'skillPhase', 'targetPhase');
// if (step === 'targetPhase') return showTutorial(TARGET_PHASE_MESSAGE, 'targetPhase', 'resolutionPhase');
if (step === 'resolutionPhase') return showTutorial(RESOLUTION_PHASE_MESSAGE, 'resolutionPhase', 'finishPhase');
if (step === 'finishPhase') return showTutorial(FINISH_PHASE_MESSAGE, 'finishPhase', 'none');
return true;
}
if (window.localStorage.getItem('tutorial') !== 'none') window.localStorage.setItem('tutorial', 'init');
return show;
}
module.exports = tutorial;

View File

@ -1,36 +0,0 @@
const get = require('lodash/get');
const stringSort = (k, desc) => {
if (desc) {
return (a, b) => {
if (!get(a, k)) return 1;
if (!get(b, k)) return -1;
return get(b, k).localeCompare(get(a, k));
};
}
return (a, b) => {
if (!get(a, k)) return 1;
if (!get(b, k)) return -1;
return get(a, k).localeCompare(get(b, k));
};
};
const numSort = (k, desc) => {
if (desc) {
return (a, b) => {
if (!get(a, k)) return 1;
if (!get(b, k)) return -1;
return get(b, k) - get(a, k);
};
}
return (a, b) => {
if (!get(a, k)) return 1;
if (!get(b, k)) return -1;
return get(a, k) - get(b, k);
};
};
module.exports = {
stringSort,
numSort,
};