void CWaterPuddle::ZapEnemiesOnPuddle(int ownTeam, EntityId shooterId, EntityId weaponId, float damage, int hitTypeId, IParticleEffect* hitEffect)
{
	IGameVolumes::VolumeInfo volumeInfo;
	if (!GetVolumeInfoForEntity(GetEntityId(), &volumeInfo))
		return;
	IEntity* pEntity = GetEntity();
	Matrix34 worldTM = pEntity->GetWorldTM();
	float waterLevel = worldTM.GetTranslation().z + volumeInfo.volumeHeight * 0.5f;

	CActorManager* pActorManager = CActorManager::GetActorManager();
	const int numberOfActors	= pActorManager->GetNumActors();

	for(int i = 0; i < numberOfActors; i++)
	{
		SActorData actorData;
		pActorManager->GetNthActorData(i, actorData);

		bool isActorAlive = (actorData.health > 0.0f);
		bool isActorEnemy = (actorData.teamId != ownTeam);
		bool isActorInsidevolume = IsActorInsideVolume(worldTM, volumeInfo, actorData.entityId);

		if (isActorAlive && isActorEnemy && isActorInsidevolume)
			ApplyHit(actorData, shooterId, weaponId, damage, hitTypeId, waterLevel, hitEffect);
	}
}
void CGameVolume_Water::DebugDrawVolume()
{
	IGameVolumes::VolumeInfo volumeInfo;
	if (GetVolumeInfoForEntity(GetEntityId(), volumeInfo) == false)
		return;

	if (volumeInfo.verticesCount < 3)
		return;

	const Matrix34 worldTM = GetEntity()->GetWorldTM();
	const Vec3 depthOffset = worldTM.GetColumn2().GetNormalized() * - m_volumeDepth;

	IRenderAuxGeom* pRenderAux = gEnv->pRenderer->GetIRenderAuxGeom();
	for (uint32 i = 0; i < volumeInfo.verticesCount - 1; ++i)
	{
		const Vec3 point1 = worldTM.TransformPoint(volumeInfo.pVertices[i]);
		const Vec3 point2 = worldTM.TransformPoint(volumeInfo.pVertices[i + 1]);

		pRenderAux->DrawLine( point1, Col_SlateBlue, point1 + depthOffset, Col_SlateBlue, 2.0f );
		pRenderAux->DrawLine( point1 + depthOffset, Col_SlateBlue, point2 + depthOffset, Col_SlateBlue, 2.0f );
	}

	const Vec3 firstPoint = worldTM.TransformPoint(volumeInfo.pVertices[0]);
	const Vec3 lastPoint = worldTM.TransformPoint(volumeInfo.pVertices[volumeInfo.verticesCount - 1]);

	pRenderAux->DrawLine( lastPoint, Col_SlateBlue, lastPoint + depthOffset, Col_SlateBlue, 2.0f );
	pRenderAux->DrawLine( lastPoint + depthOffset, Col_SlateBlue, firstPoint + depthOffset, Col_SlateBlue, 2.0f );
}
void CGameVolume_Water::SetupVolume()
{
	IGameVolumes::VolumeInfo volumeInfo;
	if (GetVolumeInfoForEntity(GetEntityId(), volumeInfo) == false)
		return;

	if (volumeInfo.verticesCount < 3)
		return;

	WaterProperties waterProperties(GetEntity()); 

	if(waterProperties.isRiver)
	{
		if(volumeInfo.verticesCount < 4 || volumeInfo.verticesCount % 2 != 0)
			return;

		int numSegments = (volumeInfo.verticesCount / 2) - 1;

		m_segments.resize(numSegments);

		for(int i = 0; i < numSegments; ++i)
		{
			SetupVolumeSegment(waterProperties, i, &volumeInfo.pVertices[0], volumeInfo.verticesCount);
		}
	}
	else
	{
		SetupVolumeSegment(waterProperties, 0, &volumeInfo.pVertices[0], volumeInfo.verticesCount);
	}

#if CRY_PLATFORM_WINDOWS && !defined(_RELEASE)
	{
		if (gEnv->IsEditor())
		{
			IEntity* pEnt = GetEntity();
			IMaterial* pMat = pEnt ? pEnt->GetMaterial() : 0;
			if (pMat)
			{
				const SShaderItem& si = pMat->GetShaderItem();
				if (si.m_pShader && si.m_pShader->GetShaderType() != eST_Water)
				{
					CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_ERROR, "Incorrect shader set for water / water fog volume \"%s\"!", pEnt->GetName());
				}
			}
		}
	}
