/* 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 . * */ /* * Copyright (C) 2006-2010 - Frictional Games * * This file is part of Penumbra Overture. */ #include "hpl1/penumbra-overture/GameEnemy_Worm.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_Worm_Base::iGameEnemyState_Worm_Base(int alId, cInit *apInit, iGameEnemy *apEnemy) : iGameEnemyState(alId, apInit, apEnemy) { mpEnemyWorm = static_cast(mpEnemy); } //----------------------------------------------------------------------- void iGameEnemyState_Worm_Base::OnSeePlayer(const cVector3f &avPosition, float afChance) { if (mpPlayer->GetHealth() <= 0) return; if (afChance >= mpEnemyWorm->mfIdleMinSeeChance) { mpEnemy->ChangeState(STATE_HUNT); // mpEnemyWorm->PlaySound(mpEnemyWorm->msIdleFoundPlayerSound); } } bool iGameEnemyState_Worm_Base::OnHearNoise(const cVector3f &avPosition, float afVolume) { /*float afDistance = (mpMover->GetCharBody()->GetPosition() - avPosition).Length(); if(afVolume >= mpEnemyWorm->mfIdleMinHearVolume && afDistance > 0.4f) { mpEnemy->SetTempPosition(avPosition); mpEnemy->ChangeState(STATE_INVESTIGATE); return true; }*/ return false; } void iGameEnemyState_Worm_Base::OnTakeHit(float afDamage) { } void iGameEnemyState_Worm_Base::OnFlashlight(const cVector3f &avPosition) { // mpInit->mpEffectHandler->GetSubTitle()->Add("Flashlight!",0.5f); OnSeePlayer(mpPlayer->GetCharacterBody()->GetFeetPosition(), 1.0f); } void iGameEnemyState_Worm_Base::OnDeath(float afDamage) { // mpEnemy->ChangeState(STATE_KNOCKDOWN); mpEnemy->ChangeState(STATE_DEAD); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // IDLE STATE ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cGameEnemyState_Worm_Idle::OnEnterState(iGameEnemyState *apPrevState) { // Animation mpEnemy->PlayAnim("Idle", true, 0.2f); // Setup body mpEnemy->SetupBody(); // Setup enemy mpEnemy->SetFOV(mpEnemyWorm->mfIdleFOV); // mpInit->mpMusicHandler->RemoveAttacker(mpEnemy); } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Idle::OnLeaveState(iGameEnemyState *apNextState) { } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Idle::OnUpdate(float afTimeStep) { mpEnemy->ChangeState(STATE_HUNT); return; if (mpMover->IsMoving() == false || mpMover->GetStuckCounter() > 2.0f) { mpMover->ResetStuckCounter(); if (mbStopped == false) { mbStopped = true; mpEnemy->PlayAnim("Idle", false, 0.9f); mfNextWalkTime = cMath::RandRectf(mpEnemyWorm->mfIdleMinWaitLength, mpEnemyWorm->mfIdleMaxWaitLength); } else if (mfNextWalkTime <= 0) { mbStopped = false; // Animation mpEnemy->UseMoveStateAnimations(); // Setup body mpEnemy->SetupBody(); mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyWorm->mfHuntSpeed); cAINode *pNode = NULL; if (mpEnemy->GetPatrolNodeNum() == 0) { pNode = mpMover->GetAINodeInRange(1, 5); } else { int lNodeNum = cMath::RandRectl(0, mpEnemy->GetPatrolNodeNum() - 1); tString sName = mpEnemy->GetPatrolNode(lNodeNum)->msNodeName; pNode = mpMover->GetNodeContainer()->GetNodeFromName(sName); } if (pNode) { mpMover->MoveToPos(pNode->GetPosition()); } else { mpEnemy->ChangeState(STATE_IDLE); } } else { mfNextWalkTime -= afTimeStep; } } } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // HUNT STATE ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cGameEnemyState_Worm_Hunt::OnEnterState(iGameEnemyState *apPrevState) { // Animation mpEnemy->PlayAnim("Idle", true, 0.2f); float fMul = 1.0f; // Setup body mpEnemy->SetupBody(); if (mpInit->mDifficulty == eGameDifficulty_Easy) mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyWorm->mfHuntSpeed * 0.7f * fMul); else if (mpInit->mDifficulty == eGameDifficulty_Normal) mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyWorm->mfHuntSpeed * fMul); else mpMover->GetCharBody()->SetMaxPositiveMoveSpeed(eCharDir_Forward, mpEnemyWorm->mfHuntSpeed * 1.2f * fMul); // Setup enemy mpEnemy->SetFOV(mpEnemyWorm->mfHuntFOV); mfUpdatePathCount = 0; mfUpdateFreq = 1.0f; mbFreePlayerPath = false; mbLostPlayer = false; mfLostPlayerCount = 0; mfMaxLostPlayerCount = mpEnemyWorm->mfHuntForLostPlayerTime; mpInit->mpMusicHandler->AddAttacker(mpEnemy); mfAttackCount = mpEnemyWorm->mfAttackInterval; mfAttackSoundCount = 0; mfSoundCount = cMath::RandRectf(mpEnemyWorm->mfHuntSoundMinInteraval, mpEnemyWorm->mfHuntSoundMaxInteraval); } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Hunt::OnLeaveState(iGameEnemyState *apNextState) { } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Hunt::OnUpdate(float afTimeStep) { // cAnimationState *pState = mpEnemy->GetMeshEntity()->GetAnimationState(0); // Log("Anim: %s %f %d\n",pState->GetName(),pState->GetTimePosition(),pState->IsActive()); mpEnemy->SetLastPlayerPos(mpInit->mpPlayer->GetCharacterBody()->GetFeetPosition()); if (mpPlayer->GetHealth() <= 0) { mpEnemy->ChangeState(STATE_IDLE); return; } //////////////////////////////// // Check if stuck if (mpMover->GetStuckCounter() > 2.1f) { // mpEnemy->ChangeState(STATE_FLEE); mpMover->ResetStuckCounter(); return; } ////////////////////////////// // Hunt sound count if (mfSoundCount <= 0) { mfSoundCount = cMath::RandRectf(mpEnemyWorm->mfHuntSoundMinInteraval, mpEnemyWorm->mfHuntSoundMaxInteraval); mpEnemy->PlaySound(mpEnemyWorm->msHuntSound); } else { mfSoundCount -= afTimeStep; } ////////////////////////////// // Attack sound count if (mfAttackSoundCount > 0) { mfAttackSoundCount -= afTimeStep; } //////////////////////////////// // Check if attack should begin if (mfAttackCount <= 0) { mfAttackCount = mpEnemyWorm->mfAttackInterval; cVector3f vPos = mpMover->GetCharBody()->GetPosition() + mpMover->GetCharBody()->GetForward() * (0 + mpEnemyWorm->mvAttackDamageSize.z / 2.0f); 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(mpEnemyWorm->GetAttackShape(), mtxOffset, mpMover->GetCharBody()->GetPosition(), mpEnemyWorm->mfAttackDamage, mpEnemyWorm->mfAttackMinMass, mpEnemyWorm->mfAttackMaxMass, mpEnemyWorm->mfAttackMinImpulse, mpEnemyWorm->mfAttackMaxImpulse, mpEnemyWorm->mlAttackStrength, target, NULL)) { if (mfAttackSoundCount <= 0) { mpEnemy->PlaySound(mpEnemyWorm->msAttackHitSound); mfAttackSoundCount = mpEnemyWorm->mfAttackHitSoundInterval; } } } else { mfAttackCount -= afTimeStep; } //////////////////////////////// // Update the path if (mfUpdatePathCount <= 0) { mfUpdatePathCount = mfUpdateFreq; /*cAINodeContainer *pNodeCont = */ mpEnemy->GetMover()->GetNodeContainer(); // Check if there is a free path to the player if (mbLostPlayer == false && mpMover->FreeDirectPathToChar(mpPlayer->GetCharacterBody())) { mbFreePlayerPath = true; mpMover->Stop(); } else { mbFreePlayerPath = false; } // Get path to player if (mbFreePlayerPath == false && mbLostPlayer == false) { if (mpMover->MoveToPos(mpEnemy->GetLastPlayerPos()) == false) { mfUpdatePathCount *= 2.0f; } } } else { mfUpdatePathCount -= afTimeStep; } //////////////////////////////// // Go directly towards the player if (mbFreePlayerPath) { // Go towards player mpMover->MoveDirectToPos(mpPlayer->GetCharacterBody()->GetFeetPosition(), afTimeStep); } //////////////////////////////// // Update path search else { if (mbLostPlayer == false && mpMover->IsMoving() == false) { mbLostPlayer = true; mfLostPlayerCount = mfMaxLostPlayerCount; } if (mbLostPlayer) { mpMover->GetCharBody()->Move(eCharDir_Forward, 1.0f, afTimeStep); mfLostPlayerCount -= afTimeStep; if (mfLostPlayerCount <= 0 || mpMover->GetStuckCounter() > 0.5f) { } } } } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Hunt::OnSeePlayer(const cVector3f &avPosition, float afChance) { if (mbLostPlayer && afChance >= mpEnemyWorm->mfHuntMinSeeChance) { mbLostPlayer = false; mfUpdatePathCount = 0; } } //----------------------------------------------------------------------- bool cGameEnemyState_Worm_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 >= mpEnemyWorm->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_Worm_Hunt::OnDraw() { // mpInit->mpDefaultFont->Draw(cVector3f(230,10,100),14,cColor(1,1,1,1),eFontAlign_Left, // "Freepath: %d",mbFreePlayerPath); } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Hunt::OnPostSceneDraw() { cCamera3D *pCamera = static_cast(mpInit->mpGame->GetScene()->GetCamera()); cVector3f vPos = mpMover->GetCharBody()->GetPosition() + mpMover->GetCharBody()->GetForward() * (0 + mpEnemyWorm->mvAttackDamageSize.z / 2.0f); 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 = mpEnemyWorm->GetAttackShape()->GetSize(); mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize * 0.5f, vSize * -0.5f, cColor(1, 0, 1, 1)); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // DEAD STATE ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cGameEnemyState_Worm_Dead::OnEnterState(iGameEnemyState *apPrevState) { // Animation mpEnemy->PlayAnim("Idle", true, 0.2f); // Setup body mpInit->mpMusicHandler->RemoveAttacker(mpEnemy); if (mpEnemyWorm->mpMoveSound) mpEnemyWorm->mpMoveSound->Stop(false); mpEnemyWorm->mpMoveSound = NULL; } //----------------------------------------------------------------------- void cGameEnemyState_Worm_Dead::OnLeaveState(iGameEnemyState *apNextState) { } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cGameEnemy_Worm::cGameEnemy_Worm(cInit *apInit, const tString &asName, TiXmlElement *apGameElem) : iGameEnemy(apInit, asName, apGameElem) { mpMoveSound = NULL; LoadBaseProperties(apGameElem); mbSetFeetAtGroundOnStart = false; mbAttachMeshToBody = false; mbRemoveAttackerOnDisable = false; ////////////////////////////// // State properties msMoveSound = cString::ToString(apGameElem->Attribute("MoveSound"), ""); 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); mfHuntFOV = cMath::ToRad(cString::ToFloat(apGameElem->Attribute("HuntFOV"), 0)); mfHuntSpeed = cString::ToFloat(apGameElem->Attribute("HuntSpeed"), 0); mfHuntMinSeeChance = cString::ToFloat(apGameElem->Attribute("IdleMinSeeChance"), 0); mfHuntMinHearVolume = cString::ToFloat(apGameElem->Attribute("IdleMinHearVolume"), 0); msHuntSound = cString::ToString(apGameElem->Attribute("HuntSound"), ""); mfHuntSoundMinInteraval = cString::ToFloat(apGameElem->Attribute("HuntSoundMinInteraval"), 0); mfHuntSoundMaxInteraval = cString::ToFloat(apGameElem->Attribute("HuntSoundMaxInteraval"), 0); mfAttackInterval = cString::ToFloat(apGameElem->Attribute("AttackInterval"), 0); mfAttackDamage = cString::ToFloat(apGameElem->Attribute("AttackDamage"), 0); msAttackHitSound = cString::ToString(apGameElem->Attribute("AttackHitSound"), ""); mfAttackHitSoundInterval = cString::ToFloat(apGameElem->Attribute("AttackHitSoundInterval"), 0); 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); mvAttackDamageSize = cString::ToVector3f(apGameElem->Attribute("AttackDamageSize"), 0); ////////////////////////////// // Set up states AddState(hplNew(cGameEnemyState_Worm_Idle, (STATE_IDLE, mpInit, this))); AddState(hplNew(cGameEnemyState_Worm_Hunt, (STATE_HUNT, mpInit, this))); AddState(hplNew(cGameEnemyState_Worm_Dead, (STATE_DEAD, mpInit, this))); ///////////////////////////// // Internal variables mvLastForward = cVector3f(0, 0, 1); mlMaxSegmentPositions = 20; mfTurnSpeed = cMath::ToRad(160.0f); } //----------------------------------------------------------------------- cGameEnemy_Worm::~cGameEnemy_Worm() { if (mpMoveSound) mpInit->mpGame->GetScene()->GetWorld3D()->DestroySoundEntity(mpMoveSound); STLDeleteAll(mvTailSegments); hplDelete(mpMeshCallback); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // MESH ENTITY CALLBACK ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cGameEnemy_Worm_MeshCallback::cGameEnemy_Worm_MeshCallback(cGameEnemy_Worm *apWorm) { mpWorm = apWorm; } //----------------------------------------------------------------------- /*OLD CALC MATRIX VERSION: cMatrixf mtxRot = cMatrixf::Identity; mtxRot.SetRight(mpWorm->mvRootRight*-1); // *-1 = straange mtxRot.SetUp(mpWorm->mvRootUp); mtxRot.SetForward(mpWorm->mvRootForward); mtxRot = cMath::MatrixInverse(mtxRot); iCharacterBody *pCharBody = mpWorm->mpMover->GetCharBody(); cMeshEntity *pEntity = mpWorm->mpMeshEntity; cMatrixf mtxEntity = mtxRot; //mtxEntity = cMath::MatrixMul(pCharBody->GetEntityPostOffset(),mtxEntity); mtxEntity.SetTranslation(mtxEntity.GetTranslation() + mpWorm->mvRootPosition + pCharBody->GetEntityOffset().GetTranslation()); //mtxEntity = cMath::MatrixMul(mtxEntity,pCharBody->GetEntityOffset()); //pEntity->GetRootNode()->SetMatrix(mtxEntity); pEntity->SetMatrix(mtxEntity); //mpWorm->mpRootBone->SetWorldMatrix(mtxEntity); */ void cGameEnemy_Worm_MeshCallback::AfterAnimationUpdate(cMeshEntity *apMeshEntity, float afTimeStep) { // Set bone matrix of root // if(mpWorm->mbAttachMeshToBody==false) if (mpWorm->mpMover->GetCharBody()->GetEntity() == NULL) { iCharacterBody *pCharBody = mpWorm->mpMover->GetCharBody(); cMeshEntity *pEntity = mpWorm->mpMeshEntity; cMatrixf mtxEntity = cMatrixf::Identity; cVector3f vAngles = cMath::GetAngleFromPoints3D(0, mpWorm->mvRootForward); mtxEntity = cMath::MatrixRotate(cVector3f(-vAngles.x, kPif + vAngles.y, 0), eEulerRotationOrder_XYZ); mtxEntity.SetTranslation(mtxEntity.GetTranslation() + mpWorm->mvRootPosition + pCharBody->GetEntityOffset().GetTranslation()); pEntity->SetMatrix(mtxEntity); } // return; // Set bone matrix of segments for (size_t i = 0; i < mpWorm->mvTailSegments.size(); ++i) { cWormTailSegment *pSegment = mpWorm->mvTailSegments[i]; ////////////////////////////////////////////////// // Change orientation according to forward cMatrixf mtxTrans = cMatrixf::Identity; { /*mtxTrans.SetRight(pSegment->mvRight*-1); // *-1 = straaange mtxTrans.SetUp(pSegment->mvUp); mtxTrans.SetForward(pSegment->mvForward); mtxTrans = cMath::MatrixInverse(mtxTrans);*/ cVector3f vAngles = cMath::GetAngleFromPoints3D(0, pSegment->mvForward); mtxTrans = cMath::MatrixRotate(cVector3f(-vAngles.x, kPif + vAngles.y, 0), eEulerRotationOrder_XYZ); mtxTrans = cMath::MatrixMul(mtxTrans, pSegment->m_mtxBaseRot); } //////////////////////////////////////// // Set world matrix of bone mtxTrans.SetTranslation(pSegment->mvPosition); pSegment->mpBone->SetWorldMatrix(mtxTrans); } } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cVector3Smoother::cVector3Smoother() { mlMaxVecs = 20; } void cVector3Smoother::Add(const cVector3f &avVec) { mlstVecs.push_back(avVec); if ((int)mlstVecs.size() > mlMaxVecs) mlstVecs.pop_front(); } cVector3f cVector3Smoother::GetAverage() { cVector3f vAverage = 0; Common::List::iterator it = mlstVecs.begin(); for (; it != mlstVecs.end(); ++it) { vAverage += *it; } return vAverage / (float)mlstVecs.size(); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cGameEnemy_Worm::OnLoad() { mbFirstUpdate = true; mvFirstUpdatePos = mpMover->GetCharBody()->GetPosition(); mvRootPosition = mvFirstUpdatePos; mvLastForward = mpMover->GetCharBody()->GetForward(); // Create attack shape iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); mpAttackShape = pPhysicsWorld->CreateBoxShape(mvAttackDamageSize, NULL); mpMeshCallback = hplNew(cGameEnemy_Worm_MeshCallback, (this)); mpMeshEntity->SetCallback(mpMeshCallback); // Set up enemy ChangeState(STATE_IDLE); SetupTail(); } //----------------------------------------------------------------------- cVector3f gvBackPos, gvPrevBackPos; void cGameEnemy_Worm::OnUpdate(float afTimeStep) { cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D(); iCharacterBody *pCharBody = mpMover->GetCharBody(); cVector3f vMovement; //////////////////////////////////////// // Update movesound if (mlCurrentState == STATE_HUNT) { if (mpMoveSound == NULL) { mpMoveSound = pWorld->CreateSoundEntity("WormMove", msMoveSound, true); // Log("created move sound...\n"); } else { if (pWorld->SoundEntityExists(mpMoveSound)) { // Log("Updating move sound..."); mpMoveSound->SetPosition(pCharBody->GetPosition()); // Log("done\n"); } else { mpMoveSound = NULL; } } } else { if (mpMoveSound) { pWorld->DestroySoundEntity(mpMoveSound); mpMoveSound = NULL; } } //////////////////////////////////////// // Check so the body is moving if (pCharBody->GetForceVelocity().Length() < 0.0001f && pCharBody->GetMoveSpeed(eCharDir_Forward) < 0.001f) { return; } ///////////////////////////////////////// // Get the position of the worm cVector3f vPrevRootPos = mvRootPosition; // Add newer position to list if (mbFirstUpdate) { mlstRootPositions.push_back(mvFirstUpdatePos); mbFirstUpdate = false; } else mlstRootPositions.push_back(pCharBody->GetPosition()); if ((int)mlstRootPositions.size() > mlMaxSegmentPositions) mlstRootPositions.pop_front(); // Get smooth position mvRootPosition = 0; Common::List::iterator posIt = mlstRootPositions.begin(); for (; posIt != mlstRootPositions.end(); ++posIt) { mvRootPosition += *posIt; } mvRootPosition = mvRootPosition / (float)mlstRootPositions.size(); // Get movement this update vMovement = mvRootPosition - vPrevRootPos; ///////////////////////////////////////// // Calculate direction vectors if (vMovement.SqrLength() > 0.00001f) mvRootGoalForward = cMath::Vector3Normalize(vMovement); // else // mvRootGoalForward = mvRootGoalForward; // Rotate vectors to get closer to goal { float fAngleDist = cMath::Vector3Angle(mvRootForward, mvRootGoalForward); if (fAngleDist > 0.001f) { // Iterate to split the turning into 10 smaller parts cVector3f vTurnVec = cMath::Vector3Cross(mvRootForward, mvRootGoalForward); cQuaternion qRotation; if (fAngleDist <= afTimeStep * mfTurnSpeed) qRotation = cQuaternion(fAngleDist, vTurnVec); else qRotation = cQuaternion(afTimeStep * mfTurnSpeed, vTurnVec); cMatrixf mtxRot = cMath::MatrixQuaternion(qRotation); mvRootForward = cMath::MatrixMul(mtxRot, mvRootForward); // mvRootForward.y=0; mvRootForward.Normalise(); /*cVector3f vLastRight = mvRootRight; mvRootRight = cMath::MatrixMul( mtxRot, mvRootRight); mvRootRight.y =0; mvRootRight.Normalise(); //Make sure x-z plane mvRootUp = cMath::Vector3Cross(mvRootRight,mvRootForward); mvRootUp.Normalise();*/ } } //////////////////////////////////////// // Get the ass position and direction cVector3f vSegForward = mvRootForward; cVector3f vSegBackPos = mpRootBone->GetWorldPosition() + vSegForward * -mvTailSegments[0]->mfDistToFront; gvBackPos = vSegBackPos; ///////////////////////////////////////// // Iterate the segments. for (size_t i = 0; i < mvTailSegments.size(); ++i) { cWormTailSegment *pSegment = mvTailSegments[i]; ////////////////////////////////// // Change position of segment // Get add newer pos and smooth all the previous cVector3f vPrevPos = pSegment->mvPosition; pSegment->mlstPositions.push_back(vSegBackPos); if ((int)pSegment->mlstPositions.size() > mlMaxSegmentPositions) { pSegment->mlstPositions.pop_front(); } pSegment->mvPosition = 0; Common::List::iterator posIt2 = pSegment->mlstPositions.begin(); for (; posIt2 != pSegment->mlstPositions.end(); ++posIt2) { pSegment->mvPosition += *posIt2; } pSegment->mvPosition = pSegment->mvPosition / (float)pSegment->mlstPositions.size(); ///////////////////////////////////////////// // Get the movement vector cVector3f vSegMovement = pSegment->mvPosition - vPrevPos; ///////////////////////////////////////////// // Update body position pSegment->mpBody->SetPosition(pSegment->mvPosition); ////////////////////////////////////////////// // Get the direction vector of the segment { if (vSegMovement.SqrLength() > 0.00001f) pSegment->mvGoalForward = cMath::Vector3Normalize(vSegMovement); // Used for more stiff worms // pSegment->mvGoalForward = vSegForward; float fAngleDist = cMath::Vector3Angle(pSegment->mvForward, pSegment->mvGoalForward); if (fAngleDist > 0.001f) { cVector3f vTurnVec = cMath::Vector3Cross(pSegment->mvForward, pSegment->mvGoalForward); cQuaternion qRotation; if (fAngleDist <= afTimeStep * mfTurnSpeed) qRotation = cQuaternion(fAngleDist, vTurnVec); else qRotation = cQuaternion(afTimeStep * mfTurnSpeed, vTurnVec); pSegment->mvForward = cMath::MatrixMul(cMath::MatrixQuaternion(qRotation), pSegment->mvForward); pSegment->mvForward.Normalise(); /*pSegment->mvRight = cMath::MatrixMul( cMath::MatrixQuaternion(qRotation), pSegment->mvRight); pSegment->mvRight.y =0; pSegment->mvRight.Normalise(); pSegment->mvUp = cMath::Vector3Cross(pSegment->mvRight,pSegment->mvForward); pSegment->mvUp.Normalise();*/ } } vSegForward = pSegment->mvForward; //////////////////////////////////////// // Set the New back pos if (i < mvTailSegments.size() - 1) { vSegBackPos = pSegment->mvPosition + pSegment->mvForward * -mvTailSegments[i + 1]->mfDistToFront; } } } //----------------------------------------------------------------------- void cGameEnemy_Worm::ExtraPostSceneDraw() { iLowLevelGraphics *pLowLevelGfx = mpInit->mpGame->GetGraphics()->GetLowLevel(); pLowLevelGfx->SetDepthTestActive(false); // pLowLevelGfx->DrawLine(mpRootBone->GetWorldPosition(),gvBackPos,cColor(1,1)); pLowLevelGfx->DrawLine(mpRootBone->GetWorldPosition(), mpRootBone->GetWorldPosition() + mvRootForward, cColor(0, 0, 1, 1)); pLowLevelGfx->DrawLine(mpRootBone->GetWorldPosition(), mpRootBone->GetWorldPosition() + mvRootUp, cColor(0, 1, 0, 1)); pLowLevelGfx->DrawLine(mpRootBone->GetWorldPosition(), mpRootBone->GetWorldPosition() + mvRootRight, cColor(1, 0, 0, 1)); pLowLevelGfx->DrawSphere(mpRootBone->GetWorldPosition(), 0.3f, cColor(1, 0, 1, 1)); for (size_t i = 0; i < mvTailSegments.size(); ++i) { cWormTailSegment *pSegment = mvTailSegments[i]; pLowLevelGfx->DrawSphere(pSegment->mvPosition, 0.3f, cColor(1, 1)); pLowLevelGfx->DrawLine(pSegment->mvPosition, pSegment->mvPosition + pSegment->mvForward * 0.5f, cColor(1, 0, 1, 1)); cVector3f vForward = cMath::MatrixMul(cMatrixf::Identity, pSegment->mvForward); cVector3f vRight = cMath::MatrixMul(cMatrixf::Identity, pSegment->mvRight); cVector3f vUp = cMath::MatrixMul(cMatrixf::Identity, pSegment->mvUp); cVector3f vPos = pSegment->mpBone->GetWorldPosition(); pLowLevelGfx->DrawLine(vPos, vPos + vRight * 0.6f, cColor(1, 0, 0, 1)); pLowLevelGfx->DrawLine(vPos, vPos + vUp * 0.6f, cColor(0, 1, 0, 1)); pLowLevelGfx->DrawLine(vPos, vPos + vForward * 0.6f, cColor(0, 0, 1, 1)); // pLowLevelGfx->DrawLine( pSegment->mvPostion,pSegment->mvBackPos,cColor(0,0,1,1)); } pLowLevelGfx->SetDepthTestActive(true); } //----------------------------------------------------------------------- void cGameEnemy_Worm::ShowPlayer(const cVector3f &avPlayerFeetPos) { if (mlCurrentState == STATE_IDLE || mlCurrentState == STATE_PATROL || mlCurrentState == STATE_INVESTIGATE) { mvLastPlayerPos = avPlayerFeetPos; ChangeState(STATE_HUNT); } } //----------------------------------------------------------------------- bool cGameEnemy_Worm::MoveToPos(const cVector3f &avFeetPos) { if (mlCurrentState == STATE_IDLE || mlCurrentState == STATE_PATROL) { SetTempPosition(avFeetPos); ChangeState(STATE_INVESTIGATE); return true; } else { return false; } } //----------------------------------------------------------------------- bool cGameEnemy_Worm::IsFighting() { if (mfHealth <= 0 || IsActive() == false) return false; if (mlCurrentState == STATE_IDLE || mlCurrentState == STATE_PATROL || mlCurrentState == STATE_INVESTIGATE) return false; return true; } //----------------------------------------------------------------------- void cGameEnemy_Worm::SetupTail() { // Log("Setting up tail!\n"); iCharacterBody *pCharBody = mpMover->GetCharBody(); cMeshEntity *pEntity = mpMeshEntity; // Set up character body pCharBody->SetCollideCharacter(false); // Set up mesh entity matrix. // Do not set any rotation here since then the base // bone matrices will not be correct. if (mbAttachMeshToBody == false) { cMatrixf mtxEntity = cMatrixf::Identity; mtxEntity.SetTranslation(pCharBody->GetPosition() + pCharBody->GetEntityOffset().GetTranslation()); pEntity->SetMatrix(mtxEntity); } // Get root bone and directions mpRootBone = mpMeshEntity->GetBoneStateFromName("Root"); mvRootForward = mpMover->GetCharBody()->GetForward(); mvRootUp = mpMover->GetCharBody()->GetUp(); mvRootRight = mpMover->GetCharBody()->GetRight(); mvTailSegments.resize(7); for (int i = 0; i < 7; ++i) { // Create and set to right data mvTailSegments[i] = hplNew(cWormTailSegment, ()); cWormTailSegment *pSegment = mvTailSegments[i]; if (i == 0) { mpRootSegment = pSegment; } else { mvTailSegments[i - 1]->mpChildSegment = pSegment; } ///////////////////////////////////////// // Set up // Get bones tString sBoneName = "Tail0" + cString::ToString(i + 1); pSegment->mpBone = mpMeshEntity->GetBoneStateFromName(sBoneName); pSegment->mpBone->SetActive(false); // Start Position,forward and rotation pSegment->mvPosition = pSegment->mpBone->GetWorldPosition(); pSegment->mvForward = mpMover->GetCharBody()->GetForward(); pSegment->mvUp = mpMover->GetCharBody()->GetUp(); pSegment->mvRight = mpMover->GetCharBody()->GetRight(); pSegment->mvGoalForward = pSegment->mvForward; // Get the rotation it needs to be pointed forward. pSegment->m_mtxBaseRot = mvTailSegments[i]->mpBone->GetWorldMatrix().GetRotation(); // Create body iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); iCollideShape *pShape = pPhysicsWorld->CreateSphereShape(pCharBody->GetSize().x / 2.0f, NULL); pSegment->mpBody = pPhysicsWorld->CreateBody("Tail0" + cString::ToString(i + 1), pShape); pSegment->mpBody->SetMass(0); pSegment->mpBody->SetPosition(pSegment->mvPosition); pSegment->mpBody->SetIsCharacter(true); pSegment->mpBody->SetActive(IsActive()); mvBodies.push_back(pSegment->mpBody); if (i == 0) { pSegment->mfDistToFront = cMath::Vector3Dist(mpRootBone->GetWorldPosition(), pSegment->mpBone->GetWorldPosition()); } else { pSegment->mfDistToFront = cMath::Vector3Dist( mvTailSegments[i - 1]->mpBone->GetWorldPosition(), pSegment->mpBone->GetWorldPosition()); } // Log("%d Dist: %f Fwd: %s\n",i,pSegment->mfDistToFront,pSegment->mvForward.ToString().c_str()); } } //-----------------------------------------------------------------------