const Phaser = require('phaser'); const { DELAYS, TEXT, POSITIONS: { COMBAT }, COLOURS } = require('./constants'); const CRYP_MARGIN = COMBAT.height() / 5; const TEXT_MARGIN = COMBAT.height() / 35; const TEAM_MARGIN = COMBAT.width() * 0.7; const healthBarDimensions = (team, iter) => { const healthBarX = 1.25 * TEAM_MARGIN * team; const healthBarY = COMBAT.y() + TEXT_MARGIN + CRYP_MARGIN * iter + COMBAT.height() * 0.07; const healthBarWidth = TEAM_MARGIN / 10; const healthBarHeight = TEXT_MARGIN / 1.5; return { healthBarX, healthBarY, healthBarWidth, healthBarHeight }; }; const crypAvatarText = (team, iter) => { const nameX = 1.25 * TEAM_MARGIN * team; const nameY = COMBAT.y() + CRYP_MARGIN * iter + COMBAT.height() * 0.07; const healthX = 1.25 * TEAM_MARGIN * team; const healthY = COMBAT.y() + TEXT_MARGIN * 2 + CRYP_MARGIN * iter + COMBAT.height() * 0.07; const statusX = 1.25 * TEAM_MARGIN * 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 = COMBAT.width() / 7.5 + TEAM_MARGIN * team; const crypEffectsY = TEXT_MARGIN * 2 + CRYP_MARGIN * iter; return { crypEffectsX, crypEffectsY }; }; const crypPosition = (team, iter) => { const crypAvatarX = COMBAT.width() / 7.5 + TEAM_MARGIN * team; const crypAvatarY = TEXT_MARGIN * 5 + CRYP_MARGIN * iter; return { crypAvatarX, crypAvatarY }; }; class HealthBar extends Phaser.GameObjects.Graphics { constructor(scene, cryp, crypHpText) { super(scene); this.crypObj = cryp; this.hpText = crypHpText; this.hp = this.crypObj.cryp.hp.base; this.stam = this.crypObj.cryp.stamina.base; this.drawHealthBar(); } drawHealthBar() { this.clear(); const { healthBarX, healthBarY, healthBarWidth, healthBarHeight, } = healthBarDimensions(this.crypObj.team, this.crypObj.iter); this.hpText.text = `${this.hp.toString()} / ${this.stam.toString()} HP`; // 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.hp / this.stam; 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, healthBarHeight - 4); } takeDamage(value) { if (value > 0) { this.hp = (value >= this.hp) ? 0 : this.hp -= value; } else { this.hp = (this.hp - value > this.stam) ? this.stam : this.hp -= value; } if (this.hp === 0) this.crypObj.setKo(); this.drawHealthBar(); } } 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', `sprite${Math.floor(Math.random() * 19) + 1}`); 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); // Add cryp hp const healthText = scene.add.text(healthX, healthY, '', TEXT.NORMAL); this.healthBar = scene.add.existing(new HealthBar(scene, this, healthText)); 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.drawHealthBar(); 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 targetCryp = this.cryps.children.entries .find(c => c.cryp.id === status.id); if (this.phase === 'Skill') { targetCryp.statusText.text = status.target === targetCryp.cryp.account ? `${status.skill} on ally team` : `${status.skill} on enemy team`; } if (this.phase === 'Target') { const sourceCryp = this.cryps.children.entries .find(c => c.cryp.id === status.skill.source_cryp_id); targetCryp.statusText.text = `${sourceCryp.cryp.name} ${status.skill.skill} ${targetCryp.cryp.name}`; } } cleanUp() { this.registry.events.off('changedata', this.updateData); this.scene.remove(); } } module.exports = CombatCryps;