Esempio n. 1
0
/// Moves items from a furnace above the hopper into this hopper. Returns true if contents have changed.
bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
{
	cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ);
	ASSERT(Furnace != NULL);
	
	// Try move from the output slot:
	if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true))
	{
		cItem NewOutput(Furnace->GetOutputSlot());
		Furnace->SetOutputSlot(NewOutput.AddCount(-1));
		return true;
	}
	
	// No output moved, check if we can move an empty bucket out of the fuel slot:
	if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
	{
		if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel, true))
		{
			Furnace->SetFuelSlot(cItem());
			return true;
		}
	}
	
	// Nothing can be moved
	return false;
}
Esempio n. 2
0
bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk)
{
	cFurnaceEntity * Furnace = static_cast<cFurnaceEntity *>(a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ));
	if (Furnace == nullptr)
	{
		LOGWARNING("%s: A furnace entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX, m_PosY + 1, m_PosZ);
		return false;
	}

	// Try move from the output slot:
	if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput))
	{
		cItem NewOutput(Furnace->GetOutputSlot());
		Furnace->SetOutputSlot(NewOutput.AddCount(-1));
		return true;
	}

	// No output moved, check if we can move an empty bucket out of the fuel slot:
	if (Furnace->GetFuelSlot().m_ItemType == E_ITEM_BUCKET)
	{
		if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsFuel))
		{
			Furnace->SetFuelSlot(cItem());
			return true;
		}
	}

	// Nothing can be moved
	return false;
}
Esempio n. 3
0
void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
	int DispX = m_PosX;
	int DispY = m_PosY;
	int DispZ = m_PosZ;
	NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
	AddDropSpenserDir(DispX, DispY, DispZ, Meta);

	cItems Pickups;
	Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));

	const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2;  // At least 2, at most 6
	int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0;
	switch (Meta)
	{
		case E_META_DROPSPENSER_FACING_YP: PickupSpeedY =  PickupSpeed; break;
		case E_META_DROPSPENSER_FACING_YM: PickupSpeedY = -PickupSpeed; break;
		case E_META_DROPSPENSER_FACING_XM: PickupSpeedX = -PickupSpeed; break;
		case E_META_DROPSPENSER_FACING_XP: PickupSpeedX =  PickupSpeed; break;
		case E_META_DROPSPENSER_FACING_ZM: PickupSpeedZ = -PickupSpeed; break;
		case E_META_DROPSPENSER_FACING_ZP: PickupSpeedZ =  PickupSpeed; break;
	}

	double MicroX, MicroY, MicroZ;
	MicroX = DispX + 0.5;
	MicroY = DispY + 0.4;  // Slightly less than half, to accomodate actual texture hole on DropSpenser
	MicroZ = DispZ + 0.5;


	m_World->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ, PickupSpeedX, PickupSpeedY, PickupSpeedZ);
}
Esempio n. 4
0
void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
	if (m_InvulnerableTicks > 0)
	{
		m_InvulnerableTicks--;
	}

	if (m_AttachedTo != NULL)
	{
		Vector3d DeltaPos = m_Pos - m_AttachedTo->GetPosition();
		if (DeltaPos.Length() > 0.5)
		{
			SetPosition(m_AttachedTo->GetPosition());

			if (IsPlayer())
			{
				cPlayer * Player = (cPlayer *)this;
				Player->UpdateMovementStats(DeltaPos);
			}
		}
	}
	else
	{
		if (!a_Chunk.IsValid())
		{
			return;
		}

		// Position changed -> super::Tick() called
		GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, POSX_TOINT, POSZ_TOINT)

		TickBurning(*NextChunk);

		if (GetPosY() < VOID_BOUNDARY)
		{
			TickInVoid(*NextChunk);
		}
		else
		{
			m_TicksSinceLastVoidDamage = 0;
		}

		if (IsMob() || IsPlayer() || IsPickup() || IsExpOrb())
		{
			DetectCacti();
		}
		if (IsMob() || IsPlayer())
		{
			// Set swimming state
			SetSwimState(*NextChunk);

			// Handle drowning
			HandleAir();
		}

		// None of the above functions change position, we remain in the chunk of NextChunk
		HandlePhysics(a_Dt, *NextChunk);
	}
}
Esempio n. 5
0
bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
	Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();
	
	bool res = false;
	res = MoveItemsIn  (a_Chunk, CurrentTick) || res;
	res = MovePickupsIn(a_Chunk, CurrentTick) || res;
	res = MoveItemsOut (a_Chunk, CurrentTick) || res;
	return res;
}
Esempio n. 6
0
/// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed.
bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk)
{
	if (MoveItemsFromGrid(((cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))->GetContents()))
	{
		// Moved the item from the chest directly above the hopper
		return true;
	}
	
	// Check if the chest is a double-chest, if so, try to move from there:
	static const struct
	{
		int x, z;
	}
	Coords [] =
	{
		{1, 0},
		{-1, 0},
		{0, 1},
		{0, -1},
	} ;
	for (int i = 0; i < ARRAYCOUNT(Coords); i++)
	{
		int x = m_RelX + Coords[i].x;
		int z = m_RelZ + Coords[i].z;
		cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
		if (
			(Neighbor == NULL) ||
			(Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
		)
		{
			continue;
		}
		if (MoveItemsFromGrid(((cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))->GetContents()))
		{
			return true;
		}
		return false;
	}
	
	// The chest was single and nothing could be moved
	return false;
}
Esempio n. 7
0
/// Moves items to the chest at the specified coords. Returns true if contents have changed
bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ)
{
	// Try the chest directly connected to the hopper:
	if (MoveItemsToGrid(((cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
	{
		return true;
	}

	// Check if the chest is a double-chest, if so, try to move into the other half:
	static const struct
	{
		int x, z;
	}
	Coords [] =
	{
		{1, 0},
		{-1, 0},
		{0, 1},
		{0, -1},
	} ;
	for (int i = 0; i < ARRAYCOUNT(Coords); i++)
	{
		int x = m_RelX + Coords[i].x;
		int z = m_RelZ + Coords[i].z;
		cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z);
		if (
			(Neighbor == NULL) ||
			(Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST)
		)
		{
			continue;
		}
		if (MoveItemsToGrid(((cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))->GetContents()))
		{
			return true;
		}
		return false;
	}
	
	// The chest was single and nothing could be moved
	return false;
}
Esempio n. 8
0
bool cHopperEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	UNUSED(a_Dt);
	Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge();

	bool res = false;
	res = MoveItemsIn  (a_Chunk, CurrentTick) || res;
	res = MovePickupsIn(a_Chunk, CurrentTick) || res;
	res = MoveItemsOut (a_Chunk, CurrentTick) || res;
	return res;
}
Esempio n. 9
0
void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);
	BroadcastMovementUpdate();  // Notify clients of position

	m_Timer += a_Dt;
	
	if (!m_bCollected)
	{
		int BlockY = POSY_TOINT;
		int BlockX = POSX_TOINT;
		int BlockZ = POSZ_TOINT;

		if ((BlockY >= 0) && (BlockY < cChunkDef::Height))  // Don't do anything except for falling when outside the world
		{
			// Position might have changed due to physics. So we have to make sure we have the correct chunk.
			GET_AND_VERIFY_CURRENT_CHUNK(CurrentChunk, BlockX, BlockZ)
			
			int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
			int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
				
			// If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
			BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
			BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);

			if (
				IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) ||
				IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE)
			)
			{
				m_bCollected = true;
				m_Timer = std::chrono::milliseconds(0);  // We have to reset the timer.
				m_Timer += a_Dt;  // In case we have to destroy the pickup in the same tick.
				if (m_Timer > std::chrono::milliseconds(500))
				{
					Destroy(true);
					return;
				}
			}

			// Try to combine the pickup with adjacent same-item pickups:
			if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize()))  // Don't combine if already full
			{
				// By using a_Chunk's ForEachEntity() instead of cWorld's, pickups don't combine across chunk boundaries.
				// That is a small price to pay for not having to traverse the entire world for each entity.
				// The speedup in the tick thread is quite considerable.
				cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
				a_Chunk.ForEachEntity(PickupCombiningCallback);
				if (PickupCombiningCallback.FoundMatchingPickup())
				{
					m_World->BroadcastEntityMetadata(*this);
				}
			}
		}
