187 lines
4.0 KiB
JavaScript
187 lines
4.0 KiB
JavaScript
import {compose} from '@avocado/core';
|
|
import {StateProperty, Trait} from '@avocado/entity';
|
|
import {Rectangle, Vector} from '@avocado/math';
|
|
|
|
import {Image} from '../image';
|
|
import {Sprite} from '../sprite';
|
|
|
|
const decorate = compose(
|
|
StateProperty('currentImage', {
|
|
track: true,
|
|
}),
|
|
);
|
|
|
|
export class Pictured extends decorate(Trait) {
|
|
|
|
static defaultParams() {
|
|
return {
|
|
images: {},
|
|
};
|
|
}
|
|
|
|
static defaultState() {
|
|
return {
|
|
currentImage: 'initial',
|
|
};
|
|
}
|
|
|
|
static type() {
|
|
return 'pictured';
|
|
}
|
|
|
|
constructor(entity, params, state) {
|
|
super(entity, params, state);
|
|
this._cachedAabbs = {};
|
|
this._images = this.params.images;
|
|
this.sprites = undefined;
|
|
}
|
|
|
|
destroy() {
|
|
if (this.sprites) {
|
|
for (const key in this.sprites) {
|
|
this.hideImage(key);
|
|
const sprite = this.sprites[key];
|
|
sprite.image.destroy();
|
|
sprite.destroy();
|
|
}
|
|
this.sprites = undefined;
|
|
}
|
|
}
|
|
|
|
hideImage(key) {
|
|
if (!this.sprites) {
|
|
return;
|
|
}
|
|
const sprite = this.sprites[key];
|
|
if (!sprite) {
|
|
return;
|
|
}
|
|
this.entity.container.removeChild(sprite);
|
|
}
|
|
|
|
loadImagesIfPossible() {
|
|
if (!this.entity.container) {
|
|
return;
|
|
}
|
|
if (this.sprites) {
|
|
return;
|
|
}
|
|
// Load all images.
|
|
const imagePromises = [];
|
|
this.sprites = {};
|
|
for (const key in this._images) {
|
|
const {uri} = this._images[key];
|
|
const imagePromise = Image.load(uri).then((image) => {
|
|
const sprite = this.sprites[key] = new Sprite(image);
|
|
// Calculate any offset.
|
|
sprite.position = this.offsetFor(key);
|
|
// Set current image upfront.
|
|
const isCurrentImage = key === this.entity.currentImage;
|
|
if (isCurrentImage) {
|
|
this.showImage(key);
|
|
}
|
|
});
|
|
imagePromises.push(imagePromise);
|
|
}
|
|
Promise.all(imagePromises).then((images) => {
|
|
// Bounding box update.
|
|
this.entity.updateVisibleBoundingBox();
|
|
this.setSpriteScale();
|
|
});
|
|
}
|
|
|
|
offsetFor(key) {
|
|
if (!this._images[key] || !this._images[key].offset) {
|
|
return [0, 0];
|
|
}
|
|
return this._images[key].offset;
|
|
}
|
|
|
|
setSpriteScale() {
|
|
if (!this.sprites) {
|
|
return;
|
|
}
|
|
const rawScale = this.entity.rawVisibleScale;
|
|
for (const key in this.sprites) {
|
|
const sprite = this.sprites[key];
|
|
sprite.scale = rawScale;
|
|
}
|
|
}
|
|
|
|
showImage(key) {
|
|
if (!this.sprites) {
|
|
return;
|
|
}
|
|
const sprite = this.sprites[key];
|
|
if (!sprite) {
|
|
return;
|
|
}
|
|
this.entity.container.addChild(sprite);
|
|
}
|
|
|
|
sizeFor(key) {
|
|
if (!this._images[key] || !this._images[key].size) {
|
|
return [0, 0];
|
|
}
|
|
return this._images[key].size;
|
|
}
|
|
|
|
hooks() {
|
|
return {
|
|
|
|
visibleAabbs: () => {
|
|
const key = this.entity.currentImage;
|
|
if (this._cachedAabbs[key]) {
|
|
return this._cachedAabbs[key];
|
|
}
|
|
const image = this._images[key];
|
|
if (!image) {
|
|
return [0, 0, 0, 0];
|
|
}
|
|
const viewPosition = this.offsetFor(key);
|
|
const size = this.sizeFor(key);
|
|
const rectangle = Rectangle.compose(viewPosition, size);
|
|
const expanded = Rectangle.expand(
|
|
rectangle,
|
|
Vector.sub(Vector.mul(size, this.entity.rawVisibleScale), size),
|
|
);
|
|
return this._cachedAabbs[key] = expanded;
|
|
},
|
|
|
|
}
|
|
}
|
|
|
|
listeners() {
|
|
return {
|
|
|
|
currentImageChanged: (oldKey) => {
|
|
// Bounding box update.
|
|
this.entity.updateVisibleBoundingBox();
|
|
// Only client/graphics.
|
|
if (!this.sprites) {
|
|
return;
|
|
}
|
|
// Swap the image.
|
|
this.hideImage(oldKey);
|
|
this.showImage(this.entity.currentImage);
|
|
},
|
|
|
|
visibleScaleChanged: () => {
|
|
this.setSpriteScale();
|
|
},
|
|
|
|
traitAdded: (type) => {
|
|
if (-1 === [
|
|
'visible',
|
|
'pictured',
|
|
].indexOf(type)) {
|
|
return;
|
|
}
|
|
this.loadImagesIfPossible();
|
|
},
|
|
|
|
};
|
|
}
|
|
|
|
}
|