示例#1
0
文件: Monster.cpp 项目: 4264/cuberite
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
{
	if (m_IsFollowingPath)
	{
		return;  // Still getting there
	}

	m_IdleInterval += a_Dt;

	if (m_IdleInterval > std::chrono::seconds(1))
	{
		// At this interval the results are predictable
		int rem = m_World->GetTickRandomNumber(6) + 1;
		m_IdleInterval -= std::chrono::seconds(1);  // So nothing gets dropped when the server hangs for a few seconds

		Vector3d Dist;
		Dist.x = (double)m_World->GetTickRandomNumber(10) - 5;
		Dist.z = (double)m_World->GetTickRandomNumber(10) - 5;

		if ((Dist.SqrLength() > 2)  && (rem >= 3))
		{
			Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z);
			Destination.y = FindFirstNonAirBlockPosition(Destination.x, Destination.z);
			MoveToPosition(Destination);
		}
	}
}
示例#2
0
void cMonster::InStateIdle(float a_Dt)
{
	if (m_bMovingToDestination)
	{
		return; // Still getting there
	}

	m_IdleInterval += a_Dt;

	if (m_IdleInterval > 1)
	{
		// At this interval the results are predictable
		int rem = m_World->GetTickRandomNumber(6) + 1;
		m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds

		Vector3d Dist;
		Dist.x = (double)m_World->GetTickRandomNumber(10) - 5;
		Dist.z = (double)m_World->GetTickRandomNumber(10) - 5;

		if ((Dist.SqrLength() > 2)  && (rem >= 3))
		{
			Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z);

			int NextHeight = FindFirstNonAirBlockPosition(Destination.x, Destination.z);

			if (IsNextYPosReachable(NextHeight))
			{
				Destination.y = NextHeight;
				MoveToPosition(Destination);
			}
		}
	}
}
示例#3
0
void cMonster::SetPitchAndYawFromDestination()
{
	Vector3d FinalDestination = m_FinalDestination;
	if (m_Target != NULL)
	{
		if (m_Target->IsPlayer())
		{
			FinalDestination.y = ((cPlayer *)m_Target)->GetStance();
		}
		else
		{
			FinalDestination.y = GetHeight();
		}
	}

	Vector3d Distance = FinalDestination - GetPosition();
	if (Distance.SqrLength() > 0.1f)
	{
		{
			double Rotation, Pitch;
			Distance.Normalize();
			VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch);
			SetHeadYaw(Rotation);
			SetPitch(-Pitch);
		}

		{
			Vector3d BodyDistance = m_Destination - GetPosition();
			double Rotation, Pitch;
			Distance.Normalize();
			VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch);
			SetYaw(Rotation);
		}
	}
}
示例#4
0
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	if (m_PathfinderActivated)
	{
		return;  // Still getting there
	}

	m_IdleInterval += a_Dt;

	if (m_IdleInterval > std::chrono::seconds(1))
	{
		// At this interval the results are predictable
		int rem = m_World->GetTickRandomNumber(6) + 1;
		m_IdleInterval -= std::chrono::seconds(1);  // So nothing gets dropped when the server hangs for a few seconds

		Vector3d Dist;
		Dist.x = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0;
		Dist.z = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0;

		if ((Dist.SqrLength() > 2)  && (rem >= 3))
		{

			Vector3d Destination(GetPosX() + Dist.x, GetPosition().y, GetPosZ() + Dist.z);

			cChunk * Chunk = a_Chunk.GetNeighborChunk(static_cast<int>(Destination.x), static_cast<int>(Destination.z));
			if ((Chunk == nullptr) || !Chunk->IsValid())
			{
				return;
			}

			BLOCKTYPE BlockType;
			NIBBLETYPE BlockMeta;
			int RelX = static_cast<int>(Destination.x) - Chunk->GetPosX() * cChunkDef::Width;
			int RelZ = static_cast<int>(Destination.z) - Chunk->GetPosZ() * cChunkDef::Width;
			int YBelowUs = static_cast<int>(Destination.y) - 1;
			if (YBelowUs >= 0)
			{
				Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
				if (BlockType != E_BLOCK_STATIONARY_WATER)  // Idle mobs shouldn't enter water on purpose
				{
					MoveToPosition(Destination);
				}
			}
		}
	}
}
示例#5
0
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	int BlockX = POSX_TOINT;
	int BlockY = POSY_TOINT;
	int BlockZ = POSZ_TOINT;

	// Position changed -> super::HandlePhysics() called
	GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ)

	// TODO Add collision detection with entities.
	a_Dt /= 1000;  // Convert from msec to sec
	Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ());
	Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ());
	
	if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
	{
		// Outside of the world		
		AddSpeedY(m_Gravity * a_Dt);
		AddPosition(GetSpeed() * a_Dt);
		return;
	}
	
	int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
	int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
	BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
	BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
	if (!cBlockInfo::IsSolid(BlockIn))  // Making sure we are not inside a solid block
	{
		if (m_bOnGround)  // check if it's still on the ground
		{
			if (!cBlockInfo::IsSolid(BlockBelow))  // Check if block below is air or water.
			{
				m_bOnGround = false;
			}
		}
	}
	else
	{
		// Push out entity.
		BLOCKTYPE GotBlock;

		static const struct
		{
			int x, y, z;
		} gCrossCoords[] =
		{
			{ 1, 0,  0},
			{-1, 0,  0},
			{ 0, 0,  1},
			{ 0, 0, -1},
		} ;

		bool IsNoAirSurrounding = true;
		for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
		{
			if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
			{
				// The pickup is too close to an unloaded chunk, bail out of any physics handling
				return;
			}
			if (!cBlockInfo::IsSolid(GotBlock))
			{
				NextPos.x += gCrossCoords[i].x;
				NextPos.z += gCrossCoords[i].z;
				IsNoAirSurrounding = false;
				break;
			}
		}  // for i - gCrossCoords[]
			
		if (IsNoAirSurrounding)
		{
			NextPos.y += 0.5;
		}

		m_bOnGround = true;

		/*
		// DEBUG:
		LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
			m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
		);
		*/
	}

	if (!m_bOnGround)
	{
		float fallspeed;
		if (IsBlockWater(BlockIn))
		{
			fallspeed = m_Gravity * a_Dt / 3;  // Fall 3x slower in water.
		}
		else if (BlockIn == E_BLOCK_COBWEB)
		{
			NextSpeed.y *= 0.05;  // Reduce overall falling speed
			fallspeed = 0;  // No falling.
		}
		else
		{
			// Normal gravity
			fallspeed = m_Gravity * a_Dt;
		}
		NextSpeed.y += fallspeed;
	}
	else
	{
		// Friction
		if (NextSpeed.SqrLength() > 0.0004f)
		{
			NextSpeed.x *= 0.7f / (1 + a_Dt);
			if (fabs(NextSpeed.x) < 0.05)
			{
				NextSpeed.x = 0;
			}
			NextSpeed.z *= 0.7f / (1 + a_Dt);
			if (fabs(NextSpeed.z) < 0.05)
			{
				NextSpeed.z = 0;
			}
		}
	}

	// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
	// might have different speed modifiers according to terrain.
	if (BlockIn == E_BLOCK_COBWEB)
	{
		NextSpeed.x *= 0.25;
		NextSpeed.z *= 0.25;
	}
					
	//Get water direction
	Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);

	m_WaterSpeed *= 0.9f;		//Reduce speed each tick

	switch(WaterDir)
	{
		case X_PLUS:
			m_WaterSpeed.x = 0.2f;
			m_bOnGround = false;
			break;
		case X_MINUS:
			m_WaterSpeed.x = -0.2f;
			m_bOnGround = false;
			break;
		case Z_PLUS:
			m_WaterSpeed.z = 0.2f;
			m_bOnGround = false;
			break;
		case Z_MINUS:
			m_WaterSpeed.z = -0.2f;
			m_bOnGround = false;
			break;
			
	default:
		break;
	}

	if (fabs(m_WaterSpeed.x) < 0.05)
	{
		m_WaterSpeed.x = 0;
	}

	if (fabs(m_WaterSpeed.z) < 0.05)
	{
		m_WaterSpeed.z = 0;
	}

	NextSpeed += m_WaterSpeed;

	if (NextSpeed.SqrLength() > 0.f)
	{
		cTracer Tracer(GetWorld());
		// Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse
		int DistanceToTrace = (int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2);
		bool HasHit = Tracer.Trace(NextPos, NextSpeed, DistanceToTrace);

		if (HasHit)
		{
			// Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current)
			// This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement
			if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength())
			{
				// Block hit was within our projected path
				// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
				// For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
				if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f;
				if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
				if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;

				if (Tracer.HitNormal.y == 1) // Hit BLOCK_FACE_YP, we are on the ground
				{
					m_bOnGround = true;
				}

				// Now, set our position to the hit block (i.e. move part way along our intended trajectory)
				NextPos.Set(Tracer.RealHit.x, Tracer.RealHit.y, Tracer.RealHit.z);
				NextPos.x += Tracer.HitNormal.x * 0.1;
				NextPos.y += Tracer.HitNormal.y * 0.05;
				NextPos.z += Tracer.HitNormal.z * 0.1;
			}
			else
			{
				// We have hit a block but overshot our intended trajectory, move normally, safe in the warm cocoon of knowledge that we won't appear to teleport forwards on clients,
				// and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never
				// be henceforth seen again in the time of programmers and man alike
				// </&sensationalist>
				NextPos += (NextSpeed * a_Dt);
			}
		}
		else
		{
			// We didn't hit anything, so move =]
			NextPos += (NextSpeed * a_Dt);
		}
	}

	SetPosition(NextPos);
	SetSpeed(NextSpeed);
}
示例#6
0
void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);

	if (m_Health <= 0)
	{
		// The mob is dead, but we're still animating the "puff" they leave when they die
		m_DestroyTimer += a_Dt / 1000;
		if (m_DestroyTimer > 1)
		{
			Destroy(true);
		}
		return;
	}

	// Burning in daylight
	HandleDaylightBurning(a_Chunk);
	
	HandlePhysics(a_Dt,a_Chunk);
	BroadcastMovementUpdate();

	a_Dt /= 1000;

	if (m_bMovingToDestination)
	{
		Vector3f Pos( GetPosition() );
		Vector3f Distance = m_Destination - Pos;
		if( !ReachedDestination() )
		{
			Distance.y = 0;
			Distance.Normalize();
			Distance *= 3;
			SetSpeedX( Distance.x );
			SetSpeedZ( Distance.z );

			if (m_EMState == ESCAPING)
			{	//Runs Faster when escaping :D otherwise they just walk away
				SetSpeedX (GetSpeedX() * 2.f);
				SetSpeedZ (GetSpeedZ() * 2.f);
			}
		}
		else
		{
			m_bMovingToDestination = false;
		}

		if( GetSpeed().SqrLength() > 0.f )
		{
			if( m_bOnGround )
			{
				Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy();
				Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed;
				int NextHeight;
				if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight))
				{
					// The chunk at NextBlock is not loaded
					return;
				}
				if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 )
				{
					m_bOnGround = false;
					SetSpeedY(5.f); // Jump!!
				}
			}
		}
	}

	Vector3d Distance = m_Destination - GetPosition();
	if (Distance.SqrLength() > 0.1f)
	{
		double Rotation, Pitch;
		Distance.Normalize();
		VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
		SetHeadYaw (Rotation);
		SetRotation( Rotation );
		SetPitch( -Pitch );
	}

	switch (m_EMState)
	{
		case IDLE:
		{
			// If enemy passive we ignore checks for player visibility
			InStateIdle(a_Dt);
			break;
		}
	
		case CHASING:
		{
			// If we do not see a player anymore skip chasing action
			InStateChasing(a_Dt);
			break;
		}
	
		case ESCAPING:
		{
			InStateEscaping(a_Dt);
			break;
		}
	}  // switch (m_EMState)
}
示例#7
0
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	// TODO Add collision detection with entities.
	a_Dt /= 1000;
	Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
	Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
	int BlockX = (int) floor(NextPos.x);
	int BlockY = (int) floor(NextPos.y);
	int BlockZ = (int) floor(NextPos.z);
	
	if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
	{
		// Outside of the world
		// TODO: Current speed should still be added to the entity position
		// Otherwise TNT explosions in the void will still effect the bottommost layers of the world
		return;
	}
	
	// Make sure we got the correct chunk and a valid one. No one ever knows...
	cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX,BlockZ);
	if (NextChunk != NULL)
	{
		int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
		int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
		BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
		if (!g_BlockIsSolid[BlockIn])  // Making sure we are not inside a solid block
		{
			if (m_bOnGround)  // check if it's still on the ground
			{
				BLOCKTYPE BlockBelow = NextChunk->GetBlock( RelBlockX, BlockY - 1, RelBlockZ );
				if (!g_BlockIsSolid[BlockBelow])  // Check if block below is air or water.
				{
					m_bOnGround = false;
				}
			}
		}
		else
		{
			//Push out entity.
			m_bOnGround = true;
			NextPos.y += 0.2;
			LOGD("Entity #%d (%s) is inside a block at {%d,%d,%d}",
				m_UniqueID, GetClass(), BlockX, BlockY, BlockZ);
		}

		if (!m_bOnGround)
		{
			float fallspeed;
			if (IsBlockWater(BlockIn))
			{
				fallspeed = -3.0f * a_Dt; //Fall slower in water.
			}
			else if (BlockIn == E_BLOCK_COBWEB)
			{
				NextSpeed.y *= 0.05; //Reduce overall falling speed
				fallspeed = 0; //No falling.
			}
			else
			{
				//Normal gravity
				fallspeed = m_Gravity * a_Dt;
			}
			NextSpeed.y += fallspeed;
		}
		else
		{
			//Friction
			if (NextSpeed.SqrLength() > 0.0004f)
			{
				NextSpeed.x *= 0.7f/(1+a_Dt);
				if ( fabs(NextSpeed.x) < 0.05 ) NextSpeed.x = 0;
				NextSpeed.z *= 0.7f/(1+a_Dt);
				if ( fabs(NextSpeed.z) < 0.05 ) NextSpeed.z = 0;
			}
		}

		//Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
		//might have different speed modifiers according to terrain.
		if (BlockIn == E_BLOCK_COBWEB)
		{
			NextSpeed.x *= 0.25;
			NextSpeed.z *= 0.25;
		}
					
		//Get water direction
		Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);

		m_WaterSpeed *= 0.9f;		//Reduce speed each tick

		switch(WaterDir)
		{
			case X_PLUS:
				m_WaterSpeed.x = 1.f;
				m_bOnGround = false;
				break;
			case X_MINUS:
				m_WaterSpeed.x = -1.f;
				m_bOnGround = false;
				break;
			case Z_PLUS:
				m_WaterSpeed.z = 1.f;
				m_bOnGround = false;
				break;
			case Z_MINUS:
				m_WaterSpeed.z = -1.f;
				m_bOnGround = false;
				break;
			
		default:
			break;
		}

		if (fabs(m_WaterSpeed.x) < 0.05)
		{
			m_WaterSpeed.x = 0;
		}

		if (fabs(m_WaterSpeed.z) < 0.05)
		{
			m_WaterSpeed.z = 0;
		}

		NextSpeed += m_WaterSpeed;

		if( NextSpeed.SqrLength() > 0.f )
		{
			cTracer Tracer( GetWorld() );
			int Ret = Tracer.Trace( NextPos, NextSpeed, 2 );
			if( Ret ) // Oh noez! we hit something
			{
				// Set to hit position
				if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() )
				{
					if( Ret == 1 )
					{

						if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f;
						if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f;
						if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f;

						if( Tracer.HitNormal.y > 0 ) // means on ground
						{
							m_bOnGround = true;
						}
					}
					NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
					NextPos.x += Tracer.HitNormal.x * 0.5f;
					NextPos.z += Tracer.HitNormal.z * 0.5f;
				}
				else
					NextPos += (NextSpeed * a_Dt);
			}
			else
			{
				// We didn't hit anything, so move =]
				NextPos += (NextSpeed * a_Dt);
			}
		}
		BlockX = (int) floor(NextPos.x);
		BlockZ = (int) floor(NextPos.z);
		NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ);
		// See if we can commit our changes. If not, we will discard them.
		if (NextChunk != NULL)
		{
			if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
			if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
			if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
			if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
			if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
			if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
		}
	}
}
示例#8
0
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	// TODO Add collision detection with entities.
	a_Dt /= 1000;  // Convert from msec to sec
	Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
	Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
	int BlockX = (int) floor(NextPos.x);
	int BlockY = (int) floor(NextPos.y);
	int BlockZ = (int) floor(NextPos.z);
	
	if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
	{
		// Outside of the world

		cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
		// See if we can commit our changes. If not, we will discard them.
		if (NextChunk != NULL)
		{
			SetSpeed(NextSpeed);
			NextPos += (NextSpeed * a_Dt);
			SetPosition(NextPos);
		}
		return;
	}
	
	// Make sure we got the correct chunk and a valid one. No one ever knows...
	cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
	if (NextChunk != NULL)
	{
		int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
		int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
		BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
		BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
		if (!g_BlockIsSolid[BlockIn])  // Making sure we are not inside a solid block
		{
			if (m_bOnGround)  // check if it's still on the ground
			{
				if (!g_BlockIsSolid[BlockBelow])  // Check if block below is air or water.
				{
					m_bOnGround = false;
				}
			}
		}
		else
		{
			// Push out entity.
			BLOCKTYPE GotBlock;

			static const struct
			{
				int x, y, z;
			} gCrossCoords[] =
			{
				{ 1, 0,  0},
				{-1, 0,  0},
				{ 0, 0,  1},
				{ 0, 0, -1},
			} ;

			bool IsNoAirSurrounding = true;
			for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
			{
				if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
				{
					// The pickup is too close to an unloaded chunk, bail out of any physics handling
					return;
				}
				if (!g_BlockIsSolid[GotBlock])
				{
					NextPos.x += gCrossCoords[i].x;
					NextPos.z += gCrossCoords[i].z;
					IsNoAirSurrounding = false;
					break;
				}
			}  // for i - gCrossCoords[]
			
			if (IsNoAirSurrounding)
			{
				NextPos.y += 0.5;
			}

			m_bOnGround = true;

			LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
				m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
			);
		}

		if (!m_bOnGround)
		{
			float fallspeed;
			if (IsBlockWater(BlockIn))
			{
				fallspeed = m_Gravity * a_Dt / 3;  // Fall 3x slower in water.
			}
			else if (IsBlockRail(BlockBelow) && IsMinecart())  // Rails aren't solid, except for Minecarts
			{
				fallspeed = 0;
				m_bOnGround = true;
			}
			else if (BlockIn == E_BLOCK_COBWEB)
			{
				NextSpeed.y *= 0.05;  // Reduce overall falling speed
				fallspeed = 0;  // No falling.
			}
			else
			{
				// Normal gravity
				fallspeed = m_Gravity * a_Dt;
			}
			NextSpeed.y += fallspeed;
		}
		else
		{
			if (IsMinecart())
			{
				if (!IsBlockRail(BlockBelow))
				{
					// Friction if minecart is off track, otherwise, Minecart.cpp handles this
					if (NextSpeed.SqrLength() > 0.0004f)
					{
						NextSpeed.x *= 0.7f / (1 + a_Dt);
						if (fabs(NextSpeed.x) < 0.05)
						{
							NextSpeed.x = 0;
						}
						NextSpeed.z *= 0.7f / (1 + a_Dt);
						if (fabs(NextSpeed.z) < 0.05)
						{
							NextSpeed.z = 0;
						}
					}
				}
			}
			else
			{
				// Friction for non-minecarts
				if (NextSpeed.SqrLength() > 0.0004f)
				{
					NextSpeed.x *= 0.7f / (1 + a_Dt);
					if (fabs(NextSpeed.x) < 0.05)
					{
						NextSpeed.x = 0;
					}
					NextSpeed.z *= 0.7f / (1 + a_Dt);
					if (fabs(NextSpeed.z) < 0.05)
					{
						NextSpeed.z = 0;
					}
				}
			}
		}

		// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
		// might have different speed modifiers according to terrain.
		if (BlockIn == E_BLOCK_COBWEB)
		{
			NextSpeed.x *= 0.25;
			NextSpeed.z *= 0.25;
		}
					
		//Get water direction
		Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ);

		m_WaterSpeed *= 0.9f;		//Reduce speed each tick

		switch(WaterDir)
		{
			case X_PLUS:
				m_WaterSpeed.x = 0.2f;
				m_bOnGround = false;
				break;
			case X_MINUS:
				m_WaterSpeed.x = -0.2f;
				m_bOnGround = false;
				break;
			case Z_PLUS:
				m_WaterSpeed.z = 0.2f;
				m_bOnGround = false;
				break;
			case Z_MINUS:
				m_WaterSpeed.z = -0.2f;
				m_bOnGround = false;
				break;
			
		default:
			break;
		}

		if (fabs(m_WaterSpeed.x) < 0.05)
		{
			m_WaterSpeed.x = 0;
		}

		if (fabs(m_WaterSpeed.z) < 0.05)
		{
			m_WaterSpeed.z = 0;
		}

		NextSpeed += m_WaterSpeed;

		if( NextSpeed.SqrLength() > 0.f )
		{
			cTracer Tracer( GetWorld() );
			int Ret = Tracer.Trace( NextPos, NextSpeed, 2 );
			if( Ret ) // Oh noez! we hit something
			{
				// Set to hit position
				if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() )
				{
					if( Ret == 1 )
					{
						if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f;
						if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f;
						if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f;

						if( Tracer.HitNormal.y > 0 ) // means on ground
						{
							m_bOnGround = true;
						}
					}
					NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
					NextPos.x += Tracer.HitNormal.x * 0.3f;
					NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot
					NextPos.z += Tracer.HitNormal.z * 0.3f;
				}
				else
				{
					NextPos += (NextSpeed * a_Dt);
				}
			}
			else
			{
				// We didn't hit anything, so move =]
				NextPos += (NextSpeed * a_Dt);
			}
		}
		BlockX = (int) floor(NextPos.x);
		BlockZ = (int) floor(NextPos.z);
		NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ);
		// See if we can commit our changes. If not, we will discard them.
		if (NextChunk != NULL)
		{
			if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
			if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
			if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
			if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
			if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
			if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
		}
	}
}