/* 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/HudModel_Weapon.h" #include "hpl1/penumbra-overture/AttackHandler.h" #include "hpl1/penumbra-overture/EffectHandler.h" #include "hpl1/penumbra-overture/GameEnemy.h" #include "hpl1/penumbra-overture/GameEntity.h" #include "hpl1/penumbra-overture/Init.h" #include "hpl1/penumbra-overture/MapHandler.h" #include "hpl1/penumbra-overture/Player.h" #include "hpl1/penumbra-overture/PlayerHelper.h" #include "hpl1/penumbra-overture/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(); } //----------------------------------------------------------------------- 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 lstPositions; //////////////////////////////// // 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, &lstPositions, 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 *pBody2 = pEnemy->GetBody(i); pBody2->AddImpulse(vForceDir * fForceSize * 0.5f); cVector3f vTorque = vSpinMul * fMass * fForceSize * 0.5f; pBody2->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 = lstPositions.begin(); it != lstPositions.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)); } lstPositions.clear(); bHit = true; } } Hpl1::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 if 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 already 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); } 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); } } //-----------------------------------------------------------------------