mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-23 10:35:35 -04:00
23987f63c7
As flipping can be considered as a way to "rotate" things, it makes sense to have it used as a center for flipping too.i This is useful for objects that are moving and can be flipped according to if they are going left or right: they can now be rotated and flipped "properly" (properly means that the center will stay at the same position when flipped/rotated) This means that the center point won't move when the sprite is rotated or flipped. The origin is still used as the point not moving in case of scaling. (this make sense because scaling is about the size, and origin about positioning) This remove some calculations in the renderers, but add others in getDrawableX/Y and getCenterX/Y in case of flipping.
907 lines
33 KiB
JavaScript
907 lines
33 KiB
JavaScript
/**
|
|
GDevelop - Platform Behavior Extension
|
|
Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)
|
|
*/
|
|
|
|
/**
|
|
* PlatformerObjectRuntimeBehavior represents a behavior allowing objects to be
|
|
* considered as a platform by objects having PlatformerObject Behavior.
|
|
*
|
|
* @class PlatformerObjectRuntimeBehavior
|
|
* @augments {gdjs.RuntimeBehavior}
|
|
* @constructor
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior = function(runtimeScene, behaviorData, owner)
|
|
{
|
|
gdjs.RuntimeBehavior.call(this, runtimeScene, behaviorData, owner);
|
|
|
|
// To achieve pixel-perfect precision when positioning object on platform or
|
|
// handling collision with "walls", edges of the hitboxes must be ignored during
|
|
// collision checks, so that two overlapping edges are not considered as colliding.
|
|
// For example, if a character is 10px width and is at position (0, 0), it must not be
|
|
// considered as colliding with a platform which is at position (10, 0). Edges will
|
|
// still be overlapping (because character hitbox right edge is at X position 10 and
|
|
// platform hitbox left edge is also at X position 10).
|
|
// This parameter "_ignoreTouchingEdges" will be passed to all collision handling functions.
|
|
this._ignoreTouchingEdges = true;
|
|
|
|
this._roundCoordinates = behaviorData.roundCoordinates;
|
|
this._gravity = behaviorData.gravity;
|
|
this._maxFallingSpeed = behaviorData.maxFallingSpeed;
|
|
this._acceleration = behaviorData.acceleration;
|
|
this._deceleration = behaviorData.deceleration;
|
|
this._maxSpeed = behaviorData.maxSpeed;
|
|
this._jumpSpeed = behaviorData.jumpSpeed;
|
|
this._canGrabPlatforms = behaviorData.canGrabPlatforms || false;
|
|
this._yGrabOffset = behaviorData.yGrabOffset || 0;
|
|
this._xGrabTolerance = behaviorData.xGrabTolerance || 10;
|
|
this._isOnFloor = false;
|
|
this._isOnLadder = false;
|
|
this._floorPlatform = null;
|
|
this._currentFallSpeed = 0;
|
|
this._currentSpeed = 0;
|
|
this._jumping = false;
|
|
this._currentJumpSpeed = 0;
|
|
this._canJump = false;
|
|
this._isGrabbingPlatform = false;
|
|
this._grabbedPlatform = null;
|
|
this._ignoreDefaultControls = behaviorData.ignoreDefaultControls;
|
|
this._leftKey = false;
|
|
this._rightKey = false;
|
|
this._ladderKey = false;
|
|
this._upKey = false;
|
|
this._downKey = false;
|
|
this._jumpKey = false;
|
|
this._potentialCollidingObjects = this._potentialCollidingObjects || []; //Platforms near the object, updated with _updatePotentialCollidingObjects.
|
|
this._potentialCollidingObjects.length = 0;
|
|
this._overlappedJumpThru = this._overlappedJumpThru || [];
|
|
this._overlappedJumpThru.length = 0;
|
|
this._oldHeight = 0;//owner.getHeight(); //Be careful, object might not be initialized.
|
|
this._hasReallyMoved = false;
|
|
this.setSlopeMaxAngle(behaviorData.slopeMaxAngle);
|
|
this._manager = gdjs.PlatformObjectsManager.getManager(runtimeScene);
|
|
};
|
|
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype = Object.create( gdjs.RuntimeBehavior.prototype );
|
|
gdjs.PlatformerObjectRuntimeBehavior.thisIsARuntimeBehaviorConstructor = "PlatformBehavior::PlatformerObjectBehavior";
|
|
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.doStepPreEvents = function(runtimeScene)
|
|
{
|
|
var LEFTKEY = 37;
|
|
var UPKEY = 38;
|
|
var RIGHTKEY = 39;
|
|
var DOWNKEY = 40;
|
|
var SHIFTKEY = 16;
|
|
var SPACEKEY = 32;
|
|
var object = this.owner;
|
|
var timeDelta = this.owner.getElapsedTime(runtimeScene)/1000;
|
|
|
|
//0.1) Get the player input:
|
|
var requestedDeltaX = 0;
|
|
var requestedDeltaY = 0;
|
|
|
|
//Change the speed according to the player's input.
|
|
this._leftKey |= !this._ignoreDefaultControls && runtimeScene.getGame().getInputManager().isKeyPressed(LEFTKEY);
|
|
this._rightKey |= !this._ignoreDefaultControls && runtimeScene.getGame().getInputManager().isKeyPressed(RIGHTKEY);
|
|
if ( this._leftKey ) this._currentSpeed -= this._acceleration*timeDelta;
|
|
if ( this._rightKey ) this._currentSpeed += this._acceleration*timeDelta;
|
|
|
|
//Take deceleration into account only if no key is pressed.
|
|
if ( this._leftKey == this._rightKey ) {
|
|
var wasPositive = this._currentSpeed>0;
|
|
this._currentSpeed -= this._deceleration*timeDelta*(wasPositive ? 1.0 : -1.0);
|
|
|
|
//Set the speed to 0 if the speed was top low.
|
|
if ( wasPositive && this._currentSpeed < 0 ) this._currentSpeed = 0;
|
|
if ( !wasPositive && this._currentSpeed > 0 ) this._currentSpeed = 0;
|
|
}
|
|
|
|
if (this._currentSpeed > this._maxSpeed) this._currentSpeed = this._maxSpeed;
|
|
if (this._currentSpeed < -this._maxSpeed) this._currentSpeed = -this._maxSpeed;
|
|
requestedDeltaX += this._currentSpeed*timeDelta;
|
|
|
|
//0.2) Track changes in object size
|
|
|
|
//Stick the object to the floor if its height has changed.
|
|
if ( this._isOnFloor && this._oldHeight !== object.getHeight() ) {
|
|
object.setY(this._floorLastY
|
|
- object.getHeight()
|
|
+ (object.getY() - object.getDrawableY())
|
|
- 1
|
|
);
|
|
}
|
|
this._oldHeight = object.getHeight();
|
|
|
|
//0.3) Update list of platforms around/related to the object
|
|
|
|
//Compute the list of the objects that will be used
|
|
this._updatePotentialCollidingObjects(Math.max(requestedDeltaX, this._maxFallingSpeed*timeDelta) );
|
|
this._updateOverlappedJumpThru();
|
|
|
|
//Check that the floor object still exists and is near the object.
|
|
if ( this._isOnFloor && !this._isIn(this._potentialCollidingObjects, this._floorPlatform.owner.id) ) {
|
|
this._isOnFloor = false;
|
|
this._floorPlatform = null;
|
|
}
|
|
|
|
//Check that the grabbed platform object still exists and is near the object.
|
|
if ( this._isGrabbingPlatform && !this._isIn(this._potentialCollidingObjects, this._grabbedPlatform.owner.id) ) {
|
|
this._releaseGrabbedPlatform();
|
|
}
|
|
|
|
//1) X axis:
|
|
|
|
//Shift the object according to the floor movement.
|
|
if ( this._isOnFloor ) {
|
|
requestedDeltaX += this._floorPlatform.owner.getX() - this._floorLastX;
|
|
requestedDeltaY += this._floorPlatform.owner.getY() - this._floorLastY;
|
|
}
|
|
|
|
//Shift the object according to the grabbed platform movement.
|
|
if ( this._isGrabbingPlatform ) {
|
|
// This erases any other movement
|
|
requestedDeltaX = this._grabbedPlatform.owner.getX() - this._grabbedPlatformLastX;
|
|
requestedDeltaY = this._grabbedPlatform.owner.getY() - this._grabbedPlatformLastY;
|
|
}
|
|
|
|
//Ensure the object is not stuck
|
|
if (this._separateFromPlatforms(this._potentialCollidingObjects, true)) {
|
|
this._canJump = true; //After being unstuck, the object must be able to jump again.
|
|
}
|
|
|
|
//Move the object on x axis.
|
|
var oldX = object.getX();
|
|
if ( requestedDeltaX !== 0 ) {
|
|
var floorPlatformId = this._floorPlatform !== null ? this._floorPlatform.owner.id : null;
|
|
object.setX(object.getX()+requestedDeltaX);
|
|
var tryRounding = true;
|
|
//Colliding: Try to push out from the solid.
|
|
//Note that jump thru are never obstacle on X axis.
|
|
while ( this._isCollidingWith(this._potentialCollidingObjects, floorPlatformId, /*excludeJumpthrus=*/true) ) {
|
|
if ( (requestedDeltaX > 0 && object.getX() <= oldX) ||
|
|
(requestedDeltaX < 0 && object.getX() >= oldX) ) {
|
|
object.setX(oldX); //Unable to move the object without being stuck in an obstacle.
|
|
break;
|
|
}
|
|
|
|
//If on floor: try get up a bit to bypass not perfectly aligned floors.
|
|
if ( this._isOnFloor ){
|
|
object.setY(object.getY()-1);
|
|
if ( !this._isCollidingWith(this._potentialCollidingObjects, floorPlatformId, /*excludeJumpthrus=*/true) )
|
|
break;
|
|
object.setY(object.getY()+1);
|
|
}
|
|
|
|
if (tryRounding) {
|
|
// First try rounding the position as this might be sufficient to get the object
|
|
// out of the wall.
|
|
object.setX(Math.round(object.getX()));
|
|
tryRounding = false;
|
|
} else {
|
|
object.setX(Math.round(object.getX())+(requestedDeltaX > 0 ? -1 : 1));
|
|
}
|
|
this._currentSpeed = 0; //Collided with a wall
|
|
}
|
|
}
|
|
|
|
//2) Y axis:
|
|
|
|
//Go on a ladder
|
|
this._ladderKey |= !this._ignoreDefaultControls && runtimeScene.getGame().getInputManager().isKeyPressed(UPKEY);
|
|
if (this._ladderKey && this._isOverlappingLadder()) {
|
|
this._canJump = true;
|
|
this._isOnFloor = false;
|
|
this._floorPlatform = null;
|
|
this._currentJumpSpeed = 0;
|
|
this._currentFallSpeed = 0;
|
|
this._isOnLadder = true;
|
|
}
|
|
|
|
if ( this._isOnLadder ) {
|
|
this._upKey |= !this._ignoreDefaultControls && runtimeScene.getGame().getInputManager().isKeyPressed(UPKEY);
|
|
this._downKey |= !this._ignoreDefaultControls && runtimeScene.getGame().getInputManager().isKeyPressed(DOWNKEY);
|
|
if ( this._upKey )
|
|
requestedDeltaY -= 150*timeDelta;
|
|
if ( this._downKey )
|
|
requestedDeltaY += 150*timeDelta;
|
|
|
|
//Coming to an extremity of a ladder
|
|
if ( !this._isOverlappingLadder() ) {
|
|
this._isOnLadder = false;
|
|
}
|
|
}
|
|
|
|
//Fall
|
|
if (!this._isOnFloor && !this._isOnLadder && !this._isGrabbingPlatform) {
|
|
this._currentFallSpeed += this._gravity*timeDelta;
|
|
if ( this._currentFallSpeed > this._maxFallingSpeed ) this._currentFallSpeed = this._maxFallingSpeed;
|
|
|
|
requestedDeltaY += this._currentFallSpeed*timeDelta;
|
|
requestedDeltaY = Math.min(requestedDeltaY, this._maxFallingSpeed*timeDelta);
|
|
}
|
|
|
|
//Grabbing a platform
|
|
if (this._canGrabPlatforms && requestedDeltaX !== 0 &&
|
|
!this._isOnLadder && !this._isOnFloor) {
|
|
var tryGrabbingPlatform = false;
|
|
|
|
object.setX(object.getX() + (requestedDeltaX > 0 ? this._xGrabTolerance : -this._xGrabTolerance));
|
|
var collidingPlatform = this._getCollidingPlatform();
|
|
if (collidingPlatform !== null && this._canGrab(collidingPlatform, requestedDeltaY)) {
|
|
tryGrabbingPlatform = true;
|
|
}
|
|
object.setX(object.getX() + (requestedDeltaX > 0 ? -this._xGrabTolerance : this._xGrabTolerance));
|
|
|
|
//Check if we can grab the collided platform
|
|
if (tryGrabbingPlatform)
|
|
{
|
|
var oldY = object.getY();
|
|
object.setY(collidingPlatform.owner.getY() + collidingPlatform.getYGrabOffset() - this._yGrabOffset);
|
|
if (!this._isCollidingWith(this._potentialCollidingObjects, null, /*excludeJumpthrus=*/true)) {
|
|
this._isGrabbingPlatform = true;
|
|
this._grabbedPlatform = collidingPlatform;
|
|
requestedDeltaY = 0;
|
|
} else {
|
|
object.setY(oldY);
|
|
}
|
|
}
|
|
}
|
|
|
|
this._releaseKey |= !this._ignoreDefaultControls && runtimeScene.getGame().getInputManager().isKeyPressed(DOWNKEY);
|
|
if (this._isGrabbingPlatform && !this._releaseKey) {
|
|
this._canJump = true;
|
|
this._currentJumpSpeed = 0;
|
|
this._currentFallSpeed = 0;
|
|
this._grabbedPlatformLastX = this._grabbedPlatform.owner.getX();
|
|
this._grabbedPlatformLastY = this._grabbedPlatform.owner.getY();
|
|
}
|
|
if (this._releaseKey) this._releaseGrabbedPlatform();
|
|
|
|
//Jumping
|
|
this._jumpKey |= !this._ignoreDefaultControls &&
|
|
(runtimeScene.getGame().getInputManager().isKeyPressed(SHIFTKEY) ||
|
|
runtimeScene.getGame().getInputManager().isKeyPressed(SPACEKEY));
|
|
if ( this._canJump && this._jumpKey ) {
|
|
this._jumping = true;
|
|
this._canJump = false;
|
|
//this._isOnFloor = false; If floor is a very steep slope, the object could go into it.
|
|
this._isOnLadder = false;
|
|
this._currentJumpSpeed = this._jumpSpeed;
|
|
this._currentFallSpeed = 0;
|
|
this._isGrabbingPlatform = false;
|
|
//object.setY(object.getY()-1); //Useless and dangerous
|
|
}
|
|
|
|
if ( this._jumping ) {
|
|
requestedDeltaY -= this._currentJumpSpeed*timeDelta;
|
|
this._currentJumpSpeed -= this._gravity*timeDelta;
|
|
if ( this._currentJumpSpeed < 0 ) {
|
|
this._currentJumpSpeed = 0;
|
|
this._jumping = false;
|
|
}
|
|
}
|
|
|
|
//Follow the floor
|
|
if ( this._isOnFloor ) {
|
|
if ( gdjs.RuntimeObject.collisionTest(object, this._floorPlatform.owner, this._ignoreTouchingEdges) ) {
|
|
//Floor is getting up, as the object is colliding with it.
|
|
var oldY = object.getY();
|
|
var step = 0;
|
|
var stillInFloor = false;
|
|
do {
|
|
if ( step >= Math.floor(Math.abs(requestedDeltaX*this._slopeClimbingFactor)) ) { //Slope is too step ( > 50% )
|
|
|
|
object.setY(object.getY()-(Math.abs(requestedDeltaX*this._slopeClimbingFactor)-step)); //Try to add the decimal part.
|
|
if ( gdjs.RuntimeObject.collisionTest(object, this._floorPlatform.owner, this._ignoreTouchingEdges) )
|
|
stillInFloor = true; //Too steep.
|
|
|
|
break;
|
|
}
|
|
|
|
//Try to get out of the floor.
|
|
object.setY(object.getY()-1);
|
|
step++;
|
|
}
|
|
while ( gdjs.RuntimeObject.collisionTest(object, this._floorPlatform.owner, this._ignoreTouchingEdges) );
|
|
|
|
if ( stillInFloor ) {
|
|
object.setY(oldY); //Unable to follow the floor ( too steep ): Go back to the original position.
|
|
object.setX(oldX); //And also revert the shift on X axis.
|
|
}
|
|
} else {
|
|
//Floor is flat or get down.
|
|
var oldY = object.getY();
|
|
var tentativeStartY = object.getY()+1;
|
|
object.setY(this._roundCoordinates ? Math.round(tentativeStartY) : tentativeStartY);
|
|
var step = 0;
|
|
var noMoreOnFloor = false;
|
|
while ( !this._isCollidingWith(this._potentialCollidingObjects) ) {
|
|
if ( step > Math.abs(requestedDeltaX*this._slopeClimbingFactor) ) { //Slope is too step ( > 50% )
|
|
noMoreOnFloor = true;
|
|
break;
|
|
}
|
|
|
|
//Object was on floor, but no more: Maybe a slope, try to follow it.
|
|
object.setY(object.getY()+1);
|
|
step++;
|
|
}
|
|
if ( noMoreOnFloor )
|
|
object.setY(oldY); //Unable to follow the floor: Go back to the original position. Fall will be triggered next tick.
|
|
else
|
|
object.setY(object.getY()-1); //Floor touched: Go back 1 pixel over.
|
|
}
|
|
|
|
}
|
|
|
|
//Move the object on Y axis
|
|
if ( requestedDeltaY !== 0 ) {
|
|
var oldY = object.getY();
|
|
object.setY(object.getY()+requestedDeltaY);
|
|
|
|
//Stop when colliding with an obstacle.
|
|
while ( (requestedDeltaY < 0 && this._isCollidingWith(this._potentialCollidingObjects, null, /*excludeJumpThrus=*/true)) //Jumpthru = obstacle <=> Never when going up
|
|
|| (requestedDeltaY > 0 && this._isCollidingWithExcluding(this._potentialCollidingObjects, this._overlappedJumpThru)) ) //Jumpthru = obstacle <=> Only if not already overlapped when goign down
|
|
{
|
|
this._jumping = false;
|
|
this._currentJumpSpeed = 0;
|
|
if ( (requestedDeltaY > 0 && object.getY() <= oldY) ||
|
|
(requestedDeltaY < 0 && object.getY() >= oldY) ) {
|
|
object.setY(oldY); //Unable to move the object without being stuck in an obstacle.
|
|
break;
|
|
}
|
|
|
|
object.setY(Math.floor(object.getY())+(requestedDeltaY > 0 ? -1 : 1));
|
|
}
|
|
|
|
}
|
|
|
|
//3) Update the current floor data for the next tick:
|
|
this._updateOverlappedJumpThru();
|
|
if ( !this._isOnLadder ) {
|
|
//Check if the object is on a floor:
|
|
//In priority, check if the last floor platform is still the floor.
|
|
var oldY = object.getY();
|
|
object.setY(object.getY()+1);
|
|
if ( this._isOnFloor && gdjs.RuntimeObject.collisionTest(object, this._floorPlatform.owner, this._ignoreTouchingEdges) ) {
|
|
//Still on the same floor
|
|
this._floorLastX = this._floorPlatform.owner.getX();
|
|
this._floorLastY = this._floorPlatform.owner.getY();
|
|
|
|
this._releaseGrabbedPlatform(); //Ensure nothing is grabbed.
|
|
}
|
|
else {
|
|
// Avoid landing on a platform if the object is not going down.
|
|
// (which could happen for Jumpthru, when the object jump and pass just at the top
|
|
// of a jumpthru, it could be considered as landing if not this this extra check).
|
|
var canLand = requestedDeltaY >= 0;
|
|
|
|
//Check if landing on a new floor: (Exclude already overlapped jump truh)
|
|
var collidingPlatform = this._getCollidingPlatform();
|
|
if (canLand && collidingPlatform !== null) {
|
|
this._isOnFloor = true;
|
|
this._canJump = true;
|
|
this._jumping = false;
|
|
this._currentJumpSpeed = 0;
|
|
this._currentFallSpeed = 0;
|
|
|
|
//Register the colliding platform as the floor.
|
|
this._floorPlatform = collidingPlatform;
|
|
this._floorLastX = this._floorPlatform.owner.getX();
|
|
this._floorLastY = this._floorPlatform.owner.getY();
|
|
|
|
this._releaseGrabbedPlatform(); //Ensure nothing is grabbed.
|
|
} else { //In the air
|
|
this._canJump = false;
|
|
this._isOnFloor = false;
|
|
this._floorPlatform = null;
|
|
}
|
|
}
|
|
object.setY(oldY);
|
|
}
|
|
|
|
//4) Do not forget to reset pressed keys
|
|
this._leftKey = false;
|
|
this._rightKey = false;
|
|
this._ladderKey = false;
|
|
this._upKey = false;
|
|
this._downKey = false;
|
|
this._releaseKey = false;
|
|
this._jumpKey = false;
|
|
|
|
//5) Track the movement
|
|
this._hasReallyMoved = Math.abs(object.getX()-oldX) >= 1;
|
|
};
|
|
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.doStepPostEvents = function(runtimeScene) {
|
|
//Scene change is not supported
|
|
/*
|
|
if ( parentScene != &scene ) //Parent scene has changed
|
|
{
|
|
parentScene = &scene;
|
|
sceneManager = parentScene ? &ScenePlatformObjectsManager::managers[&scene] : null;
|
|
floorPlatform = null;
|
|
}
|
|
*/
|
|
};
|
|
|
|
/**
|
|
* Return true if the object owning the behavior can grab the specified platform. There must be a collision
|
|
* between the object and the platform.
|
|
* @param platform The platform the object is in collision with
|
|
* @param y The value in pixels on Y axis the object wants to move to
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._canGrab = function(platform, requestedDeltaY) {
|
|
var y1 = this.owner.getY() + this._yGrabOffset;
|
|
var y2 = this.owner.getY() + this._yGrabOffset + requestedDeltaY;
|
|
var platformY = platform.owner.getY() + platform.getYGrabOffset();
|
|
|
|
return (
|
|
platform.canBeGrabbed() &&
|
|
((y1 < platformY && platformY < y2) || (y2 < platformY && platformY < y1))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Mark the platformer object has not being grabbing any platform.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._releaseGrabbedPlatform = function() {
|
|
this._isGrabbingPlatform = false; //Ensure nothing is grabbed.
|
|
this._grabbedPlatform = null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Among the platforms passed in parameter, return true if there is a platform colliding with the object.
|
|
* Ladders are *always* excluded from the test.
|
|
* @param candidates The platform to be tested for collision
|
|
* @param exceptThisOne The object identifier of a platform to be excluded from the check. Can be null.
|
|
* @param excludeJumpThrus If set to true, jumpthru platforms are excluded. false if not defined.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._isCollidingWith = function(candidates, exceptThisOne, excludeJumpThrus)
|
|
{
|
|
excludeJumpThrus = !!excludeJumpThrus;
|
|
|
|
for (var i = 0;i<candidates.length;++i) {
|
|
var platform = candidates[i];
|
|
|
|
if ( platform.owner.id === exceptThisOne ) continue;
|
|
if ( platform.getPlatformType() === gdjs.PlatformRuntimeBehavior.LADDER ) continue;
|
|
if ( excludeJumpThrus && platform.getPlatformType() === gdjs.PlatformRuntimeBehavior.JUMPTHRU ) continue;
|
|
|
|
if ( gdjs.RuntimeObject.collisionTest(this.owner, platform.owner, this._ignoreTouchingEdges) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Separate the object from all platforms passed in parameter.
|
|
* @param candidates The platform to be tested for collision
|
|
* @param excludeJumpThrus If set to true, jumpthru platforms are excluded. false if not defined.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._separateFromPlatforms = function(candidates, excludeJumpThrus)
|
|
{
|
|
excludeJumpThrus = !!excludeJumpThrus;
|
|
|
|
var objects = gdjs.staticArray(gdjs.PlatformerObjectRuntimeBehavior.prototype._separateFromPlatforms);
|
|
objects.length = 0;
|
|
|
|
for (var i = 0;i<candidates.length;++i) {
|
|
var platform = candidates[i];
|
|
|
|
if ( platform.getPlatformType() === gdjs.PlatformRuntimeBehavior.LADDER ) continue;
|
|
if ( excludeJumpThrus && platform.getPlatformType() === gdjs.PlatformRuntimeBehavior.JUMPTHRU ) continue;
|
|
|
|
objects.push(platform.owner);
|
|
}
|
|
|
|
return this.owner.separateFromObjects(objects, this._ignoreTouchingEdges);
|
|
};
|
|
|
|
/**
|
|
* Among the platforms passed in parameter, return true if there is a platform colliding with the object.
|
|
* Ladders are *always* excluded from the test.
|
|
* @param candidates The platform to be tested for collision
|
|
* @param exceptTheseOnes The platforms to be excluded from the test
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._isCollidingWithExcluding = function(candidates, exceptTheseOnes)
|
|
{
|
|
for (var i = 0;i<candidates.length;++i) {
|
|
var platform = candidates[i];
|
|
|
|
if (exceptTheseOnes && this._isIn(exceptTheseOnes, platform.owner.id)) continue;
|
|
if ( platform.getPlatformType() === gdjs.PlatformRuntimeBehavior.LADDER ) continue;
|
|
if ( gdjs.RuntimeObject.collisionTest(this.owner, platform.owner, this._ignoreTouchingEdges) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Return (one of) the platform which is colliding with the behavior owner object.
|
|
* Overlapped jump thru and ladders are excluded.
|
|
* _updatePotentialCollidingObjects and _updateOverlappedJumpThru should have been called before.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._getCollidingPlatform = function()
|
|
{
|
|
for (var i = 0;i < this._potentialCollidingObjects.length;++i) {
|
|
var platform = this._potentialCollidingObjects[i];
|
|
|
|
if ( platform.getPlatformType() !== gdjs.PlatformRuntimeBehavior.LADDER
|
|
&& !this._isIn(this._overlappedJumpThru, platform.owner.id)
|
|
&& gdjs.RuntimeObject.collisionTest(this.owner, platform.owner, this._ignoreTouchingEdges) ) {
|
|
return platform;
|
|
}
|
|
}
|
|
|
|
return null; //Nothing is being colliding with the behavior object.
|
|
};
|
|
|
|
/**
|
|
* Update _overlappedJumpThru member, so that it contains all the jumpthru platforms colliding with
|
|
* the behavior owner object.
|
|
* Note: _updatePotentialCollidingObjects must have been called before.
|
|
* @private
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._updateOverlappedJumpThru = function()
|
|
{
|
|
this._overlappedJumpThru.length = 0;
|
|
for (var i = 0;i < this._potentialCollidingObjects.length;++i) {
|
|
var platform = this._potentialCollidingObjects[i];
|
|
|
|
if ( platform.getPlatformType() === gdjs.PlatformRuntimeBehavior.JUMPTHRU
|
|
&& gdjs.RuntimeObject.collisionTest(this.owner, platform.owner, this._ignoreTouchingEdges) ) {
|
|
this._overlappedJumpThru.push(platform);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Return true if the object is overlapping a ladder.
|
|
* Note: _updatePotentialCollidingObjects must have been called before.
|
|
* @private
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._isOverlappingLadder = function() {
|
|
for (var i = 0;i < this._potentialCollidingObjects.length;++i) {
|
|
var platform = this._potentialCollidingObjects[i];
|
|
|
|
if ( platform.getPlatformType() !== gdjs.PlatformRuntimeBehavior.LADDER ) continue;
|
|
if ( gdjs.RuntimeObject.collisionTest(this.owner, platform.owner, this._ignoreTouchingEdges) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._isIn = function(platformArray, id) {
|
|
for (var i = 0;i < platformArray.length;++i) {
|
|
if (platformArray[i].owner.id === id)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Update _potentialCollidingObjects member with platforms near the object.
|
|
* @private
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype._updatePotentialCollidingObjects = function(maxMovementLength)
|
|
{
|
|
this._manager.getAllPlatformsAround(this.owner, maxMovementLength, this._potentialCollidingObjects);
|
|
|
|
//This is the naive implementation when the platforms manager is simply containing a list
|
|
//of all existing platforms:
|
|
|
|
/*var o1w = this.owner.getWidth();
|
|
var o1h = this.owner.getHeight();
|
|
var obj1BoundingRadius = Math.sqrt(o1w*o1w+o1h*o1h)/2.0 + maxMovementLength;
|
|
|
|
//Get all platforms and keep only
|
|
var allPlatforms = this._manager.getAllPlatforms();
|
|
for (var k in allPlatforms.items) {
|
|
if (allPlatforms.items.hasOwnProperty(k)) {
|
|
var obj2 = allPlatforms.items[k].owner;
|
|
|
|
var o2w = obj2.getWidth();
|
|
var o2h = obj2.getHeight();
|
|
|
|
// 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 x = this.owner.getDrawableX()+this.owner.getCenterX()-(obj2.getDrawableX()+obj2.getCenterX());
|
|
var y = this.owner.getDrawableY()+this.owner.getCenterY()-(obj2.getDrawableY()+obj2.getCenterY());
|
|
var obj2BoundingRadius = Math.sqrt(o2w*o2w+o2h*o2h)/2.0;
|
|
|
|
if ( Math.sqrt(x*x+y*y) <= obj1BoundingRadius + obj2BoundingRadius ) {
|
|
if ( !this._potentialCollidingObjects.hasOwnProperty(k) )
|
|
this._potentialCollidingObjects[k] = allPlatforms.items[k];
|
|
}
|
|
else {
|
|
if ( this._potentialCollidingObjects.hasOwnProperty(k) )
|
|
delete this._potentialCollidingObjects[k];
|
|
}
|
|
}
|
|
}*/
|
|
};
|
|
|
|
/**
|
|
* Simulate a control action in the Platformer Object by specifying an input.
|
|
* @param {string} input The string expression of the control action [Left,Right,Up,Down,Ladder,Jump,Release].
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateControl = function(input) {
|
|
if ( input === "Left" ) this._leftKey = true;
|
|
else if ( input === "Right" ) this._rightKey = true;
|
|
else if ( input === "Up" ) this._upKey = true;
|
|
else if ( input === "Down" ) this._downKey = true;
|
|
else if ( input === "Ladder" ) this._ladderKey = true;
|
|
else if ( input === "Jump" ) this._jumpKey = true;
|
|
else if ( input === "Release" ) this._releaseKey = true;
|
|
};
|
|
|
|
/**
|
|
* Get the gravity of the Platformer Object.
|
|
* @returns {number} The current gravity.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.getGravity = function()
|
|
{
|
|
return this._gravity;
|
|
};
|
|
|
|
/**
|
|
* Get the maximum falling speed of the Platformer Object.
|
|
* @returns {number} The maximum falling speed.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.getMaxFallingSpeed = function()
|
|
{
|
|
return this._maxFallingSpeed;
|
|
};
|
|
|
|
/**
|
|
* Get the acceleration value of the Platformer Object.
|
|
* @returns {number} The current acceleration.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.getAcceleration = function()
|
|
{
|
|
return this._acceleration;
|
|
};
|
|
|
|
/**
|
|
* Get the deceleration of the Platformer Object.
|
|
* @returns {number} The current deceleration.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.getDeceleration = function()
|
|
{
|
|
return this._deceleration;
|
|
};
|
|
|
|
/**
|
|
* Get the maximum speed of the Platformer Object.
|
|
* @returns {number} The maximum speed.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.getMaxSpeed = function()
|
|
{
|
|
return this._maxSpeed;
|
|
};
|
|
|
|
/**
|
|
* Get the jump speed of the Platformer Object.
|
|
* @returns {number} The jump speed.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.getJumpSpeed = function()
|
|
{
|
|
return this._jumpSpeed;
|
|
};
|
|
|
|
/**
|
|
* Set the gravity of the Platformer Object.
|
|
* @param {number} gravity The new gravity.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setGravity = function(gravity)
|
|
{
|
|
this._gravity = gravity;
|
|
};
|
|
|
|
/**
|
|
* Set the maximum falling speed of the Platformer Object.
|
|
* @param {number} maxFallingSpeed The maximum falling speed.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setMaxFallingSpeed = function(maxFallingSpeed)
|
|
{
|
|
this._maxFallingSpeed = maxFallingSpeed;
|
|
};
|
|
|
|
/**
|
|
* Set the acceleration of the Platformer Object.
|
|
* @param {number} acceleration The new acceleration.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setAcceleration = function(acceleration)
|
|
{
|
|
this._acceleration = acceleration;
|
|
};
|
|
|
|
/**
|
|
* Set the deceleration of the Platformer Object.
|
|
* @param {number} deceleration The new deceleration.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setDeceleration = function(deceleration)
|
|
{
|
|
this._deceleration = deceleration;
|
|
};
|
|
|
|
/**
|
|
* Set the maximum speed of the Platformer Object.
|
|
* @param {number} maxSpeed The new maximum speed.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setMaxSpeed = function(maxSpeed)
|
|
{
|
|
this._maxSpeed = maxSpeed;
|
|
};
|
|
|
|
/**
|
|
* Set the jump speed of the Platformer Object.
|
|
* @param {number} jumpSpeed The new jump speed.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setJumpSpeed = function(jumpSpeed)
|
|
{
|
|
this._jumpSpeed = jumpSpeed;
|
|
};
|
|
|
|
/**
|
|
* Set the maximum slope angle of the Platformer Object.
|
|
* @param {number} slopeMaxAngle The new maximum slope angle.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setSlopeMaxAngle = function(slopeMaxAngle)
|
|
{
|
|
if (slopeMaxAngle < 0 || slopeMaxAngle >= 90) return;
|
|
|
|
this._slopeMaxAngle = slopeMaxAngle;
|
|
if ( slopeMaxAngle == 45 )
|
|
this._slopeClimbingFactor = 1; //Avoid rounding errors
|
|
else
|
|
this._slopeClimbingFactor = Math.tan(slopeMaxAngle*3.1415926/180.0);
|
|
};
|
|
|
|
/**
|
|
* Allow the Platformer Object to jump again.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setCanJump = function()
|
|
{
|
|
this._canJump = true;
|
|
};
|
|
|
|
/**
|
|
* Set if the Platformer Object can grab platforms.
|
|
* @param {boolean} enable Enable / Disable grabbing of platforms.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.setCanGrabPlatforms = function(enable)
|
|
{
|
|
this._canGrabPlatforms = enable;
|
|
if (!this._canGrabPlatforms) {
|
|
this._releaseGrabbedPlatform();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Ignore the default controls of the Platformer Object.
|
|
* @param {boolean} ignore Enable / Disable default controls.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.ignoreDefaultControls = function(ignore)
|
|
{
|
|
this._ignoreDefaultControls = ignore;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Left" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateLeftKey = function()
|
|
{
|
|
this._leftKey = true;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Right" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateRightKey = function()
|
|
{
|
|
this._rightKey = true;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Ladder" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateLadderKey = function()
|
|
{
|
|
this._ladderKey = true;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Up" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateUpKey = function()
|
|
{
|
|
this._upKey = true;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Down" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateDownKey = function()
|
|
{
|
|
this._downKey = true;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Jump" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateJumpKey = function()
|
|
{
|
|
this._jumpKey = true;
|
|
};
|
|
|
|
/**
|
|
* Simulate the "Release" control of the Platformer Object.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.simulateReleaseKey = function()
|
|
{
|
|
this._releaseKey = true;
|
|
};
|
|
|
|
/**
|
|
* Check if the Platformer Object is on the floor.
|
|
* @returns {boolean} Returns true if on the floor and false if not.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.isOnFloor = function()
|
|
{
|
|
return this._isOnFloor;
|
|
};
|
|
|
|
/**
|
|
* Check if the Platformer Object is on a ladder.
|
|
* @returns {boolean} Returns true if on a ladder and false if not.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.isOnLadder = function()
|
|
{
|
|
return this._isOnLadder;
|
|
};
|
|
|
|
/**
|
|
* Check if the Platformer Object is jumping.
|
|
* @returns {boolean} Returns true if jumping and false if not.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.isJumping = function()
|
|
{
|
|
return this._jumping;
|
|
};
|
|
|
|
/**
|
|
* Check if the Platformer Object is grabbing a platform.
|
|
* @returns {boolean} Returns true if a platform is grabbed and false if not.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.isGrabbingPlatform = function()
|
|
{
|
|
return this._isGrabbingPlatform;
|
|
};
|
|
|
|
/**
|
|
* Check if the Platformer Object is falling.
|
|
* @returns {boolean} Returns true if it is falling and false if not.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.isFalling = function()
|
|
{
|
|
return !this._isOnFloor &&
|
|
!this._isGrabbingPlatform &&
|
|
!this._isOnLadder &&
|
|
(!this._jumping || this._currentJumpSpeed < this._currentFallSpeed) ;
|
|
};
|
|
|
|
/**
|
|
* Check if the Platformer Object is moving.
|
|
* @returns {boolean} Returns true if it is moving and false if not.
|
|
*/
|
|
gdjs.PlatformerObjectRuntimeBehavior.prototype.isMoving = function()
|
|
{
|
|
return (this._hasReallyMoved &&this._currentSpeed !== 0) || this._currentJumpSpeed !== 0 || this._currentFallSpeed !== 0;
|
|
};
|