mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1814 lines
59 KiB
C++
1814 lines
59 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2006-2010 - Frictional Games
|
|
*
|
|
* This file is part of Penumbra Overture.
|
|
*/
|
|
|
|
#include "hpl1/penumbra-overture/GameEnemy_Dog.h"
|
|
|
|
#include "hpl1/penumbra-overture/AttackHandler.h"
|
|
#include "hpl1/penumbra-overture/EffectHandler.h"
|
|
#include "hpl1/penumbra-overture/GameMusicHandler.h"
|
|
#include "hpl1/penumbra-overture/GameSwingDoor.h"
|
|
#include "hpl1/penumbra-overture/MapHandler.h"
|
|
#include "hpl1/penumbra-overture/Player.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// BASE STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
iGameEnemyState_Dog_Base::iGameEnemyState_Dog_Base(int alId, cInit *apInit, iGameEnemy *apEnemy)
|
|
: iGameEnemyState(alId, apInit, apEnemy) {
|
|
mpEnemyDog = static_cast<cGameEnemy_Dog *>(mpEnemy);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void iGameEnemyState_Dog_Base::OnSeePlayer(const cVector3f &avPosition, float afChance) {
|
|
// return;
|
|
if (mpPlayer->GetHealth() <= 0)
|
|
return;
|
|
|
|
if (afChance >= mpEnemyDog->mfIdleMinSeeChance) {
|
|
/*if( (mlId == STATE_IDLE || mlId == STATE_INVESTIGATE || mlId == STATE_PATROL) &&
|
|
cMath::RandRectf(0,1) < mpEnemyDog->mfIdleCallBackupChance &&
|
|
mpEnemy->CheckForTeamMate(8,true)==false)
|
|
{
|
|
mpEnemy->ChangeState(STATE_CALLBACKUP);
|
|
}
|
|
else
|
|
{
|
|
//mpEnemy->ChangeState(STATE_HUNT);
|
|
//mpEnemyDog->PlaySound(mpEnemyDog->msIdleFoundPlayerSound);
|
|
}*/
|
|
|
|
float fDist = cMath::Vector3Dist(mpMover->GetCharBody()->GetFeetPosition(),
|
|
mpPlayer->GetCharacterBody()->GetFeetPosition());
|
|
if (fDist >= mpEnemyDog->mfAttentionMinDist) {
|
|
mpEnemy->ChangeState(STATE_ATTENTION);
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msIdleFoundPlayerSound);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool iGameEnemyState_Dog_Base::OnHearNoise(const cVector3f &avPosition, float afVolume) {
|
|
// return false;
|
|
float afDistance = (mpMover->GetCharBody()->GetPosition() - avPosition).Length();
|
|
|
|
if (afVolume >= mpEnemyDog->mfIdleMinHearVolume && afDistance > 0.4f) {
|
|
mpEnemy->SetTempPosition(avPosition);
|
|
mpEnemy->ChangeState(STATE_INVESTIGATE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void iGameEnemyState_Dog_Base::OnTakeHit(float afDamage) {
|
|
if (afDamage >= mpEnemyDog->mfMinKnockDamage) {
|
|
if (mpInit->mbWeaponAttacking) {
|
|
float fChance = afDamage / mpEnemyDog->mfCertainKnockDamage; //(mpEnemyDog->mfCertainKnockDamage*4);
|
|
if (fChance > cMath::RandRectf(0, 1)) {
|
|
mpEnemy->ChangeState(STATE_KNOCKDOWN);
|
|
}
|
|
} else {
|
|
if (afDamage >= mpEnemyDog->mfCertainKnockDamage) {
|
|
mpEnemy->ChangeState(STATE_KNOCKDOWN);
|
|
} else {
|
|
float fChance = afDamage / mpEnemyDog->mfCertainKnockDamage;
|
|
if (fChance > cMath::RandRectf(0, 1)) {
|
|
mpEnemy->ChangeState(STATE_KNOCKDOWN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void iGameEnemyState_Dog_Base::OnFlashlight(const cVector3f &avPosition) {
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add("Flashlight!",0.5f,true);
|
|
// OnSeePlayer(mpPlayer->GetCharacterBody()->GetFeetPosition(),1.0f);
|
|
|
|
// mpEnemy->SetLastPlayerPos(mpPlayer->GetCharacterBody()->GetFeetPosition());
|
|
// mpEnemy->ChangeState(STATE_HUNT);
|
|
|
|
mpEnemy->SetTempPosition(avPosition);
|
|
mpEnemy->ChangeState(STATE_INVESTIGATE);
|
|
}
|
|
|
|
void iGameEnemyState_Dog_Base::OnDeath(float afDamage) {
|
|
mpEnemy->ChangeState(STATE_DEAD);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// IDLE STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Idle::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->UseMoveStateAnimations();
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfIdleFOV);
|
|
|
|
mpInit->mpMusicHandler->RemoveAttacker(mpEnemy);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Idle::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Idle::OnUpdate(float afTimeStep) {
|
|
if (mpEnemy->GetPatrolNodeNum() > 0) {
|
|
mpEnemy->ChangeState(STATE_PATROL);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PATROL STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Patrol::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->UseMoveStateAnimations();
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfIdleFOV);
|
|
|
|
// Setup patrol
|
|
cEnemyPatrolNode *pPatrolNode = mpEnemy->CurrentPatrolNode();
|
|
cAINode *pNode = mpMover->GetNodeContainer()->GetNodeFromName(pPatrolNode->msNodeName);
|
|
|
|
if (mpEnemy->GetDoorBreakCount() > 3.0f) {
|
|
mpEnemy->SetDoorBreakCount(0);
|
|
mpMover->SetMaxDoorToughness(0);
|
|
}
|
|
|
|
mbWaiting = false;
|
|
mbAnimation = false;
|
|
mlStuckAtMaxCount = 0;
|
|
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
mpMover->SetMaxDoorToughness(-1);
|
|
|
|
if (mpMover->MoveToPos(pNode->GetPosition()) == false) {
|
|
// tString sStr = "Could not get to path node "+pPatrolNode->msNodeName;
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add(sStr,3,true);
|
|
|
|
mpEnemy->IncCurrentPatrolNode();
|
|
mbWaiting = true;
|
|
mpEnemy->SetWaitTime(1.0f);
|
|
} else {
|
|
// tString sStr = "Moving to path node "+pPatrolNode->msNodeName;
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add(sStr,3,true);
|
|
}
|
|
|
|
mpInit->mpMusicHandler->RemoveAttacker(mpEnemy);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Patrol::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Patrol::OnUpdate(float afTimeStep) {
|
|
/////////////////////////////////
|
|
// Waiting for timer or animation to end
|
|
if (mbWaiting) {
|
|
//////////////////////////////
|
|
// Play idle sound
|
|
if (mfIdleSoundTime <= 0) {
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
mpEnemy->PlaySound(mpEnemyDog->msIdleSound);
|
|
} else {
|
|
mfIdleSoundTime -= afTimeStep;
|
|
}
|
|
|
|
//////////////////////////////
|
|
// Timer is up
|
|
if (mpEnemy->GetWaitTimeCount() >= mpEnemy->GetWaitTime()) {
|
|
if (mbAnimation == false) {
|
|
mpEnemy->SetWaitTimeCount(0);
|
|
cEnemyPatrolNode *pPatrolNode = mpEnemy->CurrentPatrolNode();
|
|
cAINode *pNode = mpMover->GetNodeContainer()->GetNodeFromName(pPatrolNode->msNodeName);
|
|
|
|
mpEnemy->UseMoveStateAnimations();
|
|
mbWaiting = false;
|
|
|
|
if (mpMover->MoveToPos(pNode->GetPosition()) == false) {
|
|
// tString sStr = "Could not get to path node "+pPatrolNode->msNodeName;
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add(sStr,3,false);
|
|
mpEnemy->IncCurrentPatrolNode();
|
|
mbWaiting = true;
|
|
mpEnemy->SetWaitTime(1.0f);
|
|
} else {
|
|
// tString sStr = "Moving to path node "+pPatrolNode->msNodeName;
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add(sStr,3,false);
|
|
}
|
|
|
|
} else {
|
|
mpEnemy->GetCurrentAnimation()->SetLoop(false);
|
|
}
|
|
} else {
|
|
mpEnemy->AddWaitTimeCount(afTimeStep);
|
|
}
|
|
}
|
|
/////////////////////////////////
|
|
// Check if path is over
|
|
else {
|
|
//////////////////////////////
|
|
// Play idle sound
|
|
if (mfIdleSoundTime <= 0) {
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
mpEnemy->PlaySound(mpEnemyDog->msIdleSound);
|
|
} else {
|
|
mfIdleSoundTime -= afTimeStep;
|
|
}
|
|
|
|
//////////////////////////////
|
|
// Stuck counter
|
|
if (mpMover->GetStuckCounter() > 1.7f) {
|
|
if (mpEnemy->CheckForDoor()) {
|
|
mpEnemy->ChangeState(STATE_BREAKDOOR);
|
|
} else {
|
|
mlStuckAtMaxCount++;
|
|
if (mlStuckAtMaxCount >= 6) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
mpEnemy->SetWaitTime(1.0f);
|
|
mpEnemy->IncCurrentPatrolNode();
|
|
}
|
|
}
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
//////////////////////////////
|
|
// Got to ned of path
|
|
if (mpMover->IsMoving() == false) {
|
|
cEnemyPatrolNode *pPatrolNode = mpEnemy->CurrentPatrolNode();
|
|
|
|
mpEnemy->SetWaitTime(pPatrolNode->mfWaitTime);
|
|
mpEnemy->IncCurrentPatrolNode();
|
|
|
|
if (pPatrolNode->msAnimation != "") {
|
|
mpEnemy->PlayAnim(pPatrolNode->msAnimation, true, 0.2f);
|
|
mbAnimation = true;
|
|
}
|
|
|
|
mbWaiting = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Patrol::OnAnimationOver(const tString &asName) {
|
|
mbAnimation = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ATTENTION STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attention::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->PlayAnim("Angry", true, 0.2f);
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfIdleFOV);
|
|
|
|
mpMover->Stop();
|
|
mpMover->TurnToPos(mpPlayer->GetCharacterBody()->GetFeetPosition());
|
|
|
|
mpEnemy->PlaySound(mpEnemyDog->msAttentionSound);
|
|
mfTime = mpEnemyDog->mfAttentionTime;
|
|
|
|
#ifndef DEMO_VERSION
|
|
if (mpInit->mDifficulty == eGameDifficulty_Easy)
|
|
mfTime *= 1.7f;
|
|
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attention::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attention::OnUpdate(float afTimeStep) {
|
|
mpMover->TurnToPos(mpPlayer->GetCharacterBody()->GetFeetPosition());
|
|
mfTime -= afTimeStep;
|
|
|
|
if (mfTime <= 0) {
|
|
if (mpEnemy->CanSeePlayer()) {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
mpEnemy->PlaySound(mpEnemyDog->msIdleFoundPlayerSound);
|
|
} else {
|
|
if (mlPreviousState == STATE_ATTENTION)
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
else
|
|
mpEnemy->ChangeState(mlPreviousState);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attention::OnSeePlayer(const cVector3f &avPosition, float afChance) {
|
|
}
|
|
bool cGameEnemyState_Dog_Attention::OnHearNoise(const cVector3f &avPosition, float afVolume) {
|
|
return false;
|
|
}
|
|
void cGameEnemyState_Dog_Attention::OnFlashlight(const cVector3f &avPosition) {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attention::OnDraw() {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// EAT STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Eat::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->PlayAnim("Eating", true, 0.2f);
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfEatFOV);
|
|
|
|
mfTime = mpEnemy->GetTempFloat();
|
|
|
|
mpMover->GetCharBody()->SetMoveSpeed(eCharDir_Forward, 0);
|
|
mpMover->Stop();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Eat::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Eat::OnUpdate(float afTimeStep) {
|
|
mfTime -= afTimeStep;
|
|
if (mfTime <= 0) {
|
|
mpEnemy->ChangeState(mlPreviousState); // STATE_IDLE);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cGameEnemyState_Dog_Eat::OnHearNoise(const cVector3f &avPosition, float afVolume) {
|
|
// return false;
|
|
if (afVolume >= mpEnemyDog->mfEatMinHearVolume) {
|
|
mpEnemy->SetTempPosition(avPosition);
|
|
mpEnemy->ChangeState(STATE_INVESTIGATE);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void cGameEnemyState_Dog_Eat::OnSeePlayer(const cVector3f &avPosition, float afChance) {
|
|
// return;
|
|
if (mpPlayer->GetHealth() <= 0)
|
|
return;
|
|
|
|
if (afChance >= mpEnemyDog->mfEatMinSeeChance) {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msIdleFoundPlayerSound);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// INVESTIGATE STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Investigate::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->UseMoveStateAnimations();
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfIdleFOV);
|
|
|
|
// Play sound
|
|
mpEnemy->PlaySound(mpEnemyDog->msInvestigateSound);
|
|
|
|
cAINode *pNode = mpMover->GetAINodeAtPosInRange(mpEnemy->GetTempPosition(), 0.0f, 5.0f, true, 0.1f);
|
|
|
|
if (mpEnemy->GetDoorBreakCount() > 6.0f) {
|
|
mpEnemy->SetDoorBreakCount(0);
|
|
mpMover->SetMaxDoorToughness(0);
|
|
}
|
|
|
|
if (pNode) {
|
|
if (mpMover->MoveToPos(pNode->GetPosition()) == false) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
}
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
}
|
|
|
|
mpMover->SetMaxDoorToughness(-1);
|
|
|
|
mpInit->mpMusicHandler->RemoveAttacker(mpEnemy);
|
|
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
if (apPrevState->GetId() != STATE_INVESTIGATE) {
|
|
mfHighestVolume = 0.0f;
|
|
}
|
|
|
|
mfHearSoundCount = 5.0f;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Investigate::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Investigate::OnUpdate(float afTimeStep) {
|
|
if (mfHearSoundCount > 0) {
|
|
mfHearSoundCount -= afTimeStep;
|
|
if (mfHearSoundCount <= 0)
|
|
mfHearSoundCount = 0;
|
|
}
|
|
|
|
////////////////////////////////
|
|
// Play idle sound
|
|
if (mfIdleSoundTime <= 0) {
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
mpEnemy->PlaySound(mpEnemyDog->msIdleSound);
|
|
} else {
|
|
mfIdleSoundTime -= afTimeStep;
|
|
}
|
|
|
|
////////////////////////////////
|
|
// Stuck counter
|
|
if (mpMover->GetStuckCounter() > 1.5f) {
|
|
if (mlKnockCount == 1) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
mlKnockCount = 0;
|
|
} else {
|
|
if (mpEnemy->CheckForDoor()) {
|
|
mpEnemy->ChangeState(STATE_BREAKDOOR);
|
|
}
|
|
mpMover->ResetStuckCounter();
|
|
mlKnockCount++;
|
|
}
|
|
}
|
|
|
|
if (mpMover->IsMoving() == false) {
|
|
mlKnockCount = 0;
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cGameEnemyState_Dog_Investigate::OnHearNoise(const cVector3f &avPosition, float afVolume) {
|
|
if (mfHearSoundCount <= 0 && mfHighestVolume < afVolume &&
|
|
afVolume >= mpEnemyDog->mfIdleMinHearVolume) {
|
|
mfHighestVolume = afVolume;
|
|
mpEnemy->SetTempPosition(avPosition);
|
|
OnEnterState(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// MOVE TO STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_MoveTo::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->UseMoveStateAnimations();
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfIdleFOV);
|
|
|
|
// Play sound
|
|
mpEnemy->PlaySound(mpEnemyDog->msInvestigateSound);
|
|
|
|
if (mpMover->MoveToPos(mpEnemy->GetTempPosition()) == false) {
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add("Could not move to pos!\n",3,true);
|
|
mpEnemy->ChangeState(apPrevState->GetId());
|
|
return;
|
|
} else {
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add("Moving to pos!\n",3,true);
|
|
}
|
|
|
|
mpInit->mpMusicHandler->RemoveAttacker(mpEnemy);
|
|
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
mlBreakCount = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_MoveTo::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_MoveTo::OnUpdate(float afTimeStep) {
|
|
////////////////////////////////
|
|
// Play idle sound
|
|
if (mfIdleSoundTime <= 0) {
|
|
mfIdleSoundTime = cMath::RandRectf(mpEnemyDog->mfIdleSoundMinInteraval,
|
|
mpEnemyDog->mfIdleSoundMaxInteraval);
|
|
|
|
mpEnemy->PlaySound(mpEnemyDog->msIdleSound);
|
|
} else {
|
|
mfIdleSoundTime -= afTimeStep;
|
|
}
|
|
|
|
////////////////////////////////
|
|
// Stuck counter
|
|
if (mpMover->GetStuckCounter() > 1.5f) {
|
|
if (mlBreakCount == 1) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
} else {
|
|
if (mpEnemy->CheckForDoor()) {
|
|
mpEnemy->ChangeState(STATE_BREAKDOOR);
|
|
}
|
|
mpMover->ResetStuckCounter();
|
|
mlBreakCount++;
|
|
}
|
|
}
|
|
|
|
if (mpMover->IsMoving() == false) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// HUNT STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Hunt::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->UseMoveStateAnimations();
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
#ifndef DEMO_VERSION
|
|
float fMul = 1.0f;
|
|
|
|
if (mpInit->mDifficulty == eGameDifficulty_Easy)
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfHuntSpeed * 0.7f * fMul);
|
|
else if (mpInit->mDifficulty == eGameDifficulty_Normal)
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfHuntSpeed * fMul);
|
|
else
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfHuntSpeed * 1.25f * fMul);
|
|
#else
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfHuntSpeed);
|
|
#endif
|
|
|
|
// Setup enemy
|
|
mpEnemy->SetFOV(mpEnemyDog->mfHuntFOV);
|
|
|
|
mfUpdatePathCount = 0;
|
|
mfUpdateFreq = 1.0f;
|
|
mbFreePlayerPath = false;
|
|
|
|
if (mbBreakingDoor && mpEnemy->CanSeePlayer() == false) {
|
|
mlBreakDoorCount++;
|
|
if (mlBreakDoorCount >= 3) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
return;
|
|
}
|
|
} else {
|
|
mlBreakDoorCount = 0;
|
|
}
|
|
|
|
mbBreakingDoor = false;
|
|
mbFoundNoPath = false;
|
|
mbLostPlayer = false;
|
|
mfLostPlayerCount = 0;
|
|
mfMaxLostPlayerCount = mpEnemyDog->mfHuntForLostPlayerTime;
|
|
|
|
mlStuckAtMaxCount = 0;
|
|
|
|
mpInit->mpMusicHandler->AddAttacker(mpEnemy);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Hunt::OnLeaveState(iGameEnemyState *apNextState) {
|
|
mpMover->SetMaxDoorToughness(-1);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Hunt::OnUpdate(float afTimeStep) {
|
|
if (mpPlayer->GetHealth() <= 0) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
return;
|
|
}
|
|
|
|
if (mpMover->GetStuckCounter() > 1.1f) {
|
|
if (mpEnemy->CheckForDoor()) {
|
|
mbBreakingDoor = true;
|
|
mpEnemy->ChangeState(STATE_BREAKDOOR);
|
|
} else {
|
|
mlStuckAtMaxCount++;
|
|
if (mlStuckAtMaxCount >= 6) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
}
|
|
}
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
if (mfUpdatePathCount <= 0) {
|
|
mbFoundNoPath = false;
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add("Update Path!",1.0f,true);
|
|
mfUpdatePathCount = mfUpdateFreq;
|
|
|
|
/*cAINodeContainer *pNodeCont = */ mpEnemy->GetMover()->GetNodeContainer();
|
|
|
|
// Log("%s: Checking free path\n",mpEnemy->GetName().c_str());
|
|
|
|
// Check if there is a free path to the player
|
|
if (mbLostPlayer == false && mpMover->FreeDirectPathToChar(mpPlayer->GetCharacterBody())) {
|
|
mbFreePlayerPath = true;
|
|
mpMover->Stop();
|
|
mpMover->SetMaxDoorToughness(-1);
|
|
} else {
|
|
mbFreePlayerPath = false;
|
|
}
|
|
|
|
// Get path to player
|
|
if (mbFreePlayerPath == false && mbLostPlayer == false) {
|
|
if (mpEnemy->GetDoorBreakCount() > 6.0f) {
|
|
mpMover->SetMaxDoorToughness(0);
|
|
}
|
|
|
|
// Log("%s: Move to pos\n",mpEnemy->GetName().c_str());
|
|
|
|
if (mpMover->MoveToPos(mpEnemy->GetLastPlayerPos()) == false) {
|
|
bool bFoundAnotherWay = false;
|
|
/*float fHeight = mpMover->GetCharBody()->GetPosition().y -
|
|
mpPlayer->GetCharacterBody()->GetPosition().y;
|
|
if(cMath::Abs(fHeight) > mpMover->GetNodeContainer()->GetMaxHeight())
|
|
{
|
|
cVector3f vPos = mpEnemy->GetLastPlayerPos();
|
|
vPos.y = mpMover->GetCharBody()->GetFeetPosition().y+0.1f;
|
|
|
|
if(mpMover->MoveToPos(vPos))
|
|
{
|
|
bFoundAnotherWay = true;
|
|
}
|
|
}*/
|
|
|
|
if (bFoundAnotherWay == false) {
|
|
mfUpdatePathCount = mfUpdateFreq * 5.0f;
|
|
mpMover->Stop();
|
|
// Set this so the enemey at least runs toward the player.
|
|
mbFoundNoPath = true;
|
|
}
|
|
}
|
|
|
|
// Log("%s: Done with that.\n",mpEnemy->GetName().c_str());
|
|
}
|
|
} else {
|
|
mfUpdatePathCount -= afTimeStep;
|
|
}
|
|
|
|
////////////////////////////////
|
|
// Go directly towards the player
|
|
if (mbFreePlayerPath || (mbFoundNoPath && mpMover->IsMoving() == false)) {
|
|
// Go towards player
|
|
mpMover->MoveDirectToPos(mpPlayer->GetCharacterBody()->GetFeetPosition(), afTimeStep);
|
|
|
|
// Check if he should attack.
|
|
if (mpMover->DistanceToChar2D(mpPlayer->GetCharacterBody()) < mpEnemyDog->mfAttackDistance) {
|
|
float fHeight = mpMover->GetCharBody()->GetPosition().y -
|
|
mpPlayer->GetCharacterBody()->GetPosition().y;
|
|
|
|
// Player is above
|
|
if (fHeight < 0) {
|
|
fHeight += mpMover->GetCharBody()->GetSize().y / 2.0f;
|
|
float fMax = mpEnemyDog->mvAttackDamageSize.y; /// 2.0f;
|
|
if (fHeight > -fMax) {
|
|
mpEnemy->ChangeState(STATE_ATTACK);
|
|
} else {
|
|
// random attack if player is not too far up.
|
|
if (cMath::RandRectf(0, 1) < 0.2f) // fHeight*2 > -fMax &&
|
|
mpEnemy->ChangeState(STATE_ATTACK);
|
|
else
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
}
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_ATTACK);
|
|
}
|
|
}
|
|
}
|
|
////////////////////////////////
|
|
// Update path search
|
|
else if (mbFreePlayerPath == false) {
|
|
if (mbLostPlayer == false && mpMover->IsMoving() == false && mpEnemy->CanSeePlayer() == false) {
|
|
mbLostPlayer = true;
|
|
mfLostPlayerCount = mfMaxLostPlayerCount;
|
|
}
|
|
|
|
if (mbLostPlayer) {
|
|
mpMover->GetCharBody()->Move(eCharDir_Forward, 1.0f, afTimeStep);
|
|
|
|
mfLostPlayerCount -= afTimeStep;
|
|
if (mfLostPlayerCount <= 0 || mpMover->GetStuckCounter() > 0.5f) {
|
|
mpEnemy->ChangeState(STATE_IDLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Hunt::OnSeePlayer(const cVector3f &avPosition, float afChance) {
|
|
if (mbLostPlayer && afChance >= mpEnemyDog->mfHuntMinSeeChance) {
|
|
mbLostPlayer = false;
|
|
mfUpdatePathCount = 0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cGameEnemyState_Dog_Hunt::OnHearNoise(const cVector3f &avPosition, float afVolume) {
|
|
//////////////////////////////////
|
|
// If player is lost the sound might be of help
|
|
if (mbLostPlayer) {
|
|
// Check if sound can be heard
|
|
if (afVolume >= mpEnemyDog->mfHuntMinHearVolume) {
|
|
// Check if a node is found near the sound.
|
|
cAINode *pNode = mpMover->GetAINodeAtPosInRange(avPosition, 0.0f, 5.0f, true, 0.1f);
|
|
if (pNode) {
|
|
// Update last player position.
|
|
mbLostPlayer = false;
|
|
mfUpdatePathCount = 0;
|
|
mpEnemy->SetLastPlayerPos(pNode->GetPosition());
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Hunt::OnDraw() {
|
|
/*float fWantedSpeed = */ mpMover->GetCharBody()->GetMoveSpeed(eCharDir_Forward);
|
|
float fRealSpeed = cMath::Vector3Dist(mpMover->GetCharBody()->GetPosition(),
|
|
mpMover->GetCharBody()->GetLastPosition());
|
|
fRealSpeed = fRealSpeed / (1.0f / 60.0f);
|
|
|
|
float fDist = mpMover->DistanceToChar2D(mpPlayer->GetCharacterBody());
|
|
|
|
mpInit->mpDefaultFont->draw(cVector3f(0, 110, 100), 14, cColor(1, 1, 1, 1), eFontAlign_Left,
|
|
Common::U32String::format("LostPlayerCount: %f FreePath: %d NoPath: %d MaxStuck: %d Dist: %f / %f",
|
|
mfLostPlayerCount, mbFreePlayerPath,
|
|
mbFoundNoPath,
|
|
mlStuckAtMaxCount,
|
|
fDist,
|
|
mpEnemyDog->mfAttackDistance));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ATTACK STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attack::OnEnterState(iGameEnemyState *apPrevState) {
|
|
///////////////
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
if (mpEnemyDog->mfAttackSpeed > 0) {
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfAttackSpeed);
|
|
mpMover->SetMaxTurnSpeed(10000.0f);
|
|
|
|
// mpMover->SetMinBreakAngle(cMath::ToRad(140));
|
|
}
|
|
|
|
///////////////
|
|
// Animation
|
|
float fHeight = mpPlayer->GetCharacterBody()->GetPosition().y -
|
|
mpMover->GetCharBody()->GetPosition().y;
|
|
|
|
// Player is above
|
|
if (fHeight > 0.1f)
|
|
mpEnemy->PlayAnim("Attack", false, 0.2f);
|
|
else
|
|
mpEnemy->PlayAnim("AttackLow", false, 0.2f);
|
|
|
|
///////////////
|
|
// Other
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msAttackStartSound);
|
|
|
|
mfDamageTimer = mpEnemyDog->mfAttackDamageTime;
|
|
mfJumpTimer = mpEnemyDog->mfAttackJumpTime;
|
|
mbAttacked = false;
|
|
|
|
if (mpEnemy->GetOnAttackCallback() != "") {
|
|
tString sCommand = mpEnemy->GetOnAttackCallback() + "(\"" + mpEnemy->GetName() + "\")";
|
|
mpInit->RunScriptCommand(sCommand);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attack::OnLeaveState(iGameEnemyState *apNextState) {
|
|
mpEnemyDog->SetSkipSoundTriggerCount(2.0f);
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attack::OnUpdate(float afTimeStep) {
|
|
// Move forward
|
|
if (mpEnemyDog->mfAttackSpeed > 0) {
|
|
if (mfJumpTimer <= 0) {
|
|
mpMover->MoveDirectToPos(mpPlayer->GetCharacterBody()->GetFeetPosition(), afTimeStep);
|
|
} else {
|
|
mpMover->TurnToPos(mpPlayer->GetCharacterBody()->GetFeetPosition());
|
|
mfJumpTimer -= afTimeStep;
|
|
}
|
|
}
|
|
|
|
if (mbAttacked)
|
|
return;
|
|
|
|
//////////////////////////////////////
|
|
// Get the 2D distance to the player
|
|
cVector3f vStart = mpPlayer->GetCharacterBody()->GetPosition();
|
|
vStart.y = 0;
|
|
cVector3f vEnd = mpMover->GetCharBody()->GetPosition();
|
|
vEnd.y = 0;
|
|
float fDist2D = cMath::Vector3DistSqr(vStart, vEnd);
|
|
float fMinRange = mpEnemyDog->mfAttackDamageRange;
|
|
|
|
////////////////////////////////////////
|
|
// Check if dog is in range of player
|
|
if (fDist2D <= fMinRange * fMinRange && mfDamageTimer <= 0) {
|
|
if (mbAttacked == false) {
|
|
cVector3f vPos = mpMover->GetCharBody()->GetPosition() +
|
|
mpMover->GetCharBody()->GetForward() *
|
|
mpEnemyDog->mfAttackDamageRange;
|
|
|
|
cVector3f vRot = cVector3f(0, mpMover->GetCharBody()->GetYaw(), 0);
|
|
cMatrixf mtxOffset = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
|
|
mtxOffset.SetTranslation(vPos);
|
|
|
|
eAttackTargetFlag target = eAttackTargetFlag_Player | eAttackTargetFlag_Bodies;
|
|
|
|
mpInit->mpPlayer->mbDamageFromPos = true;
|
|
mpInit->mpPlayer->mvDamagePos = mpMover->GetCharBody()->GetPosition();
|
|
if (mpInit->mpAttackHandler->CreateShapeAttack(mpEnemyDog->GetAttackShape(),
|
|
mtxOffset,
|
|
mpMover->GetCharBody()->GetPosition(),
|
|
cMath::RandRectf(mpEnemyDog->mfAttackMinDamage,
|
|
mpEnemyDog->mfAttackMaxDamage),
|
|
|
|
mpEnemyDog->mfAttackMinMass, mpEnemyDog->mfAttackMaxMass,
|
|
mpEnemyDog->mfAttackMinImpulse, mpEnemyDog->mfAttackMaxImpulse,
|
|
|
|
mpEnemyDog->mlAttackStrength,
|
|
|
|
target, NULL)) {
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msAttackHitSound);
|
|
}
|
|
mpInit->mpPlayer->mbDamageFromPos = false;
|
|
mbAttacked = true;
|
|
}
|
|
} else {
|
|
mfDamageTimer -= afTimeStep;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attack::OnAnimationOver(const tString &asName) {
|
|
if (mpPlayer->GetHealth() <= 0) {
|
|
float fDist = mpMover->DistanceToChar2D(mpInit->mpPlayer->GetCharacterBody());
|
|
if (fDist < 2.3f) {
|
|
mpEnemy->SetTempFloat(60.0f);
|
|
mpEnemy->ChangeState(STATE_EAT);
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
}
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Attack::OnPostSceneDraw() {
|
|
cCamera3D *pCamera = static_cast<cCamera3D *>(mpInit->mpGame->GetScene()->GetCamera());
|
|
|
|
cVector3f vPos = mpMover->GetCharBody()->GetPosition() +
|
|
mpMover->GetCharBody()->GetForward() *
|
|
mpEnemyDog->mfAttackDamageRange;
|
|
|
|
cVector3f vRot = cVector3f(0, mpMover->GetCharBody()->GetYaw(), 0);
|
|
cMatrixf mtxOffset = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
|
|
mtxOffset.SetTranslation(vPos);
|
|
|
|
cMatrixf mtxCollider = cMath::MatrixMul(pCamera->GetViewMatrix(), mtxOffset);
|
|
|
|
mpInit->mpGame->GetGraphics()->GetLowLevel()->SetMatrix(eMatrix_ModelView, mtxCollider);
|
|
|
|
cVector3f vSize = mpEnemyDog->GetAttackShape()->GetSize();
|
|
mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize * 0.5f, vSize * -0.5f,
|
|
cColor(1, 0, 1, 1));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FLEE STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Flee::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->UseMoveStateAnimations();
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfHuntSpeed);
|
|
mpMover->GetCharBody()->SetMaxNegativeMoveSpeed(eCharDir_Forward, -mpEnemyDog->mfFleeBackSpeed);
|
|
|
|
float fPosMul = 1;
|
|
if (apPrevState->GetId() == STATE_KNOCKDOWN)
|
|
fPosMul = 4.0f;
|
|
|
|
mbBackingFromBreakDoor = false;
|
|
if (apPrevState->GetId() == STATE_BREAKDOOR)
|
|
mbBackingFromBreakDoor = true;
|
|
|
|
///////////////////////////////////////
|
|
// The dog has just broken a door
|
|
if (mbBackingFromBreakDoor) {
|
|
mfBackAngle = mpMover->GetCharBody()->GetYaw();
|
|
mbBackwards = true;
|
|
|
|
mfTimer = mpEnemyDog->mfFleeBackTime;
|
|
mfCheckBehindTime = 1.0f / 10.0f;
|
|
}
|
|
///////////////////////////////////////
|
|
// Normal flee
|
|
else {
|
|
if ((apPrevState->GetId() == STATE_KNOCKDOWN || apPrevState->GetId() == STATE_HUNT ||
|
|
cMath::RandRectf(0, 1) < 0) // mpEnemyDog->mfFleePositionChance)
|
|
) {
|
|
cAINode *pNode = mpMover->GetAINodeInRange(mpEnemyDog->mfFleePositionMinDistance * fPosMul,
|
|
mpEnemyDog->mfFleePositionMaxDistance * fPosMul);
|
|
if (pNode) {
|
|
mpMover->MoveToPos(pNode->GetPosition());
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
mfTimer = mpEnemyDog->mfFleePositionMaxTime;
|
|
mbBackwards = false;
|
|
} else if (cMath::RandRectf(0, 1) < mpEnemyDog->mfFleeBackChance) {
|
|
mfBackAngle = mpMover->GetCharBody()->GetYaw();
|
|
mbBackwards = true;
|
|
|
|
mfTimer = mpEnemyDog->mfFleeBackTime;
|
|
mfCheckBehindTime = 1.0f / 10.0f;
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Flee::OnLeaveState(iGameEnemyState *apNextState) {
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Flee::OnUpdate(float afTimeStep) {
|
|
mfTimer -= afTimeStep;
|
|
|
|
if (mbBackwards) {
|
|
if (mfCheckBehindTime <= 0) {
|
|
mfCheckBehindTime = 1.0f / 20.0f;
|
|
|
|
bool bHit = mpEnemy->GetGroundFinder()->GetGround(mpMover->GetCharBody()->GetPosition(),
|
|
mpMover->GetCharBody()->GetForward() * -1,
|
|
NULL, NULL, 1.9f);
|
|
if (bHit) {
|
|
if (mbBackingFromBreakDoor) {
|
|
if (mlPreviousState == STATE_FLEE)
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
else
|
|
mpEnemy->ChangeState(mlPreviousState);
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
}
|
|
} else {
|
|
mfCheckBehindTime -= afTimeStep;
|
|
}
|
|
|
|
if (mfTimer <= 0) {
|
|
if (mbBackingFromBreakDoor)
|
|
mpEnemy->ChangeState(mlPreviousState);
|
|
else
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
mpMover->GetCharBody()->Move(eCharDir_Forward, -1.0f, afTimeStep);
|
|
mpMover->TurnToPos(mpInit->mpPlayer->GetCharacterBody()->GetFeetPosition());
|
|
} else {
|
|
// Move forward
|
|
if (mpMover->IsMoving() == false || mpMover->GetStuckCounter() > 0.3f || mfTimer <= 0) {
|
|
// Check if there are any enemies nearby and if anyone is already fighting
|
|
if (mpEnemy->CheckForTeamMate(mpEnemyDog->mfCallBackupRange * 1.5f, false) &&
|
|
mpEnemy->CheckForTeamMate(8, true) == false) {
|
|
float fPlayerDist = mpMover->DistanceToChar(mpInit->mpPlayer->GetCharacterBody());
|
|
// Log("Dist: %f\n",fPlayerDist);
|
|
if (fPlayerDist > 8) {
|
|
mpEnemy->ChangeState(STATE_CALLBACKUP);
|
|
} else {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
// Log("Back from flee!!\n");
|
|
} else {
|
|
// if(mpInit->mpMusicHandler->AttackerExist(mpEnemy))
|
|
{
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
// else
|
|
//{
|
|
// mpEnemy->ChangeState(STATE_IDLE);
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CALL BACKUP STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_CallBackup::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->PlayAnim(mpEnemyDog->msCallBackupAnimation, false, 0.2f);
|
|
|
|
// Sound
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msCallBackupSound);
|
|
|
|
// Iterate enemies and show them the player
|
|
cVector3f vPostion = mpMover->GetCharBody()->GetFeetPosition();
|
|
|
|
tGameEnemyIterator it = mpInit->mpMapHandler->GetGameEnemyIterator();
|
|
while (it.HasNext()) {
|
|
iGameEnemy *pEnemy = it.Next();
|
|
if (pEnemy->GetEnemyType() != mpEnemy->GetEnemyType())
|
|
continue;
|
|
if (pEnemy == mpEnemy || pEnemy->IsActive() == false || pEnemy->GetHealth() <= 0)
|
|
continue;
|
|
|
|
cGameEnemy_Dog *pDog = static_cast<cGameEnemy_Dog *>(pEnemy);
|
|
|
|
float fDist = cMath::Vector3Dist(pDog->GetMover()->GetCharBody()->GetPosition(),
|
|
vPostion);
|
|
|
|
if (fDist <= mpEnemyDog->mfCallBackupRange) {
|
|
pDog->ShowPlayer(mpEnemyDog->GetLastPlayerPos());
|
|
break; // Call only for one dog backup!
|
|
}
|
|
}
|
|
|
|
mpMover->Stop();
|
|
mpMover->GetCharBody()->SetMoveSpeed(eCharDir_Forward, 0);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_CallBackup::OnLeaveState(iGameEnemyState *apNextState) {
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_CallBackup::OnUpdate(float afTimeStep) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_CallBackup::OnAnimationOver(const tString &asName) {
|
|
mpEnemy->ChangeState(STATE_HUNT);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// BREAK DOOR STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_BreakDoor::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
if (mpEnemyDog->mfBreakDoorSpeed > 0)
|
|
mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyDog->mfBreakDoorSpeed);
|
|
|
|
// Animation
|
|
mpEnemy->PlayAnim(mpEnemyDog->msBreakDoorAnimation, false, 0.2f);
|
|
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msBreakDoorStartSound);
|
|
|
|
mfDamageTimer = mpEnemyDog->mfBreakDoorDamageTime;
|
|
mfStopMoveTimer = mpEnemyDog->mfBreakDoorDamageTime + 1.1f;
|
|
mbAttacked = false;
|
|
mbStopped = false;
|
|
|
|
mlCount = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_BreakDoor::OnLeaveState(iGameEnemyState *apNextState) {
|
|
mpEnemyDog->SetSkipSoundTriggerCount(2.0f);
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_BreakDoor::OnUpdate(float afTimeStep) {
|
|
// Move forward
|
|
if (mpEnemyDog->mfBreakDoorSpeed > 0 && mlCount == 0 && mbAttacked == false) {
|
|
// Skip this for now
|
|
// mpMover->GetCharBody()->Move(eCharDir_Forward,1.0f,afTimeStep);
|
|
}
|
|
|
|
if (mfDamageTimer <= 0) {
|
|
if (!mbStopped) {
|
|
mpMover->Stop();
|
|
mbStopped = true;
|
|
}
|
|
} else {
|
|
mfDamageTimer -= afTimeStep;
|
|
}
|
|
|
|
if (mfDamageTimer <= 0) {
|
|
if (mbAttacked == false) {
|
|
cVector3f vPos = mpMover->GetCharBody()->GetPosition() +
|
|
mpMover->GetCharBody()->GetForward() *
|
|
mpEnemyDog->mfBreakDoorDamageRange;
|
|
|
|
cVector3f vRot = cVector3f(0, mpMover->GetCharBody()->GetYaw(), 0);
|
|
cMatrixf mtxOffset = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
|
|
mtxOffset.SetTranslation(vPos);
|
|
|
|
eAttackTargetFlag target = eAttackTargetFlag_Player | eAttackTargetFlag_Bodies;
|
|
|
|
if (mpInit->mpAttackHandler->CreateShapeAttack(mpEnemyDog->GetBreakDoorShape(),
|
|
mtxOffset,
|
|
mpMover->GetCharBody()->GetPosition(),
|
|
cMath::RandRectf(mpEnemyDog->mfBreakDoorMinDamage,
|
|
mpEnemyDog->mfBreakDoorMaxDamage),
|
|
|
|
mpEnemyDog->mfBreakDoorMinMass, mpEnemyDog->mfBreakDoorMaxMass,
|
|
mpEnemyDog->mfBreakDoorMinImpulse, mpEnemyDog->mfBreakDoorMaxImpulse,
|
|
|
|
mpEnemyDog->mlBreakDoorStrength,
|
|
|
|
target, NULL)) {
|
|
mpEnemyDog->PlaySound(mpEnemyDog->msBreakDoorHitSound);
|
|
|
|
cGameSwingDoor *pDoor = mpInit->mpAttackHandler->GetLastSwingDoor();
|
|
if (pDoor) {
|
|
// FIXME: Code identical in each branch. Development leftover?
|
|
#if 0
|
|
/////////////////////////////
|
|
// The door is unbreakable
|
|
if (pDoor->GetToughness() - mpEnemyDog->mlBreakDoorStrength >= 4) {
|
|
cMatrixf mtxDoor = pDoor->GetBody(0)->GetWorldMatrix();
|
|
cMatrixf mtxInvDoor = cMath::MatrixInverse(mtxDoor);
|
|
|
|
cVector3f vDoorForward = mtxInvDoor.GetForward();
|
|
cVector3f vEnemyForward = mpMover->GetCharBody()->GetForward();
|
|
|
|
if (cMath::Vector3Dot(vDoorForward, vEnemyForward) < 0) {
|
|
mpEnemy->AddDoorBreakCount(2); // 8);
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add("Cannot break this door! On BAD side",4);
|
|
} else {
|
|
mpEnemy->AddDoorBreakCount(2);
|
|
// Just continue hitting it since it is just barricaded.
|
|
// mpInit->mpEffectHandler->GetSubTitle()->Add("Cannot break this door! On GOOD side",4);
|
|
}
|
|
}
|
|
/////////////////////////////
|
|
// The door is breakable
|
|
else {
|
|
mpEnemy->AddDoorBreakCount(2);
|
|
}
|
|
#else
|
|
mpEnemy->AddDoorBreakCount(2);
|
|
#endif
|
|
}
|
|
}
|
|
mbAttacked = true;
|
|
}
|
|
} else {
|
|
mfDamageTimer -= afTimeStep;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_BreakDoor::OnAnimationOver(const tString &asName) {
|
|
if (mlCount == 0) {
|
|
if (mpEnemyDog->mbBreakDoorRiseAtEnd) {
|
|
mpEnemy->PlayAnim("RiseRight", false, 0.2f);
|
|
mlCount++;
|
|
} else {
|
|
// mpEnemy->ChangeState(mlPreviousState);
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
mpEnemy->GetState(STATE_FLEE)->SetPreviousState(mlPreviousState);
|
|
}
|
|
} else {
|
|
// mpEnemy->ChangeState(mlPreviousState);
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
mpEnemy->GetState(STATE_FLEE)->SetPreviousState(mlPreviousState);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_BreakDoor::OnPostSceneDraw() {
|
|
cCamera3D *pCamera = static_cast<cCamera3D *>(mpInit->mpGame->GetScene()->GetCamera());
|
|
|
|
cVector3f vPos = mpMover->GetCharBody()->GetPosition() +
|
|
mpMover->GetCharBody()->GetForward() *
|
|
mpEnemyDog->mfBreakDoorDamageRange;
|
|
|
|
cVector3f vRot = cVector3f(0, mpMover->GetCharBody()->GetYaw(), 0);
|
|
cMatrixf mtxOffset = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
|
|
mtxOffset.SetTranslation(vPos);
|
|
|
|
cMatrixf mtxCollider = cMath::MatrixMul(pCamera->GetViewMatrix(), mtxOffset);
|
|
|
|
mpInit->mpGame->GetGraphics()->GetLowLevel()->SetMatrix(eMatrix_ModelView, mtxCollider);
|
|
|
|
cVector3f vSize = mpEnemyDog->GetBreakDoorShape()->GetSize();
|
|
mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize * 0.5f, vSize * -0.5f,
|
|
cColor(1, 0, 1, 1));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// KNOCK DOWN STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_KnockDown::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->PlayAnim("Idle", true, 0.7f);
|
|
|
|
// Sound
|
|
mpEnemy->PlaySound(mpEnemyDog->msKnockDownSound);
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Go to rag doll
|
|
mpEnemy->GetMeshEntity()->AlignBodiesToSkeleton(false);
|
|
mpEnemy->GetMeshEntity()->SetSkeletonPhysicsActive(true);
|
|
mpEnemy->GetMeshEntity()->Stop();
|
|
|
|
mpEnemy->GetMover()->GetCharBody()->SetEntity(NULL);
|
|
mpEnemy->GetMover()->GetCharBody()->SetActive(false);
|
|
|
|
mpEnemy->GetMover()->Stop();
|
|
|
|
mfTimer = 2.0f;
|
|
mbCheckAnim = false;
|
|
|
|
mlPrevToughness = mpEnemy->GetToughness();
|
|
mpEnemy->SetToughness(12);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_KnockDown::OnLeaveState(iGameEnemyState *apNextState) {
|
|
mpEnemy->SetToughness(mlPrevToughness);
|
|
|
|
mpEnemy->GetMover()->GetCharBody()->SetEntity(mpEnemy->GetMeshEntity());
|
|
mpEnemy->GetMover()->GetCharBody()->SetActive(true);
|
|
mpMover->ResetStuckCounter();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_KnockDown::OnUpdate(float afTimeStep) {
|
|
if (mbCheckAnim) {
|
|
iCharacterBody *pCharBody = mpEnemy->GetMover()->GetCharBody();
|
|
cBoundingVolume *pBV = pCharBody->GetBody()->GetBoundingVolume();
|
|
|
|
////////////////////////////////////////////////
|
|
// Add a force to all objects around dog.
|
|
iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
|
|
|
|
Common::List<iPhysicsBody *> lstBodies;
|
|
cPhysicsBodyIterator bodyIt = pWorld->GetBodyIterator();
|
|
while (bodyIt.HasNext()) {
|
|
lstBodies.push_back(bodyIt.Next());
|
|
}
|
|
|
|
//////////////////////////
|
|
// Force Iteration
|
|
Common::List<iPhysicsBody *>::iterator it = lstBodies.begin();
|
|
for (; it != lstBodies.end(); ++it) {
|
|
iPhysicsBody *pBody = *it;
|
|
|
|
if (pBody->GetCollideCharacter() == false)
|
|
continue;
|
|
if (pBody->IsActive() == false)
|
|
continue;
|
|
if (pBody == pCharBody->GetBody())
|
|
continue;
|
|
|
|
if (cMath::CheckCollisionBV(*pBody->GetBoundingVolume(), *pBV)) {
|
|
cVector3f vDir = pBody->GetWorldPosition() - pCharBody->GetPosition();
|
|
float fLength = vDir.Length();
|
|
// vDir.y *= 0.1f;
|
|
// if(vDir.x ==0 && vDir.z ==0) vDir.x = 0.3f;
|
|
vDir.Normalise();
|
|
|
|
if (fLength == 0)
|
|
fLength = 0.001f;
|
|
float fForce = (1 / fLength) * 2;
|
|
if (fForce > 300)
|
|
fForce = 300;
|
|
if (mpInit->mpPlayer->GetState() == ePlayerState_Grab &&
|
|
mpInit->mpPlayer->GetPushBody() == pBody) {
|
|
fForce *= 40;
|
|
}
|
|
|
|
if (pBody->IsCharacter()) {
|
|
pBody->GetCharacterBody()->AddForce(vDir * fForce * 10 *
|
|
pBody->GetCharacterBody()->GetMass());
|
|
} else {
|
|
pBody->AddForce(vDir * fForce * pBody->GetMass());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
mfTimer -= afTimeStep;
|
|
|
|
if (mfTimer <= 0) {
|
|
// Get the forward vector from root bone (the right vector)
|
|
cNodeIterator StateIt = mpEnemy->GetMeshEntity()->GetRootNode()->GetChildIterator();
|
|
cBoneState *pBoneState = static_cast<cBoneState *>(StateIt.Next());
|
|
|
|
cVector3f vRight = cMath::MatrixInverse(pBoneState->GetWorldMatrix()).GetForward();
|
|
|
|
// Play animation and fade physics
|
|
float fFadeTime = 1.0f;
|
|
mbCheckAnim = true;
|
|
|
|
mpEnemy->GetMeshEntity()->Stop();
|
|
if (cMath::Vector3Dot(vRight, cVector3f(0, 1, 0)) < 0)
|
|
mpEnemy->PlayAnim("RiseRight", false, fFadeTime);
|
|
else
|
|
mpEnemy->PlayAnim("RiseLeft", false, fFadeTime);
|
|
|
|
mpEnemy->GetMeshEntity()->FadeSkeletonPhysicsWeight(fFadeTime);
|
|
|
|
// Calculate values
|
|
cVector3f vPosition;
|
|
cVector3f vAngles;
|
|
/*cMatrixf mtxTransform = */ mpEnemy->GetMeshEntity()->CalculateTransformFromSkeleton(&vPosition, &vAngles);
|
|
|
|
// Seems to work better...
|
|
vPosition = mpEnemy->GetMeshEntity()->GetBoundingVolume()->GetWorldCenter();
|
|
cVector3f vGroundPos = vPosition;
|
|
|
|
/*bool bFoundGround = */ mpEnemy->GetGroundFinder()->GetGround(vPosition, cVector3f(0, -1, 0), &vGroundPos, NULL);
|
|
|
|
// Log("Found ground: %d | %s -> %s\n",bFoundGround,vPosition.ToString().c_str(),
|
|
// vGroundPos.ToString().c_str());
|
|
|
|
// Set body
|
|
iCharacterBody *pCharBody = mpEnemy->GetMover()->GetCharBody();
|
|
|
|
// vGroundPos -= pCharBody->GetEntityOffset().GetTranslation();
|
|
float fYAngle = vAngles.y - mpEnemy->GetModelOffsetAngles().y;
|
|
// cVector3f vAdd = cVector3f(0,0,pCharBody->GetEntityOffset().GetTranslation().z);
|
|
// vAdd = cMath::MatrixMul(cMath::MatrixRotateY(fYAngle),vAdd);
|
|
// vGroundPos += vAdd;
|
|
pCharBody->SetFeetPosition(vGroundPos);
|
|
pCharBody->SetYaw(fYAngle);
|
|
pCharBody->SetEntity(mpEnemy->GetMeshEntity());
|
|
pCharBody->SetActive(true);
|
|
// pCharBody->Update(1.0f / 60.0f);
|
|
// pCharBody->SetActive(false);
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
pCharBody->Update(1.0f / 60.0f);
|
|
|
|
mpEnemy->GetMeshEntity()->UpdateLogic(1.0f / 60.0f);
|
|
mpEnemy->GetMeshEntity()->UpdateGraphics(NULL, 1.0f / 60.0f, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_KnockDown::OnAnimationOver(const tString &asName) {
|
|
iCharacterBody *pCharBody = mpEnemy->GetMover()->GetCharBody();
|
|
|
|
// FIXME: Code identical in each branch. Development leftover?
|
|
#if 0
|
|
if (mpEnemy->CheckForTeamMate(mpEnemyDog->mfCallBackupRange * 1.5f, false) &&
|
|
mpEnemy->CheckForTeamMate(14, true) == false) {
|
|
pCharBody->SetActive(true);
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
} else {
|
|
pCharBody->SetActive(true);
|
|
// mpEnemy->ChangeState(STATE_HUNT);
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
}
|
|
#else
|
|
pCharBody->SetActive(true);
|
|
mpEnemy->ChangeState(STATE_FLEE);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// DEAD STATE
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Dead::OnEnterState(iGameEnemyState *apPrevState) {
|
|
// Animation
|
|
mpEnemy->PlayAnim("Idle", true, 0.7f);
|
|
|
|
// Sound
|
|
if (mpEnemy->IsLoading() == false)
|
|
mpEnemy->PlaySound(mpEnemyDog->msDeathSound);
|
|
|
|
// Setup body
|
|
mpEnemy->SetupBody();
|
|
|
|
// Go to ragdoll
|
|
if (mpEnemy->IsLoading() == false)
|
|
mpEnemy->GetMeshEntity()->AlignBodiesToSkeleton(false);
|
|
|
|
mpEnemy->GetMeshEntity()->SetSkeletonPhysicsActive(true);
|
|
mpEnemy->GetMeshEntity()->Stop();
|
|
|
|
mpEnemy->GetMover()->GetCharBody()->SetEntity(NULL);
|
|
mpEnemy->GetMover()->GetCharBody()->SetActive(false);
|
|
|
|
mpEnemy->GetMover()->Stop();
|
|
|
|
mpInit->mpMusicHandler->RemoveAttacker(mpEnemy);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Dead::OnLeaveState(iGameEnemyState *apNextState) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemyState_Dog_Dead::OnUpdate(float afTimeStep) {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CONSTRUCTORS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cGameEnemy_Dog::cGameEnemy_Dog(cInit *apInit, const tString &asName, TiXmlElement *apGameElem) : iGameEnemy(apInit, asName, apGameElem) {
|
|
LoadBaseProperties(apGameElem);
|
|
|
|
//////////////////////////////
|
|
// Special properties
|
|
|
|
mfLengthBodyToAss = cString::ToFloat(apGameElem->Attribute("LengthBodyToAss"), 1.5f);
|
|
|
|
mfMinKnockDamage = cString::ToFloat(apGameElem->Attribute("MinKnockDamage"), 0);
|
|
mfCertainKnockDamage = cString::ToFloat(apGameElem->Attribute("CertainKnockDamage"), 0);
|
|
|
|
//////////////////////////////
|
|
// State properties
|
|
mfIdleFOV = cMath::ToRad(cString::ToFloat(apGameElem->Attribute("IdleFOV"), 0));
|
|
msIdleFoundPlayerSound = cString::ToString(apGameElem->Attribute("IdleFoundPlayerSound"), "");
|
|
mfIdleMinSeeChance = cString::ToFloat(apGameElem->Attribute("IdleMinSeeChance"), 0);
|
|
mfIdleMinHearVolume = cString::ToFloat(apGameElem->Attribute("IdleMinHearVolume"), 0);
|
|
msIdleSound = cString::ToString(apGameElem->Attribute("IdleSound"), "");
|
|
mfIdleSoundMinInteraval = cString::ToFloat(apGameElem->Attribute("IdleSoundMinInteraval"), 0);
|
|
mfIdleSoundMaxInteraval = cString::ToFloat(apGameElem->Attribute("IdleSoundMaxInteraval"), 0);
|
|
mfIdleCallBackupChance = cString::ToFloat(apGameElem->Attribute("IdleCallBackupChance"), 0);
|
|
mvPreloadSounds.push_back(msIdleSound);
|
|
|
|
msInvestigateSound = cString::ToString(apGameElem->Attribute("InvestigateSound"), "");
|
|
mvPreloadSounds.push_back(msInvestigateSound);
|
|
|
|
msAttentionSound = cString::ToString(apGameElem->Attribute("AttentionSound"), "");
|
|
mfAttentionTime = cString::ToFloat(apGameElem->Attribute("AttentionTime"), 0);
|
|
mfAttentionMinDist = cString::ToFloat(apGameElem->Attribute("AttentionMinDist"), 0);
|
|
mvPreloadSounds.push_back(msAttentionSound);
|
|
|
|
mfHuntFOV = cMath::ToRad(cString::ToFloat(apGameElem->Attribute("HuntFOV"), 0));
|
|
mfHuntSpeed = cString::ToFloat(apGameElem->Attribute("HuntSpeed"), 0);
|
|
mfHuntForLostPlayerTime = cString::ToFloat(apGameElem->Attribute("HuntForLostPlayerTime"), 0);
|
|
mfHuntMinSeeChance = cString::ToFloat(apGameElem->Attribute("IdleMinSeeChance"), 0);
|
|
mfHuntMinHearVolume = cString::ToFloat(apGameElem->Attribute("IdleMinHearVolume"), 0);
|
|
|
|
mfAttackDistance = cString::ToFloat(apGameElem->Attribute("AttackDistance"), 0);
|
|
mfAttackSpeed = cString::ToFloat(apGameElem->Attribute("AttackSpeed"), 0);
|
|
mfAttackJumpTime = cString::ToFloat(apGameElem->Attribute("AttackJumpTime"), 0);
|
|
mfAttackDamageTime = cString::ToFloat(apGameElem->Attribute("AttackDamageTime"), 0);
|
|
mvAttackDamageSize = cString::ToVector3f(apGameElem->Attribute("AttackDamageSize"), 0);
|
|
mfAttackDamageRange = cString::ToFloat(apGameElem->Attribute("AttackDamageRange"), 0);
|
|
mfAttackMinDamage = cString::ToFloat(apGameElem->Attribute("AttackMinDamage"), 0);
|
|
mfAttackMaxDamage = cString::ToFloat(apGameElem->Attribute("AttackMaxDamage"), 0);
|
|
msAttackStartSound = cString::ToString(apGameElem->Attribute("AttackStartSound"), "");
|
|
msAttackHitSound = cString::ToString(apGameElem->Attribute("AttackHitSound"), "");
|
|
mfAttackMinMass = cString::ToFloat(apGameElem->Attribute("AttackMinMass"), 0);
|
|
mfAttackMaxMass = cString::ToFloat(apGameElem->Attribute("AttackMaxMass"), 0);
|
|
mfAttackMinImpulse = cString::ToFloat(apGameElem->Attribute("AttackMinImpulse"), 0);
|
|
mfAttackMaxImpulse = cString::ToFloat(apGameElem->Attribute("AttackMaxImpulse"), 0);
|
|
mlAttackStrength = cString::ToInt(apGameElem->Attribute("AttackStrength"), 0);
|
|
mvPreloadSounds.push_back(msAttackStartSound);
|
|
mvPreloadSounds.push_back(msAttackHitSound);
|
|
|
|
msBreakDoorAnimation = cString::ToString(apGameElem->Attribute("BreakDoorAnimation"), "");
|
|
mfBreakDoorSpeed = cString::ToFloat(apGameElem->Attribute("BreakDoorSpeed"), 0);
|
|
mfBreakDoorDamageTime = cString::ToFloat(apGameElem->Attribute("BreakDoorDamageTime"), 0);
|
|
mvBreakDoorDamageSize = cString::ToVector3f(apGameElem->Attribute("BreakDoorDamageSize"), 0);
|
|
mfBreakDoorDamageRange = cString::ToFloat(apGameElem->Attribute("BreakDoorDamageRange"), 0);
|
|
mfBreakDoorMinDamage = cString::ToFloat(apGameElem->Attribute("BreakDoorMinDamage"), 0);
|
|
mfBreakDoorMaxDamage = cString::ToFloat(apGameElem->Attribute("BreakDoorMaxDamage"), 0);
|
|
msBreakDoorStartSound = cString::ToString(apGameElem->Attribute("BreakDoorStartSound"), "");
|
|
msBreakDoorHitSound = cString::ToString(apGameElem->Attribute("BreakDoorHitSound"), "");
|
|
mfBreakDoorMinMass = cString::ToFloat(apGameElem->Attribute("BreakDoorMinMass"), 0);
|
|
mfBreakDoorMaxMass = cString::ToFloat(apGameElem->Attribute("BreakDoorMaxMass"), 0);
|
|
mfBreakDoorMinImpulse = cString::ToFloat(apGameElem->Attribute("BreakDoorMinImpulse"), 0);
|
|
mfBreakDoorMaxImpulse = cString::ToFloat(apGameElem->Attribute("BreakDoorMaxImpulse"), 0);
|
|
mlBreakDoorStrength = cString::ToInt(apGameElem->Attribute("BreakDoorStrength"), 0);
|
|
mbBreakDoorRiseAtEnd = cString::ToBool(apGameElem->Attribute("BreakDoorRiseAtEnd"), false);
|
|
mvPreloadSounds.push_back(msBreakDoorStartSound);
|
|
mvPreloadSounds.push_back(msBreakDoorHitSound);
|
|
|
|
msKnockDownSound = cString::ToString(apGameElem->Attribute("KnockDownSound"), "");
|
|
mvPreloadSounds.push_back(msKnockDownSound);
|
|
|
|
msDeathSound = cString::ToString(apGameElem->Attribute("DeathSound"), "");
|
|
mvPreloadSounds.push_back(msDeathSound);
|
|
|
|
mfFleePositionChance = cString::ToFloat(apGameElem->Attribute("FleePositionChance"), 0);
|
|
mfFleePositionMaxTime = cString::ToFloat(apGameElem->Attribute("FleePositionMaxTime"), 0);
|
|
mfFleePositionMinDistance = cString::ToFloat(apGameElem->Attribute("FleePositionMinDistance"), 0);
|
|
mfFleePositionMaxDistance = cString::ToFloat(apGameElem->Attribute("FleePositionMaxDistance"), 0);
|
|
mfFleeBackChance = cString::ToFloat(apGameElem->Attribute("FleeBackChance"), 0);
|
|
mfFleeBackTime = cString::ToFloat(apGameElem->Attribute("FleeBackTime"), 0);
|
|
mfFleeBackSpeed = cString::ToFloat(apGameElem->Attribute("FleeBackSpeed"), 0);
|
|
|
|
msCallBackupAnimation = cString::ToString(apGameElem->Attribute("CallBackupAnimation"), "");
|
|
msCallBackupSound = cString::ToString(apGameElem->Attribute("CallBackupSound"), "");
|
|
mfCallBackupRange = cString::ToFloat(apGameElem->Attribute("CallBackupRange"), 0);
|
|
mvPreloadSounds.push_back(msCallBackupSound);
|
|
|
|
mfEatFOV = cMath::ToRad(cString::ToFloat(apGameElem->Attribute("EatFOV"), 0));
|
|
mfEatMinSeeChance = cString::ToFloat(apGameElem->Attribute("EatMinSeeChance"), 0);
|
|
mfEatMinHearVolume = cString::ToFloat(apGameElem->Attribute("EatMinHearVolume"), 0);
|
|
|
|
//////////////////////////////
|
|
// Set up states
|
|
AddState(hplNew(cGameEnemyState_Dog_Idle, (STATE_IDLE, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Hunt, (STATE_HUNT, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Attack, (STATE_ATTACK, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Flee, (STATE_FLEE, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_KnockDown, (STATE_KNOCKDOWN, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Dead, (STATE_DEAD, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Patrol, (STATE_PATROL, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Investigate, (STATE_INVESTIGATE, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_BreakDoor, (STATE_BREAKDOOR, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_CallBackup, (STATE_CALLBACKUP, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_MoveTo, (STATE_MOVETO, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Eat, (STATE_EAT, mpInit, this)));
|
|
AddState(hplNew(cGameEnemyState_Dog_Attention, (STATE_ATTENTION, mpInit, this)));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
cGameEnemy_Dog::~cGameEnemy_Dog() {
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemy_Dog::OnLoad() {
|
|
// Create attack shape
|
|
iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();
|
|
mpAttackShape = pPhysicsWorld->CreateBoxShape(mvAttackDamageSize, NULL);
|
|
mpBreakDoorShape = pPhysicsWorld->CreateBoxShape(mvBreakDoorDamageSize, NULL);
|
|
|
|
// Set up shape
|
|
ChangeState(STATE_IDLE);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemy_Dog::OnUpdate(float afTimeStep) {
|
|
if (IsActive() == false)
|
|
return;
|
|
|
|
///////////////////////////////////
|
|
// Regenerate health:
|
|
if (mfHealth > 0) {
|
|
if (mpInit->mDifficulty != eGameDifficulty_Easy &&
|
|
mfHealth <= mfMaxHealth * 0.5f) {
|
|
mfHealth += afTimeStep * (10.0f / 60.0f); // 10 heal units / min
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////
|
|
// Check for ass in wall
|
|
if (mfHealth > 0 && mpMover->GetCharBody()->IsActive()) {
|
|
float fMaxMove = afTimeStep * 2;
|
|
static int lAssCount = 0;
|
|
lAssCount++;
|
|
if (lAssCount % 2 == 0) {
|
|
iCharacterBody *pCharBody = mpMover->GetCharBody();
|
|
|
|
cVector3f vPos, vNormal;
|
|
mFindGround.GetGround(pCharBody->GetPosition(), pCharBody->GetForward() * -1,
|
|
&vPos, &vNormal, mfLengthBodyToAss);
|
|
float fDist = cMath::Vector3Dist(pCharBody->GetPosition(), vPos);
|
|
if (fDist < mfLengthBodyToAss) {
|
|
float fAdd = mfLengthBodyToAss - fDist;
|
|
if (fAdd > fMaxMove)
|
|
fAdd = fMaxMove;
|
|
|
|
cVector3f vAdd = pCharBody->GetForward() * fAdd;
|
|
pCharBody->SetPosition(pCharBody->GetPosition() + vAdd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void cGameEnemy_Dog::ShowPlayer(const cVector3f &avPlayerFeetPos) {
|
|
if (mlCurrentState == STATE_IDLE || mlCurrentState == STATE_PATROL ||
|
|
mlCurrentState == STATE_INVESTIGATE) {
|
|
mvLastPlayerPos = avPlayerFeetPos;
|
|
ChangeState(STATE_HUNT);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cGameEnemy_Dog::MoveToPos(const cVector3f &avFeetPos) {
|
|
if (mlCurrentState == STATE_IDLE || mlCurrentState == STATE_PATROL) {
|
|
SetTempPosition(avFeetPos);
|
|
ChangeState(STATE_MOVETO);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool cGameEnemy_Dog::IsFighting() {
|
|
if (mfHealth <= 0 || IsActive() == false)
|
|
return false;
|
|
if (mlCurrentState == STATE_IDLE || mlCurrentState == STATE_PATROL ||
|
|
mlCurrentState == STATE_INVESTIGATE)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|