mnml/client/src/scenes/combat.cryps.js
2019-01-13 18:40:57 +10:00

293 lines
10 KiB
JavaScript

const Phaser = require('phaser');
const genAvatar = require('./avatar');
const { DELAYS, TEXT, POSITIONS: { COMBAT }, COLOURS } = require('./constants');
const CRYP_MARGIN = COMBAT.height() / 4.5;
const TEXT_MARGIN = COMBAT.height() / 35;
const healthBarDimensions = (team, iter, margin) => {
const healthBarWidth = COMBAT.width() * 0.07;
const healthBarHeight = TEXT_MARGIN / 1.5;
const healthBarX = (COMBAT.width() - healthBarWidth) * team;
const healthBarY = COMBAT.y() + TEXT_MARGIN * (margin + 1) + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
return { healthBarX, healthBarY, healthBarWidth, healthBarHeight };
};
const crypAvatarText = (team, iter) => {
const nameX = COMBAT.width() * team;
const nameY = COMBAT.y() + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
const healthX = team ? COMBAT.width() - COMBAT.width() * 0.075 : COMBAT.width() * 0.075;
const healthY = COMBAT.y() + TEXT_MARGIN + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
const statusX = COMBAT.width() * team;
const statusY = COMBAT.y() + TEXT_MARGIN * 3 + CRYP_MARGIN * iter + COMBAT.height() * 0.07;
return { statusX, statusY, nameX, nameY, healthX, healthY };
};
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 StatBar extends Phaser.GameObjects.Graphics {
constructor(scene, cryp, crypStatText, type) {
super(scene);
this.crypObj = cryp;
this.type = type;
this.statText = crypStatText;
if (type === 'HP') {
this.val = this.crypObj.cryp.hp.base;
this.max = this.crypObj.cryp.stamina.base;
this.margin = 0;
} else if (type === 'Armour') {
this.val = this.crypObj.cryp.armour.base;
this.max = this.crypObj.cryp.armour.base;
this.margin = 1;
} else if (type === 'Evasion') {
this.val = this.crypObj.cryp.evasion.base;
this.max = this.crypObj.cryp.evasion.base;
this.margin = 2;
} else if (type === 'Spell Shield') {
this.val = this.crypObj.cryp.spell_shield.base;
this.max = this.crypObj.cryp.spell_shield.base;
this.margin = 3;
}
this.drawStatBar();
}
drawStatBar() {
this.clear();
const {
healthBarX, healthBarY, healthBarWidth, healthBarHeight,
} = healthBarDimensions(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(healthBarX, healthBarY, healthBarWidth, healthBarHeight);
// White fill
this.fillStyle(COLOURS.WHITE);
this.fillRect(healthBarX + 2, healthBarY + 2, healthBarWidth - 4, healthBarHeight - 4);
// Fill the health bar
const healthPercentage = this.val / this.max;
if (healthPercentage < 0.3) {
this.fillStyle(COLOURS.RED);
} else if (healthPercentage < 0.65) {
this.fillStyle(COLOURS.YELLOW);
} else {
this.fillStyle(0x00ff00); // str8 up green
}
const healthWidth = Math.floor(healthBarWidth * healthPercentage);
this.fillRect(healthBarX + 2, healthBarY + 2, healthWidth - 4, healthBarHeight - 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.crypObj.setKo();
this.drawStatBar();
}
}
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, healthX, healthY,
} = 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 hp
const healthText = scene.add.text(healthX, healthY, '', TEXT.NORMAL).setOrigin(team, 0);
this.healthBar = scene.add.existing(new StatBar(scene, this, healthText, 'HP'));
const armourText = scene.add.text(healthX, healthY + TEXT_MARGIN, '', TEXT.NORMAL).setOrigin(team, 0);
this.armour = scene.add.existing(new StatBar(scene, this, armourText, 'Armour'));
const evasionText = scene.add.text(healthX, healthY + TEXT_MARGIN * 2, '', TEXT.NORMAL).setOrigin(team, 0);
this.evasion = scene.add.existing(new StatBar(scene, this, evasionText, 'Evasion'));
const ssText = scene.add.text(healthX, healthY + TEXT_MARGIN * 3, '', TEXT.NORMAL).setOrigin(team, 0);
this.spellShield = scene.add.existing(new StatBar(scene, this, ssText, 'Spell Shield'));
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 = '';
}
takeDamage(damage) {
if (damage > 0) this.setTint(0xff0000);
else this.setTint(0x00bb00);
this.healthBar.takeDamage(damage);
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);
}
updateData(parent, key, data) {
if (key === 'game' && data) {
const isAnimating = this.phase === 'animating';
if (isAnimating) return false;
this.drawCryps(data);
}
if (key === 'gamePhase' && data) {
const shouldUpdate = data !== this.phase;
this.phase = data;
if (shouldUpdate) this.cryps.children.entries.forEach(c => c.clearStatus());
}
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.healthBar.hp = cryp.hp.base;
crypObj.healthBar.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;