import * as I from 'immutable'; import {Trait} from '@avocado/entity'; import {hasGraphics, TextNodeRenderer} from '@avocado/graphics'; import {DamageEmitter} from './emitter'; export class Vulnerable extends Trait { static defaultState() { return { damageList: I.Map(), }; } initialize() { this.damageId = 0; this.damageList = {}; this.locks = new Map(); if (hasGraphics) { this.emitter = new DamageEmitter(); this.setRenderer(); } } patchStateStep(key, step) { if ('state' !== key) { return; } const stateKey = step.path.substr(1); const value = this.transformPatchValue(stateKey, step.value); const stateKeyParts = stateKey.split('/'); switch (stateKeyParts[0]) { case 'damageList': for (let i = 0; i < value.length; ++i) { const damage = value[i]; this.emitter.emit(this.entity.position, damage); } break; default: super.patchStateStep(key, step); break; } } setRenderer() { if (this.entity.is('staged') && this.entity.stage) { const renderer = new TextNodeRenderer('.damage', this.entity.stage); this.emitter.addRenderer(renderer); } } hooks() { return { afterDestructionTickers: () => { return (elapsed) => { if (!hasGraphics) { return true; } this.emitter.tick(elapsed); return !this.emitter.hasParticles(); }; }, } } listeners() { return { stageChanged: () => { this.setRenderer(); }, }; } methods() { return { takeDamageFrom: (entity) => { const damageSpecs = entity.damageSpecs; for (let i = 0; i < damageSpecs.length; ++i) { const damageSpec = damageSpecs[i]; if (this.locks.has(damageSpec)) { return; } this.locks.set(damageSpec, damageSpec.lock); const variance = Math.random() * damageSpec.variance * 2 - damageSpec.variance; const difference = damageSpec.power * variance; // Account for variance past 0, so track if it's damage or not. let amount = Math.round(damageSpec.power + difference); let isDamage; if (damageSpec.power < 0) { isDamage = false; if (amount > 0) { amount = 0; } } else { isDamage = true; if (amount < 0) { amount = 0; } } amount = Math.abs(amount); if (!this.damageList[entity.instanceUuid]) { this.damageList[entity.instanceUuid] = []; } const damage = { id: this.damageId++, isDamage, amount, damageSpec, }; this.damageList[entity.instanceUuid].push(damage); this.entity.emit('tookDamage', damage); } }, }; } tick(elapsed) { if (hasGraphics) { this.emitter.tick(elapsed); } if (this.state.get('damageList').size > 0) { this.state = this.state.set('damageList', I.Map()); } if (Object.keys(this.damageList).length > 0) { this.state = this.state.set('damageList', I.Map(this.damageList)); this.isDirty = true; this.damageList = {}; } const keys = Array.from(this.locks.keys()); for (let i = 0; i < keys.length; ++i) { const key = keys[i]; const remaining = this.locks.get(key) - elapsed; if (remaining <= 0) { this.locks.delete(key); } else { this.locks.set(key, remaining); } } } }