import type { Vector } from '../../math/vector'; import { CoordPlane } from '../../math/coord-plane'; import { Transform } from '../../math/transform'; import { Component } from '../component'; import type { Entity } from '../entity'; import { Observable } from '../../util/observable'; import { Logger } from '../../util/log'; // ============================================================= // Transform Component Serialization Data // ============================================================ export interface TransformComponentData { type: string; pos: { x: number; y: number }; rotation: number; scale: { x: number; y: number }; z: number; coordPlane?: CoordPlane; } export class TransformComponent extends Component { // @ts-ignore private static _NAME = 'TransformComponent'; private _logger = Logger.getInstance(); private _parentComponent: TransformComponent | null = null; private _transform = new Transform(); public get() { return this._transform; } private _addChildTransform = (child: Entity) => { const childTxComponent = child.get(TransformComponent); if (childTxComponent) { childTxComponent._transform.parent = this._transform; childTxComponent._parentComponent = this; } }; override onAdd(owner: Entity): void { for (const child of owner.children) { this._addChildTransform(child); } owner.childrenAdded$.subscribe((child) => this._addChildTransform(child)); owner.childrenRemoved$.subscribe((child) => { const childTxComponent = child.get(TransformComponent); if (childTxComponent) { childTxComponent._transform.parent = null; childTxComponent._parentComponent = null; } }); } override onRemove(_previousOwner: Entity): void { this._transform.parent = null; this._parentComponent = null; } /** * Observable that emits when the z index changes on this component */ public zIndexChanged$ = new Observable(); /** * The z-index ordering of the entity, a higher values are drawn on top of lower values. * For example z=99 would be drawn on top of z=0. */ public get z(): number { return this._transform.z; } public set z(val: number) { const oldz = this._transform.z; this._transform.z = val; if (oldz !== val) { this.zIndexChanged$.notifyAll(val); } } public get globalZ() { return this._transform.globalZ; } public set globalZ(z: number) { this._transform.globalZ = z; } private _coordPlane = CoordPlane.World; /** * The {@apilink CoordPlane | `coordinate plane`} for this transform for the entity. */ public get coordPlane() { if (this._parentComponent) { return this._parentComponent.coordPlane; } return this._coordPlane; } public set coordPlane(value: CoordPlane) { if (!this._parentComponent) { this._coordPlane = value; } else { this._logger.warn( `Cannot set coordinate plane on child entity ${this.owner?.name}, children inherit their coordinate plane from their parents.` ); } } get pos() { return this._transform.pos; } set pos(v: Vector) { this._transform.pos = v; } get globalPos() { return this._transform.globalPos; } set globalPos(v: Vector) { this._transform.globalPos = v; } get rotation() { return this._transform.rotation; } set rotation(rotation) { this._transform.rotation = rotation; } get globalRotation() { return this._transform.globalRotation; } set globalRotation(rotation) { this._transform.globalRotation = rotation; } get scale() { return this._transform.scale; } set scale(v: Vector) { this._transform.scale = v; } get globalScale() { return this._transform.globalScale; } set globalScale(v: Vector) { this._transform.globalScale = v; } applyInverse(v: Vector) { return this._transform.applyInverse(v); } apply(v: Vector) { return this._transform.apply(v); } override clone(): TransformComponent { const component = new TransformComponent(); component._transform = this._transform.clone(); return component; } /** * Custom serialization - only store local transform values * Private fields (_transform, _parentComponent) are automatically excluded */ public override serialize(): TransformComponentData { const type = this.constructor.name; const data: TransformComponentData = { type, pos: { x: this.pos.x, y: this.pos.y }, rotation: this.rotation, scale: { x: this.scale.x, y: this.scale.y }, z: this.z }; // Only serialize coordPlane if not inherited from parent if (!this._parentComponent) { data.coordPlane = this._coordPlane; } return data; } /** * Restore state from serialized data */ public override deserialize(data: TransformComponentData): void { // Set local transform values this.pos = { x: data.pos.x, y: data.pos.y } as Vector; this.rotation = data.rotation; this.scale = { x: data.scale.x, y: data.scale.y } as Vector; this.z = data.z; // Only set coordPlane if provided (root entities only) if (data.coordPlane !== undefined && !this._parentComponent) { this._coordPlane = data.coordPlane; } } }