/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of Penumbra Overture.
*
* Penumbra Overture 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.
*
* Penumbra Overture 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 Penumbra Overture. If not, see .
*/
#include "StdAfx.h"
#include "GameStickArea.h"
#include "Init.h"
#include "MapHandler.h"
#include "Player.h"
#include "PlayerState.h"
#include "GlobalInit.h"
//////////////////////////////////////////////////////////////////////////
// LOADER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cAreaLoader_GameStickArea::cAreaLoader_GameStickArea(const tString &asName, cInit *apInit)
: iArea3DLoader(asName)
{
mpInit = apInit;
}
cAreaLoader_GameStickArea::~cAreaLoader_GameStickArea()
{
}
//-----------------------------------------------------------------------
iEntity3D* cAreaLoader_GameStickArea::Load(const tString &asName, const cVector3f &avSize,
const cMatrixf &a_mtxTransform,cWorld3D *apWorld)
{
cGameStickArea *pArea = hplNew( cGameStickArea, (mpInit,asName) );
pArea->m_mtxOnLoadTransform = a_mtxTransform;
//Create physics data
iPhysicsWorld *pPhysicsWorld = apWorld->GetPhysicsWorld();
iCollideShape* pShape = pPhysicsWorld->CreateBoxShape(avSize,NULL);
std::vector vBodies;
vBodies.push_back(pPhysicsWorld->CreateBody(asName,pShape));
vBodies[0]->SetCollide(false);
vBodies[0]->SetCollideCharacter(false);
vBodies[0]->SetMatrix(a_mtxTransform);
vBodies[0]->SetUserData(pArea);
pArea->SetBodies(vBodies);
mpInit->mpMapHandler->AddGameEntity(pArea);
mpInit->mpMapHandler->AddStickArea(pArea);
//Return something else later perhaps.
return NULL;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
bool cGameStickArea::mbAllowAttachment = false;
//-----------------------------------------------------------------------
cGameStickArea::cGameStickArea(cInit *apInit,const tString& asName) : iGameEntity(apInit,asName)
{
mType = eGameEntityType_StickArea;
mpAttachedBody = NULL;
mpLastAttachedBody = NULL;
msAttachFunction = "";
msDetachFunction = "";
msAttachSound = "";
msDetachFunction = "";
msAttachSound = "";
msDetachSound = "";
mbMoveBody = true;
mbRotateBody = true;
mbCheckCenterInArea = true;
mfPoseTime = 0.2f;
mbCanDeatch = true;
mfSetMtxTime = 1.0f;
}
//-----------------------------------------------------------------------
cGameStickArea::~cGameStickArea(void)
{
mpInit->mpMapHandler->RemoveStickArea(this);
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cGameStickArea::OnPlayerPick()
{
}
//-----------------------------------------------------------------------
void cGameStickArea::Update(float afTimeStep)
{
iPhysicsBody *pAreaBody = mvBodies[0];
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
if(mpAttachedBody && mfSetMtxTime < 1)
{
if(mbMoveBody && mbRotateBody)
{
if(mfPoseTime==0) mfSetMtxTime = 1.0f;
else mfSetMtxTime += afTimeStep / mfPoseTime;
cMatrixf mtxGoal = pAreaBody->GetWorldMatrix();
mtxGoal.SetTranslation(pAreaBody->GetWorldPosition() - mpAttachedBody->GetMassCentre());
cMatrixf mtxNew = cMath::MatrixSlerp(mfSetMtxTime,mtxAttachedStart,mtxGoal,true);
mpAttachedBody->SetMatrix(mtxNew);
}
else if(mbMoveBody && !mbRotateBody)
{
if(mfPoseTime==0) mfSetMtxTime = 1.0f;
else mfSetMtxTime += afTimeStep / mfPoseTime;
cVector3f vGoal = pAreaBody->GetWorldPosition() - mpAttachedBody->GetMassCentre();
cVector3f vNew = mtxAttachedStart.GetTranslation()* (1-mfSetMtxTime) +
vGoal *mfSetMtxTime;
mpAttachedBody->SetPosition(vNew);
}
else if(!mbMoveBody && mbRotateBody)
{
if(mfPoseTime==0) mfSetMtxTime = 1.0f;
else mfSetMtxTime += afTimeStep / mfPoseTime;
cMatrixf mtxGoal = pAreaBody->GetWorldMatrix();
cMatrixf mtxNew = cMath::MatrixSlerp(mfSetMtxTime,mtxAttachedStart,mtxGoal,true);
mtxNew.SetTranslation(mpAttachedBody->GetWorldPosition());
mpAttachedBody->SetMatrix(mtxNew);
}
else
{
mfSetMtxTime = 1.0f;
}
if(mfSetMtxTime >= 1)
{
//Sound
if(msAttachSound!="")
{
cSoundEntity *pSound = pWorld->CreateSoundEntity("AttachSound",msAttachSound,true);
if(pSound) pSound->SetPosition(pAreaBody->GetWorldPosition());
}
//Particle System
if(msAttachPS!="")
{
pWorld->CreateParticleSystem("AttachPS",msAttachPS,1,pAreaBody->GetWorldMatrix());
}
}
}
if(mpAttachedBody) return;
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
iPhysicsBody *pAttachBody = NULL;
////////////////////////////////////////////////////////
//Iterate all bodies in world and check for intersection
cPhysicsBodyIterator bodyIt = pPhysicsWorld->GetBodyIterator();
while(bodyIt.HasNext())
{
iPhysicsBody *pBody = bodyIt.Next();
/*if( mpInit->mpPlayer->GetPickedBody() == pBody &&
( mpInit->mpPlayer->GetState() == ePlayerState_Grab ||
mpInit->mpPlayer->GetState() == ePlayerState_Move ||
mpInit->mpPlayer->GetState() == ePlayerState_Push)
)
{
continue;
}*/
if( pBody->IsActive() && pBody->GetCollide() && pBody->GetMass()>0 &&
pBody->IsCharacter()==false)
{
//Bounding volume check
//if(cMath::CheckCollisionBV(*pBody->GetBV(), *pAreaBody->GetBV())==false) continue;
bool bCheck = false;
if(mbCheckCenterInArea)
{
cVector3f vPos = pBody->GetLocalPosition() +
cMath::MatrixMul( pBody->GetLocalMatrix().GetRotation(),
pBody->GetMassCentre());
bCheck = cMath::PointBVCollision(vPos,*pAreaBody->GetBV());
}
else
{
bCheck = cMath::CheckCollisionBV(*pBody->GetBV(),*pAreaBody->GetBV());
}
if(bCheck==false)
{
if(pBody == mpLastAttachedBody)
{
mpLastAttachedBody = NULL;
}
continue;
}
if(pBody == mpLastAttachedBody) continue;
//Shape collision check.
cCollideData collideData;
collideData.SetMaxSize(1);
bool bCollide = pPhysicsWorld->CheckShapeCollision(pAreaBody->GetShape(),
pAreaBody->GetLocalMatrix(),
pBody->GetShape(),
pBody->GetLocalMatrix(),
collideData,1);
if(bCollide)
{
if(msAttachFunction!="")
{
mbAllowAttachment = false;
tString sCommand = GetCallbackFunc(msAttachFunction,pBody);
mpInit->RunScriptCommand(sCommand);
if(mbAllowAttachment==false) continue;
}
pAttachBody = pBody;
}
}
}
////////////////////////////////////////////////////////
// Do some stuff with the body to be attached.
if(pAttachBody)
{
//Log("Attaching body %s\n", pAttachBody->GetName().c_str());
/////////////////////////
//If in an interact state, set the previous state
if( mpInit->mpPlayer->GetPushBody() == pAttachBody &&
( mpInit->mpPlayer->GetState() == ePlayerState_Grab ||
mpInit->mpPlayer->GetState() == ePlayerState_Move ||
mpInit->mpPlayer->GetState() == ePlayerState_Push)
)
{
//Log(" Setting a prev state\n");
ePlayerState state = mpInit->mpPlayer->GetState();
ePlayerState prev = mpInit->mpPlayer->GetStateData(state)->mPreviuosState;
if(prev == ePlayerState_InteractMode)
mpInit->mpPlayer->ChangeState(ePlayerState_InteractMode);
else
mpInit->mpPlayer->ChangeState(ePlayerState_Normal);
}
/////////////////////////
//Snap it into place.
//pAttachBody->SetMatrix(pAreaBody->GetWorldMatrix());
//pAttachBody->SetPosition(pAreaBody->GetWorldPosition() - pAttachBody->GetMassCentre());
mtxAttachedStart = pAttachBody->GetLocalMatrix();
mfSetMtxTime = 0;
pAttachBody->SetLinearVelocity(0);
pAttachBody->SetAngularVelocity(0);
mbBodyGravity = pAttachBody->GetGravity();
pAttachBody->SetGravity(false);
mfBodyMass = pAttachBody->GetMass();
pAttachBody->SetMass(0);
mpAttachedBody = pAttachBody;
mpLastAttachedBody = mpAttachedBody;
//Log("Attaching body!\n");
}
}
//-----------------------------------------------------------------------
void cGameStickArea::DetachBody()
{
if(mpAttachedBody)
{
//Log("Dettached body %s\n", mpAttachedBody->GetName().c_str());
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
iPhysicsBody *pAreaBody = mvBodies[0];
//Callback function
if(msDetachFunction!="")
{
tString sCommand = GetCallbackFunc(msDetachFunction,mpAttachedBody);
mpInit->RunScriptCommand(sCommand);
}
//Sound
if(msDetachSound!="")
{
cSoundEntity *pSound = pWorld->CreateSoundEntity("DetachSound",msDetachSound,true);
if(pSound) pSound->SetPosition(pAreaBody->GetWorldPosition());
}
//Particle System
if(msDetachPS!="")
{
pWorld->CreateParticleSystem("DetachPS",msDetachPS,1,pAreaBody->GetWorldMatrix());
}
mpAttachedBody->SetGravity(true);
mpAttachedBody->SetMass(mfBodyMass);
mpAttachedBody->SetEnabled(true);
//Log("Body mass: %f\n",mpAttachedBody->GetMass());
mpAttachedBody = NULL;
}
}
//-----------------------------------------------------------------------
bool cGameStickArea::GetCanDeatch()
{
if(mfSetMtxTime < 1) return false;
return mbCanDeatch;
}
//-----------------------------------------------------------------------
tString cGameStickArea::GetCallbackFunc(const tString &asFunc,iPhysicsBody *apBody)
{
return asFunc + "(\"" + msName + "\",\"" + apBody->GetName() + "\")";
}
//-----------------------------------------------------------------------
void cGameStickArea::OnPostSceneDraw()
{
//iPhysicsBody *pAreaBody = mvBodies[0];
//pAreaBody->RenderDebugGeometry(mpInit->mpGame->GetGraphics()->GetLowLevel(),cColor(1,1));
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SAVE OBJECT STUFF
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
kBeginSerialize(cGameStickArea_SaveData,iGameEntity_SaveData)
kSerializeVar(mvSize,eSerializeType_Vector3f)
kSerializeVar(msAttachFunction,eSerializeType_String)
kSerializeVar(msDetachFunction,eSerializeType_String)
kSerializeVar(msAttachSound,eSerializeType_String)
kSerializeVar(msDetachSound,eSerializeType_String)
kSerializeVar(msAttachPS,eSerializeType_String)
kSerializeVar(msDetachPS,eSerializeType_String)
kSerializeVar(mbCanDeatch,eSerializeType_Bool)
kSerializeVar(msAttachedBody,eSerializeType_String)
kSerializeVar(mfSetMtxTime,eSerializeType_Float32)
kSerializeVar(mbCheckCenterInArea,eSerializeType_Bool)
kSerializeVar(mfPoseTime,eSerializeType_Float32)
kEndSerialize()
//-----------------------------------------------------------------------
iGameEntity* cGameStickArea_SaveData::CreateEntity()
{
return NULL;
}
//-----------------------------------------------------------------------
iGameEntity_SaveData* cGameStickArea::CreateSaveData()
{
return hplNew( cGameStickArea_SaveData, () );
}
//-----------------------------------------------------------------------
void cGameStickArea::SaveToSaveData(iGameEntity_SaveData *apSaveData)
{
__super::SaveToSaveData(apSaveData);
cGameStickArea_SaveData *pData = static_cast(apSaveData);
kCopyToVar(pData,msAttachFunction);
kCopyToVar(pData,msDetachFunction);
kCopyToVar(pData,msAttachSound);
kCopyToVar(pData,msDetachSound);
kCopyToVar(pData,msAttachPS);
kCopyToVar(pData,msDetachPS);
kCopyToVar(pData,mbCanDeatch);
kCopyToVar(pData,mbCheckCenterInArea);
kCopyToVar(pData,mfPoseTime);
kCopyToVar(pData,mfSetMtxTime);
pData->mvSize = mvBodies[0]->GetShape()->GetSize();
if(mpAttachedBody)
{
pData->msAttachedBody = mpAttachedBody->GetName();
}
}
//-----------------------------------------------------------------------
void cGameStickArea::LoadFromSaveData(iGameEntity_SaveData *apSaveData)
{
__super::LoadFromSaveData(apSaveData);
cGameStickArea_SaveData *pData = static_cast(apSaveData);
kCopyFromVar(pData,msAttachFunction);
kCopyFromVar(pData,msDetachFunction);
kCopyFromVar(pData,msAttachSound);
kCopyFromVar(pData,msDetachSound);
kCopyFromVar(pData,msAttachPS);
kCopyFromVar(pData,msDetachPS);
kCopyFromVar(pData,mbCanDeatch);
kCopyFromVar(pData,mbCheckCenterInArea);
kCopyFromVar(pData,mfPoseTime);
kCopyFromVar(pData,mfSetMtxTime);
}
//-----------------------------------------------------------------------
void cGameStickArea::SetupSaveData(iGameEntity_SaveData *apSaveData)
{
__super::SetupSaveData(apSaveData);
cGameStickArea_SaveData *pData = static_cast(apSaveData);
if(pData->msAttachedBody!="")
{
iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
mpAttachedBody = pWorld->GetBody(pData->msAttachedBody);
mpLastAttachedBody = mpAttachedBody;
}
}
//-----------------------------------------------------------------------