/* * Copyright (C) 2006-2010 - Frictional Games * * This file is part of Penumbra Overture. * * Penumbra Overture is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Penumbra Overture is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Penumbra Overture. If not, see . */ #include "AttackHandler.h" #include "Init.h" #include "MapHandler.h" #include "Player.h" #include "GameEnemy.h" #include "GameObject.h" #include "GameSwingDoor.h" ////////////////////////////////////////////////////////////////////////// // RAY CALLBACK ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cAttackRayCallback::cAttackRayCallback() { mbSkipCharacter = false; } //----------------------------------------------------------------------- void cAttackRayCallback::Reset() { mpClosestBody = NULL; mbSkipCharacter = false; } //----------------------------------------------------------------------- bool cAttackRayCallback::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams) { if(pBody->GetCollide()==false) return true; if(pBody == mpSkipBody) return true; if(mbSkipCharacter && pBody->IsCharacter()) return true; if(apParams->mfDist < mfShortestDist || mpClosestBody == NULL) { mpClosestBody = pBody; mfShortestDist = apParams->mfDist; mvPosition = apParams->mvPoint; } return true; } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // SPLASH DAMAGE BLOCK CHECK ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cSplashDamageBlockCheck::cSplashDamageBlockCheck(cInit *apInit) { mpInit = apInit; mbIntersected = false; } //----------------------------------------------------------------------- bool cSplashDamageBlockCheck::CheckBlock(const cVector3f& avStart, const cVector3f&avEnd) { mbIntersected = false; iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); pWorld->CastRay(this,avStart,avEnd,false,false,false, true); return mbIntersected; } //----------------------------------------------------------------------- bool cSplashDamageBlockCheck::BeforeIntersect(iPhysicsBody *pBody) { if(pBody->IsCharacter() || pBody->GetMass() != 0 || pBody->GetBlocksSound()==false) { return false; } return true; } //----------------------------------------------------------------------- bool cSplashDamageBlockCheck::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams) { mbIntersected = true; return false; } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cAttackHandler::cAttackHandler(cInit *apInit) : iUpdateable("AttackHandler") { mpInit = apInit; mpSplashBlockCheck = hplNew( cSplashDamageBlockCheck,(apInit) ); } //----------------------------------------------------------------------- cAttackHandler::~cAttackHandler(void) { hplDelete( mpSplashBlockCheck ); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- bool cAttackHandler::CreateLineAttack(const cVector3f& avStart, const cVector3f& avEnd, float afDamage, eAttackTargetFlag aTarget, iPhysicsBody *apSkipBody, iPhysicsBody **apPickedBody) { iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); mRayCallback.mTarget = aTarget; mRayCallback.mpSkipBody = apSkipBody; if(apPickedBody)*apPickedBody = NULL; mRayCallback.Reset(); pPhysicsWorld->CastRay(&mRayCallback,avStart,avEnd,true,false,true); if(mRayCallback.mpClosestBody == NULL) return false; if(apPickedBody) *apPickedBody = mRayCallback.mpClosestBody; if(aTarget & eAttackTargetFlag_Player) { if(mpInit->mpPlayer->GetCharacterBody()->GetBody() == mRayCallback.mpClosestBody) { if(afDamage>0) mpInit->mpPlayer->Damage(afDamage,ePlayerDamageType_BloodSplash); return true; } } if(aTarget & eAttackTargetFlag_Enemy) { } return false; } //----------------------------------------------------------------------- bool cAttackHandler::CreateShapeAttack( iCollideShape *apShape, const cMatrixf& a_mtxOffset, const cVector3f &avOrigin, float afDamage, float afMinMass, float afMaxMass, float afMinImpulse, float afMaxImpulse, int alStrength, eAttackTargetFlag aTarget, iPhysicsBody *apSkipBody) { bool bHit = false; tPhysicsBodyList lstBodies; mpLastSwingDoor = NULL; /////////////////////////////// //Set up boudning box cBoundingVolume tempBV = apShape->GetBoundingVolume(); tempBV.SetTransform(a_mtxOffset); /////////////////////////////// //Iterate bodies cCollideData collideData; collideData.SetMaxSize(1); cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D(); iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld(); //Get bodies and add to list, this incase the portal contaniner gets changed. std::list lstTempBodies; cPhysicsBodyIterator bodyIt = pPhysicsWorld->GetBodyIterator(); while(bodyIt.HasNext()) { iPhysicsBody *pBody = static_cast(bodyIt.Next()); lstTempBodies.push_back(pBody); } std::list::iterator it = lstTempBodies.begin(); for(; it != lstTempBodies.end(); ++it) { iPhysicsBody *pBody = *it; float fMass = pBody->GetMass(); if(pBody->IsActive()==false) continue; if(pBody->GetCollide()==false) continue; if(cMath::CheckCollisionBV(tempBV, *pBody->GetBV())) { iGameEntity *pEntity = (iGameEntity*)pBody->GetUserData(); /////////////////////////////// //Check for collision if(pPhysicsWorld->CheckShapeCollision(pBody->GetShape(),pBody->GetLocalMatrix(), apShape, a_mtxOffset,collideData,1)==false) { continue; } /////////////////////////// //Player if(aTarget & eAttackTargetFlag_Player) { if(mpInit->mpPlayer->GetCharacterBody()->GetBody() == pBody) { //Check with line if there is a free path, if not skip damage. cVector3f vEnd = pBody->GetWorldPosition(); mRayCallback.Reset(); mRayCallback.mbSkipCharacter = true; pPhysicsWorld->CastRay(&mRayCallback,avOrigin,vEnd,true,false,false); mRayCallback.mbSkipCharacter = false; //Damage if(mRayCallback.mpClosestBody==NULL) { if(afDamage>0) { mpInit->mpPlayer->Damage(afDamage,ePlayerDamageType_BloodSplash); } } //Impulse float fMass = mpInit->mpPlayer->GetCharacterBody()->GetMass(); float fForceSize =0; if(fMass > afMaxMass*10) fForceSize =0; else if(fMass <= afMinMass*10) fForceSize = afMaxImpulse*10; else{ float fT = (fMass - afMinMass*10) / (afMaxMass*10 - afMinMass*10); fForceSize = afMinImpulse*10 * fT + afMaxImpulse*10 * (1-fT); } cVector3f vForceDir = mpInit->mpPlayer->GetCharacterBody()->GetPosition() - avOrigin; vForceDir.Normalise(); vForceDir += cVector3f(0,0.1f,0); mpInit->mpPlayer->GetCharacterBody()->AddForce(vForceDir * fForceSize * 300); bHit = true; continue; } } /////////////////////////// //Enemy if(aTarget & eAttackTargetFlag_Enemy) { } /////////////////////////// //Bodies if(aTarget & eAttackTargetFlag_Bodies) { if(pBody->IsCharacter()==false) { lstBodies.push_back(pBody); if(pEntity) { pEntity->Damage(afDamage,alStrength); if(pEntity->GetType() == eGameEntityType_SwingDoor) { mpLastSwingDoor = static_cast(pEntity); } } bHit = true; } } } } //////////////////////////////// //Iterate bodies hit, this to //spread out the impulse. float fAmount = (float)lstBodies.size(); for(tPhysicsBodyListIt it = lstBodies.begin(); it != lstBodies.end(); ++it) { iPhysicsBody *pBody = *it; //Calculate force float fMass = pBody->GetMass(); float fForceSize =0; if(fMass > afMaxMass) fForceSize =0; else if(fMass <= afMinMass) fForceSize = afMaxImpulse; else{ float fT = (fMass - afMinMass) / (afMaxMass - afMinMass); fForceSize = afMinImpulse * fT + afMaxImpulse * (1-fT); } fForceSize /= fAmount; if(fMass >0 && fForceSize >0) { cVector3f vDir = pBody->GetWorldPosition() - avOrigin; vDir.Normalise(); pBody->AddImpulse(vDir * fForceSize); } } return bHit; } //----------------------------------------------------------------------- bool cAttackHandler::CreateLineDestroyBody(const cVector3f& avStart, const cVector3f& avEnd, float afStrength,float afForce,iPhysicsBody *apSkipBody, iPhysicsBody **apPickedBody) { iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); mRayCallback.mpSkipBody = apSkipBody; if(apPickedBody)*apPickedBody = NULL; mRayCallback.Reset(); pPhysicsWorld->CastRay(&mRayCallback,avStart,avEnd,true,false,true); if(mRayCallback.mpClosestBody == NULL) return false; if(apPickedBody) *apPickedBody = mRayCallback.mpClosestBody; iPhysicsBody *pBody = mRayCallback.mpClosestBody; if(pBody->IsCharacter()==false && pBody->GetMass()>0 && pBody->GetUserData()) { iGameEntity *pEntity = (iGameEntity*)pBody->GetUserData(); if(pEntity->GetType() != eGameEntityType_Object) return false; cGameObject *pObject = static_cast(pEntity); cVector3f vForward = avEnd - avStart; vForward.Normalise(); pBody->AddForce(vForward * afForce); //Destroy object if possible. if(pObject->IsDestroyable() && pObject->GetDestroyStrength() <= afStrength) { for(int i=0; iGetJointNum(); ++i) { iPhysicsJoint *pJoint = pBody->GetJoint(i); pJoint->Break(); } if(pObject->GetInteractMode() == eObjectInteractMode_Move) pObject->SetInteractMode(eObjectInteractMode_Grab); if(pObject->GetDestroySound()!="") { cSoundEntity *pSound = mpInit->mpGame->GetScene()->GetWorld3D()->CreateSoundEntity( "Destroy",pObject->GetDestroySound(), true); if(pSound) pSound->SetPosition(pBody->GetWorldPosition()); } } } return false; } //---------------------------------------------------------------------- static float CalcSize(float afDist, float afMaxDist, float afMinVal,float afMaxVal) { float fMul = 1 - (afDist / afMaxDist); if(fMul<0) return 0; return afMinVal + (afMaxVal - afMinVal)*fMul; } //---------------------------------------------------------------------- void cAttackHandler::CreateSplashDamage(const cVector3f& avCenter, float afRadius, float afMinDamage, float afMaxDamge,float afMinForce, float afMaxForce, float afMaxImpulse, unsigned int aTarget, float afMinMass, int alStrength) { cBoundingVolume tempBV; tempBV.SetSize(afRadius * 2); tempBV.SetPosition(avCenter); if(aTarget & eAttackTargetFlag_Player) { cBoundingVolume* pCharBV = mpInit->mpPlayer->GetCharacterBody()->GetBody()->GetBV(); if(cMath::CheckCollisionBV(tempBV, *pCharBV) && mpSplashBlockCheck->CheckBlock(pCharBV->GetWorldCenter(), avCenter)==false) { float fDist = cMath::Vector3Dist(pCharBV->GetPosition(),avCenter); mpInit->mpPlayer->Damage(CalcSize(fDist,afRadius,afMinDamage,afMaxDamge),ePlayerDamageType_BloodSplash); float fForceSize = CalcSize(fDist,afRadius,afMinForce,afMaxForce); cVector3f vForceDir = mpInit->mpPlayer->GetCharacterBody()->GetPosition() - avCenter; vForceDir.Normalise(); mpInit->mpPlayer->GetCharacterBody()->AddForce(vForceDir * fForceSize * 10); } } //Enemies now have the user data added to the body, this is not needed. /*if(aTarget & eAttackTargetFlag_Enemy) { tGameEnemyIterator it = mpMapHandler->GetGameEnemyIterator(); while(it.HasNext()) { iGameEnemy *pEnemy = it.Next(); cBoundingVolume* pCharBV = pEnemy->GetMover()->GetCharBody()->GetBody()->GetBV(); if(cMath::CheckCollisionBV(tempBV, *pCharBV)) { float fDist = cMath::Vector3Dist(pCharBV->GetPosition(),avCenter); pEnemy->Damage(CalcSize(fDist,afRadius,afMinDamage,afMaxDamge),-1); } } }*/ if(aTarget & eAttackTargetFlag_Bodies) { iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); std::list lstBodies; cPhysicsBodyIterator bodyIt = pWorld->GetBodyIterator(); while(bodyIt.HasNext()) { lstBodies.push_back(bodyIt.Next()); } ////////////////////////// //Damage Iteration std::list::iterator it = lstBodies.begin(); for(; it != lstBodies.end(); ++it) { iPhysicsBody *pBody = *it; //if(pBody->IsCharacter() || pBody->GetMass()==0) continue; //if(pBody->IsCharacter()) continue; if(pBody->IsActive()==false) continue; //if(pBody->GetMass() <= afMinMass) continue; iGameEntity *pEntity = (iGameEntity*)pBody->GetUserData(); if(pEntity && pEntity->IsActive() && cMath::CheckCollisionBV(tempBV, *pBody->GetBV()) && mpSplashBlockCheck->CheckBlock(pBody->GetWorldPosition(), avCenter)==false) { //If enemies are not to be target, skip. if(!(aTarget & eAttackTargetFlag_Enemy) && pEntity->GetType() == eGameEntityType_Enemy) { continue; } float fDist = cMath::Vector3Dist(avCenter,pBody->GetLocalPosition()); /*float fForceSize = CalcSize(fDist,afRadius,afMinForce,afMaxForce); cVector3f vForceDir = pBody->GetLocalPosition() - avCenter; vForceDir.Normalise(); if(fForceSize / pBody->GetMass() > afMaxImpulse) { fForceSize = afMaxImpulse * pBody->GetMass(); } pBody->AddForce(vForceDir * fForceSize);*/ if(pEntity) { float fDamage = CalcSize(fDist,afRadius,afMinDamage,afMaxDamge); pEntity->Damage(fDamage,alStrength); } } } //Update map handler so all stuff that should be broken is. //mpInit->mpMapHandler->Update(1.0f/60.0f); ////////////////////////// //Force Iteration it = lstBodies.begin(); for(; it != lstBodies.end(); ++it) { iPhysicsBody *pBody = *it; if(pBody->IsCharacter() || pBody->GetMass()==0) continue; if(pBody->IsActive()==false) continue; if(pBody->GetMass() <= afMinMass) continue; iGameEntity *pEntity = (iGameEntity*)pBody->GetUserData(); if(cMath::CheckCollisionBV(tempBV, *pBody->GetBV())) { cVector3f vBodyPos = pBody->GetLocalPosition() + cMath::MatrixMul( pBody->GetLocalMatrix().GetRotation(), pBody->GetMassCentre()); float fDist = cMath::Vector3Dist(avCenter,vBodyPos); float fForceSize = CalcSize(fDist,afRadius,afMinForce,afMaxForce); cVector3f vForceDir = vBodyPos - avCenter; vForceDir.Normalise(); if(fForceSize / pBody->GetMass() > afMaxImpulse) { fForceSize = afMaxImpulse * pBody->GetMass(); } pBody->AddForce(vForceDir * fForceSize); if(pEntity) { float fImpulse = (fForceSize/pBody->GetMass())*1.0f/60.0f; if(fImpulse > 15)fImpulse = 15; pEntity->SetLastImpulse(vForceDir * fImpulse); } } } } } //----------------------------------------------------------------------- void cAttackHandler::OnStart() { mpMapHandler = mpInit->mpMapHandler; } //----------------------------------------------------------------------- void cAttackHandler::Update(float afTimeStep) { } //----------------------------------------------------------------------- void cAttackHandler::Reset() { }