/* * 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 "HudModel_Weapon.h" #include "Init.h" #include "Player.h" #include "PlayerHelper.h" #include "AttackHandler.h" #include "GameEntity.h" #include "GameEnemy.h" #include "MapHandler.h" #include "EffectHandler.h" #include "GlobalInit.h" ////////////////////////////////////////////////////////////////////////// // MELEE RAY CALLBACK ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void cMeleeRayCallback::Reset() { mpClosestBody = NULL; } //----------------------------------------------------------------------- bool cMeleeRayCallback::OnIntersect(iPhysicsBody *pBody,cPhysicsRayParams *apParams) { if(pBody->GetCollide()==false) return true; if(pBody->IsCharacter()) return true; if(apParams->mfDist < mfShortestDist || mpClosestBody == NULL) { mpClosestBody = pBody; mfShortestDist = apParams->mfDist; mvPosition = apParams->mvPoint; mvNormal = apParams->mvNormal; } return true; } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // HUD MODEL MELEE WEAPON ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- cHudModel_WeaponMelee::cHudModel_WeaponMelee() : iHudModel(ePlayerHandType_WeaponMelee) { ResetExtraData(); if(gpInit->mbHasHaptics) { mpLowLevelHaptic = gpInit->mpGame->GetHaptic()->GetLowLevel(); mpHHitForce = mpLowLevelHaptic->CreateSinusWaveForce(cVector3f(0,1,0),0.63f,5); mpHHitForce->SetActive(false); } else { mpLowLevelHaptic = NULL; } } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::LoadData(TiXmlElement *apRootElem) { //////////////////////////////////////////////// //Load the MAIN element. TiXmlElement *pMeleeElem = apRootElem->FirstChildElement("MELEE"); if(pMeleeElem==NULL){ Error("Couldn't load MELEE element from XML document\n"); return; } mvHapticSize = cString::ToVector3f(pMeleeElem->Attribute("HapticSize"),0); mvHapticRot = cString::ToVector3f(pMeleeElem->Attribute("HapticRotate"),0); mfHapticScale = cString::ToFloat(pMeleeElem->Attribute("HapticScale"),2); mvHapticRot.x = cMath::ToRad(mvHapticRot.x); mvHapticRot.y = cMath::ToRad(mvHapticRot.y); mvHapticRot.z = cMath::ToRad(mvHapticRot.z); mbDrawDebug = cString::ToBool(pMeleeElem->Attribute("DrawDebug"),false); //////////////////////////////////////////////// //Go through the ATTACK elements. TiXmlElement *pAttackElem = apRootElem->FirstChildElement("ATTACK"); for(; pAttackElem != NULL; pAttackElem = pAttackElem->NextSiblingElement("ATTACK")) { cMeleeWeaponAttack meleeAttack; meleeAttack.mStart = GetPoseFromElem("StartPose",pAttackElem); meleeAttack.mEnd = GetPoseFromElem("EndPose",pAttackElem); meleeAttack.mfAttackLength = cString::ToFloat(pAttackElem->Attribute("AttackLength"),0); meleeAttack.mfChargeLength = cString::ToFloat(pAttackElem->Attribute("ChargeLength"),0); meleeAttack.mfTimeOfAttack = cString::ToFloat(pAttackElem->Attribute("TimeOfAttack"),0); meleeAttack.mfMaxImpulse = cString::ToFloat(pAttackElem->Attribute("MaxImpulse"),0); meleeAttack.mfMinImpulse = cString::ToFloat(pAttackElem->Attribute("MinImpulse"),0); meleeAttack.mfMinMass = cString::ToFloat(pAttackElem->Attribute("MinMass"),0); meleeAttack.mfMaxMass = cString::ToFloat(pAttackElem->Attribute("MaxMass"),0); meleeAttack.mfMinDamage = cString::ToFloat(pAttackElem->Attribute("MinDamage"),0); meleeAttack.mfMaxDamage = cString::ToFloat(pAttackElem->Attribute("MaxDamage"),0); meleeAttack.msSwingSound = cString::ToString(pAttackElem->Attribute("SwingSound"),""); meleeAttack.msChargeSound = cString::ToString(pAttackElem->Attribute("ChargeSound"),""); meleeAttack.msHitSound = cString::ToString(pAttackElem->Attribute("HitSound"),""); meleeAttack.mvSpinMul = cString::ToVector3f(pAttackElem->Attribute("SpinMul"),0); meleeAttack.mfDamageRange = cString::ToFloat(pAttackElem->Attribute("DamageRange"),0); meleeAttack.mvDamageSize = cString::ToVector3f(pAttackElem->Attribute("DamageSize"),0); meleeAttack.mfAttackRange = cString::ToFloat(pAttackElem->Attribute("AttackRange"),0); meleeAttack.mfAttackSpeed = cString::ToFloat(pAttackElem->Attribute("AttackSpeed"),0); meleeAttack.mlAttackStrength = cString::ToInt(pAttackElem->Attribute("AttackStrength"),0); meleeAttack.msHitPS = cString::ToString(pAttackElem->Attribute("HitPS"),""); meleeAttack.mlHitPSPrio = cString::ToInt(pAttackElem->Attribute("HitPSPrio"),0); //Get largest side and use that to make bounding box. float fMax = meleeAttack.mvDamageSize.x; if(fMax < meleeAttack.mvDamageSize.y) fMax = meleeAttack.mvDamageSize.y; if(fMax < meleeAttack.mvDamageSize.z) fMax = meleeAttack.mvDamageSize.z; meleeAttack.mBV.SetSize(fMax * kSqrt2f); mvAttacks.push_back(meleeAttack); } } //----------------------------------------------------------------------- bool cHudModel_WeaponMelee::UpdatePoseMatrix(cMatrixf& aPoseMtx, float afTimeStep) { //////////////////////// //Idle and waiting for movement if(mlAttackState<=1) { return false; } //////////////////// //Movement else { aPoseMtx = cMath::MatrixSlerp(mfTime,m_mtxPrevPose,m_mtxNextPose,true); float fMul = 1.0f; //if(mlAttackState == 2 && mpInit->mDifficulty== eGameDifficulty_Easy) fMul = 1.6f; mfTime += mfMoveSpeed * afTimeStep * fMul; //Attack if(mlAttackState == 4 &&mfTime >= mvAttacks[mlCurrentAttack].mfTimeOfAttack && mbAttacked==false) { Attack(); mbAttacked = true; } //Time is up if(mfTime >= 1.0f) { mfTime =1.0f; switch(mlAttackState) { case 2: mlAttackState = 3; break; case 4: mlAttackState = 5; mbAttacked = false; m_mtxPrevPose = m_mtxNextPose; m_mtxNextPose = mEquipPose.ToMatrix(); mfMoveSpeed = 2; mfTime =0; break; case 5: if(mbButtonDown) mlAttackState = 1; else mlAttackState = 0; break; } } return true; } return false; } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::OnAttackDown() { if(mState == eHudModelState_Idle && mlAttackState ==0) { mlAttackState = 1; mfTime =0; mbButtonDown = true; } } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::OnAttackUp() { if(mpInit->mbSimpleWeaponSwing) { } else { if(mlAttackState != 0 && mlAttackState != 4 && mlAttackState != 5) { mlAttackState = 5; mfMoveSpeed = 2; mfTime =0; m_mtxPrevPose = m_mtxNextPose; m_mtxNextPose = mEquipPose.ToMatrix(); } } mbButtonDown = false; } //----------------------------------------------------------------------- bool cHudModel_WeaponMelee::OnMouseMove(const cVector2f &avMovement) { float fMinMovement = 0.015f; if(mlAttackState ==0 || (mbButtonDown==false && mpInit->mbSimpleWeaponSwing==false)) { return true; } else { ///////////////////////////// //Check for charge if(mlAttackState == 1) { if(mpInit->mbSimpleWeaponSwing) { //if(avMovement.y < -0.03f) // mlCurrentAttack = 2; //else mlCurrentAttack = 0;//cMath::RandRectl(0,1); mlAttackState =2; } //Right charge else if(avMovement.x > fMinMovement) { mlCurrentAttack = 0; mlAttackState = 2; } //Left charge else if(avMovement.x < -fMinMovement) { mlCurrentAttack = 1; mlAttackState = 2; } //Down charge else if(avMovement.y > fMinMovement) { mlCurrentAttack = 2; mlAttackState = 2; } //Go to charge if(mlAttackState==2) { mfTime = 0.0f; mfMoveSpeed = 1/mvAttacks[mlCurrentAttack].mfChargeLength; //if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch) // mfMoveSpeed *= 0.8f; PlaySound(mvAttacks[mlCurrentAttack].msChargeSound); m_mtxPrevPose = mEquipPose.ToMatrix(); m_mtxNextPose = mvAttacks[mlCurrentAttack].mStart.ToMatrix(); } } else if(mlAttackState == 3) { //If right key is down enable looking. cInput *pInput = mpInit->mpGame->GetInput(); if(pInput->IsTriggerd("Examine")) return true; if(mpInit->mbSimpleWeaponSwing) { if(mlCurrentAttack != 2 && pInput->IsTriggerd("Interact")==false) { mfTime = 0.0f; mfMoveSpeed = 1/mvAttacks[mlCurrentAttack].mfChargeLength; //if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch) mfMoveSpeed *= 0.8f; m_mtxPrevPose = mvAttacks[mlCurrentAttack].mStart.ToMatrix(); m_mtxNextPose = mvAttacks[2].mStart.ToMatrix(); mlCurrentAttack = 2; mlAttackState = 2; } else { mlAttackState = 4; } } else if(mlCurrentAttack==0) { if(avMovement.x < -fMinMovement) { mlAttackState = 4; } } else if(mlCurrentAttack==1) { if(avMovement.x > fMinMovement) { mlAttackState = 4; } } else if(mlCurrentAttack==2) { if(avMovement.y < -fMinMovement) { mlAttackState = 4; } } if(mlAttackState == 4) { mfTime = 0.0f; mfMoveSpeed = 1.0f/mvAttacks[mlCurrentAttack].mfAttackLength; //if(mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch) // mfMoveSpeed *= 0.55f; PlaySound(mvAttacks[mlCurrentAttack].msSwingSound); mpInit->mpPlayer->GetHidden()->UnHide(); m_mtxPrevPose = m_mtxNextPose; m_mtxNextPose = mvAttacks[mlCurrentAttack].mEnd.ToMatrix(); } } return mpInit->mbSimpleWeaponSwing; } } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::PlaySound(const tString &asSound) { cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler(); pSoundHandler->PlayGui(asSound,false,1.0f); } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::LoadExtraEntites() { iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); for(size_t i=0; i< mvAttacks.size(); ++i) { //Attack shapes mvAttacks[i].mpCollider = pWorld->CreateBoxShape(mvAttacks[i].mvDamageSize,NULL); //Preload particle system mpInit->PreloadParticleSystem(mvAttacks[i].msHitPS); //Preload sounds mpInit->PreloadSoundEntityData(mvAttacks[i].msHitSound); mpInit->PreloadSoundEntityData(mvAttacks[i].msSwingSound); mpInit->PreloadSoundEntityData(mvAttacks[i].msChargeSound); } } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::DestroyExtraEntities() { iPhysicsWorld *pWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld(); for(size_t i=0; i< mvAttacks.size(); ++i) { if(mvAttacks[i].mpCollider) pWorld->DestroyShape(mvAttacks[i].mpCollider); } } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::PostSceneDraw() { if(mbDrawDebug==false) return; cCamera3D *pCamera = static_cast(mpInit->mpGame->GetScene()->GetCamera()); float fAttackRange = mvAttacks[mlCurrentAttack].mfAttackRange; cVector3f vPos = pCamera->GetPosition() + pCamera->GetForward()*fAttackRange; mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawSphere(vPos,0.1f,cColor(1,0,1,1)); //return; float fDamageRange = mvAttacks[mlCurrentAttack].mfDamageRange; cVector3f vCenter = pCamera->GetPosition() + pCamera->GetForward()*fDamageRange; cMatrixf mtxDamage = cMath::MatrixRotate( cVector3f(pCamera->GetPitch(),pCamera->GetYaw(),pCamera->GetRoll()), eEulerRotationOrder_XYZ); mtxDamage.SetTranslation(vCenter); bool bCollide=false; /*{ cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D(); iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld(); bCollide = pPhysicsWorld->CheckShapeWorldCollision(NULL,mvAttacks[mlCurrentAttack].mpCollider, mtxDamage,NULL,false,false,NULL,false); }*/ cMatrixf mtxCollider = cMath::MatrixMul(pCamera->GetViewMatrix(),mtxDamage); mpInit->mpGame->GetGraphics()->GetLowLevel()->SetMatrix(eMatrix_ModelView,mtxCollider); cVector3f vSize = mvAttacks[mlCurrentAttack].mvDamageSize; if(bCollide) mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize*0.5f,vSize*-0.5f, cColor(0,1,0,1)); else mpInit->mpGame->GetGraphics()->GetLowLevel()->DrawBoxMaxMin(vSize*0.5f,vSize*-0.5f, cColor(1,0,1,1)); } //----------------------------------------------------------------------- bool cHudModel_WeaponMelee::IsAttacking() { if(mlAttackState >1) return true; return false; } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::ResetExtraData() { mlAttackState = 0; mfTime =0; mlCurrentAttack =0; mbButtonDown = false; mbAttacked = false; m_mtxPrevPose = cMatrixf::Identity; m_mtxNextPose = cMatrixf::Identity; mfMoveSpeed = 1.0f; } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::Attack() { mpInit->mbWeaponAttacking = true; //Log("----------------- BEGIN ATTACK WITH WEAPON ------------ \n"); //////////////////////////////// //Set up float fDamageRange = mvAttacks[mlCurrentAttack].mfDamageRange; float fMaxImpulse = mvAttacks[mlCurrentAttack].mfMaxImpulse; float fMinImpulse = mvAttacks[mlCurrentAttack].mfMinImpulse; float fMaxMass = mvAttacks[mlCurrentAttack].mfMaxMass; float fMinMass = mvAttacks[mlCurrentAttack].mfMinMass; cCamera3D *pCamera =mpInit->mpPlayer->GetCamera(); cVector3f vCenter = pCamera->GetPosition() + pCamera->GetForward()*fDamageRange; cBoundingVolume tempBV = mvAttacks[mlCurrentAttack].mBV; tempBV.SetPosition(vCenter); cVector3f vSpinMul = cVector3f(0, 1.0f, 0.0f); vSpinMul = pCamera->GetRight() * vSpinMul.x + pCamera->GetUp() * vSpinMul.y + pCamera->GetForward() * vSpinMul.z; cMatrixf mtxDamage = cMath::MatrixRotate( cVector3f(pCamera->GetPitch(),pCamera->GetYaw(),pCamera->GetRoll()), eEulerRotationOrder_XYZ); mtxDamage.SetTranslation(vCenter); cCollideData collideData; collideData.SetMaxSize(1); bool bHit = false; cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D(); iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld(); tVector3fList lstPostions; //////////////////////////////// //Iterate Enemies tGameEnemyIterator enemyIt = mpInit->mpMapHandler->GetGameEnemyIterator(); while(enemyIt.HasNext()) { iGameEnemy *pEnemy = enemyIt.Next(); iPhysicsBody *pBody = pEnemy->GetMover()->GetCharBody()->GetBody(); float fMass = pBody->GetMass(); if(pEnemy->GetMover()->GetCharBody()->IsActive()==false) continue; if(cMath::CheckCollisionBV(tempBV, *pBody->GetBV())) { /*if(pPhysicsWorld->CheckShapeCollision(pBody->GetShape(),pBody->GetLocalMatrix(), mvAttacks[mlCurrentAttack].mpCollider, mtxDamage,collideData,1)==false) { continue; }*/ if(pEnemy->GetMeshEntity()->CheckColliderShapeCollision(pPhysicsWorld, mvAttacks[mlCurrentAttack].mpCollider, mtxDamage,&lstPostions,NULL)==false) { continue; } //Calculate force float fForceSize =0; if(fMass > fMaxMass) fForceSize =0; else if(fMass <= fMinMass) fForceSize = fMaxImpulse; else{ float fT = (fMass - fMinMass) / (fMaxMass - fMinMass); fForceSize = fMinImpulse * fT + fMaxImpulse * (1-fT); } cVector3f vForceDir = pCamera->GetForward(); vForceDir.Normalise(); //Add force to bodies for(int i=0; i < pEnemy->GetBodyNum(); ++i) { iPhysicsBody* pBody = pEnemy->GetBody(i); pBody->AddImpulse(vForceDir *fForceSize*0.5f); cVector3f vTorque = vSpinMul * fMass * fForceSize *0.5f; pBody->AddTorque(vTorque); } //Calculate damage float fDamage = cMath::RandRectf( mvAttacks[mlCurrentAttack].mfMinDamage, mvAttacks[mlCurrentAttack].mfMaxDamage); pEnemy->Damage(fDamage,mvAttacks[mlCurrentAttack].mlAttackStrength); //Get closest position float fClosestDist = 9999.0f; cVector3f vClosestPostion = vCenter; for(tVector3fListIt it = lstPostions.begin(); it != lstPostions.end(); ++it) { cVector3f &vPos = *it; float fDist = cMath::Vector3DistSqr(pCamera->GetPosition(),vPos); if(fDist < fClosestDist) { fClosestDist = fDist; vClosestPostion = vPos; } } //Particle system if(pEnemy->GetHitPS()!="") { pWorld->CreateParticleSystem("Hit",pEnemy->GetHitPS(),1, cMath::MatrixTranslate(vClosestPostion)); } lstPostions.clear(); bHit = true; } } std::set m_setHitBodies; //////////////////////////////// //Iterate bodies float fClosestHitDist = 9999.0f; cVector3f vClosestHitPos; iPhysicsMaterial* pClosestHitMat = NULL; cPhysicsBodyIterator it = pPhysicsWorld->GetBodyIterator(); while(it.HasNext()) { iPhysicsBody *pBody = it.Next(); float fMass = pBody->GetMass(); if(pBody->IsActive()==false) continue; if(pBody->GetCollide()==false) continue; if(pBody->IsCharacter()) continue; if(cMath::CheckCollisionBV(tempBV, *pBody->GetBV())) { if(pPhysicsWorld->CheckShapeCollision(pBody->GetShape(),pBody->GetLocalMatrix(), mvAttacks[mlCurrentAttack].mpCollider, mtxDamage,collideData,1)==false) { continue; } cVector3f vHitPos = collideData.mvContactPoints[0].mvPoint; //Check if collision is in line of sight { mRayCallback.Reset(); cVector3f vRayStart = pCamera->GetPosition(); cVector3f vRayEnd = vHitPos; pPhysicsWorld->CastRay(&mRayCallback,vRayStart,vRayEnd,true,true,true,false); if(mRayCallback.mpClosestBody && mRayCallback.mpClosestBody != pBody) { continue; } } m_setHitBodies.insert(pBody); //Deal damage and force HitBody(pBody); //Check if this is the closest hit body float fDist = cMath::Vector3DistSqr(vHitPos, pCamera->GetPosition()); if(fDist < fClosestHitDist) { fClosestHitDist = fDist; vClosestHitPos = collideData.mvContactPoints[0].mvPoint; pClosestHitMat = pBody->GetMaterial(); } bHit = true; } } //////////////////////////////////////////// //Check with ray and see a closer material can be found. { float fAttackRange = mvAttacks[mlCurrentAttack].mfAttackRange; mRayCallback.Reset(); cVector3f vRayStart = pCamera->GetPosition(); cVector3f vRayEnd = pCamera->GetPosition() + pCamera->GetForward()*fAttackRange; pPhysicsWorld->CastRay(&mRayCallback,vRayStart,vRayEnd,true,true,true,false); if(mRayCallback.mpClosestBody) { //Use ray cast to check hit as well //Check first if body has not allready been hit. if(m_setHitBodies.find(mRayCallback.mpClosestBody)==m_setHitBodies.end()) { HitBody(mRayCallback.mpClosestBody); } float fDist = cMath::Vector3DistSqr(mRayCallback.mvPosition, pCamera->GetPosition()); if(fDist < fClosestHitDist) { fClosestHitDist = fDist; vClosestHitPos = mRayCallback.mvPosition; pClosestHitMat = mRayCallback.mpClosestBody->GetMaterial(); } } } //////////////////////////////////////////// //Check the closest material and play sounds and effects depending on it. if(pClosestHitMat) { bHit = true; cMatrixf mtxPosition = cMath::MatrixTranslate(vClosestHitPos); cSurfaceData *pData = pClosestHitMat->GetSurfaceData(); cSurfaceImpactData *pImpact = pData->GetHitDataFromSpeed(mvAttacks[mlCurrentAttack].mfAttackSpeed); if(pImpact) { cSoundEntity *pSound = pWorld->CreateSoundEntity("Hit",pImpact->GetSoundName(),true); if(pSound) pSound->SetPosition(vClosestHitPos); if(mvAttacks[mlCurrentAttack].mlHitPSPrio <= pImpact->GetPSPrio()) { if(pImpact->GetPSName()!="") pWorld->CreateParticleSystem("Hit",pImpact->GetPSName(),1,mtxPosition); } else { if(mvAttacks[mlCurrentAttack].msHitPS!="") pWorld->CreateParticleSystem("Hit",mvAttacks[mlCurrentAttack].msHitPS,1,mtxPosition); } } } //Log("----------------- END ATTACK WITH WEAPON ------------ \n"); ///////////////////////// //Play hit sound if(bHit) { PlaySound(mvAttacks[mlCurrentAttack].msHitSound); if(mpInit->mbHasHaptics) { if(mpHHitForce->IsActive()) mpHHitForce->SetActive(false); mpHHitForce->SetActive(true); mpHHitForce->SetTimeControl(false,0.3f, 0.2f, 0,0.1f); } } mpInit->mbWeaponAttacking = false; } //----------------------------------------------------------------------- void cHudModel_WeaponMelee::HitBody(iPhysicsBody *apBody) { iGameEntity *pEntity = (iGameEntity*)apBody->GetUserData(); if(pEntity && pEntity->GetType() == eGameEntityType_Enemy) return; cCamera3D *pCamera =mpInit->mpPlayer->GetCamera(); cVector3f vSpinMul = mvAttacks[mlCurrentAttack].mvSpinMul; vSpinMul = pCamera->GetRight() * vSpinMul.x + pCamera->GetUp() * vSpinMul.y + pCamera->GetForward() * vSpinMul.z; float fMass = apBody->GetMass(); float fMaxImpulse = mvAttacks[mlCurrentAttack].mfMaxImpulse; float fMinImpulse = mvAttacks[mlCurrentAttack].mfMinImpulse; float fMaxMass = mvAttacks[mlCurrentAttack].mfMaxMass; float fMinMass = mvAttacks[mlCurrentAttack].mfMinMass; //Calculate force float fForceSize =0; if(fMass > fMaxMass) fForceSize =0; else if(fMass <= fMinMass) fForceSize = fMaxImpulse; else{ float fT = (fMass - fMinMass) / (fMaxMass - fMinMass); fForceSize = fMinImpulse * fT + fMaxImpulse * (1-fT); } //Calculate damage float fDamage = cMath::RandRectf( mvAttacks[mlCurrentAttack].mfMinDamage, mvAttacks[mlCurrentAttack].mfMaxDamage); cVector3f vForceDir = pCamera->GetForward(); if(fMass>0 && fForceSize >0) { vForceDir.Normalise(); //pBody->AddForce(vForceDir * fForceSize); apBody->AddImpulse(vForceDir *fForceSize); cVector3f vTorque = vSpinMul * fMass * fForceSize; //vTorque = cMath::MatrixMul(pBody->GetInertiaMatrix(),vTorque); apBody->AddTorque(vTorque); } if(pEntity) { pEntity->SetLastImpulse(vForceDir *fForceSize); pEntity->Damage(fDamage,mvAttacks[mlCurrentAttack].mlAttackStrength); } } //-----------------------------------------------------------------------