First commit for "Laureati Invaders", as downloaded from website
After Width: | Height: | Size: 81 B |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 69 KiB |
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
GDevelop - DestroyOutside Behavior Extension
|
||||
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* The destroyOutsideRuntimeBehavior represents a behavior allowing objects to be
|
||||
* moved using the mouse.
|
||||
*
|
||||
* @class DestroyOutsideRuntimeBehavior
|
||||
* @constructor
|
||||
*/
|
||||
gdjs.DestroyOutsideRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
{
|
||||
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
||||
|
||||
this._extraBorder = behaviorData.extraBorder || 0;
|
||||
};
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
||||
gdjs.registerBehavior("DestroyOutsideBehavior::DestroyOutside", gdjs.DestroyOutsideRuntimeBehavior);
|
||||
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
// TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point
|
||||
// is not necessarily in the middle of the object (for sprites for example).
|
||||
var ow = this.owner.getWidth();
|
||||
var oh = this.owner.getHeight();
|
||||
var ocx = this.owner.getDrawableX()+this.owner.getCenterX();
|
||||
var ocy = this.owner.getDrawableY()+this.owner.getCenterY();
|
||||
var layer = runtimeScene.getLayer(this.owner.getLayer());
|
||||
|
||||
var boundingCircleRadius = Math.sqrt(ow*ow+oh*oh)/2.0;
|
||||
if ( ocx+boundingCircleRadius+this._extraBorder < layer.getCameraX()-layer.getCameraWidth()/2
|
||||
|| ocx-boundingCircleRadius-this._extraBorder > layer.getCameraX()+layer.getCameraWidth()/2
|
||||
|| ocy+boundingCircleRadius+this._extraBorder < layer.getCameraY()-layer.getCameraHeight()/2
|
||||
|| ocy-boundingCircleRadius-this._extraBorder > layer.getCameraY()+layer.getCameraHeight()/2 ) {
|
||||
//We are outside the camera area.
|
||||
this.owner.deleteFromScene(runtimeScene);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an additional border to the camera viewport as a buffer before the object gets destroyed.
|
||||
* @param {number} val Border in pixels.
|
||||
*/
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.setExtraBorder = function(val) {
|
||||
this._extraBorder = val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the additional border of the camera viewport buffer which triggers the destruction of an object.
|
||||
* @return {number} The additional border around the camera viewport in pixels
|
||||
*/
|
||||
gdjs.DestroyOutsideRuntimeBehavior.prototype.getExtraBorder = function() {
|
||||
return this._extraBorder;
|
||||
};
|
|
@ -0,0 +1,343 @@
|
|||
gdjs.PanelSpriteRuntimeObjectPixiRenderer = function(
|
||||
runtimeObject,
|
||||
runtimeScene,
|
||||
textureName,
|
||||
tiled
|
||||
) {
|
||||
this._object = runtimeObject;
|
||||
|
||||
if (this._spritesContainer === undefined) {
|
||||
var texture = runtimeScene
|
||||
.getGame()
|
||||
.getImageManager()
|
||||
.getPIXITexture(textureName);
|
||||
|
||||
var StretchedSprite = !tiled ? PIXI.Sprite : PIXI.extras.TilingSprite;
|
||||
|
||||
this._spritesContainer = new PIXI.Container();
|
||||
this._centerSprite = new StretchedSprite(new PIXI.Texture(texture));
|
||||
this._borderSprites = [
|
||||
new StretchedSprite(new PIXI.Texture(texture)), //Right
|
||||
new PIXI.Sprite(texture), //Top-Right
|
||||
new StretchedSprite(new PIXI.Texture(texture)), //Top
|
||||
new PIXI.Sprite(texture), //Top-Left
|
||||
new StretchedSprite(new PIXI.Texture(texture)), //Left
|
||||
new PIXI.Sprite(texture), //Bottom-Left
|
||||
new StretchedSprite(new PIXI.Texture(texture)), //Bottom
|
||||
new PIXI.Sprite(texture), //Bottom-Right
|
||||
];
|
||||
}
|
||||
|
||||
this.setTexture(textureName, runtimeScene);
|
||||
|
||||
this._spritesContainer.removeChildren();
|
||||
this._spritesContainer.addChild(this._centerSprite);
|
||||
for (var i = 0; i < this._borderSprites.length; ++i) {
|
||||
this._spritesContainer.addChild(this._borderSprites[i]);
|
||||
}
|
||||
|
||||
this._alpha = this._spritesContainer.alpha;
|
||||
runtimeScene
|
||||
.getLayer('')
|
||||
.getRenderer()
|
||||
.addRendererObject(this._spritesContainer, runtimeObject.getZOrder());
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectRenderer =
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.getRendererObject = function() {
|
||||
return this._spritesContainer;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
if (this._spritesContainer.visible && this._wasRendered) {
|
||||
// Update the alpha of the container to make sure that it's applied.
|
||||
// If not done, the alpha will be back to full opaque when changing the color
|
||||
// of the object.
|
||||
this._spritesContainer.alpha = this._alpha;
|
||||
this._spritesContainer.cacheAsBitmap = true;
|
||||
}
|
||||
|
||||
this._wasRendered = true;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.updateOpacity = function() {
|
||||
//TODO: Workaround a not working property in PIXI.js:
|
||||
this._spritesContainer.alpha = this._spritesContainer.visible
|
||||
? this._object.opacity / 255
|
||||
: 0;
|
||||
this._alpha = this._spritesContainer.alpha;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.updateAngle = function() {
|
||||
this._spritesContainer.rotation = gdjs.toRad(this._object.angle);
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
this._spritesContainer.position.x = this._object.x + this._object._width / 2;
|
||||
this._spritesContainer.position.y = this._object.y + this._object._height / 2;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype._updateLocalPositions = function() {
|
||||
var obj = this._object;
|
||||
|
||||
this._centerSprite.position.x = obj._lBorder;
|
||||
this._centerSprite.position.y = obj._tBorder;
|
||||
|
||||
//Right
|
||||
this._borderSprites[0].position.x = obj._width - obj._rBorder;
|
||||
this._borderSprites[0].position.y = obj._tBorder;
|
||||
|
||||
//Top-right
|
||||
this._borderSprites[1].position.x = obj._width - this._borderSprites[1].width;
|
||||
this._borderSprites[1].position.y = 0;
|
||||
|
||||
//Top
|
||||
this._borderSprites[2].position.x = obj._lBorder;
|
||||
this._borderSprites[2].position.y = 0;
|
||||
//Top-Left
|
||||
this._borderSprites[3].position.x = 0;
|
||||
this._borderSprites[3].position.y = 0;
|
||||
//Left
|
||||
this._borderSprites[4].position.x = 0;
|
||||
this._borderSprites[4].position.y = obj._tBorder;
|
||||
//Bottom-Left
|
||||
this._borderSprites[5].position.x = 0;
|
||||
this._borderSprites[5].position.y =
|
||||
obj._height - this._borderSprites[5].height;
|
||||
//Bottom
|
||||
this._borderSprites[6].position.x = obj._lBorder;
|
||||
this._borderSprites[6].position.y = obj._height - obj._bBorder;
|
||||
//Bottom-Right
|
||||
this._borderSprites[7].position.x = obj._width - this._borderSprites[7].width;
|
||||
this._borderSprites[7].position.y =
|
||||
obj._height - this._borderSprites[7].height;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype._updateSpritesAndTexturesSize = function() {
|
||||
var obj = this._object;
|
||||
|
||||
this._centerSprite.width = Math.max(
|
||||
obj._width - obj._rBorder - obj._lBorder,
|
||||
0
|
||||
);
|
||||
this._centerSprite.height = Math.max(
|
||||
obj._height - obj._tBorder - obj._bBorder,
|
||||
0
|
||||
);
|
||||
|
||||
//Right
|
||||
this._borderSprites[0].width = obj._rBorder;
|
||||
this._borderSprites[0].height = Math.max(
|
||||
obj._height - obj._tBorder - obj._bBorder,
|
||||
0
|
||||
);
|
||||
|
||||
//Top
|
||||
this._borderSprites[2].height = obj._tBorder;
|
||||
this._borderSprites[2].width = Math.max(
|
||||
obj._width - obj._rBorder - obj._lBorder,
|
||||
0
|
||||
);
|
||||
//Left
|
||||
this._borderSprites[4].width = obj._lBorder;
|
||||
this._borderSprites[4].height = Math.max(
|
||||
obj._height - obj._tBorder - obj._bBorder,
|
||||
0
|
||||
);
|
||||
//Bottom
|
||||
this._borderSprites[6].height = obj._bBorder;
|
||||
this._borderSprites[6].width = Math.max(
|
||||
obj._width - obj._rBorder - obj._lBorder,
|
||||
0
|
||||
);
|
||||
|
||||
this._wasRendered = true;
|
||||
this._spritesContainer.cacheAsBitmap = false;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.setTexture = function(
|
||||
textureName,
|
||||
runtimeScene
|
||||
) {
|
||||
var obj = this._object;
|
||||
var texture = runtimeScene
|
||||
.getGame()
|
||||
.getImageManager()
|
||||
.getPIXITexture(textureName);
|
||||
|
||||
function makeInsideTexture(rect) {
|
||||
//TODO
|
||||
if (rect.width < 0) rect.width = 0;
|
||||
if (rect.height < 0) rect.height = 0;
|
||||
if (rect.x < 0) rect.x = 0;
|
||||
if (rect.y < 0) rect.y = 0;
|
||||
if (rect.x > texture.width) rect.x = texture.width;
|
||||
if (rect.y > texture.height) rect.y = texture.height;
|
||||
if (rect.x + rect.width > texture.width)
|
||||
rect.width = texture.width - rect.x;
|
||||
if (rect.y + rect.height > texture.height)
|
||||
rect.height = texture.height - rect.y;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
this._centerSprite.texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
obj._lBorder,
|
||||
obj._tBorder,
|
||||
texture.width - obj._lBorder - obj._rBorder,
|
||||
texture.height - obj._tBorder - obj._bBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
//Top, Bottom, Right, Left borders:
|
||||
this._borderSprites[0].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
texture.width - obj._rBorder,
|
||||
obj._tBorder,
|
||||
obj._rBorder,
|
||||
texture.height - obj._tBorder - obj._bBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[2].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
obj._lBorder,
|
||||
0,
|
||||
texture.width - obj._lBorder - obj._rBorder,
|
||||
obj._tBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[4].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
0,
|
||||
obj._tBorder,
|
||||
obj._lBorder,
|
||||
texture.height - obj._tBorder - obj._bBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[6].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
obj._lBorder,
|
||||
texture.height - obj._bBorder,
|
||||
texture.width - obj._lBorder - obj._rBorder,
|
||||
obj._bBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
this._borderSprites[1].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
this._borderSprites[1].width - obj._rBorder,
|
||||
0,
|
||||
obj._rBorder,
|
||||
obj._tBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[3].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(new PIXI.Rectangle(0, 0, obj._lBorder, obj._tBorder))
|
||||
);
|
||||
this._borderSprites[5].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
0,
|
||||
this._borderSprites[5].height - obj._bBorder,
|
||||
obj._lBorder,
|
||||
obj._bBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
this._borderSprites[7].texture = new PIXI.Texture(
|
||||
texture,
|
||||
makeInsideTexture(
|
||||
new PIXI.Rectangle(
|
||||
this._borderSprites[7].width - obj._rBorder,
|
||||
this._borderSprites[7].height - obj._bBorder,
|
||||
obj._rBorder,
|
||||
obj._bBorder
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
this._updateSpritesAndTexturesSize();
|
||||
this._updateLocalPositions();
|
||||
this.updatePosition();
|
||||
this._spritesContainer.pivot.x = this._object._width / 2;
|
||||
this._spritesContainer.pivot.y = this._object._height / 2;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.updateWidth = function() {
|
||||
this._spritesContainer.pivot.x = this._object._width / 2;
|
||||
this._updateSpritesAndTexturesSize();
|
||||
this._updateLocalPositions();
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.updateHeight = function() {
|
||||
this._spritesContainer.pivot.y = this._object._height / 2;
|
||||
this._updateSpritesAndTexturesSize();
|
||||
this._updateLocalPositions();
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.setColor = function(
|
||||
rgbColor
|
||||
) {
|
||||
var colors = rgbColor.split(';');
|
||||
if (colors.length < 3) return;
|
||||
|
||||
this._centerSprite.tint =
|
||||
'0x' +
|
||||
gdjs.rgbToHex(
|
||||
parseInt(colors[0], 10),
|
||||
parseInt(colors[1], 10),
|
||||
parseInt(colors[2], 10)
|
||||
);
|
||||
|
||||
for (
|
||||
var borderCounter = 0;
|
||||
borderCounter < this._borderSprites.length;
|
||||
borderCounter++
|
||||
) {
|
||||
this._borderSprites[borderCounter].tint =
|
||||
'0x' +
|
||||
gdjs.rgbToHex(
|
||||
parseInt(colors[0], 10),
|
||||
parseInt(colors[1], 10),
|
||||
parseInt(colors[2], 10)
|
||||
);
|
||||
}
|
||||
|
||||
this._spritesContainer.cacheAsBitmap = false;
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObjectPixiRenderer.prototype.getColor = function() {
|
||||
var rgb = PIXI.utils.hex2rgb(this._centerSprite.tint);
|
||||
return (
|
||||
Math.floor(rgb[0] * 255) +
|
||||
';' +
|
||||
Math.floor(rgb[1] * 255) +
|
||||
';' +
|
||||
Math.floor(rgb[2] * 255)
|
||||
);
|
||||
};
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* 2013 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PanelSpriteObjectDataType
|
||||
* @property {number} rightMargin The right margin
|
||||
* @property {number} leftMargin The left margin
|
||||
* @property {number} topMargin The top margin
|
||||
* @property {number} bottomMargin The bottom margin
|
||||
* @property {boolean} [tiled] Are the central part and borders tiled?
|
||||
* @property {number} width The object width
|
||||
* @property {number} height The object height
|
||||
* @property {string} texture The name of the resource containing the texture to use
|
||||
*
|
||||
* @typedef {ObjectData & PanelSpriteObjectDataType} PanelSpriteObjectData
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PanelSpriteRuntimeObject displays a tiled texture.
|
||||
*
|
||||
* @class PanelSpriteRuntimeObject
|
||||
* @extends RuntimeObject
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to
|
||||
* @param {PanelSpriteObjectData} panelSpriteObjectData The initial properties of the object
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject = function(runtimeScene, panelSpriteObjectData) {
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, panelSpriteObjectData);
|
||||
|
||||
/** @type {number} */
|
||||
this._rBorder = panelSpriteObjectData.rightMargin;
|
||||
|
||||
/** @type {number} */
|
||||
this._lBorder = panelSpriteObjectData.leftMargin;
|
||||
|
||||
/** @type {number} */
|
||||
this._tBorder = panelSpriteObjectData.topMargin;
|
||||
|
||||
/** @type {number} */
|
||||
this._bBorder = panelSpriteObjectData.bottomMargin;
|
||||
|
||||
/** @type {boolean} */
|
||||
this._tiled = panelSpriteObjectData.tiled;
|
||||
|
||||
/** @type {number} */
|
||||
this._width = panelSpriteObjectData.width;
|
||||
|
||||
/** @type {number} */
|
||||
this._height = panelSpriteObjectData.height;
|
||||
|
||||
/** @type {number} */
|
||||
this.opacity = 255;
|
||||
|
||||
if (this._renderer) {
|
||||
gdjs.PanelSpriteRuntimeObjectRenderer.call(
|
||||
this._renderer,
|
||||
this,
|
||||
runtimeScene,
|
||||
panelSpriteObjectData.texture,
|
||||
panelSpriteObjectData.tiled
|
||||
);
|
||||
} else {
|
||||
/** @type {gdjs.PanelSpriteRuntimeObjectRenderer} */
|
||||
this._renderer = new gdjs.PanelSpriteRuntimeObjectRenderer(
|
||||
this,
|
||||
runtimeScene,
|
||||
panelSpriteObjectData.texture,
|
||||
panelSpriteObjectData.tiled
|
||||
);
|
||||
}
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObject.prototype = Object.create(
|
||||
gdjs.RuntimeObject.prototype
|
||||
);
|
||||
gdjs.registerObject("PanelSpriteObject::PanelSprite", gdjs.PanelSpriteRuntimeObject);
|
||||
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.onDestroyFromScene = function(
|
||||
runtimeScene
|
||||
) {
|
||||
gdjs.RuntimeObject.prototype.onDestroyFromScene.call(this, runtimeScene);
|
||||
|
||||
if (this._renderer.onDestroy) {
|
||||
this._renderer.onDestroy();
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.update = function() {
|
||||
this._renderer.ensureUpToDate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.extraInitializationFromInitialInstance = function(
|
||||
initialInstanceData
|
||||
) {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the x position of the panel sprite.
|
||||
* @param {number} x The new x position in pixels.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setX = function(x) {
|
||||
gdjs.RuntimeObject.prototype.setX.call(this, x);
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the y position of the panel sprite.
|
||||
* @param {number} y The new y position in pixels.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setY = function(y) {
|
||||
gdjs.RuntimeObject.prototype.setY.call(this, y);
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the texture of the panel sprite.
|
||||
* @param {string} textureName The name of the texture.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene the object lives in.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setTexture = function(
|
||||
textureName,
|
||||
runtimeScene
|
||||
) {
|
||||
this._renderer.setTexture(textureName, runtimeScene);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the angle of the panel sprite.
|
||||
* @param {number} angle The new angle in degrees.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setAngle = function(angle) {
|
||||
gdjs.RuntimeObject.prototype.setAngle.call(this, angle);
|
||||
this._renderer.updateAngle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width of the panel sprite in pixels
|
||||
* @return {number} The width in pixels
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getWidth = function() {
|
||||
return this._width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the height of the panel sprite in pixels
|
||||
* @return {number} The height in pixels
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getHeight = function() {
|
||||
return this._height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the width of the panel sprite.
|
||||
* @param {number} width The new width in pixels.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setWidth = function(width) {
|
||||
this._width = width;
|
||||
this._renderer.updateWidth();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the height of the panel sprite.
|
||||
* @param {number} height The new height in pixels.
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setHeight = function(height) {
|
||||
this._height = height;
|
||||
this._renderer.updateHeight();
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the transparency of the object.
|
||||
* @param {number} opacity The new opacity, between 0 (transparent) and 255 (opaque).
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setOpacity = function(opacity) {
|
||||
if (opacity < 0) opacity = 0;
|
||||
if (opacity > 255) opacity = 255;
|
||||
|
||||
this.opacity = opacity;
|
||||
this._renderer.updateOpacity();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the transparency of the object.
|
||||
* @return {number} The opacity, between 0 (transparent) and 255 (opaque).
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getOpacity = function() {
|
||||
return this.opacity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the tint of the panel sprite object.
|
||||
*
|
||||
* @param {string} rgbColor The color, in RGB format ("128;200;255").
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.setColor = function(rgbColor) {
|
||||
this._renderer.setColor(rgbColor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the tint of the panel sprite object.
|
||||
*
|
||||
* @returns {string} rgbColor The color, in RGB format ("128;200;255").
|
||||
*/
|
||||
gdjs.PanelSpriteRuntimeObject.prototype.getColor = function() {
|
||||
return this._renderer.getColor();
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
GDevelop - SystemInfo Extension
|
||||
Copyright (c) 2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @memberof gdjs.evtTools
|
||||
* @class linkedObjects
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.systemInfo = {};
|
||||
|
||||
gdjs.evtTools.systemInfo.isMobile = function() {
|
||||
if (typeof cc !== "undefined" && cc.sys) {
|
||||
return cc.sys.isMobile;
|
||||
} else if (typeof Cocoon !== "undefined" && Cocoon.App) {
|
||||
return true;
|
||||
} else if (typeof window !== "undefined" && window.cordova) {
|
||||
return true;
|
||||
} else if (typeof window !== "undefined") {
|
||||
// Try to detect mobile device browsers.
|
||||
if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
|
||||
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the the device supports WebGL.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene
|
||||
* @returns {boolean} true if WebGL is supported
|
||||
*/
|
||||
gdjs.evtTools.systemInfo.isWebGLSupported = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getRenderer().isWebGLSupported();
|
||||
};
|
|
@ -0,0 +1,166 @@
|
|||
gdjs.TextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene)
|
||||
{
|
||||
this._object = runtimeObject;
|
||||
this._fontManager = runtimeScene.getGame().getFontManager();
|
||||
|
||||
if ( this._text === undefined ) this._text = new PIXI.Text(" ", {align:"left"});
|
||||
this._text.anchor.x = 0.5;
|
||||
this._text.anchor.y = 0.5;
|
||||
runtimeScene.getLayer("").getRenderer().addRendererObject(this._text, runtimeObject.getZOrder());
|
||||
|
||||
this._text.text = runtimeObject._str.length === 0 ? " " : runtimeObject._str;
|
||||
this._justCreated = true; //Work around a PIXI.js bug. See updateTime method.
|
||||
this.updateStyle();
|
||||
this.updatePosition();
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectRenderer = gdjs.TextRuntimeObjectPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.getRendererObject = function() {
|
||||
return this._text;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
if (this._justCreated) { //Work around a PIXI.js bug:
|
||||
this._text.updateText();
|
||||
this.updatePosition(); //Width seems not to be correct when text is not rendered yet.
|
||||
this._justCreated = false;
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateStyle = function() {
|
||||
var fontName = "\"" + this._fontManager.getFontFamily(this._object._fontName) + "\"";
|
||||
|
||||
var style = this._text.style;
|
||||
style.fontStyle = this._object._italic ? 'italic' : 'normal';
|
||||
style.fontWeight = this._object._bold ? 'bold' : 'normal';
|
||||
style.fontSize = this._object._characterSize;
|
||||
style.fontFamily = fontName;
|
||||
|
||||
if (this._object._useGradient){
|
||||
style.fill = this._getGradientHex();
|
||||
} else {
|
||||
style.fill = this._getColorHex();
|
||||
}
|
||||
|
||||
if (this._object._gradientType === 'LINEAR_VERTICAL'){
|
||||
style.fillGradientType = PIXI.TEXT_GRADIENT.LINEAR_VERTICAL;
|
||||
} else {
|
||||
style.fillGradientType = PIXI.TEXT_GRADIENT.LINEAR_HORIZONTAL;
|
||||
}
|
||||
|
||||
style.align = this._object._textAlign;
|
||||
style.wordWrap = this._object._wrapping;
|
||||
style.wordWrapWidth = this._object._wrappingWidth;
|
||||
style.breakWords = true;
|
||||
style.stroke = gdjs.rgbToHexNumber(
|
||||
this._object._outlineColor[0],
|
||||
this._object._outlineColor[1],
|
||||
this._object._outlineColor[2]
|
||||
);
|
||||
style.strokeThickness = this._object._outlineThickness;
|
||||
style.dropShadow = this._object._shadow;
|
||||
style.dropShadowColor = gdjs.rgbToHexNumber(
|
||||
this._object._shadowColor[0],
|
||||
this._object._shadowColor[1],
|
||||
this._object._shadowColor[2]
|
||||
);
|
||||
style.dropShadowBlur = this._object._shadowBlur;
|
||||
style.dropShadowAngle = this._object._shadowAngle;
|
||||
style.dropShadowDistance = this._object._shadowDistance;
|
||||
style.padding = this._object._padding;
|
||||
// Prevent spikey outlines by adding a miter limit
|
||||
style.miterLimit = 3;
|
||||
|
||||
this.updatePosition();
|
||||
|
||||
// Manually ask the PIXI object to re-render as we changed a style property
|
||||
// see http://www.html5gamedevs.com/topic/16924-change-text-style-post-render/
|
||||
this._text.dirty = true;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updatePosition = function() {
|
||||
this._text.position.x = this._object.x+this._text.width/2;
|
||||
this._text.position.y = this._object.y+this._text.height/2;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateAngle = function() {
|
||||
this._text.rotation = gdjs.toRad(this._object.angle);
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateOpacity = function() {
|
||||
this._text.alpha = this._object.opacity / 255;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateString = function() {
|
||||
this._text.text = this._object._str.length === 0 ? " " : this._object._str;
|
||||
this._text.updateText(); //Work around a PIXI.js bug.
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.getWidth = function() {
|
||||
return this._text.width;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.getHeight = function() {
|
||||
return this._text.height;
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype._getColorHex = function() {
|
||||
return gdjs.rgbToHexNumber(
|
||||
this._object._color[0],
|
||||
this._object._color[1],
|
||||
this._object._color[2]
|
||||
);
|
||||
}
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype._getGradientHex = function() {
|
||||
var gradient = [];
|
||||
for (var colorIndex = 0; colorIndex < this._object._gradient.length; colorIndex++){
|
||||
gradient.push(
|
||||
'#' + gdjs.rgbToHex(
|
||||
this._object._gradient[colorIndex][0],
|
||||
this._object._gradient[colorIndex][1],
|
||||
this._object._gradient[colorIndex][2]
|
||||
)
|
||||
);
|
||||
}
|
||||
return gradient;
|
||||
}
|
||||
/**
|
||||
* Get y-scale of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.getScaleX = function() {
|
||||
return this._text.scale.x;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get x-scale of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.getScaleY = function() {
|
||||
return this._text.scale.y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text object scale.
|
||||
* @param {number} newScale The new scale for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.setScale = function(newScale) {
|
||||
this._text.scale.x = newScale;
|
||||
this._text.scale.y = newScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text object x-scale.
|
||||
* @param {number} newScale The new x-scale for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.setScaleX = function(newScale) {
|
||||
this._text.scale.x = newScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text object y-scale.
|
||||
* @param {number} newScale The new y-scale for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.setScaleY = function(newScale) {
|
||||
this._text.scale.y = newScale;
|
||||
};
|
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TextObjectDataType Base parameters for gdjs.TextRuntimeObject
|
||||
* @property {number} characterSize The size of the characters
|
||||
* @property {string} font The font name
|
||||
* @property {boolean} bold Is Bold?
|
||||
* @property {boolean} italic Is Italic?
|
||||
* @property {boolean} underlined Is Underlined?
|
||||
* @property {Object} color The text color in an RGB representation
|
||||
* @property {number} color.r The Red level from 0 to 255
|
||||
* @property {number} color.g The Green level from 0 to 255
|
||||
* @property {number} color.b The Blue level from 0 to 255
|
||||
* @property {string} string The text of the object
|
||||
*
|
||||
* @typedef {ObjectData & TextObjectDataType} TextObjectData
|
||||
*/
|
||||
|
||||
/**
|
||||
* Displays a text.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class TextRuntimeObject
|
||||
* @extends RuntimeObject
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to
|
||||
* @param {TextObjectData} textObjectData The initial properties of the object
|
||||
*/
|
||||
gdjs.TextRuntimeObject = function(runtimeScene, textObjectData)
|
||||
{
|
||||
gdjs.RuntimeObject.call(this, runtimeScene, textObjectData);
|
||||
|
||||
/** @type {number} */
|
||||
this._characterSize = Math.max(1, textObjectData.characterSize);
|
||||
|
||||
/** @type {string} */
|
||||
this._fontName = textObjectData.font;
|
||||
|
||||
/** @type {boolean} */
|
||||
this._bold = textObjectData.bold;
|
||||
|
||||
/** @type {boolean} */
|
||||
this._italic = textObjectData.italic;
|
||||
|
||||
/** @type {boolean} */
|
||||
this._underlined = textObjectData.underlined;
|
||||
|
||||
/** @type {Array<number>} */
|
||||
this._color = [textObjectData.color.r, textObjectData.color.g, textObjectData.color.b];
|
||||
|
||||
/** @type {boolean} */
|
||||
this._useGradient = false;
|
||||
|
||||
/** @type {Array} */
|
||||
this._gradient = [];
|
||||
|
||||
/** @type {string} */
|
||||
this._gradientType = '';
|
||||
|
||||
/** @type {number} */
|
||||
this.opacity = 255;
|
||||
|
||||
/** @type {string} */
|
||||
this._textAlign = 'left';
|
||||
|
||||
/** @type {boolean} */
|
||||
this._wrapping = false;
|
||||
|
||||
/** @type {number} */
|
||||
this._wrappingWidth = 1;
|
||||
|
||||
/** @type {number} */
|
||||
this._outlineThickness = 0;
|
||||
|
||||
/** @type {Array<number>} */
|
||||
this._outlineColor = [255,255,255];
|
||||
|
||||
/** @type {boolean} */
|
||||
this._shadow = false;
|
||||
|
||||
/** @type {Array<number>} */
|
||||
this._shadowColor = [0,0,0];
|
||||
|
||||
/** @type {number} */
|
||||
this._shadowDistance = 1;
|
||||
|
||||
/** @type {number} */
|
||||
this._shadowBlur = 1;
|
||||
|
||||
/** @type {number} */
|
||||
this._shadowAngle = 0;
|
||||
|
||||
/** @type {number} */
|
||||
this._padding = 5;
|
||||
|
||||
/** @type {number} */
|
||||
this._scaleX = 1;
|
||||
|
||||
/** @type {number} */
|
||||
this._scaleY = 1;
|
||||
|
||||
/** @type {string} */
|
||||
this._str = textObjectData.string;
|
||||
|
||||
if (this._renderer)
|
||||
gdjs.TextRuntimeObjectRenderer.call(this._renderer, this, runtimeScene);
|
||||
else
|
||||
/** @type {gdjs.TextRuntimeObjectRenderer} */
|
||||
this._renderer = new gdjs.TextRuntimeObjectRenderer(this, runtimeScene);
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObject.prototype = Object.create( gdjs.RuntimeObject.prototype );
|
||||
gdjs.registerObject("TextObject::Text", gdjs.TextRuntimeObject);
|
||||
|
||||
gdjs.TextRuntimeObject.prototype.getRendererObject = function() {
|
||||
return this._renderer.getRendererObject();
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObject.prototype.update = function() {
|
||||
this._renderer.ensureUpToDate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
* @private
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.extraInitializationFromInitialInstance = function(initialInstanceData) {
|
||||
if ( initialInstanceData.customSize ) {
|
||||
this.setWrapping(true);
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the rendered object position.
|
||||
* @private
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype._updateTextPosition = function() {
|
||||
this.hitBoxesDirty = true;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set object position on X axis.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setX = function(x) {
|
||||
gdjs.RuntimeObject.prototype.setX.call(this, x);
|
||||
this._updateTextPosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set object position on Y axis.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setY = function(y) {
|
||||
gdjs.RuntimeObject.prototype.setY.call(this, y);
|
||||
this._updateTextPosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the angle of the object.
|
||||
* @param {number} angle The new angle of the object
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setAngle = function(angle) {
|
||||
gdjs.RuntimeObject.prototype.setAngle.call(this, angle);
|
||||
this._renderer.updateAngle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set object opacity.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setOpacity = function(opacity) {
|
||||
if ( opacity < 0 ) opacity = 0;
|
||||
if ( opacity > 255 ) opacity = 255;
|
||||
|
||||
this.opacity = opacity;
|
||||
this._renderer.updateOpacity();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get object opacity.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getOpacity = function() {
|
||||
return this.opacity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the string displayed by the object.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getString = function() {
|
||||
return this._str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the string displayed by the object.
|
||||
* @param {string} str The new text
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setString = function(str) {
|
||||
if ( str === this._str ) return;
|
||||
|
||||
this._str = str;
|
||||
this._renderer.updateString();
|
||||
this._updateTextPosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the font size of the characters of the object.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getCharacterSize = function() {
|
||||
return this._characterSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the font size for characters of the object.
|
||||
* @param {number} newSize The new font size for the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setCharacterSize = function(newSize) {
|
||||
if (newSize <= 1) newSize = 1;
|
||||
this._characterSize = newSize;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the text is bold.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.isBold = function() {
|
||||
return this._bold;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set bold for the object text.
|
||||
* @param enable {boolean} true to have a bold text, false otherwise.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setBold = function(enable) {
|
||||
this._bold = enable;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the text is italic.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.isItalic = function() {
|
||||
return this._italic;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set italic for the object text.
|
||||
* @param enable {boolean} true to have an italic text, false otherwise.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setItalic = function(enable) {
|
||||
this._italic = enable;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get width of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getWidth = function() {
|
||||
return this._renderer.getWidth();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get height of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getHeight = function() {
|
||||
return this._renderer.getHeight();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get scale of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getScale = function() {
|
||||
return (Math.abs(this._scaleX)+Math.abs(this._scaleY))/2.0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get y-scale of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getScaleX = function() {
|
||||
return this._renderer.getScaleX();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get x-scale of the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getScaleY = function() {
|
||||
return this._renderer.getScaleY();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text object scale.
|
||||
* @param {number} newScale The new scale for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setScale = function(newScale) {
|
||||
this._scaleX = newScale;
|
||||
this._scaleY = newScale;
|
||||
this._renderer.setScale(newScale);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text object x-scale.
|
||||
* @param {number} newScale The new x-scale for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setScaleX = function(newScale) {
|
||||
this._scaleX = newScale;
|
||||
this._renderer.setScaleX(newScale);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text object y-scale.
|
||||
* @param {number} newScale The new y-scale for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setScaleY = function(newScale) {
|
||||
this._scaleY = newScale;
|
||||
this._renderer.setScaleY(newScale);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the text color.
|
||||
* @param {String} color color as a "R;G;B" string, for example: "255;0;0"
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setColor = function(str) {
|
||||
var color = str.split(";");
|
||||
if ( color.length < 3 ) return;
|
||||
|
||||
this._color[0] = parseInt(color[0], 10);
|
||||
this._color[1] = parseInt(color[1], 10);
|
||||
this._color[2] = parseInt(color[2], 10);
|
||||
|
||||
this._useGradient = false;
|
||||
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text color.
|
||||
* @return {String} The color as a "R;G;B" string, for example: "255;0;0"
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getColor = function(str) {
|
||||
return this._color[0] + ";" + this._color[1] + ";" + this._color[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text alignment for multiline text objects.
|
||||
* @param {string} alignment The text alignment.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setTextAlignment = function(alignment) {
|
||||
this._textAlign = alignment;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text alignment of text object.
|
||||
* @return {string} The text alignment.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getTextAlignment = function() {
|
||||
return this._textAlign;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if word wrapping is enabled for the text.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.isWrapping = function() {
|
||||
return this._wrapping;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set word wrapping for the object text.
|
||||
* @param {boolean} enable true to enable word wrapping, false to disable it.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setWrapping = function(enable) {
|
||||
this._wrapping = enable;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the word wrapping width for the text object.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getWrappingWidth = function() {
|
||||
return this._wrappingWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the word wrapping width for the text object.
|
||||
* @param {number} width The new width to set.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setWrappingWidth = function(width) {
|
||||
if (width <= 1) width = 1;
|
||||
this._wrappingWidth = width;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the outline for the text object.
|
||||
* @param {string} str color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param {number} thickness thickness of the outline (0 = disabled)
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setOutline = function(str, thickness) {
|
||||
var color = str.split(";");
|
||||
if ( color.length < 3 ) return;
|
||||
|
||||
this._outlineColor[0] = parseInt(color[0], 10);
|
||||
this._outlineColor[1] = parseInt(color[1], 10);
|
||||
this._outlineColor[2] = parseInt(color[2], 10);
|
||||
this._outlineThickness = thickness;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the shadow for the text object.
|
||||
* @param {string} str color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param {number} distance distance between the shadow and the text, in pixels.
|
||||
* @param {number} blur amout of shadow blur, in pixels.
|
||||
* @param {number} angle shadow offset direction, in degrees.
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setShadow = function(str, distance, blur, angle) {
|
||||
var color = str.split(";");
|
||||
if ( color.length < 3 ) return;
|
||||
|
||||
this._shadowColor[0] = parseInt(color[0], 10);
|
||||
this._shadowColor[1] = parseInt(color[1], 10);
|
||||
this._shadowColor[2] = parseInt(color[2], 10);
|
||||
this._shadowDistance = distance;
|
||||
this._shadowBlur = blur;
|
||||
this._shadowAngle = angle;
|
||||
this._shadow = true;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the gradient for the text object.
|
||||
* @param {string} strFirstColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param {string} strSecondColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param {string} strThirdColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param {string} strFourthColor color as a "R;G;B" string, for example: "255;0;0"
|
||||
* @param {string} strGradientType gradient type
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setGradient = function(strGradientType, strFirstColor, strSecondColor, strThirdColor, strFourthColor) {
|
||||
var colorFirst = strFirstColor.split(";");
|
||||
var colorSecond = strSecondColor.split(";");
|
||||
var colorThird = strThirdColor.split(";");
|
||||
var colorFourth = strFourthColor.split(";");
|
||||
|
||||
this._gradient = [];
|
||||
|
||||
if (colorFirst.length == 3){
|
||||
this._gradient.push([
|
||||
parseInt(colorFirst[0], 10),
|
||||
parseInt(colorFirst[1], 10),
|
||||
parseInt(colorFirst[2], 10)
|
||||
]);
|
||||
}
|
||||
|
||||
if (colorSecond.length == 3){
|
||||
this._gradient.push([
|
||||
parseInt(colorSecond[0], 10),
|
||||
parseInt(colorSecond[1], 10),
|
||||
parseInt(colorSecond[2], 10)
|
||||
]);
|
||||
}
|
||||
|
||||
if (colorThird.length == 3){
|
||||
this._gradient.push([
|
||||
parseInt(colorThird[0], 10),
|
||||
parseInt(colorThird[1], 10),
|
||||
parseInt(colorThird[2], 10)
|
||||
]);
|
||||
}
|
||||
|
||||
if (colorFourth.length == 3){
|
||||
this._gradient.push([
|
||||
parseInt(colorFourth[0], 10),
|
||||
parseInt(colorFourth[1], 10),
|
||||
parseInt(colorFourth[2], 10)
|
||||
]);
|
||||
}
|
||||
|
||||
this._gradientType = strGradientType;
|
||||
|
||||
this._useGradient = (this._gradient.length > 1) ? true : false;
|
||||
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the shadow of the text object.
|
||||
* @param {boolean} enable true to show the shadow, false to hide it
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.showShadow = function(enable) {
|
||||
this._shadow = enable;
|
||||
this._renderer.updateStyle();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get padding of the text object.
|
||||
* @return {number} number of pixels around the text before it gets cropped
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.getPadding = function() {
|
||||
return this._padding;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set padding of the text object.
|
||||
* @param {number} value number of pixels around the text before it gets cropped
|
||||
*/
|
||||
gdjs.TextRuntimeObject.prototype.setPadding = function(value) {
|
||||
this._padding = value;
|
||||
this._renderer.updateStyle();
|
||||
};
|
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,121 @@
|
|||
gdjs.Start_32GameCode = {};
|
||||
gdjs.Start_32GameCode.GDSfondoObjects1= [];
|
||||
gdjs.Start_32GameCode.GDSfondoObjects2= [];
|
||||
gdjs.Start_32GameCode.GDTitoloGiocoObjects1= [];
|
||||
gdjs.Start_32GameCode.GDTitoloGiocoObjects2= [];
|
||||
gdjs.Start_32GameCode.GDNewObjectObjects1= [];
|
||||
gdjs.Start_32GameCode.GDNewObjectObjects2= [];
|
||||
gdjs.Start_32GameCode.GDNewObject2Objects1= [];
|
||||
gdjs.Start_32GameCode.GDNewObject2Objects2= [];
|
||||
gdjs.Start_32GameCode.GDOptionsObjects1= [];
|
||||
gdjs.Start_32GameCode.GDOptionsObjects2= [];
|
||||
gdjs.Start_32GameCode.GDCreditsObjects1= [];
|
||||
gdjs.Start_32GameCode.GDCreditsObjects2= [];
|
||||
gdjs.Start_32GameCode.GDPlayObjects1= [];
|
||||
gdjs.Start_32GameCode.GDPlayObjects2= [];
|
||||
|
||||
gdjs.Start_32GameCode.conditionTrue_0 = {val:false};
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0 = {val:false};
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0 = {val:false};
|
||||
gdjs.Start_32GameCode.condition2IsTrue_0 = {val:false};
|
||||
|
||||
|
||||
gdjs.Start_32GameCode.mapOfGDgdjs_46Start_9532GameCode_46GDPlayObjects1Objects = Hashtable.newFrom({"Play": gdjs.Start_32GameCode.GDPlayObjects1});gdjs.Start_32GameCode.mapOfGDgdjs_46Start_9532GameCode_46GDOptionsObjects1Objects = Hashtable.newFrom({"Options": gdjs.Start_32GameCode.GDOptionsObjects1});gdjs.Start_32GameCode.mapOfGDgdjs_46Start_9532GameCode_46GDCreditsObjects1Objects = Hashtable.newFrom({"Credits": gdjs.Start_32GameCode.GDCreditsObjects1});gdjs.Start_32GameCode.eventsList0x5b6e18 = function(runtimeScene) {
|
||||
|
||||
{
|
||||
|
||||
gdjs.Start_32GameCode.GDPlayObjects1.createFrom(runtimeScene.getObjects("Play"));
|
||||
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = false;
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.Start_32GameCode.mapOfGDgdjs_46Start_9532GameCode_46GDPlayObjects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.Start_32GameCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.Start_32GameCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Phase1", false);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
gdjs.Start_32GameCode.GDOptionsObjects1.createFrom(runtimeScene.getObjects("Options"));
|
||||
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = false;
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.Start_32GameCode.mapOfGDgdjs_46Start_9532GameCode_46GDOptionsObjects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.Start_32GameCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.Start_32GameCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Options", false);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
gdjs.Start_32GameCode.GDCreditsObjects1.createFrom(runtimeScene.getObjects("Credits"));
|
||||
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = false;
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.Start_32GameCode.mapOfGDgdjs_46Start_9532GameCode_46GDCreditsObjects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.Start_32GameCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.Start_32GameCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.Start_32GameCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Credits", false);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.Start_32GameCode.condition0IsTrue_0.val = gdjs.evtTools.runtimeScene.sceneJustBegins(runtimeScene);
|
||||
}if (gdjs.Start_32GameCode.condition0IsTrue_0.val) {
|
||||
{gdjs.evtTools.sound.playSound(runtimeScene, "Festa-di-Laurea-piena-da-convertire.ogg", false, 100, 1);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}; //End of gdjs.Start_32GameCode.eventsList0x5b6e18
|
||||
|
||||
|
||||
gdjs.Start_32GameCode.func = function(runtimeScene) {
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
|
||||
gdjs.Start_32GameCode.GDSfondoObjects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDSfondoObjects2.length = 0;
|
||||
gdjs.Start_32GameCode.GDTitoloGiocoObjects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDTitoloGiocoObjects2.length = 0;
|
||||
gdjs.Start_32GameCode.GDNewObjectObjects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDNewObjectObjects2.length = 0;
|
||||
gdjs.Start_32GameCode.GDNewObject2Objects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDNewObject2Objects2.length = 0;
|
||||
gdjs.Start_32GameCode.GDOptionsObjects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDOptionsObjects2.length = 0;
|
||||
gdjs.Start_32GameCode.GDCreditsObjects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDCreditsObjects2.length = 0;
|
||||
gdjs.Start_32GameCode.GDPlayObjects1.length = 0;
|
||||
gdjs.Start_32GameCode.GDPlayObjects2.length = 0;
|
||||
|
||||
gdjs.Start_32GameCode.eventsList0x5b6e18(runtimeScene);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
gdjs['Start_32GameCode'] = gdjs.Start_32GameCode;
|
|
@ -0,0 +1,111 @@
|
|||
gdjs.TitleCode = {};
|
||||
gdjs.TitleCode.GDBackgroundObjects1= [];
|
||||
gdjs.TitleCode.GDBackgroundObjects2= [];
|
||||
gdjs.TitleCode.GDTitleObjects1= [];
|
||||
gdjs.TitleCode.GDTitleObjects2= [];
|
||||
gdjs.TitleCode.GDInstruction1Objects1= [];
|
||||
gdjs.TitleCode.GDInstruction1Objects2= [];
|
||||
gdjs.TitleCode.GDInstruction2Objects1= [];
|
||||
gdjs.TitleCode.GDInstruction2Objects2= [];
|
||||
gdjs.TitleCode.GDNewObject3Objects1= [];
|
||||
gdjs.TitleCode.GDNewObject3Objects2= [];
|
||||
gdjs.TitleCode.GDNewObjectObjects1= [];
|
||||
gdjs.TitleCode.GDNewObjectObjects2= [];
|
||||
gdjs.TitleCode.GDGiocaAncoraObjects1= [];
|
||||
gdjs.TitleCode.GDGiocaAncoraObjects2= [];
|
||||
|
||||
gdjs.TitleCode.conditionTrue_0 = {val:false};
|
||||
gdjs.TitleCode.condition0IsTrue_0 = {val:false};
|
||||
gdjs.TitleCode.condition1IsTrue_0 = {val:false};
|
||||
gdjs.TitleCode.condition2IsTrue_0 = {val:false};
|
||||
|
||||
|
||||
gdjs.TitleCode.mapOfGDgdjs_46TitleCode_46GDGiocaAncoraObjects1Objects = Hashtable.newFrom({"GiocaAncora": gdjs.TitleCode.GDGiocaAncoraObjects1});gdjs.TitleCode.eventsList0xb12b54 = function(runtimeScene) {
|
||||
|
||||
{
|
||||
|
||||
gdjs.TitleCode.GDGiocaAncoraObjects1.createFrom(runtimeScene.getObjects("GiocaAncora"));
|
||||
|
||||
gdjs.TitleCode.condition0IsTrue_0.val = false;
|
||||
gdjs.TitleCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.TitleCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.TitleCode.mapOfGDgdjs_46TitleCode_46GDGiocaAncoraObjects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.TitleCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.TitleCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.TitleCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Phase1", true);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}; //End of gdjs.TitleCode.eventsList0xb12b54
|
||||
gdjs.TitleCode.eventsList0x5b6e18 = function(runtimeScene) {
|
||||
|
||||
{
|
||||
|
||||
|
||||
gdjs.TitleCode.condition0IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.TitleCode.condition0IsTrue_0.val = gdjs.evtTools.runtimeScene.sceneJustBegins(runtimeScene);
|
||||
}if (gdjs.TitleCode.condition0IsTrue_0.val) {
|
||||
{gdjs.evtTools.window.setFullScreen(runtimeScene, true, true);
|
||||
}{gdjs.evtTools.runtimeScene.resetTimer(runtimeScene, "");
|
||||
}{runtimeScene.getGame().getVariables().getFromIndex(0).setNumber(0);
|
||||
}{gdjs.evtTools.sound.playSound(runtimeScene, "Lose-da-converitre-in-ogg.ogg", false, 100, 1);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
|
||||
gdjs.TitleCode.condition0IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.TitleCode.condition0IsTrue_0.val = gdjs.evtTools.runtimeScene.timerElapsedTime(runtimeScene, 2, "");
|
||||
}if (gdjs.TitleCode.condition0IsTrue_0.val) {
|
||||
|
||||
{ //Subevents
|
||||
gdjs.TitleCode.eventsList0xb12b54(runtimeScene);} //End of subevents
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}; //End of gdjs.TitleCode.eventsList0x5b6e18
|
||||
|
||||
|
||||
gdjs.TitleCode.func = function(runtimeScene) {
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
|
||||
gdjs.TitleCode.GDBackgroundObjects1.length = 0;
|
||||
gdjs.TitleCode.GDBackgroundObjects2.length = 0;
|
||||
gdjs.TitleCode.GDTitleObjects1.length = 0;
|
||||
gdjs.TitleCode.GDTitleObjects2.length = 0;
|
||||
gdjs.TitleCode.GDInstruction1Objects1.length = 0;
|
||||
gdjs.TitleCode.GDInstruction1Objects2.length = 0;
|
||||
gdjs.TitleCode.GDInstruction2Objects1.length = 0;
|
||||
gdjs.TitleCode.GDInstruction2Objects2.length = 0;
|
||||
gdjs.TitleCode.GDNewObject3Objects1.length = 0;
|
||||
gdjs.TitleCode.GDNewObject3Objects2.length = 0;
|
||||
gdjs.TitleCode.GDNewObjectObjects1.length = 0;
|
||||
gdjs.TitleCode.GDNewObjectObjects2.length = 0;
|
||||
gdjs.TitleCode.GDGiocaAncoraObjects1.length = 0;
|
||||
gdjs.TitleCode.GDGiocaAncoraObjects2.length = 0;
|
||||
|
||||
gdjs.TitleCode.eventsList0x5b6e18(runtimeScene);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
gdjs['TitleCode'] = gdjs.TitleCode;
|
|
@ -0,0 +1,71 @@
|
|||
gdjs.WinPageCode = {};
|
||||
gdjs.WinPageCode.GDSfondoObjects1= [];
|
||||
gdjs.WinPageCode.GDSfondoObjects2= [];
|
||||
gdjs.WinPageCode.GDNewObjectObjects1= [];
|
||||
gdjs.WinPageCode.GDNewObjectObjects2= [];
|
||||
gdjs.WinPageCode.GDNewObject2Objects1= [];
|
||||
gdjs.WinPageCode.GDNewObject2Objects2= [];
|
||||
gdjs.WinPageCode.GDGiocaAncoraObjects1= [];
|
||||
gdjs.WinPageCode.GDGiocaAncoraObjects2= [];
|
||||
|
||||
gdjs.WinPageCode.conditionTrue_0 = {val:false};
|
||||
gdjs.WinPageCode.condition0IsTrue_0 = {val:false};
|
||||
gdjs.WinPageCode.condition1IsTrue_0 = {val:false};
|
||||
gdjs.WinPageCode.condition2IsTrue_0 = {val:false};
|
||||
|
||||
|
||||
gdjs.WinPageCode.mapOfGDgdjs_46WinPageCode_46GDGiocaAncoraObjects1Objects = Hashtable.newFrom({"GiocaAncora": gdjs.WinPageCode.GDGiocaAncoraObjects1});gdjs.WinPageCode.eventsList0x5b6e18 = function(runtimeScene) {
|
||||
|
||||
{
|
||||
|
||||
gdjs.WinPageCode.GDGiocaAncoraObjects1.createFrom(runtimeScene.getObjects("GiocaAncora"));
|
||||
|
||||
gdjs.WinPageCode.condition0IsTrue_0.val = false;
|
||||
gdjs.WinPageCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.WinPageCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.WinPageCode.mapOfGDgdjs_46WinPageCode_46GDGiocaAncoraObjects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.WinPageCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.WinPageCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.WinPageCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Phase1", false);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
|
||||
gdjs.WinPageCode.condition0IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.WinPageCode.condition0IsTrue_0.val = gdjs.evtTools.runtimeScene.sceneJustBegins(runtimeScene);
|
||||
}if (gdjs.WinPageCode.condition0IsTrue_0.val) {
|
||||
{gdjs.evtTools.sound.playSound(runtimeScene, "Win-da-convertire-in-OGG.ogg", false, 100, 1);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}; //End of gdjs.WinPageCode.eventsList0x5b6e18
|
||||
|
||||
|
||||
gdjs.WinPageCode.func = function(runtimeScene) {
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
|
||||
gdjs.WinPageCode.GDSfondoObjects1.length = 0;
|
||||
gdjs.WinPageCode.GDSfondoObjects2.length = 0;
|
||||
gdjs.WinPageCode.GDNewObjectObjects1.length = 0;
|
||||
gdjs.WinPageCode.GDNewObjectObjects2.length = 0;
|
||||
gdjs.WinPageCode.GDNewObject2Objects1.length = 0;
|
||||
gdjs.WinPageCode.GDNewObject2Objects2.length = 0;
|
||||
gdjs.WinPageCode.GDGiocaAncoraObjects1.length = 0;
|
||||
gdjs.WinPageCode.GDGiocaAncoraObjects2.length = 0;
|
||||
|
||||
gdjs.WinPageCode.eventsList0x5b6e18(runtimeScene);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
gdjs['WinPageCode'] = gdjs.WinPageCode;
|
|
@ -0,0 +1,54 @@
|
|||
gdjs.OptionsCode = {};
|
||||
gdjs.OptionsCode.GDNewObjectObjects1= [];
|
||||
gdjs.OptionsCode.GDNewObjectObjects2= [];
|
||||
gdjs.OptionsCode.GDComandiGiocoObjects1= [];
|
||||
gdjs.OptionsCode.GDComandiGiocoObjects2= [];
|
||||
gdjs.OptionsCode.GDHomeObjects1= [];
|
||||
gdjs.OptionsCode.GDHomeObjects2= [];
|
||||
|
||||
gdjs.OptionsCode.conditionTrue_0 = {val:false};
|
||||
gdjs.OptionsCode.condition0IsTrue_0 = {val:false};
|
||||
gdjs.OptionsCode.condition1IsTrue_0 = {val:false};
|
||||
gdjs.OptionsCode.condition2IsTrue_0 = {val:false};
|
||||
|
||||
|
||||
gdjs.OptionsCode.mapOfGDgdjs_46OptionsCode_46GDHomeObjects1Objects = Hashtable.newFrom({"Home": gdjs.OptionsCode.GDHomeObjects1});gdjs.OptionsCode.eventsList0x5b6e18 = function(runtimeScene) {
|
||||
|
||||
{
|
||||
|
||||
gdjs.OptionsCode.GDHomeObjects1.createFrom(runtimeScene.getObjects("Home"));
|
||||
|
||||
gdjs.OptionsCode.condition0IsTrue_0.val = false;
|
||||
gdjs.OptionsCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.OptionsCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.OptionsCode.mapOfGDgdjs_46OptionsCode_46GDHomeObjects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.OptionsCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.OptionsCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.OptionsCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Start Game", false);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}; //End of gdjs.OptionsCode.eventsList0x5b6e18
|
||||
|
||||
|
||||
gdjs.OptionsCode.func = function(runtimeScene) {
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
|
||||
gdjs.OptionsCode.GDNewObjectObjects1.length = 0;
|
||||
gdjs.OptionsCode.GDNewObjectObjects2.length = 0;
|
||||
gdjs.OptionsCode.GDComandiGiocoObjects1.length = 0;
|
||||
gdjs.OptionsCode.GDComandiGiocoObjects2.length = 0;
|
||||
gdjs.OptionsCode.GDHomeObjects1.length = 0;
|
||||
gdjs.OptionsCode.GDHomeObjects2.length = 0;
|
||||
|
||||
gdjs.OptionsCode.eventsList0x5b6e18(runtimeScene);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
gdjs['OptionsCode'] = gdjs.OptionsCode;
|
|
@ -0,0 +1,58 @@
|
|||
gdjs.CreditsCode = {};
|
||||
gdjs.CreditsCode.GDNewObjectObjects1= [];
|
||||
gdjs.CreditsCode.GDNewObjectObjects2= [];
|
||||
gdjs.CreditsCode.GDNewObject2Objects1= [];
|
||||
gdjs.CreditsCode.GDNewObject2Objects2= [];
|
||||
gdjs.CreditsCode.GDNewObject3Objects1= [];
|
||||
gdjs.CreditsCode.GDNewObject3Objects2= [];
|
||||
gdjs.CreditsCode.GDNewObject4Objects1= [];
|
||||
gdjs.CreditsCode.GDNewObject4Objects2= [];
|
||||
|
||||
gdjs.CreditsCode.conditionTrue_0 = {val:false};
|
||||
gdjs.CreditsCode.condition0IsTrue_0 = {val:false};
|
||||
gdjs.CreditsCode.condition1IsTrue_0 = {val:false};
|
||||
gdjs.CreditsCode.condition2IsTrue_0 = {val:false};
|
||||
|
||||
|
||||
gdjs.CreditsCode.mapOfGDgdjs_46CreditsCode_46GDNewObject2Objects1Objects = Hashtable.newFrom({"NewObject2": gdjs.CreditsCode.GDNewObject2Objects1});gdjs.CreditsCode.eventsList0x5b6e18 = function(runtimeScene) {
|
||||
|
||||
{
|
||||
|
||||
gdjs.CreditsCode.GDNewObject2Objects1.createFrom(runtimeScene.getObjects("NewObject2"));
|
||||
|
||||
gdjs.CreditsCode.condition0IsTrue_0.val = false;
|
||||
gdjs.CreditsCode.condition1IsTrue_0.val = false;
|
||||
{
|
||||
gdjs.CreditsCode.condition0IsTrue_0.val = gdjs.evtTools.input.cursorOnObject(gdjs.CreditsCode.mapOfGDgdjs_46CreditsCode_46GDNewObject2Objects1Objects, runtimeScene, true, false);
|
||||
}if ( gdjs.CreditsCode.condition0IsTrue_0.val ) {
|
||||
{
|
||||
gdjs.CreditsCode.condition1IsTrue_0.val = gdjs.evtTools.input.isMouseButtonPressed(runtimeScene, "Left");
|
||||
}}
|
||||
if (gdjs.CreditsCode.condition1IsTrue_0.val) {
|
||||
{gdjs.evtTools.runtimeScene.replaceScene(runtimeScene, "Start Game", false);
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}; //End of gdjs.CreditsCode.eventsList0x5b6e18
|
||||
|
||||
|
||||
gdjs.CreditsCode.func = function(runtimeScene) {
|
||||
runtimeScene.getOnceTriggers().startNewFrame();
|
||||
|
||||
gdjs.CreditsCode.GDNewObjectObjects1.length = 0;
|
||||
gdjs.CreditsCode.GDNewObjectObjects2.length = 0;
|
||||
gdjs.CreditsCode.GDNewObject2Objects1.length = 0;
|
||||
gdjs.CreditsCode.GDNewObject2Objects2.length = 0;
|
||||
gdjs.CreditsCode.GDNewObject3Objects1.length = 0;
|
||||
gdjs.CreditsCode.GDNewObject3Objects2.length = 0;
|
||||
gdjs.CreditsCode.GDNewObject4Objects1.length = 0;
|
||||
gdjs.CreditsCode.GDNewObject4Objects2.length = 0;
|
||||
|
||||
gdjs.CreditsCode.eventsList0x5b6e18(runtimeScene);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
gdjs['CreditsCode'] = gdjs.CreditsCode;
|
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @memberof gdjs.evtTools
|
||||
* @class camera
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.camera = gdjs.evtTools.camera || {};
|
||||
|
||||
gdjs.evtTools.camera.setCameraX = function(runtimeScene, x, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
runtimeScene.getLayer(layer).setCameraX(x, cameraId);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.setCameraY = function(runtimeScene, y, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
runtimeScene.getLayer(layer).setCameraY(y, cameraId);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.getCameraX = function(runtimeScene, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
|
||||
|
||||
return runtimeScene.getLayer(layer).getCameraX();
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.getCameraY = function(runtimeScene, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
|
||||
|
||||
return runtimeScene.getLayer(layer).getCameraY();
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.getCameraWidth = function(runtimeScene, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
|
||||
|
||||
return runtimeScene.getLayer(layer).getCameraWidth();
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.getCameraHeight = function(runtimeScene, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
|
||||
|
||||
return runtimeScene.getLayer(layer).getCameraHeight();
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.showLayer = function(runtimeScene, layer) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).show(true);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.hideLayer = function(runtimeScene, layer) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).show(false);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.layerIsVisible = function(runtimeScene, layer) {
|
||||
return runtimeScene.hasLayer(layer) && runtimeScene.getLayer(layer).isVisible();
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.setCameraRotation = function(runtimeScene, rotation, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).setCameraRotation(rotation, cameraId);
|
||||
}
|
||||
gdjs.evtTools.camera.getCameraRotation = function(runtimeScene, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return 0; }
|
||||
|
||||
return runtimeScene.getLayer(layer).getCameraRotation(cameraId);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.setCameraZoom = function(runtimeScene, newZoom, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).setCameraZoom(newZoom, cameraId);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.centerCamera = function(runtimeScene, object, anticipateMove, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) || object == null ) { return; }
|
||||
|
||||
var layer = runtimeScene.getLayer(layer);
|
||||
var xOffset = 0; var yOffset = 0;
|
||||
if ( anticipateMove && !object.hasNoForces() ) {
|
||||
var objectAverageForce = object.getAverageForce();
|
||||
var elapsedTimeInSeconds = object.getElapsedTime(runtimeScene) / 1000;
|
||||
|
||||
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
|
||||
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
|
||||
}
|
||||
|
||||
layer.setCameraX(object.getDrawableX()+object.getCenterX(), cameraId);
|
||||
layer.setCameraY(object.getDrawableY()+object.getCenterY(), cameraId);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.centerCameraWithinLimits = function(runtimeScene, object, left, top, right, bottom, anticipateMove, layer, cameraId) {
|
||||
if ( !runtimeScene.hasLayer(layer) || object == null ) { return; }
|
||||
|
||||
var layer = runtimeScene.getLayer(layer);
|
||||
var xOffset = 0; var yOffset = 0;
|
||||
if ( anticipateMove && !object.hasNoForces() ) {
|
||||
var objectAverageForce = object.getAverageForce();
|
||||
var elapsedTimeInSeconds = object.getElapsedTime(runtimeScene) / 1000;
|
||||
|
||||
xOffset = objectAverageForce.getX() * elapsedTimeInSeconds;
|
||||
yOffset = objectAverageForce.getY() * elapsedTimeInSeconds;
|
||||
}
|
||||
|
||||
var newX = object.getDrawableX()+object.getCenterX()+xOffset;
|
||||
if ( newX < left +layer.getCameraWidth(cameraId)/2 ) newX = left+layer.getCameraWidth(cameraId)/2;
|
||||
if ( newX > right-layer.getCameraWidth(cameraId)/2 ) newX = right-layer.getCameraWidth(cameraId)/2;
|
||||
|
||||
var newY = object.getDrawableY()+object.getCenterY()+yOffset;
|
||||
if ( newY < top +layer.getCameraHeight(cameraId)/2 ) newY = top+layer.getCameraHeight(cameraId)/2;
|
||||
if ( newY > bottom-layer.getCameraHeight(cameraId)/2 ) newY = bottom-layer.getCameraHeight(cameraId)/2;
|
||||
|
||||
layer.setCameraX(newX, cameraId);
|
||||
layer.setCameraY(newY, cameraId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a layer effect parameter (with a number).
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene
|
||||
* @param {string} layer The name of the layer
|
||||
* @param {string} effect The name of the effect
|
||||
* @param {string} parameter The parameter to update
|
||||
* @param {number} value The new value
|
||||
*/
|
||||
gdjs.evtTools.camera.setLayerEffectDoubleParameter = function(runtimeScene, layer, effect, parameter, value) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).setEffectDoubleParameter(effect, parameter, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a layer effect parameter (with a string).
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene
|
||||
* @param {string} layer The name of the layer
|
||||
* @param {string} effect The name of the effect
|
||||
* @param {string} parameter The parameter to update
|
||||
* @param {string} value The new value
|
||||
*/
|
||||
gdjs.evtTools.camera.setLayerEffectStringParameter = function(runtimeScene, layer, effect, parameter, value) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).setEffectStringParameter(effect, parameter, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable a layer effect parameter (boolean).
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene
|
||||
* @param {string} layer The name of the layer
|
||||
* @param {string} effect The name of the effect
|
||||
* @param {string} parameter The parameter to update
|
||||
* @param {boolean} value The new value
|
||||
*/
|
||||
gdjs.evtTools.camera.setLayerEffectBooleanParameter = function(runtimeScene, layer, effect, parameter, value) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
return runtimeScene.getLayer(layer).setEffectBooleanParameter(effect, parameter, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable, or disable, an effect of a layer.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene
|
||||
* @param {string} layer The name of the layer
|
||||
* @param {string} effect The name of the effect
|
||||
* @param {boolean} enabled true to enable, false to disable.
|
||||
*/
|
||||
gdjs.evtTools.camera.enableLayerEffect = function(runtimeScene, layer, effect, enabled) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
|
||||
runtimeScene.getLayer(layer).enableEffect(effect, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an effect is enabled.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene
|
||||
* @param {string} layer The name of the layer
|
||||
* @param {string} effect The name of the effect
|
||||
* @return {boolean} true if the effect is enabled, false otherwise.
|
||||
*/
|
||||
gdjs.evtTools.camera.layerEffectEnabled = function(runtimeScene, layer, effect) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return true; }
|
||||
|
||||
return runtimeScene.getLayer(layer).isEffectEnabled(effect);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.setLayerTimeScale = function(runtimeScene, layer, timeScale) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return; }
|
||||
return runtimeScene.getLayer(layer).setTimeScale(timeScale);
|
||||
}
|
||||
|
||||
gdjs.evtTools.camera.getLayerTimeScale = function(runtimeScene, layer) {
|
||||
if ( !runtimeScene.hasLayer(layer) ) { return 1; }
|
||||
return runtimeScene.getLayer(layer).getTimeScale();
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @memberof gdjs.evtTools
|
||||
* @class common
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common = gdjs.evtTools.common || {};
|
||||
|
||||
/**
|
||||
* Get the value of a variable. Equivalent of variable.getAsNumber().
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.getVariableNumber = function(variable) {
|
||||
return variable.getAsNumber();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the string of a variable. Equivalent of variable.getAsString().
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.getVariableString = function(variable) {
|
||||
return variable.getAsString();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.sceneVariableExists = function(
|
||||
runtimeScene,
|
||||
variableName
|
||||
) {
|
||||
return runtimeScene.getVariables().has(variableName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.globalVariableExists = function(
|
||||
runtimeScene,
|
||||
variableName
|
||||
) {
|
||||
return runtimeScene
|
||||
.getGame()
|
||||
.getVariables()
|
||||
.has(variableName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.variableChildExists = function(variable, childName) {
|
||||
return variable.hasChild(childName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.variableRemoveChild = function(variable, childName) {
|
||||
return variable.removeChild(childName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.variableClearChildren = function(variable) {
|
||||
variable.clearChildren();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.getVariableChildCount = function(variable) {
|
||||
if (variable.isStructure() == false) return 0;
|
||||
return Object.keys(variable.getAllChildren()).length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a string to a float.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.toNumber = function(str) {
|
||||
return parseFloat(str);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a number to a string.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.toString = function(num) {
|
||||
//Using String literal is fastest than using toString according to
|
||||
//http://jsperf.com/number-to-string/2 and http://jsben.ch/#/ghQYR
|
||||
return '' + num;
|
||||
};
|
||||
|
||||
/**
|
||||
* Negate the boolean.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.common.logicalNegation = function(bool) {
|
||||
return !bool;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.clamp = function(x, min, max) {
|
||||
return Math.min(Math.max(x, min), max);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.acosh = function(arg) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Onno Marsman
|
||||
return Math.log(arg + Math.sqrt(arg * arg - 1));
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.asinh = function(arg) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Onno Marsman
|
||||
return Math.log(arg + Math.sqrt(arg * arg + 1));
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.atanh = function(arg) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Onno Marsman
|
||||
return 0.5 * Math.log((1 + arg) / (1 - arg));
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.cosh = function(arg) {
|
||||
return (Math.exp(arg) + Math.exp(-arg)) / 2;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.sinh = function(arg) {
|
||||
return (Math.exp(arg) - Math.exp(-arg)) / 2;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.tanh = function(arg) {
|
||||
return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.cot = function(arg) {
|
||||
return 1 / Math.tan(arg);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.csc = function(arg) {
|
||||
return 1 / Math.sin(arg);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.sec = function(arg) {
|
||||
return 1 / Math.cos(arg);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.log10 = function(arg) {
|
||||
return Math.log(arg) / Math.LN10;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.log2 = function(arg) {
|
||||
return Math.log(arg) / Math.LN2;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.sign = function(arg) {
|
||||
if (arg === 0) return 0;
|
||||
|
||||
return arg > 0 ? +1 : -1;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.cbrt = function(x) {
|
||||
return Math.pow(x, 1 / 3);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.nthroot = function(x, n) {
|
||||
return Math.pow(x, 1 / n);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.mod = function(x, y) {
|
||||
return x - y * Math.floor(x / y);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.angleDifference = function(angle1, angle2) {
|
||||
return (
|
||||
gdjs.evtTools.common.mod(
|
||||
gdjs.evtTools.common.mod(angle1 - angle2, 360.0) + 180.0,
|
||||
360.0
|
||||
) - 180.0
|
||||
);
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.lerp = function(a, b, x) {
|
||||
return a + (b - a) * x;
|
||||
};
|
||||
|
||||
gdjs.evtTools.common.trunc = function(x) {
|
||||
return x | 0;
|
||||
};
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tools related to input ( Keyboard, mouse ), for events generated code.
|
||||
*
|
||||
* @memberof gdjs.evtTools
|
||||
* @class input
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.input = gdjs.evtTools.input || {};
|
||||
|
||||
/**
|
||||
* Return true if the specified key is pressed
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.input.isKeyPressed = function(runtimeScene, key) {
|
||||
if (gdjs.evtTools.input.keysNameToCode.hasOwnProperty(key)) {
|
||||
return runtimeScene.getGame().getInputManager().isKeyPressed(gdjs.evtTools.input.keysNameToCode[key]);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key was just released
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.input.wasKeyReleased = function(runtimeScene, key) {
|
||||
if (gdjs.evtTools.input.keysNameToCode.hasOwnProperty(key)) {
|
||||
return runtimeScene.getGame().getInputManager().wasKeyReleased(gdjs.evtTools.input.keysNameToCode[key]);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the name of the last key pressed in the game
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.input.lastPressedKey = function(runtimeScene) {
|
||||
//Ensure _keysCodeToName is constructed
|
||||
if (gdjs.evtTools.input._keysCodeToName === undefined) {
|
||||
gdjs.evtTools.input._keysCodeToName = {};
|
||||
var keysNameToCode = gdjs.evtTools.input.keysNameToCode;
|
||||
for(var p in keysNameToCode) {
|
||||
if (keysNameToCode.hasOwnProperty(p)) {
|
||||
gdjs.evtTools.input._keysCodeToName[keysNameToCode[p]] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var keyCode = runtimeScene.getGame().getInputManager().getLastPressedKey();
|
||||
if (gdjs.evtTools.input._keysCodeToName.hasOwnProperty(keyCode)) {
|
||||
return gdjs.evtTools.input._keysCodeToName[keyCode];
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Hashmap associated each name of a key to its keyCode.
|
||||
* @memberof gdjs.evtTools
|
||||
*/
|
||||
gdjs.evtTools.input.keysNameToCode = {
|
||||
"a": 65,
|
||||
"b": 66,
|
||||
"c": 67,
|
||||
"d": 68,
|
||||
"e": 69,
|
||||
"f": 70,
|
||||
"g": 71,
|
||||
"h": 72,
|
||||
"i": 73,
|
||||
"j": 74,
|
||||
"k": 75,
|
||||
"l": 76,
|
||||
"m": 77,
|
||||
"n": 78,
|
||||
"o": 79,
|
||||
"p": 80,
|
||||
"q": 81,
|
||||
"r": 82,
|
||||
"s": 83,
|
||||
"t": 84,
|
||||
"u": 85,
|
||||
"v": 86,
|
||||
"w": 87,
|
||||
"x": 88,
|
||||
"y": 89,
|
||||
"z": 90,
|
||||
|
||||
"Num0": 48,
|
||||
"Num1": 49,
|
||||
"Num2": 50,
|
||||
"Num3": 51,
|
||||
"Num4": 52,
|
||||
"Num5": 53,
|
||||
"Num6": 54,
|
||||
"Num7": 55,
|
||||
"Num8": 56,
|
||||
"Num9": 57,
|
||||
|
||||
"Numpad0": 96,
|
||||
"Numpad1": 97,
|
||||
"Numpad2": 98,
|
||||
"Numpad3": 99,
|
||||
"Numpad4": 100,
|
||||
"Numpad5": 101,
|
||||
"Numpad6": 102,
|
||||
"Numpad7": 103,
|
||||
"Numpad8": 104,
|
||||
"Numpad9": 105,
|
||||
|
||||
"RControl": 17,
|
||||
"RShift": 16,
|
||||
"RAlt": 18,
|
||||
"LControl": 17,
|
||||
"LShift": 16,
|
||||
"LAlt": 18,
|
||||
"LSystem": 91,
|
||||
"RSystem": 91,
|
||||
/*"Menu": sf::Keyboard::Menu ,
|
||||
"LBracket": sf::Keyboard::LBracket ,
|
||||
"RBracket": sf::Keyboard::RBracket ,
|
||||
"SemiColon": sf::Keyboard::SemiColon ,
|
||||
"Comma": sf::Keyboard::Comma ,
|
||||
"Period": sf::Keyboard::Period ,
|
||||
"Quote": sf::Keyboard::Quote ,
|
||||
"Slash": sf::Keyboard::Slash ,
|
||||
"BackSlash": sf::Keyboard::BackSlash ,
|
||||
"Tilde": sf::Keyboard::Tilde ,
|
||||
"Equal": sf::Keyboard::Equal ,
|
||||
"Dash": sf::Keyboard::Dash,*/
|
||||
"Space": 32,
|
||||
"Return": 13,
|
||||
"Back": 8,
|
||||
"Tab": 9,
|
||||
"PageUp": 33,
|
||||
"PageDown": 34,
|
||||
"End": 35,
|
||||
"Home": 36,
|
||||
"Delete": 46,
|
||||
"Insert": 45,
|
||||
"Escape": 27,
|
||||
|
||||
"Add": 107,
|
||||
"Subtract": 109,
|
||||
"Multiply": 106,
|
||||
"Divide": 111,
|
||||
|
||||
"Left": 37,
|
||||
"Up": 38,
|
||||
"Right": 39,
|
||||
"Down": 40,
|
||||
|
||||
"F1": 112,
|
||||
"F2": 113,
|
||||
"F3": 114,
|
||||
"F4": 115,
|
||||
"F5": 116,
|
||||
"F6": 117,
|
||||
"F7": 118,
|
||||
"F8": 119,
|
||||
"F9": 120,
|
||||
"F10": 121,
|
||||
"F11": 122,
|
||||
"F12": 123,
|
||||
|
||||
"Pause": 19
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.anyKeyPressed = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getInputManager().anyKeyPressed();
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.isMouseButtonPressed = function(runtimeScene, button) {
|
||||
if ( button === "Left" ) return runtimeScene.getGame().getInputManager().isMouseButtonPressed(0);
|
||||
if ( button === "Right" ) return runtimeScene.getGame().getInputManager().isMouseButtonPressed(1);
|
||||
if ( button === "Middle" ) return runtimeScene.getGame().getInputManager().isMouseButtonPressed(2);
|
||||
return false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.isMouseButtonReleased = function(runtimeScene, button) {
|
||||
if ( button === "Left" ) return runtimeScene.getGame().getInputManager().isMouseButtonReleased(0);
|
||||
if ( button === "Right" ) return runtimeScene.getGame().getInputManager().isMouseButtonReleased(1);
|
||||
if ( button === "Middle" ) return runtimeScene.getGame().getInputManager().isMouseButtonReleased(2);
|
||||
return false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.hideCursor = function(runtimeScene) {
|
||||
runtimeScene.getRenderer().hideCursor();
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.showCursor = function(runtimeScene) {
|
||||
runtimeScene.getRenderer().showCursor();
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getMouseWheelDelta = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getInputManager().getMouseWheelDelta();
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.isScrollingUp = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getInputManager().isScrollingUp();
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.isScrollingDown = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getInputManager().isScrollingDown();
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getMouseX = function(runtimeScene, layer, camera) {
|
||||
return runtimeScene.getLayer(layer).convertCoords(
|
||||
runtimeScene.getGame().getInputManager().getMouseX(),
|
||||
runtimeScene.getGame().getInputManager().getMouseY())[0];
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getMouseY = function(runtimeScene, layer, camera) {
|
||||
return runtimeScene.getLayer(layer).convertCoords(
|
||||
runtimeScene.getGame().getInputManager().getMouseX(),
|
||||
runtimeScene.getGame().getInputManager().getMouseY())[1];
|
||||
};
|
||||
|
||||
gdjs.evtTools.input._cursorIsOnObject = function(obj, runtimeScene) {
|
||||
return obj.cursorOnObject(runtimeScene);
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.cursorOnObject = function(objectsLists, runtimeScene, accurate, inverted) {
|
||||
return gdjs.evtTools.object.pickObjectsIf(gdjs.evtTools.input._cursorIsOnObject,
|
||||
objectsLists, inverted, runtimeScene);
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getTouchX = function(runtimeScene, identifier, layer, camera) {
|
||||
return runtimeScene.getLayer(layer).convertCoords(
|
||||
runtimeScene.getGame().getInputManager().getTouchX(identifier),
|
||||
runtimeScene.getGame().getInputManager().getTouchY(identifier))[0];
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getTouchY = function(runtimeScene, identifier, layer, camera) {
|
||||
return runtimeScene.getLayer(layer).convertCoords(
|
||||
runtimeScene.getGame().getInputManager().getTouchX(identifier),
|
||||
runtimeScene.getGame().getInputManager().getTouchY(identifier))[1];
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getLastTouchId = function() {
|
||||
return gdjs.evtTools.input.lastTouchId || 0;
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.getLastEndedTouchId = function() {
|
||||
return gdjs.evtTools.input.lastEndedTouchId || 0;
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.popStartedTouch = function(runtimeScene) {
|
||||
var startedTouchId = runtimeScene.getGame().getInputManager().popStartedTouch();
|
||||
|
||||
if (startedTouchId !== undefined) {
|
||||
gdjs.evtTools.input.lastTouchId = startedTouchId;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.input.popEndedTouch = function(runtimeScene) {
|
||||
var endedTouchId = runtimeScene.getGame().getInputManager().popEndedTouch();
|
||||
|
||||
if (endedTouchId !== undefined) {
|
||||
gdjs.evtTools.input.lastEndedTouchId = endedTouchId;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
gdjs.evtTools.input.touchSimulateMouse = function(runtimeScene, enable) {
|
||||
runtimeScene.getGame().getInputManager().touchSimulateMouse(enable);
|
||||
};
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @memberof gdjs.evtTools
|
||||
* @namespace network
|
||||
*/
|
||||
gdjs.evtTools.network = gdjs.evtTools.network || {};
|
||||
|
||||
gdjs.evtTools.network.sendHttpRequest = function(host, uri, body, method, contentType, responseVar)
|
||||
{
|
||||
try {
|
||||
var xhr;
|
||||
if (typeof XMLHttpRequest !== 'undefined')
|
||||
xhr = new XMLHttpRequest();
|
||||
else {
|
||||
var versions = ["MSXML2.XmlHttp.5.0",
|
||||
"MSXML2.XmlHttp.4.0",
|
||||
"MSXML2.XmlHttp.3.0",
|
||||
"MSXML2.XmlHttp.2.0",
|
||||
"Microsoft.XmlHttp"]
|
||||
|
||||
for(var i = 0, len = versions.length; i < len; i++) {
|
||||
try {
|
||||
xhr = new ActiveXObject(versions[i]);
|
||||
break;
|
||||
}
|
||||
catch(e){}
|
||||
} // end for
|
||||
}
|
||||
|
||||
if ( xhr === undefined ) return;
|
||||
|
||||
xhr.open(method, host+uri, false);
|
||||
xhr.setRequestHeader( "Content-Type", contentType === "" ? "application/x-www-form-urlencoded" : contentType );
|
||||
xhr.send(body);
|
||||
responseVar.setString(xhr.responseText);
|
||||
}
|
||||
catch(e){}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a variable to JSON.
|
||||
* TODO: Move to gdjs.Variable static
|
||||
* @param {gdjs.Variable} variable The variable to convert to JSON
|
||||
* @returns {string} The JSON string representing the variable
|
||||
*/
|
||||
gdjs.evtTools.network.variableStructureToJSON = function(variable)
|
||||
{
|
||||
if ( !variable.isStructure() ) {
|
||||
if ( variable.isNumber() )
|
||||
return JSON.stringify(variable.getAsNumber());
|
||||
else
|
||||
return JSON.stringify(variable.getAsString());
|
||||
}
|
||||
|
||||
var str = "{";
|
||||
var firstChild = true;
|
||||
var children = variable.getAllChildren();
|
||||
for(var p in children) {
|
||||
if (children.hasOwnProperty(p)) {
|
||||
if ( !firstChild ) str += ",";
|
||||
str += JSON.stringify(p) + ": " + gdjs.evtTools.network.variableStructureToJSON(children[p]);
|
||||
|
||||
firstChild = false;
|
||||
}
|
||||
}
|
||||
|
||||
str += "}";
|
||||
return str;
|
||||
};
|
||||
|
||||
gdjs.evtTools.network.objectVariableStructureToJSON = function(object, variable)
|
||||
{
|
||||
return gdjs.evtTools.network.variableStructureToJSON(variable);
|
||||
}
|
||||
|
||||
gdjs.evtTools.network._objectToVariable = function(obj, variable)
|
||||
{
|
||||
if(!isNaN(obj)) { //Number
|
||||
variable.setNumber(obj);
|
||||
}
|
||||
else if (typeof obj == 'string' || obj instanceof String) {
|
||||
variable.setString(obj);
|
||||
}
|
||||
else if ( Array.isArray(obj) ) {
|
||||
for(var i = 0;i<obj.length;++i) {
|
||||
gdjs.evtTools.network._objectToVariable(obj[i], variable.getChild(i.toString()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(var p in obj) {
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
gdjs.evtTools.network._objectToVariable(obj[p], variable.getChild(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given JSON and fill the content of the variable with it
|
||||
* TODO: Move to gdjs.Variable static
|
||||
* @param {string} jsonStr The JSON string
|
||||
* @param {gdjs.Variable} variable The variable where to put the parsed JSON
|
||||
* @returns {boolean} true if JSON was properly parsed
|
||||
*/
|
||||
gdjs.evtTools.network.jsonToVariableStructure = function(jsonStr, variable)
|
||||
{
|
||||
if ( jsonStr.length === 0 ) return false;
|
||||
try {
|
||||
var obj = JSON.parse(jsonStr);
|
||||
gdjs.evtTools.network._objectToVariable(obj, variable);
|
||||
return true;
|
||||
} catch(e) {
|
||||
//Do nothing iF JSON was not properly parsed;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.evtTools.network.jsonToObjectVariableStructure = function(jsonStr, object, variable)
|
||||
{
|
||||
gdjs.evtTools.network.jsonToVariableStructure(jsonStr, variable);
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tools related to objects, for events generated code.
|
||||
* @memberof gdjs.evtTools
|
||||
* @namespace object
|
||||
*/
|
||||
gdjs.evtTools.object = gdjs.evtTools.object || {};
|
||||
|
||||
|
||||
/**
|
||||
* Keep only the specified object in the lists of picked objects.
|
||||
*
|
||||
* @param {Hashtable} objectsLists The lists of objects to trim
|
||||
* @param {gdjs.RuntimeObject} runtimeObject The object to keep in the lists
|
||||
*/
|
||||
gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) {
|
||||
for (var listName in objectsLists.items) {
|
||||
if (objectsLists.items.hasOwnProperty(listName)) {
|
||||
var list = objectsLists.items[listName];
|
||||
|
||||
if (list.indexOf(runtimeObject) === -1) {
|
||||
list.length = 0; //Be sure not to lose the reference to the original array
|
||||
} else {
|
||||
list.length = 0; //Be sure not to lose the reference to the original array
|
||||
list.push(runtimeObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A predicate to be passed to `gdjs.evtTools.object.twoListsTest`.
|
||||
* @callback gdjsTwoListsTestPredicate
|
||||
* @param {gdjs.RuntimeObject} object1 First object
|
||||
* @param {gdjs.RuntimeObject} object2 Second object
|
||||
* @param {*} extraArg An optional extra argument
|
||||
* @return {boolean} true if the pair satisfy the predicate (for example,there is a collision), meaning that the objects will be picked, false otherwise (no collision).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Do a test on two tables of objects so as to pick only the pair of objects for which the test is true.
|
||||
*
|
||||
* Note that the predicate method is not called stricly for each pair: When considering a pair of objects, if
|
||||
* these objects have already been marked as picked, the predicate method won't be called again.
|
||||
*
|
||||
* Cost (Worst case, predicate being always false):
|
||||
* Cost(Setting property 'picked' of NbObjList1+NbObjList2 objects to false)
|
||||
* + Cost(predicate)*NbObjList1*NbObjList2
|
||||
* + Cost(Testing NbObjList1+NbObjList2 booleans)
|
||||
* + Cost(Removing NbObjList1+NbObjList2 objects from all the lists)
|
||||
*
|
||||
* Cost (Best case, predicate being always true):
|
||||
* Cost(Setting property 'picked' of NbObjList1+NbObjList2 objects to false)
|
||||
* + Cost(predicate)*(NbObjList1+NbObjList2)
|
||||
* + Cost(Testing NbObjList1+NbObjList2 booleans)
|
||||
*
|
||||
*
|
||||
* @param {gdjsTwoListsTestPredicate} predicate The predicate function is called with the two objects to compare, and an optional argument `extraArg`
|
||||
* @param {Hashtable} objectsLists1 e.g. Hashtable.newFrom({ A: objects1 });
|
||||
* @param {Hashtable} objectsLists2 e.g. Hashtable.newFrom({ B: objects2 });
|
||||
* @param {boolean} inverted If `inverted` == true, only the objects of the first table are filtered.
|
||||
* @param {*} extraArg (optional) This argument should be used to avoid declaring the predicate as a closure that would be created and destroyed at each call to twoListsTest (potentially multiple time per frame).
|
||||
*/
|
||||
gdjs.evtTools.object.twoListsTest = function(predicate, objectsLists1, objectsLists2, inverted, extraArg) {
|
||||
|
||||
var isTrue = false;
|
||||
var objects1Lists = gdjs.staticArray(gdjs.evtTools.object.twoListsTest);
|
||||
objectsLists1.values(objects1Lists);
|
||||
var objects2Lists = gdjs.staticArray2(gdjs.evtTools.object.twoListsTest);
|
||||
objectsLists2.values(objects2Lists);
|
||||
|
||||
for(var i = 0, leni = objects1Lists.length;i<leni;++i) {
|
||||
var arr = objects1Lists[i];
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
arr[k].pick = false;
|
||||
}
|
||||
}
|
||||
for(var i = 0, leni = objects2Lists.length;i<leni;++i) {
|
||||
var arr = objects2Lists[i];
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
arr[k].pick = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Launch the function for each object of the first list with each object
|
||||
//of the second list.
|
||||
for(var i = 0, leni = objects1Lists.length;i<leni;++i) {
|
||||
var arr1 = objects1Lists[i];
|
||||
|
||||
for(var k = 0, lenk = arr1.length;k<lenk;++k) {
|
||||
var atLeastOneObject = false;
|
||||
|
||||
for(var j = 0, lenj = objects2Lists.length;j<lenj;++j) {
|
||||
var arr2 = objects2Lists[j];
|
||||
|
||||
for(var l = 0, lenl = arr2.length;l<lenl;++l) {
|
||||
if (arr1[k].pick && arr2[l].pick) continue; //Avoid unnecessary costly call to predicate.
|
||||
|
||||
if (arr1[k].id !== arr2[l].id && predicate(arr1[k], arr2[l], extraArg)) {
|
||||
if ( !inverted ) {
|
||||
isTrue = true;
|
||||
|
||||
//Pick the objects
|
||||
arr1[k].pick = true;
|
||||
arr2[l].pick = true;
|
||||
}
|
||||
|
||||
atLeastOneObject = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !atLeastOneObject && inverted ) {
|
||||
//For example, the object is not overlapping any other object.
|
||||
isTrue = true;
|
||||
arr1[k].pick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Trim not picked objects from lists.
|
||||
for(var i = 0, leni = objects1Lists.length;i<leni;++i) {
|
||||
var arr = objects1Lists[i];
|
||||
var finalSize = 0;
|
||||
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
var obj = arr[k];
|
||||
if ( arr[k].pick ) {
|
||||
arr[finalSize] = obj;
|
||||
finalSize++;
|
||||
}
|
||||
}
|
||||
arr.length = finalSize;
|
||||
}
|
||||
|
||||
if ( !inverted ) {
|
||||
for(var i = 0, leni = objects2Lists.length;i<leni;++i) {
|
||||
var arr = objects2Lists[i];
|
||||
var finalSize = 0;
|
||||
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
var obj = arr[k];
|
||||
if ( arr[k].pick ) {
|
||||
arr[finalSize] = obj;
|
||||
finalSize++;
|
||||
}
|
||||
}
|
||||
arr.length = finalSize;
|
||||
}
|
||||
}
|
||||
|
||||
return isTrue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter objects to keep only the one that fullfil the predicate
|
||||
*
|
||||
* Objects that do not fullfil the predicate are removed from objects lists.
|
||||
*
|
||||
* @param {Function} predicate The function applied to each object: must return true if the object fulfill the predicate.
|
||||
* @param {Hashtable} objectsLists The lists of objects to trim
|
||||
* @param {boolean} negatePredicate If set to true, the result of the predicate is negated.
|
||||
* @param {*} extraArg Argument passed to the predicate (along with the object). Useful for avoiding relying on temporary closures.
|
||||
* @return {boolean} true if at least one object fulfill the predicate.
|
||||
*/
|
||||
gdjs.evtTools.object.pickObjectsIf = function(predicate, objectsLists, negatePredicate, extraArg) {
|
||||
var isTrue = false;
|
||||
var lists = gdjs.staticArray(gdjs.evtTools.object.pickObjectsIf);
|
||||
objectsLists.values(lists);
|
||||
|
||||
//Create a boolean for each object
|
||||
for(var i = 0, leni = lists.length;i<leni;++i) {
|
||||
var arr = lists[i];
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
arr[k].pick = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Pick only objects that are fulfilling the predicate
|
||||
for(var i = 0, leni = lists.length;i<leni;++i) {
|
||||
var arr = lists[i];
|
||||
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
if (negatePredicate ^ predicate(arr[k], extraArg)) {
|
||||
isTrue = true;
|
||||
arr[k].pick = true; //Pick the objects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Trim not picked objects from lists.
|
||||
for(var i = 0, leni = lists.length;i<leni;++i) {
|
||||
var arr = lists[i];
|
||||
var finalSize = 0;
|
||||
|
||||
for(var k = 0, lenk = arr.length;k<lenk;++k) {
|
||||
var obj = arr[k];
|
||||
if ( arr[k].pick ) {
|
||||
arr[finalSize] = obj;
|
||||
finalSize++;
|
||||
}
|
||||
}
|
||||
arr.length = finalSize;
|
||||
}
|
||||
|
||||
return isTrue;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.hitBoxesCollisionTest = function(objectsLists1, objectsLists2, inverted, runtimeScene, ignoreTouchingEdges) {
|
||||
return gdjs.evtTools.object.twoListsTest(gdjs.RuntimeObject.collisionTest,
|
||||
objectsLists1, objectsLists2, inverted, ignoreTouchingEdges);
|
||||
};
|
||||
|
||||
gdjs.evtTools.object._distanceBetweenObjects = function(obj1, obj2, distance) {
|
||||
return obj1.getSqDistanceToObject(obj2) <= distance;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.distanceTest = function(objectsLists1, objectsLists2, distance, inverted) {
|
||||
return gdjs.evtTools.object.twoListsTest(gdjs.evtTools.object._distanceBetweenObjects,
|
||||
objectsLists1, objectsLists2, inverted, distance*distance);
|
||||
};
|
||||
|
||||
gdjs.evtTools.object._movesToward = function(obj1, obj2, tolerance) {
|
||||
if ( obj1.hasNoForces() ) return false;
|
||||
|
||||
var objAngle = Math.atan2(obj2.getDrawableY()+obj2.getCenterY() - (obj1.getDrawableY()+obj1.getCenterY()),
|
||||
obj2.getDrawableX()+obj2.getCenterX() - (obj1.getDrawableX()+obj1.getCenterX()));
|
||||
objAngle *= 180/3.14159;
|
||||
|
||||
return Math.abs(gdjs.evtTools.common.angleDifference(obj1.getAverageForce().getAngle(), objAngle)) <= tolerance/2;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.movesTowardTest = function(objectsLists1, objectsLists2, tolerance, inverted) {
|
||||
return gdjs.evtTools.object.twoListsTest(gdjs.evtTools.object._movesToward,
|
||||
objectsLists1, objectsLists2, inverted, tolerance);
|
||||
};
|
||||
|
||||
gdjs.evtTools.object._turnedToward = function(obj1, obj2, tolerance) {
|
||||
var objAngle = Math.atan2(obj2.getDrawableY()+obj2.getCenterY() - (obj1.getDrawableY()+obj1.getCenterY()),
|
||||
obj2.getDrawableX()+obj2.getCenterX() - (obj1.getDrawableX()+obj1.getCenterX()));
|
||||
objAngle *= 180/3.14159;
|
||||
|
||||
return Math.abs(gdjs.evtTools.common.angleDifference(obj1.getAngle(), objAngle)) <= tolerance/2;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.turnedTowardTest = function(objectsLists1, objectsLists2, tolerance, inverted) {
|
||||
return gdjs.evtTools.object.twoListsTest(gdjs.evtTools.object._turnedToward,
|
||||
objectsLists1, objectsLists2, inverted, tolerance);
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.pickAllObjects = function(objectsContext, objectsLists) {
|
||||
|
||||
for (var name in objectsLists.items) {
|
||||
if (objectsLists.items.hasOwnProperty(name)) {
|
||||
var allObjects = objectsContext.getObjects(name);
|
||||
var objectsList = objectsLists.items[name];
|
||||
objectsList.length = 0;
|
||||
objectsList.push.apply(objectsList, allObjects);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.pickRandomObject = function(runtimeScene, objectsLists) {
|
||||
// Compute one many objects we have
|
||||
var objectsCount = 0;
|
||||
for (var listName in objectsLists.items) {
|
||||
if (objectsLists.items.hasOwnProperty(listName)) {
|
||||
var list = objectsLists.items[listName];
|
||||
objectsCount += list.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (objectsCount === 0)
|
||||
return false;
|
||||
|
||||
// Pick one random object
|
||||
var index = Math.floor(Math.random()*objectsCount);
|
||||
if (index >= objectsCount) index = objectsCount-1; //Should never happen.
|
||||
|
||||
// Find the object
|
||||
var startIndex = 0;
|
||||
var theChosenOne = null;
|
||||
for (var listName in objectsLists.items) {
|
||||
if (objectsLists.items.hasOwnProperty(listName)) {
|
||||
var list = objectsLists.items[listName];
|
||||
|
||||
if (index - startIndex < list.length) {
|
||||
theChosenOne = list[index - startIndex];
|
||||
break;
|
||||
}
|
||||
|
||||
startIndex += list.length;
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.evtTools.object.pickOnly(objectsLists, theChosenOne);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.pickNearestObject = function(objectsLists, x, y, inverted) {
|
||||
var bestObject = null;
|
||||
var best = 0;
|
||||
var first = true;
|
||||
var lists = gdjs.staticArray(gdjs.evtTools.object.pickNearestObject);
|
||||
objectsLists.values(lists);
|
||||
for(var i = 0, len = lists.length;i<len;++i) {
|
||||
var list = lists[i];
|
||||
|
||||
for(var j = 0;j < list.length;++j) {
|
||||
var object = list[j];
|
||||
var distance = object.getSqDistanceTo(x, y);
|
||||
if( first || (distance < best ^ inverted)) {
|
||||
best = distance;
|
||||
bestObject = object;
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestObject)
|
||||
return false;
|
||||
|
||||
gdjs.evtTools.object.pickOnly(objectsLists, bestObject);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.raycastObject = function(objectsLists, x, y, angle, dist, varX, varY, inverted) {
|
||||
return gdjs.evtTools.object.raycastObjectToPosition(
|
||||
objectsLists,
|
||||
x, y,
|
||||
x + dist*Math.cos(angle*Math.PI/180.0),
|
||||
y + dist*Math.sin(angle*Math.PI/180.0),
|
||||
varX, varY, inverted);
|
||||
};
|
||||
|
||||
gdjs.evtTools.object.raycastObjectToPosition = function(objectsLists, x, y, endX, endY, varX, varY, inverted) {
|
||||
var matchObject = null;
|
||||
var testSqDist = inverted ? 0 : (endX - x)*(endX - x) + (endY - y)*(endY - y);
|
||||
var resultX = 0;
|
||||
var resultY = 0;
|
||||
|
||||
var lists = gdjs.staticArray(gdjs.evtTools.object.raycastObjectToPosition);
|
||||
objectsLists.values(lists);
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
var list = lists[i];
|
||||
|
||||
for (var j = 0; j < list.length; j++) {
|
||||
var object = list[j];
|
||||
var result = object.raycastTest(x, y, endX, endY, !inverted);
|
||||
|
||||
if( result.collision ) {
|
||||
if ( !inverted && (result.closeSqDist <= testSqDist) ) {
|
||||
testSqDist = result.closeSqDist;
|
||||
matchObject = object;
|
||||
resultX = result.closeX;
|
||||
resultY = result.closeY;
|
||||
}
|
||||
else if ( inverted && (result.farSqDist >= testSqDist) ) {
|
||||
testSqDist = result.farSqDist;
|
||||
matchObject = object;
|
||||
resultX = result.farX;
|
||||
resultY = result.farY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !matchObject )
|
||||
return false;
|
||||
|
||||
gdjs.evtTools.object.pickOnly(objectsLists, matchObject);
|
||||
varX.setNumber(resultX);
|
||||
varY.setNumber(resultY);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do the work of creating a new object
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.object.doCreateObjectOnScene = function(objectsContext, objectName, objectsLists, x, y, layer) {
|
||||
// objectsContext will either be the gdjs.RuntimeScene or, in an events function, the
|
||||
// eventsFunctionContext. We can't directly use runtimeScene because the object name could
|
||||
// be different than the real object name (this is the case in a function. The eventsFunctionContext
|
||||
// will take care of this in createObject).
|
||||
var obj = objectsContext.createObject(objectName);
|
||||
|
||||
if ( obj !== null ) {
|
||||
//Do some extra setup
|
||||
obj.setPosition(x,y);
|
||||
obj.setLayer(layer);
|
||||
|
||||
//Let the new object be picked by next actions/conditions.
|
||||
if ( objectsLists.containsKey(objectName) ) {
|
||||
objectsLists.get(objectName).push(obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows events to create a new object on a scene.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.object.createObjectOnScene = function(objectsContext, objectsLists, x, y, layer) {
|
||||
gdjs.evtTools.object.doCreateObjectOnScene(objectsContext, objectsLists.firstKey(), objectsLists, x, y, layer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows events to create a new object on a scene.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.object.createObjectFromGroupOnScene = function(objectsContext, objectsLists, objectName, x, y, layer) {
|
||||
gdjs.evtTools.object.doCreateObjectOnScene(objectsContext, objectName, objectsLists, x, y, layer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows events to get the number of objects picked.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.object.pickedObjectsCount = function(objectsLists) {
|
||||
var size = 0;
|
||||
var lists = gdjs.staticArray(gdjs.evtTools.object.pickedObjectsCount);
|
||||
objectsLists.values(lists);
|
||||
for(var i = 0, len = lists.length;i<len;++i) {
|
||||
size += lists[i].length;
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tools related to runtime scene, for events generated code.
|
||||
* @memberof gdjs.evtTools
|
||||
* @class runtimeScene
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.runtimeScene = gdjs.evtTools.runtimeScene || {};
|
||||
|
||||
gdjs.evtTools.runtimeScene.sceneJustBegins = function(runtimeScene) {
|
||||
return runtimeScene.getTimeManager().isFirstFrame();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.sceneJustResumed = function(runtimeScene) {
|
||||
return runtimeScene.sceneJustResumed();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getSceneName = function(runtimeScene) {
|
||||
return runtimeScene.getName();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.setBackgroundColor = function(runtimeScene, rgbColor) {
|
||||
|
||||
var colors = rgbColor.split(";");
|
||||
if ( colors.length < 3 ) return;
|
||||
|
||||
runtimeScene.setBackgroundColor(parseInt(colors[0]),
|
||||
parseInt(colors[1]),
|
||||
parseInt(colors[2]));
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getElapsedTimeInSeconds = function(runtimeScene) {
|
||||
return runtimeScene.getTimeManager().getElapsedTime() / 1000;
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.setTimeScale = function(runtimeScene, timeScale) {
|
||||
return runtimeScene.getTimeManager().setTimeScale(timeScale);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getTimeScale = function(runtimeScene) {
|
||||
return runtimeScene.getTimeManager().getTimeScale();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.timerElapsedTime = function(runtimeScene, timeInSeconds, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
if ( !timeManager.hasTimer(timerName) ) {
|
||||
timeManager.addTimer(timerName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return timeManager.getTimer(timerName).getTime() / 1000 >= timeInSeconds;
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.timerPaused = function(runtimeScene, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
if ( !timeManager.hasTimer(timerName) ) return false;
|
||||
|
||||
return timeManager.getTimer(timerName).isPaused();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.resetTimer = function(runtimeScene, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
if ( !timeManager.hasTimer(timerName) )
|
||||
timeManager.addTimer(timerName);
|
||||
else
|
||||
timeManager.getTimer(timerName).reset();
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.pauseTimer = function(runtimeScene, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
if ( !timeManager.hasTimer(timerName) ) timeManager.addTimer(timerName);
|
||||
|
||||
timeManager.getTimer(timerName).setPaused(true);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.unpauseTimer = function(runtimeScene, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
if ( !timeManager.hasTimer(timerName) ) timeManager.addTimer(timerName);
|
||||
|
||||
return timeManager.getTimer(timerName).setPaused(false);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.removeTimer = function(runtimeScene, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
timeManager.removeTimer(timerName);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getTimerElapsedTimeInSeconds = function(runtimeScene, timerName) {
|
||||
var timeManager = runtimeScene.getTimeManager();
|
||||
if (!timeManager.hasTimer(timerName)) return 0;
|
||||
|
||||
return timeManager.getTimer(timerName).getTime() / 1000;
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getTimeFromStartInSeconds = function(runtimeScene) {
|
||||
return runtimeScene.getTimeManager().getTimeFromStart() / 1000;
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.getTime = function(runtimeScene, what) {
|
||||
if ( what === "timestamp" ) {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
|
||||
if ( what === "hour" )
|
||||
return now.getHours();
|
||||
else if ( what === "min" )
|
||||
return now.getMinutes();
|
||||
else if ( what === "sec" )
|
||||
return now.getSeconds();
|
||||
else if ( what === "mday" )
|
||||
return now.getDate();
|
||||
else if ( what === "mon" )
|
||||
return now.getMonth();
|
||||
else if ( what === "year" )
|
||||
return now.getFullYear() - 1900; //Conform to the C way of returning years.
|
||||
else if ( what === "wday" )
|
||||
return now.getDay();
|
||||
else if ( what === "yday" ) {
|
||||
var start = new Date(now.getFullYear(), 0, 0);
|
||||
var diff = now - start;
|
||||
var oneDay = 1000 * 60 * 60 * 24;
|
||||
return Math.floor(diff / oneDay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.replaceScene = function(runtimeScene, newSceneName, clearOthers) {
|
||||
if (!runtimeScene.getGame().getSceneData(newSceneName)) return;
|
||||
|
||||
runtimeScene.requestChange(clearOthers ?
|
||||
gdjs.RuntimeScene.CLEAR_SCENES :
|
||||
gdjs.RuntimeScene.REPLACE_SCENE, newSceneName);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.pushScene = function(runtimeScene, newSceneName) {
|
||||
if (!runtimeScene.getGame().getSceneData(newSceneName)) return;
|
||||
|
||||
runtimeScene.requestChange(gdjs.RuntimeScene.PUSH_SCENE, newSceneName);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.popScene = function(runtimeScene) {
|
||||
runtimeScene.requestChange(gdjs.RuntimeScene.POP_SCENE);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.stopGame = function(runtimeScene) {
|
||||
runtimeScene.requestChange(gdjs.RuntimeScene.STOP_GAME);
|
||||
};
|
||||
|
||||
gdjs.evtTools.runtimeScene.createObjectsFromExternalLayout = function(scene, externalLayout, xPos, yPos) {
|
||||
var externalLayoutData = scene.getGame().getExternalLayoutData(externalLayout);
|
||||
if ( externalLayoutData === null ) return;
|
||||
|
||||
scene.createObjectsFrom(externalLayoutData.instances, xPos, yPos);
|
||||
};
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class used by events to interact with the soundManager.
|
||||
*
|
||||
* @memberof gdjs.evtTools
|
||||
* @class sound
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.sound = gdjs.evtTools.sound || {};
|
||||
|
||||
gdjs.evtTools.sound.getGlobalVolume = function(runtimeScene) {
|
||||
return runtimeScene.getSoundManager().getGlobalVolume();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setGlobalVolume = function(runtimeScene, globalVolume) {
|
||||
runtimeScene.getSoundManager().setGlobalVolume(globalVolume);
|
||||
};
|
||||
|
||||
//Sounds:
|
||||
|
||||
gdjs.evtTools.sound.playSound = function(runtimeScene, soundFile, loop, volume, pitch) {
|
||||
runtimeScene.getSoundManager().playSound(soundFile, loop, volume, pitch);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.playSoundOnChannel = function(runtimeScene, soundFile, channel, loop, volume, pitch) {
|
||||
runtimeScene.getSoundManager().playSoundOnChannel(soundFile, channel, loop, volume, pitch);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.stopSoundOnChannel = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
sound && sound.stop();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.pauseSoundOnChannel = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
sound && sound.pause();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.continueSoundOnChannel = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
if (sound && !sound.playing()) sound.play();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.isSoundOnChannelPlaying = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
return sound ? sound.playing() : false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.isSoundOnChannelPaused = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
return sound ? sound.paused() : false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.isSoundOnChannelStopped = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
return sound ? sound.stopped() : true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.getSoundOnChannelVolume = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
return sound ? sound.volume() * 100 : 100;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setSoundOnChannelVolume = function(runtimeScene, channel, volume) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
sound && sound.volume(volume / 100);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.getSoundOnChannelPlayingOffset = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
return sound ? sound.seek() : 0;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setSoundOnChannelPlayingOffset = function(runtimeScene, channel, playingOffset) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
sound && sound.seek(playingOffset);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.getSoundOnChannelPitch = function(runtimeScene, channel) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
return sound ? sound.getRate() : 1;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setSoundOnChannelPitch = function(runtimeScene, channel, pitch) {
|
||||
var sound = runtimeScene.getSoundManager().getSoundOnChannel(channel);
|
||||
sound && sound.setRate(pitch);
|
||||
};
|
||||
|
||||
//Musics:
|
||||
|
||||
gdjs.evtTools.sound.playMusic = function(runtimeScene, soundFile, loop, volume, pitch) {
|
||||
runtimeScene.getSoundManager().playMusic(soundFile, loop, volume, pitch);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.playMusicOnChannel = function(runtimeScene, soundFile, channel, loop, volume, pitch) {
|
||||
runtimeScene.getSoundManager().playMusicOnChannel(soundFile, channel, loop, volume, pitch);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.stopMusicOnChannel = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
music && music.stop();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.pauseMusicOnChannel = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
music && music.pause();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.continueMusicOnChannel = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
if (music && !music.playing()) music.play();
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.isMusicOnChannelPlaying = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
return music ? music.playing() : false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.isMusicOnChannelPaused = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
return music ? music.paused() : false;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.isMusicOnChannelStopped = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
return music ? music.stopped() : true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.getMusicOnChannelVolume = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
return music ? music.volume() * 100 : 100;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setMusicOnChannelVolume = function(runtimeScene, channel, volume) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
music && music.volume(volume / 100);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.getMusicOnChannelPlayingOffset = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
return music ? music.seek() : 0;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setMusicOnChannelPlayingOffset = function(runtimeScene, channel, playingOffset) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
music && music.seek(playingOffset);
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.getMusicOnChannelPitch = function(runtimeScene, channel) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
return music ? music.getRate() : 1;
|
||||
};
|
||||
|
||||
gdjs.evtTools.sound.setMusicOnChannelPitch = function(runtimeScene, channel, pitch) {
|
||||
var music = runtimeScene.getSoundManager().getMusicOnChannel(channel);
|
||||
music && music.setRate(pitch);
|
||||
};
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tools related to storage, for events generated code.
|
||||
*
|
||||
* @memberof gdjs.evtTools
|
||||
* @namespace storage
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.storage = gdjs.evtTools.storage || {
|
||||
loadedFiles: new Hashtable(),
|
||||
localStorage: typeof cc !== 'undefined' ? cc.sys.localStorage : localStorage,
|
||||
fileUtils: null //Disabled for now
|
||||
};
|
||||
|
||||
/**
|
||||
* Load into memory a JSON object stored in the local storage object
|
||||
* provided by the browser.
|
||||
* The JSON object is named GDJS_filename in the localStorage object.
|
||||
*
|
||||
* @param filename {String} The name of the JSON object
|
||||
*/
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage = function(filename) {
|
||||
if ( gdjs.evtTools.storage.loadedFiles.containsKey(filename) )
|
||||
return; //Already loaded.
|
||||
|
||||
var rawStr = null;
|
||||
if (gdjs.evtTools.storage.fileUtils) {
|
||||
var fileUtils = gdjs.evtTools.storage.fileUtils;
|
||||
|
||||
var fullPath = jsb.fileUtils.getWritablePath() + filename;
|
||||
if (jsb.fileUtils.isFileExist(fullPath)) {
|
||||
rawStr = jsb.fileUtils.getStringFromFile(fullPath);
|
||||
} else {
|
||||
console.log('File "' + filename + '" does not exist.');
|
||||
}
|
||||
} else {
|
||||
var localStorage = gdjs.evtTools.storage.localStorage;
|
||||
rawStr = localStorage.getItem("GDJS_"+filename);
|
||||
}
|
||||
|
||||
try {
|
||||
if ( rawStr !== null )
|
||||
gdjs.evtTools.storage.loadedFiles.put(filename, JSON.parse(rawStr));
|
||||
else
|
||||
gdjs.evtTools.storage.loadedFiles.put(filename, {});
|
||||
}
|
||||
catch(e) {
|
||||
console.log('Unable to load data from "' + filename + '"!');
|
||||
gdjs.evtTools.storage.loadedFiles.put(filename, {});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unload from memory a JSON object, which is then stored in the local storage
|
||||
* object provided by the browser.
|
||||
* The JSON object is named GDJS_filename in the localStorage object.
|
||||
*
|
||||
* @param filename {String} The name of the JSON object
|
||||
*/
|
||||
gdjs.evtTools.storage.unloadJSONFile = function(filename) {
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) )
|
||||
return; //Not loaded.
|
||||
|
||||
var jsonObject = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
var rawStr = JSON.stringify(jsonObject);
|
||||
if (gdjs.evtTools.storage.fileUtils) {
|
||||
var fileUtils = gdjs.evtTools.storage.fileUtils;
|
||||
|
||||
var fullPath = jsb.fileUtils.getWritablePath() + filename;
|
||||
if (!jsb.fileUtils.writeToFile(rawStr, fullPath)) {
|
||||
console.log('Unable to save data to file "' + filename + '"!');
|
||||
}
|
||||
|
||||
} else {
|
||||
var localStorage = gdjs.evtTools.storage.localStorage;
|
||||
try {
|
||||
localStorage.setItem("GDJS_"+filename, rawStr);
|
||||
} catch(e) {
|
||||
//TODO: Handle storage error.
|
||||
console.log('Unable to save data to localStorage for "' + filename + '"!');
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.evtTools.storage.loadedFiles.remove(filename);
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.clearJSONFile = function(filename) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var JSONobject = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for ( var p in JSONobject ) {
|
||||
if ( JSONobject.hasOwnProperty(p) )
|
||||
delete JSONobject[p];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.elementExistsInJSONFile = function(filename, element) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var elemArray = element.split("/");
|
||||
var currentElem = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for (var i =0;i<elemArray.length;++i) {
|
||||
|
||||
if ( !currentElem[elemArray[i]] ) {
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentElem = currentElem[elemArray[i]];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.deleteElementFromJSONFile = function(filename, element) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var elemArray = element.split("/");
|
||||
var currentElem = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for (var i =0;i<elemArray.length;++i) {
|
||||
|
||||
if ( !currentElem[elemArray[i]] ) {
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( i == elemArray.length-1)
|
||||
delete currentElem[elemArray[i]];
|
||||
else
|
||||
currentElem = currentElem[elemArray[i]];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.writeNumberInJSONFile = function(filename, element, val) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var elemArray = element.split("/");
|
||||
var currentElem = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for (var i =0;i<elemArray.length;++i) {
|
||||
|
||||
if ( !currentElem[elemArray[i]] ) currentElem[elemArray[i]] = {};
|
||||
|
||||
if ( i == elemArray.length-1)
|
||||
currentElem[elemArray[i]].value = val;
|
||||
else
|
||||
currentElem = currentElem[elemArray[i]];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.writeStringInJSONFile = function(filename, element, str) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var elemArray = element.split("/");
|
||||
var currentElem = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for (var i =0;i<elemArray.length;++i) {
|
||||
|
||||
if ( !currentElem[elemArray[i]] ) currentElem[elemArray[i]] = {};
|
||||
|
||||
if ( i == elemArray.length-1)
|
||||
currentElem[elemArray[i]].str = str;
|
||||
else
|
||||
currentElem = currentElem[elemArray[i]];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.readNumberFromJSONFile = function(filename, element, runtimeScene, variable) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var elemArray = element.split("/");
|
||||
var currentElem = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for (var i =0;i<elemArray.length;++i) {
|
||||
|
||||
if ( !currentElem[elemArray[i]] ) {
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( i == elemArray.length-1 && typeof currentElem[elemArray[i]].value !== "undefined")
|
||||
variable.setNumber(currentElem[elemArray[i]].value);
|
||||
else
|
||||
currentElem = currentElem[elemArray[i]];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.evtTools.storage.readStringFromJSONFile = function(filename, element, runtimeScene, variable) {
|
||||
var notPermanentlyLoaded = false;
|
||||
if ( !gdjs.evtTools.storage.loadedFiles.containsKey(filename) ) {
|
||||
notPermanentlyLoaded = true;
|
||||
gdjs.evtTools.storage.loadJSONFileFromStorage(filename);
|
||||
}
|
||||
|
||||
var elemArray = element.split("/");
|
||||
var currentElem = gdjs.evtTools.storage.loadedFiles.get(filename);
|
||||
for (var i =0;i<elemArray.length;++i) {
|
||||
|
||||
if ( !currentElem[elemArray[i]] ) {
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( i == elemArray.length-1 && typeof currentElem[elemArray[i]].str !== "undefined")
|
||||
variable.setString(currentElem[elemArray[i]].str);
|
||||
else
|
||||
currentElem = currentElem[elemArray[i]];
|
||||
}
|
||||
|
||||
if ( notPermanentlyLoaded ) gdjs.evtTools.storage.unloadJSONFile(filename);
|
||||
return true;
|
||||
};
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tools related to strings manipulation, for events generated code.
|
||||
*
|
||||
* @memberof gdjs.evtTools
|
||||
* @namespace string
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string = gdjs.evtTools.string || {};
|
||||
|
||||
/**
|
||||
* Return a string containing a new line character.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.newLine = function() {
|
||||
return "\n";
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a character from its codepoint.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.fromCodePoint = function(codePoint) {
|
||||
return String.fromCodePoint(codePoint);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the uppercased version of the string.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.toUpperCase = function(str) {
|
||||
return str.toUpperCase();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the lowercased version of the string.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.toLowerCase = function(str) {
|
||||
return str.toLowerCase()
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new string containing the substring of the original string.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.subStr = function(str, start, len) {
|
||||
if ( start < str.length && start >= 0 )
|
||||
return str.substr(start, len);
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new string containing the character at the specified position.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strAt = function(str, start) {
|
||||
if ( start < str.length && start >= 0 )
|
||||
return str.substr(start, 1);
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the string repeated.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strRepeat = function(str, count) {
|
||||
var result = "";
|
||||
for ( var i = 0; i < count; i++ )
|
||||
result += str;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the string
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strLen = function(str) {
|
||||
return str.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search in a string
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strFind = function(str, what) {
|
||||
return str.indexOf(what);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverse search in a string
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strRFind = function(str, what) {
|
||||
return str.lastIndexOf(what);
|
||||
};
|
||||
|
||||
/**
|
||||
* Search in a string, starting from a specified position.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strFindFrom = function(str, what, pos) {
|
||||
return str.indexOf(what, pos);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverse search in a string, starting from a specified position.
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.string.strRFindFrom = function(str, what, pos) {
|
||||
return str.lastIndexOf(what, pos);
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tools related to window, for events generated code.
|
||||
* @memberof gdjs.evtTools
|
||||
* @class window
|
||||
* @static
|
||||
* @private
|
||||
*/
|
||||
gdjs.evtTools.window = gdjs.evtTools.window || {};
|
||||
|
||||
gdjs.evtTools.window.setMargins = function(runtimeScene, top, right, bottom, left) {
|
||||
runtimeScene.getGame().getRenderer().setMargins(top, right, bottom, left);
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.setFullScreen = function(runtimeScene, enable, keepAspectRatio) {
|
||||
runtimeScene.getGame().getRenderer().keepAspectRatio(keepAspectRatio);
|
||||
runtimeScene.getGame().getRenderer().setFullScreen(enable);
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.setWindowSize = function(runtimeScene, width, height, updateGameResolution) {
|
||||
runtimeScene.getGame().getRenderer().setWindowSize(width, height);
|
||||
if (updateGameResolution) {
|
||||
runtimeScene.getGame().setGameResolutionSize(width, height);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.centerWindow = function(runtimeScene) {
|
||||
runtimeScene.getGame().getRenderer().centerWindow();
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.setGameResolutionSize = function(runtimeScene, width, height) {
|
||||
runtimeScene.getGame().setGameResolutionSize(width, height);
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.setGameResolutionResizeMode = function(runtimeScene, resizeMode) {
|
||||
runtimeScene.getGame().setGameResolutionResizeMode(resizeMode);
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.setAdaptGameResolutionAtRuntime = function(runtimeScene, enable) {
|
||||
runtimeScene.getGame().setAdaptGameResolutionAtRuntime(enable);
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.setWindowTitle = function(runtimeScene, title) {
|
||||
runtimeScene.getGame().getRenderer().setWindowTitle(title);
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.getWindowTitle = function(runtimeScene) {
|
||||
runtimeScene.getGame().getRenderer().getWindowTitle();
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.getWindowInnerWidth = function() {
|
||||
if (gdjs.RuntimeGameRenderer && gdjs.RuntimeGameRenderer.getWindowInnerWidth)
|
||||
return gdjs.RuntimeGameRenderer.getWindowInnerWidth();
|
||||
|
||||
return (typeof window !== "undefined") ? window.innerWidth : 800;
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.getWindowInnerHeight = function() {
|
||||
if (gdjs.RuntimeGameRenderer && gdjs.RuntimeGameRenderer.getWindowInnerHeight)
|
||||
return gdjs.RuntimeGameRenderer.getWindowInnerHeight();
|
||||
|
||||
return (typeof window !== "undefined") ? window.innerHeight : 800;
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.getGameResolutionWidth = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getGameResolutionWidth();
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.getGameResolutionHeight = function(runtimeScene) {
|
||||
return runtimeScene.getGame().getGameResolutionHeight();
|
||||
};
|
||||
|
||||
gdjs.evtTools.window.openURL = function(url, runtimeScene) {
|
||||
return runtimeScene.getGame().getRenderer().openURL(url);
|
||||
};
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-present Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FontFaceObserverFontManager loads fonts (using fontfaceobserver library)
|
||||
* from the game resources (see `loadFonts`), and allow to access to
|
||||
* the font families of the loaded fonts during the game (see `getFontFamily`).
|
||||
*
|
||||
* "@font-face" declarations must be have been added separately in the index.html
|
||||
* (or any CSS file).
|
||||
*
|
||||
* @class FontFaceObserverFontManager
|
||||
* @memberof gdjs
|
||||
* @param {Object} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager = function(resources)
|
||||
{
|
||||
this._resources = resources;
|
||||
this._loadedFontFamily = {}; // Associate font resource names to the loaded font family
|
||||
this._loadedFonts = {}; // Associate font resource names to the resources, for faster access
|
||||
};
|
||||
|
||||
gdjs.FontManager = gdjs.FontFaceObserverFontManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Return the font family associated to the specified font resource name.
|
||||
* The font resource must have been loaded before. If that's not the case,
|
||||
* a font family will be returned but without guarantee of it being loaded (to
|
||||
* keep compatibility with GDevelop 5.0-beta56 and previous).
|
||||
*
|
||||
* @param {string} resourceName The name of the resource to get.
|
||||
* @returns {string} The font family to be used for this font resource,
|
||||
* or "Arial" if `resourceName` is empty.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.getFontFamily = function(resourceName) {
|
||||
if (this._loadedFontFamily[resourceName]) {
|
||||
return this._loadedFontFamily[resourceName];
|
||||
}
|
||||
|
||||
return resourceName ?
|
||||
gdjs.FontFaceObserverFontManager._getFontFamilyFromFilename(resourceName) :
|
||||
'Arial';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the font file associated to the specified font resource name.
|
||||
* The font resource must have been loaded before. If that's not the case,
|
||||
* the resource name will be returned (to
|
||||
* keep compatibility with GDevelop 5.0-beta56 and previous).
|
||||
*
|
||||
* Should only be useful for renderers running on a non HTML5/non browser environment.
|
||||
*
|
||||
* @param {string} resourceName The name of the resource to get.
|
||||
* @returns {string} The file of the font resource.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.getFontFile = function(resourceName) {
|
||||
if (this._loadedFonts[resourceName]) {
|
||||
return this._loadedFonts[resourceName].file || '';
|
||||
}
|
||||
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the font family for a given filename.
|
||||
* Should be kept in sync with the declaration of "@font-face" during exports.
|
||||
*
|
||||
* @private
|
||||
* @param {string} filename The filename of the font
|
||||
* @returns {string} The font family to be used for this font resource.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager._getFontFamilyFromFilename = function(filename) {
|
||||
return "gdjs_font_" + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified resources, so that fonts are loaded and can then be
|
||||
* used by using the font family returned by getFontFamily.
|
||||
* @param onProgress Callback called each time a new file is loaded.
|
||||
* @param onComplete Callback called when loading is done.
|
||||
* @param resources The resources to be loaded. If not specified, will load the resources
|
||||
* specified in the FontFaceObserverFontManager constructor.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.loadFonts = function(onProgress, onComplete, resources) {
|
||||
resources = resources || this._resources;
|
||||
|
||||
//Construct the list of files to be loaded.
|
||||
//For one loaded file, it can have one or more resources
|
||||
//that use it.
|
||||
var filesResources = {};
|
||||
for(var i = 0, len = resources.length;i<len;++i) {
|
||||
var res = resources[i];
|
||||
if ( res.file && res.kind === "font" ) {
|
||||
filesResources[res.file] = filesResources[res.file] ? filesResources[res.file].concat(res) : [res];
|
||||
}
|
||||
}
|
||||
|
||||
var totalCount = Object.keys(filesResources).length;
|
||||
if (totalCount === 0)
|
||||
return onComplete(totalCount); //Nothing to load.
|
||||
|
||||
var loadingCount = 0;
|
||||
var that = this;
|
||||
var onFontLoaded = function(fontFamily, resources) {
|
||||
resources.forEach(function(resource) {
|
||||
that._loadedFontFamily[resource.name] = fontFamily;
|
||||
that._loadedFonts[resource.name] = resource;
|
||||
});
|
||||
|
||||
loadingCount++;
|
||||
onProgress(loadingCount, totalCount);
|
||||
if (loadingCount === totalCount) onComplete(totalCount);
|
||||
}
|
||||
|
||||
Object.keys(filesResources).forEach(function(file) {
|
||||
var fontFamily = gdjs.FontFaceObserverFontManager._getFontFamilyFromFilename(file);
|
||||
var resources = filesResources[file];
|
||||
new FontFaceObserver(fontFamily).load().then(function() {
|
||||
onFontLoaded(fontFamily, resources);
|
||||
}, function() {
|
||||
console.error("Error loading font resource \"" + resources[0].name + "\" (file: " + file + ")");
|
||||
onFontLoaded(fontFamily, resources);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
(function() {
|
||||
var module; //Define an undefined module variable to avoid fontfaceobserver thinking it's used in an environment using require.
|
||||
|
||||
/* Font Face Observer v2.0.13 - © Bram Stein. License: BSD-3-Clause */(function(){'use strict';var f,g=[];function l(a){g.push(a);1==g.length&&f()}function m(){for(;g.length;)g[0](),g.shift()}f=function(){setTimeout(m)};function n(a){this.a=p;this.b=void 0;this.f=[];var b=this;try{a(function(a){q(b,a)},function(a){r(b,a)})}catch(c){r(b,c)}}var p=2;function t(a){return new n(function(b,c){c(a)})}function u(a){return new n(function(b){b(a)})}function q(a,b){if(a.a==p){if(b==a)throw new TypeError;var c=!1;try{var d=b&&b.then;if(null!=b&&"object"==typeof b&&"function"==typeof d){d.call(b,function(b){c||q(a,b);c=!0},function(b){c||r(a,b);c=!0});return}}catch(e){c||r(a,e);return}a.a=0;a.b=b;v(a)}}
|
||||
function r(a,b){if(a.a==p){if(b==a)throw new TypeError;a.a=1;a.b=b;v(a)}}function v(a){l(function(){if(a.a!=p)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0==a.a?"function"==typeof c?e(c.call(void 0,a.b)):e(a.b):1==a.a&&("function"==typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}n.prototype.g=function(a){return this.c(void 0,a)};n.prototype.c=function(a,b){var c=this;return new n(function(d,e){c.f.push([a,b,d,e]);v(c)})};
|
||||
function w(a){return new n(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e==a.length&&b(h)}}var e=0,h=[];0==a.length&&b(h);for(var k=0;k<a.length;k+=1)u(a[k]).c(d(k),c)})}function x(a){return new n(function(b,c){for(var d=0;d<a.length;d+=1)u(a[d]).c(b,c)})};window.Promise||(window.Promise=n,window.Promise.resolve=u,window.Promise.reject=t,window.Promise.race=x,window.Promise.all=w,window.Promise.prototype.then=n.prototype.c,window.Promise.prototype["catch"]=n.prototype.g);}());
|
||||
|
||||
(function(){function l(a,b){document.addEventListener?a.addEventListener("scroll",b,!1):a.attachEvent("scroll",b)}function m(a){document.body?a():document.addEventListener?document.addEventListener("DOMContentLoaded",function c(){document.removeEventListener("DOMContentLoaded",c);a()}):document.attachEvent("onreadystatechange",function k(){if("interactive"==document.readyState||"complete"==document.readyState)document.detachEvent("onreadystatechange",k),a()})};function r(a){this.a=document.createElement("div");this.a.setAttribute("aria-hidden","true");this.a.appendChild(document.createTextNode(a));this.b=document.createElement("span");this.c=document.createElement("span");this.h=document.createElement("span");this.f=document.createElement("span");this.g=-1;this.b.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.c.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";
|
||||
this.f.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.h.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;max-width:none;";this.b.appendChild(this.h);this.c.appendChild(this.f);this.a.appendChild(this.b);this.a.appendChild(this.c)}
|
||||
function t(a,b){a.a.style.cssText="max-width:none;min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;white-space:nowrap;font-synthesis:none;font:"+b+";"}function y(a){var b=a.a.offsetWidth,c=b+100;a.f.style.width=c+"px";a.c.scrollLeft=c;a.b.scrollLeft=a.b.scrollWidth+100;return a.g!==b?(a.g=b,!0):!1}function z(a,b){function c(){var a=k;y(a)&&a.a.parentNode&&b(a.g)}var k=a;l(a.b,c);l(a.c,c);y(a)};function A(a,b){var c=b||{};this.family=a;this.style=c.style||"normal";this.weight=c.weight||"normal";this.stretch=c.stretch||"normal"}var B=null,C=null,E=null,F=null;function G(){if(null===C)if(J()&&/Apple/.test(window.navigator.vendor)){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent);C=!!a&&603>parseInt(a[1],10)}else C=!1;return C}function J(){null===F&&(F=!!document.fonts);return F}
|
||||
function K(){if(null===E){var a=document.createElement("div");try{a.style.font="condensed 100px sans-serif"}catch(b){}E=""!==a.style.font}return E}function L(a,b){return[a.style,a.weight,K()?a.stretch:"","100px",b].join(" ")}
|
||||
A.prototype.load=function(a,b){var c=this,k=a||"BESbswy",q=0,D=b||3E3,H=(new Date).getTime();return new Promise(function(a,b){if(J()&&!G()){var M=new Promise(function(a,b){function e(){(new Date).getTime()-H>=D?b():document.fonts.load(L(c,'"'+c.family+'"'),k).then(function(c){1<=c.length?a():setTimeout(e,25)},function(){b()})}e()}),N=new Promise(function(a,c){q=setTimeout(c,D)});Promise.race([N,M]).then(function(){clearTimeout(q);a(c)},function(){b(c)})}else m(function(){function u(){var b;if(b=-1!=
|
||||
f&&-1!=g||-1!=f&&-1!=h||-1!=g&&-1!=h)(b=f!=g&&f!=h&&g!=h)||(null===B&&(b=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent),B=!!b&&(536>parseInt(b[1],10)||536===parseInt(b[1],10)&&11>=parseInt(b[2],10))),b=B&&(f==v&&g==v&&h==v||f==w&&g==w&&h==w||f==x&&g==x&&h==x)),b=!b;b&&(d.parentNode&&d.parentNode.removeChild(d),clearTimeout(q),a(c))}function I(){if((new Date).getTime()-H>=D)d.parentNode&&d.parentNode.removeChild(d),b(c);else{var a=document.hidden;if(!0===a||void 0===a)f=e.a.offsetWidth,
|
||||
g=n.a.offsetWidth,h=p.a.offsetWidth,u();q=setTimeout(I,50)}}var e=new r(k),n=new r(k),p=new r(k),f=-1,g=-1,h=-1,v=-1,w=-1,x=-1,d=document.createElement("div");d.dir="ltr";t(e,L(c,"sans-serif"));t(n,L(c,"serif"));t(p,L(c,"monospace"));d.appendChild(e.a);d.appendChild(n.a);d.appendChild(p.a);document.body.appendChild(d);v=e.a.offsetWidth;w=n.a.offsetWidth;x=p.a.offsetWidth;I();z(e,function(a){f=a;u()});t(e,L(c,'"'+c.family+'",sans-serif'));z(n,function(a){g=a;u()});t(n,L(c,'"'+c.family+'",serif'));
|
||||
z(p,function(a){h=a;u()});t(p,L(c,'"'+c.family+'",monospace'))})})};"object"===typeof module?module.exports=A:(window.FontFaceObserver=A,window.FontFaceObserver.prototype.load=A.prototype.load);}());
|
||||
})()
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A vector used to move objects.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class Force
|
||||
* @param {number} x The initial x component
|
||||
* @param {number} y The initial y component
|
||||
* @param {number} multiplier The multiplier (0 for a force that disappear on next frame, 1 for a permanent force)
|
||||
*/
|
||||
gdjs.Force = function(x,y, multiplier)
|
||||
{
|
||||
this._x = x || 0;
|
||||
this._y = y || 0;
|
||||
this._angle = Math.atan2(y,x)*180/Math.PI;
|
||||
this._length = Math.sqrt(x*x+y*y);
|
||||
this._dirty = false;
|
||||
this._multiplier = multiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the X component of the force.
|
||||
*/
|
||||
gdjs.Force.prototype.getX = function() {
|
||||
return this._x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y component of the force.
|
||||
*/
|
||||
gdjs.Force.prototype.getY = function() {
|
||||
return this._y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the x component of the force.
|
||||
* @param {number} x The new X component
|
||||
*/
|
||||
gdjs.Force.prototype.setX = function(x) {
|
||||
this._x = x;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the y component of the force.
|
||||
* @param {number} y The new Y component
|
||||
*/
|
||||
gdjs.Force.prototype.setY = function(y) {
|
||||
this._y = y;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the angle of the force.
|
||||
* @param {number} angle The new angle
|
||||
*/
|
||||
gdjs.Force.prototype.setAngle = function(angle) {
|
||||
|
||||
if ( this._dirty ) {
|
||||
this._length = Math.sqrt(this._x*this._x+this._y*this._y);
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
this._angle = angle;
|
||||
var angleInRadians = angle/180*Math.PI;
|
||||
this._x = Math.cos(angleInRadians)*this._length;
|
||||
this._y = Math.sin(angleInRadians)*this._length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the length of the force.
|
||||
* @param {number} len The length
|
||||
*/
|
||||
gdjs.Force.prototype.setLength = function(len) {
|
||||
|
||||
if ( this._dirty ) {
|
||||
this._angle = Math.atan2(this._y, this._x)*180/Math.PI;
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
this._length = len;
|
||||
var angleInRadians = this._angle/180*Math.PI;
|
||||
this._x = Math.cos(angleInRadians)*this._length;
|
||||
this._y = Math.sin(angleInRadians)*this._length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the angle of the force
|
||||
*/
|
||||
gdjs.Force.prototype.getAngle = function() {
|
||||
if ( this._dirty ) {
|
||||
this._angle = Math.atan2(this._y, this._x)*180/Math.PI;
|
||||
this._length = Math.sqrt(this._x*this._x+this._y*this._y);
|
||||
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
return this._angle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the length of the force
|
||||
*/
|
||||
gdjs.Force.prototype.getLength = function() {
|
||||
if ( this._dirty ) {
|
||||
this._angle = Math.atan2(this._y, this._x)*180/Math.PI;
|
||||
this._length = Math.sqrt(this._x*this._x+this._y*this._y);
|
||||
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
return this._length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return 1 (true) if the force is permanent, 0 (false) if it is instant.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.Force.prototype.getMultiplier = function() {
|
||||
return this._multiplier;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set if the force multiplier.
|
||||
* @param {number} multiplier The new value
|
||||
*/
|
||||
gdjs.Force.prototype.setMultiplier = function(multiplier) {
|
||||
this._multiplier = multiplier;
|
||||
};
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The `gdjs` namespace contains all classes and objects of the game engine.
|
||||
* @namespace
|
||||
*/
|
||||
window.gdjs = {
|
||||
objectsTypes: new Hashtable(),
|
||||
behaviorsTypes: new Hashtable(),
|
||||
/**
|
||||
* Contains functions used by events (this is a convention only, functions can actually
|
||||
* by anywhere).
|
||||
* @namespace
|
||||
* @memberOf gdjs
|
||||
*/
|
||||
evtTools: {},
|
||||
callbacksFirstRuntimeSceneLoaded: [],
|
||||
callbacksRuntimeSceneLoaded: [],
|
||||
callbacksRuntimeScenePreEvents: [],
|
||||
callbacksRuntimeScenePostEvents: [],
|
||||
callbacksRuntimeScenePaused: [],
|
||||
callbacksRuntimeSceneResumed: [],
|
||||
callbacksRuntimeSceneUnloading: [],
|
||||
callbacksRuntimeSceneUnloaded: [],
|
||||
callbacksObjectDeletedFromScene: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a rgb color value to a hex string.
|
||||
*
|
||||
* No "#" or "0x" are added.
|
||||
* @param {number} r Red
|
||||
* @param {number} g Green
|
||||
* @param {number} b Blue
|
||||
* @returns {string}
|
||||
*/
|
||||
gdjs.rgbToHex = function(r, g, b) {
|
||||
return '' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a rgb color value to a hex value.
|
||||
* @param {number} r Red
|
||||
* @param {number} g Green
|
||||
* @param {number} b Blue
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.rgbToHexNumber = function(r, g, b) {
|
||||
return (r << 16) + (g << 8) + b;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random integer between 0 and max.
|
||||
* @param {number} max The maximum value (inclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.random = function(max) {
|
||||
if (max <= 0) return 0;
|
||||
return Math.floor(Math.random() * (max + 1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random integer between min and max.
|
||||
* @param {number} min The minimum value (inclusive).
|
||||
* @param {number} max The maximum value (inclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomInRange = function(min, max) {
|
||||
return min + gdjs.random(max - min); // return min if min >= max
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random float in the range 0 to less than max (inclusive of 0, but not max).
|
||||
* @param {number} max The maximum value (exclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomFloat = function(max) {
|
||||
if (max <= 0) return 0;
|
||||
return Math.random() * max;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random float between min and max
|
||||
* @param {number} min The minimum value (inclusive).
|
||||
* @param {number} max The maximum value (exclusive).
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomFloatInRange = function(min, max) {
|
||||
return min + gdjs.randomFloat(max - min); // return min if min >= max
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a random number between min and max in steps
|
||||
* @param {number} min The minimum value (inclusive).
|
||||
* @param {number} max The maximum value (inclusive).
|
||||
* @param {number} step The interval between each value.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.randomWithStep = function(min, max, step) {
|
||||
if (step <= 0) return min + gdjs.random(max - min);
|
||||
return min + gdjs.random(Math.floor((max - min) / step)) * step; // return min if min >= max
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an angle in degrees to radians.
|
||||
* @param {number} angleInDegrees The angle in degrees.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.toRad = function(angleInDegrees) {
|
||||
return (angleInDegrees / 180) * 3.14159;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an angle in radians to degrees.
|
||||
* @param {number} angleInRadians The angle in radians.
|
||||
* @returns {number}
|
||||
*/
|
||||
gdjs.toDegrees = function(angleInRadians) {
|
||||
return (angleInRadians * 180) / 3.14159;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Constructor for a {@link gdjs.RuntimeObject}.
|
||||
* @name RuntimeObjectConstructor
|
||||
* @function
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The {@link gdjs.RuntimeScene} the object belongs to.
|
||||
* @param {ObjectData} objectData The initial properties of the object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a runtime object (class extending {@link gdjs.RuntimeObject}) that can be used in a scene.
|
||||
*
|
||||
* The name of the type of the object must be complete, with the namespace if any. For
|
||||
* example, if you are providing a Text object in the TextObject extension, the full name
|
||||
* of the type of the object is "TextObject::Text".
|
||||
*
|
||||
* @param {string} objectTypeName The name of the type of the Object.
|
||||
* @param {RuntimeObjectConstructor} Ctor The constructor of the Object.
|
||||
*/
|
||||
gdjs.registerObject = function(objectTypeName, Ctor) {
|
||||
gdjs.objectsTypes.put(objectTypeName, Ctor);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Constructor for a {@link gdjs.RuntimeBehavior}.
|
||||
* @name RuntimeBehaviorConstructor
|
||||
* @function
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene owning the object of the behavior
|
||||
* @param {BehaviorData} behaviorData The properties used to setup the behavior
|
||||
* @param {gdjs.RuntimeObject} owner The object owning the behavior
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a runtime behavior (class extending {@link gdjs.RuntimeBehavior}) that can be used by a
|
||||
* {@link gdjs.RuntimeObject}.
|
||||
*
|
||||
* The type of the behavior must be complete, with the namespace of the extension. For
|
||||
* example, if you are providing a Draggable behavior in the DraggableBehavior extension,
|
||||
* the full name of the type of the behavior is "DraggableBehavior::Draggable".
|
||||
*
|
||||
* @param {string} behaviorTypeName The name of the type of the behavior.
|
||||
* @param {RuntimeBehaviorConstructor} Ctor The constructor of the Object.
|
||||
*/
|
||||
gdjs.registerBehavior = function(behaviorTypeName, Ctor) {
|
||||
gdjs.behaviorsTypes.put(behaviorTypeName, Ctor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when the first {@link gdjs.RuntimeScene} is loaded, after
|
||||
* resources loading is done. This can be considered as the "start of the game".
|
||||
*
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback = function(callback) {
|
||||
gdjs.callbacksFirstRuntimeSceneLoaded.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when a scene is loaded.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneLoadedCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeSceneLoaded.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called each time a scene is stepped (i.e: at every frame),
|
||||
* before events are run.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeScenePreEventsCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeScenePreEvents.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called each time a scene is stepped (i.e: at every frame),
|
||||
* after events are run and before rendering.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeScenePostEventsCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeScenePostEvents.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when a scene is paused.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeScenePausedCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeScenePaused.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when a scene is resumed.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneResumedCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeSceneResumed.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when a scene unload started. This is
|
||||
* before the object deletion and renderer destruction. It is safe to
|
||||
* manipulate these. It is **not** be safe to release resources as other
|
||||
* callbacks might do operations on objects or the scene.
|
||||
*
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneUnloadingCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeSceneUnloading.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when a scene is unloaded. The objects
|
||||
* and renderer are now destroyed - it is **not** safe to do anything apart
|
||||
* from releasing resources.
|
||||
*
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerRuntimeSceneUnloadedCallback = function(callback) {
|
||||
gdjs.callbacksRuntimeSceneUnloaded.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a function to be called when an object is deleted from a scene.
|
||||
* @param {Function} callback The function to be called.
|
||||
*/
|
||||
gdjs.registerObjectDeletedFromSceneCallback = function(callback) {
|
||||
gdjs.callbacksObjectDeletedFromScene.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Keep this function until we're sure now client is using it anymore.
|
||||
* @deprecated
|
||||
* @private
|
||||
*/
|
||||
gdjs.registerGlobalCallbacks = function() {
|
||||
console.warning(
|
||||
"You're calling gdjs.registerGlobalCallbacks. This method is now useless and you must not call it anymore."
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all the global callbacks that were registered previously.
|
||||
*
|
||||
* Should only be used for testing - this should never be used at runtime.
|
||||
*/
|
||||
gdjs.clearGlobalCallbacks = function() {
|
||||
gdjs.callbacksFirstRuntimeSceneLoaded = [];
|
||||
gdjs.callbacksRuntimeSceneLoaded = [];
|
||||
gdjs.callbacksRuntimeScenePreEvents = [];
|
||||
gdjs.callbacksRuntimeScenePostEvents = [];
|
||||
gdjs.callbacksRuntimeScenePaused = [];
|
||||
gdjs.callbacksRuntimeSceneResumed = [];
|
||||
gdjs.callbacksRuntimeSceneUnloading = [];
|
||||
gdjs.callbacksRuntimeSceneUnloaded = [];
|
||||
gdjs.callbacksObjectDeletedFromScene = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the constructor of an object.
|
||||
*
|
||||
* @param {string} name The name of the type of the object.
|
||||
* @returns {ObjectCtor}
|
||||
*/
|
||||
gdjs.getObjectConstructor = function(name) {
|
||||
if (name !== undefined && gdjs.objectsTypes.containsKey(name))
|
||||
return gdjs.objectsTypes.get(name);
|
||||
|
||||
console.warn('Object type "' + name + '" was not found.');
|
||||
return gdjs.objectsTypes.get(''); //Create a base empty runtime object.
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the constructor of a behavior.
|
||||
*
|
||||
* @param {string} name The name of the type of the behavior.
|
||||
* @returns {BehaviorCtor}
|
||||
*/
|
||||
gdjs.getBehaviorConstructor = function(name) {
|
||||
if (name !== undefined && gdjs.behaviorsTypes.containsKey(name))
|
||||
return gdjs.behaviorsTypes.get(name);
|
||||
|
||||
console.warn('Behavior type "' + name + '" was not found.');
|
||||
return gdjs.behaviorsTypes.get(''); //Create a base empty runtime behavior.
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a static array that won't need a new allocation each time it's used.
|
||||
* @param {any} owner The owner of the Array.
|
||||
* @returns {Array<any>}
|
||||
*/
|
||||
gdjs.staticArray = function(owner) {
|
||||
owner._staticArray = owner._staticArray || [];
|
||||
return owner._staticArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a second static array that won't need a new allocation each time it's used.
|
||||
* @param {any} owner The owner of the Array.
|
||||
* @returns {Array<any>}
|
||||
*/
|
||||
gdjs.staticArray2 = function(owner) {
|
||||
owner._staticArray2 = owner._staticArray2 || [];
|
||||
return owner._staticArray2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a static object that won't need a new allocation each time it's used.
|
||||
* @param {any} owner The owner of the Array.
|
||||
* @returns {Object}
|
||||
*/
|
||||
gdjs.staticObject = function(owner) {
|
||||
owner._staticObject = owner._staticObject || {};
|
||||
return owner._staticObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new array of objects that is the concatenation of all the objects passed
|
||||
* as parameters.
|
||||
* @param objectsLists
|
||||
* @returns {Array}
|
||||
*/
|
||||
gdjs.objectsListsToArray = function(objectsLists) {
|
||||
var lists = gdjs.staticArray(gdjs.objectsListsToArray);
|
||||
objectsLists.values(lists);
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < lists.length; ++i) {
|
||||
var arr = lists[i];
|
||||
for (var k = 0; k < arr.length; ++k) {
|
||||
result.push(arr[k]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Array.prototype.remove = function(from) {
|
||||
//Adapted from the nice article available at
|
||||
//https://www.scirra.com/blog/76/how-to-write-low-garbage-real-time-javascript
|
||||
for (var i = from, len = this.length - 1; i < len; i++) this[i] = this[i + 1];
|
||||
|
||||
this.length = len;
|
||||
};
|
||||
|
||||
Array.prototype.createFrom = function(arr) {
|
||||
var len = arr.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
this[i] = arr[i];
|
||||
}
|
||||
this.length = len;
|
||||
};
|
||||
|
||||
//Make sure console.warn and console.error are available.
|
||||
console.warn = console.warn || console.log;
|
||||
console.error = console.error || console.log;
|
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-present Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A thin wrapper around a Howl object with:
|
||||
* * Extra methods `paused`, `stopped`, `getRate`/`setRate` and `canBeDestroyed` methods.
|
||||
* * Automatic clamping when calling `setRate` to ensure a valid value is passed to Howler.js.
|
||||
*
|
||||
* See https://github.com/goldfire/howler.js#methods for the full documentation.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class HowlerSound
|
||||
*/
|
||||
gdjs.HowlerSound = function(o) {
|
||||
Howl.call(this, o);
|
||||
this._paused = false;
|
||||
this._stopped = true;
|
||||
this._canBeDestroyed = false;
|
||||
this._rate = o.rate || 1;
|
||||
|
||||
//Add custom events listener to keep
|
||||
//track of the sound status.
|
||||
var that = this;
|
||||
this.on('end', function() {
|
||||
if (!that.loop()) {
|
||||
that._canBeDestroyed = true;
|
||||
that._paused = false;
|
||||
that._stopped = true;
|
||||
}
|
||||
});
|
||||
this.on('playerror', function(id, error) {
|
||||
console.error(
|
||||
"Can't play a sound, considering it as stopped. Error is:",
|
||||
error
|
||||
);
|
||||
that._paused = false;
|
||||
that._stopped = true;
|
||||
});
|
||||
|
||||
// Track play/pause event to be sure the status is
|
||||
// sync'ed with the sound - though this should be redundant
|
||||
// with `play`/`pause` methods already doing that. Keeping
|
||||
// that to be sure that the status is always correct.
|
||||
this.on('play', function() {
|
||||
that._paused = false;
|
||||
that._stopped = false;
|
||||
});
|
||||
this.on('pause', function() {
|
||||
that._paused = true;
|
||||
that._stopped = false;
|
||||
});
|
||||
};
|
||||
gdjs.HowlerSound.prototype = Object.create(Howl.prototype);
|
||||
|
||||
// Redefine `stop`/`play`/`pause` to ensure the status of the sound
|
||||
// is immediately updated (so that calling `stopped` just after
|
||||
// `play` will return false).
|
||||
|
||||
gdjs.HowlerSound.prototype.stop = function() {
|
||||
this._paused = false;
|
||||
this._stopped = true;
|
||||
return Howl.prototype.stop.call(this);
|
||||
};
|
||||
gdjs.HowlerSound.prototype.play = function() {
|
||||
this._paused = false;
|
||||
this._stopped = false;
|
||||
return Howl.prototype.play.call(this);
|
||||
};
|
||||
gdjs.HowlerSound.prototype.pause = function() {
|
||||
this._paused = true;
|
||||
this._stopped = false;
|
||||
return Howl.prototype.pause.call(this);
|
||||
};
|
||||
|
||||
// Add methods to query the status of the sound:
|
||||
|
||||
gdjs.HowlerSound.prototype.paused = function() {
|
||||
return this._paused;
|
||||
};
|
||||
gdjs.HowlerSound.prototype.stopped = function() {
|
||||
return this._stopped;
|
||||
};
|
||||
gdjs.HowlerSound.prototype.canBeDestroyed = function() {
|
||||
return this._canBeDestroyed;
|
||||
};
|
||||
|
||||
// Methods to safely update the rate of the sound:
|
||||
|
||||
gdjs.HowlerSound.prototype.getRate = function() {
|
||||
return this._rate;
|
||||
};
|
||||
gdjs.HowlerSound.prototype.setRate = function(rate) {
|
||||
this._rate = gdjs.HowlerSoundManager.clampRate(rate);
|
||||
this.rate(this._rate);
|
||||
};
|
||||
|
||||
/**
|
||||
* HowlerSoundManager is used to manage the sounds and musics of a RuntimeScene.
|
||||
*
|
||||
* It is basically a container to associate channels to sounds and keep a list
|
||||
* of all sounds being played.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class HowlerSoundManager
|
||||
*/
|
||||
gdjs.HowlerSoundManager = function(resources) {
|
||||
this._resources = resources;
|
||||
this._availableResources = {}; //Map storing "audio" resources for faster access.
|
||||
|
||||
this._globalVolume = 100;
|
||||
|
||||
this._sounds = {};
|
||||
this._musics = {};
|
||||
this._freeSounds = []; //Sounds without an assigned channel.
|
||||
this._freeMusics = []; //Musics without an assigned channel.
|
||||
|
||||
this._pausedSounds = [];
|
||||
this._paused = false;
|
||||
|
||||
var that = this;
|
||||
this._checkForPause = function() {
|
||||
if (that._paused) {
|
||||
this.pause();
|
||||
that._pausedSounds.push(this);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('deviceready', function() {
|
||||
// pause/resume sounds in Cordova when the app is being paused/resumed
|
||||
document.addEventListener(
|
||||
'pause',
|
||||
function() {
|
||||
var soundList = that._freeSounds.concat(that._freeMusics);
|
||||
for (var key in that._sounds) {
|
||||
if (that._sounds.hasOwnProperty(key)) {
|
||||
soundList.push(that._sounds[key]);
|
||||
}
|
||||
}
|
||||
for (var key in that._musics) {
|
||||
if (that._musics.hasOwnProperty(key)) {
|
||||
soundList.push(that._musics[key]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < soundList.length; i++) {
|
||||
var sound = soundList[i];
|
||||
if (!sound.paused() && !sound.stopped()) {
|
||||
sound.pause();
|
||||
that._pausedSounds.push(sound);
|
||||
}
|
||||
}
|
||||
that._paused = true;
|
||||
},
|
||||
false
|
||||
);
|
||||
document.addEventListener(
|
||||
'resume',
|
||||
function() {
|
||||
for (var i = 0; i < that._pausedSounds.length; i++) {
|
||||
var sound = that._pausedSounds[i];
|
||||
if (!sound.stopped()) {
|
||||
sound.play();
|
||||
}
|
||||
}
|
||||
that._pausedSounds.length = 0;
|
||||
that._paused = false;
|
||||
},
|
||||
false
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
gdjs.SoundManager = gdjs.HowlerSoundManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Ensure rate is in a range valid for Howler.js
|
||||
* @return The clamped rate
|
||||
* @private
|
||||
*/
|
||||
gdjs.HowlerSoundManager.clampRate = function(rate) {
|
||||
if (rate > 4.0) return 4.0;
|
||||
if (rate < 0.5) return 0.5;
|
||||
|
||||
return rate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the file associated to the given sound name.
|
||||
*
|
||||
* Names and files are loaded from resources when preloadAudio is called. If no
|
||||
* file is associated to the given name, then the name will be considered as a
|
||||
* filename and will be returned.
|
||||
*
|
||||
* @private
|
||||
* @return The associated filename
|
||||
*/
|
||||
gdjs.HowlerSoundManager.prototype._getFileFromSoundName = function(soundName) {
|
||||
if (
|
||||
this._availableResources.hasOwnProperty(soundName) &&
|
||||
this._availableResources[soundName].file
|
||||
) {
|
||||
return this._availableResources[soundName].file;
|
||||
}
|
||||
|
||||
return soundName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the sound in the specified array, put it at the first index that
|
||||
* is free, or add it at the end if no element is free
|
||||
* ("free" means that the gdjs.HowlerSound can be destroyed).
|
||||
*
|
||||
* @param {Array} arr The array containing the sounds.
|
||||
* @param {gdjs.HowlerSound} arr The gdjs.HowlerSound to add.
|
||||
* @return The gdjs.HowlerSound that have been added (i.e: the second parameter).
|
||||
* @private
|
||||
*/
|
||||
gdjs.HowlerSoundManager.prototype._storeSoundInArray = function(arr, sound) {
|
||||
//Try to recycle an old sound.
|
||||
var index = null;
|
||||
for (var i = 0, len = arr.length; i < len; ++i) {
|
||||
if (arr[i] !== null && arr[i].canBeDestroyed()) {
|
||||
arr[index] = sound;
|
||||
return sound;
|
||||
}
|
||||
}
|
||||
|
||||
arr.push(sound);
|
||||
return sound;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playSound = function(
|
||||
soundName,
|
||||
loop,
|
||||
volume,
|
||||
pitch
|
||||
) {
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
|
||||
var sound = new gdjs.HowlerSound({
|
||||
src: [soundFile], //TODO: ogg, mp3...
|
||||
loop: loop,
|
||||
volume: volume / 100,
|
||||
rate: gdjs.HowlerSoundManager.clampRate(pitch),
|
||||
});
|
||||
|
||||
this._storeSoundInArray(this._freeSounds, sound).play();
|
||||
|
||||
sound.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playSoundOnChannel = function(
|
||||
soundName,
|
||||
channel,
|
||||
loop,
|
||||
volume,
|
||||
pitch
|
||||
) {
|
||||
var oldSound = this._sounds[channel];
|
||||
if (oldSound) {
|
||||
oldSound.unload();
|
||||
}
|
||||
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
|
||||
var sound = new gdjs.HowlerSound({
|
||||
src: [soundFile], //TODO: ogg, mp3...
|
||||
loop: loop,
|
||||
volume: volume / 100,
|
||||
rate: gdjs.HowlerSoundManager.clampRate(pitch),
|
||||
});
|
||||
|
||||
sound.play();
|
||||
this._sounds[channel] = sound;
|
||||
|
||||
sound.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getSoundOnChannel = function(channel) {
|
||||
return this._sounds[channel];
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playMusic = function(
|
||||
soundName,
|
||||
loop,
|
||||
volume,
|
||||
pitch
|
||||
) {
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
|
||||
var sound = new gdjs.HowlerSound({
|
||||
src: [soundFile], //TODO: ogg, mp3...
|
||||
loop: loop,
|
||||
html5: true, //Force HTML5 audio so we don't wait for the full file to be loaded on Android.
|
||||
volume: volume / 100,
|
||||
rate: gdjs.HowlerSoundManager.clampRate(pitch),
|
||||
});
|
||||
|
||||
this._storeSoundInArray(this._freeMusics, sound).play();
|
||||
|
||||
sound.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.playMusicOnChannel = function(
|
||||
soundName,
|
||||
channel,
|
||||
loop,
|
||||
volume,
|
||||
pitch
|
||||
) {
|
||||
var oldMusic = this._musics[channel];
|
||||
if (oldMusic) {
|
||||
oldMusic.unload();
|
||||
}
|
||||
|
||||
var soundFile = this._getFileFromSoundName(soundName);
|
||||
|
||||
var music = new gdjs.HowlerSound({
|
||||
src: [soundFile], //TODO: ogg, mp3...
|
||||
loop: loop,
|
||||
html5: true, //Force HTML5 audio so we don't wait for the full file to be loaded on Android.
|
||||
volume: volume / 100,
|
||||
rate: gdjs.HowlerSoundManager.clampRate(pitch),
|
||||
});
|
||||
|
||||
music.play();
|
||||
this._musics[channel] = music;
|
||||
|
||||
music.on('play', this._checkForPause);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getMusicOnChannel = function(channel) {
|
||||
return this._musics[channel];
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.setGlobalVolume = function(volume) {
|
||||
this._globalVolume = volume;
|
||||
if (this._globalVolume > 100) this._globalVolume = 100;
|
||||
if (this._globalVolume < 0) this._globalVolume = 0;
|
||||
Howler.volume(this._globalVolume / 100);
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.getGlobalVolume = function() {
|
||||
return this._globalVolume;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.clearAll = function() {
|
||||
for (var i = 0; i < this._freeSounds.length; ++i) {
|
||||
if (this._freeSounds[i]) this._freeSounds[i].unload();
|
||||
}
|
||||
for (var i = 0; i < this._freeMusics.length; ++i) {
|
||||
if (this._freeMusics[i]) this._freeMusics[i].unload();
|
||||
}
|
||||
this._freeSounds.length = 0;
|
||||
this._freeMusics.length = 0;
|
||||
|
||||
for (var p in this._sounds) {
|
||||
if (this._sounds.hasOwnProperty(p) && this._sounds[p]) {
|
||||
this._sounds[p].unload();
|
||||
delete this._sounds[p];
|
||||
}
|
||||
}
|
||||
for (var p in this._musics) {
|
||||
if (this._musics.hasOwnProperty(p) && this._musics[p]) {
|
||||
this._musics[p].unload();
|
||||
delete this._musics[p];
|
||||
}
|
||||
}
|
||||
this._pausedSounds.length = 0;
|
||||
};
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.preloadAudio = function(
|
||||
onProgress,
|
||||
onComplete,
|
||||
resources
|
||||
) {
|
||||
resources = resources || this._resources;
|
||||
|
||||
//Construct the list of files to be loaded.
|
||||
//For one loaded file, it can have one or more resources
|
||||
//that use it.
|
||||
var files = [];
|
||||
for (var i = 0, len = resources.length; i < len; ++i) {
|
||||
var res = resources[i];
|
||||
if (res.file && res.kind === 'audio') {
|
||||
this._availableResources[res.name] = res;
|
||||
|
||||
if (files.indexOf(res.file) === -1) {
|
||||
files.push(res.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (files.length === 0) return onComplete(files.length);
|
||||
|
||||
var loaded = 0;
|
||||
function onLoad(audioFile) {
|
||||
loaded++;
|
||||
if (loaded === files.length) {
|
||||
return onComplete(files.length);
|
||||
}
|
||||
|
||||
onProgress(loaded, files.length);
|
||||
}
|
||||
|
||||
var that = this;
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
(function(audioFile) {
|
||||
var sound = new XMLHttpRequest();
|
||||
sound.addEventListener('load', onLoad.bind(that, audioFile));
|
||||
sound.addEventListener('error', onLoad.bind(that, audioFile));
|
||||
sound.addEventListener('abort', onLoad.bind(that, audioFile));
|
||||
sound.open('GET', audioFile);
|
||||
sound.send();
|
||||
})(files[i]);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #000000;
|
||||
overflow: hidden;
|
||||
}
|
||||
#canvasArea {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@font-face{ font-family : "gdjs_font_Kenney Future Narrow.ttf"; src : url('Kenney Future Narrow.ttf') format('truetype'); }
|
||||
|
||||
</style>
|
||||
<!-- Libs and GDJS core files : -->
|
||||
<script src="libs\jshashtable.js" crossorigin="anonymous"></script>
|
||||
<script src="gd.js" crossorigin="anonymous"></script>
|
||||
<script src="gd-splash-image.js" crossorigin="anonymous"></script>
|
||||
<script src="libs\hshg.js" crossorigin="anonymous"></script>
|
||||
<script src="libs\rbush.js" crossorigin="anonymous"></script>
|
||||
<script src="inputmanager.js" crossorigin="anonymous"></script>
|
||||
<script src="jsonmanager.js" crossorigin="anonymous"></script>
|
||||
<script src="timemanager.js" crossorigin="anonymous"></script>
|
||||
<script src="runtimeobject.js" crossorigin="anonymous"></script>
|
||||
<script src="profiler.js" crossorigin="anonymous"></script>
|
||||
<script src="runtimescene.js" crossorigin="anonymous"></script>
|
||||
<script src="scenestack.js" crossorigin="anonymous"></script>
|
||||
<script src="polygon.js" crossorigin="anonymous"></script>
|
||||
<script src="force.js" crossorigin="anonymous"></script>
|
||||
<script src="layer.js" crossorigin="anonymous"></script>
|
||||
<script src="timer.js" crossorigin="anonymous"></script>
|
||||
<script src="runtimegame.js" crossorigin="anonymous"></script>
|
||||
<script src="variable.js" crossorigin="anonymous"></script>
|
||||
<script src="variablescontainer.js" crossorigin="anonymous"></script>
|
||||
<script src="oncetriggers.js" crossorigin="anonymous"></script>
|
||||
<script src="runtimebehavior.js" crossorigin="anonymous"></script>
|
||||
<script src="spriteruntimeobject.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\commontools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\runtimescenetools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\inputtools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\objecttools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\cameratools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\soundtools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\storagetools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\stringtools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\windowtools.js" crossorigin="anonymous"></script>
|
||||
<script src="events-tools\networktools.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\pixi.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\pixi-filters-tools.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\runtimegame-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\runtimescene-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\layer-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\pixi-image-manager.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\spriteruntimeobject-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="pixi-renderers\loadingscreen-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="howler-sound-manager\howler.min.js" crossorigin="anonymous"></script>
|
||||
<script src="howler-sound-manager\howler-sound-manager.js" crossorigin="anonymous"></script>
|
||||
<script src="fontfaceobserver-font-manager\fontfaceobserver.js" crossorigin="anonymous"></script>
|
||||
<script src="fontfaceobserver-font-manager\fontfaceobserver-font-manager.js" crossorigin="anonymous"></script>
|
||||
<script src="Extensions\PanelSpriteObject\panelspriteruntimeobject-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="Extensions\PanelSpriteObject\panelspriteruntimeobject.js" crossorigin="anonymous"></script>
|
||||
<script src="Extensions\TextObject\textruntimeobject-pixi-renderer.js" crossorigin="anonymous"></script>
|
||||
<script src="Extensions\TextObject\textruntimeobject.js" crossorigin="anonymous"></script>
|
||||
<script src="code0.js" crossorigin="anonymous"></script>
|
||||
<script src="Extensions\DestroyOutsideBehavior\destroyoutsideruntimebehavior.js" crossorigin="anonymous"></script>
|
||||
<script src="Extensions\SystemInfo\systeminfotools.js" crossorigin="anonymous"></script>
|
||||
<script src="code1.js" crossorigin="anonymous"></script>
|
||||
<script src="code2.js" crossorigin="anonymous"></script>
|
||||
<script src="code3.js" crossorigin="anonymous"></script>
|
||||
<script src="code4.js" crossorigin="anonymous"></script>
|
||||
<script src="code5.js" crossorigin="anonymous"></script>
|
||||
<script src="data.js" crossorigin="anonymous"></script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
(function() {
|
||||
//Initialization
|
||||
var game = new gdjs.RuntimeGame(gdjs.projectData, {});
|
||||
|
||||
//Create a renderer
|
||||
game.getRenderer().createStandardCanvas(document.body);
|
||||
|
||||
//Bind keyboards/mouse/touch events
|
||||
game.getRenderer().bindStandardEvents(game.getInputManager(), window, document);
|
||||
|
||||
//Load all assets and start the game
|
||||
game.loadAllAssets(function() {
|
||||
setTimeout(function() {
|
||||
game.startGameLoop();
|
||||
}, 2000)
|
||||
});
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Store input made on a canvas: mouse position, key pressed
|
||||
* and touches states.
|
||||
*
|
||||
* See **bindStandardEvents** method for connecting the input
|
||||
* manager to a canvas and **onFrameEnded** for signaling the
|
||||
* end of a frame (necessary for proper touches events handling).
|
||||
*
|
||||
* @constructor
|
||||
* @memberof gdjs
|
||||
* @class InputManager
|
||||
*/
|
||||
gdjs.InputManager = function()
|
||||
{
|
||||
this._pressedKeys = new Hashtable();
|
||||
this._releasedKeys = new Hashtable();
|
||||
this._lastPressedKey = 0;
|
||||
this._pressedMouseButtons = new Array(5);
|
||||
this._releasedMouseButtons = new Array(5);
|
||||
this._mouseX = 0;
|
||||
this._mouseY = 0;
|
||||
this._mouseWheelDelta = 0;
|
||||
this._touches = new Hashtable();
|
||||
this._startedTouches = []; //Identifiers of the touches that started during/before the frame.
|
||||
this._endedTouches = []; //Identifiers of the touches that ended during/before the frame.
|
||||
|
||||
this._touchSimulateMouse = true;
|
||||
};
|
||||
|
||||
/** @constant {number} */
|
||||
gdjs.InputManager.MOUSE_LEFT_BUTTON = 0;
|
||||
|
||||
/** @constant {number} */
|
||||
gdjs.InputManager.MOUSE_RIGHT_BUTTON = 1;
|
||||
|
||||
/** @constant {number} */
|
||||
gdjs.InputManager.MOUSE_MIDDLE_BUTTON = 2;
|
||||
|
||||
/**
|
||||
* Should be called whenever a key is pressed
|
||||
* @param {number} keyCode The key code associated to the key press.
|
||||
*/
|
||||
gdjs.InputManager.prototype.onKeyPressed = function(keyCode) {
|
||||
this._pressedKeys.put(keyCode, true);
|
||||
this._lastPressedKey = keyCode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Should be called whenever a key is released
|
||||
* @param {number} keyCode The key code associated to the key release.
|
||||
*/
|
||||
gdjs.InputManager.prototype.onKeyReleased = function(keyCode) {
|
||||
this._pressedKeys.put(keyCode, false);
|
||||
this._releasedKeys.put(keyCode, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the code of the last key that was pressed.
|
||||
* @return {number} The code of the last key pressed.
|
||||
*/
|
||||
gdjs.InputManager.prototype.getLastPressedKey = function() {
|
||||
return this._lastPressedKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the key corresponding to keyCode is pressed.
|
||||
* @param {number} keyCode The key code to be tested.
|
||||
*/
|
||||
gdjs.InputManager.prototype.isKeyPressed = function(keyCode) {
|
||||
return this._pressedKeys.containsKey(keyCode) && this._pressedKeys.get(keyCode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the key corresponding to keyCode was released during the last frame.
|
||||
* @param {number} keyCode The key code to be tested.
|
||||
*/
|
||||
gdjs.InputManager.prototype.wasKeyReleased = function(keyCode) {
|
||||
return this._releasedKeys.containsKey(keyCode) && this._releasedKeys.get(keyCode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if any key is pressed
|
||||
*/
|
||||
gdjs.InputManager.prototype.anyKeyPressed = function() {
|
||||
for(var keyCode in this._pressedKeys.items) {
|
||||
if (this._pressedKeys.items.hasOwnProperty(keyCode)) {
|
||||
if (this._pressedKeys.items[keyCode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Should be called when the mouse is moved.<br>
|
||||
* Please note that the coordinates must be expressed relative to the view position.
|
||||
*
|
||||
* @param {number} x The mouse new X position
|
||||
* @param {number} y The mouse new Y position
|
||||
*/
|
||||
gdjs.InputManager.prototype.onMouseMove = function(x,y) {
|
||||
this._mouseX = x;
|
||||
this._mouseY = y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the mouse X position
|
||||
*
|
||||
* @return the mouse X position, relative to the game view.
|
||||
*/
|
||||
gdjs.InputManager.prototype.getMouseX = function() {
|
||||
return this._mouseX;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the mouse Y position
|
||||
*
|
||||
* @return the mouse Y position, relative to the game view.
|
||||
*/
|
||||
gdjs.InputManager.prototype.getMouseY = function() {
|
||||
return this._mouseY;
|
||||
};
|
||||
|
||||
/**
|
||||
* Should be called whenever a mouse button is pressed
|
||||
* @param {number} buttonCode The mouse button code associated to the event.
|
||||
* See gdjs.InputManager.MOUSE_LEFT_BUTTON, gdjs.InputManager.MOUSE_RIGHT_BUTTON, gdjs.InputManager.MOUSE_MIDDLE_BUTTON
|
||||
*/
|
||||
gdjs.InputManager.prototype.onMouseButtonPressed = function(buttonCode) {
|
||||
this._pressedMouseButtons[buttonCode] = true;
|
||||
this._releasedMouseButtons[buttonCode] = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Should be called whenever a mouse button is released
|
||||
* @param {number} buttonCode The mouse button code associated to the event. (see onMouseButtonPressed)
|
||||
*/
|
||||
gdjs.InputManager.prototype.onMouseButtonReleased = function(buttonCode) {
|
||||
this._pressedMouseButtons[buttonCode] = false;
|
||||
this._releasedMouseButtons[buttonCode] = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the mouse button corresponding to buttonCode is pressed.
|
||||
* @param {number} buttonCode The mouse button code (0: Left button, 1: Right button).
|
||||
*/
|
||||
gdjs.InputManager.prototype.isMouseButtonPressed = function(buttonCode) {
|
||||
return this._pressedMouseButtons[buttonCode] !== undefined && this._pressedMouseButtons[buttonCode];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the mouse button corresponding to buttonCode was just released.
|
||||
* @param {number} buttonCode The mouse button code (0: Left button, 1: Right button).
|
||||
*/
|
||||
gdjs.InputManager.prototype.isMouseButtonReleased = function(buttonCode) {
|
||||
return this._releasedMouseButtons[buttonCode] !== undefined && this._releasedMouseButtons[buttonCode];
|
||||
};
|
||||
|
||||
/**
|
||||
* Should be called whenever the mouse wheel is used
|
||||
* @param {number} wheelDelta The mouse wheel delta
|
||||
*/
|
||||
gdjs.InputManager.prototype.onMouseWheel = function(wheelDelta) {
|
||||
this._mouseWheelDelta = wheelDelta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta
|
||||
*/
|
||||
gdjs.InputManager.prototype.getMouseWheelDelta = function() {
|
||||
return this._mouseWheelDelta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a touch X position
|
||||
*
|
||||
* @return the touch X position, relative to the game view.
|
||||
*/
|
||||
gdjs.InputManager.prototype.getTouchX = function(identifier) {
|
||||
if (!this._touches.containsKey(identifier))
|
||||
return 0;
|
||||
|
||||
return this._touches.get(identifier).x;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a touch Y position
|
||||
*
|
||||
* @return the touch Y position, relative to the game view.
|
||||
*/
|
||||
gdjs.InputManager.prototype.getTouchY = function(identifier) {
|
||||
if (!this._touches.containsKey(identifier))
|
||||
return 0;
|
||||
|
||||
return this._touches.get(identifier).y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update and return the array containing the identifiers of all touches.
|
||||
*
|
||||
*/
|
||||
gdjs.InputManager.prototype.getAllTouchIdentifiers = function() {
|
||||
gdjs.InputManager._allTouchIds = gdjs.InputManager._allTouchIds || [];
|
||||
gdjs.InputManager._allTouchIds.length = 0;
|
||||
|
||||
for(var id in this._touches.items) {
|
||||
if (this._touches.items.hasOwnProperty(id)) {
|
||||
gdjs.InputManager._allTouchIds.push(parseInt(id, 10));
|
||||
}
|
||||
}
|
||||
|
||||
return gdjs.InputManager._allTouchIds;
|
||||
};
|
||||
|
||||
gdjs.InputManager.prototype.onTouchStart = function(identifier, x, y) {
|
||||
this._startedTouches.push(identifier);
|
||||
this._touches.put(identifier, {x: x, y: y});
|
||||
|
||||
if (this._touchSimulateMouse) {
|
||||
this.onMouseMove(x, y);
|
||||
this.onMouseButtonPressed(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.InputManager.prototype.onTouchMove = function(identifier, x, y) {
|
||||
var touch = this._touches.get(identifier);
|
||||
if (!touch) return;
|
||||
|
||||
touch.x = x;
|
||||
touch.y = y;
|
||||
|
||||
if (this._touchSimulateMouse) {
|
||||
this.onMouseMove(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.InputManager.prototype.onTouchEnd = function(identifier) {
|
||||
this._endedTouches.push(identifier);
|
||||
if (this._touches.containsKey(identifier)) { //Postpone deletion at the end of the frame
|
||||
this._touches.get(identifier).justEnded = true;
|
||||
}
|
||||
|
||||
if (this._touchSimulateMouse) {
|
||||
this.onMouseButtonReleased(gdjs.InputManager.MOUSE_LEFT_BUTTON);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.InputManager.prototype.getStartedTouchIdentifiers = function() {
|
||||
return this._startedTouches;
|
||||
};
|
||||
|
||||
gdjs.InputManager.prototype.popStartedTouch = function() {
|
||||
return this._startedTouches.shift();
|
||||
};
|
||||
|
||||
gdjs.InputManager.prototype.popEndedTouch = function() {
|
||||
return this._endedTouches.shift();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set if touch events should simulate mouse events.
|
||||
*
|
||||
* If true, any touch will move the mouse position and set mouse buttons
|
||||
* as pressed/released.
|
||||
* @param enable {Boolean} true to simulate mouse events, false to disable it.
|
||||
*/
|
||||
gdjs.InputManager.prototype.touchSimulateMouse = function(enable) {
|
||||
if (enable === undefined) enable = true;
|
||||
|
||||
this._touchSimulateMouse = enable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify the input manager that the frame ended, so anything that last
|
||||
* only for one frame (started/ended touches) should be reset.
|
||||
*
|
||||
* This method should be called in the game loop (see gdjs.RuntimeGame.startGameLoop).
|
||||
*/
|
||||
gdjs.InputManager.prototype.onFrameEnded = function() {
|
||||
//Only clear the ended touches at the end of the frame.
|
||||
for(var id in this._touches.items) {
|
||||
if (this._touches.items.hasOwnProperty(id)) {
|
||||
var touch = this._touches.items[id];
|
||||
if(touch.justEnded) {
|
||||
this._touches.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._startedTouches.length = 0;
|
||||
this._endedTouches.length = 0;
|
||||
this._releasedKeys.clear();
|
||||
this._releasedMouseButtons.length = 0;
|
||||
this._mouseWheelDelta = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the mouse wheel scroll to up
|
||||
*/
|
||||
gdjs.InputManager.prototype.isScrollingUp = function() {
|
||||
return this.getMouseWheelDelta() > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the mouse wheel scroll to down
|
||||
*/
|
||||
gdjs.InputManager.prototype.isScrollingDown = function() {
|
||||
return this.getMouseWheelDelta() < 0;
|
||||
};
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-present Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JsonManager loads json files (using XMLHttpRequest), using the "json" resources
|
||||
* registered in the game resources.
|
||||
*
|
||||
* Contrary to audio/fonts, json files are loaded asynchronously, when requested.
|
||||
* You should properly handle errors, and give the developer/player a way to know
|
||||
* that loading failed.
|
||||
*
|
||||
* @class JsonManager
|
||||
* @memberof gdjs
|
||||
* @param {Object[]} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.JsonManager = function(resources) {
|
||||
this._resources = resources;
|
||||
|
||||
/** @type Object.<string, Object> */
|
||||
this._loadedJsons = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback called when a json is preloaded
|
||||
* @callback JsonManagerOnProgressCallback
|
||||
* @param {number} loaded The number of json files loaded so far
|
||||
* @param {number} total The total number to be loaded
|
||||
* @returns {undefined} Nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
* The callback called when all jsons are preloaded
|
||||
* @callback JsonManagerOnCompleteCallback
|
||||
* @param {number} total The total number to be loaded
|
||||
* @returns {undefined} Nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request all the json resources to be preloaded (unless they are marked as not preloaded).
|
||||
*
|
||||
* @param {JsonManagerOnProgressCallback} onProgress The function called after each json is loaded.
|
||||
* @param {JsonManagerOnCompleteCallback} onComplete The function called when all jsons are loaded.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.preloadJsons = function(onProgress, onComplete) {
|
||||
var resources = this._resources;
|
||||
|
||||
var jsonResources = resources.filter(function(resource) {
|
||||
return resource.kind === 'json' && !resource.disablePreload;
|
||||
});
|
||||
if (jsonResources.length === 0) return onComplete(jsonResources.length);
|
||||
|
||||
var loaded = 0;
|
||||
/** @type JsonManagerRequestCallback */
|
||||
var onLoad = function(error, jsonContent) {
|
||||
if (error) {
|
||||
console.error('Error while preloading a json resource:' + error);
|
||||
}
|
||||
|
||||
loaded++;
|
||||
if (loaded === jsonResources.length) {
|
||||
return onComplete(jsonResources.length);
|
||||
}
|
||||
|
||||
onProgress(loaded, jsonResources.length);
|
||||
};
|
||||
|
||||
for (var i = 0; i < jsonResources.length; ++i) {
|
||||
this.loadJson(jsonResources[i].name, onLoad);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback called when a json that was requested is loaded (or an error occured).
|
||||
* @callback JsonManagerRequestCallback
|
||||
* @param {?Error} error The error, if any. `null` otherwise.
|
||||
* @param {?Object} jsonContent The content of the json file (or null if an error occured).
|
||||
* @returns {undefined} Nothing
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request the json file from the given resource name.
|
||||
* This method is asynchronous. When loaded, the `callback` is called with the error
|
||||
* (null if none) and the loaded json (a JS Object).
|
||||
*
|
||||
* @param {string} resourceName The resource pointing to the json file to load.
|
||||
* @param {JsonManagerRequestCallback} callback The callback function called when json is loaded (or an error occured).
|
||||
*/
|
||||
gdjs.JsonManager.prototype.loadJson = function(resourceName, callback) {
|
||||
var resource = this._resources.find(function(resource) {
|
||||
return resource.kind === 'json' && resource.name === resourceName;
|
||||
});
|
||||
if (!resource) {
|
||||
callback(
|
||||
new Error(
|
||||
'Can\'t find resource with name: "' +
|
||||
resourceName +
|
||||
'" (or is not a json resource).'
|
||||
),
|
||||
null
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't fetch again an object that is already in memory
|
||||
if (this._loadedJsons[resourceName]) {
|
||||
callback(null, this._loadedJsons[resourceName]);
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.open('GET', resource.file);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status !== 200) {
|
||||
callback(
|
||||
new Error('HTTP error: ' + xhr.status + '(' + xhr.statusText + ')'),
|
||||
null
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
that._loadedJsons[resourceName] = xhr.response;
|
||||
|
||||
callback(null, xhr.response);
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
callback(new Error('Network error'), null);
|
||||
};
|
||||
xhr.onabort = function() {
|
||||
callback(new Error('Request aborted'), null);
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given json resource was loaded (preloaded or loaded with `loadJson`).
|
||||
* @param {string} resourceName The name of the json resource.
|
||||
* @returns {boolean} true if the content of the json resource is loaded. false otherwise.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.isJsonLoaded = function(resourceName) {
|
||||
return !!this._loadedJsons[resourceName];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the object for the given resource that is already loaded (preloaded or loaded with `loadJson`).
|
||||
* If the resource is not loaded, `null` will be returned.
|
||||
*
|
||||
* @param {string} resourceName The name of the json resource.
|
||||
* @returns {?Object} the content of the json resource, if loaded. `null` otherwise.
|
||||
*/
|
||||
gdjs.JsonManager.prototype.getLoadedJson = function(resourceName) {
|
||||
return this._loadedJsons[resourceName] || null;
|
||||
};
|
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 816 B |
|
@ -0,0 +1,391 @@
|
|||
// @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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a layer of a scene, used to display objects.
|
||||
*
|
||||
* Viewports and multiple cameras are not supported.
|
||||
*
|
||||
* @class Layer
|
||||
* @param {Object} layerData The data used to initialize the layer
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene in which the layer is used
|
||||
* @memberof gdjs
|
||||
*/
|
||||
gdjs.Layer = function(layerData, runtimeScene) {
|
||||
this._name = layerData.name;
|
||||
this._cameraRotation = 0;
|
||||
this._zoomFactor = 1;
|
||||
this._timeScale = 1;
|
||||
this._hidden = !layerData.visibility;
|
||||
this._effects = layerData.effects || [];
|
||||
this._cameraX = runtimeScene.getGame().getGameResolutionWidth() / 2;
|
||||
this._cameraY = runtimeScene.getGame().getGameResolutionHeight() / 2;
|
||||
this._cachedGameResolutionWidth = runtimeScene
|
||||
.getGame()
|
||||
.getGameResolutionWidth();
|
||||
this._cachedGameResolutionHeight = runtimeScene
|
||||
.getGame()
|
||||
.getGameResolutionHeight();
|
||||
this._runtimeScene = runtimeScene;
|
||||
|
||||
// @ts-ignore - assume the proper renderer is passed
|
||||
this._renderer = new gdjs.LayerRenderer(this, runtimeScene.getRenderer());
|
||||
this.show(!this._hidden);
|
||||
this._setEffectsDefaultParameters();
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getRenderer = function() {
|
||||
return this._renderer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the RuntimeScene whenever the game resolution size is changed.
|
||||
* Updates the layer width/height and position.
|
||||
*/
|
||||
gdjs.Layer.prototype.onGameResolutionResized = function() {
|
||||
var oldGameResolutionWidth = this._cachedGameResolutionWidth;
|
||||
var oldGameResolutionHeight = this._cachedGameResolutionHeight;
|
||||
this._cachedGameResolutionWidth = this._runtimeScene
|
||||
.getGame()
|
||||
.getGameResolutionWidth();
|
||||
this._cachedGameResolutionHeight = this._runtimeScene
|
||||
.getGame()
|
||||
.getGameResolutionHeight();
|
||||
|
||||
// Adapt position of the camera center as:
|
||||
// * Most cameras following a player/object on the scene will be updating this
|
||||
// in events anyway.
|
||||
// * Cameras not following a player/object are usually UIs which are intuitively
|
||||
// expected not to "move". Not adapting the center position would make the camera
|
||||
// move from its initial position (which is centered in the screen) - and anchor
|
||||
// behavior would behave counterintuitively.
|
||||
this._cameraX +=
|
||||
(this._cachedGameResolutionWidth - oldGameResolutionWidth) / 2;
|
||||
this._cameraY +=
|
||||
(this._cachedGameResolutionHeight - oldGameResolutionHeight) / 2;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the scene the layer belongs to
|
||||
* @returns {gdjs.RuntimeScene} the scene the layer belongs to
|
||||
*/
|
||||
gdjs.Layer.prototype.getRuntimeScene = function() {
|
||||
return this._runtimeScene;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called at each frame, after events are run and before rendering.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene the layer belongs to.
|
||||
*/
|
||||
gdjs.Layer.prototype.update = function(runtimeScene) {
|
||||
return this._renderer.updateTime();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of the layer
|
||||
* @return {String} The name of the layer
|
||||
*/
|
||||
gdjs.Layer.prototype.getName = function() {
|
||||
return this._name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the camera center X position.
|
||||
*
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return The x position of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraX = function(cameraId) {
|
||||
return this._cameraX;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the camera center Y position.
|
||||
*
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return The y position of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraY = function(cameraId) {
|
||||
return this._cameraY;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the camera center X position.
|
||||
*
|
||||
* @param {number} x The new x position
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraX = function(x, cameraId) {
|
||||
this._cameraX = x;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the camera center Y position.
|
||||
*
|
||||
* @param {number} y The new y position
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraY = function(y, cameraId) {
|
||||
this._cameraY = y;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the camera width (which can be different than the game resolution width
|
||||
* if the camera is zoomed).
|
||||
*
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The width of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraWidth = function(cameraId) {
|
||||
return (+this._cachedGameResolutionWidth * 1) / this._zoomFactor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the camera height (which can be different than the game resolution height
|
||||
* if the camera is zoomed).
|
||||
*
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The height of the camera
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraHeight = function(cameraId) {
|
||||
return (+this._cachedGameResolutionHeight * 1) / this._zoomFactor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Show (or hide) the layer.
|
||||
* @param {boolean} enable true to show the layer, false to hide it.
|
||||
*/
|
||||
gdjs.Layer.prototype.show = function(enable) {
|
||||
this._hidden = !enable;
|
||||
this._renderer.updateVisibility(enable);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the layer is visible.
|
||||
*
|
||||
* @return true if the layer is visible.
|
||||
*/
|
||||
gdjs.Layer.prototype.isVisible = function() {
|
||||
return !this._hidden;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the zoom of a camera.
|
||||
*
|
||||
* @param {number} newZoom The new zoom. Must be superior to 0. 1 is the default zoom.
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraZoom = function(newZoom, cameraId) {
|
||||
this._zoomFactor = newZoom;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the zoom of a camera.
|
||||
*
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The zoom.
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraZoom = function(cameraId) {
|
||||
return this._zoomFactor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the rotation of the camera, expressed in degrees.
|
||||
*
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
* @return {number} The rotation, in degrees.
|
||||
*/
|
||||
gdjs.Layer.prototype.getCameraRotation = function(cameraId) {
|
||||
return this._cameraRotation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the rotation of the camera, expressed in degrees.
|
||||
* The rotation is made around the camera center.
|
||||
*
|
||||
* @param {number} rotation The new rotation, in degrees.
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.setCameraRotation = function(rotation, cameraId) {
|
||||
this._cameraRotation = rotation;
|
||||
this._renderer.updatePosition();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a point from the canvas coordinates (For example, the mouse position) to the
|
||||
* "world" coordinates.
|
||||
*
|
||||
* TODO: Update this method to store the result in a static array
|
||||
*
|
||||
* @param {number} x The x position, in canvas coordinates.
|
||||
* @param {number} y The y position, in canvas coordinates.
|
||||
* @param {number=} cameraId The camera number. Currently ignored.
|
||||
*/
|
||||
gdjs.Layer.prototype.convertCoords = function(x, y, cameraId) {
|
||||
x -= this._cachedGameResolutionWidth / 2;
|
||||
y -= this._cachedGameResolutionHeight / 2;
|
||||
x /= Math.abs(this._zoomFactor);
|
||||
y /= Math.abs(this._zoomFactor);
|
||||
|
||||
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
|
||||
var angleInRadians = (this._cameraRotation / 180) * Math.PI;
|
||||
var tmp = x;
|
||||
var cosValue = Math.cos(angleInRadians);
|
||||
var sinValue = Math.sin(angleInRadians);
|
||||
x = cosValue * x - sinValue * y;
|
||||
y = sinValue * tmp + cosValue * y;
|
||||
|
||||
return [x + this.getCameraX(cameraId), y + this.getCameraY(cameraId)];
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.convertInverseCoords = function(x, y, cameraId) {
|
||||
x -= this.getCameraX(cameraId);
|
||||
y -= this.getCameraY(cameraId);
|
||||
|
||||
// Only compute angle and cos/sin once (allow heavy optimization from JS engines).
|
||||
var angleInRadians = (this._cameraRotation / 180) * Math.PI;
|
||||
var tmp = x;
|
||||
var cosValue = Math.cos(-angleInRadians);
|
||||
var sinValue = Math.sin(-angleInRadians);
|
||||
x = cosValue * x - sinValue * y;
|
||||
y = sinValue * tmp + cosValue * y;
|
||||
|
||||
x *= Math.abs(this._zoomFactor);
|
||||
y *= Math.abs(this._zoomFactor);
|
||||
|
||||
return [
|
||||
x + this._cachedGameResolutionWidth / 2,
|
||||
y + this._cachedGameResolutionHeight / 2,
|
||||
];
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getWidth = function() {
|
||||
return this._cachedGameResolutionWidth;
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getHeight = function() {
|
||||
return this._cachedGameResolutionHeight;
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype.getEffects = function() {
|
||||
return this._effects;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change an effect parameter value (for parameters that are numbers).
|
||||
* @param {string} name The name of the effect to update.
|
||||
* @param {string} parameterName The name of the parameter to update.
|
||||
* @param {number} value The new value (number).
|
||||
*/
|
||||
gdjs.Layer.prototype.setEffectDoubleParameter = function(
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
) {
|
||||
return this._renderer.setEffectDoubleParameter(name, parameterName, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change an effect parameter value (for parameters that are strings).
|
||||
* @param {string} name The name of the effect to update.
|
||||
* @param {string} parameterName The name of the parameter to update.
|
||||
* @param {string} value The new value (string).
|
||||
*/
|
||||
gdjs.Layer.prototype.setEffectStringParameter = function(
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
) {
|
||||
return this._renderer.setEffectStringParameter(name, parameterName, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change an effect parameter value (for parameters that are booleans).
|
||||
* @param {string} name The name of the effect to update.
|
||||
* @param {string} parameterName The name of the parameter to update.
|
||||
* @param {boolean} value The new value (boolean).
|
||||
*/
|
||||
gdjs.Layer.prototype.setEffectBooleanParameter = function(
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
) {
|
||||
return this._renderer.setEffectBooleanParameter(name, parameterName, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable or disable an effect.
|
||||
* @param {string} name The name of the effect to enable or disable.
|
||||
* @param {boolean} enable true to enable, false to disable
|
||||
*/
|
||||
gdjs.Layer.prototype.enableEffect = function(name, enable) {
|
||||
this._renderer.enableEffect(name, enable);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if an effect is enabled
|
||||
* @param {string} name The name of the effect
|
||||
* @return {boolean} true if the effect is enabled, false otherwise.
|
||||
*/
|
||||
gdjs.Layer.prototype.isEffectEnabled = function(name) {
|
||||
return this._renderer.isEffectEnabled(name);
|
||||
};
|
||||
|
||||
gdjs.Layer.prototype._setEffectsDefaultParameters = function() {
|
||||
for (var i = 0; i < this._effects.length; ++i) {
|
||||
var effect = this._effects[i];
|
||||
for (var name in effect.doubleParameters) {
|
||||
this.setEffectDoubleParameter(
|
||||
effect.name,
|
||||
name,
|
||||
effect.doubleParameters[name]
|
||||
);
|
||||
}
|
||||
for (var name in effect.stringParameters) {
|
||||
this.setEffectStringParameter(
|
||||
effect.name,
|
||||
name,
|
||||
effect.stringParameters[name]
|
||||
);
|
||||
}
|
||||
for (var name in effect.booleanParameters) {
|
||||
this.setEffectBooleanParameter(
|
||||
effect.name,
|
||||
name,
|
||||
effect.booleanParameters[name]
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the time scale for the objects on the layer:
|
||||
* time will be slower if time scale is < 1, faster if > 1.
|
||||
* @param {number} timeScale The new time scale (must be positive).
|
||||
*/
|
||||
gdjs.Layer.prototype.setTimeScale = function(timeScale) {
|
||||
if (timeScale >= 0) this._timeScale = timeScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the time scale for the objects on the layer.
|
||||
*/
|
||||
gdjs.Layer.prototype.getTimeScale = function() {
|
||||
return this._timeScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the time elapsed since the last frame,
|
||||
* in milliseconds, for objects on the layer.
|
||||
*/
|
||||
gdjs.Layer.prototype.getElapsedTime = function() {
|
||||
return this._runtimeScene.getTimeManager().getElapsedTime() * this._timeScale;
|
||||
};
|
|
@ -0,0 +1,617 @@
|
|||
// Hierarchical Spatial Hash Grid: HSHG
|
||||
//Note that this file has been customized so that HSHG is put into the gdjs object.
|
||||
//Thus, it must be included after gd.js
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @memberof gdjs
|
||||
*/
|
||||
gdjs.HSHG = gdjs.HSHG || {};
|
||||
|
||||
(function(root, undefined){
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// GLOBAL FUNCTIONS
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Updates every object's position in the grid, but only if
|
||||
* the hash value for that object has changed.
|
||||
* This method DOES NOT take into account object expansion or
|
||||
* contraction, just position, and does not attempt to change
|
||||
* the grid the object is currently in; it only (possibly) changes
|
||||
* the cell.
|
||||
*
|
||||
* If the object has significantly changed in size, the best bet is to
|
||||
* call removeObject() and addObject() sequentially, outside of the
|
||||
* normal update cycle of HSHG.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function update_RECOMPUTE(){
|
||||
|
||||
var i
|
||||
,obj
|
||||
,grid
|
||||
,meta
|
||||
,objAABB
|
||||
,newObjHash;
|
||||
|
||||
// for each object
|
||||
for(i = 0; i < this._globalObjects.length; i++){
|
||||
obj = this._globalObjects[i];
|
||||
meta = obj.HSHG;
|
||||
grid = meta.grid;
|
||||
|
||||
// recompute hash
|
||||
objAABB = obj.getAABB();
|
||||
newObjHash = grid.toHash(objAABB.min[0], objAABB.min[1]);
|
||||
|
||||
if(newObjHash !== meta.hash){
|
||||
// grid position has changed, update!
|
||||
grid.removeObject(obj);
|
||||
grid.addObject(obj, newObjHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function update_REMOVEALL(){
|
||||
// not implemented yet :)
|
||||
}
|
||||
|
||||
function testAABBOverlap(objA, objB){
|
||||
var a = objA.getAABB()
|
||||
,b = objB.getAABB();
|
||||
|
||||
if(a.min[0] > b.max[0] || a.min[1] > b.max[1]
|
||||
|| a.max[0] < b.min[0] || a.max[1] < b.min[1]){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function getLongestAABBEdge(min, max){
|
||||
return Math.max(
|
||||
Math.abs(max[0] - min[0])
|
||||
,Math.abs(max[1] - min[1])
|
||||
);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// ENTITIES
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A hierarchical spatial grid containing objects and allowing fast test collisions between them.
|
||||
*
|
||||
* @class HSHG
|
||||
* @memberof gdjs.HSHG
|
||||
* @constructor
|
||||
*/
|
||||
function HSHG(){
|
||||
|
||||
this.MAX_OBJECT_CELL_DENSITY = 1/8 // objects / cells
|
||||
this.INITIAL_GRID_LENGTH = 256 // 16x16
|
||||
this.HIERARCHY_FACTOR = 2
|
||||
this.HIERARCHY_FACTOR_SQRT = Math.SQRT2
|
||||
this.UPDATE_METHOD = update_RECOMPUTE // or update_REMOVEALL
|
||||
|
||||
this._grids = [];
|
||||
this._globalObjects = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the grid. The object can be anything as long as it provides a getAABB method.
|
||||
* An 'HSHG' property is added to the object, and is then deleted when the object is removed from the HSHG.
|
||||
*/
|
||||
HSHG.prototype.addObject = function(obj){
|
||||
var x ,i
|
||||
,cellSize
|
||||
,objAABB = obj.getAABB()
|
||||
,objSize = getLongestAABBEdge(objAABB.min, objAABB.max)
|
||||
,oneGrid, newGrid;
|
||||
|
||||
// for HSHG metadata
|
||||
obj.HSHG = {
|
||||
globalObjectsIndex: this._globalObjects.length
|
||||
}; //TODO: recycle existing object if necessary.
|
||||
|
||||
// add to global object array
|
||||
this._globalObjects.push(obj);
|
||||
|
||||
if(this._grids.length === 0) {
|
||||
// no grids exist yet
|
||||
cellSize = objSize * this.HIERARCHY_FACTOR_SQRT;
|
||||
newGrid = new Grid(cellSize, this.INITIAL_GRID_LENGTH, this);
|
||||
newGrid.initCells();
|
||||
newGrid.addObject(obj);
|
||||
|
||||
this._grids.push(newGrid);
|
||||
} else {
|
||||
x = 0;
|
||||
|
||||
// grids are sorted by cellSize, smallest to largest
|
||||
for(i = 0; i < this._grids.length; i++){
|
||||
oneGrid = this._grids[i];
|
||||
x = oneGrid.cellSize;
|
||||
if(objSize < x){
|
||||
x = x / this.HIERARCHY_FACTOR;
|
||||
if(objSize < x) {
|
||||
// find appropriate size
|
||||
while( objSize < x ) {
|
||||
x = x / this.HIERARCHY_FACTOR;
|
||||
}
|
||||
newGrid = new Grid(x * this.HIERARCHY_FACTOR, this.INITIAL_GRID_LENGTH, this);
|
||||
newGrid.initCells();
|
||||
// assign obj to grid
|
||||
newGrid.addObject(obj)
|
||||
// insert grid into list of grids directly before oneGrid
|
||||
this._grids.splice(i, 0, newGrid);
|
||||
} else {
|
||||
// insert obj into grid oneGrid
|
||||
oneGrid.addObject(obj);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while( objSize >= x ){
|
||||
x = x * this.HIERARCHY_FACTOR;
|
||||
}
|
||||
|
||||
newGrid = new Grid(x, this.INITIAL_GRID_LENGTH, this);
|
||||
newGrid.initCells();
|
||||
// insert obj into grid
|
||||
newGrid.addObject(obj)
|
||||
// add newGrid as last element in grid list
|
||||
this._grids.push(newGrid);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an object from the HSHG. The object must be in the HSHG before being removed.
|
||||
*/
|
||||
HSHG.prototype.removeObject = function(obj){
|
||||
var meta = obj.HSHG
|
||||
,globalObjectsIndex
|
||||
,replacementObj;
|
||||
|
||||
if(meta === undefined){
|
||||
throw Error( obj + ' was not in the HSHG.' );
|
||||
return;
|
||||
}
|
||||
|
||||
// remove object from global object list
|
||||
globalObjectsIndex = meta.globalObjectsIndex
|
||||
if(globalObjectsIndex === this._globalObjects.length - 1){
|
||||
this._globalObjects.pop();
|
||||
} else {
|
||||
replacementObj = this._globalObjects.pop();
|
||||
replacementObj.HSHG.globalObjectsIndex = globalObjectsIndex;
|
||||
this._globalObjects[ globalObjectsIndex ] = replacementObj;
|
||||
}
|
||||
|
||||
meta.grid.removeObject(obj);
|
||||
|
||||
// remove meta data
|
||||
delete obj.HSHG;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Must be called when objects have been moved ( typically at each "tick" of the game/simulation ).
|
||||
*/
|
||||
HSHG.prototype.update = function(){
|
||||
this.UPDATE_METHOD.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a list of objects colliding with theObject.
|
||||
* @param {gdjs.RuntimeObject} theObject The object to be tested against.
|
||||
*/
|
||||
HSHG.prototype.queryForCollisionWith = function(theObject, result){
|
||||
|
||||
var i, j, k, l, c
|
||||
,grid
|
||||
,cell
|
||||
,objA
|
||||
,objB
|
||||
,offset
|
||||
,adjacentCell
|
||||
,biggerGrid
|
||||
,objAAABB
|
||||
,objAHashInBiggerGrid;
|
||||
|
||||
result.length = 0;
|
||||
|
||||
theObject.HSHG.excludeMe = true;
|
||||
var theObjectAABB = theObject.getAABB();
|
||||
var theObjectHashInItsGrid = theObject.HSHG.grid.toHash(theObjectAABB.min[0], theObjectAABB.min[1]);
|
||||
var theObjectCellInItsGrid = theObject.HSHG.grid.allCells[theObjectHashInItsGrid];
|
||||
|
||||
// default broad test to internal aabb overlap test
|
||||
broadOverlapTest = testAABBOverlap;
|
||||
|
||||
// for all grids ordered by cell size ASC
|
||||
for(i = 0; i < this._grids.length; i++){
|
||||
grid = this._grids[i];
|
||||
|
||||
if ( grid.cellSize === theObject.HSHG.grid.cellSize ) { //We're in the grid of theObject:
|
||||
|
||||
// 1)Test against neighbors:
|
||||
|
||||
// For each cell of the grid that is occupied
|
||||
for(j = 0; j < grid.occupiedCells.length; j++){
|
||||
cell = grid.occupiedCells[j];
|
||||
|
||||
// Collide all objects within the occupied cell
|
||||
for(l = 0; l < cell.objectContainer.length; l++){
|
||||
objB = cell.objectContainer[l]; //Note that objB could be theObject.
|
||||
if(!objB.HSHG.excludeMe && broadOverlapTest(theObject, objB) === true){
|
||||
result.push( objB );
|
||||
}
|
||||
}
|
||||
|
||||
// For the first half of all adjacent cells (offset 4 is the current cell)
|
||||
for(c = 0; c < 4; c++){
|
||||
offset = cell.neighborOffsetArray[c];
|
||||
adjacentCell = grid.allCells[ cell.allCellsIndex + offset ];
|
||||
|
||||
// Collide all objects in cell with adjacent cell
|
||||
for(l = 0; l < adjacentCell.objectContainer.length; l++){
|
||||
objB = adjacentCell.objectContainer[l]; //Note that objB could be theObject.
|
||||
if(!objB.HSHG.excludeMe && broadOverlapTest(theObject, objB) === true){
|
||||
result.push( objB );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2)Test against objects of bigger grids:
|
||||
|
||||
// For all grids with cellsize larger than the grid of theObject:
|
||||
for(k = i + 1; k < this._grids.length; k++){
|
||||
biggerGrid = this._grids[k];
|
||||
var objectHashInBiggerGrid = biggerGrid.toHash(theObjectAABB.min[0], theObjectAABB.min[1]);
|
||||
cell = biggerGrid.allCells[objectHashInBiggerGrid];
|
||||
|
||||
// Check theObject against every object in all cells in offset array of cell
|
||||
// for all adjacent cells...
|
||||
for(c = 0; c < cell.neighborOffsetArray.length; c++){
|
||||
offset = cell.neighborOffsetArray[c];
|
||||
adjacentCell = biggerGrid.allCells[ cell.allCellsIndex + offset ];
|
||||
|
||||
// for all objects in the adjacent cell...
|
||||
for(l = 0; l < adjacentCell.objectContainer.length; l++){
|
||||
objB = adjacentCell.objectContainer[l];
|
||||
|
||||
// Test against theObject: Note that objB is necessarily different from theObject.
|
||||
if(broadOverlapTest(theObject, objB) === true){
|
||||
result.push( objB );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break; //All collisions with object have now been registered
|
||||
}
|
||||
else if ( grid.cellSize < theObject.HSHG.grid.cellSize ) { //We're in a grid with smaller objects
|
||||
|
||||
// For all objects that are stored in this smaller grid
|
||||
for(j = 0; j < grid.allObjects.length; j++){
|
||||
|
||||
//Get the object of the smaller grid.
|
||||
objA = grid.allObjects[j];
|
||||
objAAABB = objA.getAABB();
|
||||
|
||||
//Get its position in the (bigger) grid containing theObject.
|
||||
objAHashInBiggerGrid = theObject.HSHG.grid.toHash(objAAABB.min[0], objAAABB.min[1]);
|
||||
|
||||
//Check if it is near theObject (i.e: Check if the cell of objA is a neighbor of the cell
|
||||
//of theObject ).
|
||||
var objAIsInAdjacentCellToObject = false;
|
||||
for(c = 0; c < theObjectCellInItsGrid.neighborOffsetArray.length; c++){
|
||||
offset = theObjectCellInItsGrid.neighborOffsetArray[c];
|
||||
if ( objAHashInBiggerGrid === theObjectCellInItsGrid.allCellsIndex + offset ) {
|
||||
objAIsInAdjacentCellToObject = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//If objA is near theObject, trigger a collision test.
|
||||
if ( objAIsInAdjacentCellToObject ) {
|
||||
//Note that objA is necessarily different from theObject
|
||||
if(broadOverlapTest(theObject, objA) === true){
|
||||
result.push( objA );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete theObject.HSHG.excludeMe;
|
||||
};
|
||||
|
||||
HSHG.update_RECOMPUTE = update_RECOMPUTE;
|
||||
HSHG.update_REMOVEALL = update_REMOVEALL;
|
||||
|
||||
/**
|
||||
* Grid
|
||||
*
|
||||
* @class Grid
|
||||
* @memberof gdjs.HSHG
|
||||
* @constructor
|
||||
* @param cellSize {int} the pixel size of each cell of the grid
|
||||
* @param cellCount {int} the total number of cells for the grid (width x height)
|
||||
* @param parentHierarchy {HSHG} the HSHG to which this grid belongs
|
||||
* @return void
|
||||
*/
|
||||
function Grid(cellSize, cellCount, parentHierarchy){
|
||||
this.cellSize = cellSize;
|
||||
this.inverseCellSize = 1/cellSize;
|
||||
this.rowColumnCount = ~~Math.sqrt(cellCount);
|
||||
this.xyHashMask = this.rowColumnCount - 1;
|
||||
this.occupiedCells = [];
|
||||
this.allCells = Array(this.rowColumnCount*this.rowColumnCount);
|
||||
this.allObjects = [];
|
||||
this.sharedInnerOffsets = [];
|
||||
|
||||
this._parentHierarchy = parentHierarchy || null;
|
||||
}
|
||||
|
||||
Grid.prototype.initCells = function(){
|
||||
|
||||
// TODO: inner/unique offset rows 0 and 2 may need to be
|
||||
// swapped due to +y being "down" vs "up"
|
||||
|
||||
var i, gridLength = this.allCells.length
|
||||
,x, y
|
||||
,wh = this.rowColumnCount
|
||||
,isOnRightEdge, isOnLeftEdge, isOnTopEdge, isOnBottomEdge
|
||||
,innerOffsets = [
|
||||
// y+ down offsets
|
||||
//-1 + -wh, -wh, -wh + 1,
|
||||
//-1, 0, 1,
|
||||
//wh - 1, wh, wh + 1
|
||||
|
||||
// y+ up offsets
|
||||
wh - 1, wh, wh + 1,
|
||||
-1, 0, 1,
|
||||
-1 + -wh, -wh, -wh + 1
|
||||
]
|
||||
,leftOffset, rightOffset, topOffset, bottomOffset
|
||||
,uniqueOffsets = []
|
||||
,cell;
|
||||
|
||||
this.sharedInnerOffsets = innerOffsets;
|
||||
|
||||
// init all cells, creating offset arrays as needed
|
||||
|
||||
for(i = 0; i < gridLength; i++){
|
||||
|
||||
cell = new Cell();
|
||||
// compute row (y) and column (x) for an index
|
||||
y = ~~(i / this.rowColumnCount);
|
||||
x = ~~(i - (y*this.rowColumnCount));
|
||||
|
||||
// reset / init
|
||||
isOnRightEdge = false;
|
||||
isOnLeftEdge = false;
|
||||
isOnTopEdge = false;
|
||||
isOnBottomEdge = false;
|
||||
|
||||
// right or left edge cell
|
||||
if((x+1) % this.rowColumnCount == 0){ isOnRightEdge = true; }
|
||||
else if(x % this.rowColumnCount == 0){ isOnLeftEdge = true; }
|
||||
|
||||
// top or bottom edge cell
|
||||
if((y+1) % this.rowColumnCount == 0){ isOnTopEdge = true; }
|
||||
else if(y % this.rowColumnCount == 0){ isOnBottomEdge = true; }
|
||||
|
||||
// if cell is edge cell, use unique offsets, otherwise use inner offsets
|
||||
if(isOnRightEdge || isOnLeftEdge || isOnTopEdge || isOnBottomEdge){
|
||||
|
||||
// figure out cardinal offsets first
|
||||
rightOffset = isOnRightEdge === true ? -wh + 1 : 1;
|
||||
leftOffset = isOnLeftEdge === true ? wh - 1 : -1;
|
||||
topOffset = isOnTopEdge === true ? -gridLength + wh : wh;
|
||||
bottomOffset = isOnBottomEdge === true ? gridLength - wh : -wh;
|
||||
|
||||
// diagonals are composites of the cardinals
|
||||
uniqueOffsets = [
|
||||
// y+ down offset
|
||||
//leftOffset + bottomOffset, bottomOffset, rightOffset + bottomOffset,
|
||||
//leftOffset, 0, rightOffset,
|
||||
//leftOffset + topOffset, topOffset, rightOffset + topOffset
|
||||
|
||||
// y+ up offset
|
||||
leftOffset + topOffset, topOffset, rightOffset + topOffset,
|
||||
leftOffset, 0, rightOffset,
|
||||
leftOffset + bottomOffset, bottomOffset, rightOffset + bottomOffset
|
||||
];
|
||||
|
||||
cell.neighborOffsetArray = uniqueOffsets;
|
||||
} else {
|
||||
cell.neighborOffsetArray = this.sharedInnerOffsets;
|
||||
}
|
||||
|
||||
cell.allCellsIndex = i;
|
||||
this.allCells[i] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
Grid.prototype.toHash = function(x, y, z){
|
||||
var i, xHash, yHash, zHash;
|
||||
|
||||
if(x < 0){
|
||||
i = (-x) * this.inverseCellSize;
|
||||
xHash = this.rowColumnCount - 1 - ( ~~i & this.xyHashMask );
|
||||
} else {
|
||||
i = x * this.inverseCellSize;
|
||||
xHash = ~~i & this.xyHashMask;
|
||||
}
|
||||
|
||||
if(y < 0){
|
||||
i = (-y) * this.inverseCellSize;
|
||||
yHash = this.rowColumnCount - 1 - ( ~~i & this.xyHashMask );
|
||||
} else {
|
||||
i = y * this.inverseCellSize;
|
||||
yHash = ~~i & this.xyHashMask;
|
||||
}
|
||||
|
||||
return xHash + yHash * this.rowColumnCount;
|
||||
}
|
||||
|
||||
Grid.prototype.addObject = function(obj, hash){
|
||||
var objAABB
|
||||
,objHash
|
||||
,targetCell;
|
||||
|
||||
// technically, passing this in this should save some computational effort when updating objects
|
||||
if(hash !== undefined){
|
||||
objHash = hash;
|
||||
} else {
|
||||
objAABB = obj.getAABB()
|
||||
objHash = this.toHash(objAABB.min[0], objAABB.min[1])
|
||||
}
|
||||
targetCell = this.allCells[objHash];
|
||||
|
||||
if(targetCell.objectContainer.length === 0){
|
||||
// insert this cell into occupied cells list
|
||||
targetCell.occupiedCellsIndex = this.occupiedCells.length;
|
||||
this.occupiedCells.push(targetCell);
|
||||
}
|
||||
|
||||
// add meta data to obj, for fast update/removal
|
||||
obj.HSHG.objectContainerIndex = targetCell.objectContainer.length;
|
||||
obj.HSHG.hash = objHash;
|
||||
obj.HSHG.grid = this;
|
||||
obj.HSHG.allGridObjectsIndex = this.allObjects.length;
|
||||
// add obj to cell
|
||||
targetCell.objectContainer.push(obj);
|
||||
|
||||
// we can assume that the targetCell is already a member of the occupied list
|
||||
|
||||
// add to grid-global object list
|
||||
this.allObjects.push(obj);
|
||||
|
||||
// do test for grid density
|
||||
if(this.allObjects.length / this.allCells.length > this._parentHierarchy.MAX_OBJECT_CELL_DENSITY){
|
||||
// grid must be increased in size
|
||||
this.expandGrid();
|
||||
}
|
||||
}
|
||||
|
||||
Grid.prototype.removeObject = function(obj){
|
||||
var meta = obj.HSHG
|
||||
,hash
|
||||
,containerIndex
|
||||
,allGridObjectsIndex
|
||||
,cell
|
||||
,replacementCell
|
||||
,replacementObj;
|
||||
|
||||
hash = meta.hash;
|
||||
containerIndex = meta.objectContainerIndex;
|
||||
allGridObjectsIndex = meta.allGridObjectsIndex;
|
||||
cell = this.allCells[hash];
|
||||
|
||||
// remove object from cell object container
|
||||
if(cell.objectContainer.length === 1){
|
||||
// this is the last object in the cell, so reset it
|
||||
cell.objectContainer.length = 0;
|
||||
|
||||
// remove cell from occupied list
|
||||
if(cell.occupiedCellsIndex === this.occupiedCells.length - 1){
|
||||
// special case if the cell is the newest in the list
|
||||
this.occupiedCells.pop();
|
||||
} else {
|
||||
replacementCell = this.occupiedCells.pop();
|
||||
replacementCell.occupiedCellsIndex = cell.occupiedCellsIndex;
|
||||
this.occupiedCells[ cell.occupiedCellsIndex ] = replacementCell;
|
||||
}
|
||||
|
||||
cell.occupiedCellsIndex = null;
|
||||
} else {
|
||||
// there is more than one object in the container
|
||||
if(containerIndex === cell.objectContainer.length - 1){
|
||||
// special case if the obj is the newest in the container
|
||||
cell.objectContainer.pop();
|
||||
} else {
|
||||
replacementObj = cell.objectContainer.pop();
|
||||
replacementObj.HSHG.objectContainerIndex = containerIndex;
|
||||
cell.objectContainer[ containerIndex ] = replacementObj;
|
||||
}
|
||||
}
|
||||
|
||||
// remove object from grid object list
|
||||
if(allGridObjectsIndex === this.allObjects.length - 1){
|
||||
this.allObjects.pop();
|
||||
} else {
|
||||
replacementObj = this.allObjects.pop();
|
||||
replacementObj.HSHG.allGridObjectsIndex = allGridObjectsIndex;
|
||||
this.allObjects[ allGridObjectsIndex ] = replacementObj;
|
||||
}
|
||||
}
|
||||
|
||||
Grid.prototype.expandGrid = function(){
|
||||
var i, j
|
||||
,currentCellCount = this.allCells.length
|
||||
,currentRowColumnCount = this.rowColumnCount
|
||||
,currentXYHashMask = this.xyHashMask
|
||||
|
||||
,newCellCount = currentCellCount * 4 // double each dimension
|
||||
,newRowColumnCount = ~~Math.sqrt(newCellCount)
|
||||
,newXYHashMask = newRowColumnCount - 1
|
||||
,allObjects = this.allObjects.slice(0) // duplicate array, not objects contained
|
||||
,aCell
|
||||
,push = Array.prototype.push;
|
||||
|
||||
// remove all objects
|
||||
for(i = 0; i < allObjects.length; i++){
|
||||
this.removeObject(allObjects[i]);
|
||||
}
|
||||
|
||||
// reset grid values, set new grid to be 4x larger than last
|
||||
this.rowColumnCount = newRowColumnCount;
|
||||
this.allCells = Array(this.rowColumnCount*this.rowColumnCount);
|
||||
this.xyHashMask = newXYHashMask;
|
||||
|
||||
// initialize new cells
|
||||
this.initCells();
|
||||
|
||||
// re-add all objects to grid
|
||||
for(i = 0; i < allObjects.length; i++){
|
||||
this.addObject(allObjects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cell of a grid
|
||||
*
|
||||
* @class Cell
|
||||
* @memberof gdjs.HSHG
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
function Cell(){
|
||||
this.objectContainer = [];
|
||||
this.neighborOffsetArray;
|
||||
this.occupiedCellsIndex = null;
|
||||
this.allCellsIndex = null;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// EXPORTS
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
root['HSHG'] = HSHG;
|
||||
HSHG._private = {
|
||||
Grid: Grid,
|
||||
Cell: Cell,
|
||||
testAABBOverlap: testAABBOverlap,
|
||||
getLongestAABBEdge: getLongestAABBEdge
|
||||
};
|
||||
|
||||
})(gdjs.HSHG);
|
|
@ -0,0 +1,134 @@
|
|||
// @ts-check
|
||||
/**
|
||||
* A generic map (key-value) container.
|
||||
*
|
||||
* Mostly used for storing lists of objects for
|
||||
* GDevelop generated events.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function Hashtable()
|
||||
{
|
||||
/**
|
||||
* The content of the Hashtable. Prefer using methods rather
|
||||
* than accessing this internal object, unless you need to iterate
|
||||
* on the values.
|
||||
* @type {Object.<string, any>}
|
||||
*/
|
||||
this.items = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a Hashtable from a JS object.
|
||||
*
|
||||
* @param {Object.<string, any>} items The content of the Hashtable.
|
||||
* @returns {Hashtable} The new hashtable.
|
||||
* @static
|
||||
*/
|
||||
Hashtable.newFrom = function(items) {
|
||||
var hashtable = new Hashtable();
|
||||
hashtable.items = items;
|
||||
return hashtable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a key-value pair to the Hashtable.
|
||||
* If a value already exists for this key, it is overwritten.
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @param {string} key The key.
|
||||
* @param {any} value The value to associate to the key.
|
||||
*/
|
||||
Hashtable.prototype.put = function(key, value) {
|
||||
this.items[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value corresponding to a key, or undefined if not found.
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @param {string} key The key associated to the value.
|
||||
*/
|
||||
Hashtable.prototype.get = function(key) {
|
||||
return this.items[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if a key exists in the Hashtable.
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @param {string} key The key to search in the Hashtable.
|
||||
* @returns {boolean} true if the key exists.
|
||||
*/
|
||||
Hashtable.prototype.containsKey = function(key) {
|
||||
return this.items.hasOwnProperty(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the value associated to the specified key.
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @param {string} key The key to remove.
|
||||
*/
|
||||
Hashtable.prototype.remove = function(key) {
|
||||
delete this.items[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first key of the Hashtable.
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @returns {?string} The first key of the Hashtable, or undefined if empty.
|
||||
*/
|
||||
Hashtable.prototype.firstKey = function() {
|
||||
for (var k in this.items) {
|
||||
if (this.items.hasOwnProperty(k)) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all the keys of the Hashtable to an array (which is cleared first).
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @param {Array<string>} result The Array where the result gets pushed.
|
||||
*/
|
||||
Hashtable.prototype.keys = function(result) {
|
||||
result.length = 0;
|
||||
for (var k in this.items) {
|
||||
if (this.items.hasOwnProperty(k)) {
|
||||
result.push(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all the values of the Hashtable to an array (which is cleared first).
|
||||
*
|
||||
* @memberof Hashtable
|
||||
* @param {Array<any>} result The Array where the results get pushed.
|
||||
*/
|
||||
Hashtable.prototype.values = function(result) {
|
||||
result.length = 0;
|
||||
for (var k in this.items) {
|
||||
if (this.items.hasOwnProperty(k)) {
|
||||
result.push(this.items[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the Hashtable.
|
||||
*
|
||||
* @memberof Hashtable
|
||||
*/
|
||||
Hashtable.prototype.clear = function() {
|
||||
for (var k in this.items) {
|
||||
if (this.items.hasOwnProperty(k)) {
|
||||
delete this.items[k];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,624 @@
|
|||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rbush = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
module.exports = rbush;
|
||||
|
||||
var quickselect = require('quickselect');
|
||||
|
||||
function rbush(maxEntries, format) {
|
||||
if (!(this instanceof rbush)) return new rbush(maxEntries, format);
|
||||
|
||||
// max entries in a node is 9 by default; min node fill is 40% for best performance
|
||||
this._maxEntries = Math.max(4, maxEntries || 9);
|
||||
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
||||
|
||||
if (format) {
|
||||
this._initFormat(format);
|
||||
}
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
rbush.prototype = {
|
||||
|
||||
all: function () {
|
||||
return this._all(this.data, []);
|
||||
},
|
||||
|
||||
search: function (bbox) {
|
||||
|
||||
var node = this.data,
|
||||
result = [],
|
||||
toBBox = this.toBBox;
|
||||
|
||||
if (!intersects(bbox, node)) return result;
|
||||
|
||||
var nodesToSearch = [],
|
||||
i, len, child, childBBox;
|
||||
|
||||
while (node) {
|
||||
for (i = 0, len = node.children.length; i < len; i++) {
|
||||
|
||||
child = node.children[i];
|
||||
childBBox = node.leaf ? toBBox(child) : child;
|
||||
|
||||
if (intersects(bbox, childBBox)) {
|
||||
if (node.leaf) result.push(child);
|
||||
else if (contains(bbox, childBBox)) this._all(child, result);
|
||||
else nodesToSearch.push(child);
|
||||
}
|
||||
}
|
||||
node = nodesToSearch.pop();
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
collides: function (bbox) {
|
||||
|
||||
var node = this.data,
|
||||
toBBox = this.toBBox;
|
||||
|
||||
if (!intersects(bbox, node)) return false;
|
||||
|
||||
var nodesToSearch = [],
|
||||
i, len, child, childBBox;
|
||||
|
||||
while (node) {
|
||||
for (i = 0, len = node.children.length; i < len; i++) {
|
||||
|
||||
child = node.children[i];
|
||||
childBBox = node.leaf ? toBBox(child) : child;
|
||||
|
||||
if (intersects(bbox, childBBox)) {
|
||||
if (node.leaf || contains(bbox, childBBox)) return true;
|
||||
nodesToSearch.push(child);
|
||||
}
|
||||
}
|
||||
node = nodesToSearch.pop();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
load: function (data) {
|
||||
if (!(data && data.length)) return this;
|
||||
|
||||
if (data.length < this._minEntries) {
|
||||
for (var i = 0, len = data.length; i < len; i++) {
|
||||
this.insert(data[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// recursively build the tree with the given data from stratch using OMT algorithm
|
||||
var node = this._build(data.slice(), 0, data.length - 1, 0);
|
||||
|
||||
if (!this.data.children.length) {
|
||||
// save as is if tree is empty
|
||||
this.data = node;
|
||||
|
||||
} else if (this.data.height === node.height) {
|
||||
// split root if trees have the same height
|
||||
this._splitRoot(this.data, node);
|
||||
|
||||
} else {
|
||||
if (this.data.height < node.height) {
|
||||
// swap trees if inserted one is bigger
|
||||
var tmpNode = this.data;
|
||||
this.data = node;
|
||||
node = tmpNode;
|
||||
}
|
||||
|
||||
// insert the small tree into the large tree at appropriate level
|
||||
this._insert(node, this.data.height - node.height - 1, true);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
insert: function (item) {
|
||||
if (item) this._insert(item, this.data.height - 1);
|
||||
return this;
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
this.data = createNode([]);
|
||||
return this;
|
||||
},
|
||||
|
||||
remove: function (item, equalsFn) {
|
||||
if (!item) return this;
|
||||
|
||||
var node = this.data,
|
||||
bbox = this.toBBox(item),
|
||||
path = [],
|
||||
indexes = [],
|
||||
i, parent, index, goingUp;
|
||||
|
||||
// depth-first iterative tree traversal
|
||||
while (node || path.length) {
|
||||
|
||||
if (!node) { // go up
|
||||
node = path.pop();
|
||||
parent = path[path.length - 1];
|
||||
i = indexes.pop();
|
||||
goingUp = true;
|
||||
}
|
||||
|
||||
if (node.leaf) { // check current node
|
||||
index = findItem(item, node.children, equalsFn);
|
||||
|
||||
if (index !== -1) {
|
||||
// item found, remove the item and condense tree upwards
|
||||
node.children.splice(index, 1);
|
||||
path.push(node);
|
||||
this._condense(path);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
if (!goingUp && !node.leaf && contains(node, bbox)) { // go down
|
||||
path.push(node);
|
||||
indexes.push(i);
|
||||
i = 0;
|
||||
parent = node;
|
||||
node = node.children[0];
|
||||
|
||||
} else if (parent) { // go right
|
||||
i++;
|
||||
node = parent.children[i];
|
||||
goingUp = false;
|
||||
|
||||
} else node = null; // nothing found
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
toBBox: function (item) { return item; },
|
||||
|
||||
compareMinX: compareNodeMinX,
|
||||
compareMinY: compareNodeMinY,
|
||||
|
||||
toJSON: function () { return this.data; },
|
||||
|
||||
fromJSON: function (data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
},
|
||||
|
||||
_all: function (node, result) {
|
||||
var nodesToSearch = [];
|
||||
while (node) {
|
||||
if (node.leaf) result.push.apply(result, node.children);
|
||||
else nodesToSearch.push.apply(nodesToSearch, node.children);
|
||||
|
||||
node = nodesToSearch.pop();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
_build: function (items, left, right, height) {
|
||||
|
||||
var N = right - left + 1,
|
||||
M = this._maxEntries,
|
||||
node;
|
||||
|
||||
if (N <= M) {
|
||||
// reached leaf level; return leaf
|
||||
node = createNode(items.slice(left, right + 1));
|
||||
calcBBox(node, this.toBBox);
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!height) {
|
||||
// target height of the bulk-loaded tree
|
||||
height = Math.ceil(Math.log(N) / Math.log(M));
|
||||
|
||||
// target number of root entries to maximize storage utilization
|
||||
M = Math.ceil(N / Math.pow(M, height - 1));
|
||||
}
|
||||
|
||||
node = createNode([]);
|
||||
node.leaf = false;
|
||||
node.height = height;
|
||||
|
||||
// split the items into M mostly square tiles
|
||||
|
||||
var N2 = Math.ceil(N / M),
|
||||
N1 = N2 * Math.ceil(Math.sqrt(M)),
|
||||
i, j, right2, right3;
|
||||
|
||||
multiSelect(items, left, right, N1, this.compareMinX);
|
||||
|
||||
for (i = left; i <= right; i += N1) {
|
||||
|
||||
right2 = Math.min(i + N1 - 1, right);
|
||||
|
||||
multiSelect(items, i, right2, N2, this.compareMinY);
|
||||
|
||||
for (j = i; j <= right2; j += N2) {
|
||||
|
||||
right3 = Math.min(j + N2 - 1, right2);
|
||||
|
||||
// pack each entry recursively
|
||||
node.children.push(this._build(items, j, right3, height - 1));
|
||||
}
|
||||
}
|
||||
|
||||
calcBBox(node, this.toBBox);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
_chooseSubtree: function (bbox, node, level, path) {
|
||||
|
||||
var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
|
||||
|
||||
while (true) {
|
||||
path.push(node);
|
||||
|
||||
if (node.leaf || path.length - 1 === level) break;
|
||||
|
||||
minArea = minEnlargement = Infinity;
|
||||
|
||||
for (i = 0, len = node.children.length; i < len; i++) {
|
||||
child = node.children[i];
|
||||
area = bboxArea(child);
|
||||
enlargement = enlargedArea(bbox, child) - area;
|
||||
|
||||
// choose entry with the least area enlargement
|
||||
if (enlargement < minEnlargement) {
|
||||
minEnlargement = enlargement;
|
||||
minArea = area < minArea ? area : minArea;
|
||||
targetNode = child;
|
||||
|
||||
} else if (enlargement === minEnlargement) {
|
||||
// otherwise choose one with the smallest area
|
||||
if (area < minArea) {
|
||||
minArea = area;
|
||||
targetNode = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = targetNode || node.children[0];
|
||||
}
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
_insert: function (item, level, isNode) {
|
||||
|
||||
var toBBox = this.toBBox,
|
||||
bbox = isNode ? item : toBBox(item),
|
||||
insertPath = [];
|
||||
|
||||
// find the best node for accommodating the item, saving all nodes along the path too
|
||||
var node = this._chooseSubtree(bbox, this.data, level, insertPath);
|
||||
|
||||
// put the item into the node
|
||||
node.children.push(item);
|
||||
extend(node, bbox);
|
||||
|
||||
// split on node overflow; propagate upwards if necessary
|
||||
while (level >= 0) {
|
||||
if (insertPath[level].children.length > this._maxEntries) {
|
||||
this._split(insertPath, level);
|
||||
level--;
|
||||
} else break;
|
||||
}
|
||||
|
||||
// adjust bboxes along the insertion path
|
||||
this._adjustParentBBoxes(bbox, insertPath, level);
|
||||
},
|
||||
|
||||
// split overflowed node into two
|
||||
_split: function (insertPath, level) {
|
||||
|
||||
var node = insertPath[level],
|
||||
M = node.children.length,
|
||||
m = this._minEntries;
|
||||
|
||||
this._chooseSplitAxis(node, m, M);
|
||||
|
||||
var splitIndex = this._chooseSplitIndex(node, m, M);
|
||||
|
||||
var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
|
||||
newNode.height = node.height;
|
||||
newNode.leaf = node.leaf;
|
||||
|
||||
calcBBox(node, this.toBBox);
|
||||
calcBBox(newNode, this.toBBox);
|
||||
|
||||
if (level) insertPath[level - 1].children.push(newNode);
|
||||
else this._splitRoot(node, newNode);
|
||||
},
|
||||
|
||||
_splitRoot: function (node, newNode) {
|
||||
// split root node
|
||||
this.data = createNode([node, newNode]);
|
||||
this.data.height = node.height + 1;
|
||||
this.data.leaf = false;
|
||||
calcBBox(this.data, this.toBBox);
|
||||
},
|
||||
|
||||
_chooseSplitIndex: function (node, m, M) {
|
||||
|
||||
var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
|
||||
|
||||
minOverlap = minArea = Infinity;
|
||||
|
||||
for (i = m; i <= M - m; i++) {
|
||||
bbox1 = distBBox(node, 0, i, this.toBBox);
|
||||
bbox2 = distBBox(node, i, M, this.toBBox);
|
||||
|
||||
overlap = intersectionArea(bbox1, bbox2);
|
||||
area = bboxArea(bbox1) + bboxArea(bbox2);
|
||||
|
||||
// choose distribution with minimum overlap
|
||||
if (overlap < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
index = i;
|
||||
|
||||
minArea = area < minArea ? area : minArea;
|
||||
|
||||
} else if (overlap === minOverlap) {
|
||||
// otherwise choose distribution with minimum area
|
||||
if (area < minArea) {
|
||||
minArea = area;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
},
|
||||
|
||||
// sorts node children by the best axis for split
|
||||
_chooseSplitAxis: function (node, m, M) {
|
||||
|
||||
var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
|
||||
compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
|
||||
xMargin = this._allDistMargin(node, m, M, compareMinX),
|
||||
yMargin = this._allDistMargin(node, m, M, compareMinY);
|
||||
|
||||
// if total distributions margin value is minimal for x, sort by minX,
|
||||
// otherwise it's already sorted by minY
|
||||
if (xMargin < yMargin) node.children.sort(compareMinX);
|
||||
},
|
||||
|
||||
// total margin of all possible split distributions where each node is at least m full
|
||||
_allDistMargin: function (node, m, M, compare) {
|
||||
|
||||
node.children.sort(compare);
|
||||
|
||||
var toBBox = this.toBBox,
|
||||
leftBBox = distBBox(node, 0, m, toBBox),
|
||||
rightBBox = distBBox(node, M - m, M, toBBox),
|
||||
margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
|
||||
i, child;
|
||||
|
||||
for (i = m; i < M - m; i++) {
|
||||
child = node.children[i];
|
||||
extend(leftBBox, node.leaf ? toBBox(child) : child);
|
||||
margin += bboxMargin(leftBBox);
|
||||
}
|
||||
|
||||
for (i = M - m - 1; i >= m; i--) {
|
||||
child = node.children[i];
|
||||
extend(rightBBox, node.leaf ? toBBox(child) : child);
|
||||
margin += bboxMargin(rightBBox);
|
||||
}
|
||||
|
||||
return margin;
|
||||
},
|
||||
|
||||
_adjustParentBBoxes: function (bbox, path, level) {
|
||||
// adjust bboxes along the given tree path
|
||||
for (var i = level; i >= 0; i--) {
|
||||
extend(path[i], bbox);
|
||||
}
|
||||
},
|
||||
|
||||
_condense: function (path) {
|
||||
// go through the path, removing empty nodes and updating bboxes
|
||||
for (var i = path.length - 1, siblings; i >= 0; i--) {
|
||||
if (path[i].children.length === 0) {
|
||||
if (i > 0) {
|
||||
siblings = path[i - 1].children;
|
||||
siblings.splice(siblings.indexOf(path[i]), 1);
|
||||
|
||||
} else this.clear();
|
||||
|
||||
} else calcBBox(path[i], this.toBBox);
|
||||
}
|
||||
},
|
||||
|
||||
_initFormat: function (format) {
|
||||
// data format (minX, minY, maxX, maxY accessors)
|
||||
|
||||
// uses eval-type function compilation instead of just accepting a toBBox function
|
||||
// because the algorithms are very sensitive to sorting functions performance,
|
||||
// so they should be dead simple and without inner calls
|
||||
|
||||
var compareArr = ['return a', ' - b', ';'];
|
||||
|
||||
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
|
||||
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
|
||||
|
||||
this.toBBox = new Function('a',
|
||||
'return {minX: a' + format[0] +
|
||||
', minY: a' + format[1] +
|
||||
', maxX: a' + format[2] +
|
||||
', maxY: a' + format[3] + '};');
|
||||
}
|
||||
};
|
||||
|
||||
function findItem(item, items, equalsFn) {
|
||||
if (!equalsFn) return items.indexOf(item);
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (equalsFn(item, items[i])) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// calculate node's bbox from bboxes of its children
|
||||
function calcBBox(node, toBBox) {
|
||||
distBBox(node, 0, node.children.length, toBBox, node);
|
||||
}
|
||||
|
||||
// min bounding rectangle of node children from k to p-1
|
||||
function distBBox(node, k, p, toBBox, destNode) {
|
||||
if (!destNode) destNode = createNode(null);
|
||||
destNode.minX = Infinity;
|
||||
destNode.minY = Infinity;
|
||||
destNode.maxX = -Infinity;
|
||||
destNode.maxY = -Infinity;
|
||||
|
||||
for (var i = k, child; i < p; i++) {
|
||||
child = node.children[i];
|
||||
extend(destNode, node.leaf ? toBBox(child) : child);
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
|
||||
function extend(a, b) {
|
||||
a.minX = Math.min(a.minX, b.minX);
|
||||
a.minY = Math.min(a.minY, b.minY);
|
||||
a.maxX = Math.max(a.maxX, b.maxX);
|
||||
a.maxY = Math.max(a.maxY, b.maxY);
|
||||
return a;
|
||||
}
|
||||
|
||||
function compareNodeMinX(a, b) { return a.minX - b.minX; }
|
||||
function compareNodeMinY(a, b) { return a.minY - b.minY; }
|
||||
|
||||
function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }
|
||||
function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
|
||||
|
||||
function enlargedArea(a, b) {
|
||||
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
|
||||
(Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
|
||||
}
|
||||
|
||||
function intersectionArea(a, b) {
|
||||
var minX = Math.max(a.minX, b.minX),
|
||||
minY = Math.max(a.minY, b.minY),
|
||||
maxX = Math.min(a.maxX, b.maxX),
|
||||
maxY = Math.min(a.maxY, b.maxY);
|
||||
|
||||
return Math.max(0, maxX - minX) *
|
||||
Math.max(0, maxY - minY);
|
||||
}
|
||||
|
||||
function contains(a, b) {
|
||||
return a.minX <= b.minX &&
|
||||
a.minY <= b.minY &&
|
||||
b.maxX <= a.maxX &&
|
||||
b.maxY <= a.maxY;
|
||||
}
|
||||
|
||||
function intersects(a, b) {
|
||||
return b.minX <= a.maxX &&
|
||||
b.minY <= a.maxY &&
|
||||
b.maxX >= a.minX &&
|
||||
b.maxY >= a.minY;
|
||||
}
|
||||
|
||||
function createNode(children) {
|
||||
return {
|
||||
children: children,
|
||||
height: 1,
|
||||
leaf: true,
|
||||
minX: Infinity,
|
||||
minY: Infinity,
|
||||
maxX: -Infinity,
|
||||
maxY: -Infinity
|
||||
};
|
||||
}
|
||||
|
||||
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
|
||||
// combines selection algorithm with binary divide & conquer approach
|
||||
|
||||
function multiSelect(arr, left, right, n, compare) {
|
||||
var stack = [left, right],
|
||||
mid;
|
||||
|
||||
while (stack.length) {
|
||||
right = stack.pop();
|
||||
left = stack.pop();
|
||||
|
||||
if (right - left <= n) continue;
|
||||
|
||||
mid = left + Math.ceil((right - left) / n / 2) * n;
|
||||
quickselect(arr, mid, left, right, compare);
|
||||
|
||||
stack.push(left, mid, mid, right);
|
||||
}
|
||||
}
|
||||
|
||||
},{"quickselect":2}],2:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
module.exports = partialSort;
|
||||
|
||||
// Floyd-Rivest selection algorithm:
|
||||
// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right];
|
||||
// The k-th element will have the (k - left + 1)th smallest value in [left, right]
|
||||
|
||||
function partialSort(arr, k, left, right, compare) {
|
||||
|
||||
while (right > left) {
|
||||
if (right - left > 600) {
|
||||
var n = right - left + 1;
|
||||
var m = k - left + 1;
|
||||
var z = Math.log(n);
|
||||
var s = 0.5 * Math.exp(2 * z / 3);
|
||||
var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
|
||||
var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
|
||||
var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
|
||||
partialSort(arr, k, newLeft, newRight, compare);
|
||||
}
|
||||
|
||||
var t = arr[k];
|
||||
var i = left;
|
||||
var j = right;
|
||||
|
||||
swap(arr, left, k);
|
||||
if (compare(arr[right], t) > 0) swap(arr, left, right);
|
||||
|
||||
while (i < j) {
|
||||
swap(arr, i, j);
|
||||
i++;
|
||||
j--;
|
||||
while (compare(arr[i], t) < 0) i++;
|
||||
while (compare(arr[j], t) > 0) j--;
|
||||
}
|
||||
|
||||
if (compare(arr[left], t) === 0) swap(arr, left, j);
|
||||
else {
|
||||
j++;
|
||||
swap(arr, j, right);
|
||||
}
|
||||
|
||||
if (j <= k) left = j + 1;
|
||||
if (k <= j) right = j - 1;
|
||||
}
|
||||
}
|
||||
|
||||
function swap(arr, i, j) {
|
||||
var tmp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = tmp;
|
||||
}
|
||||
|
||||
function defaultCompare(a, b) {
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}
|
||||
|
||||
},{}]},{},[1])(1)
|
||||
});
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* OnceTriggers is used to store the status of the conditions "Trigger once",
|
||||
* that are used in events to have conditions that are only valid for one frame in a row.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class OnceTriggers
|
||||
* @constructor
|
||||
*/
|
||||
gdjs.OnceTriggers = function()
|
||||
{
|
||||
this._onceTriggers = {};
|
||||
this._lastFrameOnceTrigger = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* To be called when events begin so that "Trigger once" conditions
|
||||
* are properly handled.
|
||||
*/
|
||||
gdjs.OnceTriggers.prototype.startNewFrame = function() {
|
||||
this._clearObject(this._lastFrameOnceTrigger);
|
||||
for (var k in this._onceTriggers) {
|
||||
if (this._onceTriggers.hasOwnProperty(k)) {
|
||||
this._lastFrameOnceTrigger[k] = this._onceTriggers[k];
|
||||
delete this._onceTriggers[k];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used by "Trigger once" conditions: return true only if
|
||||
* this method was not called with the same identifier during the last frame.
|
||||
* @param triggerId The identifier of the "Trigger once" condition.
|
||||
*/
|
||||
gdjs.OnceTriggers.prototype.triggerOnce = function(triggerId) {
|
||||
this._onceTriggers[triggerId] = true;
|
||||
|
||||
return !this._lastFrameOnceTrigger.hasOwnProperty(triggerId);
|
||||
};
|
||||
|
||||
gdjs.OnceTriggers.prototype._clearObject = function(obj) {
|
||||
for (var k in obj) {
|
||||
if (obj.hasOwnProperty(k)) {
|
||||
delete obj[k];
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,228 @@
|
|||
// @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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The renderer for a gdjs.Layer using Pixi.js.
|
||||
*
|
||||
* @class LayerPixiRenderer
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.Layer} layer The layer
|
||||
* @param {gdjs.RuntimeScenePixiRenderer} runtimeSceneRenderer The scene renderer
|
||||
*/
|
||||
gdjs.LayerPixiRenderer = function(layer, runtimeSceneRenderer) {
|
||||
// @ts-ignore
|
||||
this._pixiContainer = new PIXI.Container();
|
||||
/** @type Object.<string, gdjsPixiFiltersToolsFilter> */
|
||||
this._filters = {};
|
||||
this._layer = layer;
|
||||
runtimeSceneRenderer.getPIXIContainer().addChild(this._pixiContainer);
|
||||
|
||||
this._setupFilters();
|
||||
};
|
||||
|
||||
gdjs.LayerRenderer = gdjs.LayerPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Update the position of the PIXI container. To be called after each change
|
||||
* made to position, zoom or rotation of the camera.
|
||||
* @private
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.updatePosition = function() {
|
||||
var angle = -gdjs.toRad(this._layer.getCameraRotation());
|
||||
var zoomFactor = this._layer.getCameraZoom();
|
||||
|
||||
this._pixiContainer.rotation = angle;
|
||||
this._pixiContainer.scale.x = zoomFactor;
|
||||
this._pixiContainer.scale.y = zoomFactor;
|
||||
|
||||
var cosValue = Math.cos(angle);
|
||||
var sinValue = Math.sin(angle);
|
||||
var centerX =
|
||||
this._layer.getCameraX() * zoomFactor * cosValue -
|
||||
this._layer.getCameraY() * zoomFactor * sinValue;
|
||||
var centerY =
|
||||
this._layer.getCameraX() * zoomFactor * sinValue +
|
||||
this._layer.getCameraY() * zoomFactor * cosValue;
|
||||
|
||||
this._pixiContainer.position.x = -centerX;
|
||||
this._pixiContainer.position.y = -centerY;
|
||||
this._pixiContainer.position.x += this._layer.getWidth() / 2;
|
||||
this._pixiContainer.position.y += this._layer.getHeight() / 2;
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.updateVisibility = function(visible) {
|
||||
this._pixiContainer.visible = !!visible;
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype.updateTime = function() {
|
||||
for(var filterName in this._filters) {
|
||||
var filter = this._filters[filterName];
|
||||
filter.update(filter.pixiFilter, this._layer);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LayerPixiRenderer.prototype._setupFilters = function() {
|
||||
var effects = this._layer.getEffects();
|
||||
if (effects.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._filters = {};
|
||||
|
||||
// @ts-ignore
|
||||
/** @type PIXI.Filter[] */
|
||||
var pixiFilters = [];
|
||||
for (var i = 0; i < effects.length; ++i) {
|
||||
var effect = effects[i];
|
||||
var filterCreator = gdjs.PixiFiltersTools.getFilterCreator(
|
||||
effect.effectType
|
||||
);
|
||||
if (!filterCreator) {
|
||||
console.log(
|
||||
'Filter "' +
|
||||
effect.name +
|
||||
'" has an unknown effect type: "' +
|
||||
effect.effectType +
|
||||
'". Was it registered properly? Is the effect type correct?'
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @type gdjsPixiFiltersToolsFilter */
|
||||
var filter = {
|
||||
pixiFilter: filterCreator.makePIXIFilter(this._layer, effect),
|
||||
updateDoubleParameter: filterCreator.updateDoubleParameter,
|
||||
updateStringParameter: filterCreator.updateStringParameter,
|
||||
updateBooleanParameter: filterCreator.updateBooleanParameter,
|
||||
update: filterCreator.update,
|
||||
};
|
||||
|
||||
pixiFilters.push(filter.pixiFilter);
|
||||
this._filters[effect.name] = filter;
|
||||
}
|
||||
|
||||
this._pixiContainer.filters = pixiFilters;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a child to the pixi container associated to the layer.
|
||||
* All objects which are on this layer must be children of this container.
|
||||
*
|
||||
* @param child The child (PIXI object) to be added.
|
||||
* @param zOrder The z order of the associated object.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.addRendererObject = function(child, zOrder) {
|
||||
child.zOrder = zOrder; //Extend the pixi object with a z order.
|
||||
|
||||
for (var i = 0, len = this._pixiContainer.children.length; i < len; ++i) {
|
||||
if (this._pixiContainer.children[i].zOrder >= zOrder) {
|
||||
//TODO : Dichotomic search
|
||||
this._pixiContainer.addChildAt(child, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._pixiContainer.addChild(child);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the z order of a child associated to an object.
|
||||
*
|
||||
* @param child The child (PIXI object) to be modified.
|
||||
* @param newZOrder The z order of the associated object.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.changeRendererObjectZOrder = function(
|
||||
child,
|
||||
newZOrder
|
||||
) {
|
||||
this._pixiContainer.removeChild(child);
|
||||
this.addRendererObject(child, newZOrder);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a child from the internal pixi container.
|
||||
* Should be called when an object is deleted or removed from the layer.
|
||||
*
|
||||
* @param child The child (PIXI object) to be removed.
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.removeRendererObject = function(child) {
|
||||
this._pixiContainer.removeChild(child);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the parameter of an effect (with a number).
|
||||
* @param {string} name The effect name
|
||||
* @param {string} parameterName The parameter name
|
||||
* @param {number} value The new value for the parameter
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectDoubleParameter = function(
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return;
|
||||
|
||||
filter.updateDoubleParameter(filter.pixiFilter, parameterName, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the parameter of an effect (with a string).
|
||||
* @param {string} name The effect name
|
||||
* @param {string} parameterName The parameter name
|
||||
* @param {string} value The new value for the parameter
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectStringParameter = function(
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return;
|
||||
|
||||
filter.updateStringParameter(filter.pixiFilter, parameterName, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable or disable the parameter of an effect (boolean).
|
||||
* @param {string} name The effect name
|
||||
* @param {string} parameterName The parameter name
|
||||
* @param {boolean} value The new value for the parameter
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.setEffectBooleanParameter = function(
|
||||
name,
|
||||
parameterName,
|
||||
value
|
||||
) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return;
|
||||
|
||||
filter.updateBooleanParameter(filter.pixiFilter, parameterName, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable an effect.
|
||||
* @param {string} name The effect name
|
||||
* @param {boolean} value Set to true to enable, false to disable
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.enableEffect = function(name, value) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return;
|
||||
|
||||
gdjs.PixiFiltersTools.enableEffect(filter, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if an effect is enabled.
|
||||
* @param {string} name The effect name
|
||||
* @return {boolean} true if the filter is enabled
|
||||
*/
|
||||
gdjs.LayerPixiRenderer.prototype.isEffectEnabled = function(name) {
|
||||
var filter = this._filters[name];
|
||||
if (!filter) return false;
|
||||
|
||||
return gdjs.PixiFiltersTools.isEffectEnabled(filter);
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
gdjs.LoadingScreenPixiRenderer = function(runtimeGamePixiRenderer, loadingScreenSetup) {
|
||||
this._pixiRenderer = runtimeGamePixiRenderer.getPIXIRenderer();
|
||||
if (!this._pixiRenderer) {
|
||||
// A PIXI Renderer can be missing during tests, when creating a runtime game
|
||||
// without a canvas.
|
||||
return;
|
||||
}
|
||||
|
||||
this._loadingScreen = new PIXI.Container();
|
||||
|
||||
this._progressText = new PIXI.Text(' ', {
|
||||
fontSize: '30px',
|
||||
fontFamily: 'Arial',
|
||||
fill: '#FFFFFF',
|
||||
align: 'center',
|
||||
});
|
||||
this._loadingScreen.addChild(this._progressText);
|
||||
|
||||
if (loadingScreenSetup && loadingScreenSetup.showGDevelopSplash) {
|
||||
this._madeWithText = new PIXI.Text('Made with', {
|
||||
fontSize: '30px',
|
||||
fontFamily: 'Arial',
|
||||
fill: '#FFFFFF',
|
||||
align: 'center',
|
||||
});
|
||||
this._madeWithText.position.y = this._pixiRenderer.height / 2 - 130;
|
||||
this._websiteText = new PIXI.Text('gdevelop-app.com', {
|
||||
fontSize: '30px',
|
||||
fontFamily: 'Arial',
|
||||
fill: '#FFFFFF',
|
||||
align: 'center',
|
||||
});
|
||||
this._websiteText.position.y = this._pixiRenderer.height / 2 + 100;
|
||||
|
||||
this._splashImage = new PIXI.Sprite.fromImage(gdjs.splashImage);
|
||||
this._splashImage.position.x = this._pixiRenderer.width / 2;
|
||||
this._splashImage.position.y = this._pixiRenderer.height / 2;
|
||||
this._splashImage.anchor.x = 0.5;
|
||||
this._splashImage.anchor.y = 0.5;
|
||||
this._splashImage.scale.x = this._pixiRenderer.width / 800;
|
||||
this._splashImage.scale.y = this._pixiRenderer.width / 800;
|
||||
this._loadingScreen.addChild(this._splashImage);
|
||||
this._loadingScreen.addChild(this._madeWithText);
|
||||
this._loadingScreen.addChild(this._websiteText);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.LoadingScreenRenderer = gdjs.LoadingScreenPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.LoadingScreenPixiRenderer.prototype.render = function(percent) {
|
||||
if (!this._pixiRenderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var screenBorder = 10;
|
||||
|
||||
if (this._madeWithText) {
|
||||
this._madeWithText.position.x =
|
||||
this._pixiRenderer.width / 2 - this._madeWithText.width / 2;
|
||||
this._madeWithText.position.y =
|
||||
this._pixiRenderer.height / 2 -
|
||||
this._splashImage.height / 2 -
|
||||
this._madeWithText.height -
|
||||
20;
|
||||
}
|
||||
if (this._websiteText) {
|
||||
this._websiteText.position.x =
|
||||
this._pixiRenderer.width - this._websiteText.width - screenBorder;
|
||||
this._websiteText.position.y =
|
||||
this._pixiRenderer.height - this._websiteText.height - screenBorder;
|
||||
}
|
||||
|
||||
this._progressText.text = percent + '%';
|
||||
this._progressText.position.x = screenBorder;
|
||||
this._progressText.position.y =
|
||||
this._pixiRenderer.height - this._progressText.height - screenBorder;
|
||||
|
||||
this._pixiRenderer.render(this._loadingScreen);
|
||||
};
|
||||
|
||||
gdjs.LoadingScreenPixiRenderer.prototype.unload = function() {
|
||||
// Nothing to do
|
||||
};
|
|
@ -0,0 +1,120 @@
|
|||
// @ts-check
|
||||
/**
|
||||
* Contains tools related to PIXI filters handling.
|
||||
*/
|
||||
gdjs.PixiFiltersTools = {};
|
||||
|
||||
gdjs.PixiFiltersTools.clampValue = function(value, min, max) { return Math.max(min, Math.min(max, value)); };
|
||||
gdjs.PixiFiltersTools.clampKernelSize = function(value) { return (([5, 7, 9, 11, 13, 15].indexOf(value) !== -1) ? value : 5); };
|
||||
|
||||
/** Object.<string, gdjsPixiFiltersToolsFilterCreator> */
|
||||
gdjs.PixiFiltersTools._filterCreators = {};
|
||||
|
||||
/**
|
||||
* Enable an effect.
|
||||
* @param {gdjsPixiFiltersToolsFilter} filter The filter to enable or disable
|
||||
* @param {boolean} value Set to true to enable, false to disable
|
||||
*/
|
||||
gdjs.PixiFiltersTools.enableEffect = function(filter, value) {
|
||||
filter.pixiFilter.enabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an effect is enabled.
|
||||
* @param {gdjsPixiFiltersToolsFilter} filter The filter to be checked
|
||||
* @return {boolean} true if the filter is enabled
|
||||
*/
|
||||
gdjs.PixiFiltersTools.isEffectEnabled = function(filter) {
|
||||
return filter.pixiFilter.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the creator for the filter with the given name, if any.
|
||||
* @param {string} filterName The name of the filter to get
|
||||
* @return {?gdjsPixiFiltersToolsFilterCreator} The filter creator, if any (null otherwise).
|
||||
*/
|
||||
gdjs.PixiFiltersTools.getFilterCreator = function(filterName) {
|
||||
if (gdjs.PixiFiltersTools._filterCreators.hasOwnProperty(filterName))
|
||||
return gdjs.PixiFiltersTools._filterCreators[filterName];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new PIXI filter creator, to be used by GDJS.
|
||||
* @param {string} filterName The name of the filter to get
|
||||
* @param {gdjsPixiFiltersToolsFilterCreator} filterCreator The object used to create the filter.
|
||||
*/
|
||||
gdjs.PixiFiltersTools.registerFilterCreator = function(filterName, filterCreator) {
|
||||
if (gdjs.PixiFiltersTools._filterCreators.hasOwnProperty(filterName))
|
||||
console.warn("Filter \"" + filterName + "\" was already registered in gdjs.PixiFiltersTools. Replacing it with the new one.");
|
||||
|
||||
gdjs.PixiFiltersTools._filterCreators[filterName] = filterCreator;
|
||||
}
|
||||
|
||||
// Type definitions:
|
||||
|
||||
/**
|
||||
* Function to call to create the PIXI filter used at runtime
|
||||
* @callback gdjsPixiFiltersToolsFilterCreatorMakePIXIFilter
|
||||
* @param {gdjs.Layer} layer
|
||||
* @param {Object} effectData
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The function to be called to update the filter at every frame
|
||||
* @callback gdjsPixiFiltersToolsUpdate
|
||||
* @param {Object} filter
|
||||
* @param {gdjs.Layer} layer
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The function to be called to update a parameter (with a number)
|
||||
* @callback gdjsPixiFiltersToolsUpdateDoubleParameter
|
||||
* @param {Object} filter
|
||||
* @param {string} parameterName
|
||||
* @param {number} value
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The function to be called to update a parameter (with a string)
|
||||
* @callback gdjsPixiFiltersToolsUpdateStringParameter
|
||||
* @param {Object} filter
|
||||
* @param {string} parameterName
|
||||
* @param {string} value
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* The function to be called to update a parameter (with a boolean)
|
||||
* @callback gdjsPixiFiltersToolsUpdateBooleanParameter
|
||||
* @param {Object} filter
|
||||
* @param {string} parameterName
|
||||
* @param {boolean} value
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* A wrapper allowing to create a PIXI filter and update it using a common interface
|
||||
* @typedef gdjsPixiFiltersToolsFilterCreator
|
||||
* @type {Object}
|
||||
* @property {gdjsPixiFiltersToolsFilterCreatorMakePIXIFilter} makePIXIFilter Function to call to create the filter
|
||||
* @property {gdjsPixiFiltersToolsUpdate} update The function to be called to update the filter at every frame
|
||||
* @property {gdjsPixiFiltersToolsUpdateDoubleParameter} updateDoubleParameter The function to be called to update a parameter (with a number)
|
||||
* @property {gdjsPixiFiltersToolsUpdateStringParameter} updateStringParameter The function to be called to update a parameter (with a string)
|
||||
* @property {gdjsPixiFiltersToolsUpdateBooleanParameter} updateBooleanParameter The function to be called to update a parameter (with a boolean)
|
||||
*/
|
||||
|
||||
/**
|
||||
* The type of a filter used to manipulate a Pixi filter.
|
||||
* @typedef gdjsPixiFiltersToolsFilter
|
||||
* @type {Object}
|
||||
* @property {any} pixiFilter The PIXI filter
|
||||
* @property {gdjsPixiFiltersToolsUpdate} update The function to be called to update the filter at every frame
|
||||
* @property {gdjsPixiFiltersToolsUpdateDoubleParameter} updateDoubleParameter The function to be called to update a parameter (with a number)
|
||||
* @property {gdjsPixiFiltersToolsUpdateStringParameter} updateStringParameter The function to be called to update a parameter (with a string)
|
||||
* @property {gdjsPixiFiltersToolsUpdateBooleanParameter} updateBooleanParameter The function to be called to update a parameter (with a boolean)
|
||||
*/
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PixiImageManager loads and stores textures that can be used by the Pixi.js renderers.
|
||||
*
|
||||
* @class PixiImageManager
|
||||
* @memberof gdjs
|
||||
* @param {Object} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.PixiImageManager = function(resources)
|
||||
{
|
||||
this._resources = resources;
|
||||
|
||||
// The invalid texture is a 8x8 PNG file filled with magenta (#ff00ff), to be
|
||||
// easily spotted if rendered on screen.
|
||||
this._invalidTexture = PIXI.Texture.from("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFElEQVQoU2P8z/D/PwMewDgyFAAApMMX8Zi0uXAAAAAASUVORK5CYIIA");
|
||||
this._loadedTextures = new Hashtable();
|
||||
};
|
||||
|
||||
gdjs.ImageManager = gdjs.PixiImageManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Return the PIXI texture associated to the specified resource name.
|
||||
* Returns a placeholder texture if not found.
|
||||
* @param {string} resourceName The name of the resource
|
||||
* @returns {PIXI.Texture} The requested texture, or a placeholder if not found.
|
||||
*/
|
||||
gdjs.PixiImageManager.prototype.getPIXITexture = function(resourceName) {
|
||||
if ( this._loadedTextures.containsKey(resourceName) ) {
|
||||
return this._loadedTextures.get(resourceName);
|
||||
}
|
||||
if ( resourceName === "" ) {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
//Texture is not loaded, load it now from the resources list.
|
||||
if ( this._resources ) {
|
||||
var texture = null;
|
||||
|
||||
for(var i = 0, len = this._resources.length;i<len;++i) {
|
||||
var res = this._resources[i];
|
||||
|
||||
if (res.name === resourceName && res.kind === "image") {
|
||||
texture = PIXI.Texture.fromImage(res.file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( texture !== null ) {
|
||||
console.log("Loaded texture for resource \""+resourceName+"\".");
|
||||
this._loadedTextures.put(resourceName, texture);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn("Unable to find texture for resource \""+resourceName+"\".");
|
||||
return this._invalidTexture;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the PIXI video texture associated to the specified resource name.
|
||||
* Returns a placeholder texture if not found.
|
||||
* @param {string} resourceName The name of the resource to get.
|
||||
*/
|
||||
gdjs.PixiImageManager.prototype.getPIXIVideoTexture = function(resourceName) {
|
||||
if ( this._loadedTextures.containsKey(resourceName) ) {
|
||||
return this._loadedTextures.get(resourceName);
|
||||
}
|
||||
if ( resourceName === "" ) {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
//Texture is not loaded, load it now from the resources list.
|
||||
if ( this._resources ) {
|
||||
var texture = null;
|
||||
|
||||
for(var i = 0, len = this._resources.length;i<len;++i) {
|
||||
var res = this._resources[i];
|
||||
|
||||
if (res.name === resourceName && res.kind === "video") {
|
||||
texture = PIXI.Texture.fromVideo(res.file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( texture !== null ) {
|
||||
console.log("Loaded video texture for resource \""+resourceName+"\".");
|
||||
this._loadedTextures.put(resourceName, texture);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn("Unable to find video texture for resource \""+resourceName+"\".");
|
||||
return this._invalidTexture;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a PIXI texture which can be used as a placeholder when no
|
||||
* suitable texture can be found.
|
||||
*/
|
||||
gdjs.PixiImageManager.prototype.getInvalidPIXITexture = function() {
|
||||
return this._invalidTexture;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the specified resources, so that textures are loaded and can then be
|
||||
* used by calling `getPIXITexture`.
|
||||
* @param onProgress Callback called each time a new file is loaded.
|
||||
* @param onComplete Callback called when loading is done.
|
||||
* @param resources The resources to be loaded. If not specified, will load the resources
|
||||
* specified in the PixiImageManager constructor.
|
||||
*/
|
||||
gdjs.PixiImageManager.prototype.loadTextures = function(onProgress, onComplete, resources) {
|
||||
resources = resources || this._resources;
|
||||
|
||||
//Construct the list of files to be loaded.
|
||||
//For one loaded file, it can have one or more resources
|
||||
//that use it.
|
||||
var files = {};
|
||||
for(var i = 0, len = resources.length;i<len;++i) {
|
||||
var res = resources[i];
|
||||
|
||||
if ( res.file && res.kind === "image" ) {
|
||||
if (this._loadedTextures.containsKey(res.name)) {
|
||||
console.log("Texture \"" + res.name + "\" is already loaded.");
|
||||
continue;
|
||||
}
|
||||
|
||||
files[res.file] = files[res.file] ? files[res.file].concat(res) : [res];
|
||||
}
|
||||
}
|
||||
|
||||
var totalCount = Object.keys(files).length;
|
||||
if (totalCount === 0)
|
||||
return onComplete(totalCount); //Nothing to load.
|
||||
|
||||
var loadingCount = 0;
|
||||
var loader = PIXI.loader;
|
||||
var that = this;
|
||||
loader.once('complete', function(loader, loadedFiles) {
|
||||
//Store the loaded textures so that they are ready to use.
|
||||
for (var file in loadedFiles) {
|
||||
if (loadedFiles.hasOwnProperty(file)) {
|
||||
if (!files.hasOwnProperty(file)) continue;
|
||||
|
||||
files[file].forEach(function(res) {
|
||||
that._loadedTextures.put(res.name, loadedFiles[file].texture);
|
||||
if (!res.smoothed) {
|
||||
loadedFiles[file].texture.baseTexture.scaleMode =
|
||||
PIXI.SCALE_MODES.NEAREST;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onComplete(totalCount);
|
||||
});
|
||||
loader.on('progress', function() {
|
||||
loadingCount++;
|
||||
onProgress(loadingCount, totalCount);
|
||||
});
|
||||
|
||||
for (var file in files) {
|
||||
if (files.hasOwnProperty(file)) {
|
||||
loader.add(file, file);
|
||||
}
|
||||
}
|
||||
|
||||
loader.load();
|
||||
}
|
|
@ -0,0 +1,496 @@
|
|||
/**
|
||||
* The renderer for a gdjs.RuntimeGame using Pixi.js.
|
||||
* @class RuntimeGamePixiRenderer
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.RuntimeGame} game The game that is being rendered
|
||||
* @param {boolean} forceFullscreen If fullscreen should be always activated
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer = function(game, forceFullscreen) {
|
||||
this._game = game;
|
||||
|
||||
this._isFullPage = true; //Used to track if the canvas is displayed on the full page.
|
||||
this._isFullscreen = false; //Used to track if the window is displayed as fullscreen (see setFullscreen method).
|
||||
this._forceFullscreen = forceFullscreen; //If set to true, the canvas will always be displayed as fullscreen, even if _isFullscreen == false.
|
||||
|
||||
/** @type {PIXI.SystemRenderer} */
|
||||
this._pixiRenderer = null;
|
||||
this._canvasWidth = 0; // Current width of the canvas (might be scaled down/up compared to renderer)
|
||||
this._canvasHeight = 0; // Current height of the canvas (might be scaled down/up compared to renderer)
|
||||
this._keepRatio = true;
|
||||
this._marginLeft = this._marginTop = this._marginRight = this._marginBottom = 0;
|
||||
};
|
||||
|
||||
gdjs.RuntimeGameRenderer = gdjs.RuntimeGamePixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Create a standard canvas inside canvasArea.
|
||||
*
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.createStandardCanvas = function(
|
||||
parentElement
|
||||
) {
|
||||
//This prevents flickering on some mobile devices
|
||||
PIXI.glCore.VertexArrayObject.FORCE_NATIVE = true;
|
||||
|
||||
//Create the renderer and setup the rendering area
|
||||
//"preserveDrawingBuffer: true" is needed to avoid flickering and background issues on some mobile phones (see #585 #572 #566 #463)
|
||||
this._pixiRenderer = PIXI.autoDetectRenderer(
|
||||
this._game.getGameResolutionWidth(),
|
||||
this._game.getGameResolutionHeight(),
|
||||
{
|
||||
preserveDrawingBuffer: true,
|
||||
antialias: false,
|
||||
}
|
||||
);
|
||||
parentElement.appendChild(this._pixiRenderer.view); // add the renderer view element to the DOM
|
||||
this._pixiRenderer.view.style['position'] = 'absolute';
|
||||
this._pixiRenderer.view.tabindex = '1'; //Ensure that the canvas has the focus.
|
||||
this._resizeCanvas();
|
||||
|
||||
// Handle scale mode
|
||||
if (this._game.getScaleMode() === 'nearest') {
|
||||
this._pixiRenderer.view.style['image-rendering'] = '-moz-crisp-edges';
|
||||
this._pixiRenderer.view.style['image-rendering'] =
|
||||
'-webkit-optimize-contrast';
|
||||
this._pixiRenderer.view.style['image-rendering'] = '-webkit-crisp-edges';
|
||||
this._pixiRenderer.view.style['image-rendering'] = 'pixelated';
|
||||
}
|
||||
|
||||
//Handle resize
|
||||
var that = this;
|
||||
window.addEventListener('resize', function() {
|
||||
that._game.onWindowInnerSizeChanged();
|
||||
that._resizeCanvas();
|
||||
that._game._notifySceneForResize = true;
|
||||
});
|
||||
|
||||
return this._pixiRenderer;
|
||||
};
|
||||
|
||||
gdjs.RuntimeGamePixiRenderer.getWindowInnerWidth = function() {
|
||||
return typeof window !== 'undefined' ? window.innerWidth : 800;
|
||||
};
|
||||
|
||||
gdjs.RuntimeGamePixiRenderer.getWindowInnerHeight = function() {
|
||||
return typeof window !== 'undefined' ? window.innerHeight : 800;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the game renderer size according to the "game resolution".
|
||||
* Called when game resolution changes.
|
||||
*
|
||||
* Note that if the canvas is fullscreen, it won't be resized, but when going back to
|
||||
* non fullscreen mode, the requested size will be used.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.updateRendererSize = function() {
|
||||
this._resizeCanvas();
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize the renderer (the "game resolution") and the canvas (which can be larger
|
||||
* or smaller to fill the page, with optional margins).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype._resizeCanvas = function() {
|
||||
// Set the Pixi renderer size to the game size.
|
||||
// There is no "smart" resizing to be done here: the rendering of the game
|
||||
// should be done with the size set on the game.
|
||||
if (
|
||||
this._pixiRenderer.width !== this._game.getGameResolutionWidth() ||
|
||||
this._pixiRenderer.height !== this._game.getGameResolutionHeight()
|
||||
) {
|
||||
this._pixiRenderer.resize(
|
||||
this._game.getGameResolutionWidth(),
|
||||
this._game.getGameResolutionHeight()
|
||||
);
|
||||
}
|
||||
|
||||
// Set the canvas size.
|
||||
// Resizing is done according to the settings. This is a "CSS" resize
|
||||
// only, so won't create visual artifacts during the rendering.
|
||||
var isFullPage =
|
||||
this._forceFullscreen || this._isFullPage || this._isFullscreen;
|
||||
var canvasWidth = this._game.getGameResolutionWidth();
|
||||
var canvasHeight = this._game.getGameResolutionHeight();
|
||||
var maxWidth = window.innerWidth - this._marginLeft - this._marginRight;
|
||||
var maxHeight = window.innerHeight - this._marginTop - this._marginBottom;
|
||||
if (maxWidth < 0) maxWidth = 0;
|
||||
if (maxHeight < 0) maxHeight = 0;
|
||||
|
||||
if (isFullPage && !this._keepRatio) {
|
||||
canvasWidth = maxWidth;
|
||||
canvasHeight = maxHeight;
|
||||
} else if (
|
||||
(isFullPage && this._keepRatio) ||
|
||||
canvasWidth > maxWidth ||
|
||||
canvasHeight > maxHeight
|
||||
) {
|
||||
var factor = maxWidth / canvasWidth;
|
||||
if (canvasHeight * factor > maxHeight) factor = maxHeight / canvasHeight;
|
||||
|
||||
canvasWidth *= factor;
|
||||
canvasHeight *= factor;
|
||||
}
|
||||
|
||||
this._pixiRenderer.view.style['top'] =
|
||||
this._marginTop + (maxHeight - canvasHeight) / 2 + 'px';
|
||||
this._pixiRenderer.view.style['left'] =
|
||||
this._marginLeft + (maxWidth - canvasWidth) / 2 + 'px';
|
||||
this._pixiRenderer.view.style.width = canvasWidth + 'px';
|
||||
this._pixiRenderer.view.style.height = canvasHeight + 'px';
|
||||
|
||||
// Store the canvas size for fast access to it.
|
||||
this._canvasWidth = canvasWidth;
|
||||
this._canvasHeight = canvasHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set if the aspect ratio must be kept when the game canvas is resized to fill
|
||||
* the page.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.keepAspectRatio = function(enable) {
|
||||
if (this._keepRatio === enable) return;
|
||||
|
||||
this._keepRatio = enable;
|
||||
this._resizeCanvas();
|
||||
this._game._notifySceneForResize = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the margin that must be preserved around the game canvas.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.setMargins = function(
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left
|
||||
) {
|
||||
if (
|
||||
this._marginTop === top &&
|
||||
this._marginRight === right &&
|
||||
this._marginBottom === bottom &&
|
||||
this._marginLeft === left
|
||||
)
|
||||
return;
|
||||
|
||||
this._marginTop = top;
|
||||
this._marginRight = right;
|
||||
this._marginBottom = bottom;
|
||||
this._marginLeft = left;
|
||||
this._resizeCanvas();
|
||||
this._game._notifySceneForResize = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the window size, if possible.
|
||||
* @param {number} width The new width, in pixels.
|
||||
* @param {number} height The new height, in pixels.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.setWindowSize = function(width, height) {
|
||||
var electron = this.getElectron();
|
||||
if (electron) {
|
||||
// Use Electron BrowserWindow API
|
||||
var browserWindow = electron.remote.getCurrentWindow();
|
||||
if (browserWindow) {
|
||||
browserWindow.setContentSize(width, height);
|
||||
}
|
||||
} else {
|
||||
console.warn("Window size can't be changed on this platform.");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Center the window on screen.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.centerWindow = function() {
|
||||
var electron = this.getElectron();
|
||||
if (electron) {
|
||||
// Use Electron BrowserWindow API
|
||||
var browserWindow = electron.remote.getCurrentWindow();
|
||||
if (browserWindow) {
|
||||
browserWindow.center();
|
||||
}
|
||||
} else {
|
||||
// Not supported
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* De/activate fullscreen for the game.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.setFullScreen = function(enable) {
|
||||
if (this._forceFullscreen) return;
|
||||
|
||||
if (this._isFullscreen !== enable) {
|
||||
this._isFullscreen = !!enable;
|
||||
|
||||
var electron = this.getElectron();
|
||||
if (electron) {
|
||||
// Use Electron BrowserWindow API
|
||||
var browserWindow = electron.remote.getCurrentWindow();
|
||||
if (browserWindow) {
|
||||
browserWindow.setFullScreen(this._isFullscreen);
|
||||
}
|
||||
} else {
|
||||
// Use HTML5 Fullscreen API
|
||||
//TODO: Do this on a user gesture, otherwise most browsers won't activate fullscreen
|
||||
if (this._isFullscreen) {
|
||||
if (document.documentElement.requestFullScreen) {
|
||||
document.documentElement.requestFullScreen();
|
||||
} else if (document.documentElement.mozRequestFullScreen) {
|
||||
document.documentElement.mozRequestFullScreen();
|
||||
} else if (document.documentElement.webkitRequestFullScreen) {
|
||||
document.documentElement.webkitRequestFullScreen();
|
||||
}
|
||||
} else {
|
||||
if (document.cancelFullScreen) {
|
||||
document.cancelFullScreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._resizeCanvas();
|
||||
this._notifySceneForResize = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the standard events handler.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.bindStandardEvents = function(
|
||||
manager,
|
||||
window,
|
||||
document
|
||||
) {
|
||||
var renderer = this._pixiRenderer;
|
||||
var canvas = renderer.view;
|
||||
|
||||
//Translate an event (mouse or touch) made on the canvas on the page
|
||||
//to game coordinates.
|
||||
var that = this;
|
||||
function getEventPosition(e) {
|
||||
var pos = [0, 0];
|
||||
if (e.pageX) {
|
||||
pos[0] = e.pageX - canvas.offsetLeft;
|
||||
pos[1] = e.pageY - canvas.offsetTop;
|
||||
} else {
|
||||
pos[0] =
|
||||
e.clientX +
|
||||
document.body.scrollLeft +
|
||||
document.documentElement.scrollLeft -
|
||||
canvas.offsetLeft;
|
||||
pos[1] =
|
||||
e.clientY +
|
||||
document.body.scrollTop +
|
||||
document.documentElement.scrollTop -
|
||||
canvas.offsetTop;
|
||||
}
|
||||
|
||||
//Handle the fact that the game is stretched to fill the canvas.
|
||||
pos[0] *= that._game.getGameResolutionWidth() / (that._canvasWidth || 1);
|
||||
pos[1] *= that._game.getGameResolutionHeight() / (that._canvasHeight || 1);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
//Some browsers lacks definition of some variables used to do calculations
|
||||
//in getEventPosition. They are defined to 0 as they are useless.
|
||||
(function ensureOffsetsExistence() {
|
||||
if (isNaN(canvas.offsetLeft)) {
|
||||
canvas.offsetLeft = 0;
|
||||
canvas.offsetTop = 0;
|
||||
}
|
||||
if (isNaN(document.body.scrollLeft)) {
|
||||
document.body.scrollLeft = 0;
|
||||
document.body.scrollTop = 0;
|
||||
}
|
||||
if (
|
||||
document.documentElement === undefined ||
|
||||
document.documentElement === null
|
||||
) {
|
||||
document.documentElement = {};
|
||||
}
|
||||
if (isNaN(document.documentElement.scrollLeft)) {
|
||||
document.documentElement.scrollLeft = 0;
|
||||
document.documentElement.scrollTop = 0;
|
||||
}
|
||||
if (isNaN(canvas.offsetLeft)) {
|
||||
canvas.offsetLeft = 0;
|
||||
canvas.offsetTop = 0;
|
||||
}
|
||||
})();
|
||||
|
||||
//Keyboard
|
||||
document.onkeydown = function(e) {
|
||||
manager.onKeyPressed(e.keyCode);
|
||||
};
|
||||
document.onkeyup = function(e) {
|
||||
manager.onKeyReleased(e.keyCode);
|
||||
};
|
||||
//Mouse
|
||||
renderer.view.onmousemove = function(e) {
|
||||
var pos = getEventPosition(e);
|
||||
manager.onMouseMove(pos[0], pos[1]);
|
||||
};
|
||||
renderer.view.onmousedown = function(e) {
|
||||
manager.onMouseButtonPressed(
|
||||
e.button === 2
|
||||
? gdjs.InputManager.MOUSE_RIGHT_BUTTON
|
||||
: e.button === 1
|
||||
? gdjs.InputManager.MOUSE_MIDDLE_BUTTON
|
||||
: gdjs.InputManager.MOUSE_LEFT_BUTTON
|
||||
);
|
||||
if (window.focus !== undefined) window.focus();
|
||||
return false;
|
||||
};
|
||||
renderer.view.onmouseup = function(e) {
|
||||
manager.onMouseButtonReleased(
|
||||
e.button === 2
|
||||
? gdjs.InputManager.MOUSE_RIGHT_BUTTON
|
||||
: e.button === 1
|
||||
? gdjs.InputManager.MOUSE_MIDDLE_BUTTON
|
||||
: gdjs.InputManager.MOUSE_LEFT_BUTTON
|
||||
);
|
||||
return false;
|
||||
};
|
||||
window.addEventListener(
|
||||
'click',
|
||||
function(e) {
|
||||
if (window.focus !== undefined) window.focus();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
},
|
||||
false
|
||||
);
|
||||
renderer.view.oncontextmenu = function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
};
|
||||
renderer.view.onmousewheel = function(event) {
|
||||
manager.onMouseWheel(event.wheelDelta);
|
||||
};
|
||||
//Touches
|
||||
//Also simulate mouse events when receiving touch events
|
||||
window.addEventListener('touchmove', function(e) {
|
||||
e.preventDefault();
|
||||
if (e.changedTouches) {
|
||||
for (var i = 0; i < e.changedTouches.length; ++i) {
|
||||
var pos = getEventPosition(e.changedTouches[i]);
|
||||
manager.onTouchMove(e.changedTouches[i].identifier, pos[0], pos[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
window.addEventListener('touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
if (e.changedTouches) {
|
||||
for (var i = 0; i < e.changedTouches.length; ++i) {
|
||||
var pos = getEventPosition(e.changedTouches[i]);
|
||||
manager.onTouchStart(e.changedTouches[i].identifier, pos[0], pos[1]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
window.addEventListener('touchend', function(e) {
|
||||
e.preventDefault();
|
||||
if (e.changedTouches) {
|
||||
for (var i = 0; i < e.changedTouches.length; ++i) {
|
||||
var pos = getEventPosition(e.changedTouches[i]);
|
||||
manager.onTouchEnd(e.changedTouches[i].identifier);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.setWindowTitle = function(title) {
|
||||
if (typeof document !== 'undefined') document.title = title;
|
||||
};
|
||||
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.getWindowTitle = function() {
|
||||
return typeof document !== 'undefined' ? document.title : '';
|
||||
};
|
||||
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.startGameLoop = function(fn) {
|
||||
requestAnimationFrame(gameLoop);
|
||||
|
||||
var oldTime = 0;
|
||||
function gameLoop(time) {
|
||||
var dt = oldTime ? time - oldTime : 0;
|
||||
oldTime = time;
|
||||
|
||||
if (fn(dt)) requestAnimationFrame(gameLoop);
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.getPIXIRenderer = function() {
|
||||
return this._pixiRenderer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the given URL in the system browser (or a new tab)
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.openURL = function(url) {
|
||||
// Try to detect the environment to use the most adapted
|
||||
// way of opening an URL.
|
||||
if (typeof Cocoon !== 'undefined' && Cocoon.App && Cocoon.App.openURL) {
|
||||
Cocoon.App.openURL(url);
|
||||
} else if (typeof window !== 'undefined') {
|
||||
var target = window.cordova ? '_system' : '_blank';
|
||||
window.open(url, target);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the game, if applicable
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.stopGame = function() {
|
||||
// Try to detect the environment to use the most adapted
|
||||
// way of closing the app
|
||||
var electron = this.getElectron();
|
||||
if (electron) {
|
||||
var browserWindow = electron.remote.getCurrentWindow();
|
||||
if (browserWindow) {
|
||||
browserWindow.close();
|
||||
}
|
||||
} else if (
|
||||
typeof navigator !== 'undefined' &&
|
||||
navigator.app &&
|
||||
navigator.app.exitApp
|
||||
) {
|
||||
navigator.app.exitApp();
|
||||
}
|
||||
|
||||
// HTML5 games on mobile/browsers don't have a way to close their window/page.
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the canvas DOM element.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.getCanvas = function() {
|
||||
return this._pixiRenderer.view;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the device supports WebGL.
|
||||
* @returns {boolean} true if WebGL is supported
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.isWebGLSupported = function() {
|
||||
return this._pixiRenderer.type === PIXI.RENDERER_TYPE.WEBGL;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the electron module, if running as a electron renderer process.
|
||||
*/
|
||||
gdjs.RuntimeGamePixiRenderer.prototype.getElectron = function() {
|
||||
if (typeof require !== 'undefined') {
|
||||
return require('electron');
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
|
@ -0,0 +1,87 @@
|
|||
gdjs.RuntimeScenePixiRenderer = function(runtimeScene, runtimeGameRenderer) {
|
||||
this._pixiRenderer = runtimeGameRenderer
|
||||
? runtimeGameRenderer.getPIXIRenderer()
|
||||
: null;
|
||||
this._runtimeScene = runtimeScene;
|
||||
this._pixiContainer = new PIXI.Container(); //The Container meant to contains all pixi objects of the scene.
|
||||
};
|
||||
|
||||
gdjs.RuntimeSceneRenderer = gdjs.RuntimeScenePixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.onGameResolutionResized = function() {
|
||||
if (!this._pixiRenderer) return;
|
||||
|
||||
var runtimeGame = this._runtimeScene.getGame();
|
||||
this._pixiContainer.scale.x =
|
||||
this._pixiRenderer.width / runtimeGame.getGameResolutionWidth();
|
||||
this._pixiContainer.scale.y =
|
||||
this._pixiRenderer.height / runtimeGame.getGameResolutionHeight();
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.render = function() {
|
||||
if (!this._pixiRenderer) return;
|
||||
|
||||
// this._renderProfileText(); //Uncomment to display profiling times
|
||||
|
||||
// render the PIXI container of the scene
|
||||
this._pixiRenderer.backgroundColor = this._runtimeScene.getBackgroundColor();
|
||||
this._pixiRenderer.render(this._pixiContainer);
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype._renderProfileText = function() {
|
||||
if (!this._runtimeScene.getProfiler()) return;
|
||||
|
||||
if (!this._profilerText) {
|
||||
this._profilerText = new PIXI.Text(" ", {
|
||||
align: "left",
|
||||
stroke: "#FFF",
|
||||
strokeThickness: 1
|
||||
});
|
||||
this._pixiContainer.addChild(this._profilerText);
|
||||
}
|
||||
|
||||
var average = this._runtimeScene.getProfiler().getFramesAverageMeasures();
|
||||
var outputs = [];
|
||||
gdjs.Profiler.getProfilerSectionTexts("All", average, outputs);
|
||||
|
||||
this._profilerText.text = outputs.join("\n");
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.renderDebugDraw = function(instances, layersCameraCoordinates) {
|
||||
if (!this._debugDraw) {
|
||||
this._debugDraw = new PIXI.Graphics();
|
||||
this._pixiContainer.addChild(this._debugDraw);
|
||||
}
|
||||
/** @type PIXI.Graphics */
|
||||
var debugDraw = this._debugDraw;
|
||||
|
||||
debugDraw.clear();
|
||||
debugDraw.beginFill(0x6868e8);
|
||||
debugDraw.lineStyle(1, 0x6868e8, 1);
|
||||
debugDraw.fillAlpha = 0.1;
|
||||
debugDraw.alpha = 0.8;
|
||||
|
||||
for(var i = 0;i < instances.length;i++) {
|
||||
var object = instances[i];
|
||||
var cameraCoords = layersCameraCoordinates[object.getLayer()];
|
||||
var rendererObject = object.getRendererObject();
|
||||
|
||||
if (!cameraCoords || !rendererObject) continue;
|
||||
|
||||
var aabb = object.getAABB();
|
||||
debugDraw.drawRect(aabb.min[0], aabb.min[1], aabb.max[0] - aabb.min[0], aabb.max[1] - aabb.min[1]);
|
||||
}
|
||||
debugDraw.endFill();
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.hideCursor = function() {
|
||||
this._pixiRenderer.view.style.cursor = "none";
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.showCursor = function() {
|
||||
this._pixiRenderer.view.style.cursor = "";
|
||||
};
|
||||
|
||||
gdjs.RuntimeScenePixiRenderer.prototype.getPIXIContainer = function() {
|
||||
return this._pixiContainer;
|
||||
};
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* The renderer for a gdjs.SpriteRuntimeObject using Pixi.js.
|
||||
* @class SpriteRuntimeObjectPixiRenderer
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.SpriteRuntimeObject} runtimeObject The object
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene
|
||||
*/
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene)
|
||||
{
|
||||
/** @type gdjs.SpriteRuntimeObject */
|
||||
this._object = runtimeObject;
|
||||
this._spriteDirty = true;
|
||||
this._textureDirty = true;
|
||||
if ( this._sprite === undefined )
|
||||
this._sprite = new PIXI.Sprite(runtimeScene.getGame().getImageManager().getInvalidPIXITexture());
|
||||
|
||||
var layer = runtimeScene.getLayer("");
|
||||
if (layer) layer.getRenderer().addRendererObject(this._sprite, runtimeObject.getZOrder());
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectRenderer = gdjs.SpriteRuntimeObjectPixiRenderer; //Register the class to let the engine use it.
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.getRendererObject = function() {
|
||||
return this._sprite;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the internal PIXI.Sprite position, angle...
|
||||
*/
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype._updatePIXISprite = function() {
|
||||
if (this._object._animationFrame !== null) {
|
||||
this._sprite.anchor.x = this._object._animationFrame.center.x/this._sprite.texture.frame.width;
|
||||
this._sprite.anchor.y = this._object._animationFrame.center.y/this._sprite.texture.frame.height;
|
||||
this._sprite.position.x = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
|
||||
this._sprite.position.y = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
|
||||
this._sprite.rotation = gdjs.toRad(this._object.angle);
|
||||
this._sprite.visible = !this._object.hidden;
|
||||
this._sprite.blendMode = this._object._blendMode;
|
||||
this._sprite.alpha = this._sprite.visible ? this._object.opacity/255 : 0; //TODO: Workaround not working property in PIXI.js
|
||||
this._sprite.scale.x = this._object._scaleX;
|
||||
this._sprite.scale.y = this._object._scaleY;
|
||||
this._cachedWidth = Math.abs(this._sprite.width);
|
||||
this._cachedHeight = Math.abs(this._sprite.height);
|
||||
} else {
|
||||
this._sprite.visible = false;
|
||||
this._sprite.alpha = 0;
|
||||
this._cachedWidth = 0;
|
||||
this._cachedHeight = 0;
|
||||
}
|
||||
|
||||
this._spriteDirty = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call this to make sure the sprite is ready to be rendered.
|
||||
*/
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
if ( this._spriteDirty ) this._updatePIXISprite();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the internal texture of the PIXI sprite.
|
||||
*/
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateFrame = function(animationFrame) {
|
||||
this._spriteDirty = true;
|
||||
this._sprite.texture = animationFrame.texture;
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.update = function() {
|
||||
this._spriteDirty = true;
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateX = function() {
|
||||
this._sprite.position.x = this._object.x + (this._object._animationFrame.center.x - this._object._animationFrame.origin.x)*Math.abs(this._object._scaleX);
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateY = function() {
|
||||
this._sprite.position.y = this._object.y + (this._object._animationFrame.center.y - this._object._animationFrame.origin.y)*Math.abs(this._object._scaleY);
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateAngle = function() {
|
||||
this._sprite.rotation = gdjs.toRad(this._object.angle);
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateOpacity = function() {
|
||||
//TODO: Workaround a not working property in PIXI.js:
|
||||
this._sprite.alpha = this._sprite.visible ? this._object.opacity/255 : 0;
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.updateVisibility = function() {
|
||||
this._sprite.visible = !this._object.hidden;
|
||||
|
||||
//TODO: Workaround a not working property in PIXI.js:
|
||||
this._sprite.alpha = this._sprite.visible ? this._object.opacity/255 : 0;
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.setColor = function(rgbColor) {
|
||||
var colors = rgbColor.split(";");
|
||||
if ( colors.length < 3 ) return;
|
||||
|
||||
this._sprite.tint = "0x" + gdjs.rgbToHex(parseInt(colors[0], 10), parseInt(colors[1], 10), parseInt(colors[2], 10));
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.getColor = function() {
|
||||
var rgb = PIXI.utils.hex2rgb(this._sprite.tint)
|
||||
return Math.floor(rgb[0]*255) + ';' + Math.floor(rgb[1]*255) + ';' + Math.floor(rgb[2]*255);
|
||||
}
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.getWidth = function() {
|
||||
if ( this._spriteDirty ) this._updatePIXISprite();
|
||||
return this._cachedWidth;
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.getHeight = function() {
|
||||
if ( this._spriteDirty ) this._updatePIXISprite();
|
||||
return this._cachedHeight;
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.getUnscaledWidth = function() {
|
||||
return this._sprite.texture.frame.width;
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.prototype.getUnscaledHeight = function() {
|
||||
return this._sprite.texture.frame.height;
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.getAnimationFrame = function(imageManager, imageName) {
|
||||
return imageManager.getPIXITexture(imageName);
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.getAnimationFrameWidth = function(pixiTexture) {
|
||||
return pixiTexture.width;
|
||||
};
|
||||
|
||||
gdjs.SpriteRuntimeObjectPixiRenderer.getAnimationFrameHeight = function(pixiTexture) {
|
||||
return pixiTexture.height;
|
||||
};
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Polygon represents a polygon which can be used to create collisions masks for RuntimeObject.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class Polygon
|
||||
*/
|
||||
gdjs.Polygon = function()
|
||||
{
|
||||
/**
|
||||
* The vertices of the polygon
|
||||
* @member {Array}
|
||||
*/
|
||||
this.vertices = [];
|
||||
|
||||
/**
|
||||
* The edges of the polygon. This property is only valid after calling
|
||||
* computeEdges, and remains valid until vertices are modified.
|
||||
* @member {Array}
|
||||
*/
|
||||
this.edges = [];
|
||||
|
||||
/**
|
||||
* The center of the polygon. This property is only valid after calling
|
||||
* computeCenter, and remains valid until vertices are modified.
|
||||
* @member {Array}
|
||||
*/
|
||||
this.center = [0,0];
|
||||
};
|
||||
|
||||
gdjs.Polygon.prototype.move = function(x,y) {
|
||||
for(var i = 0, len = this.vertices.length;i<len;++i) {
|
||||
|
||||
this.vertices[i][0] += x;
|
||||
this.vertices[i][1] += y;
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.Polygon.prototype.rotate = function(angle) {
|
||||
var t, cosa = Math.cos(-angle),
|
||||
sina = Math.sin(-angle); //We want a clockwise rotation
|
||||
|
||||
for (var i = 0, len = this.vertices.length;i<len;++i) {
|
||||
t = this.vertices[i][0];
|
||||
this.vertices[i][0] = t*cosa + this.vertices[i][1]*sina;
|
||||
this.vertices[i][1] = -t*sina + this.vertices[i][1]*cosa;
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.Polygon.prototype.computeEdges = function() {
|
||||
var v1, v2;
|
||||
//Ensure edge array has the right size. ( And avoid recreating an edge array ).
|
||||
while ( this.edges.length < this.vertices.length ) {
|
||||
this.edges.push([0,0]);
|
||||
}
|
||||
if ( this.edges.length != this.vertices.length )
|
||||
this.edges.length = this.vertices.length;
|
||||
|
||||
for (var i = 0, len = this.vertices.length;i<len;++i) {
|
||||
v1 = this.vertices[i];
|
||||
if ((i + 1) >= len) v2 = this.vertices[0];
|
||||
else v2 = this.vertices[i + 1];
|
||||
|
||||
this.edges[i][0] = v2[0] - v1[0];
|
||||
this.edges[i][1] = v2[1] - v1[1];
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.Polygon.prototype.isConvex = function() {
|
||||
this.computeEdges();
|
||||
var edgesLen = this.edges.length;
|
||||
|
||||
if ( edgesLen < 3 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var zProductIsPositive = (this.edges[0][0]*this.edges[0+1][1] - this.edges[0][1]*this.edges[0+1][0]) > 0;
|
||||
|
||||
for (var i = 1;i<edgesLen-1;++i) {
|
||||
var zCrossProduct = this.edges[i][0]*this.edges[i+1][1] - this.edges[i][1]*this.edges[i+1][0];
|
||||
if ( (zCrossProduct > 0) !== zProductIsPositive ) return false;
|
||||
}
|
||||
|
||||
var lastZCrossProduct = this.edges[edgesLen-1][0]*this.edges[0][1] - this.edges[edgesLen-1][1]*this.edges[0][0];
|
||||
if ( (lastZCrossProduct > 0) !== zProductIsPositive ) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.Polygon.prototype.computeCenter = function() {
|
||||
this.center[0] = 0;
|
||||
this.center[1] = 0;
|
||||
var len = this.vertices.length;
|
||||
|
||||
for (var i = 0;i<len;++i) {
|
||||
this.center[0] += this.vertices[i][0];
|
||||
this.center[1] += this.vertices[i][1];
|
||||
}
|
||||
this.center[0] /= len;
|
||||
this.center[1] /= len;
|
||||
|
||||
return this.center;
|
||||
};
|
||||
|
||||
gdjs.Polygon.createRectangle = function(width, height) {
|
||||
var rect = new gdjs.Polygon();
|
||||
rect.vertices.push([-width/2.0, -height/2.0]);
|
||||
rect.vertices.push([+width/2.0, -height/2.0]);
|
||||
rect.vertices.push([+width/2.0, +height/2.0]);
|
||||
rect.vertices.push([-width/2.0, +height/2.0]);
|
||||
|
||||
return rect;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do a collision test between two polygons.
|
||||
* Please note that polygons must *convexes*!
|
||||
*
|
||||
* Uses <a href="http://en.wikipedia.org/wiki/Hyperplane_separation_theorem">Separating Axis Theorem </a>.<br>
|
||||
* Based on <a href="http://www.codeproject.com/Articles/15573/2D-Polygon-Collision-Detection">this</a>
|
||||
* and <a href="http://stackoverflow.com/questions/5742329/problem-with-collision-response-sat">this</a> article.
|
||||
*
|
||||
* @return {{collision: boolean, move_axis: Array<number>}} returnValue.collision is equal to true if polygons are overlapping
|
||||
* @param {gdjs.Polygon} p1 The first polygon
|
||||
* @param {gdjs.Polygon} p2 The second polygon
|
||||
* @param {boolean | undefined} ignoreTouchingEdges If true, then edges that are touching each other, without the polygons actually overlapping, won't be considered in collision.
|
||||
*/
|
||||
gdjs.Polygon.collisionTest = function(p1, p2, ignoreTouchingEdges) {
|
||||
//Algorithm core :
|
||||
|
||||
p1.computeEdges();
|
||||
p2.computeEdges();
|
||||
|
||||
var edge = gdjs.Polygon.collisionTest._statics.edge;
|
||||
var move_axis = gdjs.Polygon.collisionTest._statics.move_axis;
|
||||
|
||||
var result = gdjs.Polygon.collisionTest._statics.result;
|
||||
var minDist = Number.MAX_VALUE;
|
||||
|
||||
edge[0] = 0;
|
||||
edge[1] = 0;
|
||||
edge[0] = 0;
|
||||
edge[1] = 0;
|
||||
|
||||
result.collision = false;
|
||||
result.move_axis[0] = 0;
|
||||
result.move_axis[1] = 0;
|
||||
|
||||
//Iterate over all the edges composing the polygons
|
||||
for (var i = 0, len1 = p1.vertices.length, len2 = p2.vertices.length; i < len1+len2; i++) {
|
||||
if (i < len1) { // or <=
|
||||
edge = p1.edges[i];
|
||||
} else {
|
||||
edge = p2.edges[i - len1];
|
||||
}
|
||||
|
||||
var axis = gdjs.Polygon.collisionTest._statics.axis; //Get the axis to which polygons will be projected
|
||||
axis[0] = -edge[1];
|
||||
axis[1] = edge[0];
|
||||
gdjs.Polygon.normalise(axis);
|
||||
|
||||
var minMaxA = gdjs.Polygon.collisionTest._statics.minMaxA;
|
||||
var minMaxB = gdjs.Polygon.collisionTest._statics.minMaxB;
|
||||
gdjs.Polygon.project(axis, p1, minMaxA); //Do projection on the axis.
|
||||
gdjs.Polygon.project(axis, p2, minMaxB);
|
||||
|
||||
//If the projections on the axis do not overlap, then their is no collision
|
||||
var dist = gdjs.Polygon.distance(minMaxA[0], minMaxA[1], minMaxB[0], minMaxB[1]);
|
||||
if (dist > 0 || (dist === 0 && ignoreTouchingEdges)) {
|
||||
result.collision = false;
|
||||
result.move_axis[0] = 0;
|
||||
result.move_axis[1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
var absDist = Math.abs(dist);
|
||||
|
||||
if (absDist < minDist) {
|
||||
minDist = absDist;
|
||||
move_axis[0] = axis[0];
|
||||
move_axis[1] = axis[1];
|
||||
}
|
||||
}
|
||||
|
||||
result.collision = true;
|
||||
|
||||
//Ensure move axis is correctly oriented.
|
||||
var p1Center = p1.computeCenter();
|
||||
var p2Center = p2.computeCenter();
|
||||
var d = [p1Center[0]-p2Center[0], p1Center[1]-p2Center[1]];
|
||||
if (gdjs.Polygon.dotProduct(d, move_axis) < 0) {
|
||||
move_axis[0] = -move_axis[0];
|
||||
move_axis[1] = -move_axis[1];
|
||||
}
|
||||
|
||||
//Add the magnitude to the move axis.
|
||||
result.move_axis[0] = move_axis[0] * minDist;
|
||||
result.move_axis[1] = move_axis[1] * minDist;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
//Arrays and data structure that are (re)used by gdjs.Polygon.collisionTest to
|
||||
//avoid any allocation.
|
||||
gdjs.Polygon.collisionTest._statics = {
|
||||
minMaxA: [0,0],
|
||||
minMaxB: [0,0],
|
||||
edge: [0,0],
|
||||
axis: [0,0],
|
||||
move_axis: [0,0],
|
||||
result: {
|
||||
collision:false,
|
||||
move_axis:[0,0]
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Do a raycast test.<br>
|
||||
* Please note that the polygon must be <b>convex</b>!
|
||||
*
|
||||
* For some theory, check <a href="https://www.codeproject.com/Tips/862988/Find-the-Intersection-Point-of-Two-Line-Segments">Find the Intersection Point of Two Line Segments</a>.
|
||||
*
|
||||
* @param {gdjs.Polygon} poly The polygon to test
|
||||
* @param {number} startX The raycast start point X
|
||||
* @param {number} startY The raycast start point Y
|
||||
* @param {number} endX The raycast end point X
|
||||
* @param {number} endY The raycast end point Y
|
||||
* @return A raycast result with the contact points and distances
|
||||
*/
|
||||
gdjs.Polygon.raycastTest = function(poly, startX, startY, endX, endY)
|
||||
{
|
||||
var result = gdjs.Polygon.raycastTest._statics.result;
|
||||
result.collision = false;
|
||||
|
||||
if ( poly.vertices.length < 2 )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
poly.computeEdges();
|
||||
var p = gdjs.Polygon.raycastTest._statics.p;
|
||||
var q = gdjs.Polygon.raycastTest._statics.q;
|
||||
var r = gdjs.Polygon.raycastTest._statics.r;
|
||||
var s = gdjs.Polygon.raycastTest._statics.s;
|
||||
var minSqDist = Number.MAX_VALUE;
|
||||
|
||||
// Ray segment: p + t*r, with p = start and r = end - start
|
||||
p[0] = startX;
|
||||
p[1] = startY;
|
||||
r[0] = endX - startX;
|
||||
r[1] = endY - startY;
|
||||
|
||||
for(var i=0; i<poly.edges.length; i++)
|
||||
{
|
||||
// Edge segment: q + u*s
|
||||
q[0] = poly.vertices[i][0];
|
||||
q[1] = poly.vertices[i][1];
|
||||
s[0] = poly.edges[i][0];
|
||||
s[1] = poly.edges[i][1];
|
||||
var deltaQP = gdjs.Polygon.raycastTest._statics.deltaQP;
|
||||
deltaQP[0] = q[0] - p[0];
|
||||
deltaQP[1] = q[1] - p[1];
|
||||
var crossRS = gdjs.Polygon.crossProduct(r, s);
|
||||
var t = gdjs.Polygon.crossProduct(deltaQP, s) / crossRS;
|
||||
var u = gdjs.Polygon.crossProduct(deltaQP, r) / crossRS;
|
||||
|
||||
// Collinear
|
||||
if ( Math.abs(crossRS) <= 0.0001 && Math.abs(gdjs.Polygon.crossProduct(deltaQP, r)) <= 0.0001 )
|
||||
{
|
||||
// Project the ray and the edge to work on floats, keeping linearity through t
|
||||
var axis = gdjs.Polygon.raycastTest._statics.axis;
|
||||
axis[0] = r[0];
|
||||
axis[1] = r[1];
|
||||
gdjs.Polygon.normalise(axis);
|
||||
var rayA = 0;
|
||||
var rayB = gdjs.Polygon.dotProduct(axis, r);
|
||||
var edgeA = gdjs.Polygon.dotProduct(axis, deltaQP);
|
||||
var edgeB = gdjs.Polygon.dotProduct(axis, [deltaQP[0] + s[0], deltaQP[1] + s[1]]);
|
||||
// Get overlapping range
|
||||
var minOverlap = Math.max(Math.min(rayA, rayB), Math.min(edgeA, edgeB));
|
||||
var maxOverlap = Math.min(Math.max(rayA, rayB), Math.max(edgeA, edgeB));
|
||||
if( minOverlap > maxOverlap ){
|
||||
return result;
|
||||
}
|
||||
result.collision = true;
|
||||
// Zero distance ray
|
||||
if( rayB === 0 ){
|
||||
result.closeX = startX;
|
||||
result.closeY = startY;
|
||||
result.closeSqDist = 0;
|
||||
result.farX = startX;
|
||||
result.farY = startY;
|
||||
result.farSqDist = 0;
|
||||
}
|
||||
var t1 = minOverlap / Math.abs(rayB);
|
||||
var t2 = maxOverlap / Math.abs(rayB);
|
||||
result.closeX = startX + t1*r[0];
|
||||
result.closeY = startY + t1*r[1];
|
||||
result.closeSqDist = t1*t1*(r[0]*r[0] + r[1]*r[1]);
|
||||
result.farX = startX + t2*r[0];
|
||||
result.farY = startY + t2*r[1];
|
||||
result.farSqDist = t2*t2*(r[0]*r[0] + r[1]*r[1]);
|
||||
|
||||
return result;
|
||||
}
|
||||
// One point intersection
|
||||
else if ( crossRS !== 0 && 0<=t && t<=1 && 0<=u && u<=1 )
|
||||
{
|
||||
var x = p[0] + t*r[0];
|
||||
var y = p[1] + t*r[1];
|
||||
|
||||
var sqDist = (x-startX)*(x-startX) + (y-startY)*(y-startY);
|
||||
if ( sqDist < minSqDist )
|
||||
{
|
||||
if ( !result.collision ){
|
||||
result.farX = x;
|
||||
result.farY = y;
|
||||
result.farSqDist = sqDist;
|
||||
}
|
||||
minSqDist = sqDist;
|
||||
result.closeX = x;
|
||||
result.closeY = y;
|
||||
result.closeSqDist = sqDist;
|
||||
result.collision = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.farX = x;
|
||||
result.farY = y;
|
||||
result.farSqDist = sqDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
gdjs.Polygon.raycastTest._statics = {
|
||||
p: [0,0],
|
||||
q: [0,0],
|
||||
r: [0,0],
|
||||
s: [0,0],
|
||||
deltaQP: [0,0],
|
||||
axis: [0,0],
|
||||
result: {
|
||||
collision: false,
|
||||
closeX: 0,
|
||||
closeY: 0,
|
||||
closeSqDist: 0,
|
||||
farX: 0,
|
||||
farY: 0,
|
||||
farSqDist: 0
|
||||
}
|
||||
}
|
||||
|
||||
//Tools functions :
|
||||
gdjs.Polygon.normalise = function(v)
|
||||
{
|
||||
var len = Math.sqrt(v[0]*v[0] + v[1]*v[1]);
|
||||
|
||||
if (len != 0) {
|
||||
v[0] /= len;
|
||||
v[1] /= len;
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.Polygon.dotProduct = function(a, b)
|
||||
{
|
||||
var dp = a[0]*b[0] + a[1]*b[1];
|
||||
|
||||
return dp;
|
||||
}
|
||||
|
||||
gdjs.Polygon.crossProduct = function(a, b)
|
||||
{
|
||||
var cp = a[0]*b[1] - a[1]*b[0];
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
gdjs.Polygon.project = function(axis, p, result)
|
||||
{
|
||||
var dp = gdjs.Polygon.dotProduct(axis, p.vertices[0]);
|
||||
result[0] = dp;
|
||||
result[1] = dp;
|
||||
|
||||
for (var i = 1, len = p.vertices.length; i < len; ++i) {
|
||||
dp = gdjs.Polygon.dotProduct(axis, p.vertices[i]);
|
||||
|
||||
if (dp < result[0])
|
||||
result[0] = dp;
|
||||
else if (dp > result[1])
|
||||
result[1] = dp;
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.Polygon.distance = function(minA, maxA, minB, maxB)
|
||||
{
|
||||
if (minA < minB) return minB - maxA;
|
||||
else return minA - maxB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a point is inside a polygon.
|
||||
*
|
||||
* Uses <a href="https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html">PNPOLY</a> by W. Randolph Franklin.
|
||||
*
|
||||
* @param {gdjs.Polygon} poly The polygon to test
|
||||
* @param {number} x The point x coordinate
|
||||
* @param {number} y The point y coordinate
|
||||
* @return {boolean} true if the point is inside the polygon
|
||||
*/
|
||||
gdjs.Polygon.isPointInside = function(poly, x, y)
|
||||
{
|
||||
var inside = false;
|
||||
var vi, vj;
|
||||
for (var i = 0, j = poly.vertices.length-1; i < poly.vertices.length; j = i++) {
|
||||
vi = poly.vertices[i];
|
||||
vj = poly.vertices[j];
|
||||
if ( ((vi[1]>y) != (vj[1]>y)) && (x < (vj[0]-vi[0]) * (y-vi[1]) / (vj[1]-vi[1]) + vi[0]) )
|
||||
inside = !inside;
|
||||
}
|
||||
|
||||
return inside;
|
||||
};
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
* A basic profiling tool that can be used to measure time spent in sections of the engine.
|
||||
* @class Profiler
|
||||
* @see gdjs.RuntimeGame
|
||||
* @memberof gdjs
|
||||
*/
|
||||
gdjs.Profiler = function() {
|
||||
this._framesMeasures = []; // All the measures for the last frames
|
||||
this._currentFrameIndex = 0;
|
||||
this._currentFrameMeasure = null; // The measures being done
|
||||
this._currentSection = null; // The section being measured
|
||||
|
||||
this._maxFramesCount = 600;
|
||||
this._framesCount = 0; // The number of frames that have been measured
|
||||
while (this._framesMeasures.length < this._maxFramesCount) {
|
||||
this._framesMeasures.push({
|
||||
parent: null,
|
||||
time: 0,
|
||||
subsections: {},
|
||||
});
|
||||
}
|
||||
|
||||
this._getTimeNow =
|
||||
window.performance && typeof window.performance.now === 'function'
|
||||
? window.performance.now.bind(window.performance)
|
||||
: Date.now;
|
||||
};
|
||||
|
||||
gdjs.Profiler.prototype.beginFrame = function() {
|
||||
this._currentFrameMeasure = {
|
||||
parent: null,
|
||||
time: 0,
|
||||
lastStartTime: this._getTimeNow(),
|
||||
subsections: {},
|
||||
};
|
||||
this._currentSection = this._currentFrameMeasure;
|
||||
};
|
||||
|
||||
gdjs.Profiler.prototype.begin = function(sectionName) {
|
||||
// Push the new section
|
||||
var subsections = this._currentSection.subsections;
|
||||
var subsection = (subsections[sectionName] = subsections[sectionName] || {
|
||||
parent: this._currentSection,
|
||||
time: 0,
|
||||
lastStartTime: 0,
|
||||
subsections: {},
|
||||
});
|
||||
this._currentSection = subsection;
|
||||
|
||||
// Start the timer
|
||||
this._currentSection.lastStartTime = this._getTimeNow();
|
||||
};
|
||||
|
||||
gdjs.Profiler.prototype.end = function(sectionName) {
|
||||
// Stop the timer
|
||||
var sectionTime = this._getTimeNow() - this._currentSection.lastStartTime;
|
||||
this._currentSection.time = (this._currentSection.time || 0) + sectionTime;
|
||||
|
||||
// Pop the section
|
||||
this._currentSection = this._currentSection.parent;
|
||||
};
|
||||
|
||||
gdjs.Profiler.prototype.endFrame = function() {
|
||||
if (this._currentSection.parent !== null) {
|
||||
throw new Error(
|
||||
'Mismatch in profiler, endFrame should be called on root section'
|
||||
);
|
||||
}
|
||||
|
||||
this.end();
|
||||
|
||||
this._framesCount++;
|
||||
if (this._framesCount > this._maxFramesCount)
|
||||
this._framesCount = this._maxFramesCount;
|
||||
this._framesMeasures[this._currentFrameIndex] = this._currentFrameMeasure;
|
||||
this._currentFrameIndex++;
|
||||
if (this._currentFrameIndex >= this._maxFramesCount)
|
||||
this._currentFrameIndex = 0;
|
||||
};
|
||||
|
||||
gdjs.Profiler._addAverageSectionTimes = function(
|
||||
section,
|
||||
destinationSection,
|
||||
totalCount,
|
||||
i
|
||||
) {
|
||||
destinationSection.time =
|
||||
(destinationSection.time || 0) + section.time / totalCount;
|
||||
for (var sectionName in section.subsections) {
|
||||
if (section.subsections.hasOwnProperty(sectionName)) {
|
||||
var destinationSubsections = destinationSection.subsections;
|
||||
var destinationSubsection = (destinationSubsections[
|
||||
sectionName
|
||||
] = destinationSubsections[sectionName] || {
|
||||
parent: destinationSection,
|
||||
time: 0,
|
||||
subsections: {},
|
||||
});
|
||||
|
||||
gdjs.Profiler._addAverageSectionTimes(
|
||||
section.subsections[sectionName],
|
||||
destinationSubsection,
|
||||
totalCount,
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the measures for all the section of the game during the frames
|
||||
* captured.
|
||||
*/
|
||||
gdjs.Profiler.prototype.getFramesAverageMeasures = function() {
|
||||
var framesAverageMeasures = {
|
||||
parent: null,
|
||||
time: 0,
|
||||
subsections: {},
|
||||
};
|
||||
|
||||
for (var i = 0; i < this._framesCount; ++i) {
|
||||
gdjs.Profiler._addAverageSectionTimes(
|
||||
this._framesMeasures[i],
|
||||
framesAverageMeasures,
|
||||
this._framesCount,
|
||||
i
|
||||
);
|
||||
}
|
||||
|
||||
return framesAverageMeasures;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get stats measured during the frames captured.
|
||||
*/
|
||||
gdjs.Profiler.prototype.getStats = function() {
|
||||
return {
|
||||
framesCount: this._framesCount,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert measures for a section into texts.
|
||||
* Useful for ingame profiling.
|
||||
*
|
||||
* @param {string} sectionName The name of the section
|
||||
* @param {s} profilerSection The section measures
|
||||
* @param {*} outputs The array where to push the results
|
||||
*/
|
||||
gdjs.Profiler.getProfilerSectionTexts = function(
|
||||
sectionName,
|
||||
profilerSection,
|
||||
outputs
|
||||
) {
|
||||
var percent =
|
||||
profilerSection.parent && profilerSection.parent.time !== 0
|
||||
? ((profilerSection.time / profilerSection.parent.time) * 100).toFixed(1)
|
||||
: '100%';
|
||||
var time = profilerSection.time.toFixed(2);
|
||||
outputs.push(sectionName + ': ' + time + 'ms (' + percent + ')');
|
||||
var subsectionsOutputs = [];
|
||||
|
||||
for (var subsectionName in profilerSection.subsections) {
|
||||
if (profilerSection.subsections.hasOwnProperty(subsectionName)) {
|
||||
gdjs.Profiler.getProfilerSectionTexts(
|
||||
subsectionName,
|
||||
profilerSection.subsections[subsectionName],
|
||||
subsectionsOutputs
|
||||
);
|
||||
}
|
||||
}
|
||||
outputs.push.apply(outputs, subsectionsOutputs);
|
||||
};
|
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef BehaviorData Properties to set up a behavior.
|
||||
* @property {string} name The name of the behavior (for getting from an object (object.getBehavior) for example)
|
||||
* @property {string} type The behavior type. Used by GDJS to find the proper behavior to construct.
|
||||
*/
|
||||
|
||||
/**
|
||||
* RuntimeBehavior represents a behavior being used by a RuntimeObject.
|
||||
*
|
||||
* @class RuntimeBehavior
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The scene owning the object of the behavior
|
||||
* @param {BehaviorData} behaviorData The properties used to setup the behavior
|
||||
* @param {gdjs.RuntimeObject} owner The object owning the behavior
|
||||
*/
|
||||
gdjs.RuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
||||
{
|
||||
this.name = behaviorData.name || "";
|
||||
this.type = behaviorData.type || "";
|
||||
this._nameId = gdjs.RuntimeObject.getNameIdentifier(this.name);
|
||||
this._activated = true;
|
||||
this.owner = owner;
|
||||
};
|
||||
/**
|
||||
* Get the name of the behavior.
|
||||
* @return {string} The behavior's name.
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.getName = function() {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name identifier of the behavior.
|
||||
* @return {number} The behavior's name identifier.
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.getNameId = function() {
|
||||
return this._nameId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called at each frame before events. Call doStepPreEvents.<br>
|
||||
* Behaviors writers: Please do not redefine this method. Redefine doStepPreEvents instead.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The runtimeScene owning the object
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.stepPreEvents = function(runtimeScene) {
|
||||
if ( this._activated ) {
|
||||
var profiler = runtimeScene.getProfiler();
|
||||
if (profiler) profiler.begin(this.name);
|
||||
|
||||
this.doStepPreEvents(runtimeScene);
|
||||
|
||||
if (profiler) profiler.end(this.name);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called at each frame after events. Call doStepPostEvents.<br>
|
||||
* Behaviors writers: Please do not redefine this method. Redefine doStepPreEvents instead.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The runtimeScene owning the object
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.stepPostEvents = function(runtimeScene) {
|
||||
if ( this._activated ) {
|
||||
var profiler = runtimeScene.getProfiler();
|
||||
if (profiler) profiler.begin(this.name);
|
||||
|
||||
this.doStepPostEvents(runtimeScene);
|
||||
|
||||
if (profiler) profiler.end(this.name);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* De/Activate the behavior
|
||||
* @param {boolean} enable true to enable the behavior, false to disable it
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.activate = function(enable) {
|
||||
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).
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.onCreated = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the behavior is activated
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.activated = function() {
|
||||
return this._activated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reimplement this method to do extra work when the behavior is activated (after
|
||||
* it has been deactivated, see `onDeActivate`).
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.onActivate = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Reimplement this method to do extra work when the behavior is deactivated.
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.onDeActivate = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is called each tick before events are done.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The runtimeScene owning the object
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is called each tick after events are done.
|
||||
* @param {gdjs.RuntimeScene} runtimeScene The runtimeScene owning the object
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the owner of the behavior
|
||||
* is being removed from the scene and is about to be destroyed/reused later,
|
||||
*/
|
||||
gdjs.RuntimeBehavior.prototype.onDestroy = function() {
|
||||
|
||||
};
|
||||
|
||||
gdjs.registerBehavior("", gdjs.RuntimeBehavior);
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a game being played.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @class RuntimeGame
|
||||
* @param {Object} data The object (usually stored in data.json) containing the full project data
|
||||
* @param {Object=} spec Optional object for specifiying additional options: {forceFullscreen: ...}
|
||||
*/
|
||||
gdjs.RuntimeGame = function(data, spec) {
|
||||
spec = spec || {};
|
||||
|
||||
this._variables = new gdjs.VariablesContainer(data.variables);
|
||||
this._data = data;
|
||||
this._imageManager = new gdjs.ImageManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._soundManager = new gdjs.SoundManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._fontManager = new gdjs.FontManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._jsonManager = new gdjs.JsonManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._maxFPS = data ? parseInt(data.properties.maxFPS, 10) : 60;
|
||||
this._minFPS = data ? parseInt(data.properties.minFPS, 10) : 15;
|
||||
|
||||
this._gameResolutionWidth = data.properties.windowWidth;
|
||||
this._gameResolutionHeight = data.properties.windowHeight;
|
||||
this._originalWidth = this._gameResolutionWidth;
|
||||
this._originalHeight = this._gameResolutionHeight;
|
||||
this._resizeMode = data.properties.sizeOnStartupMode;
|
||||
this._adaptGameResolutionAtRuntime =
|
||||
data.properties.adaptGameResolutionAtRuntime;
|
||||
/** @type {string} */
|
||||
this._scaleMode = data.properties.scaleMode || 'linear';
|
||||
this._renderer = new gdjs.RuntimeGameRenderer(
|
||||
this,
|
||||
spec.forceFullscreen || false
|
||||
);
|
||||
|
||||
//Game loop management (see startGameLoop method)
|
||||
this._sceneStack = new gdjs.SceneStack(this);
|
||||
this._notifyScenesForGameResolutionResize = false; // When set to true, the scenes are notified that gamre resolution size changed.
|
||||
this._paused = false;
|
||||
|
||||
//Inputs :
|
||||
this._inputManager = new gdjs.InputManager();
|
||||
|
||||
//Allow to specify an external layout to insert in the first scene:
|
||||
this._injectExternalLayout = spec.injectExternalLayout || '';
|
||||
|
||||
//Optional client to connect to a debugger:
|
||||
this._debuggerClient = gdjs.DebuggerClient
|
||||
? new gdjs.DebuggerClient(this)
|
||||
: null;
|
||||
};
|
||||
|
||||
gdjs.RuntimeGame.prototype.getRenderer = function() {
|
||||
return this._renderer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the variables of the RuntimeGame.
|
||||
* @return {gdjs.VariablesContainer} The global variables
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getVariables = function() {
|
||||
return this._variables;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the gdjs.SoundManager of the RuntimeGame.
|
||||
* @return {gdjs.SoundManager} The sound manager.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getSoundManager = function() {
|
||||
return this._soundManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the gdjs.ImageManager of the RuntimeGame.
|
||||
* @return {gdjs.ImageManager} The image manager.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getImageManager = function() {
|
||||
return this._imageManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the gdjs.FontManager of the RuntimeGame.
|
||||
* @return {gdjs.FontManager} The font manager.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getFontManager = function() {
|
||||
return this._fontManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the input manager of the game, storing mouse, keyboard
|
||||
* and touches states.
|
||||
* @return {gdjs.InputManager} The input manager owned by the game
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getInputManager = function() {
|
||||
return this._inputManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the JSON manager of the game, used to load JSON from game
|
||||
* resources.
|
||||
* @return {gdjs.JsonManager} The json manager for the game
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getJsonManager = function() {
|
||||
return this._jsonManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the object containing the game data
|
||||
* @return {Object} The object associated to the game.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getGameData = function() {
|
||||
return this._data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the data associated to a scene.
|
||||
*
|
||||
* @param {string} sceneName The name of the scene. If not defined, the first scene will be returned.
|
||||
* @return {?Object} The data associated to the scene.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getSceneData = function(sceneName) {
|
||||
var scene = undefined;
|
||||
for (var i = 0, len = this._data.layouts.length; i < len; ++i) {
|
||||
var sceneData = this._data.layouts[i];
|
||||
|
||||
if (sceneName === undefined || sceneData.name === sceneName) {
|
||||
scene = sceneData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scene === undefined)
|
||||
console.warn('The game has no scene called "' + sceneName + '"');
|
||||
|
||||
return scene;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a scene exists
|
||||
*
|
||||
* @param {string=} sceneName The name of the scene to search.
|
||||
* @return {boolean} true if the scene exists. If sceneName is undefined, true if the game has a scene.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.hasScene = function(sceneName) {
|
||||
var isTrue = false;
|
||||
for (var i = 0, len = this._data.layouts.length; i < len; ++i) {
|
||||
var sceneData = this._data.layouts[i];
|
||||
|
||||
if (sceneName === undefined || sceneData.name == sceneName) {
|
||||
isTrue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isTrue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the data associated to an external layout.
|
||||
*
|
||||
* @param {string} name The name of the external layout.
|
||||
* @return {?Object} The data associated to the external layout or null if not found.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getExternalLayoutData = function(name) {
|
||||
var externalLayout = null;
|
||||
for (var i = 0, len = this._data.externalLayouts.length; i < len; ++i) {
|
||||
var layoutData = this._data.externalLayouts[i];
|
||||
|
||||
if (layoutData.name === name) {
|
||||
externalLayout = layoutData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return externalLayout;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the data representing all the global objects of the game.
|
||||
* @return {Object} The data associated to the global objects.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getInitialObjectsData = function() {
|
||||
return this._data.objects || [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the original width of the game, as set on the startup of the game.
|
||||
*
|
||||
* This is guaranteed to never change, even if the size of the game is changed afterwards.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getOriginalWidth = function() {
|
||||
return this._originalWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the original height of the game, as set on the startup of the game.
|
||||
*
|
||||
* This is guaranteed to never change, even if the size of the game is changed afterwards.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getOriginalHeight = function() {
|
||||
return this._originalHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the game resolution (the size at which the game is played and rendered) width.
|
||||
* @returns {number} The game resolution width, in pixels.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getGameResolutionWidth = function() {
|
||||
return this._gameResolutionWidth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the game resolution (the size at which the game is played and rendered) height.
|
||||
* @returns {number} The game resolution height, in pixels.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getGameResolutionHeight = function() {
|
||||
return this._gameResolutionHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the game resolution.
|
||||
*
|
||||
* @param {number} width The new width
|
||||
* @param {number} height The new height
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.setGameResolutionSize = function(width, height) {
|
||||
this._gameResolutionWidth = width;
|
||||
this._gameResolutionHeight = height;
|
||||
|
||||
if (this._adaptGameResolutionAtRuntime) {
|
||||
if (
|
||||
gdjs.RuntimeGameRenderer &&
|
||||
gdjs.RuntimeGameRenderer.getWindowInnerWidth &&
|
||||
gdjs.RuntimeGameRenderer.getWindowInnerHeight
|
||||
) {
|
||||
var windowInnerWidth = gdjs.RuntimeGameRenderer.getWindowInnerWidth();
|
||||
var windowInnerHeight = gdjs.RuntimeGameRenderer.getWindowInnerHeight();
|
||||
|
||||
// Enlarge either the width or the eight to fill the inner window space.
|
||||
var width = this._gameResolutionWidth;
|
||||
var height = this._gameResolutionHeight;
|
||||
if (this._resizeMode === 'adaptWidth') {
|
||||
this._gameResolutionWidth =
|
||||
(this._gameResolutionHeight * windowInnerWidth) / windowInnerHeight;
|
||||
} else if (this._resizeMode === 'adaptHeight') {
|
||||
this._gameResolutionHeight =
|
||||
(this._gameResolutionWidth * windowInnerHeight) / windowInnerWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Don't alter the game resolution. The renderer
|
||||
// will maybe adapt the size of the canvas or whatever is used to render the
|
||||
// game in the window, but this does not change the "game resolution".
|
||||
}
|
||||
|
||||
// Notify the renderer that game resolution changed (so that the renderer size
|
||||
// can be updated, and maybe other things like the canvas size), and let the
|
||||
// scenes know too.
|
||||
this._renderer.updateRendererSize();
|
||||
this._notifyScenesForGameResolutionResize = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set if the width or the height of the game resolution
|
||||
* should be changed to fit the game window - or if the game
|
||||
* resolution should not be updated automatically.
|
||||
*
|
||||
* @param {string} resizeMode Either "" (don't change game resolution), "adaptWidth" or "adaptHeight".
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.setGameResolutionResizeMode = function(resizeMode) {
|
||||
this._resizeMode = resizeMode;
|
||||
this._forceGameResolutionUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns if the width or the height of the game resolution
|
||||
* should be changed to fit the game window - or if the game
|
||||
* resolution should not be updated automatically (empty string).
|
||||
*
|
||||
* @returns {string} Either "" (don't change game resolution), "adaptWidth" or "adaptHeight".
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getGameResolutionResizeMode = function() {
|
||||
return this._resizeMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set if the game resolution should be automatically adapted
|
||||
* when the game window or screen size change. This will only
|
||||
* be the case if the game resolution resize mode is
|
||||
* configured to adapt the width or the height of the game.
|
||||
* @param {boolean} enable true to change the game resolution according to the window/screen size.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.setAdaptGameResolutionAtRuntime = function(enable) {
|
||||
this._adaptGameResolutionAtRuntime = enable;
|
||||
this._forceGameResolutionUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns if the game resolution should be automatically adapted
|
||||
* when the game window or screen size change. This will only
|
||||
* be the case if the game resolution resize mode is
|
||||
* configured to adapt the width or the height of the game.
|
||||
* @returns {boolean} true if the game resolution is automatically changed according to the window/screen size.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getAdaptGameResolutionAtRuntime = function() {
|
||||
return this._adaptGameResolutionAtRuntime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the minimal fps that must be guaranteed by the game
|
||||
* (otherwise, game is slowed down).
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getMinimalFramerate = function() {
|
||||
return this._minFPS;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the scale mode of the game ("linear" or "nearest").
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getScaleMode = function() {
|
||||
return this._scaleMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or unset the game as paused.
|
||||
* When paused, the game won't step and will be freezed. Useful for debugging.
|
||||
* @param {boolean} enable true to pause the game, false to unpause
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.pause = function(enable) {
|
||||
this._paused = enable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load all assets, displaying progress in renderer.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.loadAllAssets = function(
|
||||
callback,
|
||||
progressCallback
|
||||
) {
|
||||
var loadingScreen = new gdjs.LoadingScreenRenderer(
|
||||
this.getRenderer(),
|
||||
this._data.properties.loadingScreen
|
||||
);
|
||||
var allAssetsTotal = this._data.resources.resources.length;
|
||||
|
||||
var that = this;
|
||||
this._imageManager.loadTextures(
|
||||
function(count, total) {
|
||||
var percent = Math.floor((count / allAssetsTotal) * 100);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function(texturesTotalCount) {
|
||||
that._soundManager.preloadAudio(
|
||||
function(count, total) {
|
||||
var percent = Math.floor(
|
||||
((texturesTotalCount + count) / allAssetsTotal) * 100
|
||||
);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function(audioTotalCount) {
|
||||
that._fontManager.loadFonts(
|
||||
function(count, total) {
|
||||
var percent = Math.floor(
|
||||
((texturesTotalCount + audioTotalCount + count) /
|
||||
allAssetsTotal) *
|
||||
100
|
||||
);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function(fontTotalCount) {
|
||||
that._jsonManager.preloadJsons(
|
||||
function(count, total) {
|
||||
var percent = Math.floor(
|
||||
((texturesTotalCount +
|
||||
audioTotalCount +
|
||||
fontTotalCount +
|
||||
count) /
|
||||
allAssetsTotal) *
|
||||
100
|
||||
);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function() {
|
||||
loadingScreen.unload();
|
||||
callback();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start the game loop, to be called once assets are loaded.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.startGameLoop = function() {
|
||||
if (!this.hasScene()) {
|
||||
console.log('The game has no scene.');
|
||||
return;
|
||||
}
|
||||
|
||||
this._forceGameResolutionUpdate();
|
||||
|
||||
//Load the first scene
|
||||
var firstSceneName = this._data.firstLayout;
|
||||
this._sceneStack.push(
|
||||
this.hasScene(firstSceneName) ? firstSceneName : this.getSceneData().name,
|
||||
this._injectExternalLayout
|
||||
);
|
||||
|
||||
//Uncomment to profile the first x frames of the game.
|
||||
// var x = 500;
|
||||
// var startTime = Date.now();
|
||||
// console.profile("Stepping for " + x + " frames")
|
||||
// for(var i = 0; i < x; ++i) {
|
||||
// this._sceneStack.step(16);
|
||||
// }
|
||||
// console.profileEnd();
|
||||
// var time = Date.now() - startTime;
|
||||
// console.log("Took", time, "ms");
|
||||
// return;
|
||||
|
||||
//The standard game loop
|
||||
var that = this;
|
||||
var accumulatedElapsedTime = 0;
|
||||
this._renderer.startGameLoop(function(lastCallElapsedTime) {
|
||||
if (that._paused) return true;
|
||||
|
||||
// Skip the frame if we rendering frames too fast
|
||||
accumulatedElapsedTime += lastCallElapsedTime;
|
||||
if (
|
||||
that._maxFPS > 0 &&
|
||||
1000.0 / accumulatedElapsedTime > that._maxFPS + 7
|
||||
) {
|
||||
// Only skip frame if the framerate is 7 frames above the maximum framerate.
|
||||
// Most browser/engines will try to run at slightly more than 60 frames per second.
|
||||
// If game is set to have a maximum FPS to 60, then one out of two frames will be dropped.
|
||||
// Hence, we use a 7 frames margin to ensure that we're not skipping frames too much.
|
||||
return true;
|
||||
}
|
||||
var elapsedTime = accumulatedElapsedTime;
|
||||
accumulatedElapsedTime = 0;
|
||||
|
||||
//Manage resize events.
|
||||
if (that._notifyScenesForGameResolutionResize) {
|
||||
that._sceneStack.onGameResolutionResized();
|
||||
that._notifyScenesForGameResolutionResize = false;
|
||||
}
|
||||
|
||||
//Render and step the scene.
|
||||
if (that._sceneStack.step(elapsedTime)) {
|
||||
that.getInputManager().onFrameEnded();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the game renderer when the window containing the game
|
||||
* has changed size (this can result from a resize of the window,
|
||||
* but also other factors like a device orientation change on mobile).
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.onWindowInnerSizeChanged = function() {
|
||||
this._forceGameResolutionUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Enlarge/reduce the width (or the height) of the game to fill the inner window.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype._forceGameResolutionUpdate = function() {
|
||||
this.setGameResolutionSize(
|
||||
this._gameResolutionWidth,
|
||||
this._gameResolutionHeight
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a profiler for the currently running scene.
|
||||
* @param {Function} onProfilerStopped Function to be called when the profiler is stopped. Will be passed the profiler as argument.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.startCurrentSceneProfiler = function(
|
||||
onProfilerStopped
|
||||
) {
|
||||
var currentScene = this._sceneStack.getCurrentScene();
|
||||
if (!currentScene) return false;
|
||||
|
||||
currentScene.startProfiler(onProfilerStopped);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the profiler for the currently running scene.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.stopCurrentSceneProfiler = function() {
|
||||
var currentScene = this._sceneStack.getCurrentScene();
|
||||
if (!currentScene) return null;
|
||||
|
||||
currentScene.stopProfiler();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if a scene was loaded, false otherwise (i.e: game not yet started).
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.wasFirstSceneLoaded = function() {
|
||||
return this._sceneStack.wasFirstSceneLoaded();
|
||||
}
|
|
@ -0,0 +1,822 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The runtimeScene object represents a scene being played and rendered in the browser in a canvas.
|
||||
*
|
||||
* @class RuntimeScene
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.RuntimeGame} runtimeGame The game associated to this scene.
|
||||
*/
|
||||
gdjs.RuntimeScene = function(runtimeGame)
|
||||
{
|
||||
this._eventsFunction = null;
|
||||
this._instances = new Hashtable(); //Contains the instances living on the scene
|
||||
this._instancesCache = new Hashtable(); //Used to recycle destroyed instance instead of creating new ones.
|
||||
this._objects = new Hashtable(); //Contains the objects data stored in the project
|
||||
this._objectsCtor = new Hashtable();
|
||||
this._layers = new Hashtable();
|
||||
this._initialBehaviorSharedData = new Hashtable();
|
||||
this._renderer = new gdjs.RuntimeSceneRenderer(this,
|
||||
runtimeGame ? runtimeGame.getRenderer() : null);
|
||||
this._variables = new gdjs.VariablesContainer();
|
||||
this._runtimeGame = runtimeGame;
|
||||
this._lastId = 0;
|
||||
this._name = "";
|
||||
this._timeManager = new gdjs.TimeManager(Date.now());
|
||||
this._gameStopRequested = false;
|
||||
this._requestedScene = "";
|
||||
this._isLoaded = false; // True if loadFromScene was called and the scene is being played.
|
||||
this._isJustResumed = false; // True in the first frame after resuming the paused scene
|
||||
|
||||
/** @type gdjs.RuntimeObject[] */
|
||||
this._allInstancesList = []; //An array used to create a list of all instance when necessary ( see _constructListOfAllInstances )
|
||||
|
||||
/** @type gdjs.RuntimeObject[] */
|
||||
this._instancesRemoved = []; //The instances removed from the scene and waiting to be sent to the cache.
|
||||
|
||||
/** @type gdjs.Profiler */
|
||||
this._profiler = null; // Set to `new gdjs.Profiler()` to have profiling done on the scene.
|
||||
this._onProfilerStopped = null; // The callback function to call when the profiler is stopped.
|
||||
|
||||
this.onGameResolutionResized();
|
||||
};
|
||||
|
||||
/**
|
||||
* Should be called when the canvas where the scene is rendered has been resized.
|
||||
* See gdjs.RuntimeGame.startGameLoop in particular.
|
||||
* @memberof gdjs.RuntimeScene
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.onGameResolutionResized = function() {
|
||||
for(var name in this._layers.items) {
|
||||
if (this._layers.items.hasOwnProperty(name)) {
|
||||
/** @type gdjs.Layer */
|
||||
var theLayer = this._layers.items[name];
|
||||
|
||||
theLayer.onGameResolutionResized();
|
||||
}
|
||||
}
|
||||
|
||||
this._renderer.onGameResolutionResized();
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the runtime scene from the given scene.
|
||||
* @param {Object} sceneData An object containing the scene data.
|
||||
* @see gdjs.RuntimeGame#getSceneData
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.loadFromScene = function(sceneData) {
|
||||
if ( sceneData === undefined ) {
|
||||
console.error("loadFromScene was called without a scene");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this._isLoaded ) this.unloadScene();
|
||||
|
||||
//Setup main properties
|
||||
if (this._runtimeGame) this._runtimeGame.getRenderer().setWindowTitle(sceneData.title);
|
||||
this._name = sceneData.name;
|
||||
this.setBackgroundColor(parseInt(sceneData.r, 10),
|
||||
parseInt(sceneData.v, 10),
|
||||
parseInt(sceneData.b, 10));
|
||||
|
||||
//Load layers
|
||||
for(var i = 0, len = sceneData.layers.length;i<len;++i) {
|
||||
var layerData = sceneData.layers[i];
|
||||
|
||||
this._layers.put(layerData.name, new gdjs.Layer(layerData, this));
|
||||
//console.log("Created layer : \""+name+"\".");
|
||||
}
|
||||
|
||||
//Load variables
|
||||
this._variables = new gdjs.VariablesContainer(sceneData.variables);
|
||||
|
||||
//Cache the initial shared data of the behaviors
|
||||
for(var i = 0, len = sceneData.behaviorsSharedData.length;i<len;++i) {
|
||||
var data = sceneData.behaviorsSharedData[i];
|
||||
|
||||
//console.log("Initializing shared data for "+data.name);
|
||||
this._initialBehaviorSharedData.put(data.name, data);
|
||||
}
|
||||
|
||||
var that = this;
|
||||
function loadObject(objData) {
|
||||
var objectName = objData.name;
|
||||
var objectType = objData.type;
|
||||
|
||||
that._objects.put(objectName, objData);
|
||||
that._instances.put(objectName, []); //Also reserve an array for the instances
|
||||
that._instancesCache.put(objectName, []); //and for cached instances
|
||||
//And cache the constructor for the performance sake:
|
||||
that._objectsCtor.put(objectName, gdjs.getObjectConstructor(objectType));
|
||||
}
|
||||
|
||||
//Load objects: Global objects first...
|
||||
var initialGlobalObjectsData = this.getGame().getInitialObjectsData();
|
||||
for(var i = 0, len = initialGlobalObjectsData.length;i<len;++i) {
|
||||
loadObject(initialGlobalObjectsData[i]);
|
||||
}
|
||||
//...then the scene objects
|
||||
this._initialObjectsData = sceneData.objects;
|
||||
for(var i = 0, len = this._initialObjectsData.length;i<len;++i) {
|
||||
loadObject(this._initialObjectsData[i]);
|
||||
}
|
||||
|
||||
//Create initial instances of objects
|
||||
this.createObjectsFrom(sceneData.instances, 0, 0);
|
||||
|
||||
//Set up the function to be executed at each tick
|
||||
var module = gdjs[sceneData.mangledName+"Code"];
|
||||
if ( module && module.func )
|
||||
this._eventsFunction = module.func;
|
||||
else {
|
||||
console.log("Warning: no function found for running logic of scene " + this._name);
|
||||
this._eventsFunction = (function() {});
|
||||
}
|
||||
|
||||
this._onceTriggers = new gdjs.OnceTriggers();
|
||||
|
||||
// Notify the global callbacks
|
||||
if (this._runtimeGame && !this._runtimeGame.wasFirstSceneLoaded()) {
|
||||
for(var i = 0;i<gdjs.callbacksFirstRuntimeSceneLoaded.length;++i) {
|
||||
gdjs.callbacksFirstRuntimeSceneLoaded[i](this);
|
||||
}
|
||||
}
|
||||
for(var i = 0;i<gdjs.callbacksRuntimeSceneLoaded.length;++i) {
|
||||
gdjs.callbacksRuntimeSceneLoaded[i](this);
|
||||
}
|
||||
|
||||
if (sceneData.stopSoundsOnStartup && this._runtimeGame)
|
||||
this._runtimeGame.getSoundManager().clearAll();
|
||||
|
||||
this._isLoaded = true;
|
||||
this._timeManager.reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when a scene is "paused", i.e it will be not be rendered again
|
||||
* for some time, until it's resumed or unloaded.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.onPause = function() {
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeScenePaused.length;++i) {
|
||||
gdjs.callbacksRuntimeScenePaused[i](this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a scene is "resumed", i.e it will be rendered again
|
||||
* on screen after having being paused.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.onResume = function() {
|
||||
this._isJustResumed = true;
|
||||
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeSceneResumed.length;++i) {
|
||||
gdjs.callbacksRuntimeSceneResumed[i](this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before a scene is removed from the stack of scenes
|
||||
* rendered on the screen.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.unloadScene = function() {
|
||||
if ( !this._isLoaded ) return;
|
||||
|
||||
if (this._profiler) this.stopProfiler();
|
||||
|
||||
// Notify the global callbacks (which should not release resources yet,
|
||||
// as other callbacks might still refer to the objects/scene).
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeSceneUnloading.length;++i) {
|
||||
gdjs.callbacksRuntimeSceneUnloading[i](this);
|
||||
}
|
||||
|
||||
// Notify the objects they are being destroyed
|
||||
this._constructListOfAllInstances();
|
||||
for(var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||
var object = this._allInstancesList[i];
|
||||
object.onDestroyFromScene(this);
|
||||
}
|
||||
|
||||
// Notify the renderer
|
||||
if (this._renderer && this._renderer.onSceneUnloaded)
|
||||
this._renderer.onSceneUnloaded();
|
||||
|
||||
// Notify the global callbacks (after notifying objects and renderer, because
|
||||
// callbacks from extensions might want to free resources - which can't be done
|
||||
// safely before destroying objects and the renderer).
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeSceneUnloaded.length;++i) {
|
||||
gdjs.callbacksRuntimeSceneUnloaded[i](this);
|
||||
}
|
||||
|
||||
// It should not be necessary to reset these variables, but this help
|
||||
// ensuring that all memory related to the RuntimeScene is released immediately.
|
||||
this._layers = new Hashtable();
|
||||
this._variables = new gdjs.VariablesContainer();
|
||||
this._initialBehaviorSharedData = new Hashtable();
|
||||
this._objects = new Hashtable();
|
||||
this._instances = new Hashtable();
|
||||
this._instancesCache = new Hashtable();
|
||||
this._initialObjectsData = null;
|
||||
this._eventsFunction = null;
|
||||
this._objectsCtor = new Hashtable();
|
||||
this._allInstancesList = [];
|
||||
this._instancesRemoved = [];
|
||||
|
||||
this._lastId = 0;
|
||||
this._onceTriggers = null;
|
||||
|
||||
this._isLoaded = false;
|
||||
|
||||
this.onGameResolutionResized();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create objects from initial instances data ( for example, the initial instances
|
||||
* of the scene or from an external layout ).
|
||||
*
|
||||
* @param {Object} data The instances data
|
||||
* @param {number} xPos The offset on X axis
|
||||
* @param {number} yPos The offset on Y axis
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.createObjectsFrom = function(data, xPos, yPos) {
|
||||
for(var i = 0, len = data.length;i<len;++i) {
|
||||
var instanceData = data[i];
|
||||
var objectName = instanceData.name;
|
||||
var newObject = this.createObject(objectName);
|
||||
|
||||
if ( newObject !== null ) {
|
||||
newObject.setPosition(parseFloat(instanceData.x) + xPos, parseFloat(instanceData.y) + yPos);
|
||||
newObject.setZOrder(parseFloat(instanceData.zOrder));
|
||||
newObject.setAngle(parseFloat(instanceData.angle));
|
||||
newObject.setLayer(instanceData.layer);
|
||||
newObject.getVariables().initFrom(instanceData.initialVariables, true);
|
||||
newObject.extraInitializationFromInitialInstance(instanceData);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the function called each time the scene is stepped.
|
||||
* The function will be passed the `runtimeScene` as argument.
|
||||
*
|
||||
* Note that this is already set up by the gdjs.RuntimeScene constructor and that you should
|
||||
* not need to use this method.
|
||||
*
|
||||
* @param {Function} func The function to be called.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.setEventsFunction = function(func) {
|
||||
this._eventsFunction = func;
|
||||
};
|
||||
|
||||
/**
|
||||
* Step and render the scene.
|
||||
* @return {boolean} true if the game loop should continue, false if a scene change/push/pop
|
||||
* or a game stop was requested.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.renderAndStep = function(elapsedTime) {
|
||||
if (this._profiler) this._profiler.beginFrame();
|
||||
|
||||
this._requestedChange = gdjs.RuntimeScene.CONTINUE;
|
||||
this._timeManager.update(elapsedTime, this._runtimeGame.getMinimalFramerate());
|
||||
|
||||
if (this._profiler) this._profiler.begin("objects (pre-events)");
|
||||
this._updateObjectsPreEvents();
|
||||
if (this._profiler) this._profiler.end("objects (pre-events)");
|
||||
|
||||
if (this._profiler) this._profiler.begin("callbacks and extensions (pre-events)");
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeScenePreEvents.length;++i) {
|
||||
gdjs.callbacksRuntimeScenePreEvents[i](this);
|
||||
}
|
||||
if (this._profiler) this._profiler.end("callbacks and extensions (pre-events)");
|
||||
|
||||
if (this._profiler) this._profiler.begin("events");
|
||||
this._eventsFunction(this);
|
||||
if (this._profiler) this._profiler.end("events");
|
||||
|
||||
if (this._profiler) this._profiler.begin("objects (post-events)");
|
||||
this._updateObjectsPostEvents();
|
||||
if (this._profiler) this._profiler.end("objects (post-events)");
|
||||
|
||||
if (this._profiler) this._profiler.begin("callbacks and extensions (post-events)");
|
||||
for(var i = 0;i < gdjs.callbacksRuntimeScenePostEvents.length;++i) {
|
||||
gdjs.callbacksRuntimeScenePostEvents[i](this);
|
||||
}
|
||||
if (this._profiler) this._profiler.end("callbacks and extensions (post-events)");
|
||||
|
||||
if (this._profiler) this._profiler.begin("objects (visibility)");
|
||||
this._updateObjectsVisibility();
|
||||
if (this._profiler) this._profiler.end("objects (visibility)");
|
||||
|
||||
if (this._profiler) this._profiler.begin("layers (effects update)");
|
||||
this._updateLayers();
|
||||
if (this._profiler) this._profiler.end("layers (effects update)");
|
||||
|
||||
if (this._profiler) this._profiler.begin("render");
|
||||
|
||||
// Uncomment to enable debug rendering (look for the implementation in the renderer
|
||||
// to see what is rendered)
|
||||
// if (this._layersCameraCoordinates) {
|
||||
// this.getRenderer().renderDebugDraw(this._allInstancesList, this._layersCameraCoordinates); //TODO
|
||||
// }
|
||||
|
||||
this._isJustResumed = false;
|
||||
|
||||
this.render();
|
||||
if (this._profiler) this._profiler.end("render");
|
||||
|
||||
if (this._profiler) this._profiler.endFrame();
|
||||
|
||||
return !!this.getRequestedChange();
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the PIXI container associated to the runtimeScene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.render = function() {
|
||||
this._renderer.render();
|
||||
};
|
||||
|
||||
gdjs.RuntimeScene.prototype._updateLayersCameraCoordinates = function() {
|
||||
this._layersCameraCoordinates = this._layersCameraCoordinates || {};
|
||||
|
||||
for(var name in this._layers.items) {
|
||||
if (this._layers.items.hasOwnProperty(name)) {
|
||||
var theLayer = this._layers.items[name];
|
||||
|
||||
this._layersCameraCoordinates[name] = this._layersCameraCoordinates[name] ||
|
||||
[0,0,0,0];
|
||||
this._layersCameraCoordinates[name][0] = theLayer.getCameraX() - theLayer.getCameraWidth();
|
||||
this._layersCameraCoordinates[name][1] = theLayer.getCameraY() - theLayer.getCameraHeight();
|
||||
this._layersCameraCoordinates[name][2] = theLayer.getCameraX() + theLayer.getCameraWidth();
|
||||
this._layersCameraCoordinates[name][3] = theLayer.getCameraY() + theLayer.getCameraHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.RuntimeScene.prototype._updateLayers = function() {
|
||||
for(var name in this._layers.items) {
|
||||
if (this._layers.items.hasOwnProperty(name)) {
|
||||
/** @type gdjs.Layer */
|
||||
var theLayer = this._layers.items[name];
|
||||
|
||||
theLayer.update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update visibility of PIXI.DisplayObject of objects
|
||||
* rendered on the scene.
|
||||
*
|
||||
* Visibility is set to false if object is hidden, or if
|
||||
* object is too far from the camera of its layer ("culling").
|
||||
* @private
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype._updateObjectsVisibility = function() {
|
||||
if (this._timeManager.isFirstFrame()) {
|
||||
this._constructListOfAllInstances();
|
||||
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||
var object = this._allInstancesList[i];
|
||||
var rendererObject = object.getRendererObject();
|
||||
|
||||
if (rendererObject)
|
||||
object.getRendererObject().visible = !object.isHidden();
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
//After first frame, optimise rendering by setting only objects
|
||||
//near camera as visible.
|
||||
this._updateLayersCameraCoordinates();
|
||||
this._constructListOfAllInstances();
|
||||
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||
var object = this._allInstancesList[i];
|
||||
var cameraCoords = this._layersCameraCoordinates[object.getLayer()];
|
||||
var rendererObject = object.getRendererObject();
|
||||
|
||||
if (!cameraCoords || !rendererObject) continue;
|
||||
|
||||
if (object.isHidden()) {
|
||||
rendererObject.visible = false;
|
||||
} else {
|
||||
var aabb = object.getVisibilityAABB();
|
||||
if (aabb && // If no AABB is returned, the object should always be visible
|
||||
(aabb.min[0] > cameraCoords[2] || aabb.min[1] > cameraCoords[3] ||
|
||||
aabb.max[0] < cameraCoords[0] || aabb.max[1] < cameraCoords[1])) {
|
||||
rendererObject.visible = false;
|
||||
} else {
|
||||
rendererObject.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Empty the list of the removed objects:<br>
|
||||
* When an object is removed from the scene, it is still kept in the _instancesRemoved member
|
||||
* of the RuntimeScene.<br>
|
||||
* This method should be called regularly (after events or behaviors steps) so as to clear this list
|
||||
* and allows the removed objects to be cached (or destroyed if the cache is full).<br>
|
||||
* The removed objects could not be sent directly to the cache, as events may still be using them after
|
||||
* removing them from the scene for example.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype._cacheOrClearRemovedInstances = function() {
|
||||
for(var k =0, lenk=this._instancesRemoved.length;k<lenk;++k) {
|
||||
//Cache the instance to recycle it into a new instance later.
|
||||
var cache = this._instancesCache.get(this._instancesRemoved[k].getName());
|
||||
if ( cache.length < 128 ) cache.push(this._instancesRemoved[k]);
|
||||
}
|
||||
|
||||
this._instancesRemoved.length = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tool function filling _allInstancesList member with all the living object instances.
|
||||
* @private
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype._constructListOfAllInstances = function() {
|
||||
var currentListSize = 0;
|
||||
for (var name in this._instances.items) {
|
||||
if (this._instances.items.hasOwnProperty(name)) {
|
||||
var list = this._instances.items[name];
|
||||
|
||||
var oldSize = currentListSize;
|
||||
currentListSize += list.length;
|
||||
|
||||
for(var j = 0, lenj = list.length;j<lenj;++j) {
|
||||
if (oldSize+j < this._allInstancesList.length)
|
||||
this._allInstancesList[oldSize+j] = list[j];
|
||||
else
|
||||
this._allInstancesList.push(list[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._allInstancesList.length = currentListSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the objects before launching the events.
|
||||
* @private
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype._updateObjectsPreEvents = function() {
|
||||
|
||||
//It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
||||
//may delete the objects.
|
||||
this._constructListOfAllInstances();
|
||||
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||
var obj = this._allInstancesList[i];
|
||||
var elapsedTime = obj.getElapsedTime(this);
|
||||
if (!obj.hasNoForces()) {
|
||||
var averageForce = obj.getAverageForce();
|
||||
var elapsedTimeInSeconds = elapsedTime / 1000;
|
||||
|
||||
obj.setX(obj.getX() + averageForce.getX() * elapsedTimeInSeconds);
|
||||
obj.setY(obj.getY() + averageForce.getY() * elapsedTimeInSeconds);
|
||||
obj.update(this);
|
||||
obj.updateForces(elapsedTimeInSeconds);
|
||||
} else {
|
||||
obj.update(this);
|
||||
}
|
||||
obj.updateTimers(elapsedTime);
|
||||
this._allInstancesList[i].stepBehaviorsPreEvents(this);
|
||||
}
|
||||
|
||||
this._cacheOrClearRemovedInstances(); //Some behaviors may have request objects to be deleted.
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the objects (update positions, time management...)
|
||||
* @private
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype._updateObjectsPostEvents = function() {
|
||||
this._cacheOrClearRemovedInstances();
|
||||
|
||||
//It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
||||
//may delete the objects.
|
||||
this._constructListOfAllInstances();
|
||||
for( var i = 0, len = this._allInstancesList.length;i<len;++i) {
|
||||
this._allInstancesList[i].stepBehaviorsPostEvents(this);
|
||||
}
|
||||
|
||||
this._cacheOrClearRemovedInstances(); //Some behaviors may have request objects to be deleted.
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the background color
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.setBackgroundColor = function(r,g,b) {
|
||||
this._backgroundColor = parseInt(gdjs.rgbToHex(r,g,b),16);
|
||||
};
|
||||
|
||||
gdjs.RuntimeScene.prototype.getBackgroundColor = function() {
|
||||
return this._backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the scene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getName = function() {
|
||||
return this._name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the objects positions according to their forces
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.updateObjectsForces = function() {
|
||||
for (var name in this._instances.items) {
|
||||
if (this._instances.items.hasOwnProperty(name)) {
|
||||
var list = this._instances.items[name];
|
||||
|
||||
for(var j = 0, listLen = list.length;j<listLen;++j) {
|
||||
var obj = list[j];
|
||||
if (!obj.hasNoForces()) {
|
||||
var averageForce = obj.getAverageForce();
|
||||
var elapsedTimeInSeconds = obj.getElapsedTime(this) / 1000;
|
||||
|
||||
obj.setX(obj.getX() + averageForce.getX() * elapsedTimeInSeconds);
|
||||
obj.setY(obj.getY() + averageForce.getY() * elapsedTimeInSeconds);
|
||||
obj.updateForces(elapsedTimeInSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an object to the instances living on the scene.
|
||||
* @param obj The object to be added.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.addObject = function(obj) {
|
||||
if ( !this._instances.containsKey(obj.name) ) {
|
||||
console.log("RuntimeScene.addObject: No objects called \""+obj.name+"\"! Adding it.");
|
||||
this._instances.put(obj.name, []);
|
||||
}
|
||||
|
||||
this._instances.get(obj.name).push(obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all the instances of the object called name.
|
||||
* @param {string} name Name of the object for which the instances must be returned.
|
||||
* @return {gdjs.RuntimeObject[]} The list of objects with the given name
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getObjects = function(name){
|
||||
if ( !this._instances.containsKey(name) ) {
|
||||
console.log("RuntimeScene.getObjects: No instances called \""+name+"\"! Adding it.");
|
||||
this._instances.put(name, []);
|
||||
}
|
||||
|
||||
return this._instances.get(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new object from its name. The object is also added to the instances
|
||||
* living on the scene ( No need to call RuntimeScene.addObject )
|
||||
* @param {string} objectName The name of the object to be created
|
||||
* @return {gdjs.RuntimeObject} The created object
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.createObject = function(objectName){
|
||||
|
||||
if ( !this._objectsCtor.containsKey(objectName) ||
|
||||
!this._objects.containsKey(objectName) )
|
||||
return null; //There is no such object in this scene.
|
||||
|
||||
//Create a new object using the object constructor ( cached during loading )
|
||||
//and the stored object's data:
|
||||
var cache = this._instancesCache.get(objectName);
|
||||
var ctor = this._objectsCtor.get(objectName);
|
||||
var obj = null;
|
||||
if ( cache.length === 0 ) {
|
||||
obj = new ctor(this, this._objects.get(objectName));
|
||||
}
|
||||
else {
|
||||
//Reuse an objet destroyed before:
|
||||
obj = cache.pop();
|
||||
ctor.call(obj, this, this._objects.get(objectName));
|
||||
}
|
||||
|
||||
this.addObject(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Must be called whenever an object must be removed from the scene.
|
||||
* @param {gdjs.RuntimeObject} object The object to be removed.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.markObjectForDeletion = function(obj) {
|
||||
//Add to the objects removed list.
|
||||
//The objects will be sent to the instances cache or really deleted from memory later.
|
||||
if ( this._instancesRemoved.indexOf(obj) === -1 ) this._instancesRemoved.push(obj);
|
||||
|
||||
//Delete from the living instances.
|
||||
if ( this._instances.containsKey(obj.getName()) ) {
|
||||
var objId = obj.id;
|
||||
var allInstances = this._instances.get(obj.getName());
|
||||
for(var i = 0, len = allInstances.length;i<len;++i) {
|
||||
if (allInstances[i].id == objId) {
|
||||
allInstances.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Notify the object it was removed from the scene
|
||||
obj.onDestroyFromScene(this);
|
||||
|
||||
// Notify the global callbacks
|
||||
for(var j = 0;j<gdjs.callbacksObjectDeletedFromScene.length;++j) {
|
||||
gdjs.callbacksObjectDeletedFromScene[j](this, obj);
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an identifier for a new object of the scene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.createNewUniqueId = function() {
|
||||
this._lastId++;
|
||||
return this._lastId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the renderer associated to the RuntimeScene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getRenderer = function() {
|
||||
return this._renderer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the runtimeGame associated to the RuntimeScene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getGame = function() {
|
||||
return this._runtimeGame;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the variables of the runtimeScene.
|
||||
* @return The container holding the variables of the scene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getVariables = function() {
|
||||
return this._variables;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the data representing the initial shared data of the scene for the specified behavior.
|
||||
* @param {string} name The name of the behavior
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getInitialSharedDataForBehavior = function(name) {
|
||||
if ( this._initialBehaviorSharedData.containsKey(name) ) {
|
||||
return this._initialBehaviorSharedData.get(name);
|
||||
}
|
||||
|
||||
console.error("Can't find shared data for behavior with name:", name);
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the layer with the given name
|
||||
* @param {string} name The name of the layer
|
||||
* @returns {gdjs.Layer} The layer, or the base layer if not found
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getLayer = function(name) {
|
||||
if ( this._layers.containsKey(name) )
|
||||
return this._layers.get(name);
|
||||
|
||||
return this._layers.get("");
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a layer exists
|
||||
* @param {string} name The name of the layer
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.hasLayer = function(name) {
|
||||
return this._layers.containsKey(name);
|
||||
};
|
||||
|
||||
gdjs.RuntimeScene.prototype.getAllLayerNames = function(result) {
|
||||
this._layers.keys(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the TimeManager of the scene.
|
||||
* @return {gdjs.TimeManager} The gdjs.TimeManager of the scene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getTimeManager = function() {
|
||||
return this._timeManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut to get the SoundManager of the game.
|
||||
* @return {gdjs.SoundManager} The gdjs.SoundManager of the game.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getSoundManager = function() {
|
||||
return this._runtimeGame.getSoundManager();
|
||||
};
|
||||
|
||||
//The flags to describe the change request by a scene:
|
||||
gdjs.RuntimeScene.CONTINUE = 0;
|
||||
gdjs.RuntimeScene.PUSH_SCENE = 1;
|
||||
gdjs.RuntimeScene.POP_SCENE = 2;
|
||||
gdjs.RuntimeScene.REPLACE_SCENE = 3;
|
||||
gdjs.RuntimeScene.CLEAR_SCENES = 4;
|
||||
gdjs.RuntimeScene.STOP_GAME = 5;
|
||||
|
||||
/**
|
||||
* Return the value of the scene change that is requested.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getRequestedChange = function() {
|
||||
return this._requestedChange;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the name of the new scene to be launched.
|
||||
*
|
||||
* See requestChange.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getRequestedScene = function() {
|
||||
return this._requestedScene;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request a scene change to be made. The change is handled externally (see gdjs.SceneStack)
|
||||
* thanks to getRequestedChange and getRequestedScene methods.
|
||||
* @param {number} change One of gdjs.RuntimeScene.CONTINUE|PUSH_SCENE|POP_SCENE|REPLACE_SCENE|CLEAR_SCENES|STOP_GAME.
|
||||
* @param {string} sceneName The name of the new scene to launch, if applicable.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.requestChange = function(change, sceneName) {
|
||||
this._requestedChange = change;
|
||||
this._requestedScene = sceneName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the profiler associated with the scene, or null if none.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getProfiler = function() {
|
||||
return this._profiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new profiler to measures the time passed in sections of the engine
|
||||
* in the scene.
|
||||
* @param {Function} onProfilerStopped Function to be called when the profiler is stopped. Will be passed the profiler as argument.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.startProfiler = function(onProfilerStopped) {
|
||||
if (this._profiler) return;
|
||||
|
||||
this._profiler = new gdjs.Profiler();
|
||||
this._onProfilerStopped = onProfilerStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the profiler being run on the scene.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.stopProfiler = function() {
|
||||
if (!this._profiler) return null;
|
||||
|
||||
var oldProfiler = this._profiler;
|
||||
var onProfilerStopped = this._onProfilerStopped;
|
||||
this._profiler = null;
|
||||
this._onProfilerStopped = null;
|
||||
|
||||
if (onProfilerStopped) {
|
||||
onProfilerStopped(oldProfiler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the structure containing the triggers for "Trigger once" conditions.
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getOnceTriggers = function() {
|
||||
return this._onceTriggers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all gdjs.RuntimeObject living on the scene.
|
||||
* You should not, normally, need this method at all. It's only to be used
|
||||
* in exceptional use cases where you need to loop through all objects,
|
||||
* and it won't be performant.
|
||||
*
|
||||
* @returns {gdjs.RuntimeObject[]} The list of all runtime objects on the scnee
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.getAdhocListOfAllInstances = function() {
|
||||
this._constructListOfAllInstances();
|
||||
return this._allInstancesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the scene was just resumed.
|
||||
* This is true during the first frame after the scene has been unpaused.
|
||||
*
|
||||
* @returns {boolean} true if the scene was just resumed
|
||||
*/
|
||||
gdjs.RuntimeScene.prototype.sceneJustResumed = function() {
|
||||
return this._isJustResumed;
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
// @ts-check
|
||||
|
||||
/**
|
||||
* Hold the stack of scenes (gdjs.RuntimeScene) being played.
|
||||
*
|
||||
* @memberof gdjs
|
||||
* @param {gdjs.RuntimeGame} runtimeGame The runtime game that is using the scene stack
|
||||
* @class SceneStack
|
||||
*/
|
||||
gdjs.SceneStack = function(runtimeGame) {
|
||||
if (!runtimeGame) {
|
||||
throw "SceneStack must be constructed with a gdjs.RuntimeGame."
|
||||
}
|
||||
|
||||
this._runtimeGame = runtimeGame;
|
||||
|
||||
/** @type {gdjs.RuntimeScene[]} */
|
||||
this._stack = [];
|
||||
|
||||
/** @type {boolean} */
|
||||
this._wasFirstSceneLoaded = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the RuntimeGame when the game resolution is changed.
|
||||
* Useful to notify scene and layers that resolution is changed, as they
|
||||
* might be caching it.
|
||||
*/
|
||||
gdjs.SceneStack.prototype.onGameResolutionResized = function() {
|
||||
for(var i = 0;i < this._stack.length; ++i) {
|
||||
this._stack[i].onGameResolutionResized();
|
||||
}
|
||||
};
|
||||
|
||||
gdjs.SceneStack.prototype.step = function(elapsedTime) {
|
||||
if (this._stack.length === 0) return false;
|
||||
|
||||
var currentScene = this._stack[this._stack.length - 1];
|
||||
if (currentScene.renderAndStep(elapsedTime)) {
|
||||
var request = currentScene.getRequestedChange();
|
||||
//Something special was requested by the current scene.
|
||||
if (request === gdjs.RuntimeScene.STOP_GAME) {
|
||||
this._runtimeGame.getRenderer().stopGame();
|
||||
return true;
|
||||
} else if (request === gdjs.RuntimeScene.POP_SCENE) {
|
||||
this.pop();
|
||||
} else if (request === gdjs.RuntimeScene.PUSH_SCENE) {
|
||||
this.push(currentScene.getRequestedScene());
|
||||
} else if (request === gdjs.RuntimeScene.REPLACE_SCENE) {
|
||||
this.replace(currentScene.getRequestedScene());
|
||||
} else if (request === gdjs.RuntimeScene.CLEAR_SCENES) {
|
||||
this.replace(currentScene.getRequestedScene(), true);
|
||||
} else {
|
||||
console.error("Unrecognized change in scene stack.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.SceneStack.prototype.renderWithoutStep = function() {
|
||||
if (this._stack.length === 0) return false;
|
||||
|
||||
var currentScene = this._stack[this._stack.length - 1];
|
||||
currentScene.render();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
gdjs.SceneStack.prototype.pop = function() {
|
||||
if (this._stack.length <= 1) return null;
|
||||
|
||||
// Unload the current scene
|
||||
var scene = this._stack.pop();
|
||||
if (!scene) return null;
|
||||
|
||||
scene.unloadScene();
|
||||
|
||||
// Tell the new current scene it's being resumed
|
||||
var currentScene = this._stack[this._stack.length - 1];
|
||||
if (currentScene) {
|
||||
currentScene.onResume();
|
||||
}
|
||||
|
||||
return scene;
|
||||
};
|
||||
|
||||
gdjs.SceneStack.prototype.push = function(newSceneName, externalLayoutName) {
|
||||
// Tell the scene it's being paused
|
||||
var currentScene = this._stack[this._stack.length - 1];
|
||||
if (currentScene) {
|
||||
currentScene.onPause();
|
||||
}
|
||||
|
||||
// Load the new one
|
||||
var newScene = new gdjs.RuntimeScene(this._runtimeGame);
|
||||
newScene.loadFromScene(this._runtimeGame.getSceneData(newSceneName));
|
||||
this._wasFirstSceneLoaded = true;
|
||||
|
||||
//Optionally create the objects from an external layout.
|
||||
if (externalLayoutName) {
|
||||
var externalLayoutData = this._runtimeGame.getExternalLayoutData(externalLayoutName);
|
||||
if (externalLayoutData)
|
||||
newScene.createObjectsFrom(externalLayoutData.instances, 0, 0);
|
||||
}
|
||||
|
||||
this._stack.push(newScene);
|
||||
return newScene;
|
||||
};
|
||||
|
||||
gdjs.SceneStack.prototype.replace = function(newSceneName, clear) {
|
||||
if (!!clear) {
|
||||
// Unload all the scenes
|
||||
while (this._stack.length !== 0) {
|
||||
var scene = this._stack.pop();
|
||||
if (scene) scene.unloadScene();
|
||||
}
|
||||
} else {
|
||||
// Unload the current scene
|
||||
if (this._stack.length !== 0) {
|
||||
var scene = this._stack.pop();
|
||||
if (scene) scene.unloadScene();
|
||||
}
|
||||
}
|
||||
|
||||
return this.push(newSceneName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the current gdjs.RuntimeScene being played, or null if none is run.
|
||||
*/
|
||||
gdjs.SceneStack.prototype.getCurrentScene = function() {
|
||||
if (this._stack.length === 0) return null;
|
||||
|
||||
return this._stack[this._stack.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if a scene was loaded, false otherwise (i.e: game not yet started).
|
||||
*/
|
||||
gdjs.SceneStack.prototype.wasFirstSceneLoaded = function() {
|
||||
return this._wasFirstSceneLoaded;
|
||||
}
|
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manage the timers and times elapsed during last
|
||||
* frame, since the beginning of the scene and other time related values.
|
||||
*
|
||||
* @class TimeManager
|
||||
* @memberof gdjs
|
||||
*/
|
||||
gdjs.TimeManager = function()
|
||||
{
|
||||
this.reset();
|
||||
}
|
||||
|
||||
gdjs.TimeManager.prototype.reset = function() {
|
||||
this._elapsedTime = 0;
|
||||
this._timeScale = 1;
|
||||
this._timeFromStart = 0;
|
||||
this._firstFrame = true;
|
||||
this._timers = new Hashtable();
|
||||
}
|
||||
|
||||
gdjs.TimeManager.prototype.update = function(elapsedTime, minimumFPS) {
|
||||
if (this._firstUpdateDone) this._firstFrame = false;
|
||||
this._firstUpdateDone = true;
|
||||
|
||||
//Compute the elapsed time since last frame
|
||||
this._elapsedTime = Math.min(elapsedTime, 1000/minimumFPS);
|
||||
this._elapsedTime *= this._timeScale;
|
||||
|
||||
//Update timers and others members
|
||||
for(var name in this._timers.items) {
|
||||
if (this._timers.items.hasOwnProperty(name)) {
|
||||
this._timers.items[name].updateTime(this._elapsedTime);
|
||||
}
|
||||
}
|
||||
|
||||
this._timeFromStart += this._elapsedTime;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the time scale: time will be slower if time scale is < 1,
|
||||
* faster if > 1.
|
||||
* @param {number} timeScale The new time scale (must be positive).
|
||||
*/
|
||||
gdjs.TimeManager.prototype.setTimeScale = function(timeScale) {
|
||||
if ( timeScale >= 0 ) this._timeScale = timeScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the time scale.
|
||||
* @return {number} The time scale (positive, 1 is normal speed).
|
||||
*/
|
||||
gdjs.TimeManager.prototype.getTimeScale = function() {
|
||||
return this._timeScale;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the time since the instanciation of the manager (i.e: since
|
||||
* the beginning of the scene most of the time), in milliseconds.
|
||||
*/
|
||||
gdjs.TimeManager.prototype.getTimeFromStart = function() {
|
||||
return this._timeFromStart;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if update was called only once (i.e: if the scene
|
||||
* is rendering its first frame).
|
||||
*/
|
||||
gdjs.TimeManager.prototype.isFirstFrame = function() {
|
||||
return this._firstFrame;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the time elapsed since the last call to update
|
||||
* (i.e: the last frame), in milliseconds.
|
||||
*/
|
||||
gdjs.TimeManager.prototype.getElapsedTime = function() {
|
||||
return this._elapsedTime;
|
||||
};
|
||||
|
||||
gdjs.TimeManager.prototype.addTimer = function(name) {
|
||||
this._timers.put(name, new gdjs.Timer(name));
|
||||
};
|
||||
|
||||
gdjs.TimeManager.prototype.hasTimer = function(name) {
|
||||
return this._timers.containsKey(name);
|
||||
};
|
||||
|
||||
gdjs.TimeManager.prototype.getTimer = function(name) {
|
||||
return this._timers.get(name);
|
||||
};
|
||||
|
||||
gdjs.TimeManager.prototype.removeTimer = function(name) {
|
||||
if (this._timers.containsKey(name)) this._timers.remove(name);
|
||||
};
|