/* * GDevelop JS Platform * Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved. * This project is released under the MIT License. */ namespace gdjs { /** * Allow to store a behavior in a RBush (spatial data structure). * Because this duplicates the AABB, this ensures the RBush AABB * stays the same even if the underlying object is moved * (in which case the behavior is responsible for removing/adding * back/updating this BehaviorRBushAABB). * @category Core Engine > Behavior */ export class BehaviorRBushAABB { minX: float = 0; minY: float = 0; maxX: float = 0; maxY: float = 0; behavior: T; constructor(behavior: T) { this.behavior = behavior; this.updateAABBFromOwner(); } updateAABBFromOwner() { this.minX = this.behavior.owner.getAABB().min[0]; this.minY = this.behavior.owner.getAABB().min[1]; this.maxX = this.behavior.owner.getAABB().max[0]; this.maxY = this.behavior.owner.getAABB().max[1]; } } /** * RuntimeBehavior represents a behavior being used by a RuntimeObject. * @category Core Engine > Behavior */ export class RuntimeBehavior { name: string; type: string; _nameId: integer; _activated: boolean; // When synchronised over the network, a behavior is always owned by the player owning the object, // and always synced. If set to false, the behavior properties will not be synced to others. _syncOverNetwork: boolean = true; /** * @param instanceContainer The container owning the object of the behavior * @param behaviorData The properties used to setup the behavior * @param owner The object owning the behavior */ constructor( instanceContainer: gdjs.RuntimeInstanceContainer, behaviorData: BehaviorData, public owner: gdjs.RuntimeObject ) { this.name = behaviorData.name || ''; this.type = behaviorData.type || ''; this._nameId = gdjs.RuntimeObject.getNameIdentifier(this.name); const game = instanceContainer.getGame(); this._activated = !game.isInGameEdition() || !!game.isBehaviorActivatedByDefaultInEditor(this.type); } /** * Called when the behavior must be updated using the specified behaviorData. This is the * case during hot-reload, and is only called if the behavior was modified. * * @see gdjs.RuntimeBehavior#onObjectHotReloaded * * @param oldBehaviorData The previous data for the behavior. * @param newBehaviorData The new data for the behavior. * @returns true if the behavior was updated, false if it could not (i.e: hot-reload is not supported). */ updateFromBehaviorData( oldBehaviorData: BehaviorData, newBehaviorData: BehaviorData ): boolean { const diffBehaviorData: BehaviorData & { [key: string]: any } = { name: newBehaviorData.name, type: newBehaviorData.type, }; const oldData = oldBehaviorData as { [key: string]: any }; const newData = newBehaviorData as { [key: string]: any }; for (const key in newData) { if (newData[key] !== oldData[key]) { diffBehaviorData[key] = newData[key]; } } // If not redefined, mark by default the hot-reload as failed. return this.applyBehaviorOverriding(diffBehaviorData); } applyBehaviorOverriding(behaviorOverriding: BehaviorData): boolean { // If not redefined, mark by default the hot-reload as failed. return false; } getNetworkSyncData( syncOptions: GetNetworkSyncDataOptions ): BehaviorNetworkSyncData { // To be redefined by behaviors that need to synchronize properties // while calling super() to get the common properties. return { act: this._activated, props: {}, }; } /** * Update the behavior properties using the provided data. * @param networkSyncData The new properties of the behavior. */ updateFromNetworkSyncData( networkSyncData: BehaviorNetworkSyncData, options: UpdateFromNetworkSyncDataOptions ): void { // Must be redefined by behaviors that need to synchronize properties // while calling super() to get the common properties. if (networkSyncData.act !== this._activated) { this.activate(networkSyncData.act); } } /** * Get the name of the behavior. * @return The behavior's name. */ getName(): string { return this.name; } /** * Get the name identifier of the behavior. * @return The behavior's name identifier. */ getNameId(): integer { return this._nameId; } /** * Called at each frame before events. Call doStepPreEvents.
* Behaviors writers: Please do not redefine this method. Redefine doStepPreEvents instead. * @param instanceContainer The instanceContainer owning the object */ stepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer): void { if (this._activated) { const profiler = instanceContainer.getScene().getProfiler(); if (profiler) { profiler.begin(this.name); } this.doStepPreEvents(instanceContainer); if (profiler) { profiler.end(this.name); } } } /** * Called at each frame after events. Call doStepPostEvents.
* Behaviors writers: Please do not redefine this method. Redefine doStepPreEvents instead. * @param instanceContainer The instanceContainer owning the object */ stepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer): void { if (this._activated) { const profiler = instanceContainer.getScene().getProfiler(); if (profiler) { profiler.begin(this.name); } this.doStepPostEvents(instanceContainer); if (profiler) { profiler.end(this.name); } } } /** * De/Activate the behavior * @param enable true to enable the behavior, false to disable it */ activate(enable: boolean): void { if (enable === undefined) { enable = true; } if (!this._activated && enable) { this._activated = true; this.onActivate(); } else { if (this._activated && !enable) { this._activated = false; this.onDeActivate(); } } } /** * Reimplement this to do extra work when the behavior is created (i.e: an * object using it was created), after the object is fully initialized (so * you can use `this.owner` without risk). */ onCreated(): void {} /** * Return true if the behavior is activated */ activated(): boolean { return this._activated; } /** * Reimplement this method to do extra work when the behavior is activated (after * it has been deactivated, see `onDeActivate`). */ onActivate(): void {} /** * Reimplement this method to do extra work when the behavior is deactivated. */ onDeActivate(): void {} /** * This method is called each tick before events are done. * @param instanceContainer The instanceContainer owning the object */ doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer): void {} /** * This method is called each tick after events are done. * @param instanceContainer The instanceContainer owning the object */ doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer): void {} /** * This method is called when the owner of the behavior * is being removed from the scene and is about to be destroyed/reused later * or when the behavior is removed from an object (can happen in case of * hot-reloading only. Otherwise, behaviors are just de-activated, * not removed. See `onDeActivate`). */ onDestroy(): void {} /** * This method is called when the owner of the behavior * was hot reloaded, so its position, angle, size can have been changed outside * of events. */ onObjectHotReloaded(): void {} /** * Should return `false` if the behavior does not need any lifecycle function to * be called. * Default, hidden, "capability" behaviors set it to `false`. * This avoids useless calls to empty lifecycle functions, which would waste CPU * time (and have a sizeable impact for example when lots of static instances * are living in the scene). * @returns */ usesLifecycleFunction(): boolean { return true; } enableSynchronization(enable: boolean) { this._syncOverNetwork = enable; } isSyncedOverNetwork(): boolean { return this._syncOverNetwork; } } gdjs.registerBehavior('', gdjs.RuntimeBehavior); }