示例#1
0
void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
{
	/*
	NOTE: Please bear in mind that taking away from negatives make them even more negative,
	adding to negatives make them positive, etc.
	*/

	switch (a_RailMeta)
	{
		case E_META_RAIL_ZM_ZP:  // NORTHSOUTH
		{
			SetYaw(270);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(0);  // Don't move vertically as on ground
			SetSpeedX(0);  // Correct diagonal movement from curved rails

			// Execute both the entity and block collision checks
			bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
			if (EntCol || BlckCol)
			{
				return;
			}

			if (GetSpeedZ() != NO_SPEED)  // Don't do anything if cart is stationary
			{
				if (GetSpeedZ() > 0)
				{
					// Going SOUTH, slow down
					AddSpeedZ(-0.1);
				}
				else
				{
					// Going NORTH, slow down
					AddSpeedZ(0.1);
				}
			}
			break;
		}
		case E_META_RAIL_XM_XP:  // EASTWEST
		{
			SetYaw(180);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(NO_SPEED);
			SetSpeedZ(NO_SPEED);

			bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
			if (EntCol || BlckCol)
			{
				return;
			}

			if (GetSpeedX() != NO_SPEED)
			{
				if (GetSpeedX() > 0)
				{
					AddSpeedX(-0.1);
				}
				else
				{
					AddSpeedX(0.1);
				}
			}
			break;
		}
		case E_META_RAIL_ASCEND_ZM:  // ASCEND NORTH
		{
			SetYaw(270);
			SetSpeedX(0);

			if (GetSpeedZ() >= 0)
			{
				// SpeedZ POSITIVE, going SOUTH
				AddSpeedZ(0.5);  // Speed up
				SetSpeedY(-GetSpeedZ());  // Downward movement is negative (0 minus positive numbers is negative)
			}
			else
			{
				// SpeedZ NEGATIVE, going NORTH
				AddSpeedZ(1);  // Slow down
				SetSpeedY(-GetSpeedZ());  // Upward movement is positive (0 minus negative number is positive number)
			}
			break;
		}
		case E_META_RAIL_ASCEND_ZP:  // ASCEND SOUTH
		{
			SetYaw(270);
			SetSpeedX(0);

			if (GetSpeedZ() > 0)
			{
				// SpeedZ POSITIVE, going SOUTH
				AddSpeedZ(-1);  // Slow down
				SetSpeedY(GetSpeedZ());  // Upward movement positive
			}
			else
			{
				// SpeedZ NEGATIVE, going NORTH
				AddSpeedZ(-0.5);  // Speed up
				SetSpeedY(GetSpeedZ());  // Downward movement negative
			}
			break;
		}
		case E_META_RAIL_ASCEND_XM:  // ASCEND EAST
		{
			SetYaw(180);
			SetSpeedZ(NO_SPEED);

			if (GetSpeedX() >= NO_SPEED)
			{
				AddSpeedX(0.5);
				SetSpeedY(-GetSpeedX());
			}
			else
			{
				AddSpeedX(1);
				SetSpeedY(-GetSpeedX());
			}
			break;
		}
		case E_META_RAIL_ASCEND_XP:  // ASCEND WEST
		{
			SetYaw(180);
			SetSpeedZ(0);

			if (GetSpeedX() > 0)
			{
				AddSpeedX(-1);
				SetSpeedY(GetSpeedX());
			}
			else
			{
				AddSpeedX(-0.5);
				SetSpeedY(GetSpeedX());
			}
			break;
		}
		case E_META_RAIL_CURVED_ZM_XM:  // Ends pointing NORTH and WEST
		{
			SetYaw(315);  // Set correct rotation server side
			SetPosY(floor(GetPosY()) + 0.55);  // Levitate dat cart
			SetSpeedY(0);

			TestBlockCollision(a_RailMeta);
			TestEntityCollision(a_RailMeta);

			// SnapToRail handles turning

			break;
		}
		case E_META_RAIL_CURVED_ZM_XP:  // Curved NORTH EAST
		{
			SetYaw(225);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(0);

			TestBlockCollision(a_RailMeta);
			TestEntityCollision(a_RailMeta);

			break;
		}
		case E_META_RAIL_CURVED_ZP_XM:  // Curved SOUTH WEST
		{
			SetYaw(135);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(0);

			TestBlockCollision(a_RailMeta);
			TestEntityCollision(a_RailMeta);

			break;
		}
		case E_META_RAIL_CURVED_ZP_XP:  // Curved SOUTH EAST
		{
			SetYaw(45);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(0);

			TestBlockCollision(a_RailMeta);
			TestEntityCollision(a_RailMeta);

			break;
		}
		default:
		{
			ASSERT(!"Unhandled rail meta!");  // Dun dun DUN!
			break;
		}
	}
}
示例#2
0
void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
{
	// Initialise to 'slow down' values
	int AccelDecelSpeed = -2;
	int AccelDecelNegSpeed = 2;

	if ((a_RailMeta & 0x8) == 0x8)
	{
		// Rail powered - set variables to 'speed up' values
		AccelDecelSpeed = 1;
		AccelDecelNegSpeed = -1;
	}

	switch (a_RailMeta & 0x07)
	{
		case E_META_RAIL_ZM_ZP:  // NORTHSOUTH
		{
			SetYaw(270);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(0);
			SetSpeedX(0);

			bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
			if (EntCol || BlckCol)
			{
				return;
			}

			if (GetSpeedZ() != NO_SPEED)
			{
				if (GetSpeedZ() > NO_SPEED)
				{
					AddSpeedZ(AccelDecelSpeed);
				}
				else
				{
					AddSpeedZ(AccelDecelNegSpeed);
				}
			}
			break;
		}
		case E_META_RAIL_XM_XP:  // EASTWEST
		{
			SetYaw(180);
			SetPosY(floor(GetPosY()) + 0.55);
			SetSpeedY(NO_SPEED);
			SetSpeedZ(NO_SPEED);

			bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
			if (EntCol || BlckCol)
			{
				return;
			}

			if (GetSpeedX() != NO_SPEED)
			{
				if (GetSpeedX() > NO_SPEED)
				{
					AddSpeedX(AccelDecelSpeed);
				}
				else
				{
					AddSpeedX(AccelDecelNegSpeed);
				}
			}
			break;
		}
		case E_META_RAIL_ASCEND_XM:  // ASCEND EAST
		{
			SetYaw(180);
			SetSpeedZ(NO_SPEED);

			if (GetSpeedX() >= NO_SPEED)
			{
				AddSpeedX(AccelDecelSpeed);
				SetSpeedY(-GetSpeedX());
			}
			else
			{
				AddSpeedX(AccelDecelNegSpeed);
				SetSpeedY(-GetSpeedX());
			}
			break;
		}
		case E_META_RAIL_ASCEND_XP:  // ASCEND WEST
		{
			SetYaw(180);
			SetSpeedZ(NO_SPEED);

			if (GetSpeedX() > NO_SPEED)
			{
				AddSpeedX(AccelDecelSpeed);
				SetSpeedY(GetSpeedX());
			}
			else
			{
				AddSpeedX(AccelDecelNegSpeed);
				SetSpeedY(GetSpeedX());
			}
			break;
		}
		case E_META_RAIL_ASCEND_ZM:  // ASCEND NORTH
		{
			SetYaw(270);
			SetSpeedX(NO_SPEED);

			if (GetSpeedZ() >= NO_SPEED)
			{
				AddSpeedZ(AccelDecelSpeed);
				SetSpeedY(-GetSpeedZ());
			}
			else
			{
				AddSpeedZ(AccelDecelNegSpeed);
				SetSpeedY(-GetSpeedZ());
			}
			break;
		}
		case E_META_RAIL_ASCEND_ZP:  // ASCEND SOUTH
		{
			SetYaw(270);
			SetSpeedX(NO_SPEED);

			if (GetSpeedZ() > NO_SPEED)
			{
				AddSpeedZ(AccelDecelSpeed);
				SetSpeedY(GetSpeedZ());
			}
			else
			{
				AddSpeedZ(AccelDecelNegSpeed);
				SetSpeedY(GetSpeedZ());
			}
			break;
		}
		default: ASSERT(!"Unhandled powered rail metadata!"); break;
	}
}
示例#3
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;
	}

	if ((m_Target != NULL) && m_Target->IsDestroyed())
		m_Target = NULL;

	// Burning in daylight
	HandleDaylightBurning(a_Chunk);

	a_Dt /= 1000;

	if (m_bMovingToDestination)
	{
		if (m_bOnGround)
		{
			m_Destination.y = FindFirstNonAirBlockPosition(m_Destination.x, m_Destination.z);

			if (DoesPosYRequireJump((int)floor(m_Destination.y)))
			{
				m_bOnGround = false;
				AddPosY(1.5); // Jump!!
			}
		}

		Vector3f Distance = m_Destination - GetPosition();
		if(!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move
		{
			Distance.y = 0;
			Distance.Normalize();
			Distance *= 5;
			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
		{
			if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate
			{
				FinishPathFinding();
			}
			else
			{
				TickPathFinding(); // We have reached the next point in our path, calculate another point
			}
		}
	}

	SetPitchAndYawFromDestination();
	HandleFalling();

	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)

	BroadcastMovementUpdate();
}
示例#4
0
void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
{
	
	super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling

	/*
	NOTE: Please bear in mind that taking away from negatives make them even more negative,
	adding to negatives make them positive, etc.
	*/
	
	// Get block meta below the cart
	int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
	NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ);
	double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed
	
	switch (BelowMeta)
	{
		case E_META_RAIL_ZM_ZP: // NORTHSOUTH
		{
			SetRotation(270);
			SpeedY = 0; // Don't move vertically as on ground
			SpeedX = 0; // Correct diagonal movement from curved rails
			
			if (SpeedZ != 0) // Don't do anything if cart is stationary
			{
				if (SpeedZ > 0)
				{
					// Going SOUTH, slow down
					SpeedZ = SpeedZ - 0.1;
				}
				else
				{
					// Going NORTH, slow down
					SpeedZ = SpeedZ + 0.1;
				}
			}
			break;
		}

		case E_META_RAIL_XM_XP: // EASTWEST
		{
			SetRotation(180);
			SpeedY = 0;
			SpeedZ = 0;

			if (SpeedX != 0)
			{
				if (SpeedX > 0)
				{
					SpeedX = SpeedX - 0.1;
				}
				else
				{
					SpeedX = SpeedX + 0.1;
				}
			}
			break;
		}

		case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
		{
			SetRotation(270);
			SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/
			SpeedX = 0;

			if (SpeedZ >= 0)
			{
				// SpeedZ POSITIVE, going SOUTH
				if (SpeedZ <= MAX_SPEED) // Speed limit
				{
					SpeedZ = SpeedZ + 0.5; // Speed up
					SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative)
				}
				else
				{
					SpeedZ = MAX_SPEED; // Enforce speed limit
					SpeedY = (0 - SpeedZ);
				}
			}
			else
			{
				// SpeedZ NEGATIVE, going NORTH
				SpeedZ = SpeedZ + 0.4; // Slow down
				SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number)
			}
			break;
		}

		case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
		{
			SetRotation(270);
			SetPosY(floor(GetPosY()) + 0.2);
			SpeedX = 0;

			if (SpeedZ > 0)
			{
				// SpeedZ POSITIVE, going SOUTH
				SpeedZ = SpeedZ - 0.4; // Slow down
				SpeedY = SpeedZ; // Upward movement positive
			}
			else
			{
				if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit
				{
					// SpeedZ NEGATIVE, going NORTH
					SpeedZ = SpeedZ - 0.5; // Speed up
					SpeedY = SpeedZ; // Downward movement negative
				}
				else
				{
					SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit
					SpeedY = SpeedZ;
				}
			}
			break;
		}

		case E_META_RAIL_ASCEND_XM: // ASCEND EAST
		{
			SetRotation(180);
			SetPosY(floor(GetPosY()) + 0.2);
			SpeedZ = 0;

			if (SpeedX >= 0)
			{
				if (SpeedX <= MAX_SPEED)
				{
					SpeedX = SpeedX + 0.5;
					SpeedY = (0 - SpeedX);
				}
				else
				{
					SpeedX = MAX_SPEED;
					SpeedY = (0 - SpeedX);
				}
			}
			else
			{
				SpeedX = SpeedX + 0.4;
				SpeedY = (0 - SpeedX);
			}
			break;
		}

		case E_META_RAIL_ASCEND_XP: // ASCEND WEST
		{
			SetRotation(180);
			SetPosY(floor(GetPosY()) + 0.2);
			SpeedZ = 0;

			if (SpeedX > 0)
			{
				SpeedX = SpeedX - 0.4;
				SpeedY = SpeedX;
			}
			else
			{
				if (SpeedX >= MAX_SPEED_NEGATIVE)
				{
					SpeedX = SpeedX - 0.5;
					SpeedY = SpeedX;
				}
				else
				{
					SpeedX = MAX_SPEED_NEGATIVE;
					SpeedY = SpeedX;
				}
			}
			break;
		}

		case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST
		{
			SetRotation(315); // Set correct rotation server side
			SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart

			if (SpeedZ > 0) // Cart moving south
			{
				SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail)
			}
			else if (SpeedX > 0) // Cart moving east
			{
				SpeedZ = (0 - SpeedX); // Diagonally move northeast
			}
			break;
		}

		case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
		{
			SetRotation(225);
			SetPosY(floor(GetPosY()) + 0.2);

			if (SpeedZ > 0)
			{
				SpeedX = SpeedZ;
			}
			else if (SpeedX < 0)
			{
				SpeedZ = SpeedX;
			}
			break;
		}

		case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
		{
			SetRotation(135);
			SetPosY(floor(GetPosY()) + 0.2);

			if (SpeedZ < 0)
			{
				SpeedX = SpeedZ;
			}
			else if (SpeedX > 0)
			{
				SpeedZ = SpeedX;
			}
			break;
		}

		case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
		{
			SetRotation(45);
			SetPosY(floor(GetPosY()) + 0.2);

			if (SpeedZ < 0)
			{
				SpeedX = (0 - SpeedZ);
			}
			else if (SpeedX < 0)
			{
				SpeedZ = (0 - SpeedX);
			}
			break;
		}

		default:
		{
			ASSERT(!"Unhandled rail meta!"); // Dun dun DUN!
			break;
		}
	}

	// Set speed to speed variables
	SetSpeedX(SpeedX);
	SetSpeedY(SpeedY);
	SetSpeedZ(SpeedZ);


	// Broadcast position to client
	BroadcastMovementUpdate();
}
示例#5
0
void cFloater::Tick(float a_Dt, cChunk & a_Chunk)
{
	HandlePhysics(a_Dt, a_Chunk);
	if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0)
	{
		if ((!m_CanPickupItem) && (m_AttachedMobID == -1))  // Check if you can't already pickup a fish and if the floater isn't attached to a mob.
		{
			if (m_CountDownTime <= 0)
			{
				m_World->BroadcastSoundEffect("random.splash", GetPosX(), GetPosY(), GetPosZ(), 1, 1);
				SetPosY(GetPosY() - 1);
				m_CanPickupItem = true;
				m_PickupCountDown = 20;
				m_CountDownTime = 100 + m_World->GetTickRandomNumber(800);
				LOGD("Floater %i can be picked up", GetUniqueID());
			}
			else if (m_CountDownTime == 20)  // Calculate the position where the particles should spawn and start producing them.
			{
				LOGD("Started producing particles for floater %i", GetUniqueID());
				m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8)));
				m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15);
			}
			else if (m_CountDownTime < 20)
			{
				m_ParticlePos = (m_ParticlePos + (GetPosition() - m_ParticlePos) / 6);
				m_World->BroadcastParticleEffect("splash", (float) m_ParticlePos.x, (float) m_ParticlePos.y, (float) m_ParticlePos.z, 0, 0, 0, 0, 15);
			}
			
			m_CountDownTime--;
			if (m_World->GetHeight((int) GetPosX(), (int) GetPosZ()) == (int) GetPosY())
			{
				if (m_World->IsWeatherWet() && m_World->GetTickRandomNumber(3) == 0)  // 25% chance of an extra countdown when being rained on.
				{
					m_CountDownTime--;
				}
			}
			else  // if the floater is underground it has a 50% chance of not decreasing the countdown.
			{
				if (m_World->GetTickRandomNumber(1) == 0)
				{
					m_CountDownTime++;
				}
			}
		}
		SetSpeedY(0.7);
	}

	if (CanPickup())  // Make sure the floater "loses its fish"
	{
		m_PickupCountDown--;
		if (m_PickupCountDown == 0)
		{
			m_CanPickupItem = false;
			LOGD("The fish is gone. Floater %i can not pick an item up.", GetUniqueID());
		}
	}

	if ((GetSpeed().Length() > 4) && (m_AttachedMobID == -1))
	{
		cFloaterEntityCollisionCallback Callback(this, GetPosition(), GetPosition() + GetSpeed() / 20);
		
		a_Chunk.ForEachEntity(Callback);
		if (Callback.HasHit())
		{
			AttachTo(Callback.GetHitEntity());
			Callback.GetHitEntity()->TakeDamage(*this);  // TODO: the player attacked the mob not the floater.
			m_AttachedMobID = Callback.GetHitEntity()->GetUniqueID();
		}
	}

	cFloaterCheckEntityExist EntityCallback;
	m_World->DoWithEntityByID(m_PlayerID, EntityCallback);
	if (!EntityCallback.DoesExist())  // The owner doesn't exist anymore. Destroy the floater entity.
	{
		Destroy(true);
	}

	if (m_AttachedMobID != -1)
	{
		m_World->DoWithEntityByID(m_AttachedMobID, EntityCallback);  // The mob the floater was attached to doesn't exist anymore.
		if (!EntityCallback.DoesExist())
		{
			m_AttachedMobID = -1;
		}
	}

	SetSpeedX(GetSpeedX() * 0.95);
	SetSpeedZ(GetSpeedZ() * 0.95);

	BroadcastMovementUpdate();
}
示例#6
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);
		}
	}
}
bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
{
	cMinecartCollisionCallback MinecartCollisionCallback(GetPosition(), GetHeight(), GetWidth(), GetUniqueID(), ((m_Attachee == nullptr) ? -1 : m_Attachee->GetUniqueID()));
	int ChunkX, ChunkZ;
	cChunkDef::BlockToChunk(POSX_TOINT, POSZ_TOINT, ChunkX, ChunkZ);
	m_World->ForEachEntityInChunk(ChunkX, ChunkZ, MinecartCollisionCallback);

	if (!MinecartCollisionCallback.FoundIntersection())
	{
		return false;
	}

	switch (a_RailMeta)
	{
		case E_META_RAIL_ZM_ZP:
		{
			if (MinecartCollisionCallback.GetCollidedEntityPosition().z >= GetPosZ())
			{
				if ((-GetSpeedZ() * 0.4) < 0.01)
				{
					AddSpeedZ(-4);
				}
				else
				{
					SetSpeedZ(-GetSpeedZ() * 0.4);
				}
			}
			else
			{
				if ((GetSpeedZ() * 0.4) < 0.01)
				{
					AddSpeedZ(4);
				}
				else
				{
					SetSpeedZ(GetSpeedZ() * 0.4);
				}
			}
			return true;
		}
		case E_META_RAIL_XM_XP:
		{
			if (MinecartCollisionCallback.GetCollidedEntityPosition().x >= GetPosX())
			{
				if ((-GetSpeedX() * 0.4) < 0.01)
				{
					AddSpeedX(-4);
				}
				else
				{
					SetSpeedX(-GetSpeedX() * 0.4);
				}
			}
			else
			{
				if ((GetSpeedX() * 0.4) < 0.01)
				{
					AddSpeedX(4);
				}
				else
				{
					SetSpeedX(GetSpeedX() * 0.4);
				}
			}
			return true;
		}
		case E_META_RAIL_CURVED_ZM_XM:
		case E_META_RAIL_CURVED_ZP_XP:
		{
			Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());

			// Prevent division by small numbers
			if (std::abs(Distance.z) < 0.001)
			{
				Distance.z = 0.001;
			}

			/* Check to which side the minecart is to be pushed.
			Let's consider a z-x-coordinate system where the minecart is the center (0/0).
			The minecart moves along the line x = -z, the perpendicular line to this is x = z.
			In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
			if (
				((Distance.z > 0) && ((Distance.x / Distance.z) >= 1)) ||
				((Distance.z < 0) && ((Distance.x / Distance.z) <= 1))
			)
			{
				// Moving -X +Z
				if ((-GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
				{
					// ~ SpeedX >= 0 Immobile or not moving in the "right" direction. Give it a bump!
					AddSpeedX(-4 / sqrt(2.0));
					AddSpeedZ(4 / sqrt(2.0));
				}
				else
				{
					// ~ SpeedX < 0 Moving in the "right" direction. Only accelerate it a bit.
					SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
					SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
				}
			}
			else if ((GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
			{
				// Moving +X -Z
				// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
				AddSpeedX(4 / sqrt(2.0));
				AddSpeedZ(-4 / sqrt(2.0));
			}
			else
			{
				// ~ SpeedX > 0 Moving in the "right" direction
				SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
				SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
			}
			break;
		}
		case E_META_RAIL_CURVED_ZM_XP:
		case E_META_RAIL_CURVED_ZP_XM:
		{
			Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());

			// Prevent division by small numbers
			if (std::abs(Distance.z) < 0.001)
			{
				Distance.z = 0.001;
			}

			/* Check to which side the minecart is to be pushed.
			Let's consider a z-x-coordinate system where the minecart is the center (0/0).
			The minecart moves along the line x = z, the perpendicular line to this is x = -z.
			In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
			if (
				((Distance.z > 0) && ((Distance.x / Distance.z) <= -1)) ||
				((Distance.z < 0) && ((Distance.x / Distance.z) >= -1))
			)
			{
				// Moving +X +Z
				if ((GetSpeedX() * 0.4) < 0.01)
				{
					// ~ SpeedX <= 0 Immobile or not moving in the "right" direction
					AddSpeedX(4 / sqrt(2.0));
					AddSpeedZ(4 / sqrt(2.0));
				}
				else
				{
					// ~ SpeedX > 0 Moving in the "right" direction
					SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
					SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
				}
			}
			else if ((-GetSpeedX() * 0.4) < 0.01)
			{
				// Moving -X -Z
				// ~ SpeedX >= 0 Immobile or not moving in the "right" direction
				AddSpeedX(-4 / sqrt(2.0));
				AddSpeedZ(-4 / sqrt(2.0));
			}
			else
			{
				// ~ SpeedX < 0 Moving in the "right" direction
				SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
				SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
			}
			break;
		}
		default: break;
	}

	return false;
}
void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
{
	switch (a_RailMeta)
	{
		case E_META_RAIL_ASCEND_XM:
		case E_META_RAIL_ASCEND_XP:
		case E_META_RAIL_XM_XP:
		{
			SetSpeedZ(NO_SPEED);
			SetPosZ(floor(GetPosZ()) + 0.5);
			break;
		}
		case E_META_RAIL_ASCEND_ZM:
		case E_META_RAIL_ASCEND_ZP:
		case E_META_RAIL_ZM_ZP:
		{
			SetSpeedX(NO_SPEED);
			SetPosX(floor(GetPosX()) + 0.5);
			break;
		}
		// Curved rail physics: once minecart has reached more than half of the block in the direction that it is travelling in, jerk it in the direction of curvature
		case E_META_RAIL_CURVED_ZM_XM:
		{
			if (GetPosZ() > floor(GetPosZ()) + 0.5)
			{
				if (GetSpeedZ() > NO_SPEED)
				{
					SetSpeedX(-GetSpeedZ() * 0.7);
				}

				SetSpeedZ(NO_SPEED);
				SetPosZ(floor(GetPosZ()) + 0.5);
			}
			else if (GetPosX() > floor(GetPosX()) + 0.5)
			{
				if (GetSpeedX() > 0)
				{
					SetSpeedZ(-GetSpeedX() * 0.7);
				}

				SetSpeedX(NO_SPEED);
				SetPosX(floor(GetPosX()) + 0.5);
			}
			SetSpeedY(NO_SPEED);
			break;
		}
		case E_META_RAIL_CURVED_ZM_XP:
		{
			if (GetPosZ() > floor(GetPosZ()) + 0.5)
			{
				if (GetSpeedZ() > NO_SPEED)
				{
					SetSpeedX(GetSpeedZ() * 0.7);
				}

				SetSpeedZ(NO_SPEED);
				SetPosZ(floor(GetPosZ()) + 0.5);
			}
			else if (GetPosX() < floor(GetPosX()) + 0.5)
			{
				if (GetSpeedX() < NO_SPEED)
				{
					SetSpeedZ(GetSpeedX() * 0.7);
				}

				SetSpeedX(NO_SPEED);
				SetPosX(floor(GetPosX()) + 0.5);
			}
			SetSpeedY(NO_SPEED);
			break;
		}
		case E_META_RAIL_CURVED_ZP_XM:
		{
			if (GetPosZ() < floor(GetPosZ()) + 0.5)
			{
				if (GetSpeedZ() < NO_SPEED)
				{
					SetSpeedX(GetSpeedZ() * 0.7);
				}

				SetSpeedZ(NO_SPEED);
				SetPosZ(floor(GetPosZ()) + 0.5);
			}
			else if (GetPosX() > floor(GetPosX()) + 0.5)
			{
				if (GetSpeedX() > NO_SPEED)
				{
					SetSpeedZ(GetSpeedX() * 0.7);
				}

				SetSpeedX(NO_SPEED);
				SetPosX(floor(GetPosX()) + 0.5);
			}
			SetSpeedY(NO_SPEED);
			break;
		}
		case E_META_RAIL_CURVED_ZP_XP:
		{
			if (GetPosZ() < floor(GetPosZ()) + 0.5)
			{
				if (GetSpeedZ() < NO_SPEED)
				{
					SetSpeedX(-GetSpeedZ() * 0.7);
				}

				SetSpeedZ(NO_SPEED);
				SetPosZ(floor(GetPosZ()) + 0.5);
			}
			else if (GetPosX() < floor(GetPosX()) + 0.5)
			{
				if (GetSpeedX() < NO_SPEED)
				{
					SetSpeedZ(-GetSpeedX() * 0.7);
				}

				SetSpeedX(NO_SPEED);
				SetPosX(floor(GetPosX()) + 0.5);
			}
			SetSpeedY(0);
			break;
		}
		default: break;
	}
}
示例#9
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);
		}
	}
}