{"version":3,"sources":["../src/decorators.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Component, ComponentConstructor, ComponentField } from './component';\nimport { Entity } from './entity';\n\n/**\n * Pending fields collected by member decorators, consumed by @RegisterComponent.\n * Safe because decorator evaluation is synchronous per class.\n */\nexport const pendingFields: ComponentField[] = [];\nexport const pendingInitializers: Array<{\n    initFrom: string;\n    field: string;\n}> = [];\n\n/**\n * Register field\n */\nexport function field(\n    _value: undefined,\n    context: ClassFieldDecoratorContext\n): void {\n    pendingFields.push({ fieldName: String(context.name) });\n}\n\nexport const validate = <T>(\n    validateFunction: (val: T) => boolean\n) => {\n    return function (\n        value: ClassAccessorDecoratorTarget<Component, T>,\n        context: ClassAccessorDecoratorContext<Component, T>\n    ): ClassAccessorDecoratorResult<Component, T> {\n        pendingFields.push({ fieldName: String(context.name) });\n        return {\n            set(this: Component, val: T) {\n                if (validateFunction(val)) {\n                    value.set.call(this, val);\n                } else {\n                    throw new Error('Component failed validation function.');\n                }\n            }\n        };\n    };\n};\n\n/**\n * Initialize field as another field on startup.\n * Useful for patterns where you have a field that derives from another.\n * (Example: A 'current' hp value might be initialized from a 'max' hp value.\n * @param initAsField\n */\nexport function initializeAs(initAsField: string) {\n    return function (_value: undefined, context: ClassFieldDecoratorContext) {\n        pendingInitializers.push({\n            initFrom: initAsField,\n            field: String(context.name)\n        });\n    };\n}\n\nexport function init<C>(\n    initValue: C\n): (\n    _value: undefined,\n    context: ClassFieldDecoratorContext\n) => (initialValue: C) => C {\n    return function (\n        _value: undefined,\n        context: ClassFieldDecoratorContext\n    ): (initialValue: C) => C {\n        pendingFields.push({\n            fieldName: String(context.name),\n            defaultValue: initValue\n        });\n        return () => initValue;\n    };\n}\n\nexport function children(\n    value: ClassAccessorDecoratorTarget<Component, Set<Entity>>,\n    context: ClassAccessorDecoratorContext<Component, Set<Entity>>\n): ClassAccessorDecoratorResult<Component, Set<Entity>> {\n    pendingFields.push({ fieldName: String(context.name) });\n\n    return {\n        set(this: Component, childrenVal: Set<Entity>) {\n            const addCleanupCallback = (entity: Entity) => {\n                entity.addCleanupCallback(() => {\n                    extendedSet.delete(entity);\n                });\n            };\n            class ExtendedSet extends Set<Entity> {\n                public add(val: Entity) {\n                    if (super.has(val)) {\n                        return this;\n                    }\n                    super.add(val);\n                    addCleanupCallback(val);\n                    return this;\n                }\n            }\n\n            const extendedSet = new ExtendedSet(childrenVal);\n            value.set.call(this, extendedSet as Set<Entity>);\n        }\n    };\n}\n\n/**\n * Mark entity property as parent\n * @param parentComponentClass Component which will get updated refs to any child entity\n * @param aggregatePropertyKey Property name of array which will contain all child refs\n * @returns Decorator\n */\nexport function parent<\n    V extends { new (): Component & Record<U, Set<Entity>> },\n    U extends string\n>(parentComponentClass: V, aggregatePropertyKey: U) {\n    return (\n        value: ClassAccessorDecoratorTarget<Component, Entity>,\n        context: ClassAccessorDecoratorContext<Component, Entity>\n    ): ClassAccessorDecoratorResult<Component, Entity> => {\n        const propertyKey = String(context.name);\n        pendingFields.push({ fieldName: propertyKey });\n\n        return {\n            set(this: Component, newParent: Entity) {\n                const thisEntity = this.getEntity();\n\n                // has old parent?\n                const currentParent = value.get.call(this);\n                if (currentParent) {\n                    if (currentParent.has(parentComponentClass)) {\n                        const oldParentAggregateProp =\n                            currentParent.get(parentComponentClass)[\n                                aggregatePropertyKey\n                            ];\n                        oldParentAggregateProp.delete(thisEntity);\n                        if (oldParentAggregateProp.size === 0) {\n                            currentParent.remove(parentComponentClass);\n                        }\n                    }\n                }\n\n                value.set.call(this, newParent);\n\n                // Adding entity reference to parent component class\n                if (!newParent.has(parentComponentClass)) {\n                    const childrenSet = new Set<Entity>();\n                    childrenSet.add(thisEntity);\n                    newParent.addComponent(parentComponentClass, {\n                        [aggregatePropertyKey]: childrenSet\n                    } as any);\n                } else {\n                    const childrenSet = newParent.get(parentComponentClass)[\n                        aggregatePropertyKey\n                    ] as Set<Entity> | undefined;\n\n                    if (!childrenSet) {\n                        const newChildrenSet = new Set<Entity>();\n                        newChildrenSet.add(thisEntity);\n\n                        (newParent.get(parentComponentClass)[\n                            aggregatePropertyKey\n                        ] as Set<Entity>) = newChildrenSet;\n                    } else {\n                        childrenSet.add(thisEntity);\n                    }\n                }\n\n                // cleans up this reference if component is removed\n                const componentClass = this\n                    .constructor as ComponentConstructor;\n                newParent.addCleanupCallback(() => {\n                    if (thisEntity.has(this)) {\n                        // entity property has been removed from component, we remove the component\n                        thisEntity.removeComponent(componentClass);\n                    }\n                });\n            }\n        };\n    };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQO,IAAM,gBAAkC,CAAC;AACzC,IAAM,sBAGR,CAAC;AAKC,SAAS,MACZ,QACA,SACI;AACJ,gBAAc,KAAK,EAAE,WAAW,OAAO,QAAQ,IAAI,EAAE,CAAC;AAC1D;AAEO,IAAM,WAAW,CACpB,qBACC;AACD,SAAO,SACH,OACA,SAC0C;AAC1C,kBAAc,KAAK,EAAE,WAAW,OAAO,QAAQ,IAAI,EAAE,CAAC;AACtD,WAAO;AAAA,MACH,IAAqB,KAAQ;AACzB,YAAI,iBAAiB,GAAG,GAAG;AACvB,gBAAM,IAAI,KAAK,MAAM,GAAG;AAAA,QAC5B,OAAO;AACH,gBAAM,IAAI,MAAM,uCAAuC;AAAA,QAC3D;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACJ;AAQO,SAAS,aAAa,aAAqB;AAC9C,SAAO,SAAU,QAAmB,SAAqC;AACrE,wBAAoB,KAAK;AAAA,MACrB,UAAU;AAAA,MACV,OAAO,OAAO,QAAQ,IAAI;AAAA,IAC9B,CAAC;AAAA,EACL;AACJ;AAEO,SAAS,KACZ,WAIwB;AACxB,SAAO,SACH,QACA,SACsB;AACtB,kBAAc,KAAK;AAAA,MACf,WAAW,OAAO,QAAQ,IAAI;AAAA,MAC9B,cAAc;AAAA,IAClB,CAAC;AACD,WAAO,MAAM;AAAA,EACjB;AACJ;AAEO,SAAS,SACZ,OACA,SACoD;AACpD,gBAAc,KAAK,EAAE,WAAW,OAAO,QAAQ,IAAI,EAAE,CAAC;AAEtD,SAAO;AAAA,IACH,IAAqB,aAA0B;AAC3C,YAAM,qBAAqB,CAAC,WAAmB;AAC3C,eAAO,mBAAmB,MAAM;AAC5B,sBAAY,OAAO,MAAM;AAAA,QAC7B,CAAC;AAAA,MACL;AAAA,MACA,MAAM,oBAAoB,IAAY;AAAA,QAC3B,IAAI,KAAa;AACpB,cAAI,MAAM,IAAI,GAAG,GAAG;AAChB,mBAAO;AAAA,UACX;AACA,gBAAM,IAAI,GAAG;AACb,6BAAmB,GAAG;AACtB,iBAAO;AAAA,QACX;AAAA,MACJ;AAEA,YAAM,cAAc,IAAI,YAAY,WAAW;AAC/C,YAAM,IAAI,KAAK,MAAM,WAA0B;AAAA,IACnD;AAAA,EACJ;AACJ;AAQO,SAAS,OAGd,sBAAyB,sBAAyB;AAChD,SAAO,CACH,OACA,YACkD;AAClD,UAAM,cAAc,OAAO,QAAQ,IAAI;AACvC,kBAAc,KAAK,EAAE,WAAW,YAAY,CAAC;AAE7C,WAAO;AAAA,MACH,IAAqB,WAAmB;AACpC,cAAM,aAAa,KAAK,UAAU;AAGlC,cAAM,gBAAgB,MAAM,IAAI,KAAK,IAAI;AACzC,YAAI,eAAe;AACf,cAAI,cAAc,IAAI,oBAAoB,GAAG;AACzC,kBAAM,yBACF,cAAc,IAAI,oBAAoB,EAClC,oBACJ;AACJ,mCAAuB,OAAO,UAAU;AACxC,gBAAI,uBAAuB,SAAS,GAAG;AACnC,4BAAc,OAAO,oBAAoB;AAAA,YAC7C;AAAA,UACJ;AAAA,QACJ;AAEA,cAAM,IAAI,KAAK,MAAM,SAAS;AAG9B,YAAI,CAAC,UAAU,IAAI,oBAAoB,GAAG;AACtC,gBAAM,cAAc,oBAAI,IAAY;AACpC,sBAAY,IAAI,UAAU;AAC1B,oBAAU,aAAa,sBAAsB;AAAA,YACzC,CAAC,oBAAoB,GAAG;AAAA,UAC5B,CAAQ;AAAA,QACZ,OAAO;AACH,gBAAM,cAAc,UAAU,IAAI,oBAAoB,EAClD,oBACJ;AAEA,cAAI,CAAC,aAAa;AACd,kBAAM,iBAAiB,oBAAI,IAAY;AACvC,2BAAe,IAAI,UAAU;AAE7B,YAAC,UAAU,IAAI,oBAAoB,EAC/B,oBACJ,IAAoB;AAAA,UACxB,OAAO;AACH,wBAAY,IAAI,UAAU;AAAA,UAC9B;AAAA,QACJ;AAGA,cAAM,iBAAiB,KAClB;AACL,kBAAU,mBAAmB,MAAM;AAC/B,cAAI,WAAW,IAAI,IAAI,GAAG;AAEtB,uBAAW,gBAAgB,cAAc;AAAA,UAC7C;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AACJ;","names":[]}