import * as I from 'immutable'; import {compose, Property} from '@avocado/core'; import {StateProperty, Trait} from '@avocado/entity'; import {Rectangle, Vector} from '@avocado/math'; import {Container} from '../container'; import {hasGraphics} from '../has-graphics'; import {TraitVisiblePacket} from '../packets/trait-visible.packet'; const AUTO_ZINDEX = 1 << 16; const decorate = compose( StateProperty('isVisible', { track: true, }), StateProperty('opacity', { track: true, }), StateProperty('visibleScale', { track: true, }), StateProperty('zIndex', { track: true, }), ); export class Visible extends decorate(Trait) { static defaultParams() { return { filter: undefined, trackPosition: true, }; } static defaultState() { return { isVisible: true, opacity: 1, visibleScale: [1, 1], zIndex: AUTO_ZINDEX, }; } static type() { return 'visible'; } constructor(entity, params, state) { super(entity, params, state); if (hasGraphics) { this._container = new Container(); const filter = this.params.filter; if (filter) { this._container.setFilter(filter); } this._container.isVisible = this.state.isVisible; } this._rawVisibleAabb = [0, 0, 0, 0]; this.scheduledBoundingBoxUpdate = true; this.trackPosition = this.params.trackPosition; const scale = this.state.visibleScale; this._visibleScale = [scale[0], scale[1]]; this.onZIndexChanged(this.state.zIndex); } destroy() { if (this._container) { this._container.destroy(); } } acceptPacket(packet) { if (packet instanceof TraitVisiblePacket) { this.entity.isVisible = packet.data.isVisible; this.entity.opacity = packet.data.opacity / 255; } } get container() { return this._container; } onZIndexChanged(zIndex) { this._usingAutoZIndex = AUTO_ZINDEX === zIndex; if (!this._usingAutoZIndex) { if (this._container) { this._container.zIndex = zIndex; } } } packetsForUpdate() { const packets = []; if (this.isDirty) { packets.push(new TraitVisiblePacket({ isVisible: this.entity.isVisible, opacity: Math.floor(this.entity.opacity * 255), }, this.entity)); this.makeClean(); } return packets; } get rawVisibleScale() { return this._visibleScale; } set rawVisibleScale(scale) { this.entity.visibleScale = scale; } synchronizePosition() { if (!this.entity.is('positioned')) { return; } if (!this._container) { return; } this._container.position = this.entity.position; if (this._usingAutoZIndex) { if (this._container) { this._container.zIndex = this.entity.y; } } } get visibleScaleX() { return this.state.visibleScale[0]; } set visibleScaleX(x) { this.rawVisibleScale = [x, this.visibleScaleY]; } get visibleScaleY() { return this.state.visibleScale[1]; } set visibleScaleY(y) { this.rawVisibleScale = [this.visibleScaleX, y]; } listeners() { return { isVisibleChanged: () => { if (!this._container) { return; } this._container.visible = this.entity.isVisible; }, opacityChanged: () => { if (!this._container) { return; } this._container.alpha = this.entity.opacity; }, positionChanged: () => { this.translateVisibleAabb(); }, traitAdded: (type) => { if (-1 === [ 'visible', 'positioned', ].indexOf(type)) { return; } this.synchronizePosition(); }, visibleScaleChanged: () => { const scale = this.entity.visibleScale; this._visibleScale = [scale[0], scale[1]]; if (this._container) { this._container.scale = this._visibleScale; } }, zIndexChanged: (old, zIndex) => { this.onZIndexChanged(zIndex); }, }; } methods() { return { updateVisibleBoundingBox: () => { this.scheduledBoundingBoxUpdate = true; }, } } renderTick(elapsed) { this.synchronizePosition(); } translateVisibleAabb() { const visibleAabb = Rectangle.translated( this._rawVisibleAabb, this.entity.position ); this.entity.visibleAabb[0] = visibleAabb[0]; this.entity.visibleAabb[1] = visibleAabb[1]; this.entity.visibleAabb[2] = visibleAabb[2]; this.entity.visibleAabb[3] = visibleAabb[3]; this.entity.emit('visibleAabbChanged'); } tick(elapsed) { if (AVOCADO_SERVER) { if (this.scheduledBoundingBoxUpdate) { // Collect all bounding boxes. const visibleAabbs = this.entity.invokeHookFlat( 'visibleAabbs' ); if (0 === visibleAabbs.length) { this._rawVisibleAabb = [0, 0, 0, 0]; } else { let unifiedBoundingBox = [0, 0, 0, 0]; for (let i = 0; i < visibleAabbs.length; ++i) { const visibleAabb = visibleAabbs[i]; unifiedBoundingBox = Rectangle.united( unifiedBoundingBox, visibleAabb, ); } this._rawVisibleAabb = unifiedBoundingBox; } this.translateVisibleAabb(); this.scheduledBoundingBoxUpdate = false; } } } }