nginx change

This commit is contained in:
ntr
2019-04-02 18:19:33 +11:00
parent 4c16afa786
commit 23c7ae7aab
99 changed files with 538 additions and 538 deletions
+13
View File
@@ -0,0 +1,13 @@
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;
+166
View File
@@ -0,0 +1,166 @@
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;
@@ -0,0 +1,250 @@
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 } = getCrypPosition(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;
+239
View File
@@ -0,0 +1,239 @@
const Phaser = require('phaser');
const genAvatar = require('./avatar');
const StatBar = require('./elements/combat.statbar');
const { DELAYS, TEXT, POSITIONS: { COMBAT } } = require('./constants');
const CRYP_MARGIN = COMBAT.crypMargin();
const TEXT_MARGIN = COMBAT.textMargin();
const crypAvatarText = (team, iter) => {
const nameX = COMBAT.width() * team;
const nameY = COMBAT.y() + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
const statusX = COMBAT.width() * team;
const statusY = COMBAT.y() + TEXT_MARGIN * 6 + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
return { statusX, statusY, nameX, nameY };
};
const crypEffects = (team, iter) => {
const crypEffectsX = team ? COMBAT.width() - COMBAT.width() / 6.5 : COMBAT.width() / 6.5;
const crypEffectsY = TEXT_MARGIN * 2 + CRYP_MARGIN * iter;
return { crypEffectsX, crypEffectsY };
};
const crypPosition = (team, iter) => {
const crypAvatarX = team ? COMBAT.width() - COMBAT.width() / 6 : COMBAT.width() / 6;
const crypAvatarY = TEXT_MARGIN * 5 + CRYP_MARGIN * iter;
return { crypAvatarX, crypAvatarY };
};
class Effects extends Phaser.GameObjects.Group {
constructor(scene, team, iter) {
super(scene);
this.scene = scene;
const { crypEffectsX, crypEffectsY } = crypEffects(team, iter);
this.x = crypEffectsX; this.y = crypEffectsY;
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 CrypImage extends Phaser.GameObjects.Image {
constructor(scene, team, iter, cryp) {
// Get coords
const { crypAvatarX, crypAvatarY } = crypPosition(team, iter);
const { statusX, statusY, nameX, nameY } = crypAvatarText(team, iter);
// Cryp display
// const avatar = team ? 'magmar' : 'alk';
super(scene, crypAvatarX, crypAvatarY, 'aztec', genAvatar(cryp.name));
this.setScale(0.5);
if (!team) this.flipX = true;
// Save position and cryp details
this.scene = scene;
this.iter = iter;
this.team = team;
this.cryp = cryp;
this.state = 'deselect';
// Add cryp name
scene.add.text(nameX, nameY, cryp.name, TEXT.NORMAL).setOrigin(team, 0);
// Add cryp 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 CombatCryps extends Phaser.Scene {
constructor() {
super({ key: 'CombatCryps' });
}
create(game) {
this.cryps = this.add.group();
this.phase = game.phase;
this.account = this.registry.get('account');
this.drawCryps(game);
this.registry.events.on('changedata', this.updateData, this);
this.registry.set('crypStatusUpdate', 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.cryps.children.entries.forEach(c => c.clearStatus());
this.drawCryps(this.game);
}
}
if (key === 'crypStatusUpdate' && data) {
this.updateCrypStatus(data);
}
return true;
}
drawCryps(game) {
const renderCryp = (cryp, iter, team) => {
// Add Image Avatar Class
const crypObj = new CrypImage(this, team, iter, cryp);
this.add.existing(crypObj);
this.cryps.add(crypObj);
return crypObj;
};
const renderTeam = (cryp, iter, team) => {
const crypObj = this.cryps.children.entries
.find(c => c.cryp.id === cryp.id)
|| renderCryp(cryp, iter, team);
crypObj.health.val = cryp.hp.value;
crypObj.red_shield.val = cryp.red_shield.value;
crypObj.blue_shield.val = cryp.red_shield.value;
crypObj.health.drawStatBar();
crypObj.red_shield.drawStatBar();
crypObj.blue_shield.drawStatBar();
crypObj.effects.update(cryp.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.cryps.forEach((cryp, i) => renderTeam(cryp, i, 0));
if (!enemyTeam) return false;
enemyTeam.cryps.forEach((cryp, i) => renderTeam(cryp, i, 1));
return true;
}
selectCryp(crypId) {
this.cryps.children.entries.forEach(c => c.deselect());
if (crypId) this.cryps.children.entries.find(c => c.cryp.id === crypId).select();
}
updateCrypStatus(status) {
const sourceCryp = this.cryps.children.entries
.find(c => c.cryp.id === status.id);
const targetCryp = this.cryps.children.entries
.find(c => c.cryp.id === status.target);
if (this.phase === 'Skill') {
sourceCryp.statusText.text = `${status.skill} on ${targetCryp.cryp.name}`;
}
}
cleanUp() {
this.registry.events.off('changedata', this.updateData);
this.scene.remove();
}
}
module.exports = CombatCryps;
+89
View File
@@ -0,0 +1,89 @@
const Phaser = require('phaser');
const { POSITIONS: { COMBAT } } = require('./constants');
const CRYP_MARGIN = COMBAT.crypMargin();
const BOX_HEIGHT = CRYP_MARGIN * 0.8;
const BOX_WIDTH = COMBAT.width() * 0.2;
class CrypHitBox extends Phaser.GameObjects.Rectangle {
constructor(scene, iter, team, cback) {
const y = COMBAT.y() + COMBAT.height() * 0.05 + CRYP_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('CombatCryps').cryps;
const skillScene = this.scene.get('CombatSkills');
game.teams.forEach((t) => {
t.cryps.forEach((c) => {
const cback = () => {
const { activeSkill } = skillScene;
if (activeSkill) {
this.scene.get('CombatSkills').clearCrypActive(activeSkill.cryp.id);
activeSkill.activate();
skillScene.activeSkill = null;
this.game.events.emit('SEND_SKILL', game.id, activeSkill.cryp.id, c.id, activeSkill.skill.skill);
}
};
const crypSpawn = group.children.entries.find(s => s.cryp.id === c.id);
const team = c.account === account.id ? 0 : 1;
if (crypSpawn) this.add.existing(new CrypHitBox(this, crypSpawn.iter, team, cback));
});
});
this.scene.moveBelow('Combat');
}
cleanUp() {
this.registry.events.off('changedata', this.updateData);
this.scene.remove();
}
}
module.exports = CombatHitBox;
+138
View File
@@ -0,0 +1,138 @@
const Phaser = require('phaser');
const { throttle } = require('lodash');
const { TEXT, POSITIONS: { COMBAT } } = require('./constants');
const CombatLog = require('./combat.log');
const CombatCryps = require('./combat.cryps');
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('CombatCryps', CombatCryps, 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 cryps 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', 'CombatCryps', 'CombatSkills', 'CombatHitBox'];
ACTIVE_SCENES.forEach((sKey) => {
if (this.scene.get(sKey)) this.scene.get(sKey).cleanUp();
});
this.scene.remove();
return true;
}
}
module.exports = Combat;
+50
View File
@@ -0,0 +1,50 @@
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;
@@ -0,0 +1,135 @@
const { eachSeries } = require('async');
const CombatAnimations = require('./combat.animations');
const {
DELAYS: { ANIMATION_DURATION, MOVE_CREEP, DAMAGE_TICK },
POSITIONS: { COMBAT },
} = require('./constants');
function findResolutionCryps(scene, group, resolution, game) {
const sourceSpawn = group.children.entries.find(c => c.cryp.id === resolution.source.id);
/* const sourceCryp = game.teams.find(t => t.cryps.find(c => c.id === resolution.source_cryp_id))
.cryps.find(c => c.id === resolution.source_cryp_id);
const targetCryp = game.teams.find(t => t.cryps.find(c => c.id === resolution.target_cryp_id))
.cryps.find(c => c.id === resolution.target_cryp_id);
*/
const targetSpawn = group.children.entries.find(c => c.cryp.id === resolution.target.id);
return { sourceSpawn, targetSpawn };
}
function calculateTweenParams(sourceSpawn, targetSpawn, account, skill) {
const tweenParams = (targets, centreSpot) => {
const enemy = targets.cryp.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.cryp.account !== targetSpawn.cryp.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('CombatCryps').cryps;
const animations = new CombatAnimations(scene);
const account = scene.registry.get('account');
// Find cryps, targets
const { sourceSpawn, targetSpawn } = findResolutionCryps(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 cryps into position
if (moveSourceBattle) scene.tweens.add(moveSourceBattle);
scene.tweens.add(moveTargetBattle);
return scene.time.delayedCall(MOVE_CREEP, () => {
const sourceAlly = sourceSpawn.cryp.account === account.id;
const targetAlly = targetSpawn.cryp.account === account.id;
// animate animation
animations.getSkill(resolution.event[1].skill, sourceAlly, targetAlly, castLocation);
// Target cryp 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;
+235
View File
@@ -0,0 +1,235 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { COMBAT } } = require('./constants');
const CRYP_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 CRYP_MARGIN = COMBAT.crypMargin();
const TEXT_MARGIN = COMBAT.textMargin();
const SKILL_WIDTH = COMBAT.width() / 10;
const SKILL_HEIGHT = COMBAT.height() / 30;
const skillPosition = (crypIter, skillIter) => {
const skillTextX = COMBAT.width() / 3.8;
const skillTextY = (TEXT_MARGIN * skillIter) * 1.5 + CRYP_MARGIN * crypIter + 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 CrypSkill extends Phaser.GameObjects.Container {
constructor(scene, x, y, skill, cryp) {
// Avatar will be a property of cryp
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.cryp = cryp;
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('CombatCryps').selectCryp(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, cryp) => {
const skillTextPos = skillPosition(i, j);
const skillObj = new CrypSkill(this, skillTextPos[0], skillTextPos[1], skill, cryp);
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.cryps.forEach((cryp) => {
// return early if KOd
if (cryp.hp.value === 0) return true;
// find the cryp position
const { iter } = this.scene.get('CombatCryps').cryps.children.entries.find(c => c.cryp.id === cryp.id);
// draw the skills
const skillButtons = cryp.skills.map((skill, j) => addSkill(iter, j, skill, cryp));
const bindCrypKeys = () => this.mapSkillKeys(skillButtons, game.id, cryp.id, team.id, enemyTeam.id, iter);
// reset everything
keyboard.on('keydown_ESC', bindCrypKeys, this);
events.on('SEND_SKILL', bindCrypKeys, this);
bindCrypKeys();
return true;
});
return true;
}
// FIXME
// needs to send crypId not team
mapSkillKeys(skillButtons, gameId, crypId, alliesId, enemyId, i) {
const { keyboard } = this.input;
keyboard.removeListener(CRYP_KEY_MAP[i]);
keyboard.on(CRYP_KEY_MAP[i], () => {
SKILL_KEY_MAP.forEach(k => keyboard.removeListener(k));
this.scene.get('CombatCryps').selectCryp(crypId);
skillButtons.forEach((button, j) => {
keyboard.on(SKILL_KEY_MAP[j], () => {
this.activeSkill = button;
button.select();
// clear existing keys
CRYP_KEY_MAP.forEach(k => keyboard.removeListener(k));
TARGET_KEY_MAP.forEach(k => keyboard.removeListener(k));
CRYP_KEY_MAP.forEach(k => keyboard.on(k, () => {
this.clearCrypActive(crypId);
button.activate();
this.activeSkill = null;
this.game.events.emit('SEND_SKILL', gameId, crypId, alliesId, button.skill.skill);
}));
TARGET_KEY_MAP.forEach(k => keyboard.on(k, () => {
this.clearCrypActive(crypId);
button.activate();
this.activeSkill = null;
this.game.events.emit('SEND_SKILL', gameId, crypId, enemyId, button.skill.skill);
}));
}, this);
});
}, this);
return true;
}
clearCrypActive(crypId) {
this.scene.scene.children.list.forEach((s) => {
if (s.cryp.id === crypId && s.state === 'activate') s.deselect();
});
}
clearKeys() {
TARGET_KEY_MAP.forEach(tKey => this.input.keyboard.removeListener(tKey));
CRYP_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;
+388
View File
@@ -0,0 +1,388 @@
// 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 menuCrypListWidth = () => Math.floor(CANVAS_WIDTH() * 0.3);
const menuCrypListHeight = () => Math.floor(CANVAS_HEIGHT() - headerHeight());
const menuCrypListX = () => Math.floor(CANVAS_WIDTH() * 0.3);
const menuCrypListY = () => 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 combatCrypMargin = () => Math.floor((CANVAS_HEIGHT() - headerHeight()) / 4.5);
const combatTextMargin = () => Math.floor((CANVAS_HEIGHT() - headerHeight()) / 35);
const statsWidth = () => Math.floor(CANVAS_WIDTH() - menuCrypListWidth());
const statsHeight = () => CANVAS_HEIGHT() - headerHeight();
const statsY = () => headerHeight();
const statsX = () => menuCrypListWidth();
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,
},
CRYP_LIST: {
x: menuCrypListX,
y: menuCrypListY,
width: menuCrypListWidth,
height: menuCrypListHeight,
},
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,
crypMargin: combatCrypMargin,
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 cryp',
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 cryp 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 cryp 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 cryp takes increased magic damage',
colours: '2 Blue',
},
Debuff: {
description: 'reduce target cryp speed',
upgrades: 'combine with 2 red / blue / green',
},
Decay: {
description: 'afflict a cryp with a blue damage based damage over time debuff',
colours: '1 Green 1 Blue',
},
Empower: {
description: 'increase the red damage dealt by a cryp',
colours: '2 Red',
},
Haste: {
description: 'magical skill that increases speed of target cryp',
colours: '1 Red 1 Blue',
},
Heal: {
description: 'heal a cryp with blue damage',
colours: '2 Green',
},
Hex: {
description: 'magical bsed skill that prevents target cryp from using any skills',
colours: '1 Red 1 Blue',
},
Hostility: {
description: 'magical bsed skill that prevents target cryp 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 cryp',
colours: '2 Green',
},
Purify: {
description: 'remove magical debuffs from target cryp',
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 cryp',
colours: '1 Green 1 Blue',
},
Silence: {
description: 'prevent target cryp from casting magical skills',
colours: '1 Green 1 Blue',
},
Siphon: {
description: 'siphon hp from target cryp with a blue damage based debuff',
colours: '1 Green 1 Blue',
},
Slay: {
description: '????',
},
Slow: {
description: 'magical skill that reduces speed of target cryp',
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 cryp from using any skills',
upgrades: 'combine with 2 red / blue / green',
},
Taunt: {
description: 'Enemy skills will prioritise cryps 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 cryp',
upgrades: 'combine with 2 red / blue / green',
},
Hp: {
description: 'Increases health of cryp',
upgrades: 'combine with 2 red / blue / green',
},
Speed: {
description: 'Increases speed of cryp',
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',
},
},
},
};
+77
View File
@@ -0,0 +1,77 @@
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 renderCryps() {
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 = renderCryps;
+132
View File
@@ -0,0 +1,132 @@
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;
@@ -0,0 +1,86 @@
const Phaser = require('phaser');
const { TEXT, POSITIONS: { COMBAT }, COLOURS } = require('.././constants');
const CRYP_MARGIN = COMBAT.crypMargin();
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) + CRYP_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) + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
return { statTextX, statTextY };
};
class StatBar extends Phaser.GameObjects.Graphics {
constructor(scene, cryp, type) {
super(scene);
this.crypObj = cryp;
this.type = type;
if (type === 'HP') {
this.val = this.crypObj.cryp.hp.value;
this.max = this.crypObj.cryp.hp.max;
this.margin = 0;
} else if (type === 'Red Shield') {
this.val = this.crypObj.cryp.red_shield.value;
this.max = this.crypObj.cryp.red_shield.max;
this.margin = 1;
} else if (type === 'Blue Shield') {
this.val = this.crypObj.cryp.blue_shield.value;
this.max = this.crypObj.cryp.blue_shield.max;
this.margin = 2;
} else if (type === 'Evasion') {
this.val = this.crypObj.cryp.evasion.value;
this.max = this.crypObj.cryp.evasion.max;
this.margin = 3;
}
const { statTextX, statTextY } = statTextCoord(cryp.team, cryp.iter, this.margin);
this.statText = scene.add.text(statTextX, statTextY, '', TEXT.NORMAL).setOrigin(cryp.team, 0);
this.drawStatBar();
}
drawStatBar() {
this.clear();
const {
statBarX, statBarY, statBarWidth, statBarHeight,
} = statBarDimensions(this.crypObj.team, this.crypObj.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.crypObj.setKo();
this.drawStatBar();
}
}
module.exports = StatBar;
+56
View File
@@ -0,0 +1,56 @@
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;
@@ -0,0 +1,73 @@
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 }
+53
View File
@@ -0,0 +1,53 @@
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].cryps.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 cryps = this.registry.get('cryps');
const team = cryps.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;
+27
View File
@@ -0,0 +1,27 @@
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, 'cryps.gg', TEXT.HEADER);
}
}
module.exports = Header;
+142
View File
@@ -0,0 +1,142 @@
const Phaser = require('phaser');
const { remove } = require('lodash');
const { TEXT, COLOURS, POSITIONS: { CRYP_LIST } } = require('./constants');
const genAvatar = require('./avatar');
const { LineGroup, LineBox } = require('./elements/outline.rotate');
const ROW_HEIGHT = CRYP_LIST.height() * 0.1;
const ROW_WIDTH = CRYP_LIST.width();
const menuY = CRYP_LIST.height() * 0.8;
const KEY_MAP = [
'keydown-ONE',
'keydown-TWO',
'keydown-THREE',
];
const NULL_UUID = '00000000-0000-0000-0000-000000000000';
class HomeCrypList extends Phaser.Scene {
constructor() {
super({ key: 'HomeCryps' });
}
updateData(parent, key, data) {
if (key === 'crypList') {
KEY_MAP.forEach(k => this.input.keyboard.removeListener(k));
this.scene.restart();
}
}
create() {
// this.cameras.main.setViewport(CRYP_LIST.x(), CRYP_LIST.y(), CRYP_LIST.width(), CRYP_LIST.height());
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
const cryps = this.registry.get('crypList');
const lineGroup = this.add.existing(new LineGroup(this));
if (!cryps) return true;
const ws = this.registry.get('ws');
this.activeCryps = [];
// We only display 3 cryps others can be viewed in cryp list (soon TM)
for (let i = 0; i < cryps.length; i += 1) {
const cryp = cryps[i];
const BOX_WIDTH = Math.floor(ROW_WIDTH / 5);
const ROW_X = BOX_WIDTH * 2 * (i % 3);
const ROW_Y = CRYP_LIST.y() + (Math.floor(i / 3)) * ROW_HEIGHT * 1.5;
const ACTIVE_FILL = 0.2;
const FILL = Object.values(COLOURS)[i];
// Selection of cryps
// Cryp 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.activeCryps.includes(cReady)) {
remove(this.activeCryps, n => n === cReady);
cReady.setAlpha(0.2);
} else {
this.activeCryps.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.cryp = cryp;
this.add.image(
cReady.getCenter().x,
cReady.getCenter().y,
'aztec',
genAvatar(cryp.name)
);
this.add.text(ROW_X + BOX_WIDTH, ROW_Y, cryp.name, TEXT.HEADER)
.setOrigin(0.5, 0.5);
}
// Add Spawn Cryp 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('CRYP_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 playerCryps = [];
this.activeCryps.forEach(obj => playerCryps.push(obj.cryp.id));
ws.sendPlayerCrypsSet(NULL_UUID, playerCryps);
});
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 playerCryps = [];
this.activeCryps.forEach(obj => playerCryps.push(obj.cryp.id));
ws.sendInstanceJoin(playerCryps);
});
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 = HomeCrypList;
@@ -0,0 +1,48 @@
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;
+91
View File
@@ -0,0 +1,91 @@
const Phaser = require('phaser');
const HomeCryps = require('./home.cryps');
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 = [
'HomeCryps',
'HomeNavigation',
];
const VAR_SCENES = [
'HomeRankings',
'HomeNews',
'HomeShop',
'HomeInstances',
];
class Home extends Phaser.Scene {
constructor() {
super({ key: 'Home', active: true });
}
create() {
this.registry.get('ws').sendAccountPlayers();
this.registry.events.on('changedata', this.updateData, this);
this.registry.events.on('setdata', this.updateData, this);
this.scene.manager.add('HomeCryps', HomeCryps, 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;
@@ -0,0 +1,61 @@
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;
+24
View File
@@ -0,0 +1,24 @@
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;
+24
View File
@@ -0,0 +1,24 @@
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;
+24
View File
@@ -0,0 +1,24 @@
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;
+78
View File
@@ -0,0 +1,78 @@
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, cryp } = 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 cryp`, TEXT.NORMAL)
.setWordWrapWidth(WIDTH * 0.75);
}
if (!cryp) 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, cryp.id, item);
this.registry.set('crypStats', cryp);
});
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;
+323
View File
@@ -0,0 +1,323 @@
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('MenuCrypList').children;
const hitboxes = list.filter(c => c.cryp)
.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 cryp 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.cryp.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;
+137
View File
@@ -0,0 +1,137 @@
const Phaser = require('phaser');
const { TEXT, COLOURS, POSITIONS: { CRYP_LIST } } = require('./constants');
const genAvatar = require('./avatar');
const Item = require('./elements/item');
const BOX_WIDTH = Math.floor(CRYP_LIST.width());
const BOX_HEIGHT = Math.floor(CRYP_LIST.height() / 3.34);
const TEXT_MARGIN = 24;
const KEY_MAP = [
'keydown-ONE',
'keydown-TWO',
'keydown-THREE',
];
class MenuCrypList extends Phaser.Scene {
constructor() {
super({ key: 'MenuCrypList' });
}
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.drawCryps(player.cryps);
}
updateData(parent, key, data) {
if (key === 'player') {
this.drawCryps(data.cryps);
}
}
drawCryps(cryps) {
if (!cryps) return true;
if (this.crypGroup) this.crypGroup.destroy(true);
this.crypGroup = this.add.group();
const addCryp = (cryp, i) => {
const ROW_X = CRYP_LIST.x();
const ROW_Y = CRYP_LIST.y() + BOX_HEIGHT * i * 1.1;
const ACTIVE_FILL = 0.2;
const FILL = Object.values(COLOURS)[i];
// Selection of cryps
const selectFn = () => {
this.registry.set('crypStats', cryp);
};
// Cryp interaction box for adding items
const crypInteract = this.add
.rectangle(ROW_X, ROW_Y, BOX_WIDTH, BOX_HEIGHT, FILL, ACTIVE_FILL)
.setInteractive()
.setOrigin(0)
.on('pointerdown', selectFn);
crypInteract.itemSelect = () => {
crypInteract.setFillStyle(COLOURS.SELECT);
};
crypInteract.itemDeselect = () => {
crypInteract.setFillStyle(FILL, ACTIVE_FILL);
};
crypInteract.cryp = cryp;
// Cryp Avatar
const { name } = cryp;
const crypImage = this.add.image(
crypInteract.getCenter().x - crypInteract.width / 4,
crypInteract.getCenter().y,
'aztec',
genAvatar(name)
);
// Add text info
const yCrypTextAlgin = Math.floor(crypInteract.y + TEXT_MARGIN / 2);
const xCrypNameAlign = Math.floor(crypInteract.x + crypInteract.width * 1 / 10);
const crypInfoText = this.add.text(xCrypNameAlign, yCrypTextAlgin, name, TEXT.HEADER);
const colourText = (c, j) => {
// Placeholder for when gems are implemented
const gemText = this.add.text(crypInteract.x + crypInteract.width * (j + 3) / 6, yCrypTextAlgin, `${c[0]} - ${c[1]}`, TEXT.HEADER);
this.crypGroup.add(gemText);
};
const { red, green, blue } = cryp.colours;
const CRYP_COLOURS = [
['R', red],
['G', green],
['B', blue],
];
CRYP_COLOURS.forEach(colourText);
this.crypGroup.addMultiple([crypInteract, crypImage, crypInfoText]);
const crypSkill = (stat, j) => {
const SKILL_WIDTH = Math.floor(BOX_WIDTH / 2);
const SKILL_HEIGHT = Math.floor(BOX_HEIGHT / 8);
const SKILL_X = crypInteract.getCenter().x + crypInteract.width / 4;
const SKILL_Y = crypInteract.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, cryp };
itemObj.on('pointerdown', () => this.registry.set('itemInfo', itemInfo));
this.crypGroup.add(itemObj);
};
cryp.skills.forEach(crypSkill);
const crypSpec = (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(crypInteract.x + BOX_WIDTH * (0.6 + j) * 0.175);
const SKILL_Y = Math.floor(crypInteract.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, cryp };
itemObj.on('pointerdown', () => this.registry.set('itemInfo', itemInfo));
this.add.existing(itemObj);
this.crypGroup.add(itemObj);
};
cryp.specs.forEach(crypSpec);
};
cryps.forEach(addCryp);
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 = MenuCrypList;
+97
View File
@@ -0,0 +1,97 @@
const Phaser = require('phaser');
// Scenes constantly showing
const MenuCrypList = require('./menu.cryps.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 = [
'MenuCrypList',
'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('MenuCrypList', MenuCrypList, 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 'crypStats': 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;
@@ -0,0 +1,50 @@
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;
+143
View File
@@ -0,0 +1,143 @@
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('MenuCrypList').children;
const hitboxes = list.filter(c => c.cryp)
.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 cryp = data.cryps.find(c => c.id === this.cryp.id);
this.scene.restart(cryp);
}
}
create(cryp) {
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.cryp = cryp;
/*
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, cryp.name, TEXT.HEADER);
const crypStat = (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 CRYP_STATS = [
cryp.hp,
cryp.red_shield,
cryp.blue_shield,
cryp.evasion,
cryp.red_damage,
cryp.blue_damage,
cryp.speed,
];
CRYP_STATS.forEach(crypStat);
/*
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);
};
cryp.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);
};
cryp.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, cryp.id, item.item);
}
return true;
});
*/
return this;
}
cleanUp() {
this.registry.events.off('changedata', this.updateData, this);
this.scene.remove();
}
}
module.exports = StatSheet;
+41
View File
@@ -0,0 +1,41 @@
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;
+31
View File
@@ -0,0 +1,31 @@
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;
+129
View File
@@ -0,0 +1,129 @@
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('cryps').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;