#endif

	m_baseMatrix = GetEntity()->GetWorldTM();
	m_initialMatrix = m_baseMatrix;
	m_volumeDepth = waterProperties.depth;
	m_streamSpeed = waterProperties.streamSpeed;
	m_awakeAreaWhenMoving = waterProperties.awakeAreaWhenMoving;
	m_isRiver = waterProperties.isRiver;
}
void CGameVolume_Water::HandleEvent( const SGameObjectEvent& gameObjectEvent )
{
	if ((gameObjectEvent.event == eGFE_ScriptEvent) && (gameObjectEvent.param != NULL))
	{
		const char* eventName = static_cast<const char*>(gameObjectEvent.param);
		if (strcmp(eventName, "PhysicsEnable") == 0)
		{
			IGameVolumes::VolumeInfo volumeInfo;
			if (GetVolumeInfoForEntity(GetEntityId(), volumeInfo))
			{
				for(uint32 i = 0; i < m_segments.size(); ++i)
				{
					SWaterSegment& segment = m_segments[i];

					if ((segment.m_pWaterArea == NULL) && (segment.m_pWaterRenderNode != NULL))
					{
						if(!m_isRiver)
						{
							CreatePhysicsArea(i, m_baseMatrix, &volumeInfo.pVertices[0], volumeInfo.verticesCount, false, m_streamSpeed);
						}
						else
						{
							Vec3 vertices[4];
							FillOutRiverSegment(i, &volumeInfo.pVertices[0], volumeInfo.verticesCount, &vertices[0]);
							CreatePhysicsArea(i, m_baseMatrix, vertices, 4, true, m_streamSpeed);
						}

						segment.m_pWaterRenderNode->SetMatrix( m_baseMatrix );
					}
				}

				AwakeAreaIfRequired( true );

				m_lastAwakeCheckPosition.zero(); 
			}
		}
		else if (strcmp(eventName, "PhysicsDisable") == 0)
		{
			DestroyPhysicsAreas();
		}
	}
}
CWaterPuddle* CWaterPuddleManager::FindWaterPuddle(Vec3 point)
{
	size_t numPuddles = m_waterPuddles.size();
	for (size_t i = 0; i < numPuddles; ++i)
	{
		EntityId puddle = m_waterPuddles[i].m_entityId;

		IEntity* pEntity = gEnv->pEntitySystem->GetEntity(puddle);
		if (!pEntity)
			continue;

		IGameVolumes::VolumeInfo volumeInfo;
		if (!GetVolumeInfoForEntity(puddle, &volumeInfo))
			continue;

		if (IsPointInsideVolume(pEntity->GetWorldTM(), volumeInfo, point, 0.0f))
			return m_waterPuddles[i].m_pPuddle;
	}

	return 0;
}
void CGameVolume_Water::ProcessEvent( SEntityEvent& event)
{
	switch(event.event)
	{
	case ENTITY_EVENT_EDITOR_PROPERTY_CHANGED:
		{
			SetupVolume();
		}
		break;

	case ENTITY_EVENT_RESET:
		{
			// Only when exiting game mode
			if ((gEnv->IsEditor()) && (event.nParam[0] == 0))
			{
				if ( Matrix34::IsEquivalent( m_baseMatrix,m_initialMatrix ) == false)
				{
					GetEntity()->SetWorldTM( m_initialMatrix );
				}
			}
		}
		break;

	case ENTITY_EVENT_XFORM:
		{
			// Rotation requires to setup again
			// Internally the shape vertices are projected to a plane, and rotating changes that plane
			// Only allow rotations in the editor, at run time can be expensive
			if ((gEnv->IsEditing()) && (event.nParam[0] & ENTITY_XFORM_ROT))
			{
				SetupVolume();
			}
			else if (event.nParam[0] & ENTITY_XFORM_POS)
			{
				const Matrix34 entityWorldTM = GetEntity()->GetWorldTM();

				m_baseMatrix.SetTranslation(entityWorldTM.GetTranslation());


				for(uint32 i = 0; i < m_segments.size(); ++i)
				{
					SWaterSegment& segment = m_segments[i];

					UpdateRenderNode( segment.m_pWaterRenderNode, m_baseMatrix );

					if( segment.m_pWaterArea )
					{
						const Vec3 newAreaPosition = m_baseMatrix.GetTranslation() + ((Quat(m_baseMatrix) * segment.m_physicsLocalAreaCenter) - segment.m_physicsLocalAreaCenter);

						pe_params_pos position;
						position.pos = newAreaPosition;
						segment.m_pWaterArea->SetParams( &position );

						pe_params_buoyancy pb;
						pb.waterPlane.origin = newAreaPosition;
						segment.m_pWaterArea->SetParams( &pb );

						//gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere( areaPos.pos, 0.5f, ColorB(255, 0, 0));
					}
				}

				AwakeAreaIfRequired( false );
			}
		}
		break;

	case ENTITY_EVENT_HIDE:
		{
			AwakeAreaIfRequired( true );

			WaterSegments::iterator iter = m_segments.begin();
			WaterSegments::iterator end = m_segments.end();
			while(iter != end)
			{
				if (iter->m_pWaterRenderNode)
				{
					iter->m_pWaterRenderNode->Hide(true);
				}

				++iter;
			}
			
			DestroyPhysicsAreas();
		}
		break;
	case ENTITY_EVENT_UNHIDE:
		{
			IGameVolumes::VolumeInfo volumeInfo;
			if (GetVolumeInfoForEntity(GetEntityId(), volumeInfo))
			{
				for(uint32 i = 0; i < m_segments.size(); ++i)
				{
					SWaterSegment& segment = m_segments[i];
					if (segment.m_pWaterRenderNode)
					{
						segment.m_pWaterRenderNode->Hide(false);

						if(!m_isRiver)
						{
							CreatePhysicsArea(i, m_baseMatrix, &volumeInfo.pVertices[0], volumeInfo.verticesCount, false, m_streamSpeed);
						}
						else
						{
							Vec3 vertices[4];
							FillOutRiverSegment(i, &volumeInfo.pVertices[0], volumeInfo.verticesCount, &vertices[0]);
							CreatePhysicsArea(i, m_baseMatrix, vertices, 4, true, m_streamSpeed);
						}

						segment.m_pWaterRenderNode->SetMatrix( m_baseMatrix );
					}
				}

				AwakeAreaIfRequired( true );

				m_lastAwakeCheckPosition.zero(); //After unhide, next movement will awake the area
			}
			
		}
		break;
	}
}