import {behaviorItemFromJSON, createContext} from '@avocado/behavior'; import {compose} from '@avocado/core'; import {StateProperty, Trait} from '@avocado/entity'; const decorate = compose( StateProperty('life', { track: true, }), StateProperty('maxLife', { track: true, }), ); export class Alive extends decorate(Trait) { static defaultParams() { return { deathActions: { type: 'actions', traversals: [ { type: 'traversal', steps: [ { type: 'key', key: 'entity', }, { type: 'key', key: 'transition', }, { type: 'invoke', args: [ { type: 'literal', value: { opacity: 0, visibleScaleX: .3, visibleScaleY: 3, }, }, { type: 'literal', value: 0.2, }, ], }, ], }, ], }, // By default, die when life <= 0. deathCondition: { type: 'condition', operator: '<=', operands: [ { type: 'traversal', steps: [ { type: 'key', key: 'entity', }, { type: 'key', key: 'life', }, ], }, { type: 'literal', value: 0, }, ], }, }; } static defaultState() { return { life: 100, maxLife: 100, }; } initialize() { this._context = createContext(); this._context.add('entity', this.entity); const actionsJSON = this.params.get('deathActions').toJS(); this._deathActions = behaviorItemFromJSON(actionsJSON); const conditionJSON = this.params.get('deathCondition').toJS(); this._deathCondition = behaviorItemFromJSON(conditionJSON); this._isDying = false; } listeners() { return { tookDamage: (damage) => { this.entity.life -= damage.amount; // Clamp health between 0 and max. this.entity.life = Math.min( Math.max(0, this.entity.life), this.entity.maxLife ); }, } } methods() { return { die: () => { this._isDying = true; this.entity.emit('dying'); this._deathActions.on('actionsFinished', () => { this.entity.destroy(); }); }, }; } tick(elapsed) { if (this._isDying) { this._deathActions.tick(this._context, elapsed); } else { if (this._deathCondition.check(this._context)) { // It's a good day to die. this.entity.die(); } } } }