bool CGameRulesMPDamageHandling::SvOnHitScaled( const HitInfo &hitInfo ) { bool bReturnDied = false; IEntity *pTarget = gEnv->pEntitySystem->GetEntity(hitInfo.targetId); CActor *pActor = static_cast<CActor*>(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(hitInfo.targetId)); if(!pActor) { IVehicle* pVictimVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(hitInfo.targetId); if(pVictimVehicle) { float vehicleDamageMultiplier = 1.f; //Additional damage scaling check for vehicles with associated body damage files CBodyDamageManager *pBodyDamageManager = g_pGame->GetBodyDamageManager(); TBodyDamageProfileId bodyDamageProfileId; TVehicleBodyDamageMap::const_iterator iter = m_vehicleBodyDamageMap.find( hitInfo.targetId ); if( iter != m_vehicleBodyDamageMap.end() ) { bodyDamageProfileId = iter->second; } else { bodyDamageProfileId = pBodyDamageManager->GetBodyDamage( *pTarget ); m_vehicleBodyDamageMap[hitInfo.targetId] = bodyDamageProfileId; } if(bodyDamageProfileId != INVALID_BODYDAMAGEPROFILEID) { vehicleDamageMultiplier *= pBodyDamageManager->GetDamageMultiplier(bodyDamageProfileId, *pTarget, hitInfo); } float fPreviousHealth = pVictimVehicle->GetStatus().health; // for vehicles, no need to call lua any more, we can just apply the hit directly to the vehicle { HitInfo hitInfoTemp = hitInfo; hitInfoTemp.damage *= vehicleDamageMultiplier; hitInfoTemp.explosion = false; pVictimVehicle->OnHit(hitInfoTemp); } float fNewHealth = pVictimVehicle->GetStatus().health; //Hit indicator for driver // Health check is required to take into account damage removal due to friendly fire. if(fNewHealth != fPreviousHealth) { if(IActor* pDriver = pVictimVehicle->GetDriver()) { if(IEntity* pShooterEntity = gEnv->pEntitySystem->GetEntity(hitInfo.shooterId)) { Vec3 shooterPos = pShooterEntity->GetWorldPos(); if(pDriver->IsClient()) { SHUDEvent hitEvent(eHUDEvent_OnShowHitIndicatorPlayerUpdated); hitEvent.ReserveData(3); hitEvent.AddData(shooterPos.x); hitEvent.AddData(shooterPos.y); hitEvent.AddData(shooterPos.z); CHUDEventDispatcher::CallEvent(hitEvent); } else { m_pGameRules->GetGameObject()->InvokeRMI( CGameRules::ClActivateHitIndicator(), CGameRules::ActivateHitIndicatorParams(shooterPos), eRMI_ToClientChannel, pDriver->GetChannelId() ); } } } } // no need for further processing, or calling OnEntityKilled, so just return early. if(pVictimVehicle->GetDamageRatio() >= 1.f) { if(fNewHealth != fPreviousHealth) { SVehicleDestroyedParams params(hitInfo.targetId, hitInfo.weaponId, hitInfo.type, hitInfo.projectileClassId); if(hitInfo.shooterId == g_pGame->GetClientActorId()) { CPersistantStats::GetInstance()->OnClientDestroyedVehicle(params); } else if(IGameObject * pShooterGameObject = g_pGame->GetIGameFramework()->GetGameObject(hitInfo.shooterId)) { m_pGameRules->GetGameObject()->InvokeRMIWithDependentObject(CGameRules::ClVehicleDestroyed(), params, eRMI_ToClientChannel, hitInfo.shooterId, pShooterGameObject->GetChannelId()); } } m_pGameRules->OnEntityKilled(hitInfo); return true; } else { return false; } } } if (pTarget) { IScriptTable *pTargetScript = pTarget->GetScriptTable(); if (pTargetScript) { m_pGameRules->CreateScriptHitInfo(m_scriptHitInfo, hitInfo); bool isDead = false; if (pActor) { if (pActor->IsDead()) { isDead = true; // Target is already dead HSCRIPTFUNCTION pfnOnDeadHit = 0; if (pActor->GetActorStats()->isRagDoll && pTargetScript->GetValue("OnDeadHit", pfnOnDeadHit)) { Script::CallMethod(pTargetScript, pfnOnDeadHit, m_scriptHitInfo); gEnv->pScriptSystem->ReleaseFunc(pfnOnDeadHit); } } } if (!isDead) { SmartScriptTable targetServerScript; if (pTargetScript->GetValue("Server", targetServerScript)) { HSCRIPTFUNCTION pfnOnHit = 0; if (targetServerScript->GetValue("OnHit", pfnOnHit)) { if (Script::CallReturn(gEnv->pScriptSystem, pfnOnHit, pTargetScript, m_scriptHitInfo, bReturnDied)) { if (bReturnDied) { if (pActor) { ProcessDeath(hitInfo, *pActor); } else { m_pGameRules->OnEntityKilled(hitInfo); } } } gEnv->pScriptSystem->ReleaseFunc(pfnOnHit); } } } } } return bReturnDied; }
//------------------------------------------------------------------------ void CGameRulesMPDamageHandling::SvOnCollision(const IEntity *pVictimEntity, const CGameRules::SCollisionHitInfo& collisionHitInfo) { FUNCTION_PROFILER(gEnv->pSystem, PROFILE_GAME); CRY_ASSERT(gEnv->bMultiplayer); #if !defined(_RELEASE) if (g_pGameCVars->g_DisableCollisionDamage) return; #endif IGameFramework* gameFramwork = g_pGame->GetIGameFramework(); EntityId victimID = pVictimEntity->GetId(); EntityId offenderID = collisionHitInfo.targetId; const IEntity* pOffenderEntity = gEnv->pEntitySystem->GetEntity(offenderID); float currentTime = gEnv->pTimer->GetCurrTime(); CActor* victimActor = static_cast<CActor*>(gameFramwork->GetIActorSystem()->GetActor(victimID)); IVehicle* offenderVehicle = gameFramwork->GetIVehicleSystem()->GetVehicle(offenderID); IVehicle* victimVehicle = gameFramwork->GetIVehicleSystem()->GetVehicle(victimID); IActor* offenderActor = gameFramwork->GetIActorSystem()->GetActor(offenderID); if(pOffenderEntity && !offenderVehicle && !offenderActor) { if( IEntity* pParent = pOffenderEntity->GetParent() ) { offenderVehicle = gameFramwork->GetIVehicleSystem()->GetVehicle(pParent->GetId()); } } // Vehicles being flipped do no damage, for now if (offenderVehicle != NULL && offenderVehicle->GetStatus().beingFlipped) return; // Players can't damage vehicles if (victimVehicle && offenderActor) return; // Filter frequent collisions if (pOffenderEntity) { FRAME_PROFILER("Filter out recent collisions", gEnv->pSystem, PROFILE_GAME); EntityCollisionRecords::const_iterator collisionRecordIter = m_entityCollisionRecords.find(victimID); if (collisionRecordIter != m_entityCollisionRecords.end()) { const EntityCollisionRecord& record = collisionRecordIter->second; if (record.entityID == offenderID && record.time + EntityCollisionIgnoreTimeBetweenCollisions > currentTime) { return; } } } float offenderMass = collisionHitInfo.target_mass; enum { CollisionWithEntity, CollisionWithStaticWorld } collisionType = (pOffenderEntity || offenderMass > 0.0f) ? CollisionWithEntity : CollisionWithStaticWorld; const Vec3& victimVelocity = collisionHitInfo.velocity; const Vec3& offenderVelocity = collisionHitInfo.target_velocity; float relativeSpeedSq = 0.0f; float minSpeedToCareAboutCollisionSq = 0.0f; float contactMass = 0.0f; bool offenderIsBig = offenderMass > 1000.f; switch (collisionType) { case CollisionWithEntity: { Vec3 relativeVelocity = victimVelocity - offenderVelocity; relativeSpeedSq = relativeVelocity.GetLengthSquared(); minSpeedToCareAboutCollisionSq = sqr(10.0f); if (victimActor && offenderIsBig) { minSpeedToCareAboutCollisionSq = sqr(1.0f); } if (victimActor && offenderVehicle) { //Players won't be hurt by vehicles with a negative kill player speed if(offenderVehicle->GetDamageParams().aiKillPlayerSpeed < 0.f) { return; } minSpeedToCareAboutCollisionSq = sqr(2.0f); } const float offenderSpeedSq = offenderVelocity.GetLengthSquared(); if (offenderSpeedSq == 0.0f) // -- if collision target it not moving { minSpeedToCareAboutCollisionSq *= sqr(2.0f); } ////////////////////////////////////////////////////////////////////////// contactMass = offenderMass; break; } case CollisionWithStaticWorld: { // Actors don't take damage from running into walls! if (victimActor) { return; } relativeSpeedSq = victimVelocity.GetLengthSquared(); minSpeedToCareAboutCollisionSq = sqr(7.5f); contactMass = collisionHitInfo.mass; break; } } const bool contactMassIsTooLowToCare = contactMass < 0.01f; if (contactMassIsTooLowToCare) return; ////////////////////////////////////////////////////////////////////////// // Calculate the collision damage if (relativeSpeedSq >= minSpeedToCareAboutCollisionSq) { bool useDefaultCalculation = true; float fEnergy = 0.f; float damage = 0.f; EntityId kickerId = 0; // Calculate damage if (offenderVehicle && victimActor) { useDefaultCalculation = false; damage = ProcessActorVehicleCollision(victimActor, victimID, offenderVehicle, offenderID, damage, collisionHitInfo, kickerId); } else if (offenderIsBig && victimActor) // i.e. a kickable car { // Try to find the kicker CTimeValue time = gEnv->pTimer->GetAsyncTime(); IActorSystem* pActorSystem = gEnv->pGame->GetIGameFramework()->GetIActorSystem(); IActorIteratorPtr pActorIterator = pActorSystem->CreateActorIterator(); IActor* pActor = pActorIterator->Next(); float lowestTime = 5.f; while (pActor != NULL) { CPlayer* pPlayer = static_cast<CPlayer*>(pActor); EntityId kicked = pPlayer->GetLargeObjectInteraction().GetLastObjectId(); if (kicked==offenderID) { float timeSinceKick = (time - pPlayer->GetLargeObjectInteraction().GetLastObjectTime()).GetSeconds(); if (timeSinceKick < lowestTime) { // We found the kicker and the kicked kickerId = pActor->GetEntityId(); lowestTime = timeSinceKick; } } pActor = pActorIterator->Next(); } damage = ProcessActorKickedVehicle(victimActor, victimID, kickerId, offenderID, damage, collisionHitInfo); useDefaultCalculation = false; } if (useDefaultCalculation) { fEnergy = GetCollisionEnergy(pVictimEntity, collisionHitInfo); if (victimVehicle || offenderIsBig) { damage = 0.0005f * fEnergy; } else { damage = 0.0025f * fEnergy; } // Apply damage multipliers damage *= GetCollisionDamageMult(pVictimEntity, pOffenderEntity, collisionHitInfo); if (victimActor) { const bool victimIsPlayer = victimActor->IsPlayer(); if (victimIsPlayer) { damage = AdjustPlayerCollisionDamage(pVictimEntity, pOffenderEntity, collisionHitInfo, damage); } } } if (damage >= DAMAGE_THRESHOLD_COLLISIONS) { HitInfo hit; hit.damage = damage; hit.pos = collisionHitInfo.pos; if (collisionHitInfo.target_velocity.GetLengthSquared() > 1e-6) hit.dir = collisionHitInfo.target_velocity.GetNormalized(); hit.radius = 0.0f; hit.partId = collisionHitInfo.partId; hit.targetId = victimID; hit.weaponId = offenderID; hit.shooterId = kickerId != 0 ? kickerId : offenderID; hit.material = 0; hit.type = CGameRules::EHitType::Collision; hit.explosion = false; CGameRules *pGameRules = g_pGame->GetGameRules(); if (pGameRules->GetTeamCount() > 1) { int shooterTeamId = pGameRules->GetTeam(hit.shooterId); int targetTeamId = pGameRules->GetTeam(hit.targetId); if (shooterTeamId && (shooterTeamId == targetTeamId)) { damage = GetFriendlyFireDamage(damage, hit, victimActor); } } if (damage >= DAMAGE_THRESHOLD_COLLISIONS) { IScriptTable* pVictimScript = pVictimEntity ? pVictimEntity->GetScriptTable() : NULL; IScriptTable* pOffenderScript = pOffenderEntity ? pOffenderEntity->GetScriptTable() : NULL; if (!pOffenderEntity && pVictimEntity) { pOffenderEntity = pVictimEntity; offenderID = victimID; } m_entityCollisionRecords[victimID] = EntityCollisionRecord(offenderID, currentTime); if(victimVehicle) { victimVehicle->OnHit(hit); } else if (pVictimScript) { FRAME_PROFILER("Call to OnHit", gEnv->pSystem, PROFILE_GAME); if (!IsDead(victimActor, pVictimScript)) { if (IActor* offenderDriver = offenderVehicle ? offenderVehicle->GetDriver() : NULL) hit.shooterId = offenderDriver->GetEntityId(); DelegateServerHit(pVictimScript, hit, victimActor); } } } } } }