void CPlayerStateSwim_WaterTestProxy::UpdateOutOfWater(const CPlayer& player, const float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	//Out of water, only query water level to figure out if player/ai is in contact with a water volume
	const Matrix34& playerWorldTM = player.GetEntity()->GetWorldTM();
	const Vec3 playerWorldPos = playerWorldTM.GetTranslation();

	const Vec3 localReferencePos = GetLocalReferencePosition(player);

	//Note: Try to tune value and set to higher value possible which works well
	const bool lastCheckFarAwayEnough = ((m_lastWaterLevelCheckPosition - playerWorldPos).len2() >= sqr(0.6f));

	float worldWaterLevel = m_waterLevel;
	if (lastCheckFarAwayEnough)
	{
		const Vec3 worldReferencePos = playerWorldPos + (Quat(playerWorldTM) * localReferencePos);
		IPhysicalEntity* piPhysEntity = player.GetEntity()->GetPhysics();

		UpdateWaterLevel( worldReferencePos, playerWorldPos, piPhysEntity );
	}

	//Update submerged fraction
	UpdateSubmergedFraction(localReferencePos.z, playerWorldPos.z, worldWaterLevel);
}
void CPlayerStateSwim_WaterTestProxy::UpdateInWater(const CPlayer& player, const float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_GAME);

	const Matrix34& playerWorldTM = player.GetEntity()->GetWorldTM();
	const Vec3 playerWorldPos = playerWorldTM.GetTranslation();

	const Vec3 localReferencePos = GetLocalReferencePosition(player);
	const Vec3 worldReferencePos = playerWorldPos + (Quat(playerWorldTM) * localReferencePos);

	const bool shouldUpdate  = 
		((gEnv->pTimer->GetCurrTime() - m_timeWaterLevelLastUpdated) > 0.3f ) || 
		((m_lastWaterLevelCheckPosition - playerWorldPos).len2() >= sqr(0.35f)) ||
			(m_lastInternalState != m_internalState && m_internalState == eProxyInternalState_PartiallySubmerged); //Just entered partially emerged state

	if (shouldUpdate && !IsWaitingForBottomLevelResults())
	{
		RayTestBottomLevel(player, worldReferencePos, s_rayLength);

		IPhysicalEntity* piPhysEntity = player.GetEntity()->GetPhysics();

		UpdateWaterLevel( worldReferencePos, playerWorldPos, piPhysEntity );

		if (m_waterLevel > WATER_LEVEL_UNKNOWN)
		{
			m_playerWaterLevel = (worldReferencePos.z - m_waterLevel);
		}
		else
		{
			m_playerWaterLevel = -WATER_LEVEL_UNKNOWN;
			m_bottomLevel = BOTTOM_LEVEL_UNKNOWN;
			m_headUnderwater = false;
			m_headComingOutOfWater = false;
		}
	}

	m_relativeBottomLevel = (m_bottomLevel > BOTTOM_LEVEL_UNKNOWN) ? m_waterLevel - m_bottomLevel : 0.0f;

	const float localHeadZ = player.GetLocalEyePos().z + 0.2f;
	const float worldHeadZ = playerWorldPos.z + localHeadZ;

	const bool headWasUnderWater = m_headUnderwater;
	m_headUnderwater = ((worldHeadZ - m_waterLevel) < 0.0f);

	m_headComingOutOfWater = headWasUnderWater && (!m_headUnderwater);

	if (m_internalState == eProxyInternalState_Swimming)
	{
		m_shouldSwim = ShouldSwim(max(localReferencePos.z, 1.3f));
	}
	else
	{
		UpdateSubmergedFraction(localReferencePos.z, playerWorldPos.z, m_waterLevel);

		m_shouldSwim = ShouldSwim(max(localReferencePos.z, 1.3f)) && (m_swimmingTimer < - 0.5f);
	}
#ifdef STATE_DEBUG
	DebugDraw(player, worldReferencePos);
#endif
}
void AFastProjectile::Tick ()
{
	int i;
	DVector3 frac;
	int changexy;

	ClearInterpolation();
	double oldz = Z();

	if (!(flags5 & MF5_NOTIMEFREEZE))
	{
		//Added by MC: Freeze mode.
		if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN)
		{
			return;
		}
	}


	// [RH] Ripping is a little different than it was in Hexen
	FCheckPosition tm(!!(flags2 & MF2_RIP));

	int count = 8;
	if (radius > 0)
	{
		while ( fabs(Vel.X) > radius * count || fabs(Vel.Y) > radius * count)
		{
			// we need to take smaller steps.
			count += count;
		}
	}

	// Handle movement
	if (!Vel.isZero() || (Z() != floorz))
	{
		// force some lateral movement so that collision detection works as intended.
		if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage())
		{
			Vel.X = MinVel;
		}

		frac = Vel / count;
		changexy = frac.X != 0 || frac.Y != 0;
		int ripcount = count / 8;
		for (i = 0; i < count; i++)
		{
			if (changexy)
			{
				if (--ripcount <= 0)
				{
					tm.LastRipped.Clear();	// [RH] Do rip damage each step, like Hexen
				}
				
				if (!P_TryMove (this, Pos() + frac, true, NULL, tm))
				{ // Blocked move
					if (!(flags3 & MF3_SKYEXPLODE))
					{
						if (tm.ceilingline &&
							tm.ceilingline->backsector &&
							tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
							Z() >= tm.ceilingline->backsector->ceilingplane.ZatPoint(PosRelative(tm.ceilingline)))
						{
							// Hack to prevent missiles exploding against the sky.
							// Does not handle sky floors.
							Destroy ();
							return;
						}
						// [RH] Don't explode on horizon lines.
						if (BlockingLine != NULL && BlockingLine->special == Line_Horizon)
						{
							Destroy ();
							return;
						}
					}

					P_ExplodeMissile (this, BlockingLine, BlockingMobj);
					return;
				}
			}
			AddZ(frac.Z);
			UpdateWaterLevel ();
			oldz = Z();
			if (oldz <= floorz)
			{ // Hit the floor

				if (floorpic == skyflatnum && !(flags3 & MF3_SKYEXPLODE))
				{
					// [RH] Just remove the missile without exploding it
					//		if this is a sky floor.
					Destroy ();
					return;
				}

				SetZ(floorz);
				P_HitFloor (this);
				P_ExplodeMissile (this, NULL, NULL);
				return;
			}
			if (Top() > ceilingz)
			{ // Hit the ceiling

				if (ceilingpic == skyflatnum &&  !(flags3 & MF3_SKYEXPLODE))
				{
					Destroy ();
					return;
				}

				SetZ(ceilingz - Height);
				P_ExplodeMissile (this, NULL, NULL);
				return;
			}
			if (!frac.isZero() && ripcount <= 0) 
			{
				ripcount = count >> 3;
				Effect();
			}
		}
	}