void cBlockPistonHandler::ExtendPiston(int pistx, int pisty, int pistz) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); if (IsExtended(pistonMeta)) { // Already extended, bail out return; } int dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); if (dist < 0) { // FirstPassthroughBlock says piston can't push anything, bail out return; } m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, pistonBlock); m_World->BroadcastSoundEffect("tile.piston.out", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(pistx, pisty, pistz, pistonMeta, dist + 1); // "pist" now at the breakable / empty block BLOCKTYPE currBlock; NIBBLETYPE currMeta; m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currMeta); if (currBlock != E_BLOCK_AIR) { cBlockHandler * Handler = BlockHandler(currBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(m_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*m_World); Handler->DropBlock(ChunkInterface, *m_World, PluginInterface, NULL, pistx, pisty, pistz); } } // Push blocks, from the furthest to the nearest: int oldx = pistx, oldy = pisty, oldz = pistz; NIBBLETYPE currBlockMeta; for (int i = dist + 1; i > 1; i--) { AddPistonDir(pistx, pisty, pistz, pistonMeta, -1); m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currBlockMeta); m_World->QueueSetBlock( oldx, oldy, oldz, currBlock, currBlockMeta, PISTON_TICK_DELAY); oldx = pistx; oldy = pisty; oldz = pistz; } int extx = pistx; int exty = pisty; int extz = pistz; AddPistonDir(pistx, pisty, pistz, pistonMeta, -1); // "pist" now at piston body, "ext" at future extension m_World->SetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8); m_World->QueueSetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), PISTON_TICK_DELAY); }
void cVillager::HandleFarmerTryHarvestCrops() { // Harvest the crops if the villager isn't moving and if the crops are closer then 2 blocks. if (!m_PathfinderActivated && (GetPosition() - m_CropsPos).Length() < 2) { // Check if the blocks didn't change while the villager was walking to the coordinates. BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z); if (IsBlockFarmable(CropBlock) && m_World->GetBlockMeta(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z) == 0x7) { cBlockHandler * Handler = cBlockInfo::GetHandler(CropBlock); cChunkInterface ChunkInterface(m_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*m_World); Handler->DropBlock(ChunkInterface, *m_World, PluginInterface, this, m_CropsPos.x, m_CropsPos.y, m_CropsPos.z); m_World->SetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z, E_BLOCK_AIR, 0); m_ActionCountDown = 20; } } }
void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) { UNUSED(a_Item); BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); cBlockHandler * Handler = cBlockInfo::GetHandler(Block); if (a_Player->IsGameModeSurvival()) { if (!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ); } } a_Player->UseEquippedItem(); }
void cItemHandler::OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ) { UNUSED(a_Item); BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); cBlockHandler * Handler = cBlockInfo::GetHandler(Block); if (a_Player->IsGameModeSurvival()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, CanHarvestBlock(Block)); } if (!cBlockInfo::IsOneHitDig(Block)) { a_Player->UseEquippedItem(GetDurabilityLossByAction(dlaBreakBlock)); } }
void cBlockPistonHandler::PushBlocks( const Vector3iSet & a_BlocksToPush, cWorld * a_World, const Vector3i & a_PushDir ) { // Sort blocks to move the blocks first, which are farest away from the piston // This prevents the overwriting of existing blocks std::vector<Vector3i> sortedBlocks(a_BlocksToPush.begin(), a_BlocksToPush.end()); std::sort(sortedBlocks.begin(), sortedBlocks.end(), [a_PushDir](const Vector3i & a, const Vector3i & b) { return a.Dot(a_PushDir) > b.Dot(a_PushDir); }); // Move every block BLOCKTYPE moveBlock; NIBBLETYPE moveMeta; for (auto & moveBlockPos : sortedBlocks) { a_World->GetBlockTypeMeta(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta); a_World->SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, E_BLOCK_AIR, 0); moveBlockPos += a_PushDir; if (cBlockInfo::IsPistonBreakable(moveBlock)) { // Block is breakable, drop it cBlockHandler * Handler = BlockHandler(moveBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, nullptr, moveBlockPos.x, moveBlockPos.y, moveBlockPos.z ); } } else { // Not breakable, just move it a_World->SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta); } } }
bool cItemHandler::GetPlacementBlockTypeMeta( cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) { ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers if (m_ItemType >= 256) { LOGERROR("%s: Item %d is not eligible for direct block placement!", __FUNCTION__, m_ItemType); return false; } cBlockHandler * BlockH = BlockHandler((BLOCKTYPE)m_ItemType); cChunkInterface ChunkInterface(a_World->GetChunkMap()); return BlockH->GetPlacementBlockTypeMeta( ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta ); }
void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) { ASSERT(a_NewMeta <= 8); // Invalid meta values ASSERT(a_NewMeta > 0); // Source blocks aren't spread a_NearChunk = a_NearChunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); if ((a_NearChunk == NULL) || (!a_NearChunk->IsValid())) { // Chunk not available return; } const int BlockX = a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX; const int BlockZ = a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ; BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; a_NearChunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); if (IsAllowedBlock(BlockType)) { if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta)) { // Don't spread there, there's already a higher or same level there return; } } // Check water - lava interaction: if (m_FluidBlock == E_BLOCK_LAVA) { if (IsBlockWater(BlockType)) { // Lava flowing into water, change to stone / cobblestone based on direction: BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE; FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone", a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() ); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); a_NearChunk->BroadcastSoundEffect("random.fizz", BlockX * 8, a_RelY * 8, BlockZ * 8, 0.5f, 1.5f); return; } } else if (m_FluidBlock == E_BLOCK_WATER) { if (IsBlockLava(BlockType)) { // Water flowing into lava, change to cobblestone / obsidian based on dest block: BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE; FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s", a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() ); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); a_NearChunk->BroadcastSoundEffect("random.fizz", BlockX * 8, a_RelY * 8, BlockZ * 8, 0.5f, 1.5f); return; } } else { ASSERT(!"Unknown fluid!"); } if (!IsPassableForFluid(BlockType)) { // Can't spread there return; } // Wash away the block there, if possible: if (CanWashAway(BlockType)) { cBlockHandler * Handler = BlockHandler(BlockType); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockInServerPluginInterface PluginInterface(m_World); Handler->DropBlock( ChunkInterface, m_World, PluginInterface, NULL, BlockX, a_RelY, BlockZ ); } } // if (CanWashAway) // Spread: FLOG(" Spreading to {%d, %d, %d} with meta %d", BlockX, a_RelY, BlockZ, a_NewMeta); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); m_World.GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, a_NearChunk); HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); }
void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta); if (IsExtended(pistonMeta)) { // Already extended, bail out return; } int dist = FirstPassthroughBlock(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, a_World); if (dist < 0) { // FirstPassthroughBlock says piston can't push anything, bail out return; } a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock); a_World->BroadcastSoundEffect("tile.piston.out", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block BLOCKTYPE currBlock; NIBBLETYPE currMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currMeta); if (currBlock != E_BLOCK_AIR) { cBlockHandler * Handler = BlockHandler(currBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, NULL, a_BlockX, a_BlockY, a_BlockZ); } } // Push blocks, from the furthest to the nearest: int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ; NIBBLETYPE currBlockMeta; std::vector<Vector3i> ScheduledBlocks; ScheduledBlocks.reserve(PISTON_MAX_PUSH_DISTANCE); for (int i = dist + 1; i > 1; i--) { AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta); a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta, false); ScheduledBlocks.push_back(Vector3i(oldx, oldy, oldz)); oldx = a_BlockX; oldy = a_BlockY; oldz = a_BlockZ; } int extx = a_BlockX; int exty = a_BlockY; int extz = a_BlockZ; ScheduledBlocks.push_back(Vector3i(extx, exty, extz)); AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); // "a_Block" now at piston body, "ext" at future extension a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8); a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false); a_World->ScheduleTask(PISTON_TICK_DELAY, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks)); }
bool cItemHandler::OnPlayerPlace( cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ ) { if (a_BlockFace < 0) { // Clicked in air return false; } if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) { // The clicked block is outside the world, ignore this call altogether (#128) return false; } BLOCKTYPE ClickedBlock; NIBBLETYPE ClickedBlockMeta; a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); // Check if the block ignores build collision (water, grass etc.): if ( BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) ) { cChunkInterface ChunkInterface(a_World.GetChunkMap()); BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); } else { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) { // The block is being placed outside the world, ignore this packet altogether (#128) return false; } NIBBLETYPE PlaceMeta; BLOCKTYPE PlaceBlock; a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. // No need to do combinability (dblslab) checks, client will do that here. if ( !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) ) { // Tried to place a block *into* another? // Happens when you place a block aiming at side of block with a torch on it or stem beside it return false; } } BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) { // Handler refused the placement, send that information back to the client: a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player); a_Player.GetInventory().SendEquippedSlot(); return false; } if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta)) { // The placement failed, the block has already been re-sent, re-send inventory: a_Player.GetInventory().SendEquippedSlot(); return false; } AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); float Volume = 1.0f, Pitch = 0.8f; if (PlaceSound == "dig.metal") { Pitch = 1.2f; PlaceSound = "dig.stone"; } else if (PlaceSound == "random.anvil_land") { Volume = 0.65f; } a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); // Remove the "placed" item: if (a_Player.IsGameModeSurvival()) { a_Player.GetInventory().RemoveOneEquippedItem(); } return true; }
void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta); if (IsExtended(pistonMeta)) { // Already extended, bail out return; } int dist = FirstPassthroughBlock(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, a_World); if (dist < 0) { // FirstPassthroughBlock says piston can't push anything, bail out return; } a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock); a_World->BroadcastSoundEffect("tile.piston.out", static_cast<double>(a_BlockX), static_cast<double>(a_BlockY), static_cast<double>(a_BlockZ), 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block BLOCKTYPE currBlock; NIBBLETYPE currMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currMeta); if (currBlock != E_BLOCK_AIR) { cBlockHandler * Handler = BlockHandler(currBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, nullptr, a_BlockX, a_BlockY, a_BlockZ); } } // Push blocks, from the furthest to the nearest: int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ; NIBBLETYPE currBlockMeta; for (int i = dist + 1; i > 1; i--) { AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta); a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta); oldx = a_BlockX; oldy = a_BlockY; oldz = a_BlockZ; } int extx = a_BlockX; int exty = a_BlockY; int extz = a_BlockZ; AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); // "a_Block" now at piston body, "ext" at future extension a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8); a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0)); }