/*
* 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 "GameEnemy.h"
#include "GameEnemy_Dog.h"
#include "GameEnemy_Spider.h"
#include "GameEnemy_Worm.h"
#include "Init.h"
#include "MapHandler.h"
#include "Player.h"
#include "PlayerHelper.h"
#include "Triggers.h"
#include "AttackHandler.h"
#include "EffectHandler.h"
#include "GameMusicHandler.h"
#include "GameObject.h"
#include "CharacterMove.h"
#include "GlobalInit.h"
tString gvStateName[STATE_NUM] = {
"IDLE",
"HUNT",
"ATTACK",
"FLEE",
"KNOCKDOWN",
"DEAD",
"PATROL",
"INVESTIGATE",
"BREAKDOOR",
"CALLBACKUP",
"MOVETO",
"EAT",
"ATTENTION",
};
//////////////////////////////////////////////////////////////////////////
// GAME ENEMY STATE
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
iGameEnemyState::iGameEnemyState(int alId, cInit *apInit, iGameEnemy *apEnemy)
{
mlId = alId;
mpInit = apInit;
mpPlayer = mpInit->mpPlayer;
mpEnemy = apEnemy;
mpMover = mpEnemy->GetMover();
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// RAY INTERSECT
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cLineOfSightRayCallback::Reset()
{
mbIntersected = false;
if(gpInit->mpPlayer->GetState() == ePlayerState_Grab)
mpGrabBody = gpInit->mpPlayer->GetPushBody();
else
mpGrabBody = NULL;
}
//-----------------------------------------------------------------------
bool cLineOfSightRayCallback::Intersected()
{
return mbIntersected;
}
//-----------------------------------------------------------------------
static bool BodyIsTransperant(iPhysicsBody *apBody)
{
iGameEntity* pEntity = (iGameEntity*)apBody->GetUserData();
if(pEntity && pEntity->GetMeshEntity())
{
cMeshEntity *pMeshEntity = pEntity->GetMeshEntity();
bool bFoundSolid = false;
for(int i=0; i< pMeshEntity->GetSubMeshEntityNum(); ++i)
{
iMaterial *pMaterial = pMeshEntity->GetSubMeshEntity(i)->GetMaterial();
if(pMaterial &&
(pMaterial->IsTransperant()==false && pMaterial->HasAlpha()==false))
{
bFoundSolid = true;
break;
}
}
if(bFoundSolid==false) return true;
}
return false;
}
bool cLineOfSightRayCallback::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams)
{
if(pBody->GetCollide()==false) return true;
if(pBody->IsCharacter()) return true;
if(mpGrabBody == pBody) return true;
if(BodyIsTransperant(pBody)) return true;
mbIntersected = true;
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// GROUND FINDER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
bool cEnemyFindGround::GetGround( const cVector3f &avStartPos,const cVector3f &avDir,
cVector3f *apDestPosition, cVector3f *apDestNormal,
float afMaxDistance)
{
mbIntersected = false;
mfMinDist = afMaxDistance;
mfMaxDistance = afMaxDistance;
iPhysicsWorld *pPhysicsWorld = gpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
pPhysicsWorld->CastRay(this,avStartPos,avStartPos + avDir * mfMaxDistance,true,true,true);
if(mbIntersected)
{
if(apDestPosition) *apDestPosition = mvPos;
if(apDestNormal) *apDestNormal = mvNormal;
return true;
}
return false;
}
bool cEnemyFindGround::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams)
{
if(apParams->mfT < 0) return true;
if(pBody->GetCollideCharacter() == false || pBody->IsCharacter()) return true;
if(mbIntersected == false || mfMinDist > apParams->mfDist)
{
mbIntersected = true;
mfMinDist = apParams->mfDist;
mvPos = apParams->mvPoint;
mvNormal = apParams->mvNormal;
}
return true;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// DOOR CHECKER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
bool cEnemyCheckForDoor::CheckDoor(const cVector3f &avStart, const cVector3f &avEnd)
{
mbIntersected = false;
iPhysicsWorld *pPhysicsWorld = gpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
pPhysicsWorld->CastRay(this,avStart,avEnd,false,false,false);
return mbIntersected;
}
static bool BodyCanBeBroken(iPhysicsBody *pBody)
{
if(pBody->GetUserData()== NULL) return false;
iGameEntity *pEntity = (iGameEntity*)pBody->GetUserData();
if(pEntity->GetType() == eGameEntityType_SwingDoor)
{
return true;
}
if( pEntity->GetType() == eGameEntityType_Object)
{
cGameObject *pObject = static_cast(pEntity);
if(pObject->IsBreakable()) return true;
}
return false;
}
bool cEnemyCheckForDoor::BeforeIntersect(iPhysicsBody *pBody)
{
if(BodyCanBeBroken(pBody)) return true;
return false;
}
bool cEnemyCheckForDoor::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams)
{
if(apParams->mfT<0 || apParams->mfT >1) return true;
if(BodyCanBeBroken(pBody))
{
mbIntersected = true;
return false;
}
return true;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
iGameEnemy::iGameEnemy(cInit *apInit,const tString& asName,TiXmlElement *apGameElem) : iGameEntity(apInit,asName)
{
mType = eGameEntityType_Enemy;
mTriggerTypes = eGameTriggerType_Sound;
mpMover = hplNew( cCharacterMove, (mpInit) );
mlCurrentState = -1;
mbHasBeenActivated = false;
mbSetFeetAtGroundOnStart = true;
mbAttachMeshToBody = true;
mbRemoveAttackerOnDisable = true;
//States
mvStates.resize(100);
for(size_t i=0; i < mvStates.size(); ++i) mvStates[i] = NULL;
//Player find init
mvLastPlayerPos = cVector3f(0,0,0);
mbCanSeePlayer = false;
mfCanSeePlayerCount = 0;
mlPlayerInLOSCount = 0;
mlMaxPlayerInLOSCount = 3;
mfCheckForPlayerRate = 0.55f;
mfCheckForPlayerCount = cMath::RandRectf(0, mfCheckForPlayerRate);
mfDamageSoundTimer =0;
msOnDeathCallback = "";
msOnAttackCallback = "";
m_mtxStartPose = cMatrixf::Identity;
m_mtxGoalPose = cMatrixf::Identity;
mfPoseCount = 0;
//TODO: Should not be here
mpAStarAir = NULL;
mpAStarGround = NULL;
mpNodeContainerAir = NULL;
mpNodeContainerGround = NULL;
//Patroling
mlCurrentPatrolNode =0;
mfWaitTime =0;
mfWaitTimeCount =0;
mfDoorBreakCount =0;
//Default body settings
mfDisappearTime =0;
mbDisappearActive = false;
mbHasDisappeared = false;
msCloseMusic ="";
mlCloseMusicPrio =0;
msAttackMusic ="";
mlAttackMusicPrio =0;
mbShowDebug = false;
msGroundNodeType = "ground";
mvBodySize = cVector3f(0.5f,1.4f,0.5f);
mfBodyMass = 10;
mfMaxForwardSpeed = 1.0f;
mfMaxBackwardSpeed = 1.0f;
mfAcceleration = 1;
mfDeacceleration = 1;
mfMaxTurnSpeed = 8.5f;
mfAngleDistTurnMul =2.3f;
mfMinBreakAngle = cMath::ToRad(16);
mfBreakAngleMul = 1.5f;
mfSpeedMoveAnimMul = 4.7f;
mfTurnSpeedMoveAnimMul = 4.0f;
mfMaxPushMass = 10.0f;
mfPushForce = 19.0f;
mfMaxSeeDist = 10.0f;
mfMinAttackDist = 1.6f;
mfStoppedToWalkSpeed = 0.05f;
mfWalkToStoppedSpeed = 0.02f;
mfWalkToRunSpeed = 1.2f;
mfRunToWalkSpeed = 1.0f;
mfMoveAnimSpeedMul = 1.0f;
msBackwardAnim = "Backward";
msStoppedAnim = "Idle";
msWalkAnim = "Walk";
msRunAnim = "Run";
m_mtxModelOffset = cMatrixf::Identity;
mvModelOffsetAngles =0;
mfFOV = cMath::ToRad(90.0f);
mfFOVXMul = 0.7f;
//trigger init
mfTriggerUpdateCount =0;
mfTriggerUpdateRate = 1.0f/ 60.0f;
mfSkipSoundTriggerCount =0;
mpCurrentAnimation = NULL;
mbAnimationIsSpeedDependant = false;
mfAnimationSpeedMul = 1.0f;
msHitPS = "";
mbOverideMoveState = false;
mMoveState = eEnemyMoveState_LastEnum;
mbLoading = false;
mbIsAttracted = false;
mbUsesTriggers = true;
mfCalcPlayerHiddenPosCount = 0;
}
//-----------------------------------------------------------------------
iGameEnemy::~iGameEnemy(void)
{
hplDelete( mpMover );
for(size_t i=0; i < mvStates.size(); ++i)
{
if(mvStates[i]) hplDelete( mvStates[i] );
}
mvStates.clear();
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
void iGameEnemy::LoadBaseProperties(TiXmlElement *apGameElem)
{
////////////////////////////////////////
// Load settings from XML
mbShowDebug = cString::ToBool(apGameElem->Attribute("ShowDebug"),false);
mbDisappear = cString::ToBool(apGameElem->Attribute("Disappear"),false);
mfDisappearMinTime = cString::ToFloat(apGameElem->Attribute("DisappearMinTime"),0);
mfDisappearMaxTime = cString::ToFloat(apGameElem->Attribute("DisappearMaxTime"),0);
mfDisappearMinDistance = cString::ToFloat(apGameElem->Attribute("DisappearMinDistance"),0);
msDisappearPS = cString::ToString(apGameElem->Attribute("DisappearPS"),"");
msDisappearSound = cString::ToString(apGameElem->Attribute("DisappearSound"),"");
mbDisappearFreezesRagdoll = cString::ToBool(apGameElem->Attribute("DisappearFreezesRagdoll"),false);
msCloseMusic = cString::ToString(apGameElem->Attribute("CloseMusic"),"");
mlCloseMusicPrio = cString::ToInt(apGameElem->Attribute("CloseMusicPrio"),0);
mfCloseMusicStartDist = cString::ToFloat(apGameElem->Attribute("CloseMusicStartDist"),0);
mfCloseMusicStopDist = cString::ToFloat(apGameElem->Attribute("CloseMusicStopDist"),0);
msAttackMusic = cString::ToString(apGameElem->Attribute("AttackMusic"),"");
mlAttackMusicPrio = cString::ToInt(apGameElem->Attribute("AttackMusicPrio"),0);
mfFOV = cMath::ToRad(90);
mfMaxPushMass = cString::ToFloat(apGameElem->Attribute("MaxPushMass"),0);
mfPushForce = cString::ToFloat(apGameElem->Attribute("PushForce"),0);
mfMaxHealth = cString::ToFloat(apGameElem->Attribute("MaxHealth"),0);
mfHealth = mfMaxHealth;
mfMaxSeeDist = cString::ToFloat(apGameElem->Attribute("MaxSeeDist"),0);
mfMaxForwardSpeed = cString::ToFloat(apGameElem->Attribute("MaxForwardSpeed"),0);
mfAcceleration = cString::ToFloat(apGameElem->Attribute("Acceleration"),0);
mfDeacceleration = cString::ToFloat(apGameElem->Attribute("Deacceleration"),0);
mfMaxTurnSpeed = cString::ToFloat(apGameElem->Attribute("MaxTurnSpeed"),mfMaxTurnSpeed);
mfAngleDistTurnMul = cString::ToFloat(apGameElem->Attribute("AngleDistTurnMul"),mfAngleDistTurnMul);
mfMinBreakAngle = cMath::ToRad(cString::ToFloat(apGameElem->Attribute("MinBreakAngle"),mfMinBreakAngle));
mfBreakAngleMul = cString::ToFloat(apGameElem->Attribute("BreakAngleMul"),mfBreakAngleMul);
mfStoppedToWalkSpeed = cString::ToFloat(apGameElem->Attribute("StoppedToWalkSpeed"),0);
mfWalkToStoppedSpeed = cString::ToFloat(apGameElem->Attribute("WalkToStoppedSpeed"),0);
mfWalkToRunSpeed = cString::ToFloat(apGameElem->Attribute("WalkToRunSpeed"),0);
mfRunToWalkSpeed = cString::ToFloat(apGameElem->Attribute("RunToWalkSpeed"),0);
mfMoveAnimSpeedMul = cString::ToFloat(apGameElem->Attribute("MoveAnimSpeedMul"),0);
mvBodySize = cString::ToVector3f(apGameElem->Attribute("BodySize"),0);
mfBodyMass = cString::ToFloat(apGameElem->Attribute("BodyMass"),10);
cVector3f vRot = cString::ToVector3f(apGameElem->Attribute("ModelOffset_Rot"),0);
vRot = cVector3f(cMath::ToRad(vRot.x),cMath::ToRad(vRot.y),cMath::ToRad(vRot.z));
cVector3f vPos = cString::ToVector3f(apGameElem->Attribute("ModelOffset_Pos"),0);
mvModelOffsetAngles = vRot;
m_mtxModelOffset = cMath::MatrixRotate(vRot,eEulerRotationOrder_XYZ);
m_mtxModelOffset.SetTranslation(vPos);
mbAlignToGroundNormal = cString::ToBool(apGameElem->Attribute("AlignToGroundNormal"),false);
msHitPS = cString::ToString(apGameElem->Attribute("HitPS"),"");
}
//-----------------------------------------------------------------------
void iGameEnemy::OnPlayerInteract()
{
}
//-----------------------------------------------------------------------
void iGameEnemy::OnPlayerPick()
{
//SKIp this for now since this means that enemies in ragdoll can take damage.
/*mpInit->mpPlayer->mbPickAtPoint = false;
mpInit->mpPlayer->mbRotateWithPlayer = true;
mpInit->mpPlayer->mbUseNormalMass = false;
mpInit->mpPlayer->mfGrabMassMul = (float)mvBodies.size();
mpInit->mpPlayer->SetPushBody(mpInit->mpPlayer->GetPickedBody());
mpInit->mpPlayer->ChangeState(ePlayerState_Grab);*/
}
//-----------------------------------------------------------------------
void iGameEnemy::Setup(cWorld3D *apWorld)
{
/////////////////////////////////////////////
//Create body
iCharacterBody *pBody = apWorld->GetPhysicsWorld()->CreateCharacterBody("Enemy",
mvBodySize);
pBody->SetEntityOffset(m_mtxModelOffset);
pBody->SetMass(mfBodyMass);
pBody->SetMaxStepSize(0.35f);
pBody->SetStepClimbSpeed(3.35f);
pBody->SetCustomGravity(cVector3f(0,-13.0f,0));
pBody->SetEntitySmoothPosNum(10);
pBody->SetGroundFriction(10);
mpMover->SetCharBody(pBody);
SetCharBody(pBody);
SetupBody();
}
//-----------------------------------------------------------------------
void iGameEnemy::OnWorldLoad()
{
//////////////////////////////////
// Setup EnemyMove
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
mpNodeContainerGround = pWorld->CreateAINodeContainer(msEnemyType,msGroundNodeType,
mvBodySize,
false, 2 , 6 , 5.0f,0.41f);
if(mpNodeContainerGround)
mpAStarGround = pWorld->CreateAStarHandler(mpNodeContainerGround);
else
mpAStarGround = NULL;
//////////////////////////////////
//Set up the body
if(mbAttachMeshToBody && mfHealth > 0)
mpMover->GetCharBody()->SetEntity(mpMeshEntity);
mpMover->GetCharBody()->GetBody()->SetUserData(this);
mpMover->GetCharBody()->Update(0.001f);
mpMover->SetAStar(mpAStarGround);
mpMover->SetNodeContainer(mpNodeContainerGround);
//////////////////////////////////
//Stop all animations
mpMeshEntity->Stop();
mpMeshEntity->UpdateLogic(0.005f);
//////////////////////////////////
//Preload data
//Sounds
for(size_t i=0; i< mvPreloadSounds.size(); ++i)
{
mpInit->PreloadSoundEntityData(mvPreloadSounds[i]);
}
//Particle system
mpInit->PreloadParticleSystem(msHitPS);
//////////////////////////////////
//Implemented load
OnLoad();
//////////////////////////////////
//Check if dead
mbLoading = true;
if(mfHealth <= 0)
{
ChangeState(STATE_DEAD);
mpMeshEntity->SetSkeletonPhysicsCanSleep(false);
mpMeshEntity->UpdateLogic(1.0f/60.0f);
mpMeshEntity->SetSkeletonPhysicsCanSleep(true);
}
mbLoading = false;
mpMeshEntity->ResetGraphicsUpdated();
}
//-----------------------------------------------------------------------
void iGameEnemy::OnPostLoadScripts()
{
//Randomize start pos
if(IsActive() && mvPatrolNodes.size()>0 && mbHasBeenActivated)
{
int lStartNode = cMath::RandRectl(0,(int)mvPatrolNodes.size()-1);
tString sNode = mvPatrolNodes[lStartNode].msNodeName;
cAINode *pNode = mpMover->GetNodeContainer()->GetNodeFromName(sNode);
mpMover->GetCharBody()->SetFeetPosition(pNode->GetPosition());
}
else
{
mbHasBeenActivated = true;
}
}
//-----------------------------------------------------------------------
void iGameEnemy::OnWorldExit()
{
if(mfHealth <=0)
{
SetActive(false);
}
mpInit->mpMusicHandler->RemoveAttacker(this);
}
//-----------------------------------------------------------------------
float gfAngle=0;
float gfCurrentViewDist=0;
float gfCurrentMaxViewDist=0;
void iGameEnemy::OnDraw()
{
return;
if(mbActive==false) return;
if(mbCanSeePlayer){
mpInit->mpDefaultFont->Draw(cVector3f(5,15,100),14,cColor(1,1,1,1),eFontAlign_Left,
_W("Player is seen!"));
}
else {
mpInit->mpDefaultFont->Draw(cVector3f(5,15,100),14,cColor(1,1,1,1),eFontAlign_Left,
_W("Can NOT see player..."));
}
//mpInit->mpDefaultFont->Draw(cVector3f(5,29,100),14,cColor(1,1,1,1),eFontAlign_Left,
// "State: %s",mStateMachine.CurrentState()->GetName().c_str());
tWString sStateName = _W("NONE");
if(mlCurrentState >=0) sStateName = cString::To16Char(gvStateName[mlCurrentState]);
mpInit->mpDefaultFont->Draw(cVector3f(5,48,100),14,cColor(1,1,1,1),eFontAlign_Left,
_W("Health: %f State: %ls Moving: %d Stuck: %f MaxViewDist: %f"),mfHealth,
sStateName.c_str(),
mpMover->IsMoving(),
mpMover->GetStuckCounter(),
gfCurrentMaxViewDist);
mpInit->mpDefaultFont->Draw(cVector3f(5,64,100),14,cColor(1,1,1,1),eFontAlign_Left,
_W("Speed: %f"), mpMover->GetCharBody()->GetMoveSpeed(eCharDir_Forward));
mpMover->OnDraw(mpInit);
mvStates[mlCurrentState]->OnDraw();
/*mpInit->mpDefaultFont->Draw(cVector3f(5,15,100),14,cColor(1,1,1,1),eFontAlign_Left,
"Active: %d",mbActive);
mpInit->mpDefaultFont->Draw(cVector3f(5,30,100),14,cColor(1,1,1,1),eFontAlign_Left,
"Yaw: %f",cMath::ToDeg(mpMover->GetCharBody()->GetYaw()));
mpInit->mpDefaultFont->Draw(cVector3f(5,45,100),14,cColor(1,1,1,1),eFontAlign_Left,
"Pos: %s",mpMover->GetCharBody()->GetPosition().ToString().c_str());*/
}
//-----------------------------------------------------------------------
void iGameEnemy::OnPostSceneDraw()
{
if(IsActive()==false) return;
if(mbShowDebug == false) return;
iLowLevelGraphics *pLowLevelGfx = mpInit->mpGame->GetGraphics()->GetLowLevel();
mpMover->OnPostSceneDraw(pLowLevelGfx);
ExtraPostSceneDraw();
/////////////////////////////////////
// Begin debug pos
/*pLowLevelGfx->SetDepthTestActive(false);
pLowLevelGfx->SetDepthWriteActive(false);
cVector3f vNormal(0,1,0);
cVector3f vUp(0,1,0);
cVector3f vStartPos = mpMover->GetCharBody()->GetFeetPosition() + cVector3f(0,0.05f,0);
cVector3f vPosition = vStartPos;
mFindGround.GetGround(vStartPos,cVector3f(0,-1,0),NULL,&vNormal);
vNormal.Normalise();
float fAngle = cMath::Vector3Angle(vUp,vNormal);
cVector3f vRotateAxis = cMath::Vector3Cross(vUp,vNormal);
//cVector3f vRotateAxis2 = cMath::Vector3Cross(vUp,vRotateAxis);
vRotateAxis.Normalise();
cQuaternion qRotation = cQuaternion(fAngle, vRotateAxis);
cMatrixf mtxPoseRotation = cMath::MatrixQuaternion(qRotation);
cMatrixf mtxFinalOffset = cMath::MatrixMul(mtxPoseRotation,m_mtxModelOffset);
cVector3f vCenter = mpMover->GetCharBody()->GetPosition();
cVector3f vRot = cMath::MatrixMul(mtxPoseRotation,cVector3f(0,1,0));
pLowLevelGfx->DrawLine(vStartPos, vStartPos + vNormal,cColor(1,0,1,1));
pLowLevelGfx->DrawLine(vCenter, vCenter + vRotateAxis,cColor(1,0.5,0.5,1));
// pLowLevelGfx->DrawLine(vCenter, vCenter + vRotateAxis2,cColor(1,0.5,0.5,1));
pLowLevelGfx->DrawLine(vCenter, vCenter + vRot,cColor(0,1,1,1));
gfAngle =cMath::ToDeg(fAngle);
pLowLevelGfx->SetDepthTestActive(true);
pLowLevelGfx->SetDepthWriteActive(true);*/
// End debug pose
//////////////////////////////
/*pLowLevelGfx->SetDepthTestActive(false);
pLowLevelGfx->SetDepthWriteActive(false);
pLowLevelGfx->DrawSphere(mpMover->GetCharBody()->GetPosition(),0.1f,cColor(1,0.5,0));
pLowLevelGfx->DrawSphere(GetMeshEntity()->GetWorldPosition(),0.1f,cColor(0,0.5,1));
pLowLevelGfx->SetDepthTestActive(true);
pLowLevelGfx->SetDepthWriteActive(true);*/
/*for(size_t i=0; i< mvRayStartPoses.size(); ++i)
{
pLowLevelGfx->DrawLine(mvRayStartPoses[i], mvRayEndPoses[i],cColor(1,0,1,1));
pLowLevelGfx->DrawSphere(mvRayStartPoses[i],0.2f,cColor(0,1,1,1));
pLowLevelGfx->DrawSphere(mvRayEndPoses[i],0.2f,cColor(0,1,1,1));
}*/
//mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld()->RenderDebugGeometry(pLowLevelGfx,cColor(1,1));
mvStates[mlCurrentState]->OnPostSceneDraw();
}
//-----------------------------------------------------------------------
void iGameEnemy::Update(float afTimeStep)
{
if(mbActive==false) return;
START_TIMING_EX(GetName().c_str(),enemy);
if(mpMeshEntity->GetSkeletonPhysicsActive() && mpCharBody->IsActive()==false &&
mfHealth <=0)
{
mbHasInteraction = true;
}
else
{
mbHasInteraction = false;
}
START_TIMING_TAB(pose);
UpdateEnemyPose(afTimeStep);
STOP_TIMING_TAB(pose);
START_TIMING_TAB(checkforplayer);
UpdateCheckForPlayer(afTimeStep);
STOP_TIMING_TAB(checkforplayer);
START_TIMING_TAB(MoverUpdate);
mpMover->Update(afTimeStep);
STOP_TIMING_TAB(MoverUpdate);
START_TIMING_TAB(Animations);
UpdateAnimations(afTimeStep);
STOP_TIMING_TAB(Animations);
OnUpdate(afTimeStep);
#ifdef UPDATE_TIMING_ENABLED
LogUpdate("\tState: %d\n",mlCurrentState);
#endif
START_TIMING_TAB(State);
mvStates[mlCurrentState]->OnUpdate(afTimeStep);
STOP_TIMING_TAB(State);
if(mfDamageSoundTimer>0) mfDamageSoundTimer -= afTimeStep;
if(mfSkipSoundTriggerCount>0) mfSkipSoundTriggerCount -= afTimeStep;
if(mfDoorBreakCount>0) mfDoorBreakCount -= afTimeStep;
//////////////////////////////////////////////
//Disappear
if(mbDisappear && GetHealth()<=0 && mbHasDisappeared==false)
{
if(mbDisappearActive)
{
if(mfDisappearTime <= 0)
{
mbHasDisappeared = true;
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
cVector3f vPostion = mpMeshEntity->GetBoundingVolume()->GetWorldCenter();
if(msDisappearSound!="")
{
cSoundEntity *pSound = pWorld->CreateSoundEntity("Disappear",msDisappearSound,true);
if(pSound) pSound->SetPosition(vPostion);
}
if(msDisappearPS!="")
{
pWorld->CreateParticleSystem("Disappear",msDisappearPS,cVector3f(1,1,1),
cMath::MatrixTranslate(vPostion));
}
if(mbDisappearFreezesRagdoll)
{
mpMeshEntity->ResetGraphicsUpdated();
for(int i=0; i< mpMeshEntity->GetBoneStateNum(); ++i)
{
cBoneState *pBone = mpMeshEntity->GetBoneState(i);
iPhysicsBody *pBody = pBone->GetBody();
if(pBody)
{
pBody->SetMass(0);
}
}
}
else
{
SetActive(false);
}
}
else
{
mfDisappearTime -= afTimeStep;
}
}
else
{
mbDisappearActive = true;
mfDisappearTime = cMath::RandRectf(mfDisappearMinTime,mfDisappearMaxTime);
}
}
//////////////////////////////////////////////
//Outside of map
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
cBoundingVolume worldBV;
worldBV.SetLocalMinMax(pPhysicsWorld->GetWorldSizeMin(),pPhysicsWorld->GetWorldSizeMax());
if(cMath::CheckCollisionBV(worldBV, *mpMover->GetCharBody()->GetBody()->GetBV())==false)
{
SetHealth(0);
SetActive(false);
}
STOP_TIMING(enemy);
}
//-----------------------------------------------------------------------
void iGameEnemy::AddState(iGameEnemyState *apState)
{
mvStates[apState->GetId()] = apState;
}
void iGameEnemy::ChangeState(int alId)
{
if(mlCurrentState == alId) return;
/*char sStr[512];
tString sStateName1 = "NONE";
if(mlCurrentState >=0) sStateName1 = gvStateName[mlCurrentState];
tString sStateName2 = "NONE";
if(mlCurrentState >=0) sStateName2 = gvStateName[alId];
sprintf(sStr,"%s State %s -> %s",msName.c_str(),sStateName1.c_str(),sStateName2.c_str());
//mpInit->mpEffectHandler->GetSubTitle()->Add(cString::To16Char(sStr),1.2f,false);
Log("%s\n",sStr);*/
//Log("Leave old...");
if(mlCurrentState>=0)
mvStates[mlCurrentState]->OnLeaveState(mvStates[alId]);
int lPrevState = mlCurrentState;
iGameEnemyState *pPrevState = NULL;
if(mlCurrentState>=0) pPrevState = mvStates[mlCurrentState];
mlCurrentState = alId;
mbCanSeePlayer = false;
//Log("enter newer\n");
mvStates[mlCurrentState]->SetPreviousState(lPrevState);
mvStates[mlCurrentState]->OnEnterState(pPrevState);
}
iGameEnemyState *iGameEnemy::GetState(int alId)
{
return mvStates[mlCurrentState];
}
//-----------------------------------------------------------------------
bool iGameEnemy::HandleTrigger(cGameTrigger *apTrigger)
{
switch(apTrigger->GetType())
{
case eGameTriggerType_Sound: return HandleSoundTrigger(apTrigger);
}
return true;
}
//-----------------------------------------------------------------------
bool iGameEnemy::HandleSoundTrigger(cGameTrigger *apTrigger)
{
if(mfSkipSoundTriggerCount>0) return false;
cGameTrigger_Sound *pSoundTrigger = static_cast(apTrigger);
//////////////////////////////////////
//Calculate volume of sound
float fDistance = cMath::Vector3Dist(GetPosition(), pSoundTrigger->GetWorldPosition());
float fMin = pSoundTrigger->mpSound->GetMinDistance();
float fMax = pSoundTrigger->mpSound->GetMaxDistance();
float fHearVolume = 1.0f - cMath::Clamp( (fDistance - fMin)/(fMax - fMin), 0.0f ,1.0f);
fHearVolume *= pSoundTrigger->mpSound->GetVolume();
//If not audible return
if(fHearVolume <=0) return false;
return mvStates[mlCurrentState]->OnHearNoise(pSoundTrigger->GetWorldPosition(),fHearVolume);
return true;
}
//-----------------------------------------------------------------------
cVector3f iGameEnemy::GetPosition()
{
return mpMover->GetCharBody()->GetPosition();
}
//-----------------------------------------------------------------------
void iGameEnemy::PlayAnim( const tString &asName, bool abLoop, float afFadeTime,
bool abDependsOnSpeed, float afSpeedMul,
bool abSyncWithPrevFrame,
bool abOverideMoveState)
{
//Check if the animation is already playing.
if( mpCurrentAnimation != NULL &&
mpCurrentAnimation->GetName() == asName &&
mpCurrentAnimation->IsActive() &&
mpCurrentAnimation->IsOver()== false)
{
return;
}
cAnimationState *pNewAnim = mpMeshEntity->GetAnimationStateFromName(asName);
if(pNewAnim==NULL){
//Warning("Animation '%s' does not exist!\n",asName.c_str());
return;
}
pNewAnim->SetActive(true);
if(mpCurrentAnimation && mpCurrentAnimation != pNewAnim)
{
mpCurrentAnimation->FadeOut(afFadeTime);
if(pNewAnim->IsFading()==false) pNewAnim->SetWeight(0);
pNewAnim->FadeIn(afFadeTime);
}
else
{
pNewAnim->SetWeight(1.0f);
}
pNewAnim->SetLoop(abLoop);
/////////////////////////////////////////
//Check if this animation should start at the same place as the previous
if(abSyncWithPrevFrame && mpCurrentAnimation)
{
pNewAnim->SetRelativeTimePosition(mpCurrentAnimation->GetRelativeTimePosition());
}
else
{
pNewAnim->SetTimePosition(0);
}
mpCurrentAnimation = pNewAnim;
mbAnimationIsSpeedDependant = abDependsOnSpeed;
mfAnimationSpeedMul = afSpeedMul;
mbOverideMoveState = abOverideMoveState;
}
//-----------------------------------------------------------------------
void iGameEnemy::UseMoveStateAnimations()
{
if(mbOverideMoveState)
{
mbOverideMoveState = false;
mMoveState = eEnemyMoveState_LastEnum;
}
}
//-----------------------------------------------------------------------
void iGameEnemy::PlaySound(const tString &asName)
{
if(asName == "") return;
cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
cSoundEntity *pSound = pWorld->CreateSoundEntity("Enemy",asName,true);
if(pSound)
{
pSound->SetPosition(mpMover->GetCharBody()->GetPosition());
//TODO: Attach instead...
}
else
{
Warning("Couldn't play sound '%s'\n",asName.c_str());
}
}
//-----------------------------------------------------------------------
void iGameEnemy::AddPatrolNode(const tString& asNode, float afTime, const tString& asAnimation)
{
mvPatrolNodes.push_back(cEnemyPatrolNode(asNode,afTime,asAnimation));
}
//-----------------------------------------------------------------------
void iGameEnemy::ClearPatrolNodes()
{
mvPatrolNodes.clear();
mlCurrentPatrolNode =0;
if(mbActive && mfHealth >0) ChangeState(STATE_IDLE);
}
//-----------------------------------------------------------------------
void iGameEnemy::OnDeath(float afX)
{
//PlaySound("temp_roach_death");
if(msOnDeathCallback != "")
{
tString sCommand = msOnDeathCallback + "(\""+msName+"\")";
msOnDeathCallback ="";
mpInit->RunScriptCommand(sCommand);
}
mvStates[mlCurrentState]->OnDeath(afX);
}
//-----------------------------------------------------------------------
void iGameEnemy::OnDamage(float afX)
{
if(mfDamageSoundTimer<=0)
{
//PlaySound("temp_roach_damage");
mfDamageSoundTimer =0.8f;
}
mvStates[mlCurrentState]->OnTakeHit(afX);
}
//-----------------------------------------------------------------------
void iGameEnemy::OnFlashlight(const cVector3f &avPos)
{
mvStates[mlCurrentState]->OnFlashlight(avPos);
}
//-----------------------------------------------------------------------
void iGameEnemy::OnSetActive(bool abX)
{
//This will do for now:
for(size_t i=0; iSetActive(false);
}
//Make sure it is on the ground
if(mfHealth >0 && mbSetFeetAtGroundOnStart)
{
cVector3f vGroundPosition = mpMover->GetCharBody()->GetFeetPosition();
mFindGround.GetGround(mpMover->GetCharBody()->GetPosition(),cVector3f(0,-1,0),
&vGroundPosition,NULL);
mpMover->GetCharBody()->SetFeetPosition(vGroundPosition);
}
if(mbActive==false)
{
if(mbRemoveAttackerOnDisable)
mpInit->mpMusicHandler->RemoveAttacker(this);
if(mfHealth >0) ChangeState(STATE_IDLE);
}
else
{
mbHasBeenActivated = true;
}
}
//-----------------------------------------------------------------------
bool iGameEnemy::CanSeePlayer()
{
if(mpInit->mpMapHandler->IsPreUpdating() || mpInit->mpPlayer->IsDead()) return false;
return mbCanSeePlayer;
}
//-----------------------------------------------------------------------
bool iGameEnemy::CheckForDoor()
{
iCharacterBody *pBody = mpMover->GetCharBody();
float fRadius = pBody->GetSize().x/2.0f - 0.1f;
cVector3f vStart = pBody->GetPosition() + pBody->GetForward() * fRadius;
cVector3f vEnd = vStart + pBody->GetForward() * 0.4f;
bool bRet = mDoorCheck.CheckDoor(vStart,vEnd);
Log("CheckDoor: %d\n",bRet);
return bRet;
}
bool iGameEnemy::CheckForTeamMate(float afMaxDist, bool abCheckIfFighting)
{
cVector3f vPosition = mpMover->GetCharBody()->GetFeetPosition();
tGameEnemyIterator it = mpInit->mpMapHandler->GetGameEnemyIterator();
while(it.HasNext())
{
iGameEnemy *pEnemy = it.Next();
if(GetEnemyType() != pEnemy->GetEnemyType()) continue;
if(pEnemy == this || pEnemy->IsActive()==false || pEnemy->GetHealth()<=0) continue;
if(abCheckIfFighting && pEnemy->IsFighting()==false) continue;
float fDist = cMath::Vector3Dist( pEnemy->GetMover()->GetCharBody()->GetPosition(),
vPosition);
if(fDist <= afMaxDist)
{
return true;
}
}
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PROTECTED METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void iGameEnemy::UpdateEnemyPose(float afTimeStep)
{
if(mbAlignToGroundNormal==false) return;
if(mfPoseCount ==0)
{
m_mtxStartPose = m_mtxGoalPose;
cVector3f vNormal(0,1,0);
cVector3f vStartPos = mpMover->GetCharBody()->GetFeetPosition() + cVector3f(0,0.05f,0);
cVector3f vPosition = vStartPos;
mFindGround.GetGround(vStartPos,cVector3f(0,-1,0),&vPosition,&vNormal);
cVector3f vUp(0,1,0);
float fDist = vStartPos.y - vPosition.y;
vNormal.Normalise();
float fAngle = cMath::Vector3Angle(vUp,vNormal);
cVector3f vRotateAxis = cMath::Vector3Cross(vUp,vNormal);
//cVector3f vRotateAxis = cMath::Vector3Cross(vUp,mpMover->GetCharBody()->GetForward());
//cVector3f vRotateAxis2 = cMath::Vector3Cross(vUp,vRotateAxis);
vRotateAxis.Normalise();
cQuaternion qRotation = cQuaternion(fAngle, vRotateAxis);
cMatrixf mtxPoseRotation = cMath::MatrixQuaternion(qRotation);
//cVector3f vDelta = vPosition - mpMover->GetCharBody()->GetFeetPosition();
//mtxPoseRotation.SetTranslation(cVector3f(0,-fabs(vDelta.y),0));
//mtxPoseRotation.SetTranslation(vNormal * -fabs(vDelta.y));
if(vNormal != cVector3f(0,1,0))
{
mFindGround.GetGround(mpMover->GetCharBody()->GetPosition(),vNormal *-1.0f,&vPosition,NULL);
fDist = cMath::Vector3Dist(mpMover->GetCharBody()->GetPosition(),vPosition);
//So there is no warp to the ground.
float fLimit = mpMover->GetCharBody()->GetSize().y*0.82f;
if(fDist > fLimit) vNormal = cVector3f(0,1,0);
}
///////////////////////////
//Get the offest
if(vNormal != cVector3f(0,1,0))
{
fDist -= mpMover->GetCharBody()->GetSize().y/2.0f;
mtxPoseRotation.SetTranslation(vNormal * -fDist);
}
else
{
mtxPoseRotation.SetTranslation(0);
}
m_mtxGoalPose = mtxPoseRotation;
}
else
{
cMatrixf mtxPoseRotation =cMath::MatrixSlerp(mfPoseCount,m_mtxStartPose,m_mtxGoalPose,true);
mpMover->GetCharBody()->SetEntityPostOffset(mtxPoseRotation);
}
mfPoseCount+= 6.5f *afTimeStep;
if(mfPoseCount >1.0f) mfPoseCount =0;
}
//-----------------------------------------------------------------------
void iGameEnemy::UpdateCheckForPlayer(float afTimeStep)
{
//Do not check for player at pre update.
if(mpInit->mpMapHandler->IsPreUpdating() ||
mpInit->mpPlayer->IsDead() ||
mbUsesTriggers == false ||
mfHealth <= 0)
{
mbCanSeePlayer = false;
return;
}
/*if(mfCanSeePlayerCount>0)
{
mfCanSeePlayerCount -= afTimeStep;
if(mfCanSeePlayerCount<=0) mbCanSeePlayer = false;
}*/
if(mfCalcPlayerHiddenPosCount >0)
mfCalcPlayerHiddenPosCount -= afTimeStep;
//Check if it is time to check for player.
if(mfCheckForPlayerCount < mfCheckForPlayerRate)
{
mfCheckForPlayerCount += afTimeStep;
return;
}
mfCheckForPlayerCount =0;
iCharacterBody *pPlayerBody = mpInit->mpPlayer->GetCharacterBody();
float fDist = cMath::Vector3Dist(mpMover->GetCharBody()->GetPosition(),pPlayerBody->GetPosition());
float fMinLength = mpMover->GetCharBody()->GetBody()->GetBV()->GetRadius() +
pPlayerBody->GetBody()->GetBV()->GetRadius();
//Lower some stuff if player is hidden
float fStartFOV = mfFOV;
float fStartMaxSeeDist = mfMaxSeeDist;
if(mbCanSeePlayer==false && fDist >1.3f) //1.3 = really close, remove all handicap.
{
if(mpInit->mDifficulty == eGameDifficulty_Easy){
mfFOV *= 0.6f;
mfMaxSeeDist *= 0.6f;
}
if(mpInit->mpPlayer->GetHidden()->IsHidden())
{
mfFOV *= 0.36f;
mfMaxSeeDist *= 0.25f;
}
else if(mpInit->mpPlayer->GetHidden()->InShadows())
{
if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
{
mfFOV *= 0.6f;
mfMaxSeeDist *= 0.65f;
}
else
{
mfFOV *= 0.8f;
mfMaxSeeDist *= 0.85f;
}
}
}
gfCurrentViewDist = fDist;
gfCurrentMaxViewDist = mfMaxSeeDist;
if( (fDist <= mfMaxSeeDist && LineOfSight(pPlayerBody->GetPosition(), pPlayerBody->GetSize())) ||
fDist <= fMinLength)
{
//Increase LOS counter,
mlPlayerInLOSCount++;
//Player must have been in LOS mlMaxPlayerInLOSCount times before it is considered seen.
if(mlPlayerInLOSCount>= mlMaxPlayerInLOSCount)
{
mlPlayerInLOSCount = mlMaxPlayerInLOSCount;
float fChance=0;
if(fDist > mfMaxSeeDist)
fChance =0;
else
fChance = 1 - (fDist / mfMaxSeeDist);
if(mbCanSeePlayer==false)
{
mvStates[mlCurrentState]->OnSeePlayer(pPlayerBody->GetPosition(),fChance);
mpInit->mpPlayer->GetHidden()->UnHide();
}
mvLastPlayerPos = pPlayerBody->GetFeetPosition();
mbCanSeePlayer = true;
mfCanSeePlayerCount = 1.0f/ 3.0f;
mfCalcPlayerHiddenPosCount = 1.5f;
}
}
else
{
//Reset LOS counter,
mlPlayerInLOSCount--;
if(mlPlayerInLOSCount<0)mlPlayerInLOSCount=0;
//this is so that the enemy get a little better last pos
//and thus improving path finding.
if(mfCalcPlayerHiddenPosCount >0)
{
mvLastPlayerPos = pPlayerBody->GetFeetPosition();
}
mbCanSeePlayer = false;
}
mfFOV = fStartFOV;
mfMaxSeeDist = fStartMaxSeeDist;
}
//-----------------------------------------------------------------------
void iGameEnemy::UpdateAnimations(float afTimeStep)
{
iCharacterBody *pBody = mpMover->GetCharBody();
float fMoveSpeed = pBody->GetMoveSpeed(eCharDir_Forward);
float fSpeed = pBody->GetVelocity(afTimeStep).Length();
if(fMoveSpeed <0) fSpeed = -fSpeed;
float fTurnSpeed = mpMover->GetTurnSpeed();
////////////////////////////////
// Override animation
if(mbOverideMoveState && mpCurrentAnimation!=NULL)
{
if(mpCurrentAnimation->IsOver())
{
mvStates[mlCurrentState]->OnAnimationOver(mpCurrentAnimation->GetName());
}
if(mbAnimationIsSpeedDependant)
{
if(std::abs(fSpeed) > 0.05f)
mpCurrentAnimation->SetSpeed(std::abs(fSpeed) * mfAnimationSpeedMul);
else
mpCurrentAnimation->SetSpeed(std::abs(fTurnSpeed) * mfAnimationSpeedMul * 2);
}
}
////////////////////////////////
// Move state animation
else
{
eEnemyMoveState prevMoveState = mMoveState;
switch(mMoveState)
{
// Backward
case eEnemyMoveState_Backward:
if(fSpeed >= 0)
mMoveState = eEnemyMoveState_Stopped;
break;
// Stopped State
case eEnemyMoveState_Stopped:
if(fSpeed < -0.05f)
mMoveState = eEnemyMoveState_Backward;
else if(fSpeed >= mfStoppedToWalkSpeed)
mMoveState = eEnemyMoveState_Walking;
else if(std::abs(fTurnSpeed) > 0.07f)
mMoveState = eEnemyMoveState_Walking;
break;
// Walking State
case eEnemyMoveState_Walking:
if(fSpeed >= mfWalkToRunSpeed)
mMoveState = eEnemyMoveState_Running;
else if(fSpeed <= mfWalkToStoppedSpeed)
{
if(std::abs(fTurnSpeed) < 0.03f) mMoveState = eEnemyMoveState_Stopped;
}
break;
// Running State
case eEnemyMoveState_Running:
if(fSpeed <= mfRunToWalkSpeed)
mMoveState = eEnemyMoveState_Walking;
break;
// NULL
case eEnemyMoveState_LastEnum:
mMoveState = eEnemyMoveState_Stopped;
break;
}
//////////////////////////////////////////////
//If move state has changed, change animation
if(prevMoveState != mMoveState)
{
//Backward
if(mMoveState == eEnemyMoveState_Backward)
{
PlayAnim(msBackwardAnim,true,0.4f,true,mfMoveAnimSpeedMul,false,false);
}
//Stopped
else if(mMoveState == eEnemyMoveState_Stopped)
{
PlayAnim(msStoppedAnim,true,0.7f,false,1.0f,false,false);
}
//Walking
else if(mMoveState == eEnemyMoveState_Walking)
{
bool bSync = prevMoveState == eEnemyMoveState_Running ? true : false;
PlayAnim(msWalkAnim,true,0.2f,true,mfMoveAnimSpeedMul,bSync,false);
}
//Running
else if(mMoveState == eEnemyMoveState_Running)
{
bool bSync = prevMoveState == eEnemyMoveState_Walking ? true : false;
PlayAnim(msRunAnim,true,0.2f,true,mfMoveAnimSpeedMul,bSync,false);
}
}
/////////////////////////////////
//Update animation speed
if(mbAnimationIsSpeedDependant)
{
if(std::abs(fSpeed) > 0.05f)
mpCurrentAnimation->SetSpeed(std::abs(fSpeed) * mfMoveAnimSpeedMul);
else
mpCurrentAnimation->SetSpeed(std::abs(fTurnSpeed) * mfMoveAnimSpeedMul * 2);
}
}
}
//-----------------------------------------------------------------------
void iGameEnemy::SetupBody()
{
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward,mfMaxForwardSpeed);
mpMover->GetCharBody()->SetMaxNegativeMoveSpeed(eCharDir_Forward,-mfMaxBackwardSpeed);
mpMover->GetCharBody()->SetMoveAcc(eCharDir_Forward, mfAcceleration);
mpMover->GetCharBody()->SetMaxPushMass(mfMaxPushMass);
mpMover->GetCharBody()->SetPushForce(mfPushForce);
mpMover->SetMaxTurnSpeed(mfMaxTurnSpeed);
mpMover->SetAngleDistTurnMul(mfAngleDistTurnMul);
mpMover->SetMinBreakAngle(mfMinBreakAngle);
mpMover->SetBreakAngleMul(mfBreakAngleMul);
mpMover->SetMaxPushMass(mfMaxPushMass);
}
//-----------------------------------------------------------------------
static const cVector2f gvPosAdds[] = {cVector2f(0,0),
cVector2f(1,0),
cVector2f(-1,0),
cVector2f(0,1),
cVector2f(0,-1)
};
bool iGameEnemy::LineOfSight(const cVector3f &avPos, const cVector3f &avSize)
{
//Setup debug
//if(mvRayStartPoses.size()<5) mvRayStartPoses.resize(5);
//if(mvRayEndPoses.size()<5) mvRayEndPoses.resize(5);
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
cVector3f vStartCenter = mpMover->GetCharBody()->GetPosition();
cVector3f vEndCenter = avPos;
/////////////////////////////
//Calculate the right vector
const cVector3f vForward = cMath::Vector3Normalize(vEndCenter - vStartCenter);
const cVector3f vUp = cVector3f(0,1.0f,0);
const cVector3f vRight = cMath::Vector3Cross(vForward, vUp);
////////////////////////////////////
//Check if the pos is within FOV
if(mfFOV < k2Pif)
{
cVector3f vEnemyForward = mpMover->GetCharBody()->GetForward();
//float fAngle = cMath::Vector3Angle(vEnemyForward, vForward);
//if(fAngle > mfFOV*0.5f) return false;
cVector3f vToPlayerAngle = cMath::GetAngleFromPoints3D(0,vForward);
cVector3f vEnemyAngle = cMath::GetAngleFromPoints3D(0,vEnemyForward);
float fAngleX = cMath::Abs(cMath::GetAngleDistanceRad(vToPlayerAngle.x,vEnemyAngle.x));
float fAngleY = cMath::Abs(cMath::GetAngleDistanceRad(vToPlayerAngle.y,vEnemyAngle.y));
//Log("X:%f Y:%f\n",cMath::ToDeg(fAngleX), cMath::ToDeg(fAngleY));
if(fAngleY > mfFOV*0.5f) return false;
if(fAngleX > mfFOV*mfFOVXMul*0.5f) return false;
}
//Get the half with and height. Make them a little smaller so that player can slide over funk on floor.
const float fHalfWidth = avSize.x * 0.4f;
const float fHalfHeight = avSize.y * 0.4f;
//Count of 2 is need for a line of sight sucess.
int lCount=0;
//Iterate through all the rays.
for(int i=0; i< 5; ++i)
{
cVector3f vAdd = vRight * (gvPosAdds[i].x*fHalfWidth) + vUp * (gvPosAdds[i].y*fHalfHeight);
cVector3f vStart = vStartCenter + vAdd;
cVector3f vEnd = vEndCenter + vAdd;
//mvRayStartPoses[i] = vStart;
//mvRayEndPoses[i] =vEnd;
mRayCallback.Reset();
pPhysicsWorld->CastRay(&mRayCallback,vStart,vEnd,false,false,false);
if(mRayCallback.Intersected()==false) lCount++;
if(lCount==2) return true;
}
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// LOADER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cEntityLoader_GameEnemy::cEntityLoader_GameEnemy(const tString &asName, cInit *apInit)
: cEntityLoader_Object(asName)
{
mpInit = apInit;
}
cEntityLoader_GameEnemy::~cEntityLoader_GameEnemy()
{
}
//-----------------------------------------------------------------------
void cEntityLoader_GameEnemy::BeforeLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
cWorld3D *apWorld)
{
}
//-----------------------------------------------------------------------
void cEntityLoader_GameEnemy::AfterLoad(TiXmlElement *apRootElem, const cMatrixf &a_mtxTransform,
cWorld3D *apWorld)
{
iGameEnemy *pEnemy = NULL;
tString sSubtype="";
tString sName="";
///////////////////////////////////
// Load game properties
TiXmlElement *pMainElem = apRootElem->FirstChildElement("MAIN");
if(pMainElem)
{
sSubtype = cString::ToString(pMainElem->Attribute("Subtype"),"");
sName = cString::ToString(pMainElem->Attribute("Name"),"");
}
else
{
Error("Couldn't find main element for entity '%s'\n",mpEntity->GetName().c_str());
}
///////////////////////////////////
// Load the enemy type
TiXmlElement *pGameElem = apRootElem->FirstChildElement("GAME");
if(sSubtype == "Dog")
{
pEnemy = hplNew( cGameEnemy_Dog, (mpInit,mpEntity->GetName(),pGameElem) );
}
#ifndef DEMO_VERSION
else if(sSubtype == "Spider")
{
pEnemy = hplNew( cGameEnemy_Spider, (mpInit,mpEntity->GetName(),pGameElem) );
}
else if(sSubtype == "Worm")
{
pEnemy = hplNew( cGameEnemy_Worm, (mpInit,mpEntity->GetName(),pGameElem) );
}
#endif
pEnemy->msSubType = sSubtype;
pEnemy->msEnemyType = msName;
pEnemy->msFileName = msFileName;
pEnemy->m_mtxOnLoadTransform = a_mtxTransform;
//Do stuff that is not done when loading from savegame.
pEnemy->SetMeshEntity(mpEntity);
pEnemy->SetBodies(mvBodies);
pEnemy->Setup(apWorld);
/////////////////////////////////
// Add to map handler
mpInit->mpMapHandler->AddGameEntity(pEnemy);
mpInit->mpMapHandler->AddGameEnemy(pEnemy);
iCharacterBody *pBody = pEnemy->mpMover->GetCharBody();
pBody->SetPosition(mpEntity->GetWorldPosition() + cVector3f(0,pBody->GetSize().y/2,0));
//Set the correct heading
cMatrixf mtxInv = cMath::MatrixInverse(mpEntity->GetWorldMatrix());
cVector3f vBodyRotation = cMath::GetAngleFromPoints3D(cVector3f(0,0,0), mtxInv.GetForward()*-1);
pBody->SetYaw(vBodyRotation.y);
//Log("Loaded enemy!\n");
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SAVE OBJECT STUFF
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
kBeginSerializeBase(cEnemyPatrolNode)
kSerializeVar(msNodeName, eSerializeType_String)
kSerializeVar(mfWaitTime, eSerializeType_Float32)
kSerializeVar(msAnimation, eSerializeType_String)
kEndSerialize()
kBeginSerialize(iGameEnemy_SaveData,iGameEntity_SaveData)
kSerializeVar(mbHasBeenActivated, eSerializeType_Bool)
kSerializeVar(mvCharBodyPosition, eSerializeType_Vector3f)
kSerializeVar(mvCharBodyRotation, eSerializeType_Vector3f)
kSerializeVar(mlCurrentPatrolNode, eSerializeType_Int32)
kSerializeVar(mfDisappearTime, eSerializeType_Float32)
kSerializeVar(mbDisappearActive, eSerializeType_Bool)
kSerializeVar(mbHasDisappeared, eSerializeType_Bool)
kSerializeVar(mbUsesTriggers, eSerializeType_Bool)
kSerializeVar(mvLastPlayerPos, eSerializeType_Vector3f)
kSerializeVar(msOnDeathCallback, eSerializeType_String)
kSerializeVar(msOnAttackCallback, eSerializeType_String)
kSerializeClassContainer(mvPatrolNodes,cEnemyPatrolNode,eSerializeType_Class)
kEndSerialize()
//-----------------------------------------------------------------------
iGameEntity* iGameEnemy_SaveData::CreateEntity()
{
return NULL;
}
//-----------------------------------------------------------------------
iGameEntity_SaveData* iGameEnemy::CreateSaveData()
{
return hplNew( iGameEnemy_SaveData, () );
}
//-----------------------------------------------------------------------
void iGameEnemy::SaveToSaveData(iGameEntity_SaveData *apSaveData)
{
__super::SaveToSaveData(apSaveData);
iGameEnemy_SaveData *pData = static_cast(apSaveData);
kCopyToVar(pData,mbHasBeenActivated);
pData->mvCharBodyPosition = mpMover->GetCharBody()->GetPosition();
pData->mvCharBodyRotation.x = mpMover->GetCharBody()->GetPitch();
pData->mvCharBodyRotation.y = mpMover->GetCharBody()->GetYaw();
kCopyToVar(pData,mlCurrentPatrolNode);
kCopyToVar(pData,mvLastPlayerPos);
kCopyToVar(pData,msOnDeathCallback);
kCopyToVar(pData,msOnAttackCallback);
kCopyToVar(pData,mfDisappearTime);
kCopyToVar(pData,mbDisappearActive);
kCopyToVar(pData,mbHasDisappeared);
kCopyToVar(pData,mbUsesTriggers);
pData->mvPatrolNodes.Resize(mvPatrolNodes.size());
for(size_t i=0; i< mvPatrolNodes.size(); ++i)
{
pData->mvPatrolNodes[i].msNodeName = mvPatrolNodes[i].msNodeName;
pData->mvPatrolNodes[i].mfWaitTime = mvPatrolNodes[i].mfWaitTime;
pData->mvPatrolNodes[i].msAnimation = mvPatrolNodes[i].msAnimation;
}
}
//-----------------------------------------------------------------------
void iGameEnemy::LoadFromSaveData(iGameEntity_SaveData *apSaveData)
{
__super::LoadFromSaveData(apSaveData);
iGameEnemy_SaveData *pData = static_cast(apSaveData);
kCopyFromVar(pData,mbHasBeenActivated);
mpMover->GetCharBody()->SetPosition(pData->mvCharBodyPosition);
mpMover->GetCharBody()->SetPitch(pData->mvCharBodyRotation.x);
mpMover->GetCharBody()->SetYaw(pData->mvCharBodyRotation.y);
mpMover->GetCharBody()->UpdateMoveMarix();
kCopyFromVar(pData,mlCurrentPatrolNode);
kCopyFromVar(pData,mvLastPlayerPos);
kCopyFromVar(pData,msOnDeathCallback);
kCopyFromVar(pData,msOnAttackCallback);
kCopyFromVar(pData,mfDisappearTime);
kCopyFromVar(pData,mbDisappearActive);
kCopyFromVar(pData,mbHasDisappeared);
kCopyFromVar(pData,mbUsesTriggers);
mvPatrolNodes.resize(pData->mvPatrolNodes.Size());
for(size_t i=0; i< mvPatrolNodes.size(); ++i)
{
mvPatrolNodes[i].msNodeName = pData->mvPatrolNodes[i].msNodeName;
mvPatrolNodes[i].mfWaitTime = pData->mvPatrolNodes[i].mfWaitTime;
mvPatrolNodes[i].msAnimation = pData->mvPatrolNodes[i].msAnimation;
}
//Log("Load Save Data!\n");
}
//-----------------------------------------------------------------------