//------------------------------------------------------------------------
void CGameRulesHoldObjectiveBase::InsideStateChanged(SHoldEntityDetails *pDetails)
{
	pDetails->m_insideCount[0] = pDetails->m_insideEntities[0].size();
	pDetails->m_insideCount[1] = pDetails->m_insideEntities[1].size();

	int team1Count = pDetails->m_insideCount[0];
	int team2Count = pDetails->m_insideCount[1];

	int oldControllingTeamId = pDetails->m_controllingTeamId;

	DetermineControllingTeamId(pDetails, team1Count, team2Count);

	if( oldControllingTeamId != pDetails->m_controllingTeamId )
	{
		OnControllingTeamChanged(pDetails, oldControllingTeamId);
	}

	CHANGED_NETWORK_STATE(g_pGame->GetGameRules(), HOLD_OBJECTIVE_STATE_ASPECT);
	OnInsideStateChanged(pDetails);
}
//------------------------------------------------------------------------
void CGameRulesKingOfTheHillObjective::OnNewHoldEntity(SHoldEntityDetails *pDetails, int index)
{
	CRY_ASSERT(index < HOLD_OBJECTIVE_MAX_ENTITIES);

	SKotHEntity *pKotHEntity = &m_additionalInfo[index];
	pKotHEntity->Reset();

	pDetails->m_pAdditionalData = pKotHEntity;

	if (gEnv->bServer)
	{
		g_pGame->GetGameRules()->SetTeam(0, pDetails->m_id);
	}

	if (gEnv->IsClient())
	{
		pKotHEntity->m_needsIconUpdate = true;		// Can't set icon straight away since the lua 'checkFunc' will give an incorrect result
		InitEntityAudio(pDetails);
	}

	OnInsideStateChanged(pDetails);

	// Cache radius pulse effect scale
	IEntity *pEntity = gEnv->pEntitySystem->GetEntity(pDetails->m_id);
	if (pEntity)
	{
		SmartScriptTable pScriptTable = pEntity->GetScriptTable();
		if (pScriptTable)
		{
			SmartScriptTable pPropertiesTable;
			if (pScriptTable->GetValue("Properties", pPropertiesTable))
			{
				float fRadiusEffectScale = 1.f;
				if (pPropertiesTable->GetValue("radiusEffectScale", fRadiusEffectScale))
				{
					pKotHEntity->m_radiusEffectScale = fRadiusEffectScale;
				}
			}
		}
	}
}
//------------------------------------------------------------------------
void CGameRulesHoldObjectiveBase::CheckLocalPlayerInside(SHoldEntityDetails *pDetails, const IEntity *pHoldEntity, const IEntity *pLocalPlayer)
{
	const EntityId localPlayerId = pLocalPlayer->GetId();
	const int teamId = g_pGame->GetGameRules()->GetTeam(localPlayerId);
	const int teamIdx = teamId - 1;

	if ((teamId == 1) || (teamId == 2))
	{
		const Vec3 holdEntPos = pHoldEntity->GetWorldPos();
		const Vec3 playerPos = pLocalPlayer->GetWorldPos();

		const Vec3 diff = (playerPos - holdEntPos);
		// Check z first
		if ((diff.z >= pDetails->m_controlOffsetZ) && (diff.z <= (pDetails->m_controlOffsetZ + pDetails->m_controlHeight)))
		{
			// Now check x & y (box test)
			if ((fabs(diff.x) <= pDetails->m_controlRadius) && (fabs(diff.y) <= pDetails->m_controlRadius))
			{
				stl::push_back_unique(pDetails->m_insideBoxEntities[teamIdx], localPlayerId);

				// Now check cylinder
				const float flatDistSqr = (diff.x * diff.x) + (diff.y * diff.y);
				if (flatDistSqr < pDetails->m_controlRadiusSqr)
				{
					// Player is inside
					pDetails->m_localPlayerIsWithinRange = true;
					OnLocalPlayerInsideStateChanged(pDetails);
					stl::push_back_unique(pDetails->m_insideEntities[teamIdx], localPlayerId);

					// Update actual objective implementation
					OnInsideStateChanged(pDetails);
				}
			}
		}
	}
}
//------------------------------------------------------------------------
void CGameRulesHoldObjectiveBase::Update( float frameTime )
{
	if (m_bHasNetSerialized && !gEnv->bServer)
	{
		for (int i = 0; i < HOLD_OBJECTIVE_MAX_ENTITIES; ++ i)
		{
			SHoldEntityDetails *pDetails = &m_entities[i];

			int oldControllingTeamId = pDetails->m_controllingTeamId;
			int oldTeam1Count = pDetails->m_insideCount[0];
			int oldTeam2Count = pDetails->m_insideCount[1];

			int team1Count = pDetails->m_serializedInsideCount[0];
			int team2Count = pDetails->m_serializedInsideCount[1];

			DetermineControllingTeamId(pDetails, team1Count, team2Count);

			if ((oldTeam1Count != team1Count) || (oldTeam2Count != team2Count))
			{
				pDetails->m_insideCount[0] = team1Count;
				pDetails->m_insideCount[1] = team2Count;

				if (pDetails->m_id)
				{
					OnInsideStateChanged(pDetails);

					if (oldControllingTeamId != pDetails->m_controllingTeamId)
					{
						OnControllingTeamChanged(pDetails, oldControllingTeamId);
					}
				}
			}
		}

		m_bHasNetSerialized = false;
	}

	EntityId localPlayerId = g_pGame->GetIGameFramework()->GetClientActorId();

	for (int i = 0; i < HOLD_OBJECTIVE_MAX_ENTITIES; ++ i)
	{
		SHoldEntityDetails *pDetails = &m_entities[i];

#ifndef _RELEASE
		DebugDrawCylinder(pDetails);
#endif

		if (pDetails->m_id && pDetails->m_totalInsideBoxCount)
		{
			CheckCylinder(pDetails, localPlayerId);
		}
		else if (pDetails->m_pendingId)
		{
			DoAddEntityId(1, pDetails->m_pendingId, i, pDetails->m_isNewEntity);
			pDetails->m_pendingId = 0;
			pDetails->m_isNewEntity = false;
		}
	}

	UpdateEffect(frameTime);

	if (m_pStartingAnimSequence && gEnv->pMovieSystem)
	{
		m_deferredTrackViewTime -= frameTime;
		if (m_deferredTrackViewTime < 0.f)
		{
			CGameRules *pGameRules = g_pGame->GetGameRules();
			const float currentTime = (pGameRules->GetServerTime() * 0.001f);
			const float timeStarted = m_pendingTimeAdded.GetSeconds();

			const float timeSinceStarted = currentTime - timeStarted;

			const float playingTime = gEnv->pMovieSystem->GetPlayingTime(m_pStartingAnimSequence);

			CryLog("CGameRulesHoldObjectiveBase::Update() anim sequence %s at %f seconds (should be at least %f)", m_pStartingAnimSequence->GetName(), playingTime, timeSinceStarted);
			if (playingTime < timeSinceStarted)
			{
				gEnv->pMovieSystem->SetPlayingTime(m_pStartingAnimSequence, timeSinceStarted);
			}
			m_pStartingAnimSequence = NULL;
		}
	}
}
//------------------------------------------------------------------------
void CGameRulesHoldObjectiveBase::CheckCylinder( SHoldEntityDetails *pDetails, EntityId localPlayerId )
{
	IEntity *pHoldEntity = gEnv->pEntitySystem->GetEntity(pDetails->m_id);
	if (pHoldEntity)
	{
		pDetails->m_totalInsideBoxCount = 0;

		const Vec3 &holdPos = pHoldEntity->GetPos();
		bool hasChanged = false;
		bool localPlayerWithinRange = false;

		for (int teamIdx = 0; teamIdx < NUM_TEAMS; ++ teamIdx)
		{
			const int previousNumEntities = pDetails->m_insideEntities[teamIdx].size();
			pDetails->m_insideEntities[teamIdx].clear();

			const TEntityIdVec &boxEntities = pDetails->m_insideBoxEntities[teamIdx];
			const int numBoxEntities = boxEntities.size();
			pDetails->m_totalInsideBoxCount += numBoxEntities;
			for (int entIdx = 0; entIdx < numBoxEntities; ++ entIdx)
			{
				EntityId playerId = boxEntities[entIdx];
				IEntity *pPlayer = gEnv->pEntitySystem->GetEntity(playerId);
				if (pPlayer)
				{
					//Make sure we're not in a vehicle for C3
					if(IActor * pActor = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(playerId))
					{
						if(pActor->GetLinkedVehicle() != NULL)
							continue;
					}

					const Vec3 &playerPos = pPlayer->GetPos();
					
					const float xDist = playerPos.x - holdPos.x;
					const float yDist = playerPos.y - holdPos.y;

					const float flatDistSqr = (xDist * xDist) + (yDist * yDist);
					if (flatDistSqr < pDetails->m_controlRadiusSqr)
					{
						pDetails->m_insideEntities[teamIdx].push_back(playerId);
						if (playerId == localPlayerId)
						{
							localPlayerWithinRange = true;
						}
					}
				}
			}
			if (pDetails->m_insideEntities[teamIdx].size() != previousNumEntities)
			{
				hasChanged = true;
			}
		}
		if (hasChanged)
		{
			if (localPlayerWithinRange != pDetails->m_localPlayerIsWithinRange)
			{
				if (localPlayerWithinRange)
				{
					pDetails->m_localPlayerIsWithinRange = true;
					CCCPOINT(HoldObjective_LocalPlayerEnterArea);
				}
				else
				{
					pDetails->m_localPlayerIsWithinRange = false;
					CCCPOINT(HoldObjective_LocalPlayerLeaveArea);
				}
				OnLocalPlayerInsideStateChanged(pDetails);
			}
			if (gEnv->bServer)
			{
				InsideStateChanged(pDetails);
			}
			else
			{
				OnInsideStateChanged(pDetails);
			}
		}
	}
}