bool CHUDCrosshair::IsFriendlyEntity(IEntity *pEntity) { IActor *pClientActor = g_pGame->GetIGameFramework()->GetClientActor(); CGameRules *pGameRules = g_pGame->GetGameRules(); if(!pEntity || !pClientActor || !pGameRules) return false; // Less than 2 teams means we are in a FFA based game. if(pGameRules->GetTeamCount() < 2) return false; bool bFriendly = false; int iClientTeam = pGameRules->GetTeam(pClientActor->GetEntityId()); // First, check if entity is a player IActor *pActor = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntity->GetId()); if(pActor && pActor->IsPlayer()) { if(iClientTeam && (pGameRules->GetTeam(pActor->GetEntityId()) == iClientTeam)) { bFriendly = true; } } else { // Then, check if entity is a vehicle IVehicle *pVehicle = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(pEntity->GetId()); if(pVehicle && pGameRules->GetTeam(pVehicle->GetEntityId()) == iClientTeam && pVehicle->GetStatus().passengerCount) { IActor *pDriver = pVehicle->GetDriver(); /*if(pDriver && pGameRules->GetTeam(pDriver->GetEntityId()) == iClientTeam) bFriendly = true; else bFriendly = false;*/ bFriendly = true; //fix for bad raycast if(pDriver && pDriver == pClientActor) bFriendly = false; } } return bFriendly; }
//------------------------------------------------------------------------ void CGameRulesMPDamageHandling::SvOnExplosion(const ExplosionInfo &explosionInfo, const CGameRules::TExplosionAffectedEntities& affectedEntities) { // SinglePlayer.lua 1721 IActorSystem* pActorSystem = g_pGame->GetIGameFramework()->GetIActorSystem(); float totalDamage = 0.f; // calculate damage for each entity CGameRules::TExplosionAffectedEntities::const_iterator it = affectedEntities.begin(); for (; it != affectedEntities.end(); ++it) { bool success = false; IEntity* entity = it->first; float obstruction = 1.0f - it->second; bool incone=true; if (explosionInfo.angle > 0 && explosionInfo.angle < 2*3.1415f) { Vec3 explosionEntityPos = entity->GetWorldPos(); Vec3 entitypos = explosionEntityPos; float ha = explosionInfo.angle * 0.5f; Vec3 edir = entitypos - explosionInfo.pos; edir.normalize(); float dot = 1; if (edir != Vec3(ZERO)) { dot = edir.dot(explosionInfo.dir); } float angle = abs(acos_tpl(dot)); if (angle > ha) { incone = false; } } if (incone && gEnv->bServer) { float damage = explosionInfo.damage; damage = floor_tpl(0.5f + CalcExplosionDamage(entity, explosionInfo, obstruction)); bool dead = false; HitInfo explHit; Vec3 dir = entity->GetWorldPos() - explosionInfo.pos; explHit.pos = explosionInfo.pos; explHit.radius = explosionInfo.radius; explHit.partId = -1; dir.Normalize(); explHit.targetId = entity->GetId(); explHit.weaponId = explosionInfo.weaponId; explHit.shooterId = explosionInfo.shooterId; explHit.projectileId = explosionInfo.projectileId; explHit.projectileClassId = explosionInfo.projectileClassId; uint16 weaponClass = ~uint16(0); const IEntity* pWeaponEntity = gEnv->pEntitySystem->GetEntity(explosionInfo.weaponId); if (pWeaponEntity) { g_pGame->GetIGameFramework()->GetNetworkSafeClassId(weaponClass, pWeaponEntity->GetClass()->GetName()); } explHit.weaponClassId = weaponClass; explHit.material = 0; explHit.damage = damage; explHit.type = explosionInfo.type; explHit.hitViaProxy = explosionInfo.explosionViaProxy; explHit.dir = dir; explHit.normal = -dir; //set normal to reverse of direction to avoid backface cull of damage if (explHit.damage > 0.0f || explosionInfo.damage == 0.f) { CActor* pActor = static_cast<CActor*>(pActorSystem->GetActor(entity->GetId())); if (pActor) { const float damageMultiplier = pActor->GetBodyExplosionDamageMultiplier(explHit); explHit.damage *= damageMultiplier; // Protect players who are currently shielded if(pActor->IsPlayer() && static_cast<CPlayer*>(pActor)->ShouldFilterOutExplosion(explHit)) { explHit.damage = 0.0f; } } else { CInteractiveObjectEx* pInteractiveObject = static_cast<CInteractiveObjectEx*>(g_pGame->GetIGameFramework()->QueryGameObjectExtension(entity->GetId(), "InteractiveObjectEx")); if(pInteractiveObject) { pInteractiveObject->OnExploded(explHit.pos); } } if(!explosionInfo.explosionViaProxy) { if(pActor) { if(!pActor->IsFriendlyEntity(explHit.shooterId)) { totalDamage += damage; } } else { IVehicle* pVehicle = g_pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(entity->GetId()); if(pVehicle) { IActor* pDriver = pVehicle->GetDriver(); int seatNum = 0; int numSeats = pVehicle->GetSeatCount(); while(!pDriver && seatNum < numSeats) { IVehicleSeat* pSeat = pVehicle->GetSeatById(seatNum++); EntityId passengerId = pSeat ? pSeat->GetPassenger(true) : 0; pDriver = pActorSystem->GetActor(passengerId); } if(pDriver) { CActor* pDriverActor = static_cast<CActor*>(pDriver); if(!pDriverActor->IsFriendlyEntity(explHit.shooterId)) { totalDamage += damage; } } } } } m_pGameRules->ServerHit(explHit); } } } if (totalDamage > 0.f) { IGameRulesPlayerStatsModule* pPlayStatsMod = m_pGameRules->GetPlayerStatsModule(); if(pPlayStatsMod) { pPlayStatsMod->ProcessSuccessfulExplosion(explosionInfo.shooterId, totalDamage, explosionInfo.projectileClassId); } } }
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); } } } } } }