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(); } } }