/*********************************************************************** File : climbing.cpp Author: Jonathan Rust Climb uses Bullet Physics' ray cast test to find the end position of a climb, should it be successful, and then the collision sweep test to see if the player could actually climb without objects getting in the way. Then, if a climb was determined to actually be possible, it will move the body upward, then inward to the final position. All content © 2011 DigiPen (USA) Corporation, all rights reserved. ************************************************************************/ #pragma hdrstop #include "climbing.h" #include "Core/Entity/entity.h" #include "Core/GameState/gameState.h" #include "Core/input.h" #include #include "Game/GameCameraSystem.h" #include "Serialization/jsonSerializer.h" #include "Editor/editorMessages.h" #include "Physics/body.h" #include "Physics/shape.h" #include //---------------------------- // MORE STUFF NORMALLY GOES IN THIS SPACE HERE //---------------------------- // attempt to climb a ledge void Climbing::climb() { //detect if conditions are met to start climbing a wall if (!getInput().isButtonDown(' ') || isClimbing || !playerPosition->isRunningIntoWall()) return; //get the player's body, since it'll be used a lot Body* body = getOwner()->getBody(); //use the camera to calculate the dot product float rotateY = body->getRotation().getRotateY(); Vector4 bodyDir = Vector4(sin(rotateY), 0, cos(rotateY), 0); //find the dimensions of the hero Vector4 heroPos = body->getPosition(); Vector4 aabbMin; Vector4 aabbMax; body->getBoundingBox(aabbMin, aabbMax); float heroHalfHeight = (heroPos - aabbMin).y; float heroWidth = (heroPos - aabbMin).x; //determine the length of inward movement for the climb Vector4 inMove = bodyDir; inMove.y = 0.0f; inMove.normalize(); inMove *= heroWidth*inMoveFactor; startPosition = endPosition = heroPos; // cast ray down from above the object to find end position endPosition.y += heroHalfHeight; endPosition += inMove; Physics *physSystem = getOwningGameState()->findSystem(); Physics::RayTestResult rayResults = physSystem->testRay(endPosition, endPosition-Vector4(0,heroHalfHeight * 3,0,0)); // if results is empty, vault the object if (rayResults.empty()) { //TODO: vault(); //not climbing because I can't find the top of the object return; } pruneAssert(rayResults[0].entity->getID() != getOwner()->getID(), "got a collision with myself"); //calculate the ledge height for the benefit of the climb message (which is used to trigger the climb animation) float ledgeHeight = endPosition.y - rayResults[0].distance; endPosition.y = ledgeHeight + heroHalfHeight; //sweep test for any collisions during upward movement (before inward) Vector4 midPosition = startPosition; midPosition.y = endPosition.y; Physics::ConvexSweepResult sweepResults = physSystem->testSweep(startPosition, midPosition, *body); if (!sweepResults.empty()) { for (auto sweepIt = sweepResults.begin(); sweepIt != sweepResults.end(); ++sweepIt) { if (!sweepIt->collider->getBody()->getResolve()) continue; //not climbing because there is something above me return; } } //sweep test for any collisions during inward movement (after upward) endPosition += (bodyDir * heroWidth * 0.1f).zero(1,3); sweepResults = physSystem->testSweep(midPosition, endPosition, *body); if (!sweepResults.empty()) { for (auto sweepIt = sweepResults.begin(); sweepIt != sweepResults.end(); ++sweepIt) { if (!sweepIt->collider->getBody()->getResolve()) continue; if (sweepIt->normal.y < climbableGroundThreshold) { //not climbing because I can't stand on this object return; } } } //if there are no collisions during the sweep, climb away isClimbing = true; prevVelocity = body->getVelocity(); prevVelocity = prevVelocity.length(); if (prevVelocity.getValue() < defaultClimbingSpeed) { prevVelocity /= prevVelocity.getValue(); prevVelocity *= defaultClimbingSpeed; } //send the climb message to the rest of the entity's components (specifically for the animations) getOwner()->sendMessage(new ClimbMessage(ledgeHeight, calculateDuration(*body))); body->setKinematic(true); //set body to kinematic for full control while climbing }