scummvm/engines/hpl1/penumbra-overture/PlayerState_Interact.cpp
2024-08-10 11:43:44 +03:00

1087 lines
34 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of Penumbra Overture.
*/
#include "hpl1/penumbra-overture/PlayerState_Interact.h"
#include "hpl1/engine/engine.h"
#include "hpl1/penumbra-overture/GameStickArea.h"
#include "hpl1/penumbra-overture/Init.h"
#include "hpl1/penumbra-overture/MapHandler.h"
#include "hpl1/penumbra-overture/Player.h"
//////////////////////////////////////////////////////////////////////////
// GRAB STATE
//////////////////////////////////////////////////////////////////////////
float cPlayerState_Grab::mfMassDiv = 5.0f;
cPlayerState_Grab::cPlayerState_Grab(cInit *apInit, cPlayer *apPlayer) : iPlayerState(apInit, apPlayer, ePlayerState_Grab) {
mpPushBody = NULL;
// Init controllers
mGrabPid.SetErrorNum(20);
mRotatePid.SetErrorNum(20);
mRotatePid.p = 0.8f;
mRotatePid.i = 0.0f;
mRotatePid.d = 0.0f;
// Get variables
mfMaxPidForce = mpInit->mpGameConfig->GetFloat("Interaction_Grab", "MaxPidForce", 0);
mfMinThrowMass = mpInit->mpGameConfig->GetFloat("Interaction_Grab", "MinThrowMass", 0);
mfMaxThrowMass = mpInit->mpGameConfig->GetFloat("Interaction_Grab", "MaxThrowMass", 0);
mfMinThrowImpulse = mpInit->mpGameConfig->GetFloat("Interaction_Grab", "MinThrowImpulse", 0);
mfMaxThrowImpulse = mpInit->mpGameConfig->GetFloat("Interaction_Grab", "MaxThrowImpulse", 0);
// Get font
mpFont = mpInit->mpGame->GetResources()->GetFontManager()->CreateFontData("verdana.fnt");
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnUpdate(float afTimeStep) {
cCamera3D *pCamera = mpPlayer->GetCamera();
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
cInput *pInput = mpInit->mpGame->GetInput();
// cVector3f vToCharDir =
if (pInput->IsTriggerd("WheelUp")) {
if (mfGrabDist < mpPlayer->mfCurrentMaxInteractDist * 0.8f)
mfGrabDist += 0.1f;
}
if (pInput->IsTriggerd("WheelDown")) {
if (mfGrabDist > mpPlayer->GetSize().x / 2.0f + 0.25f) {
// TODO: Collison check? (might be a bit complex)
mfGrabDist -= 0.1f;
}
}
///////////////////////////////////
// Get the current position.
float fAngleDist = cMath::GetAngleDistanceRad(mfStartYaw, mpPlayer->GetCamera()->GetYaw());
// Det the desired position
cVector3f vCurrentPoint;
if (mbPickAtPoint) {
vCurrentPoint = cMath::MatrixMul(mpPushBody->GetLocalMatrix(), mvRelPickPoint);
} else {
vCurrentPoint = cMath::MatrixMul(cMath::MatrixRotateY(fAngleDist), mvRelPickPoint);
vCurrentPoint = cMath::MatrixMul(mpPushBody->GetWorldMatrix(), mpPushBody->GetMassCentre()) + vCurrentPoint;
}
//////////////////////////////////////
// Check if player is close enough
cVector3f vEnd = vCurrentPoint;
cVector3f vStart = mpPlayer->GetCamera()->GetPosition();
float fDistance = cMath::Vector3Dist(vStart, vEnd);
if (fDistance > mpPlayer->mfCurrentMaxInteractDist * 1.4f) {
mpPlayer->ChangeState(mPrevState);
return;
}
//////////////////////////////////////
// Move the object into position.
// Setup PID
if (mbPickAtPoint) {
mGrabPid.p = 80.0f;
mGrabPid.i = 0.0f;
mGrabPid.d = 8.0f;
} else {
mGrabPid.p = 180.0f;
mGrabPid.i = 0.0f;
mGrabPid.d = 40.0f;
}
///////////////////////////////////////
// Get the desired position
cVector3f vForward = pCamera->UnProject(mpPlayer->GetCrossHairPos(),
mpInit->mpGame->GetGraphics()->GetLowLevel());
cVector3f vDesired = pCamera->GetPosition() + vForward * mfGrabDist;
///////////////////////////////////////
// Calculate the force to add.
cVector3f vForce(0, 0, 0);
// Remove gravity
if (mpPushBody->GetGravity())
vForce = pPhysicsWorld->GetGravity() * -1.0f;
// get force from PID
vForce += mGrabPid.Output(vDesired - vCurrentPoint, afTimeStep);
float fForceSize = vForce.Length();
if (fForceSize > mfMaxPidForce) {
vForce = (vForce / fForceSize) * mfMaxPidForce;
}
if (mbPickAtPoint) {
// mpPushBody->AddForceAtPosition(vForce * mpPushBody->GetMass()*mpPlayer->mfGrabMassMul, vCurrentPoint);
// TODO: User intertia instead.
cVector3f vLocalPos = vCurrentPoint - mpPushBody->GetLocalPosition();
cVector3f vMassCentre = mpPushBody->GetMassCentre();
vMassCentre = cMath::MatrixMul(mpPushBody->GetLocalMatrix().GetRotation(), vMassCentre);
vLocalPos -= vMassCentre;
mpPushBody->AddForce(vForce * mpPushBody->GetMass() * mpPlayer->mfGrabMassMul);
cVector3f vTorque = cMath::Vector3Cross(vLocalPos, vForce);
vTorque = cMath::MatrixMul(mpPushBody->GetInertiaMatrix(), vTorque);
mpPushBody->AddTorque(vTorque);
} else {
mpPushBody->AddForce(vForce * mpPushBody->GetMass() * mpPlayer->mfGrabMassMul);
}
////////////////////////////
// The rotation
cVector3f vOmega = mpPushBody->GetAngularVelocity();
// Set speed to 0
if (ABS(mfYRotation) < 0.001f || mbRotateWithPlayer == false) {
cVector3f vTorque = mRotatePid.Output(cVector3f(0, 0, 0) - vOmega, afTimeStep);
vTorque = vTorque / 5.0f;
mpPushBody->AddTorque(vTorque * powf(mpPushBody->GetMass(), 1.8f));
// vTorque = cMath::MatrixMul(mpPushBody->GetInertiaMatrix(),vTorque);
// mpPushBody->AddTorque(vTorque);
} else {
float fWantedSpeed = mfYRotation * 6;
cVector3f vTorque = mRotatePid.Output(cVector3f(0, fWantedSpeed, 0) - vOmega, afTimeStep);
vTorque = vTorque / 5.0f;
mpPushBody->AddTorque(vTorque * powf(mpPushBody->GetMass(), 1.8f));
// vTorque = cMath::MatrixMul(mpPushBody->GetInertiaMatrix(),vTorque);
// mpPushBody->AddTorque(vTorque);
mfYRotation -= vOmega.y * afTimeStep;
}
// the above magic numbers 1.8f and 5.0f are a result of changes in the physics library.
// All credits for discovery and testing go to https://github.com/zenmumbler
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnDraw() {
// mpFont->Draw(cVector3f(5,30,0),12,cColor(1,1),eFontAlign_Left,"YRotate: %f",mfYRotation);
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnPostSceneDraw() {
/*iLowLevelGraphics *pLowGfx = */ mpInit->mpGame->GetGraphics()->GetLowLevel();
/*cVector3f vPickPoint = */ cMath::MatrixMul(mpPushBody->GetWorldMatrix(), mvRelPickPoint);
// pLowGfx->DrawSphere(vPickPoint, 0.2f, cColor(1,1,1,1));
// pLowGfx->DrawSphere(mvCurrentDisered, 0.2f, cColor(1,1,1,1));
}
//-----------------------------------------------------------------------
bool cPlayerState_Grab::OnJump() {
return true;
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnStartInteractMode() {
mbMoveHand = !mbMoveHand;
if (mbMoveHand) {
mPrevState = ePlayerState_InteractMode;
} else {
mPrevState = ePlayerState_Normal;
mpPlayer->ResetCrossHairPos();
}
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnStartInteract() {
}
void cPlayerState_Grab::OnStopInteract() {
mpPlayer->ChangeState(mPrevState);
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnStartExamine() {
mpPlayer->ChangeState(mPrevState);
if (mpPlayer->mbCanBeThrown == false)
return;
// Reset linear and angular speed.
mpPushBody->SetLinearVelocity(0);
mpPushBody->SetAngularVelocity(0);
// Get the forward vector
cVector3f vForward = mpPlayer->GetCamera()->UnProject(mpPlayer->GetCrossHairPos(),
mpInit->mpGame->GetGraphics()->GetLowLevel());
// Get the point which you hare grabbing at.
cVector3f vPickPoint = cMath::MatrixMul(mpPushBody->GetLocalMatrix(), mvRelPickPoint);
// Calculate the impulse
float fT = 0;
float fMass = mpPushBody->GetMass();
if (fMass <= mfMinThrowMass)
fT = 1;
else if (fMass >= mfMaxThrowMass)
fT = 0;
else
fT = 1 - (fMass - mfMinThrowMass) / (mfMaxThrowMass - mfMinThrowMass);
float fImpulse = mfMinThrowImpulse * (1 - fT) + mfMaxThrowImpulse * fT;
// Log("Mass: %f Impulse: %f\n",fMass,fImpulse);
// Add the impulse
if (mbPickAtPoint) {
mpPushBody->AddImpulseAtPosition(vForward * fImpulse, vPickPoint);
} else {
mpPushBody->AddImpulse(vForward * fImpulse);
}
}
//-----------------------------------------------------------------------
bool cPlayerState_Grab::OnAddYaw(float afVal) {
afVal *= mfSpeedMul * 0.75f;
if (mbMoveHand) {
if (mpPlayer->AddCrossHairPos(cVector2f(afVal * 800.0f, 0))) {
mpPlayer->GetCamera()->AddYaw(-afVal * mpPlayer->GetLookSpeed());
mpPlayer->GetCharacterBody()->SetYaw(mpPlayer->GetCamera()->GetYaw());
mfYRotation += -afVal * mpPlayer->GetLookSpeed();
}
return false;
} else {
mpPlayer->GetCamera()->AddYaw(-afVal * mpPlayer->GetLookSpeed());
mpPlayer->GetCharacterBody()->SetYaw(mpPlayer->GetCamera()->GetYaw());
mfYRotation += -afVal * mpPlayer->GetLookSpeed();
return false;
}
}
bool cPlayerState_Grab::OnAddPitch(float afVal) {
float fInvert = 1;
if (mpInit->mpButtonHandler->GetInvertMouseY())
fInvert = -1;
afVal *= mfSpeedMul * 0.75f;
if (mbMoveHand) {
if (mpPlayer->AddCrossHairPos(cVector2f(0, afVal * 600.0f))) {
mpPlayer->GetCamera()->AddPitch(-afVal * mpPlayer->GetLookSpeed());
}
return false;
} else {
mpPlayer->GetCamera()->AddPitch(-afVal * fInvert * mpPlayer->GetLookSpeed());
return false;
}
}
//-----------------------------------------------------------------------
bool cPlayerState_Grab::OnMoveForwards(float afMul, float afTimeStep) {
return true;
}
//-----------------------------------------------------------------------
bool cPlayerState_Grab::OnMoveSideways(float afMul, float afTimeStep) {
return true;
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::EnterState(iPlayerState *apPrevState) {
// Detach the body if stuck to a sticky area
cGameStickArea *pStickArea = mpInit->mpMapHandler->GetBodyStickArea(mpPlayer->GetPushBody());
if (pStickArea) {
if (pStickArea->GetCanDeatch()) {
pStickArea->DetachBody();
} else {
mpPlayer->ChangeState(apPrevState->mType);
return;
}
}
cCamera3D *pCamera = mpPlayer->GetCamera();
// Make sure the player is not running
if (mpPlayer->GetMoveState() == ePlayerMoveState_Run || mPrevMoveState == ePlayerMoveState_Jump)
mpPlayer->ChangeMoveState(ePlayerMoveState_Walk);
// Get last state, if this is a message use the last previous state instead.
if (apPrevState->mType != ePlayerState_Message)
mPrevState = apPrevState->mType;
mbPickAtPoint = mpPlayer->mbPickAtPoint;
mbRotateWithPlayer = mpPlayer->mbRotateWithPlayer;
if (mPrevState == ePlayerState_InteractMode)
mbMoveHand = true;
else
mbMoveHand = false;
// Get the body to push
mpPushBody = mpPlayer->GetPushBody();
mbHasGravity = mpPushBody->GetGravity();
if (mbPickAtPoint == false)
mpPushBody->SetGravity(false);
mpPushBody->SetAutoDisable(false);
mbHasPlayerGravityPush = mpPushBody->GetPushedByCharacterGravity();
mpPushBody->SetPushedByCharacterGravity(true);
// Set a newer player mass
mpPlayer->SetMass(mpPlayer->GetMass() + mpPushBody->GetMass());
// Lower the mass while holding
mfDefaultMass = mpPushBody->GetMass();
mpPushBody->SetMass(mpPushBody->GetMass() / mfMassDiv);
// Set the player speed mul
float fMass = mpPushBody->GetMass();
mfSpeedMul = 1;
if (fMass > 3) {
float fMul = 1 - ((fMass - 3.0f) / 37.0f) * 0.34f;
fMul = cMath::Min(fMul, 0.66f);
mfSpeedMul = fMul;
}
mpPlayer->SetSpeedMul(mfSpeedMul);
// If we want to use the normal mass, reset the division
if (mpPlayer->mbUseNormalMass)
mpPushBody->SetMass(mfDefaultMass);
// Get the orientation of the body
cMatrixf mtxInvModel = cMath::MatrixInverse(mpPushBody->GetLocalMatrix());
mvObjectUp = mtxInvModel.GetUp();
mvObjectRight = mtxInvModel.GetRight();
mfGrabDist = cMath::Vector3Dist(mpPlayer->GetCamera()->GetPosition(),
mpPlayer->GetPickedPos());
// mfGrabDist = mpPlayer->GetPickedDist();
// reset PID controller
mGrabPid.Reset();
// The pick point relative to the body
if (mbPickAtPoint)
mvRelPickPoint = cMath::MatrixMul(mtxInvModel, mpPlayer->GetPickedPos());
else
mvRelPickPoint = mpPlayer->GetPickedPos() -
cMath::MatrixMul(mpPushBody->GetWorldMatrix(),
mpPushBody->GetMassCentre());
// Set cross hair image.
mpPlayer->SetCrossHairState(eCrossHairState_Grab);
// The amount of rotation we wanna apply to the object, increases/decrease
// when the player turn left/right
mfYRotation = 0;
// Reset pid controllers
mRotatePid.Reset();
mGrabPid.Reset();
mfStartYaw = pCamera->GetYaw();
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::LeaveState(iPlayerState *apNextState) {
mpPushBody->SetPushedByCharacterGravity(mbHasPlayerGravityPush);
mpPushBody->SetGravity(mbHasGravity);
mpPushBody->SetActive(true);
mpPushBody->SetAutoDisable(true);
mpPushBody->AddForce(cVector3f(0, 1, 0) * mpPushBody->GetMass());
if (mpPlayer->mbUseNormalMass == false)
mpPushBody->SetMass(mfDefaultMass);
// Reset newer player mass
mpPlayer->SetMass(mpPlayer->GetDefaultMass());
mpPlayer->SetSpeedMul(1.0f);
}
//-----------------------------------------------------------------------
void cPlayerState_Grab::OnStartCrouch() {
if (mpPlayer->GetMoveState() == ePlayerMoveState_Jump)
return;
if (mpInit->mpButtonHandler->GetToggleCrouch()) {
if (mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
mpPlayer->ChangeMoveState(ePlayerMoveState_Walk);
else
mpPlayer->ChangeMoveState(ePlayerMoveState_Crouch);
} else {
mpPlayer->ChangeMoveState(ePlayerMoveState_Crouch);
}
}
void cPlayerState_Grab::OnStopCrouch() {
if (mpPlayer->GetMoveState() == ePlayerMoveState_Crouch &&
mpInit->mpButtonHandler->GetToggleCrouch() == false) {
mpPlayer->ChangeMoveState(ePlayerMoveState_Walk);
}
}
//-----------------------------------------------------------------------
bool cPlayerState_Grab::OnStartInventory() {
return false;
}
//-----------------------------------------------------------------------
bool cPlayerState_Grab::OnStartInventoryShortCut(int alNum) {
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// MOVE STATE
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cPlayerState_Move_BodyCallback::cPlayerState_Move_BodyCallback(cPlayer *apPlayer, float afTimeStep) {
mpPlayer = apPlayer;
mfTimeStep = afTimeStep;
mlBackCount = 0;
}
bool cPlayerState_Move_BodyCallback::OnBeginCollision(iPhysicsBody *apBody, iPhysicsBody *apCollideBody) {
return true;
}
void cPlayerState_Move_BodyCallback::OnCollide(iPhysicsBody *apBody, iPhysicsBody *apCollideBody, cPhysicsContactData *apContactData) {
if (mpPlayer->GetCharacterBody()->GetBody() == apCollideBody) {
mlBackCount = 5;
}
}
//-----------------------------------------------------------------------
cPlayerState_Move::cPlayerState_Move(cInit *apInit, cPlayer *apPlayer) : iPlayerState(apInit, apPlayer, ePlayerState_Move) {
mpPushBody = NULL;
mpCallback = hplNew(cPlayerState_Move_BodyCallback, (apPlayer, apInit->mpGame->GetStepSize()));
}
cPlayerState_Move::~cPlayerState_Move() {
hplDelete(mpCallback);
}
//-----------------------------------------------------------------------
void cPlayerState_Move::OnUpdate(float afTimeStep) {
//////////////////////////////////////
// Check if the player is supposed to back
if (mpCallback->mlBackCount > 0) {
mpCallback->mlBackCount--;
mpPlayer->GetCharacterBody()->Move(eCharDir_Forward, -1, afTimeStep);
}
//////////////////////////////////////
// Calculate the pick position
mvPickPoint = cMath::MatrixMul(mpPushBody->GetLocalMatrix(), mvRelPickPoint);
//////////////////////////////////////
// Check if player is close enough
cVector3f vEnd = mvPickPoint;
cVector3f vStart = mpPlayer->GetCamera()->GetPosition();
float fDistance = cMath::Vector3Dist(vStart, vEnd);
if (fDistance > mpPlayer->mfCurrentMaxInteractDist * 1.7f) {
// Log("Out of here %f!\n",mpPlayer->mfCurrentMaxInteractDist);
mpPlayer->ChangeState(mPrevState);
return;
}
/////////////////////////////////////////
// Check if the body should be stopped
if (mlMoveCount <= 0) {
mpPushBody->SetAngularVelocity(0);
mpPushBody->SetLinearVelocity(0);
}
///////////////////////////////////////////////////////////////////////
// Project the position of the pick point and set is as crosshair pos
cVector3f vProjPos = cMath::MatrixMul(mpPlayer->GetCamera()->GetViewMatrix(), mvPickPoint);
vProjPos = cMath::MatrixMulDivideW(mpPlayer->GetCamera()->GetProjectionMatrix(), vProjPos);
mpPlayer->SetCrossHairPos(cVector2f((vProjPos.x + 1) * 0.5f * 800.0f, ((-vProjPos.y) + 1) * 0.5f * 600.0f));
/////////////////////////////////////////////////
// Check if the camera should be turned
cVector2f vCrossPos = mpPlayer->GetCrossHairPos();
cVector2f vBorder = mpPlayer->GetInteractMoveBorder();
if (vCrossPos.x < vBorder.x)
mpPlayer->GetCamera()->AddYaw((vBorder.x - vCrossPos.x) / 800 * mpPlayer->GetLookSpeed());
if (vCrossPos.x > (799 - vBorder.x))
mpPlayer->GetCamera()->AddYaw(-(vCrossPos.x - (799 - vBorder.x)) / 800 * mpPlayer->GetLookSpeed());
mpPlayer->GetCharacterBody()->SetYaw(mpPlayer->GetCamera()->GetYaw());
if (vCrossPos.y < vBorder.y)
mpPlayer->GetCamera()->AddPitch((vBorder.y - vCrossPos.y) / 600 * mpPlayer->GetLookSpeed());
if (vCrossPos.y > (599 - vBorder.y))
mpPlayer->GetCamera()->AddPitch(-(vCrossPos.y - (599 - vBorder.y)) / 600 * mpPlayer->GetLookSpeed());
}
//-----------------------------------------------------------------------
void cPlayerState_Move::OnStartInteract() {
// mpPlayer->ChangeState(ePlayerState_Normal);
}
void cPlayerState_Move::OnStopInteract() {
mpPlayer->ChangeState(mPrevState);
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnJump() {
return false;
}
//-----------------------------------------------------------------------
void cPlayerState_Move::OnStartExamine() {
if (mpPlayer->mbCanBeThrown) {
cVector3f vForward = mpPlayer->GetCamera()->UnProject(mpPlayer->GetCrossHairPos(),
mpInit->mpGame->GetGraphics()->GetLowLevel());
float fMassMul = mpPushBody->GetMass();
if (fMassMul > 3)
fMassMul = 3;
mpPushBody->AddForce(vForward * 500 * fMassMul);
}
mpPlayer->ChangeState(mPrevState);
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnMoveForwards(float afMul, float afTimeStep) {
cVector3f vForce = (mvForward * (afMul * 4.0f));
mpPushBody->AddForceAtPosition(vForce, mvPickPoint);
mlMoveCount = 20;
return true;
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnMoveSideways(float afMul, float afTimeStep) {
cVector3f vForce = (mvRight * (afMul * 4.0f));
mpPushBody->AddForceAtPosition(vForce, mvPickPoint);
mlMoveCount = 20;
return true;
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnAddYaw(float afVal) {
if (ABS(afVal) > kEpsilonf) {
cVector3f vForce = (mvRight * (afVal * 100.0f * mpPlayer->mfRightMul));
mpPushBody->AddForceAtPosition(vForce, mvPickPoint);
mlMoveCount = 20;
} else {
if (mlMoveCount > 0)
mlMoveCount--;
}
return false;
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnAddPitch(float afVal) {
if (ABS(afVal) > kEpsilonf) {
cVector3f vForce = (mvUp * (-afVal * 100.0f * mpPlayer->mfUpMul)) +
(mvForward * (afVal * -80.0f * mpPlayer->mfForwardUpMul));
mpPushBody->AddForceAtPosition(vForce, mvPickPoint);
mlMoveCount = 20;
} else {
if (mlMoveCount > 0)
mlMoveCount--;
}
return false;
}
//-----------------------------------------------------------------------
void cPlayerState_Move::EnterState(iPlayerState *apPrevState) {
// Detach the body if stuck to a sticky area
cGameStickArea *pStickArea = mpInit->mpMapHandler->GetBodyStickArea(mpPlayer->GetPushBody());
if (pStickArea && pStickArea->GetCanDeatch()) {
if (pStickArea->GetCanDeatch()) {
pStickArea->DetachBody();
} else {
mpPlayer->ChangeState(apPrevState->mType);
return;
}
}
cCamera3D *pCamera = mpPlayer->GetCamera();
// Change move state so the player is still
mPrevMoveState = mpPlayer->GetMoveState();
// mpPlayer->ChangeMoveState(ePlayerMoveState_Still);
mpPlayer->SetSpeedMul(0.3f);
mpPlayer->SetHeadMoveSizeMul(0.2f);
mpPlayer->SetHeadMoveSpeedMul(0.2f);
// Get last state, if this is a message use the last previous state instead.
if (apPrevState->mType != ePlayerState_Message)
mPrevState = apPrevState->mType;
// Set the directions to move the body in
mvForward = pCamera->GetForward();
mvRight = pCamera->GetRight();
mvUp = pCamera->GetUp();
// make forward non y dependant
mvForward.y = 0;
mvForward.Normalise();
// Get the body to push
mpPushBody = mpPlayer->GetPushBody();
mpPushBody->SetAutoDisable(false);
// The pick point relative to the body
mvPickPoint = mpPlayer->GetPickedPos();
/////////////////////////////////////////
// Check if all controllers should be paused.
iGameEntity *pEntity = (iGameEntity *)mpPushBody->GetUserData();
if (pEntity->GetPauseControllers()) {
for (int i = 0; i < mpPushBody->GetJointNum(); ++i) {
mpPushBody->GetJoint(i)->SetAllControllersPaused(true);
}
}
/////////////////////////////////////////
// Check if gravtty should be paused.
if (mpPushBody->GetGravity() && pEntity->GetPauseGravity()) {
mpPushBody->SetGravity(false);
bPausedGravity = true;
} else {
bPausedGravity = false;
}
cMatrixf mtxInvModel = cMath::MatrixInverse(mpPushBody->GetLocalMatrix());
mvRelPickPoint = cMath::MatrixMul(mtxInvModel, mvPickPoint);
// Set cross hair image.
mpPlayer->SetCrossHairState(eCrossHairState_Grab);
// Add callback to body if needed
/*if(mpPushBody->GetCollideCharacter())
{
mpPushBody->AddBodyCallback(mpCallback);
}*/
mpCallback->mlBackCount = 0;
mlMoveCount = 0;
}
//-----------------------------------------------------------------------
void cPlayerState_Move::LeaveState(iPlayerState *apNextState) {
// Remove callback to body if needed
/*if(mpPushBody->GetCollideCharacter())
{
mpPushBody->RemoveBodyCallback(mpCallback);
}*/
////////////////////////////
// Pause controllers
iGameEntity *pObject = (iGameEntity *)mpPushBody->GetUserData();
if (pObject->GetPauseControllers()) {
for (int i = 0; i < mpPushBody->GetJointNum(); ++i) {
mpPushBody->GetJoint(i)->SetAllControllersPaused(false);
}
}
////////////////////////////
// Pause gravity
if (bPausedGravity) {
mpPushBody->SetGravity(true);
}
mpPushBody->SetAutoDisable(true);
if (mPrevMoveState != ePlayerMoveState_Run && mPrevMoveState != ePlayerMoveState_Jump)
mpPlayer->ChangeMoveState(mPrevMoveState);
else
mpPlayer->ChangeMoveState(ePlayerMoveState_Walk);
mpPlayer->SetSpeedMul(1.0f);
mpPlayer->SetHeadMoveSizeMul(1.0f);
mpPlayer->SetHeadMoveSpeedMul(1.0f);
if (mPrevState == ePlayerState_Normal)
mpPlayer->ResetCrossHairPos();
}
//-----------------------------------------------------------------------
void cPlayerState_Move::OnPostSceneDraw() {
// mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawSphere(mvPickPoint,0.1f,cColor(1,1,1,1));
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnStartInventory() {
return false;
}
//-----------------------------------------------------------------------
bool cPlayerState_Move::OnStartInventoryShortCut(int alNum) {
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUSH STATE
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cPlayerState_Push::cPlayerState_Push(cInit *apInit, cPlayer *apPlayer) : iPlayerState(apInit, apPlayer, ePlayerState_Push) {
mpPushBody = NULL;
}
//-----------------------------------------------------------------------
void cPlayerState_Push::OnUpdate(float afTimeStep) {
//////////////////////////////////////
// Check if player is close enough
cVector3f vEnd = mpPushBody->GetLocalPosition() + mvRelPickPoint;
cVector3f vStart = mpPlayer->GetCamera()->GetPosition();
float fDistance = cMath::Vector3Dist(vStart, vEnd);
if (fDistance > mpPlayer->mfCurrentMaxInteractDist * 1.2f) {
mpPlayer->ChangeState(mPrevState);
return;
}
//////////////////////////////////////
// Update player movement
cVector3f vPosAdd = mpPushBody->GetLocalPosition() - mvLastBodyPos;
// No need for the y value.
vPosAdd.y = 0;
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
cVector3f vPlayerPos = mpPlayer->GetCharacterBody()->GetPosition() + vPosAdd;
// mpPlayer->GetCharacterBody()->SetPosition(vPlayerPos,true);
// Not really needed. character body should fix.
cVector3f vNewPos = vPlayerPos;
iPhysicsBody *pBody = mpPlayer->GetCharacterBody()->GetBody();
pPhysicsWorld->CheckShapeWorldCollision(&vNewPos, pBody->GetShape(),
cMath::MatrixTranslate(vPlayerPos),
pBody, false, true, NULL, true);
mpPlayer->GetCharacterBody()->SetPosition(vNewPos, true);
mvLastBodyPos = mpPushBody->GetLocalPosition();
}
//-----------------------------------------------------------------------
bool cPlayerState_Push::OnJump() {
return false;
}
//-----------------------------------------------------------------------
void cPlayerState_Push::OnStartInteract() {
// mpPlayer->ChangeState(mPrevState);
}
void cPlayerState_Push::OnStopInteract() {
mpPlayer->ChangeState(mPrevState);
}
//-----------------------------------------------------------------------
void cPlayerState_Push::OnStartExamine() {
mpPlayer->ChangeState(mPrevState);
if (mpPlayer->mbCanBeThrown) {
float fMassMul = mpPushBody->GetMass();
if (fMassMul > 40)
fMassMul = 40;
mpPushBody->AddForce(mvForward * 233 * fMassMul);
}
}
//-----------------------------------------------------------------------
bool cPlayerState_Push::OnMoveForwards(float afMul, float afTimeStep) {
if (afMul < 0) {
if (mpPlayer->mbCanBePulled == false)
return false;
afMul *= 0.7f;
}
float fSpeed = mpPushBody->GetLinearVelocity().Length();
///////////////////////////////////
// Set the direction and if it is newer add extra force
if (afMul > 0) {
if (mlForward != 1 && fSpeed < 0.01f)
afMul *= 0.6f * mpPushBody->GetMass();
mlForward = 1;
} else if (afMul < 0) {
// If player is to close, push him back.
if (mlForward != -1) {
float fPosAdd = (mfMaxSpeed)*afTimeStep;
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
iCharacterBody *pPlayerBody = mpPlayer->GetCharacterBody();
cMatrixf mtxBodyMove = mpPushBody->GetLocalMatrix();
mtxBodyMove.SetTranslation(mtxBodyMove.GetTranslation() + (mvForward * -1 * fPosAdd));
cCollideData collData;
collData.SetMaxSize(32);
bool bCollide = pPhysicsWorld->CheckShapeCollision(
pPlayerBody->GetShape(), cMath::MatrixTranslate(pPlayerBody->GetPosition()),
mpPushBody->GetShape(), mtxBodyMove, collData, 32);
if (bCollide) {
cVector3f vPos = mpPlayer->GetCharacterBody()->GetPosition();
// cVector3f vOldPos = vPos;
// cVector3f vNewPos;
vPos += mvForward * -1 * (fPosAdd + 0.1f);
mpPlayer->GetCharacterBody()->SetPosition(vPos);
// Not good since slopes does not work:
/*pPhysicsWorld->CheckShapeWorldCollision(&vNewPos, pPlayerBody->GetShape(),
cMath::MatrixTranslate(pPlayerBody->GetPosition()),
mpPushBody,false,true,NULL,false);
if(vNewPos.x != vPos.x || vNewPos.y != vPos.y)
{
mpPlayer->GetCharacterBody()->SetPosition(vOldPos);
}*/
}
}
if (mlForward != -1 && fSpeed < 0.01f)
afMul *= 1.2f * mpPushBody->GetMass();
mlForward = -1;
} else {
mlForward = 0;
return false;
}
// Log("Mul: %f\n",afMul);
/////////////////////////////////////
// If the velocity is not to high add force.
if (fSpeed < mfMaxSpeed) {
if (mpPlayer->mbPickAtPoint) {
// NON WORKING ATM:
/*cVector3f vForce = mvForward * afMul*100.0f*0.5f;
cVector3f vPos = cMath::MatrixMul( mpPushBody->GetLocalMatrix(),
mvLocalPickPoint);
vPos.y = mpPushBody->GetLocalPosition().y;
mpPushBody->AddForceAtPosition(vForce,vPos);*/
} else {
cVector3f vForce = mvForward * afMul * 100.0f; //*0.5f;
// mpPushBody->AddForceAtPosition(vForce,mpPushBody->GetLocalPosition() + mvRight*0.4f);
// mpPushBody->AddForceAtPosition(vForce,mpPushBody->GetLocalPosition() + mvRight*-0.4f);
mpPushBody->AddForce(vForce);
}
}
// returning true calls move state as well.
return true;
}
//-----------------------------------------------------------------------
bool cPlayerState_Push::OnMoveSideways(float afMul, float afTimeStep) {
cVector3f vVel = mpPushBody->GetLinearVelocity();
if (vVel.Length() < mfMaxSpeed) {
// mpPushBody->AddForce(mvRight * afMul*100.0f);
cVector3f vForce = mvRight * afMul * 100.0f; //*0.5f;
// mpPushBody->AddForceAtPosition(vForce,mpPushBody->GetLocalPosition() + mvForward*0.4f);
// mpPushBody->AddForceAtPosition(vForce,mpPushBody->GetLocalPosition() + mvForward*-0.4f);
mpPushBody->AddForce(vForce);
}
// returning true calls move state as well.
return true;
}
//-----------------------------------------------------------------------
void cPlayerState_Push::EnterState(iPlayerState *apPrevState) {
// Detach the body if stuck to a sticky area
cGameStickArea *pStickArea = mpInit->mpMapHandler->GetBodyStickArea(mpPlayer->GetPushBody());
if (pStickArea && pStickArea->GetCanDeatch()) {
if (pStickArea->GetCanDeatch()) {
pStickArea->DetachBody();
} else {
mpPlayer->ChangeState(apPrevState->mType);
return;
}
}
cCamera3D *pCamera = mpPlayer->GetCamera();
mfMaxSpeed = mpPlayer->GetMaxPushSpeed();
if (mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
mfMaxSpeed *= 0.5f;
// Change move state so the player is still
mPrevMoveState = mpPlayer->GetMoveState();
mpPlayer->ChangeMoveState(ePlayerMoveState_Still);
// Get last state, if this is a message use the last previous state instead.
if (apPrevState->mType != ePlayerState_Message)
mPrevState = apPrevState->mType;
// Set the directions to move the body in
mvForward = pCamera->GetForward();
mvForward.y = 0;
mvForward.Normalise();
mvRight = pCamera->GetRight();
mvRight.y = 0;
mvRight.Normalise();
// Get the body to push
mpPushBody = mpPlayer->GetPushBody();
// All pushed bodies shall be affected by player gravity.
mbHasPlayerGravityPush = mpPushBody->GetPushedByCharacterGravity();
mpPushBody->SetPushedByCharacterGravity(true);
// The pick point relative to the body
mvRelPickPoint = mpPlayer->GetPickedPos() - mpPushBody->GetLocalPosition();
// Set the last position.
mvLastBodyPos = mpPushBody->GetLocalPosition();
// Set cross hair image.
mpPlayer->SetCrossHairState(eCrossHairState_Grab);
// Set newer yaw and pitch limits
mvPrevPitchLimits = pCamera->GetPitchLimits();
cVector2f vMaxHeadLimits = mpPlayer->GetMaxPushHeadMovement();
cVector2f vMinHeadLimits = mpPlayer->GetMinPushHeadMovement();
float fXmax = pCamera->GetYaw() + vMaxHeadLimits.x;
float fYmax = pCamera->GetPitch() + vMaxHeadLimits.y;
float fXmin = pCamera->GetYaw() + vMinHeadLimits.x;
float fYmin = pCamera->GetPitch() + vMinHeadLimits.y;
pCamera->SetPitchLimits(cVector2f(fYmax, fYmin));
pCamera->SetYawLimits(cVector2f(fXmax, fXmin));
// Create a little effect when grabbing the body
mpPushBody->SetAutoDisable(false);
// mpPushBody->AddForce(cVector3f(0,-1,0) *60.0f *mpPushBody->GetMass());
// This is to check the last used direction
mlForward = 0;
mlSideways = 0;
cMatrixf mtxInvWorld = cMath::MatrixInverse(mpPushBody->GetLocalMatrix());
mvLocalPickPoint = cMath::MatrixMul(mtxInvWorld, mpPlayer->GetPickedPos());
}
//-----------------------------------------------------------------------
void cPlayerState_Push::LeaveState(iPlayerState *apNextState) {
mpPushBody->SetPushedByCharacterGravity(mbHasPlayerGravityPush);
if (mPrevMoveState != ePlayerMoveState_Run && mPrevMoveState != ePlayerMoveState_Jump)
mpPlayer->ChangeMoveState(mPrevMoveState);
else
mpPlayer->ChangeMoveState(ePlayerMoveState_Walk);
mpPlayer->GetCamera()->SetPitchLimits(mvPrevPitchLimits);
mpPlayer->GetCamera()->SetYawLimits(cVector2f(0, 0));
// Create a little effect when letting go of the body
mpPushBody->SetAutoDisable(true);
// mpPushBody->AddForce(cVector3f(0,-1,0) *60.0f *mpPushBody->GetMass());
}
//-----------------------------------------------------------------------
void cPlayerState_Push::OnPostSceneDraw() {
return;
cVector3f vPos = cMath::MatrixMul(mpPushBody->GetLocalMatrix(),
mvLocalPickPoint);
mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawSphere(vPos, 0.3f, cColor(1, 0, 1));
}
//-----------------------------------------------------------------------
bool cPlayerState_Push::OnStartInventory() {
return false;
}
//-----------------------------------------------------------------------
bool cPlayerState_Push::OnStartInventoryShortCut(int alNum) {
return false;
}