Beispiel #1
0
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);
					}
				}
			}
		}
	}
}