/*
* 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 "GameObject.h"
#include "Init.h"
#include "MapHandler.h"
#include "Player.h"
#include "PlayerHelper.h"
#include "GameEnemy.h"
#include "AttackHandler.h"
#include "EffectHandler.h"
#include "GameStickArea.h"
#include "GlobalInit.h"
//////////////////////////////////////////////////////////////////////////
// CALLBACK
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cGameObjectBodyCallback::cGameObjectBodyCallback(cInit *apInit, cGameObject *apObject)
{
mpInit = apInit;
mpObject = apObject;
}
//-----------------------------------------------------------------------
bool cGameObjectBodyCallback::OnBeginCollision(iPhysicsBody *apBody, iPhysicsBody *apCollideBody)
{
return true;
}
//-----------------------------------------------------------------------
void cGameObjectBodyCallback::OnCollide(iPhysicsBody *apBody, iPhysicsBody *apCollideBody,
cPhysicsContactData* apContactData)
{
//Log("OnCollide %s vs %s\n",apBody->GetName().c_str(),apCollideBody->GetName().c_str());
/////////////////////////////////////////
// Damage on enemies
if(apCollideBody->IsCharacter())
{
//Check if there is an enemy.
iGameEnemy *pEnemy = (iGameEnemy*)apCollideBody->GetUserData();
if(pEnemy && apBody->GetMass()>4)
{
float fSpeed = apBody->GetLinearVelocity().Length();
float fImpulseSize = fSpeed * apBody->GetMass();
if(fSpeed > 4.5f && fImpulseSize > 25 && std::abs(apContactData->mfMaxContactNormalSpeed) >= 1.0f)
{
pEnemy->Damage(fImpulseSize * 0.1f,1);
}
}
}
/////////////////////////////////////////
// Check if the object breaks
if(mpObject->mBreakProps.mbActive)
{
float fImpulseSize = apBody->GetLinearVelocity().Length() * apBody->GetMass() +
apCollideBody->GetLinearVelocity().Length() * apCollideBody->GetMass();
if(fImpulseSize >= mpObject->mBreakProps.mfMinImpulse &&
std::abs(apContactData->mfMaxContactNormalSpeed) >= mpObject->mBreakProps.mfMinNormalSpeed)
{
if(mpInit->mbDebugInteraction)
{
Log("------ Breakage ----------\n");
Log(" Body '%s' by Body '%s'\n",apBody->GetName().c_str(), apCollideBody->GetName().c_str());
Log(" Impulse: %f (%fm/s * %fkg) + (%fm/s * %fkg)\n",fImpulseSize,
apBody->GetLinearVelocity().Length(), apBody->GetMass(),
apCollideBody->GetLinearVelocity().Length(), apCollideBody->GetMass());
Log("-------------------------\n");
}
mpObject->Break();
}
}
////////////////////////////////////////////
// Do Some Damage
if(mpObject->mDamageProps.mbActive)
{
float fDamage =0;
//Damage by linear velocity.
float fSpeed = apBody->GetLinearVelocity().Length();
if(fSpeed > mpObject->mDamageProps.mfMinLinearDamageSpeed)
{
if(fSpeed > mpObject->mDamageProps.mfMaxLinearDamageSpeed) fSpeed = mpObject->mDamageProps.mfMaxLinearDamageSpeed;
fDamage = mpObject->mDamageProps.mfMinDamage +
(mpObject->mDamageProps.mfMaxDamage - mpObject->mDamageProps.mfMinDamage) *
((fSpeed - mpObject->mDamageProps.mfMinLinearDamageSpeed) /
(mpObject->mDamageProps.mfMaxLinearDamageSpeed - mpObject->mDamageProps.mfMinLinearDamageSpeed));
}
//Damage by angular velocity.
fSpeed = apBody->GetAngularVelocity().Length();
if(fSpeed > mpObject->mDamageProps.mfMinAngularDamageSpeed)
{
if(fSpeed > mpObject->mDamageProps.mfMaxAngularDamageSpeed) fSpeed = mpObject->mDamageProps.mfMaxAngularDamageSpeed;
float fTempDamage = mpObject->mDamageProps.mfMinDamage +
(mpObject->mDamageProps.mfMaxDamage - mpObject->mDamageProps.mfMinDamage) *
((fSpeed - mpObject->mDamageProps.mfMinAngularDamageSpeed) /
(mpObject->mDamageProps.mfMaxAngularDamageSpeed - mpObject->mDamageProps.mfMinAngularDamageSpeed));
if(fTempDamage > fDamage) fDamage = fTempDamage;
}
//////////////////////////
//Do the damage
if(fDamage>0)
{
//Player
if(apCollideBody == mpInit->mpPlayer->GetCharacterBody()->GetBody())
{
mpInit->mpPlayer->Damage(fDamage, ePlayerDamageType_BloodSplash);
}
//Entity
else
{
iGameEntity *pEntity = (iGameEntity*)apCollideBody->GetUserData();
if(pEntity)
{
pEntity->Damage(fDamage,mpObject->mDamageProps.mlDamageStrength);
}
}
}
}
/*if(apCollideBody->IsCharacter()==false) return;
//Check if there is an enemy.
iGameEnemy *pEnemy = NULL;
if(apCollideBody->GetUserData())
{
pEnemy = (iGameEnemy*)apCollideBody->GetUserData();
}
////////////////////////////////////////////
// Stun
if(pEnemy && mpObject->GetInteractMode() != eObjectInteractMode_Push)
{
float fSpeed = apBody->GetLinearVelocity().Length();
float fMass = apBody->GetMass();
float fImpulse = fSpeed * fMass;
if(pEnemy->mfMinStunSpeed <= fSpeed && pEnemy->mfMinStunImpulse <= fImpulse)
{
//Log("Stun by %s speed: %f mass: %f\n",apBody->GetName().c_str(),fSpeed,fMass);
pEnemy->Stun(pEnemy->mfObjectCollideStun, true);
}
}*/
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// LOADER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cEntityLoader_GameObject::cEntityLoader_GameObject(const tString &asName, cInit *apInit)
: cEntityLoader_Object(asName)
{
mpInit = apInit;
}
cEntityLoader_GameObject::~cEntityLoader_GameObject()
{
}
//-----------------------------------------------------------------------
void cEntityLoader_GameObject::BeforeLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
cWorld3D *apWorld)
{
}
//-----------------------------------------------------------------------
void cEntityLoader_GameObject::AfterLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
cWorld3D *apWorld)
{
cGameObject *pObject = hplNew( cGameObject, (mpInit,mpEntity->GetName()) );
pObject->msFileName = msFileName;
pObject->m_mtxOnLoadTransform = a_mtxTransform;
// Set the engine objects to the object
pObject->SetBodies(mvBodies);
pObject->SetBeams(mvBeams);
pObject->SetMeshEntity(mpEntity);
pObject->SetParticleSystems(mvParticleSystems);
pObject->SetSoundEntities(mvSoundEntities);
pObject->SetLights(mvLights);
pObject->SetHapticShapes(mvHapticShapes);
///////////////////////////////////
// Load game properties
TiXmlElement *pGameElem = apRootElem->FirstChildElement("GAME");
if(pGameElem)
{
////////////////////////////////////////////
//General
pObject->mInteractMode = ToInteractMode(pGameElem->Attribute("InteractMode"));
pObject->mfHealth = cString::ToFloat(pGameElem->Attribute("Health"),0);
pObject->mlToughness = cString::ToInt(pGameElem->Attribute("Toughness"),0);
pObject->mfForwardUpMul = cString::ToFloat(pGameElem->Attribute("ForwardUpMul"),1);
pObject->mfForwardRightMul = cString::ToFloat(pGameElem->Attribute("ForwardRightMul"),1);
pObject->mfUpMul = cString::ToFloat(pGameElem->Attribute("UpMul"),1);
pObject->mfRightMul = cString::ToFloat(pGameElem->Attribute("RightMul"),1);
pObject->mbPickAtPoint = cString::ToBool(pGameElem->Attribute("PickAtPoint"),false);
pObject->mbRotateWithPlayer = cString::ToBool(pGameElem->Attribute("RotateWithPlayer"),true);
pObject->mbUseNormalMass = cString::ToBool(pGameElem->Attribute("UseNormalMass"),false);
pObject->mfGrabMassMul = cString::ToFloat(pGameElem->Attribute("GrabMassMul"),1.0f);
pObject->mbCanBeThrown = cString::ToBool(pGameElem->Attribute("CanBeThrown"),true);
pObject->mbCanBePulled = cString::ToBool(pGameElem->Attribute("CanBePulled"),true);
pObject->mbDestroyable = cString::ToBool(pGameElem->Attribute("Destroyable"),false);
pObject->mfDestroyStrength = cString::ToFloat(pGameElem->Attribute("DestroyStrength"),0.0f);
pObject->msDestoySound = cString::ToString(pGameElem->Attribute("DestoySound"),"");
pObject->mbPauseControllers = cString::ToBool(pGameElem->Attribute("PauseControllers"),true);
pObject->mbPauseGravity = cString::ToBool(pGameElem->Attribute("PauseGravity"),true);
pObject->mbForceLightOffset = cString::ToBool(pGameElem->Attribute("ForceLightOffset"),false);
pObject->mvLightOffset = cString::ToVector3f(pGameElem->Attribute("LightOffset"),0);
pObject->mfHapticTorqueMul = cString::ToFloat(pGameElem->Attribute("HapticTorqueMul"),1.0f);
////////////////////////////////////////////
//Disappear
pObject->mDisappearProps.mbActive = cString::ToBool(pGameElem->Attribute("Disappear"),false);
if(pObject->mDisappearProps.mbActive)
{
pObject->mDisappearProps.mfMinTime = cString::ToFloat(pGameElem->Attribute("DisappearMinTime"),0);
pObject->mDisappearProps.mfMaxTime = cString::ToFloat(pGameElem->Attribute("DisappearMaxTime"),0);
pObject->mDisappearProps.mfMinDistance = cString::ToFloat(pGameElem->Attribute("DisappearMinDist"),0);
pObject->mDisappearProps.mfMinCloseDistance = cString::ToFloat(pGameElem->Attribute("DisappearMinCloseDist"),0);
pObject->mDisappearProps.mfTime = cMath::RandRectf(pObject->mDisappearProps.mfMinTime,
pObject->mDisappearProps.mfMaxTime);
}
////////////////////////////////////////////
//Breakable
pObject->mBreakProps.mbActive = cString::ToBool(pGameElem->Attribute("Breakable"),false);
if(pObject->mBreakProps.mbActive || pObject->mDisappearProps.mbActive)
{
pObject->mBreakProps.msSound = cString::ToString(pGameElem->Attribute("BreakSound"),"");
pObject->mBreakProps.msEntity = cString::ToString(pGameElem->Attribute("BreakEntity"),"");
pObject->mBreakProps.msPS = cString::ToString(pGameElem->Attribute("BreakPS"),"");
pObject->mBreakProps.mfMinImpulse = cString::ToFloat(pGameElem->Attribute("BreakImpulse"),99999);
pObject->mBreakProps.mfMinNormalSpeed = cString::ToFloat(pGameElem->Attribute("BreakNormalSpeed"),99999);
pObject->mBreakProps.mfMinPlayerImpulse = cString::ToFloat(pGameElem->Attribute("BreakPlayerImpulse"),99999);
pObject->mBreakProps.mfCenterForce = cString::ToFloat(pGameElem->Attribute("BreakCenterForce"),0);
pObject->mBreakProps.mbExplosion = cString::ToBool(pGameElem->Attribute("BreakExplosion"),false);
pObject->mBreakProps.mfExpl_Radius = cString::ToFloat(pGameElem->Attribute("BreakExpl_Radius"),0);
pObject->mBreakProps.mfExpl_MinDamage = cString::ToFloat(pGameElem->Attribute("BreakExpl_MinDamage"),0);
pObject->mBreakProps.mfExpl_MaxDamage = cString::ToFloat(pGameElem->Attribute("BreakExpl_MaxDamage"),0);
pObject->mBreakProps.mfExpl_MinForce = cString::ToFloat(pGameElem->Attribute("BreakExpl_MinForce"),0);
pObject->mBreakProps.mfExpl_MaxForce = cString::ToFloat(pGameElem->Attribute("BreakExpl_MaxForce"),0);
pObject->mBreakProps.mfExpl_MaxImpulse = cString::ToFloat(pGameElem->Attribute("BreakExpl_MaxImpulse"),0);
pObject->mBreakProps.mfExpl_MinMass = cString::ToFloat(pGameElem->Attribute("BreakExpl_MinMass"),0);
pObject->mBreakProps.mlExpl_Strength = cString::ToInt(pGameElem->Attribute("BreakExpl_Strength"),0);
pObject->mBreakProps.mbLightFlash = cString::ToBool(pGameElem->Attribute("LightFlash"),false);
pObject->mBreakProps.mLight_Color = cString::ToColor(pGameElem->Attribute("LightFlash_Color"),cColor(0,0));
pObject->mBreakProps.mfLight_Radius = cString::ToFloat(pGameElem->Attribute("LightFlash_Radius"),0);
pObject->mBreakProps.mfLight_AddTime = cString::ToFloat(pGameElem->Attribute("LightFlash_AddTime"),0);
pObject->mBreakProps.mfLight_NegTime = cString::ToFloat(pGameElem->Attribute("LightFlash_NegTime"),0);
pObject->mBreakProps.mvLight_Offset = cString::ToVector3f(pGameElem->Attribute("LightFlash_Offset"),0);
pObject->mBreakProps.mbEarRing = cString::ToBool(pGameElem->Attribute("EarRing"),false);
pObject->mBreakProps.mfEarRing_MaxDist = cString::ToFloat(pGameElem->Attribute("EarRing_MaxDist"),0);
pObject->mBreakProps.mfEarRing_Time = cString::ToFloat(pGameElem->Attribute("EarRing_Time"),0);
//Set all bodies as volatile
for(size_t i =0; iSetVolatile(true);
}
}
////////////////////////////////////////////
//Attracts Enemies
pObject->mAttractProps.mbActive = cString::ToBool(pGameElem->Attribute("AttractEnemies"),false);
if(pObject->mAttractProps.mbActive)
{
pObject->mAttractProps.mfDistance = cString::ToFloat(pGameElem->Attribute("AttractDistance"),0);
tString sSubTypes = cString::ToString(pGameElem->Attribute("AttractSubtypes"),"");
cString::GetStringVec(sSubTypes,pObject->mAttractProps.mvSubtypes,NULL);
pObject->mAttractProps.mbIsEaten = cString::ToBool(pGameElem->Attribute("AttractIsEaten"),false);
pObject->mAttractProps.mfEatLength = cString::ToFloat(pGameElem->Attribute("AttractEatLength"),0);
}
////////////////////////////////////////////
//Mode specific
//Push mode
if(pObject->mInteractMode == eObjectInteractMode_Push)
{
if(mpInit->mbHasHaptics==false)
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"),mpInit->mpPlayer->GetMaxPushDist());
pObject->mbHasInteraction = true;
}
//Move Mode
else if(pObject->mInteractMode == eObjectInteractMode_Move)
{
if(mpInit->mbHasHaptics)
{
pObject->mInteractMode = eObjectInteractMode_Grab;
pObject->mbPickAtPoint = true;
if(mpInit->mbHasHaptics==false)
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"),mpInit->mpPlayer->GetMaxGrabDist());
pObject->mbHasInteraction = true;
pObject->mbIsMover = true;
}
else
{
if(mpInit->mbHasHaptics==false)
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"),mpInit->mpPlayer->GetMaxMoveDist());
pObject->mbHasInteraction = true;
}
}
//Grab Mode
else if(pObject->mInteractMode == eObjectInteractMode_Grab)
{
if(mpInit->mbHasHaptics==false)
pObject->mfMaxInteractDist = cString::ToFloat(pGameElem->Attribute("MaxInteractDist"),mpInit->mpPlayer->GetMaxGrabDist());
pObject->mbHasInteraction = true;
}
/////////////////////////////////////
//Damage
pObject->mDamageProps.mbActive = cString::ToBool(pGameElem->Attribute("DamageCharacter"),false);
if(pObject->mDamageProps.mbActive)
{
pObject->mDamageProps.mfMinLinearDamageSpeed = cString::ToFloat(pGameElem->Attribute("MinLinearDamageSpeed"),0);
pObject->mDamageProps.mfMinAngularDamageSpeed = cString::ToFloat(pGameElem->Attribute("MinAngularDamageSpeed"),0);
pObject->mDamageProps.mfMaxLinearDamageSpeed = cString::ToFloat(pGameElem->Attribute("MaxLinearDamageSpeed"),0);
pObject->mDamageProps.mfMaxAngularDamageSpeed = cString::ToFloat(pGameElem->Attribute("MaxAngularDamageSpeed"),0);
pObject->mDamageProps.mfMinDamage = cString::ToFloat(pGameElem->Attribute("MinDamage"),0);
pObject->mDamageProps.mfMaxDamage = cString::ToFloat(pGameElem->Attribute("MaxDamage"),0);
pObject->mDamageProps.mlDamageStrength = cString::ToInt(pGameElem->Attribute("DamageStrength"),0);
}
//Add callback for all bodies
pObject->mpBodyCallback = hplNew( cGameObjectBodyCallback, (mpInit,pObject) );
for(size_t i=0; iAddBodyCallback(pObject->mpBodyCallback);
}
else
{
Error("Couldn't find game element for entity '%s'\n",mpEntity->GetName().c_str());
}
///////////////////////////////////
// Add a the object as user data to the body, to get the obejct later on.
for(size_t i=0; iSetUserData((void*)pObject);
}
}
/////////////////////////////////
// Add to map handler
mpInit->mpMapHandler->AddGameEntity(pObject);
pObject->SetUpTransMaterials();
pObject->SetupBreakObject();
pObject->SetupForceOffset();
/////////////////////////////////
// Add to map handler
}
//-----------------------------------------------------------------------
eObjectInteractMode cEntityLoader_GameObject::ToInteractMode(const char* apString)
{
if(apString==NULL) return eObjectInteractMode_Static;
tString sName = cString::ToLowerCase(apString);
if(sName == "grab") return eObjectInteractMode_Grab;
if(sName == "move") return eObjectInteractMode_Move;
if(sName == "push") return eObjectInteractMode_Push;
if(sName == "static") return eObjectInteractMode_Static;
Warning("Invalid object interact mode '%s'\n",apString);
return eObjectInteractMode_Static;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cGameObject::cGameObject(cInit *apInit,const tString& asName) : iGameEntity(apInit,asName)
{
mType = eGameEntityType_Object;
mInteractMode = eObjectInteractMode_Static;
mpBodyCallback = NULL;
mpCurrentAttraction = NULL;
mfAttractCount = 4.0f;
mbIsMover = false;
mfCloseToSameCount = cMath::RandRectf(0, 5.5f);
}
//-----------------------------------------------------------------------
cGameObject::~cGameObject(void)
{
if(mpBodyCallback) hplDelete( mpBodyCallback );
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cGameObject::OnPlayerPick()
{
if( mvCallbackScripts[eGameEntityScriptType_PlayerInteract] &&
mpInit->mpPlayer->GetPickedDist() < mfMaxInteractDist &&
mpInit->mpPlayer->mbProxyTouching)
{
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Active);
}
else if(mInteractMode == eObjectInteractMode_Static && msDescription==_W(""))
{
if(mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_Inactive);
else
mpInit->mpPlayer->SetCrossHairState(eCrossHairState_None);
}
else
{
//mpInit->mpPlayer->SetCrossHairState(eCrossHairState_None);
}
}
//-----------------------------------------------------------------------
void cGameObject::OnPlayerInteract()
{
iPhysicsBody *pBody = mpInit->mpPlayer->GetPickedBody();
cGameStickArea *pStickArea = mpInit->mpMapHandler->GetBodyStickArea(pBody);
if( (pBody->GetMass()==0 && pStickArea==NULL) ||
(pStickArea && pStickArea->GetCanDeatch()==false))
{
return;
}
if(mpInit->mbHasHaptics && mpInit->mpPlayer->mbProxyTouching==false) return;
switch(mInteractMode)
{
case eObjectInteractMode_Push: PushObject(); break;
case eObjectInteractMode_Move: MoveObject(); break;
case eObjectInteractMode_Grab: GrabObject(); break;
}
}
//-----------------------------------------------------------------------
void cGameObject::Update(float afTimeStep)
{
UpdateAttraction(afTimeStep);
////////////////////////////////
// Disappear
if(mDisappearProps.mbActive)
{
//TODO: Check per body!
float fMinDist = cMath::Vector3Dist(mpInit->mpPlayer->GetCamera()->GetPosition(),
mvBodies[0]->GetWorldPosition());
for(size_t i=1; i< mvBodies.size(); ++i)
{
if(mvBodies[i]==NULL) continue;
float fDist = cMath::Vector3Dist( mpInit->mpPlayer->GetCamera()->GetPosition(),
mvBodies[i]->GetWorldPosition());
if(fDist < fMinDist) fMinDist = fDist;
}
if(fMinDist >= mDisappearProps.mfMinDistance)
{
mDisappearProps.mfTime -= afTimeStep;
if(mDisappearProps.mfTime <=0)
{
Break();
}
}
if(mDisappearProps.mfMinCloseDistance > 0)
{
cVector3f vPos = mpMeshEntity->GetBoundingVolume()->GetWorldCenter();
float fMinSqrDist = mDisappearProps.mfMinCloseDistance*
mDisappearProps.mfMinCloseDistance;
if(mfCloseToSameCount <=0)
{
mfCloseToSameCount = 5.5f;
tGameEntityIterator it = mpInit->mpMapHandler->GetGameEntityIterator();
while(it.HasNext())
{
iGameEntity *pEntity = it.Next();
if(pEntity == this) continue;
if(pEntity->GetType() != eGameEntityType_Object) continue;
if(pEntity->GetFileName() != GetFileName()) continue;
cGameObject *pObject = static_cast(pEntity);
float fSqrDist = cMath::Vector3DistSqr(
pObject->GetMeshEntity()->GetBoundingVolume()->GetWorldCenter(),
vPos);
if(fSqrDist < fMinSqrDist)
{
mDisappearProps.mfTime =0;
Break();
}
}
}
else
{
mfCloseToSameCount -= afTimeStep;
}
}
}
///////////////////////////////
// Force offset
if(mbForceLightOffset)
{
for(size_t i=0; iSetMatrix(cMath::MatrixMul(mpMeshEntity->GetWorldMatrix(),
mvLightLocalOffsets[i]));
pLight->SetPosition(pLight->GetWorldPosition() + mvLightOffset);
}
}
}
//-----------------------------------------------------------------------
void cGameObject::OnPlayerGravityCollide(iCharacterBody *apCharBody, cCollideData *apCollideData)
{
if(mBreakProps.mbActive)
{
///////////////////////////////////////////////
// Check so that the player is really on top of the
// object and not just sliding
bool bPushDown = false;
for(int i=0; i< apCollideData->mlNumOfPoints; i++)
{
cCollidePoint &point = apCollideData->mvContactPoints[i];
if( point.mvNormal.y > 0.001f &&
point.mvNormal.y > std::abs(point.mvNormal.x) &&
point.mvNormal.y > std::abs(point.mvNormal.z))
{
bPushDown = true;
}
}
if(bPushDown)
{
////////////////////////////////////
// Check the impulse created by the player
float fImpulse = apCharBody->GetMass() * std::abs(apCharBody->GetForceVelocity().y);
if(mBreakProps.mfMinPlayerImpulse <= fImpulse)
{
if(mpInit->mbDebugInteraction)
{
Log("------ Breakage ----------\n");
Log(" Body '%s' by Player\n",mvBodies[0]->GetName().c_str());
Log(" Impulse: %f : %fm/s * %fkg (from Player)\n",fImpulse,
std::abs(apCharBody->GetForceVelocity().y), apCharBody->GetMass());
Log("-------------------------\n");
}
Break();
}
}
}
}
//-----------------------------------------------------------------------
void cGameObject::SetInteractMode(eObjectInteractMode aInteractMode)
{
mInteractMode = aInteractMode;
if(mInteractMode == eObjectInteractMode_Static)
{
mbHasInteraction = false;
}
else
{
mbHasInteraction = true;
}
}
//-----------------------------------------------------------------------
void cGameObject::BreakAction()
{
if(mvBodies.empty()) return;
iPhysicsBody *pParentBody = mvBodies[0];
for(size_t i=1; i< mvBodies.size(); ++i)
{
if(pParentBody->GetMass() != 0)
{
if(mvBodies[i]->GetMass() > pParentBody->GetMass() || mvBodies[i]->GetMass()==0)
pParentBody = mvBodies[i];
}
}
//////////////////
//Script
if(mvCallbackScripts[eGameEntityScriptType_OnBreak])
{
tString sCommand = GetScriptCommand(eGameEntityScriptType_OnBreak);
mpInit->RunScriptCommand(sCommand);
}
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
//////////////////
//Check if player os holding object
if( (mpInit->mpPlayer->GetState()==ePlayerState_Grab ||
mpInit->mpPlayer->GetState()==ePlayerState_Move ||
mpInit->mpPlayer->GetState()==ePlayerState_Push)
&&
mpInit->mpPlayer->GetPushBody() == pParentBody)
{
mpInit->mpPlayer->ChangeState(ePlayerState_Normal);
}
//////////////////
//Sound
if(mBreakProps.msSound != "")
{
cSoundEntity *pSound = pWorld->CreateSoundEntity("Break",mBreakProps.msSound,true);
if(pSound) pSound->SetPosition(mpMeshEntity->GetWorldPosition());
}
//////////////////
//Particle System
if(mBreakProps.msPS != "")
{
cParticleSystem3D *pPS = pWorld->CreateParticleSystem("Break",mBreakProps.msPS,cVector3f(1,1,1),
pParentBody->GetWorldMatrix());
}
//////////////////
//Entity
if(mBreakProps.msEntity != "")
{
iEntity3D * pEntity = pWorld->CreateEntity(mpMeshEntity->GetName() + "_broken",
pParentBody->GetWorldMatrix(),
mBreakProps.msEntity, true);
if(pEntity)
{
iGameEntity *pGameEntity = mpInit->mpMapHandler->GetLatestEntity();
for(int i=0; i< pGameEntity->GetBodyNum(); ++i)
{
//Add the object velocity
iPhysicsBody *pBody = pGameEntity->GetBody(i);
pBody->SetLinearVelocity(pParentBody->GetLinearVelocity());
//TODO: Add torque
//Force from center
cVector3f vBodyCentre = cMath::MatrixMul(pBody->GetWorldMatrix(),pBody->GetMassCentre());
cVector3f vForceDir = vBodyCentre - pParentBody->GetLocalPosition();
vForceDir.Normalise();
pBody->AddForce(vForceDir * mBreakProps.mfCenterForce);
}
}
}
//////////////////
//Explosion
if(mBreakProps.mbExplosion)
{
mpInit->mpAttackHandler->CreateSplashDamage(
pParentBody->GetWorldPosition(),
mBreakProps.mfExpl_Radius,
mBreakProps.mfExpl_MinDamage,mBreakProps.mfExpl_MaxDamage,
mBreakProps.mfExpl_MinForce,mBreakProps.mfExpl_MaxForce,
mBreakProps.mfExpl_MaxImpulse,
eAttackTargetFlag_Bodies | eAttackTargetFlag_Player | eAttackTargetFlag_Enemy,
mBreakProps.mfExpl_MinMass,
mBreakProps.mlExpl_Strength);
}
//////////////////
//Light flash
if(mBreakProps.mbLightFlash)
{
mpInit->mpMapHandler->AddLightFlash(pParentBody->GetWorldPosition() + mBreakProps.mvLight_Offset,
mBreakProps.mfLight_Radius,
mBreakProps.mLight_Color,
mBreakProps.mfLight_AddTime,
mBreakProps.mfLight_NegTime);
}
//////////////////
//Ear ring
if(mBreakProps.mbEarRing)
{
cPlayer *pPlayer = mpInit->mpPlayer;
float fDist = cMath::Vector3Dist( pParentBody->GetWorldPosition(),
pPlayer->GetCamera()->GetPosition());
//Log("Ear ring dist: %f max: %f\n",fDist, mBreakProps.mfEarRing_MaxDist);
if(fDist <= mBreakProps.mfEarRing_MaxDist)
{
pPlayer->GetEarRing()->Start(mBreakProps.mfEarRing_Time);
}
}
mpInit->mpGame->ResetLogicTimer();
}
//-----------------------------------------------------------------------
void cGameObject::OnDeath(float afDamage)
{
if(mBreakProps.mbActive)
{
Break();
}
}
//-----------------------------------------------------------------------
void cGameObject::SetupBreakObject()
{
if(mBreakProps.mbActive==false) return;
if(mBreakProps.msEntity!="") PreloadModel(mBreakProps.msEntity);
if(mBreakProps.msPS!="")
{
cParticleSystem3D *pPS = mpInit->mpGame->GetResources()->GetParticleManager()->CreatePS3D(
"",mBreakProps.msPS,1,cMatrixf::Identity);
hplDelete( pPS );
}
if(mBreakProps.msSound!="")
{
mpInit->PreloadSoundEntityData(mBreakProps.msSound);
}
}
//-----------------------------------------------------------------------
void cGameObject::SetupForceOffset()
{
if(mbForceLightOffset==false) return;
mvLightLocalOffsets.resize(mvLights.size());
for(size_t i=0; iGetLocalMatrix();
if(mvLights[i]->GetParent())
mvLights[i]->GetParent()->RemoveEntity(mvLights[i]);
if(mvLights[i]->GetEntityParent())
mvLights[i]->GetEntityParent()->RemoveChild(mvLights[i]);
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cGameObject::GrabObject()
{
if( mpInit->mpPlayer->GetPickedDist() > mfMaxInteractDist)
{
if(mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
mpInit->mpEffectHandler->GetSubTitle()->Add(kTranslate("Player", "ObjectTooFar"),2.0f,true);
return;
}
//Set some properties
mpInit->mpPlayer->mbGrabbingMoveBody = mbIsMover;
mpInit->mpPlayer->mfHapticTorqueMul = mfHapticTorqueMul;
mpInit->mpPlayer->mbPickAtPoint = mbPickAtPoint;
mpInit->mpPlayer->mbRotateWithPlayer = mbRotateWithPlayer;
mpInit->mpPlayer->mbUseNormalMass = mbUseNormalMass;
mpInit->mpPlayer->mfGrabMassMul = mfGrabMassMul;
mpInit->mpPlayer->mbCanBeThrown = mbCanBeThrown;
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
mpInit->mpPlayer->ChangeState(ePlayerState_Grab);
}
//-----------------------------------------------------------------------
void cGameObject::MoveObject()
{
float fDist = GetMoveDist();
if(fDist > mfMaxInteractDist)
{
if(mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
mpInit->mpEffectHandler->GetSubTitle()->Add(kTranslate("Player", "ObjectTooFar"),2.0f,true);
return;
}
//Set some properties
mpInit->mpPlayer->mfForwardUpMul = mfForwardUpMul;
mpInit->mpPlayer->mfForwardRightMul = mfForwardRightMul;
mpInit->mpPlayer->mfUpMul = mfUpMul;
mpInit->mpPlayer->mfRightMul = mfRightMul;
mpInit->mpPlayer->mbCanBeThrown = mbCanBeThrown;
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
mpInit->mpPlayer->ChangeState(ePlayerState_Move);
}
float cGameObject::GetMoveDist()
{
/*cVector3f vBodyPos = mpInit->mpPlayer->GetPickedPos();
cVector3f vPlayerPos = mpInit->mpPlayer->GetCamera()->GetPosition();
float fDist = cMath::Vector3Dist(vBodyPos, vPlayerPos);
return fDist;*/
return mpInit->mpPlayer->GetPickedDist();
}
//-----------------------------------------------------------------------
void cGameObject::PushObject()
{
float fDist = GetPushDist();
if( fDist > mfMaxInteractDist)
{
if(mpInit->mpPlayer->GetState() == ePlayerState_InteractMode)
mpInit->mpEffectHandler->GetSubTitle()->Add(kTranslate("Player", "ObjectTooFar"),2.0f,true);
return;
}
mpInit->mpPlayer->mbPickAtPoint = mbPickAtPoint;
mpInit->mpPlayer->mbCanBeThrown = mbCanBeThrown;
mpInit->mpPlayer->mbCanBePulled = mbCanBePulled;
mpInit->mpPlayer->mfCurrentMaxInteractDist = mfMaxInteractDist;
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
mpInit->mpPlayer->ChangeState(ePlayerState_Push);
}
float cGameObject::GetPushDist()
{
/*cVector3f vBodyPos = mpInit->mpPlayer->GetPickedPos();
cVector3f vPlayerPos = mpInit->mpPlayer->GetCamera()->GetPosition();
float fDist = cMath::Dist2D(cVector2f(vBodyPos.x,vBodyPos.z),cVector2f(vPlayerPos.x,vPlayerPos.z));
*/
return mpInit->mpPlayer->GetPickedDist();
}
//-----------------------------------------------------------------------
void cGameObject::UpdateAttraction(float afTimeStep)
{
if(mAttractProps.mbActive==false) return;
///////////////////////////////////////////////
//Check if the current attraction is near enough
if(mpCurrentAttraction)
{
////////////////////////////////////////
//Check for the attracted enemy
iCharacterBody *pBody = mpCurrentAttraction->GetMover()->GetCharBody();
float fDist = cMath::Vector3Dist( pBody->GetFeetPosition(),
mvBodies[0]->GetWorldPosition());
//Check if the enemy have become busy.
if( (mpCurrentAttraction->GetCurrentStateId() != STATE_MOVETO && fDist GetSize().x*0.75f)
||
(mAttractProps.mbIsEaten && fDist GetSize().x*0.75f))
{
//Use half the width here.
if(mAttractProps.mbIsEaten)
{
mpCurrentAttraction->SetTempFloat(mAttractProps.mfEatLength);
mpCurrentAttraction->ChangeState(STATE_EAT);
}
mpCurrentAttraction->SetAttracted(false);
mpCurrentAttraction = NULL;
mbDestroyMe = true;
return;
}
else if(mpCurrentAttraction->GetCurrentStateId() != STATE_MOVETO)
{
mpCurrentAttraction->SetAttracted(false);
mpCurrentAttraction = NULL;
return;
}
}
///////////////////////////////////////////////
//Check if any enemy is near enough to be attracted
else
{
if(mfAttractCount <=0)
{
float fClosestDist = 10000.0f;
iGameEnemy *pChosenEnemy = NULL;
tGameEnemyIterator enemyIt = mpInit->mpMapHandler->GetGameEnemyIterator();
while(enemyIt.HasNext())
{
iGameEnemy *pEnemy = enemyIt.Next();
if( pEnemy->GetHealth() <= 0 || pEnemy->IsActive()==false ||
pEnemy->IsAttracted())
{
continue;
}
bool bCorrectSub = false;
for(size_t i=0; i< mAttractProps.mvSubtypes.size(); ++i)
{
if(mAttractProps.mvSubtypes[i] == pEnemy->GetSubType()) bCorrectSub = true;
}
if(bCorrectSub==false) continue;
//Check if the enemy has already been attracted by this object
if(m_setAttractedEnemies.find(pEnemy) != m_setAttractedEnemies.end()) continue;
float fDist = cMath::Vector3Dist( pEnemy->GetMover()->GetCharBody()->GetFeetPosition(),
mvBodies[0]->GetWorldPosition());
if(fDist < mAttractProps.mfDistance && fDist < fClosestDist)
{
fClosestDist = fDist;
pChosenEnemy = pEnemy;
}
}
if(pChosenEnemy)
{
if(pChosenEnemy->MoveToPos(mvBodies[0]->GetWorldPosition() +cVector3f(0,0.2f,0)))
{
mpCurrentAttraction = pChosenEnemy;
pChosenEnemy->SetAttracted(true);
m_setAttractedEnemies.insert(pChosenEnemy);
}
}
mfAttractCount = 0.2f;
}
else
{
mfAttractCount -= afTimeStep;
}
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SAVE OBJECT STUFF
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
kBeginSerialize(cGameObject_SaveData,iGameEntity_SaveData)
kSerializeVar(mInteractMode, eSerializeType_Int32)
kSerializeVar(mfMaxInteractDist, eSerializeType_Float32)
kEndSerialize()
//-----------------------------------------------------------------------
iGameEntity* cGameObject_SaveData::CreateEntity()
{
return NULL;
}
//-----------------------------------------------------------------------
iGameEntity_SaveData* cGameObject::CreateSaveData()
{
return hplNew( cGameObject_SaveData, () );
}
//-----------------------------------------------------------------------
void cGameObject::SaveToSaveData(iGameEntity_SaveData *apSaveData)
{
__super::SaveToSaveData(apSaveData);
cGameObject_SaveData *pData = static_cast(apSaveData);
kCopyToVar(pData,mInteractMode);
}
//-----------------------------------------------------------------------
void cGameObject::LoadFromSaveData(iGameEntity_SaveData *apSaveData)
{
__super::LoadFromSaveData(apSaveData);
cGameObject_SaveData *pData = static_cast(apSaveData);
kCopyFromVar(pData,mInteractMode);
}
//-----------------------------------------------------------------------