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