// @ts-check /* * GDevelop JS Platform * Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved. * This project is released under the MIT License. */ /** * @typedef {Object} ObjectData Object containing initial properties for all objects extending {@link gdjs.RuntimeObject}. * @property {string} name The name of the object. During the game, objects can be queried by their name (see {@link gdjs.RuntimeScene.prototype.getObjects} for example). * @property {string} type The object type. * @property {Array} variables The list of default variables. * @property {Array} behaviors The list of default behaviors. */ /** * @typedef {Object} AABB An axis-aligned bounding box. Used to represents a box around an object for example. * @property {Array} min The [x,y] coordinates of the top left point * @property {Array} max The [x,y] coordinates of the bottom right point */ /** * RuntimeObject represents an object being used on a RuntimeScene. * * The constructor can be called on an already existing RuntimeObject: * In this case, the constructor will try to reuse as much already existing members * as possible (recycling). * * However, you should not be calling the constructor on an already existing object * which is not a RuntimeObject. * * A `gdjs.RuntimeObject` should not be instantiated directly, always a child class * (because gdjs.RuntimeObject don't call onCreated at the end of its constructor). * * @memberOf gdjs * @name RuntimeObject * @class * @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to. * @param {ObjectData} objectData The initial properties of the object. */ gdjs.RuntimeObject = function(runtimeScene, objectData) { /** * @protected * @type {string} */ this.name = objectData.name || ""; /** * @protected * @type {string} */ this.type = objectData.type || ""; /** * @protected * @type {number} */ this.x = 0; /** * @protected * @type {number} */ this.y = 0; /** * @protected * @type {number} */ this.angle = 0; /** * @protected * @type {number} */ this.zOrder = 0; /** * @protected * @type {boolean} */ this.hidden = false; /** * @protected * @type {string} */ this.layer = ""; /** * @type {number} * @protected */ this._nameId = gdjs.RuntimeObject.getNameIdentifier(this.name); /** * @type {boolean} * @protected */ this._livingOnScene = true; /** * @type {number} * @readonly */ this.id = runtimeScene.createNewUniqueId(); /** * @type {gdjs.RuntimeScene} */ this._runtimeScene = runtimeScene; /** * An optional UUID associated to the object to be used * for hot reload. Don't modify or use otherwise. * @type {?string} */ this.persistentUuid = null; /** * A property to be used by external algorithms to indicate if the * object is picked or not in an object selection. By construction, this is * not "thread safe" or "re-entrant algorithm" safe. */ this.pick = false; //Hit boxes: if ( this._defaultHitBoxes === undefined ) { /** * @type {Array} * @protected */ this._defaultHitBoxes = []; this._defaultHitBoxes.push(gdjs.Polygon.createRectangle(0,0)); } /** * @type {Array} * @protected */ this.hitBoxes = this._defaultHitBoxes; /** * @type {boolean} * @protected */ this.hitBoxesDirty = true; if ( this.aabb === undefined ) /** * @type {AABB} * @protected */ this.aabb = { min:[0,0], max:[0,0] }; else { this.aabb.min[0] = 0; this.aabb.min[1] = 0; this.aabb.max[0] = 0; this.aabb.max[1] = 0; } //Variables: if ( !this._variables ) /** * @type {gdjs.VariablesContainer} * @protected */ this._variables = new gdjs.VariablesContainer(objectData ? objectData.variables : undefined); else gdjs.VariablesContainer.call(this._variables, objectData ? objectData.variables : undefined); //Forces: if ( this._forces === undefined ) /** * @type {Array} * @protected */ this._forces = []; else this.clearForces(); if (this._averageForce === undefined) this._averageForce = new gdjs.Force(0,0,0); //Behaviors: if (this._behaviors === undefined) /** * Contains the behaviors of the object. * @type {Array} * @protected */ this._behaviors = []; if (this._behaviorsTable === undefined) //TODO: add typing to Hashtable. /** * @type {Hashtable} * @protected */ this._behaviorsTable = new Hashtable(); //Also contains the behaviors: Used when a behavior is accessed by its name ( see getBehavior ). else this._behaviorsTable.clear(); for(var i = 0, len = objectData.behaviors.length;i typing to Hashtable. /** * @type {Hashtable} * @protected */ this._timers = new Hashtable(); else this._timers.clear(); }; /** * Table containing the id corresponding to an object name. Do not use directly or modify. * @static * @private */ gdjs.RuntimeObject._identifiers = gdjs.RuntimeObject._identifiers || new Hashtable(); /** * The next available unique identifier for an object. Do not use directly or modify. * @static * @private */ gdjs.RuntimeObject._newId = (gdjs.RuntimeObject._newId || 0); /** * Global container for unused forces, avoiding recreating forces each tick. * @static * @private */ gdjs.RuntimeObject.forcesGarbage = []; //Common members functions related to the object and its runtimeScene : /** * To be called by the child classes in their constructor, at the very end. * Notify the behaviors that they have been constructed (this must be done when * the object is ready, otherwise behaviors can do operations on the object which * could be not initialized yet). * * If you redefine this function, **make sure to call the original method** * (`gdjs.RuntimeObject.prototype.onCreated.call(this);`). */ gdjs.RuntimeObject.prototype.onCreated = function() { for(var i =0;i * Note that common properties (position, angle, z order...) have already been setup. * * @param initialInstanceData The data of the initial instance. */ gdjs.RuntimeObject.prototype.extraInitializationFromInitialInstance = function(initialInstanceData) { //Nothing to do. }; /** * Called when the object must be updated using the specified objectData. This is the * case during hot-reload, and is only called if the object was modified. * * @param {ObjectData} oldObjectData The previous data for the object. * @param {ObjectData} newObjectData The new data for the object. * @returns {boolean} true if the object was updated, false if it could not (i.e: hot-reload is not supported). */ gdjs.RuntimeObject.prototype.updateFromObjectData = function(oldObjectData, newObjectData) { // If not redefined, mark by default the hot-reload as failed. return false; } /** * Remove an object from a scene. * * Do not change/redefine this method. Instead, redefine the onDestroyFromScene method. * @param {gdjs.RuntimeScene} runtimeScene The RuntimeScene owning the object. */ gdjs.RuntimeObject.prototype.deleteFromScene = function(runtimeScene) { if ( this._livingOnScene ) { runtimeScene.markObjectForDeletion(this); this._livingOnScene = false; } }; /** * Called when the object is destroyed (because it is removed from a scene or the scene * is being unloaded). If you redefine this function, **make sure to call the original method** * (`gdjs.RuntimeObject.prototype.onDestroyFromScene.call(this, runtimeScene);`). * * @param {gdjs.RuntimeScene} runtimeScene The scene owning the object. */ gdjs.RuntimeObject.prototype.onDestroyFromScene = function(runtimeScene) { var theLayer = runtimeScene.getLayer(this.layer); var rendererObject = this.getRendererObject(); if (rendererObject) { theLayer.getRenderer().removeRendererObject(rendererObject); } for(var j = 0, lenj = this._behaviors.length;j * The identifier is set by the runtimeScene owning the object.
* You can also use the id property (this._object.id) for increased efficiency instead of * calling this method. * * @return {number} The object identifier */ gdjs.RuntimeObject.prototype.getUniqueId = function() { return this.id; } ; /** * Set the position of the object. * * @param {number} x The new X position * @param {number} y The new Y position */ gdjs.RuntimeObject.prototype.setPosition = function(x,y) { this.setX(x); this.setY(y); }; /** * Set the X position of the object. * * @param {number} x The new X position */ gdjs.RuntimeObject.prototype.setX = function(x) { if ( x === this.x ) return; this.x = x; this.hitBoxesDirty = true; }; /** * Get the X position of the object. * * @return {number} The X position of the object */ gdjs.RuntimeObject.prototype.getX = function() { return this.x; }; /** * Set the Y position of the object. * * @param {number} y The new Y position */ gdjs.RuntimeObject.prototype.setY = function(y) { if ( y === this.y ) return; this.y = y; this.hitBoxesDirty = true; }; /** * Get the Y position of the object. * * @return {number} The Y position of the object */ gdjs.RuntimeObject.prototype.getY = function() { return this.y; }; /** * Get the X position of the rendered object. * * For most objects, this will returns the same value as getX(). But if the object * has an origin that is not the same as the point (0,0) of the object displayed, * getDrawableX will differ. * * @return {number} The X position of the rendered object. */ gdjs.RuntimeObject.prototype.getDrawableX = function() { return this.getX(); }; /** * Get the Y position of the rendered object. * * For most objects, this will returns the same value as getY(). But if the object * has an origin that is not the same as the point (0,0) of the object displayed, * getDrawableY will differ. * * @return {number} The Y position of the rendered object. */ gdjs.RuntimeObject.prototype.getDrawableY = function() { return this.getY(); }; gdjs.RuntimeObject.prototype.rotateTowardPosition = function(x, y, speed, scene) { this.rotateTowardAngle(Math.atan2(y - (this.getDrawableY() + this.getCenterY()), x - (this.getDrawableX() + this.getCenterX()))*180/Math.PI, speed, scene); }; gdjs.RuntimeObject.prototype.rotateTowardAngle = function(angle, speed, runtimeScene) { if (speed === 0) { this.setAngle(angle); return; } var angularDiff = gdjs.evtTools.common.angleDifference(this.getAngle(), angle); var diffWasPositive = angularDiff >= 0; var newAngle = this.getAngle() + (diffWasPositive ? -1.0 : 1.0) * speed * this.getElapsedTime(runtimeScene) / 1000; // @ts-ignore if (gdjs.evtTools.common.angleDifference(newAngle, angle) > 0 ^ diffWasPositive) newAngle = angle; this.setAngle(newAngle); if (this.getAngle() !== newAngle) //Objects like sprite in 8 directions does not handle small increments... this.setAngle(angle); //...so force them to be in the path angle anyway. }; /** * Rotate the object at the given speed * * @param {number} speed The speed, in degrees per second. * @param {gdjs.RuntimeScene} runtimeScene The scene where the object is displayed. */ gdjs.RuntimeObject.prototype.rotate = function(speed, runtimeScene) { this.setAngle(this.getAngle() + speed * this.getElapsedTime(runtimeScene) / 1000); }; /** * Set the angle of the object. * * @param {number} angle The new angle of the object */ gdjs.RuntimeObject.prototype.setAngle = function(angle) { if ( this.angle === angle ) return; this.angle = angle; this.hitBoxesDirty = true; }; /** * Get the rotation of the object. * * @return {number} The rotation of the object, in degrees. */ gdjs.RuntimeObject.prototype.getAngle = function() { return this.angle; }; /** * Set the layer of the object. * * @param {string} layer The new layer of the object */ gdjs.RuntimeObject.prototype.setLayer = function(layer) { if (layer === this.layer) return; var oldLayer = this._runtimeScene.getLayer(this.layer); this.layer = layer; var newLayer = this._runtimeScene.getLayer(this.layer); var rendererObject = this.getRendererObject(); if (rendererObject) { oldLayer.getRenderer().removeRendererObject(rendererObject); newLayer.getRenderer().addRendererObject(rendererObject, this.zOrder); } }; /** * Get the layer of the object. * * @return {string} The layer of the object */ gdjs.RuntimeObject.prototype.getLayer = function() { return this.layer; }; /** * Return true if the object is on the specified layer * * @param {string} layer The layer to be tested. * @return {boolean} true if the object is on the specified layer */ gdjs.RuntimeObject.prototype.isOnLayer = function(layer) { return this.layer === layer; }; /** * Set the Z order of the object. * * @param {number} z The new Z order position of the object */ gdjs.RuntimeObject.prototype.setZOrder = function(z) { if ( z === this.zOrder ) return; this.zOrder = z; if ( this.getRendererObject() ) { var theLayer = this._runtimeScene.getLayer(this.layer); theLayer.getRenderer().changeRendererObjectZOrder(this.getRendererObject(), z); } }; /** * Get the Z order of the object. * * @return {number} The Z order of the object */ gdjs.RuntimeObject.prototype.getZOrder = function() { return this.zOrder; }; /** * Get the container of the object variables * @return {gdjs.VariablesContainer} The variables of the object */ gdjs.RuntimeObject.prototype.getVariables = function() { return this._variables; }; /** * Get the value of a variable considered as a number. Equivalent of variable.getAsNumber() * @param {gdjs.Variable} variable The variable to be accessed * @return {number} The value of the specified variable * @static */ gdjs.RuntimeObject.getVariableNumber = function(variable) { return variable.getAsNumber(); }; gdjs.RuntimeObject.prototype.getVariableNumber = gdjs.RuntimeObject.getVariableNumber; /** * Return the variable passed as argument without any change. * Only for usage by events. * * @param {gdjs.Variable} variable The variable to be accessed * @return The specified variable * @static */ gdjs.RuntimeObject.returnVariable = function(variable) { return variable; } gdjs.RuntimeObject.prototype.returnVariable = gdjs.RuntimeObject.returnVariable; /** * Get the value of a variable considered as a string. Equivalent of variable.getAsString() * @param variable The variable to be accessed * @return The string of the specified variable * @static */ gdjs.RuntimeObject.getVariableString = function(variable) { return variable.getAsString(); }; gdjs.RuntimeObject.prototype.getVariableString = gdjs.RuntimeObject.getVariableString; /** * Get the number of children from a variable * @param variable The variable to be accessed * @return The number of children * @static */ gdjs.RuntimeObject.getVariableChildCount = function(variable) { if (variable.isStructure() == false) return 0; return Object.keys(variable.getAllChildren()).length; }; /** * Shortcut to set the value of a variable considered as a number * @param variable The variable to be changed * @param {number} newValue The value to be set */ gdjs.RuntimeObject.setVariableNumber = function(variable, newValue) { variable.setNumber(newValue); }; gdjs.RuntimeObject.prototype.setVariableNumber = gdjs.RuntimeObject.setVariableNumber; /** * Shortcut to set the value of a variable considered as a string * @param variable The variable to be changed * @param newValue {String} The value to be set */ gdjs.RuntimeObject.setVariableString = function(variable, newValue) { variable.setString(newValue); }; gdjs.RuntimeObject.prototype.setVariableString = gdjs.RuntimeObject.setVariableString; /** * @static * @private * @param {gdjs.Variable} variable The variable to be tested * @param {string} childName The name of the child */ gdjs.RuntimeObject.variableChildExists = function(variable, childName) { return variable.hasChild(childName); }; gdjs.RuntimeObject.prototype.variableChildExists = gdjs.RuntimeObject.variableChildExists; /** * @static * @private * @param {gdjs.Variable} variable The variable to be changed * @param {string} childName The name of the child */ gdjs.RuntimeObject.variableRemoveChild = function(variable, childName) { return variable.removeChild(childName); }; gdjs.RuntimeObject.prototype.variableRemoveChild = gdjs.RuntimeObject.variableRemoveChild; /** * @static * @private * @param {gdjs.Variable} variable The variable to be cleared */ gdjs.RuntimeObject.variableClearChildren = function(variable) { variable.clearChildren(); }; gdjs.RuntimeObject.prototype.variableClearChildren = gdjs.RuntimeObject.variableClearChildren; /** * Shortcut to test if a variable exists for the object. * @param {string} name The variable to be tested * @return {boolean} true if the variable exists. */ gdjs.RuntimeObject.prototype.hasVariable = function(name) { return this._variables.has(name); }; /** * Hide (or show) the object. * @param {boolean} enable Set it to true to hide the object, false to show it. */ gdjs.RuntimeObject.prototype.hide = function(enable) { if (enable === undefined) enable = true; this.hidden = enable; }; /** * Return true if the object is not hidden. * * Note: This is unrelated to the actual visibility of the object on the screen. * For this, see `getVisibilityAABB` to get the bounding boxes of the object as displayed * on the scene. * * @return {boolean} true if the object is not hidden. */ gdjs.RuntimeObject.prototype.isVisible = function() { return !this.hidden; }; /** * Return true if the object is hidden. * @return {boolean} true if the object is hidden. */ gdjs.RuntimeObject.prototype.isHidden = function() { return this.hidden; }; /** * Set the width of the object, if applicable. * @param {number} width The new width in pixels. */ gdjs.RuntimeObject.prototype.setWidth = function(width) {}; /** * Set the height of the object, if applicable. * @param {number} height The new height in pixels. */ gdjs.RuntimeObject.prototype.setHeight = function(height) {}; /** * Return the width of the object. * @return {number} The width of the object */ gdjs.RuntimeObject.prototype.getWidth = function() { return 0; }; /** * Return the width of the object. * @return {number} The height of the object */ gdjs.RuntimeObject.prototype.getHeight = function() { return 0; }; /** * Return the X position of the object center, **relative to the object X position** (`getDrawableX`). * @return {number} the X position of the object center, relative to `getDrawableX()`. */ gdjs.RuntimeObject.prototype.getCenterX = function() { return this.getWidth() / 2; }; /** * Return the Y position of the object center, **relative to the object position** (`getDrawableY`). * @return {number} the Y position of the object center, relative to `getDrawableY()`. */ gdjs.RuntimeObject.prototype.getCenterY = function() { return this.getHeight() / 2; }; //Forces : /** * Get a force from the garbage, or create a new force is garbage is empty.
* To be used each time a force is created so as to avoid temporaries objects. * * @private * @param {number} x The x coordinates of the force * @param {number} y The y coordinates of the force * @param {number} multiplier Set the force multiplier */ gdjs.RuntimeObject.prototype._getRecycledForce = function(x, y, multiplier) { if ( gdjs.RuntimeObject.forcesGarbage.length === 0 ) return new gdjs.Force(x, y, multiplier); else { var recycledForce = gdjs.RuntimeObject.forcesGarbage.pop(); recycledForce.setX(x); recycledForce.setY(y); recycledForce.setMultiplier(multiplier); return recycledForce; } }; /** * Add a force to the object to move it. * @param {number} x The x coordinates of the force * @param {number} y The y coordinates of the force * @param {number} multiplier Set the force multiplier */ gdjs.RuntimeObject.prototype.addForce = function(x,y, multiplier) { this._forces.push(this._getRecycledForce(x, y, multiplier)); }; /** * Add a force using polar coordinates. * @param {number} angle The angle of the force, in degrees. * @param {number} len The length of the force, in pixels. * @param {number} multiplier Set the force multiplier */ gdjs.RuntimeObject.prototype.addPolarForce = function(angle, len, multiplier) { var angleInRadians = angle/180*3.14159; //TODO: Benchmark with Math.PI var forceX = Math.cos(angleInRadians)*len; var forceY = Math.sin(angleInRadians)*len; this._forces.push(this._getRecycledForce(forceX, forceY, multiplier)); }; /** * Add a force oriented toward a position * @param {number} x The target x position * @param {number} y The target y position * @param {number} len The force length, in pixels. * @param {number} multiplier Set the force multiplier */ gdjs.RuntimeObject.prototype.addForceTowardPosition = function(x,y, len, multiplier) { var angle = Math.atan2(y - (this.getDrawableY()+this.getCenterY()), x - (this.getDrawableX()+this.getCenterX())); var forceX = Math.cos(angle)*len; var forceY = Math.sin(angle)*len; this._forces.push(this._getRecycledForce(forceX, forceY, multiplier)); }; /** * Add a force oriented toward another object.
* (Shortcut for addForceTowardPosition) * @param {gdjs.RuntimeObject} object The target object * @param {number} len The force length, in pixels. * @param {number} multiplier Set the force multiplier */ gdjs.RuntimeObject.prototype.addForceTowardObject = function(object, len, multiplier) { if ( object == null ) return; this.addForceTowardPosition(object.getDrawableX() + object.getCenterX(), object.getDrawableY() + object.getCenterY(), len, multiplier); }; /** * Deletes all forces applied on the object */ gdjs.RuntimeObject.prototype.clearForces = function() { gdjs.RuntimeObject.forcesGarbage.push.apply(gdjs.RuntimeObject.forcesGarbage, this._forces); this._forces.length = 0; }; /** * Return true if no forces are applied on the object. * @return {boolean} true if no forces are applied on the object. */ gdjs.RuntimeObject.prototype.hasNoForces = function() { return this._forces.length === 0; }; /** * Called once a step by runtimeScene to update forces magnitudes and * remove null ones. */ gdjs.RuntimeObject.prototype.updateForces = function(elapsedTime) { for(var i = 0;i * The default implementation returns a basic bouding box based the size (getWidth and * getHeight) and the center point of the object (getCenterX and getCenterY). * * You should probably redefine updateHitBoxes instead of this function. * * @return {Array} An array composed of polygon. */ gdjs.RuntimeObject.prototype.getHitBoxes = function() { //Avoid a naive implementation requiring to recreate temporaries each time //the function is called: //(var rectangle = gdjs.Polygon.createRectangle(this.getWidth(), this.getHeight()); //...) if ( this.hitBoxesDirty ) { this.updateHitBoxes(); this.updateAABB(); this.hitBoxesDirty = false; } return this.hitBoxes; }; /** * Update the hit boxes for the object. * * The default implementation set a basic bounding box based on the size (getWidth and * getHeight) and the center point of the object (getCenterX and getCenterY). * Result is cached until invalidated (by a position change, angle change...). * * You should not call this function by yourself, it is called when necessary by getHitBoxes method. * However, you can redefine it if your object need custom hit boxes. */ gdjs.RuntimeObject.prototype.updateHitBoxes = function() { this.hitBoxes = this._defaultHitBoxes; var width = this.getWidth(); var height = this.getHeight(); var centerX = this.getCenterX(); var centerY = this.getCenterY(); if (centerX === width / 2 && centerY === height / 2) { this.hitBoxes[0].vertices[0][0] = - centerX; this.hitBoxes[0].vertices[0][1] = - centerY; this.hitBoxes[0].vertices[1][0] = + centerX; this.hitBoxes[0].vertices[1][1] = - centerY; this.hitBoxes[0].vertices[2][0] = + centerX; this.hitBoxes[0].vertices[2][1] = + centerY; this.hitBoxes[0].vertices[3][0] = - centerX; this.hitBoxes[0].vertices[3][1] = + centerY; } else { this.hitBoxes[0].vertices[0][0] = 0 - centerX; this.hitBoxes[0].vertices[0][1] = 0 - centerY; this.hitBoxes[0].vertices[1][0] = width - centerX; this.hitBoxes[0].vertices[1][1] = 0 - centerY; this.hitBoxes[0].vertices[2][0] = width - centerX; this.hitBoxes[0].vertices[2][1] = height - centerY; this.hitBoxes[0].vertices[3][0] = 0 - centerX; this.hitBoxes[0].vertices[3][1] = height - centerY; } this.hitBoxes[0].rotate(this.getAngle()/180*Math.PI); this.hitBoxes[0].move(this.getDrawableX()+centerX, this.getDrawableY()+centerY); }; /** * Get the AABB (axis aligned bounding box) for the object. * * The default implementation uses either the position/size of the object (when angle is 0) or * hitboxes (when angle is not 0) to compute the bounding box. * Result is cached until invalidated (by a position change, angle change...). * * You should probably redefine updateAABB instead of this function. * * @return {AABB} The bounding box (example: `{min: [10,5], max:[20,10]}`) */ gdjs.RuntimeObject.prototype.getAABB = function() { if ( this.hitBoxesDirty ) { this.updateHitBoxes(); this.updateAABB(); this.hitBoxesDirty = false; } return this.aabb; }; /** * Get the AABB (axis aligned bounding box) to be used to determine if the object * is visible on screen. The gdjs.RuntimeScene will hide the renderer object if * the object is not visible on screen ("culling"). * * The default implementation uses the AABB returned by getAABB. * * If `null` is returned, the object is assumed to be always visible. * * @return {?AABB} The bounding box (example: `{min: [10,5], max:[20,10]}`) or `null`. */ gdjs.RuntimeObject.prototype.getVisibilityAABB = function() { return this.getAABB(); }; /** * Update the AABB (axis aligned bounding box) for the object. * * Default implementation uses either the position/size of the object (when angle is 0) or * hitboxes (when angle is not 0) to compute the bounding box. * * You should not call this function by yourself, it is called when necessary by getAABB method. * However, you can redefine it if your object can have a faster implementation. */ gdjs.RuntimeObject.prototype.updateAABB = function() { if (this.getAngle() === 0) { // Fast/simple computation of AABB for non rotated object // (works even for object with non default center/origin // because we're using getDrawableX/Y) this.aabb.min[0] = this.getDrawableX(); this.aabb.min[1] = this.getDrawableY(); this.aabb.max[0] = this.aabb.min[0] + this.getWidth(); this.aabb.max[1] = this.aabb.min[1] + this.getHeight(); } else { // Use hitboxes if object is rotated to ensure that the AABB // is properly bounding the whole object. // Slower (10-15% slower). var first = true; for(var i = 0;i= timeInSeconds; }; /** * Test a if a timer is paused * @param {String} timerName The timer name * @return {boolean} True if the timer exists and is paused, false otherwise */ gdjs.RuntimeObject.prototype.timerPaused = function(timerName) { if ( !this._timers.containsKey(timerName) ) { return false; } return this._timers.get(timerName).isPaused(); }; /** * Reset a timer, if the timer doesn't exist it is created * @param {String} timerName The timer name */ gdjs.RuntimeObject.prototype.resetTimer = function(timerName) { if ( !this._timers.containsKey(timerName) ) { this._timers.put(timerName, new gdjs.Timer(timerName)); } this._timers.get(timerName).reset(); }; /** * Pause a timer, if the timer doesn't exist it is created * @param {String} timerName The timer name */ gdjs.RuntimeObject.prototype.pauseTimer = function(timerName) { if ( !this._timers.containsKey(timerName) ) { this._timers.put(timerName, new gdjs.Timer(timerName)); } this._timers.get(timerName).setPaused(true); }; /** * Unpause a timer, if the timer doesn't exist it is created * @param {String} timerName The timer name */ gdjs.RuntimeObject.prototype.unpauseTimer = function(timerName) { if ( !this._timers.containsKey(timerName) ) { this._timers.put(timerName, new gdjs.Timer(timerName)); } this._timers.get(timerName).setPaused(false); }; /** * Remove a timer * @param {String} timerName The timer name */ gdjs.RuntimeObject.prototype.removeTimer = function(timerName) { if ( this._timers.containsKey(timerName) ) { this._timers.remove(timerName); } }; /** * Get a timer elapsed time. * @param {String} timerName The timer name * @return {number} The timer elapsed time in seconds, 0 if the timer doesn't exist */ gdjs.RuntimeObject.prototype.getTimerElapsedTimeInSeconds = function(timerName) { if ( !this._timers.containsKey(timerName) ) { return 0; } return this._timers.get(timerName).getTime() / 1000.0; }; //Other : /** * Separate the object from others objects, using their hitboxes. * @param objects Objects * @param {boolean | undefined} ignoreTouchingEdges If true, then edges that are touching each other, without the hitbox polygons actually overlapping, won't be considered in collision. * @return true if the object was moved */ gdjs.RuntimeObject.prototype.separateFromObjects = function(objects, ignoreTouchingEdges) { var moved = false; var xMove = 0; var yMove = 0; var hitBoxes = this.getHitBoxes(); //Check if their is a collision with each object for(var i = 0, len = objects.length;i objects[i].getDrawableX()+objects[i].getWidth() ){ this.setX( objects[i].getDrawableX()+objects[i].getWidth() ); } if ( this.getDrawableY() < objects[i].getDrawableY() ){ this.setY( objects[i].getDrawableY() - this.getHeight() ); } else if ( this.getDrawableY()+this.getHeight() > objects[i].getDrawableY()+objects[i].getHeight() ){ this.setY( objects[i].getDrawableY()+objects[i].getHeight() ); } } } }; /** * @deprecated * @param objectsLists Tables of objects */ gdjs.RuntimeObject.prototype.separateObjectsWithForces = function(objectsLists) { //Prepare the list of objects to iterate over. var objects = gdjs.staticArray(gdjs.RuntimeObject.prototype.separateObjectsWithForces); objects.length = 0; var lists = gdjs.staticArray2(gdjs.RuntimeObject.prototype.separateObjectsWithForces); objectsLists.values(lists); for(var i = 0, len = lists.length;i obj1BoundingRadius + obj2BoundingRadius ) return false; //Do a real check if necessary. var hitBoxes1 = obj1.getHitBoxes(); var hitBoxes2 = obj2.getHitBoxes(); for(var k = 0, lenBoxes1 = hitBoxes1.length;k sqBoundingR + sqDist + 2*Math.sqrt(sqDist*sqBoundingR) ) return result; var testSqDist = closest ? sqDist : 0; var hitBoxes = this.getHitBoxes(); for (var i=0; i testSqDist) && (res.farSqDist <= sqDist) ) { testSqDist = res.farSqDist; result = res; } } } return result; }; /** * Return true if the specified position is inside object bounding box. * * The position should be in "world" coordinates, i.e use gdjs.Layer.convertCoords * if you need to pass the mouse or a touch position that you get from gdjs.InputManager. * */ gdjs.RuntimeObject.prototype.insideObject = function(x, y) { if ( this.hitBoxesDirty ) { this.updateHitBoxes(); this.updateAABB(); this.hitBoxesDirty = false; } return this.aabb.min[0] <= x && this.aabb.max[0] >= x && this.aabb.min[1] <= y && this.aabb.max[1] >= y; }; /** * Check the distance between two objects. * @static */ gdjs.RuntimeObject.distanceTest = function(obj1, obj2, distance) { return obj1.getSqDistanceToObject(obj2) <= distance; }; /** * Return true if the cursor, or any touch, is on the object. * * @return true if the cursor, or any touch, is on the object. */ gdjs.RuntimeObject.prototype.cursorOnObject = function(runtimeScene) { var inputManager = runtimeScene.getGame().getInputManager(); var layer = runtimeScene.getLayer(this.layer); var mousePos = layer.convertCoords(inputManager.getMouseX(), inputManager.getMouseY()); if (this.insideObject(mousePos[0], mousePos[1])) { return true; } var touchIds = inputManager.getAllTouchIdentifiers(); for(var i = 0;i