components/Render.js

/* global platypus */
import RenderContainer from './RenderContainer.js';
import createComponentClass from '../factory.js';
import {Sprite, TilingSprite, AnimatedSprite, NineSliceSprite} from 'pixi.js';

const
    spriteTypes = {Sprite, TilingSprite, AnimatedSprite, NineSliceSprite};

export default createComponentClass(/** @lends platypus.components.RenderSprite.prototype */{
    
    id: 'Render',
    
    properties: {
        /**
         * Whether the image should auto-resize if the owner's size changes. For NineSliceSprite, this auto-adjusts to the entity's height and width changes. For other types, it modifies the sprite's scale to match the original sprite-to-entity size ratio.
         * 
         * @property matchEntity
         * @type Boolean
         * @default false
         */
        matchEntity: false,

        /**
         * The offset of the x-axis position of the sprite from the entity's x-axis position.
         *
         * @property offsetX
         * @type Number
         * @default 0
         */
        offsetX: 0,

        /**
         * The offset of the y-axis position of the sprite from the entity's y-axis position.
         *
         * @property offsetY
         * @type Number
         * @default 0
         */
        offsetY: 0,

        /**
         * The z-index relative to other render components on the entity.
         *
         * @property offsetZ
         * @type Number
         * @default 0
         */
        offsetZ: 0,

        options: null,

        image: null,

        images: null,

        spriteType: 'Sprite',

        /**
        * The rotation for this sprite relative to the container.
        *
        * @property localRotation
        * @type Number
        * @default 0
        */
        localRotation: 0,
        
        /**
        * The scaling factor for this sprite relative to the scale of the container.
        *
        * @property localScaleX
        * @type Number|Array|Object
        * @default 1
        */
        localScaleX: 1,

        /**
        * The scaling factor for this sprite relative to the scale of the container.
        *
        * @property localScaleY
        * @type Number|Array|Object
        * @default 1
        */
        localScaleY: 1
    },

    /**
     * This component is attached to entities that will appear in the game world. It renders a static or animated image. It listens for messages triggered on the entity or changes in the logical state of the entity to play a corresponding animation.
     *
     * @memberof platypus.components
     * @uses platypus.Component
     * @constructs
     * @listens platypus.Entity#handle-render
     * @listens platypus.Entity#play-animation
     * @listens platypus.Entity#stop-animation
     * @fires platypus.Entity#animation-ended
     * @fires platypus.Entity#update-animation
     */
    initialize () {
        const
            {image, images, localRotation, localScaleX, localScaleY, offsetX, offsetY, offsetZ, options, owner, spriteType} = this,
            sprite = this.sprite = new spriteTypes[spriteType === 'Sprite' && images && !images ? 'AnimatedSprite' : spriteType]({
                ...(image ? {texture: platypus.assetCache.get(image)} : {textures: images.map((image) => platypus.assetCache.get(image))}),
                ...options ?? {}
            }),
            matchEntity = this.matchEntity ? {
                originalWidth: owner.width,
                originalHeight: owner.height
            } : null;

        sprite.x = offsetX;
        sprite.y = offsetY;
        sprite.zIndex = offsetZ;
        sprite.rotation = (localRotation / 180) * Math.PI;
        sprite.scale.x = localScaleX;
        sprite.scale.y = localScaleY;

        if (!owner.container) {
            const
                {interactive, mask, mirror, flip, visible, cache, ignoreOpacity, scaleX, scaleY, skewX, skewY} = owner;
            
            owner.addComponent(new RenderContainer(this.owner, {interactive, mask, mirror, flip, visible, cache, ignoreOpacity, scaleX, scaleY, skewX, skewY}, () => this.addToContainer()));
        } else {
            this.addToContainer();
        }

        if (matchEntity) {
            if (spriteType === 'NineSliceSprite') {
                sprite.x = offsetX - owner.width / 2;
                sprite.y = offsetY - owner.height / 2;
                sprite.width = owner.width;
                sprite.height = owner.height;
                this.addEventListener('handle-render', (renderData) => {
                    sprite.x = offsetX - owner.width / 2;
                    sprite.y = offsetY - owner.height / 2;
                    sprite.width = owner.width;
                    sprite.height = owner.height;
                });
            } else {
                this.addEventListener('handle-render', (renderData) => {
                    sprite.scale.x = localScaleX * owner.width / matchEntity.originalWidth;
                    sprite.scale.y = localScaleY * owner.height / matchEntity.originalHeight;
                });
            }
        }

        if (sprite.update) {
            this.addEventListener('handle-render', (renderData) => {
                if (sprite.update) {
                    sprite.update(renderData.delta);
                }
            });
        }
    },
    
    methods: {
        addToContainer: function () {
            const container = this.owner.container;

            container.addChild(this.sprite);
        },

        destroy: function () {
            this.owner.container.removeChild(this.sprite);
            this.sprite.destroy();
            this.sprite = null;
        }
    },
    
    getAssetList (a, b, c) {
        const
            image = a?.image ?? b?.image ?? c?.image;

        return (image ? [image] : a?.images ?? b?.images ?? c?.images ?? []).map((src) => ({
            alias: [src],
            src
        }));
    }
});