Esempio n. 10
0
void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
	int DispX = m_PosX;
	int DispY = m_PosY;
	int DispZ = m_PosZ;
	NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
	AddDropSpenserDir(DispX, DispY, DispZ, Meta);

	cItems Pickups;
	Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));
	m_World->SpawnItemPickups(Pickups, DispX, DispY, DispZ);
}
Esempio n. 11
0
void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
	if (m_AttachedTo != NULL)
	{
		if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5)
		{
			SetPosition(m_AttachedTo->GetPosition());
		}
	}
	else
	{
		if (a_Chunk.IsValid())
		{
			HandlePhysics(a_Dt, a_Chunk);
		}
	}
	if (a_Chunk.IsValid())
	{
		TickBurning(a_Chunk);
	}
}
Esempio n. 12
0
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
	int PosY = (int)floor(GetPosY());
	if ((PosY <= 0) || (PosY >= cChunkDef::Height))
	{
		// Outside the world, just process normal falling physics
		super::HandlePhysics(a_Dt, a_Chunk);
		BroadcastMovementUpdate();
		return;
	}
	
	int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
	cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
	if (Chunk == NULL)
	{
		// Inside an unloaded chunk, bail out all processing
		return;
	}
	BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ);
	BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ);

	if (IsBlockRail(BelowType))
	{
		HandleRailPhysics(a_Dt, *Chunk);
	}
	else
	{
		if (IsBlockRail(InsideType))
		{
			SetPosY(PosY + 1);
			HandleRailPhysics(a_Dt, *Chunk);
		}
		else
		{
			super::HandlePhysics(a_Dt, *Chunk);
			BroadcastMovementUpdate();
		}
	}
}
Esempio n. 13
0
void cPlayer::SetSwimState(cChunk & a_Chunk)
{
	int RelY = (int)floor(m_LastPosY + 0.1);
	if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
	{
		m_IsSwimming = false;
		m_IsSubmerged = false;
		return;
	}
	
	BLOCKTYPE BlockIn;
	int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
	
	// Check if the player is swimming:
	// Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
	VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn));
	m_IsSwimming = IsBlockWater(BlockIn);

	// Check if the player is submerged:
	VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
	m_IsSubmerged = IsBlockWater(BlockIn);
}
Esempio n. 14
0
void cSquid::Tick(float a_Dt, cChunk & a_Chunk)
{
	// We must first process current location, and only then tick, otherwise we risk processing a location in a chunk
	// that is not where the entity currently resides (FS #411)
	
	Vector3d Pos = GetPosition();

	// TODO: Not a real behavior, but cool :D
	int RelY = (int)floor(Pos.y);
	if ((RelY < 0) || (RelY >= cChunkDef::Height))
	{
		return;
	}
	int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width;
	int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
	if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire())
	{
		// Burn for 10 ticks, then decide again
		StartBurning(10);
	}

	super::Tick(a_Dt, a_Chunk);
}
Esempio n. 15
0
/// Moves items to the furnace at the specified coords. Returns true if contents have changed
bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_HopperMeta)
{
	cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
	if (a_HopperMeta == E_META_HOPPER_FACING_YM)
	{
		// Feed the input slot of the furnace
		return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsInput);
	}
	else
	{
		// Feed the fuel slot of the furnace
		return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsFuel);
	}
}
Esempio n. 16
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);
				}
			}
		}
	}
}
Esempio n. 17
0
void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
{
	// Pick one of the occupied slots:
	int OccupiedSlots[9];
	int SlotsCnt = 0;
	for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
	{
		if (!m_Contents.GetSlot(i).IsEmpty())
		{
			OccupiedSlots[SlotsCnt] = i;
			SlotsCnt++;
		}
	}  // for i - m_Contents[]
	
	if (SlotsCnt == 0)
	{
		// Nothing in the dropspenser, play the click sound
		m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f);
		return;
	}
	
	int RandomSlot = 	m_World->GetTickRandomNumber(SlotsCnt - 1);
	
	// DropSpense the item, using the specialized behavior in the subclasses:
	DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]);
	
	// Broadcast a smoke and click effects:
	NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
	int SmokeDir = 0;
	switch (Meta)
	{
		case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break;
		case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break;
		case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break;
		case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break;
	}
	m_World->BroadcastSoundParticleEffect(2000, m_PosX * 8, m_PosY * 8, m_PosZ * 8, SmokeDir);
	m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f);
	
	// Update the UI window, if open:
	cWindow * Window = GetWindow();
	if (Window != NULL)
	{
		Window->BroadcastWholeWindow();
	}
}	
Esempio n. 18
0
void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
{
	// Pick one of the occupied slots:
	int OccupiedSlots[9];
	int SlotsCnt = 0;
	for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
	{
		if (!m_Contents.GetSlot(i).IsEmpty())
		{
			OccupiedSlots[SlotsCnt] = i;
			SlotsCnt++;
		}
	}  // for i - m_Contents[]
	
	if (SlotsCnt == 0)
	{
		// Nothing in the dropspenser, play the click sound
		m_World->BroadcastSoundEffect("random.click", static_cast<double>(m_PosX), static_cast<double>(m_PosY), static_cast<double>(m_PosZ), 1.0f, 1.2f);
		return;
	}
	
	int RandomSlot = 	m_World->GetTickRandomNumber(SlotsCnt - 1);
	
	// DropSpense the item, using the specialized behavior in the subclasses:
	DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]);
	
	// Broadcast a smoke and click effects:
	NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
	int SmokeDir = 0;
	switch (Meta)
	{
		case E_META_DROPSPENSER_FACING_YP: SmokeDir = 4; break;  // YP & YM don't have associated smoke dirs, just do 4 (centre of block)
		case E_META_DROPSPENSER_FACING_YM: SmokeDir = 4; break;
		case E_META_DROPSPENSER_FACING_XM: SmokeDir = 3; break;
		case E_META_DROPSPENSER_FACING_XP: SmokeDir = 5; break;
		case E_META_DROPSPENSER_FACING_ZM: SmokeDir = 1; break;
		case E_META_DROPSPENSER_FACING_ZP: SmokeDir = 7; break;
	}
	m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir);
	m_World->BroadcastSoundEffect("random.click", static_cast<double>(m_PosX), static_cast<double>(m_PosY), static_cast<double>(m_PosZ), 1.0f, 1.0f);
}
Esempio n. 19
0
void cBlockHandler::Check(cChunkInterface & a_ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk)
{
	if (!CanBeAt(a_ChunkInterface, a_RelX, a_RelY, a_RelZ, a_Chunk))
	{
		if (DoesDropOnUnsuitable())
		{
			int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
			int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
			DropBlock(a_ChunkInterface, *a_Chunk.GetWorld(), a_PluginInterface, nullptr, BlockX, a_RelY, BlockZ);
		}

		a_Chunk.SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_AIR, 0);
	}
	else
	{
		// Wake up the simulators for this block:
		int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
		int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
		a_Chunk.GetWorld()->GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, &a_Chunk);
	}
}
Esempio n. 20
0
bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk)
{
	cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Location.x), FloorC(a_Location.z));
	if ((Chunk == nullptr) || (!Chunk->IsValid()))
	{
		return false;
	}

	int RelX = FloorC(a_Location.x) - Chunk->GetPosX() * cChunkDef::Width;
	int RelY = FloorC(a_Location.y);
	int RelZ = FloorC(a_Location.z) - Chunk->GetPosZ() * cChunkDef::Width;

	if (
		(Chunk->GetSkyLight(RelX, RelY, RelZ) == 15) &&             // In the daylight
		(Chunk->GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) &&  // Not on soulsand
		(GetWorld()->GetTimeOfDay() < (12000 + 1000)) &&             // It is nighttime
		GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT)         // Not raining
	)
	{
		return true;
	}
	return false;
}
Esempio n. 21
0
bool cFurnaceEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	UNUSED(a_Dt);

	if (m_FuelBurnTime <= 0)
	{
		// If a furnace is out of fuel, the progress bar reverses at twice the speed of cooking.
		m_TimeCooked = std::max((m_TimeCooked - 2), 0);

		// Reset progressbars, block type, and bail out
		m_BlockType = E_BLOCK_FURNACE;
		a_Chunk.FastSetBlock(GetRelX(), m_PosY, GetRelZ(), E_BLOCK_FURNACE, m_BlockMeta);
		UpdateProgressBars();
		return false;
	}

	if (m_IsCooking)
	{
		m_TimeCooked++;
		if (m_TimeCooked >= m_NeedCookTime)
		{
			// Finished smelting one item
			FinishOne();
		}
	}

	m_TimeBurned++;
	if (m_TimeBurned >= m_FuelBurnTime)
	{
		// The current fuel has been exhausted, use another one, if possible
		BurnNewFuel();
	}

	UpdateProgressBars();

	return true;
}
Esempio n. 22
0
void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);
	BroadcastMovementUpdate(); //Notify clients of position

	m_Timer += a_Dt;
	
	if (!m_bCollected)
	{
		int BlockY = (int) floor(GetPosY());
		if ((BlockY >= 0) && (BlockY < cChunkDef::Height))  // Don't do anything except for falling when outside the world
		{
			int BlockX = (int) floor(GetPosX());
			int BlockZ = (int) floor(GetPosZ());
			// Position might have changed due to physics. So we have to make sure we have the correct chunk.
			cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
			if (CurrentChunk != NULL)  // Make sure the chunk is loaded
			{
				int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
				int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
				
				// If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
				BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
				BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);

				if (
					IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) ||
					IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE)
				)
				{
					m_bCollected = true;
					m_Timer = 0;  // We have to reset the timer.
					m_Timer += a_Dt;  // In case we have to destroy the pickup in the same tick.
					if (m_Timer > 500.f)  
					{
						Destroy(true);
						return;
					}
				}

				if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me
				{
					cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
					m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
					if (PickupCombiningCallback.FoundMatchingPickup())
					{
						m_World->BroadcastEntityMetadata(*this);
					}
				}
			}
		}
	}
	else
	{
		if (m_Timer > 500.f)  // 0.5 second
		{
			Destroy(true);
			return;
		}
	}

	if (m_Timer > 1000 * 60 * 5)  // 5 minutes
	{
		Destroy(true);
		return;
	}

	if (GetPosY() < -8) // Out of this world and no more visible!
	{
		Destroy(true);
		return;
	}
}
Esempio n. 23
0
void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
	int DispX = m_RelX;
	int DispY = m_PosY;
	int DispZ = m_RelZ;
	NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
	AddDropSpenserDir(DispX, DispY, DispZ, Meta);
	cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ);
	if (DispChunk == NULL)
	{
		// Would dispense into / interact with a non-loaded chunk, ignore the tick
		return;
	}

	BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ);
	int BlockX = (DispX + DispChunk->GetPosX() * cChunkDef::Width);
	int BlockZ = (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);

	// Dispense the item:
	switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
	{
		case E_ITEM_BUCKET:
		{
			LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
			switch (DispBlock)
			{
				case E_BLOCK_STATIONARY_WATER:
				case E_BLOCK_WATER:
				{
					if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
					{
						DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
					}
					break;
				}
				case E_BLOCK_STATIONARY_LAVA:
				case E_BLOCK_LAVA:
				{
					if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
					{
						DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
					}
					break;
				}
				default:
				{
					DropFromSlot(a_Chunk, a_SlotNum);
					break;
				}
			}
			break;
		}  // E_ITEM_BUCKET

		case E_ITEM_WATER_BUCKET:
		{
			LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
			if (EmptyLiquidBucket(DispBlock, a_SlotNum))
			{
				DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0);
			}
			else
			{
				DropFromSlot(a_Chunk, a_SlotNum);
			}
			break;
		}

		case E_ITEM_LAVA_BUCKET:
		{
			LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
			if (EmptyLiquidBucket(DispBlock, a_SlotNum))
			{
				DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0);
			}
			else
			{
				DropFromSlot(a_Chunk, a_SlotNum);
			}
			break;
		}

		case E_ITEM_SPAWN_EGG:
		{
			double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
			double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
			if (m_World->SpawnMob(MobX, DispY, MobZ, (cMonster::eType)m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_BLOCK_TNT:
		{
			// Spawn a primed TNT entity, if space allows:
			if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
			{
				double TNTX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
				double TNTZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
				m_World->SpawnPrimedTNT(TNTX, DispY + 0.5, TNTZ, 80, 0);  // 80 ticks fuse, no initial velocity
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_FLINT_AND_STEEL:
		{
			// Spawn fire if the block in front is air.
			if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
			{
				DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);

				bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);

				if (ItemBroke)
				{
					m_Contents.ChangeSlotCount(a_SlotNum, -1);
				}
			}
			break;
		}

		case E_ITEM_FIRE_CHARGE:
		{
			SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkFireCharge, GetShootVector(Meta) * 20);
			m_Contents.ChangeSlotCount(a_SlotNum, -1);
			break;
		}

		case E_ITEM_ARROW:
		{
			SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkArrow, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0));
			m_Contents.ChangeSlotCount(a_SlotNum, -1);
			break;
		}

		case E_ITEM_SNOWBALL:
		{
			SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkSnowball, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0));
			m_Contents.ChangeSlotCount(a_SlotNum, -1);
			break;
		}

		case E_ITEM_EGG:
		{
			SpawnProjectileFromDispenser(BlockX, DispY, BlockZ, cProjectileEntity::pkEgg, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0));
			m_Contents.ChangeSlotCount(a_SlotNum, -1);
			break;
		}

		case E_ITEM_FIREWORK_ROCKET:
		{
			// TODO: Add the fireworks entity
			break;
		}

		default:
		{
			DropFromSlot(a_Chunk, a_SlotNum);
			break;
		}
	}  // switch (ItemType)
}
Esempio n. 24
0
void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);
	BroadcastMovementUpdate(); //Notify clients of position

	m_Timer += a_Dt;
	
	if (!m_bCollected)
	{
		int BlockY = (int) floor(GetPosY());
		if (BlockY < cChunkDef::Height)  // Don't do anything except for falling when above the world
		{
			int BlockX = (int) floor(GetPosX());
			int BlockZ = (int) floor(GetPosZ());
			//Position might have changed due to physics. So we have to make sure we have the correct chunk.
			cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
			if (CurrentChunk != NULL)  // Make sure the chunk is loaded
			{
				int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
				int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
				
				BLOCKTYPE BlockBelow = CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ);
				BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);

				if (
					IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) ||
					IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE)
				)
				{
					m_bCollected = true;
					m_Timer = 0;  // We have to reset the timer.
					m_Timer += a_Dt;  // In case we have to destroy the pickup in the same tick.
					if (m_Timer > 500.f)  
					{
						Destroy(true);
						return;
					}
				}
			}
		}
	}
	else
	{
		if (m_Timer > 500.f)  // 0.5 second
		{
			Destroy(true);
			return;
		}
	}

	if (m_Timer > 1000 * 60 * 5)  // 5 minutes
	{
		Destroy(true);
		return;
	}

	if (GetPosY() < -8) // Out of this world and no more visible!
	{
		Destroy(true);
		return;
	}
}
Esempio n. 25
0
void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
    if (m_IsInGround)
    {
        // Already-grounded projectiles don't move at all
        return;
    }

    auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt);

    const Vector3d DeltaSpeed = GetSpeed() * DtSec.count();
    const Vector3d Pos = GetPosition();
    const Vector3d NextPos = Pos + DeltaSpeed;

    // Test for entity collisions:
    cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos);
    a_Chunk.ForEachEntity(EntityCollisionCallback);
    if (EntityCollisionCallback.HasHit())
    {
        // An entity was hit:
        Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff();

        // DEBUG:
        LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)",
             m_UniqueID,
             EntityCollisionCallback.GetHitEntity()->GetUniqueID(),
             EntityCollisionCallback.GetHitEntity()->GetClass(),
             HitPos.x, HitPos.y, HitPos.z,
             EntityCollisionCallback.GetMinCoeff()
            );

        OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos);
        if (!IsTicking())
        {
            return;  // We were destroyed by an override of OnHitEntity
        }
    }
    // TODO: Test the entities in the neighboring chunks, too

    // Trace the tick's worth of movement as a line:
    cProjectileTracerCallback TracerCallback(this);
    if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
    {
        // Something has been hit, abort all other processing
        return;
    }
    // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff

    // Update the position:
    SetPosition(NextPos);

    // Add slowdown and gravity effect to the speed:
    Vector3d NewSpeed(GetSpeed());
    NewSpeed.y += m_Gravity * DtSec.count();
    NewSpeed -= NewSpeed * (m_AirDrag * 20.0f) * DtSec.count();
    SetSpeed(NewSpeed);
    SetYawFromSpeed();
    SetPitchFromSpeed();

    /*
    LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}",
    	m_UniqueID,
    	GetPosX(), GetPosY(), GetPosZ(),
    	GetSpeedX(), GetSpeedY(), GetSpeedZ(),
    	GetYaw(), GetPitch()
    );
    */
}
Esempio n. 26
0
void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	// GetWorld()->BroadcastTeleportEntity(*this);  // Test position
	
	int BlockX = POSX_TOINT;
	int BlockY = (int)(GetPosY() - 0.5);
	int BlockZ = POSZ_TOINT;
	
	if (BlockY < 0)
	{
		// Fallen out of this world, just continue falling until out of sight, then destroy:
		if (BlockY < VOID_BOUNDARY)
		{
			Destroy(true);
		}
		return;
	}
	
	if (BlockY >= cChunkDef::Height)
	{
		// Above the world, just wait for it to fall back down
		return;
	}
	
	BLOCKTYPE BlockBelow = a_Chunk.GetBlock(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
	NIBBLETYPE BelowMeta = a_Chunk.GetMeta(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width);
	if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta))
	{
		// Fallen onto a block that breaks this into pickups (e. g. half-slab)
		// Must finish the fall with coords one below the block:
		cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta);
		Destroy(true);
		return;
	}
	else if (!cSandSimulator::CanContinueFallThrough(BlockBelow))
	{
		// Fallen onto a solid block
		/*
		LOGD(
			"Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.",
			BlockX, BlockY, BlockZ,
			BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width,
			ItemTypeToString(BlockBelow).c_str()
		);
		*/

		if (BlockY < cChunkDef::Height - 1)
		{
			cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta);
		}
		Destroy(true);
		return;
	}
	
	float MilliDt = a_Dt.count() * 0.001f;
	AddSpeedY(MilliDt * -9.8f);
	AddPosition(GetSpeed() * MilliDt);

	// If not static (one billionth precision) broadcast movement
	if ((fabs(GetSpeedX()) > std::numeric_limits<double>::epsilon()) || (fabs(GetSpeedZ()) > std::numeric_limits<double>::epsilon()))
	{
		BroadcastMovementUpdate();
	}
}
Esempio n. 27
0
bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
{
	cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z));
	BLOCKTYPE BlockType;
	NIBBLETYPE BlockMeta;

	if ((Chunk == nullptr) || !Chunk->IsValid())
	{
		return false;
	}

	int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
	int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;

	// If destination in the air, first try to go 1 block north, or east, or west.
	// This fixes the player leaning issue.
	// If that failed, we instead go down to the lowest air block.
	Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
	if (!cBlockInfo::IsSolid(BlockType))
	{
		bool InTheAir = true;
		int x, z;
		for (z = -1; z <= 1; ++z)
		{
			for (x = -1; x <= 1; ++x)
			{
				if ((x==0) && (z==0))
				{
					continue;
				}
				Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
				if ((Chunk == nullptr) || !Chunk->IsValid())
				{
					return false;
				}
				RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width;
				RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
				Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
				if (cBlockInfo::IsSolid(BlockType))
				{
					m_FinalDestination.x += x;
					m_FinalDestination.z += z;
					InTheAir = false;
					goto breakBothLoops;
				}
			}
		}
		breakBothLoops:

		// Go down to the lowest air block.
		if (InTheAir)
		{
			while (m_FinalDestination.y > 0)
			{
				Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
				if (cBlockInfo::IsSolid(BlockType))
				{
					break;
				}
				m_FinalDestination.y -= 1;
			}
		}
	}

	// If destination in water, go up to the highest water block.
	// If destination in solid, go up to first air block.
	bool InWater = false;
	while (m_FinalDestination.y < cChunkDef::Height)
	{
		Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta);
		if (BlockType == E_BLOCK_STATIONARY_WATER)
		{
			InWater = true;
		}
		else if (cBlockInfo::IsSolid(BlockType))
		{
			InWater = false;
		}
		else
		{
			break;
		}
		m_FinalDestination.y += 1;
	}
	if (InWater)
	{
		m_FinalDestination.y -= 1;
	}


	return true;
}
Esempio n. 28
0
void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
{
	if (m_ClientHandle != NULL)
	{
		if (m_ClientHandle->IsDestroyed())
		{
			// This should not happen, because destroying a client will remove it from the world, but just in case
			m_ClientHandle = NULL;
			return;
		}
		
		if (!m_ClientHandle->IsPlaying())
		{
			// We're not yet in the game, ignore everything
			return;
		}
	}

	m_Stats.AddValue(statMinutesPlayed, 1);
	
	if (!a_Chunk.IsValid())
	{
		// This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
		return;
	}
	
	super::Tick(a_Dt, a_Chunk);
	
	// Handle charging the bow:
	if (m_IsChargingBow)
	{
		m_BowCharge += 1;
	}
	
	// Handle updating experience
	if (m_bDirtyExperience)
	{
		SendExperience();
	}

	if (GetPosition() != m_LastPos) // Change in position from last tick?
	{
		// Apply food exhaustion from movement:
		ApplyFoodExhaustionFromMovement();
		
		cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this);
		m_ClientHandle->StreamChunks();
	}

	BroadcastMovementUpdate(m_ClientHandle);

	if (m_Health > 0)  // make sure player is alive
	{
		m_World->CollectPickupsByPlayer(this);

		if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge()))
		{
			FinishEating();
		}
		
		HandleFood();
	}
	
	if (m_IsFishing)
	{
		HandleFloater();
	}

	// Update items (e.g. Maps)
	m_Inventory.UpdateItems();

	// Send Player List (Once per m_LastPlayerListTime/1000 ms)
	cTimer t1;
	if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
	{
		m_World->SendPlayerList(this);
		m_LastPlayerListTime = t1.GetNowTime();
	}

	if (IsFlying())
	{
		m_LastGroundHeight = (float)GetPosY();
	}
}
Esempio n. 29
0
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
    if (IsDestroyed())  // Mainly to stop detector rails triggering again after minecart is dead
    {
        return;
    }

    int PosY = POSY_TOINT;
    if ((PosY <= 0) || (PosY >= cChunkDef::Height))
    {
        // Outside the world, just process normal falling physics
        super::HandlePhysics(a_Dt, a_Chunk);
        BroadcastMovementUpdate();
        return;
    }

    int RelPosX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
    int RelPosZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
    cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
    if (Chunk == NULL)
    {
        // Inside an unloaded chunk, bail out all processing
        return;
    }

    BLOCKTYPE InsideType;
    NIBBLETYPE InsideMeta;
    Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta);

    if (!IsBlockRail(InsideType))
    {
        Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta);  // When an descending minecart hits a flat rail, it goes through the ground; check for this
        if (IsBlockRail(InsideType)) AddPosY(1);  // Push cart upwards
    }

    bool WasDetectorRail = false;
    if (IsBlockRail(InsideType))
    {
        if (InsideType == E_BLOCK_RAIL)
        {
            SnapToRail(InsideMeta);
        }
        else
        {
            SnapToRail(InsideMeta & 0x07);
        }

        switch (InsideType)
        {
        case E_BLOCK_RAIL:
            HandleRailPhysics(InsideMeta, a_Dt);
            break;
        case E_BLOCK_ACTIVATOR_RAIL:
            break;
        case E_BLOCK_POWERED_RAIL:
            HandlePoweredRailPhysics(InsideMeta);
            break;
        case E_BLOCK_DETECTOR_RAIL:
        {
            HandleDetectorRailPhysics(InsideMeta, a_Dt);
            WasDetectorRail = true;
            break;
        }
        default:
            VERIFY(!"Unhandled rail type despite checking if block was rail!");
            break;
        }

        AddPosition(GetSpeed() * (a_Dt / 1000));  // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
    }
    else
    {
        // Not on rail, default physics
        SetPosY(floor(GetPosY()) + 0.35);  // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail
        super::HandlePhysics(a_Dt, *Chunk);
    }

    if (m_bIsOnDetectorRail && !Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT).Equals(m_DetectorRailPosition))
    {
        m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
        m_bIsOnDetectorRail = false;
    }
    else if (WasDetectorRail)
    {
        m_bIsOnDetectorRail = true;
        m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
    }

    // Broadcast positioning changes to client
    BroadcastMovementUpdate();
}
Esempio n. 30
0
bool cPathFinder::EnsureProperPoint(Vector3d & a_Vector, cChunk & a_Chunk)
{
	cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x), FloorC(a_Vector.z));
	BLOCKTYPE BlockType;
	NIBBLETYPE BlockMeta;

	if ((Chunk == nullptr) || !Chunk->IsValid())
	{
		return false;
	}

	int RelX = FloorC(a_Vector.x) - Chunk->GetPosX() * cChunkDef::Width;
	int RelZ = FloorC(a_Vector.z) - Chunk->GetPosZ() * cChunkDef::Width;

	// If destination in the air, first try to go 1 block north, or east, or west.
	// This fixes the player leaning issue.
	// If that failed, we instead go down to the lowest air block.
	Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
	if (!(IsWaterOrSolid(BlockType)))
	{
		bool InTheAir = true;
		int x, z;
		for (z = -1; z <= 1; ++z)
		{
			for (x = -1; x <= 1; ++x)
			{
				if ((x == 0) && (z == 0))
				{
					continue;
				}
				Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x+x), FloorC(a_Vector.z+z));
				if ((Chunk == nullptr) || !Chunk->IsValid())
				{
					return false;
				}
				RelX = FloorC(a_Vector.x+x) - Chunk->GetPosX() * cChunkDef::Width;
				RelZ = FloorC(a_Vector.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
				Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
				if (IsWaterOrSolid((BlockType)))
				{
					a_Vector.x += x;
					a_Vector.z += z;
					InTheAir = false;
					goto breakBothLoops;
				}
			}
		}
		breakBothLoops:

		// Go down to the lowest air block.
		if (InTheAir)
		{
			while (a_Vector.y > 0)
			{
				Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
				if (IsWaterOrSolid(BlockType))
				{
					break;
				}
				a_Vector.y -= 1;
			}
		}
	}

	// If destination in water or solid, go up to the first air block.
	while (a_Vector.y < cChunkDef::Height)
	{
		Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y), RelZ, BlockType, BlockMeta);
		if (!IsWaterOrSolid(BlockType))
		{
			break;
		}
		a_Vector.y += 1;
	}

	return true;
}