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;