diff options
Diffstat (limited to '')
30 files changed, 2817 insertions, 2702 deletions
diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index 5edee888a..a7d29638a 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -150,7 +150,7 @@ void cMobSpawnerEntity::SpawnEntity(void) int RelZ = (int) (m_RelZ + (double)(Random.NextFloat() - Random.NextFloat()) * 4.0); cChunk * Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(RelX, RelZ); - if ((Chunk == NULL) || !Chunk->IsValid()) + if ((Chunk == nullptr) || !Chunk->IsValid()) { continue; } @@ -162,7 +162,7 @@ void cMobSpawnerEntity::SpawnEntity(void) double PosZ = Chunk->GetPosZ() * cChunkDef::Width + RelZ; cMonster * Monster = cMonster::NewMonsterFromType(m_MobType); - if (Monster == NULL) + if (Monster == nullptr) { continue; } diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp index bcab21e77..3c5e8d7b7 100644 --- a/src/BlockInfo.cpp +++ b/src/BlockInfo.cpp @@ -452,7 +452,6 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info) a_Info[E_BLOCK_AIR ].m_IsSolid = false; a_Info[E_BLOCK_BIG_FLOWER ].m_IsSolid = false; a_Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSolid = false; - a_Info[E_BLOCK_CAKE ].m_IsSolid = false; a_Info[E_BLOCK_CARROTS ].m_IsSolid = false; a_Info[E_BLOCK_COBWEB ].m_IsSolid = false; a_Info[E_BLOCK_CROPS ].m_IsSolid = false; diff --git a/src/Blocks/BlockCocoaPod.h b/src/Blocks/BlockCocoaPod.h new file mode 100644 index 000000000..1b659d48f --- /dev/null +++ b/src/Blocks/BlockCocoaPod.h @@ -0,0 +1,96 @@ +#pragma once + +#include "BlockHandler.h" +#include "../FastRandom.h" + + + + + +class cBlockCocoaPodHandler : + public cBlockHandler +{ +public: + cBlockCocoaPodHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + + virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); + AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockFace, true); + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + a_Chunk.UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); + + return ((BlockType == E_BLOCK_LOG) && ((BlockMeta & 0x3) == E_META_LOG_JUNGLE)); + } + + + virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override + { + cFastRandom Random; + + if (Random.NextInt(5) == 0) + { + NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); + NIBBLETYPE TypeMeta = Meta & 0x03; + int GrowState = Meta >> 2; + + if (GrowState < 2) + { + ++GrowState; + a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, (NIBBLETYPE) (GrowState << 2 | TypeMeta)); + } + } + } + + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + int GrowState = a_BlockMeta >> 2; + a_Pickups.Add(E_ITEM_DYE, ((GrowState >= 2) ? 3 : 1), E_META_DYE_BROWN); + } + + + static eBlockFace MetaToBlockFace(NIBBLETYPE a_Meta) + { + switch (a_Meta & 0x3) + { + case 0: return BLOCK_FACE_ZM; + case 1: return BLOCK_FACE_XP; + case 2: return BLOCK_FACE_ZP; + case 3: return BLOCK_FACE_XM; + default: + { + ASSERT(!"Bad meta"); + return BLOCK_FACE_NONE; + } + } + } + + + static NIBBLETYPE BlockFaceToMeta(eBlockFace a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_ZM: return 0; + case BLOCK_FACE_XM: return 3; + case BLOCK_FACE_XP: return 1; + case BLOCK_FACE_ZP: return 2; + default: + { + ASSERT(!"Unknown face"); + return 0; + } + } + } + +} ; + + + + diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 60f13a747..d532aa1dc 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -17,6 +17,7 @@ #include "BlockChest.h" #include "BlockCloth.h" #include "BlockCobWeb.h" +#include "BlockCocoaPod.h" #include "BlockCommandBlock.h" #include "BlockComparator.h" #include "BlockCrops.h" @@ -198,6 +199,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType); case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType); case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType); + case E_BLOCK_COCOA_POD: return new cBlockCocoaPodHandler (a_BlockType); case E_BLOCK_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType); case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType); diff --git a/src/Blocks/BlockPressurePlate.h b/src/Blocks/BlockPressurePlate.h index a5c34a776..bd2f283a2 100644 --- a/src/Blocks/BlockPressurePlate.h +++ b/src/Blocks/BlockPressurePlate.h @@ -28,8 +28,7 @@ public: return false; } - BLOCKTYPE BlockBelow = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); - return (cBlockInfo::IsSolid(BlockBelow)); + return (cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))); } } ; diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h index 21a34d8ce..02fe3aa95 100644 --- a/src/Blocks/BlockRail.h +++ b/src/Blocks/BlockRail.h @@ -98,7 +98,7 @@ public: { return false; } - if (!cBlockInfo::IsSolid(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))) + if (!cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))) { return false; } @@ -130,7 +130,7 @@ public: // Too close to the edge, cannot simulate return true; } - return cBlockInfo::IsSolid(BlockType); + return cBlockInfo::FullyOccupiesVoxel(BlockType); } } return true; diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h index 8e821b899..4134a01f8 100644 --- a/src/Blocks/BlockTallGrass.h +++ b/src/Blocks/BlockTallGrass.h @@ -38,7 +38,7 @@ public: virtual void DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_CanDrop) override { - if (a_CanDrop && (a_Digger != NULL) && (a_Digger->GetEquippedWeapon().m_ItemType == E_ITEM_SHEARS)) + if (a_CanDrop && (a_Digger != nullptr) && (a_Digger->GetEquippedWeapon().m_ItemType == E_ITEM_SHEARS)) { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); cItems Drops; diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h index e77bbd1b8..d63df94cf 100644 --- a/src/Blocks/BlockTorch.h +++ b/src/Blocks/BlockTorch.h @@ -2,6 +2,7 @@ #include "BlockHandler.h" #include "../Chunk.h" +#include "ChunkInterface.h" #include "MetaRotator.h" diff --git a/src/Blocks/CMakeLists.txt b/src/Blocks/CMakeLists.txt index eed949aab..ed3e321d4 100644 --- a/src/Blocks/CMakeLists.txt +++ b/src/Blocks/CMakeLists.txt @@ -24,6 +24,7 @@ SET (HDRS BlockChest.h BlockCloth.h BlockCobWeb.h + BlockCocoaPod.h BlockCommandBlock.h BlockComparator.h BlockCrops.h diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 017ceda26..39e97f5cf 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1371,9 +1371,9 @@ void cChunk::CreateBlockEntities(void) void cChunk::WakeUpSimulators(void) { - cSimulator<cChunk, cWorld> * WaterSimulator = m_World->GetWaterSimulator(); - cSimulator<cChunk, cWorld> * LavaSimulator = m_World->GetLavaSimulator(); - cSimulator<cChunk, cWorld> * RedstoneSimulator = m_World->GetRedstoneSimulator(); + cSimulator * WaterSimulator = m_World->GetWaterSimulator(); + cSimulator * LavaSimulator = m_World->GetLavaSimulator(); + cSimulator * RedstoneSimulator = m_World->GetRedstoneSimulator(); int BaseX = m_PosX * cChunkDef::Width; int BaseZ = m_PosZ * cChunkDef::Width; for (int x = 0; x < Width; x++) diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 222a756b1..6aff3a754 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -3075,7 +3075,7 @@ void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) ) { // The cChunk destructor calls our GetChunk() while removing its entities - // so we still need to be able to return the chunk. Therefore we first delete, then NULLify + // so we still need to be able to return the chunk. Therefore we first delete, then nullptrify // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355 delete m_Chunks[i]; m_Chunks[i] = nullptr; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 8d2eb1e5f..15920d6cf 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1604,6 +1604,12 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) // Update the view distance. m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance()); + // Send current weather of target world to player + if (a_World->GetDimension() == dimOverworld) + { + m_ClientHandle->SendWeather(a_World->GetWeather()); + } + return true; } diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h index ccf4714f7..da978040d 100644 --- a/src/Items/ItemDye.h +++ b/src/Items/ItemDye.h @@ -4,6 +4,7 @@ #include "ItemHandler.h" #include "../World.h" #include "../Entities/Player.h" +#include "../Blocks/BlockCocoaPod.h" @@ -16,10 +17,9 @@ public: cItemDyeHandler(int a_ItemType) : cItemHandler(a_ItemType) { - } - virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override { // Handle growing the plants: if (a_Item.m_ItemDamage == E_META_DYE_WHITE) @@ -34,8 +34,50 @@ public: } } } + else if ((a_Item.m_ItemDamage == E_META_DYE_BROWN) && (a_BlockFace >= BLOCK_FACE_ZM) && (a_BlockFace <= BLOCK_FACE_XP)) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + + // Check if the block that the player clicked is a jungle log. + if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x3) != E_META_LOG_JUNGLE)) + { + return false; + } + + // Get the location from the new cocoa pod. + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); + BlockMeta = cBlockCocoaPodHandler::BlockFaceToMeta(a_BlockFace); + + if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_AIR) + { + return false; + } + + // Check plugins + if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta)) + { + a_World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + a_Player->GetInventory().SendEquippedSlot(); + return false; + } + + // Set block and broadcast place sound + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta); + a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f); + + // Remove one cocoa pod from the inventory + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta); + return true; + } return false; } + } ; diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index 50fcf971a..0d716cca3 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -38,6 +38,7 @@ enum eMonsterType mtWolf = E_META_SPAWN_EGG_WOLF, mtZombie = E_META_SPAWN_EGG_ZOMBIE, mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + mtMax = 120, // This is just a hotfix for http://forum.mc-server.org/showthread.php?tid=1616. Tolua is too bad to find the highest value, so this is needed. } ; // tolua_end diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h index f76dbe342..9ccc3ef4f 100644 --- a/src/Simulator/FireSimulator.h +++ b/src/Simulator/FireSimulator.h @@ -16,7 +16,7 @@ it progresses to the next step (blockmeta++). This value is updated if a neighbo The simulator reads its parameters from the ini file given to the constructor. */ class cFireSimulator : - public cSimulator<cChunk, cWorld> + public cSimulator { public: cFireSimulator(cWorld & a_World, cIniFile & a_IniFile); diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index e95ef216d..37d58307b 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -83,7 +83,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re return; } } - + // New meta for the spreading to neighbors: // If this is a source block or was falling, the new meta is just the falloff // Otherwise it is the current meta plus falloff (may be larger than max height, will be checked later) @@ -103,6 +103,11 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re SpreadFurther = false; } } + // Spread to the neighbors: + if (SpreadFurther && (NewMeta < 8)) + { + SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); + } // If source creation is on, check for it here: else if ( (m_NumNeighborsForSource > 0) && // Source creation is on @@ -116,13 +121,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re return; } } - - if (SpreadFurther && (NewMeta < 8)) - { - // Spread to the neighbors: - SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); - } - + // Mark as processed: a_Chunk->FastSetBlock(a_RelX, a_RelY, a_RelZ, m_StationaryFluidBlock, MyMeta); } diff --git a/src/Simulator/FluidSimulator.h b/src/Simulator/FluidSimulator.h index d65a8e78b..6d2c6a679 100644 --- a/src/Simulator/FluidSimulator.h +++ b/src/Simulator/FluidSimulator.h @@ -37,9 +37,9 @@ public: class cFluidSimulator : - public cSimulator<cChunk, cWorld> + public cSimulator { - typedef cSimulator<cChunk, cWorld> super; + typedef cSimulator super; public: cFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid); diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index df05a9fee..8a56287a8 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -5,8 +5,6 @@ typedef cItemCallback<cChestEntity> cChestCallback; -#include "IncrementalRedstoneSimulator.inc" - #include "Chunk.h" #include "World.h" #include "Blocks/GetHandlerCompileTimeTemplate.h" @@ -17,8 +15,2195 @@ typedef cItemCallback<cChestEntity> cChestCallback; #include "Blocks/BlockDoor.h" #include "Blocks/BlockPiston.h" -cRedstoneSimulator<cChunk, cWorld> * MakeIncrementalRedstoneSimulator(cWorld & a_World) + +#include "IncrementalRedstoneSimulator.h" +#include "BoundingBox.h" +#include "Blocks/ChunkInterface.h" +#include "RedstoneSimulator.h" + + + + + +void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk) +{ + if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) + { + return; + } + else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height)) + { + return; + } + + // We may be called with coordinates in a chunk that is not the first chunk parameter + // In that case, the actual chunk (which the coordinates are in), will be passed as the second parameter + // Use that Chunk pointer to get a relative position + + int RelX = 0; + int RelZ = 0; + BLOCKTYPE Block; + NIBBLETYPE Meta; + + if (a_OtherChunk != nullptr) + { + RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width; + RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width; + a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + + // If a_OtherChunk is passed (not nullptr), it is the chunk that had a block change, and a_Chunk will be the neighbouring chunk of that block + // Because said neighbouring chunk does not know of this change but still needs to update its redstone, we set it to dirty + a_Chunk->SetIsRedstoneDirty(true); + } + else + { + RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + } + + // Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid + // Checking only when a block is changed, as opposed to every tick, also improves performance + + if ( + !IsPotentialSource(Block) || + ( + // Changeable sources + ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || + ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || + ((Block == E_BLOCK_DETECTOR_RAIL) && ((Meta & 0x08) == 0)) || + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || + ((Block == E_BLOCK_TRIPWIRE_HOOK) && ((Meta & 0x08) == 0)) + ) + ) + { + SetSourceUnpowered(RelX, a_BlockY, RelZ, a_OtherChunk != nullptr ? a_OtherChunk : a_Chunk); + } + + if (!IsViableMiddleBlock(Block)) + { + SetInvalidMiddleBlock(RelX, a_BlockY, RelZ, a_OtherChunk != nullptr ? a_OtherChunk : a_Chunk); + } + + auto & SimulatedPlayerToggleableBlocks = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_SimulatedPlayerToggleableBlocks; + SimulatedPlayerToggleableBlocks.erase(std::remove_if(SimulatedPlayerToggleableBlocks.begin(), SimulatedPlayerToggleableBlocks.end(), [RelX, a_BlockY, RelZ, Block, this](const sSimulatedPlayerToggleableList & itr) + { + return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && !IsAllowedBlock(Block); + } + ), SimulatedPlayerToggleableBlocks.end()); + + + auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; + RepeatersDelayList.erase(std::remove_if(RepeatersDelayList.begin(), RepeatersDelayList.end(), [RelX, a_BlockY, RelZ, Block](const sRepeatersDelayList & itr) + { + return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && (Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF); + } + ), RepeatersDelayList.end()); + + if (a_OtherChunk != nullptr) + { + // DO NOT touch our chunk's data structure if we are being called with coordinates from another chunk - this one caused me massive grief :P + return; + } + + cCoordWithBlockAndBoolVector & RedstoneSimulatorChunkData = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_ChunkData; + for (auto & itr : RedstoneSimulatorChunkData) + { + if ((itr.x == RelX) && (itr.y == a_BlockY) && (itr.z == RelZ)) // We are at an entry matching the current (changed) block + { + if (!IsAllowedBlock(Block)) + { + itr.DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list + } + else + { + itr.DataTwo = false; + itr.Data = Block; // Update block information + } + return; + } + } + + if (!IsAllowedBlock(Block)) + { + return; + } + + cCoordWithBlockAndBoolVector & QueuedData = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData; + for (const auto & itr : QueuedData) + { + if ((itr.x == RelX) && (itr.y == a_BlockY) && (itr.z == RelZ)) + { + // Can't have duplicates in here either, in case something adds the block again before the structure can written to the main chunk data + return; + } + } + QueuedData.emplace_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); +} + + + + + +void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); + if (m_RedstoneSimulatorChunkData == nullptr) + { + m_RedstoneSimulatorChunkData = new cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData(); + a_Chunk->SetRedstoneSimulatorData(m_RedstoneSimulatorChunkData); + } + if (m_RedstoneSimulatorChunkData->m_ChunkData.empty() && ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData.empty()) + { + return; + } + + m_RedstoneSimulatorChunkData->m_ChunkData.insert( + m_RedstoneSimulatorChunkData->m_ChunkData.end(), + m_RedstoneSimulatorChunkData->m_QueuedChunkData.begin(), + m_RedstoneSimulatorChunkData->m_QueuedChunkData.end() + ); + + m_RedstoneSimulatorChunkData->m_QueuedChunkData.clear(); + + m_PoweredBlocks = &m_RedstoneSimulatorChunkData->m_PoweredBlocks; + m_RepeatersDelayList = &m_RedstoneSimulatorChunkData->m_RepeatersDelayList; + m_SimulatedPlayerToggleableBlocks = &m_RedstoneSimulatorChunkData->m_SimulatedPlayerToggleableBlocks; + m_LinkedPoweredBlocks = &m_RedstoneSimulatorChunkData->m_LinkedBlocks; + m_Chunk = a_Chunk; + bool ShouldUpdateSimulateOnceBlocks = false; + + if (a_Chunk->IsRedstoneDirty()) + { + // Simulate the majority of devices only if something (blockwise or power-wise) has changed + // Make sure to allow the chunk to resimulate after the initial run if there was a power change (ShouldUpdateSimulateOnceBlocks helps to do this) + a_Chunk->SetIsRedstoneDirty(false); + ShouldUpdateSimulateOnceBlocks = true; + } + + HandleRedstoneRepeaterDelays(); + + for (auto dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.begin(); dataitr != m_RedstoneSimulatorChunkData->m_ChunkData.end();) + { + if (dataitr->DataTwo) + { + dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.erase(dataitr); + continue; + } + + switch (dataitr->Data) + { + case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRIPWIRE: HandleTripwire(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRIPWIRE_HOOK: HandleTripwireHook(dataitr->x, dataitr->y, dataitr->z); break; + + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + { + HandlePressurePlate(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + break; + } + default: break; + } + + if (ShouldUpdateSimulateOnceBlocks) + { + switch (dataitr->Data) + { + case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_IRON_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRAPPED_CHEST: HandleTrappedChest(dataitr->x, dataitr->y, dataitr->z); break; + + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + HandleRail(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + break; + } + case E_BLOCK_ACACIA_DOOR: + case E_BLOCK_BIRCH_DOOR: + case E_BLOCK_DARK_OAK_DOOR: + case E_BLOCK_JUNGLE_DOOR: + case E_BLOCK_SPRUCE_DOOR: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + { + HandleDoor(dataitr->x, dataitr->y, dataitr->z); + break; + } + case E_BLOCK_ACACIA_FENCE_GATE: + case E_BLOCK_BIRCH_FENCE_GATE: + case E_BLOCK_DARK_OAK_FENCE_GATE: + case E_BLOCK_FENCE_GATE: + case E_BLOCK_JUNGLE_FENCE_GATE: + case E_BLOCK_SPRUCE_FENCE_GATE: + { + HandleFenceGate(dataitr->x, dataitr->y, dataitr->z); + break; + } + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + { + HandleRedstoneLamp(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + { + HandleDropSpenser(dataitr->x, dataitr->y, dataitr->z); + break; + } + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + { + HandlePiston(dataitr->x, dataitr->y, dataitr->z); + break; + } + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + { + HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + break; + } + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + { + HandleRedstoneTorch(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + break; + } + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_WOODEN_BUTTON: + { + HandleRedstoneButton(dataitr->x, dataitr->y, dataitr->z); + break; + } + default: break; + } + } + ++dataitr; + } +} + + + + + +void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if (AreCoordsOnChunkBoundary(a_BlockX, a_BlockY, a_BlockZ)) + { + // On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk) + AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); + + // Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position + // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordinates are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk); + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk); + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk); + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 2), a_Chunk); + + return; + } + + // Not on boundary, just alert this chunk for speed + AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState) +{ + static const struct // Define which directions the torch can power + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0 }, + { -1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0, -1 }, + { 0, 1, 0 }, + }; + + if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON) + { + // Check if the block the torch is on is powered + int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ; + AddFaceDirection(X, Y, Z, GetHandlerCompileTime<E_BLOCK_TORCH>::type::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on + + cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(X, Z); + if ((Neighbour == nullptr) || !Neighbour->IsValid()) + { + return; + } + + if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour)) + { + // There was a match, torch goes off + m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); + return; + } + + // Torch still on, make all 4(X, Z) + 1(Y) sides powered + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + BLOCKTYPE Type = 0; + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type)) + { + continue; + } + if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last) + { + if ( + IsMechanism(Type) && // Is it a mechanism? Not block/other torch etc. + (!Vector3i(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on + ) + { + SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + } + } + else + { + // Top side, power whatever is there, including blocks + SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + // Power all blocks surrounding block above torch + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YP); + } + } + + if (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) != 0x5) // Is torch standing on ground? If NOT (i.e. on wall), power block beneath + { + BLOCKTYPE Type = m_Chunk->GetBlock(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ); + + if (IsMechanism(Type)) // Still can't make a normal block powered though! + { + SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + } + } + } + else + { + // Check if the block the torch is on is powered + int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ; + AddFaceDirection(X, Y, Z, GetHandlerCompileTime<E_BLOCK_TORCH>::type::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on + + cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(X, Z); + if ((Neighbour == nullptr) || !Neighbour->IsValid()) + { + return; + } + + // See if off state torch can be turned on again + if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour)) + { + return; // Something matches, torch still powered + } + + // Block torch on not powered, can be turned on again! + m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); // Set self as powered +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneLever(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + if (IsLeverOn(Meta)) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + + eBlockFace Dir = GetHandlerCompileTime<E_BLOCK_LEVER>::type::BlockMetaDataToBlockFace(Meta); + + Dir = ReverseBlockFace(Dir); + + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Dir); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + NIBBLETYPE MetaData = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + + if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) + { + if ((MetaData & 0x4) == 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) + { + if ((MetaData & 0x4) != 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & ~0x04); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneButton(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + if (IsButtonOn(Meta)) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + + eBlockFace Dir = GetHandlerCompileTime<E_BLOCK_STONE_BUTTON>::type::BlockMetaDataToBlockFace(Meta); + Dir = ReverseBlockFace(Dir); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Dir); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + static const struct // Define which directions the wire can receive power from + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0 }, /* Wires on same level start */ + { -1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0, -1 }, /* Wires on same level stop */ + { 1, 1, 0 }, /* Wires one higher, surrounding self start */ + { -1, 1, 0 }, + { 0, 1, 1 }, + { 0, 1, -1 }, /* Wires one higher, surrounding self stop */ + { 1, -1, 0 }, /* Wires one lower, surrounding self start */ + { -1, -1, 0 }, + { 0, -1, 1 }, + { 0, -1, -1 }, /* Wires one lower, surrounding self stop */ + }; + + static const struct // Define which directions the wire will check for repeater prescence + { + int x, y, z; + } gSideCoords[] = + { + { 1, 0, 0 }, + { -1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0, -1 }, + { 0, 1, 0 }, + }; + + // Check to see if directly beside a power source + unsigned char MyPower; + if (!IsWirePowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower)) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0); + return; + } + + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + + if (MyPower < 1) + { + return; + } + + MyPower--; + + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power + { + if ((i >= 4) && (i <= 7)) // If we are currently checking for wire surrounding ourself one block above... + { + BLOCKTYPE Type = 0; + if (a_RelBlockY + 1 >= cChunkDef::Height) + { + continue; + } + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, Type)) + { + continue; + } + if (cBlockInfo::IsSolid(Type)) // If there is something solid above us (wire cut off)... + { + continue; // We don't receive power from that wire + } + } + else if ((i >= 8) && (i <= 11)) // See above, but this is for wire below us + { + BLOCKTYPE Type = 0; + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY, a_RelBlockZ + gCrossCoords[i].z, Type)) + { + continue; + } + if (cBlockInfo::IsSolid(Type)) + { + continue; + } + } + + BLOCKTYPE Type = 0; + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type)) + { + continue; + } + if (Type == E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + } + } + + for (size_t i = 0; i < ARRAYCOUNT(gSideCoords); i++) // Look for repeaters immediately surrounding self and try to power them + { + BLOCKTYPE Type = 0; + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, Type)) + { + continue; + } + if (Type == E_BLOCK_REDSTONE_REPEATER_OFF) + { + SetBlockPowered(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + } + } + + // Wire still powered, power blocks beneath + SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, MyPower); + + switch (GetWireDirection(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + case REDSTONE_NONE: + { + SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower); + break; + } + case REDSTONE_X_POS: + { + SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower); + break; + } + case REDSTONE_X_NEG: + { + SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower); + break; + } + case REDSTONE_Z_POS: + { + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower); + break; + } + case REDSTONE_Z_NEG: + { + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower); + break; + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState) +{ + /* Repeater Orientation Mini Guide: + =================================== + + | + | Z Axis + V + + X Axis ----> + + Repeater directions, values from a WorldType::GetBlockMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) lookup: + + East (Right) (X+): 0x1 + West (Left) (X-): 0x3 + North (Up) (Z-): 0x2 + South (Down) (Z+): 0x0 + // TODO: Add E_META_XXX enum entries for all meta values and update project with them + + Sun rises from East (X+) + + */ + + // Create a variable holding my meta to avoid multiple lookups. + NIBBLETYPE a_Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + bool IsOn = (a_MyState == E_BLOCK_REDSTONE_REPEATER_ON); + + if (!IsRepeaterLocked(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta)) // If we're locked, change nothing. Otherwise: + { + bool IsSelfPowered = IsRepeaterPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta); + if (IsSelfPowered && !IsOn) // Queue a power change if powered, but not on and not locked. + { + QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, true); + } + else if (!IsSelfPowered && IsOn) // Queue a power change if unpowered, on, and not locked. + { + QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false); + } + } +} + + +void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() +{ + for (auto itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end();) + { + if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? + { + int RelBlockX = itr->a_RelBlockPos.x; + int RelBlockY = itr->a_RelBlockPos.y; + int RelBlockZ = itr->a_RelBlockPos.z; + BLOCKTYPE Block; + NIBBLETYPE Meta; + m_Chunk->GetBlockTypeMeta(RelBlockX, RelBlockY, RelBlockZ, Block, Meta); + if (itr->ShouldPowerOn) + { + if (Block != E_BLOCK_REDSTONE_REPEATER_ON) // For performance + { + m_Chunk->SetBlock(itr->a_RelBlockPos, E_BLOCK_REDSTONE_REPEATER_ON, Meta); + } + + switch (Meta & 0x3) // We only want the direction (bottom) bits + { + case 0x0: + { + SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ - 1, RelBlockX, RelBlockY, RelBlockZ); + SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZM); + break; + } + case 0x1: + { + SetBlockPowered(RelBlockX + 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ); + SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XP); + break; + } + case 0x2: + { + SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ + 1, RelBlockX, RelBlockY, RelBlockZ); + SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZP); + break; + } + case 0x3: + { + SetBlockPowered(RelBlockX - 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ); + SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XM); + break; + } + } + } + else if (Block != E_BLOCK_REDSTONE_REPEATER_OFF) + { + m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); + } + itr = m_RepeatersDelayList->erase(itr); + } + else + { + LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); + itr->a_ElapsedTicks++; + itr++; + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandlePiston(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + + if (IsPistonPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7)) // We only want the bottom three bits (4th controls extended-ness) + { + GetHandlerCompileTime<E_BLOCK_PISTON>::type::ExtendPiston(BlockX, a_RelBlockY, BlockZ, &this->m_World); + } + else + { + GetHandlerCompileTime<E_BLOCK_PISTON>::type::RetractPiston(BlockX, a_RelBlockY, BlockZ, &this->m_World); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleDropSpenser(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + class cSetPowerToDropSpenser : + public cRedstonePoweredCallback + { + bool m_IsPowered; + public: + cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cRedstonePoweredEntity * a_DropSpenser) override + { + a_DropSpenser->SetRedstonePower(m_IsPowered); + return false; + } + } DrSpSP(AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); + + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, DrSpSP); +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneLamp(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState) +{ + if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF) + { + if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0); + } + } + else + { + if (!AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTNT(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + + if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + m_Chunk->BroadcastSoundEffect("game.tnt.primed", (double)BlockX, (double)a_RelBlockY, (double)BlockZ, 0.5f, 0.6f); + m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_AIR, 0); + this->m_World.SpawnPrimedTNT(BlockX + 0.5, a_RelBlockY + 0.5, BlockZ + 0.5); // 80 ticks to boom + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleDoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + + typedef GetHandlerCompileTime<E_BLOCK_WOODEN_DOOR>::type DoorHandler; + + if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) + { + cChunkInterface ChunkInterface(this->m_World.GetChunkMap()); + if (!DoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ)) + { + DoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, true); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) + { + cChunkInterface ChunkInterface(this->m_World.GetChunkMap()); + if (DoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ)) + { + DoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, false); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleCommandBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + class cSetPowerToCommandBlock : + public cRedstonePoweredCallback + { + bool m_IsPowered; + public: + cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cRedstonePoweredEntity * a_CommandBlock) override + { + a_CommandBlock->SetRedstonePower(m_IsPowered); + return false; + } + } CmdBlockSP(AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); + + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, CmdBlockSP); +} + + + + + +void cIncrementalRedstoneSimulator::HandleRail(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType) +{ + switch (a_MyType) + { + case E_BLOCK_DETECTOR_RAIL: + { + if ((m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x08) == 0x08) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_MyType); + } + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0x08); + } + else + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x07); + } + break; + } + default: LOGD("Unhandled type of rail in %s", __FUNCTION__); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + + if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) + { + this->m_World.SetTrapdoorOpen(BlockX, a_RelBlockY, BlockZ, true); + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) + { + this->m_World.SetTrapdoorOpen(BlockX, a_RelBlockY, BlockZ, false); + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + bool m_bAreCoordsPowered = AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + + if (m_bAreCoordsPowered) + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) + { + class cSetPowerToNoteBlock : + public cRedstonePoweredCallback + { + public: + cSetPowerToNoteBlock() {} + + virtual bool Item(cRedstonePoweredEntity * a_NoteBlock) override + { + a_NoteBlock->SetRedstonePower(true); + return false; + } + } NoteBlockSP; + + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, NoteBlockSP); + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) + { + SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX, BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); + + if (!this->m_World.IsChunkLighted(ChunkX, ChunkZ)) + { + this->m_World.QueueLightChunk(ChunkX, ChunkZ); + } + else + { + if (m_Chunk->GetTimeAlteredLight(this->m_World.GetBlockSkyLight(BlockX, a_RelBlockY + 1, BlockZ)) > 8) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + } + else + { + WakeUp(BlockX, a_RelBlockY, BlockZ, m_Chunk); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType) +{ + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; + int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + + switch (a_MyType) + { + case E_BLOCK_STONE_PRESSURE_PLATE: + { + // MCS feature - stone pressure plates can only be triggered by players :D + cPlayer * a_Player = this->m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.5f, false); + + if (a_Player != nullptr) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1); + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); + } + else + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + } + break; + } + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + { + class cPressurePlateCallback : + public cEntityCallback + { + public: + cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_NumberOfEntities(0), + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); + double Distance = (EntityPos - BlockPos).Length(); + + if (Distance <= 0.5) + { + m_NumberOfEntities++; + } + return false; + } + + bool GetPowerLevel(unsigned char & a_PowerLevel) const + { + a_PowerLevel = std::min(m_NumberOfEntities, MAX_POWER_LEVEL); + return (a_PowerLevel > 0); + } + + protected: + int m_NumberOfEntities; + + int m_X; + int m_Y; + int m_Z; + }; + + cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); + this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); + + unsigned char Power; + NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + if (PressurePlateCallback.GetPowerLevel(Power)) + { + if (Meta == E_META_PRESSURE_PLATE_RAISED) + { + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); + } + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); + } + else + { + if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) + { + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); + } + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + } + + break; + } + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + { + class cPressurePlateCallback : + public cEntityCallback + { + public: + cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_NumberOfEntities(0), + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); + double Distance = (EntityPos - BlockPos).Length(); + + if (Distance <= 0.5) + { + m_NumberOfEntities++; + } + return false; + } + + bool GetPowerLevel(unsigned char & a_PowerLevel) const + { + a_PowerLevel = std::min((int)ceil(m_NumberOfEntities / 10.f), MAX_POWER_LEVEL); + return (a_PowerLevel > 0); + } + + protected: + int m_NumberOfEntities; + + int m_X; + int m_Y; + int m_Z; + }; + + cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); + this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); + + unsigned char Power; + NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + if (PressurePlateCallback.GetPowerLevel(Power)) + { + if (Meta == E_META_PRESSURE_PLATE_RAISED) + { + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); + } + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); + } + else + { + if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) + { + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); + } + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + } + + break; + } + case E_BLOCK_WOODEN_PRESSURE_PLATE: + { + class cPressurePlateCallback : + public cEntityCallback + { + public: + cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_FoundEntity(false), + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); + double Distance = (EntityPos - BlockPos).Length(); + + if (Distance <= 0.5) + { + m_FoundEntity = true; + return true; // Break out, we only need to know for plates that at least one entity is on top + } + return false; + } + + bool FoundEntity(void) const + { + return m_FoundEntity; + } + + protected: + bool m_FoundEntity; + + int m_X; + int m_Y; + int m_Z; + }; + + cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); + this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); + + NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + if (PressurePlateCallback.FoundEntity()) + { + if (Meta == E_META_PRESSURE_PLATE_RAISED) + { + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); + } + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); + } + else + { + if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) + { + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); + } + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + } + break; + } + default: + { + LOGD("Unimplemented pressure plate type %s in cRedstoneSimulator", ItemToFullString(cItem(a_MyType)).c_str()); + break; + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int RelX = a_RelBlockX, RelZ = a_RelBlockZ; + bool FoundActivated = false; + eBlockFace FaceToGoTowards = GetHandlerCompileTime<E_BLOCK_TRIPWIRE_HOOK>::type::MetadataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); + + for (int i = 0; i < 40; ++i) // Tripwires can be connected up to 40 blocks + { + BLOCKTYPE Type; + NIBBLETYPE Meta; + + AddFaceDirection(RelX, a_RelBlockY, RelZ, FaceToGoTowards); + m_Chunk->UnboundedRelGetBlock(RelX, a_RelBlockY, RelZ, Type, Meta); + + if (Type == E_BLOCK_TRIPWIRE) + { + if (Meta == 0x1) + { + FoundActivated = true; + } + } + else if (Type == E_BLOCK_TRIPWIRE_HOOK) + { + if (ReverseBlockFace(GetHandlerCompileTime<E_BLOCK_TRIPWIRE_HOOK>::type::MetadataToDirection(Meta)) == FaceToGoTowards) + { + // Other hook facing in opposite direction - circuit completed! + break; + } + else + { + // Tripwire hook not connected at all, AND away all the power state bits + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + return; + } + } + else + { + // Tripwire hook not connected at all, AND away all the power state bits + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + return; + } + } + + if (FoundActivated) + { + // Connected and activated, set the 3rd and 4th highest bits + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0xC); + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + } + else + { + // Connected but not activated, AND away the highest bit + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7) | 0x4); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + class cGetTrappedChestPlayers : + public cItemCallback<cChestEntity> + { + public: + cGetTrappedChestPlayers(void) : + m_NumberOfPlayers(0) + { + } + + virtual ~cGetTrappedChestPlayers() + { + } + + virtual bool Item(cChestEntity * a_Chest) override + { + ASSERT(a_Chest->GetBlockType() == E_BLOCK_TRAPPED_CHEST); + m_NumberOfPlayers = a_Chest->GetNumberOfPlayers(); + return (m_NumberOfPlayers <= 0); + } + + unsigned char GetPowerLevel(void) const + { + return std::min(m_NumberOfPlayers, MAX_POWER_LEVEL); + } + + private: + int m_NumberOfPlayers; + + } GTCP; + + int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; + int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; + if (m_Chunk->DoWithChestAt(BlockX, a_RelBlockY, BlockZ, GTCP)) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, GTCP.GetPowerLevel()); + } + else + { + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; + int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; + + class cTripwireCallback : + public cEntityCallback + { + public: + cTripwireCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_FoundEntity(false), + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + cBoundingBox bbWire(m_X, m_X + 1, m_Y, m_Y + 0.1, m_Z, m_Z + 1); + cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); + + if (bbEntity.DoesIntersect(bbWire)) + { + m_FoundEntity = true; + return true; // One entity is sufficient to trigger the wire + } + return false; + } + + bool FoundEntity(void) const + { + return m_FoundEntity; + } + + protected: + bool m_FoundEntity; + + int m_X; + int m_Y; + int m_Z; + }; + + cTripwireCallback TripwireCallback(BlockX, a_RelBlockY, BlockZ); + this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), TripwireCallback); + + if (TripwireCallback.FoundEntity()) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1); + } + else + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0); + } +} + + + + + +bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, cChunk * a_Chunk) +{ + // Torches want to access neighbour's data when on a wall, hence the extra chunk parameter + + for (const auto & itr : ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks) // Check powered list + { + if (itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + return true; + } + } + return false; +} + + + + + +bool cIncrementalRedstoneSimulator::AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + for (const auto & itr : *m_LinkedPoweredBlocks) // Check linked powered list + { + if (itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + return true; + } + } + return false; +} + + + + + +bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta) { - return new cIncrementalRedstoneSimulator<cChunk, cWorld, GetHandlerCompileTime, cChestEntity>(a_World); + // Repeaters cannot be powered by any face except their back; verify that this is true for a source + + for (const auto & itr : *m_PoweredBlocks) + { + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + + switch (a_Meta & 0x3) + { + case 0x0: + { + // Flip the coords to check the back of the repeater + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; } + break; + } + case 0x1: + { + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + break; + } + case 0x2: + { + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; } + break; + } + case 0x3: + { + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + break; + } + } + } + + for (const auto & itr : *m_LinkedPoweredBlocks) + { + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + + switch (a_Meta & 0x3) + { + case 0x0: + { + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; } + break; + } + case 0x1: + { + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + break; + } + case 0x2: + { + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; } + break; + } + case 0x3: + { + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + break; + } + } + } + return false; // Couldn't find power source behind repeater } + + + + +bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta) +{ + switch (a_Meta & 0x3) // We only want the 'direction' part of our metadata + { + // If the repeater is looking up or down (If parallel to the Z axis) + case 0x0: + case 0x2: + { + // Check if eastern(right) neighbor is a powered on repeater who is facing us + BLOCKTYPE Block = 0; + NIBBLETYPE OtherRepeaterDir = 0; + if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) // Is right neighbor a powered repeater? + { + if ((OtherRepeaterDir & 0x03) == 0x3) + { + return true; + } // If so, I am latched/locked + } + + // Check if western(left) neighbor is a powered on repeater who is facing us + if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + { + if ((OtherRepeaterDir & 0x03) == 0x1) + { + return true; + } // If so, I am latched/locked + } + + break; + } + + // If the repeater is looking left or right (If parallel to the x axis) + case 0x1: + case 0x3: + { + // Check if southern(down) neighbor is a powered on repeater who is facing us + BLOCKTYPE Block = 0; + NIBBLETYPE OtherRepeaterDir = 0; + + if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + { + if ((OtherRepeaterDir & 0x30) == 0x00) + { + return true; + } // If so, am latched/locked + } + + // Check if northern(up) neighbor is a powered on repeater who is facing us + if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + { + if ((OtherRepeaterDir & 0x03) == 0x02) + { + return true; + } // If so, I am latched/locked + } + + break; + } + } + + return false; // None of the checks succeeded, I am not a locked repeater +} + + + + +bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta) +{ + // Pistons cannot be powered through their front face; this function verifies that a source meets this requirement + + eBlockFace Face = GetHandlerCompileTime<E_BLOCK_PISTON>::type::MetaDataToDirection(a_Meta); + + for (const auto & itr : *m_PoweredBlocks) + { + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + + int X = a_RelBlockX, Z = a_RelBlockZ; + AddFaceDirection(X, a_RelBlockY, Z, Face); + + if (!itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(X, a_RelBlockY, Z)))) + { + return true; + } + } + + for (const auto & itr : *m_LinkedPoweredBlocks) + { + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + + int X = a_RelBlockX, Z = a_RelBlockZ; + AddFaceDirection(X, a_RelBlockY, Z, Face); + + if (!itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(X, a_RelBlockY, Z)))) + { + return true; + } + } + return false; // Source was in front of the piston's front face +} + + + + +bool cIncrementalRedstoneSimulator::IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel) +{ + a_PowerLevel = 0; + + for (const auto & itr : *m_PoweredBlocks) // Check powered list + { + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } + a_PowerLevel = std::max(itr.a_PowerLevel, a_PowerLevel); // Get the highest power level (a_PowerLevel is initialised already and there CAN be multiple levels for one block) + } + + for (const auto & itr : *m_LinkedPoweredBlocks) // Check linked powered list + { + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } + + BLOCKTYPE Type = E_BLOCK_AIR; + if (!m_Chunk->UnboundedRelGetBlockType(itr.a_SourcePos.x, itr.a_SourcePos.y, itr.a_SourcePos.z, Type) || (Type == E_BLOCK_REDSTONE_WIRE)) + { + continue; + } + a_PowerLevel = std::max(itr.a_PowerLevel, a_PowerLevel); + } + + return (a_PowerLevel != 0); // Answer the inital question: is the wire powered? +} + + + + + +bool cIncrementalRedstoneSimulator::AreCoordsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool IsCurrentStatePowered) +{ + for (const auto & itr : *m_SimulatedPlayerToggleableBlocks) + { + if (itr.a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + if (itr.WasLastStatePowered != IsCurrentStatePowered) // Was the last power state different to the current? + { + return false; // It was, coordinates are no longer simulated + } + else + { + return true; // It wasn't, don't resimulate block, and allow players to toggle + } + } + } + return false; // Block wasn't even in the list, not simulated +} + + + + + +void cIncrementalRedstoneSimulator::SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel) +{ + BLOCKTYPE MiddleBlock = 0; + switch (a_Direction) + { + case BLOCK_FACE_XM: + { + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, MiddleBlock)) + { + return; + } + + SetBlockLinkedPowered(a_RelBlockX - 2, a_RelBlockY, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + + break; + } + case BLOCK_FACE_XP: + { + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, MiddleBlock)) + { + return; + } + + SetBlockLinkedPowered(a_RelBlockX + 2, a_RelBlockY, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + + break; + } + case BLOCK_FACE_YM: + { + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, MiddleBlock)) + { + return; + } + + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 2, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + + break; + } + case BLOCK_FACE_YP: + { + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, MiddleBlock)) + { + return; + } + + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 2, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + + break; + } + case BLOCK_FACE_ZM: + { + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, MiddleBlock)) + { + return; + } + + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 2, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + + break; + } + case BLOCK_FACE_ZP: + { + if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, MiddleBlock)) + { + return; + } + + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 2, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); + + break; + } + default: + { + ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); // Zombies, that wasn't supposed to happen... + break; + } + } +} + + + + + +void cIncrementalRedstoneSimulator::SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel) +{ + static const struct + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0 }, + { -1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0, -1 }, + { 0, 1, 0 }, + { 0, -1, 0 } + }; + + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through struct to power all directions + { + SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_PowerLevel); + } +} + + + + + +void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel) +{ + cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); // Adjust coordinates for the later call using these values + if ((Neighbour == nullptr) || !Neighbour->IsValid()) + { + return; + } + + auto & Powered = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_PoweredBlocks; // We need to insert the value into the chunk who owns the block position + for (auto itr = Powered.begin(); itr != Powered.end(); ++itr) + { + if ( + itr->a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) && + itr->a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) + ) + { + // Check for duplicates, update power level, don't add a new listing + itr->a_PowerLevel = a_PowerLevel; + return; + } + } + + // No need to get neighbouring chunk as we can guarantee that when something is powering us, the entry will be in our chunk + for (auto itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) + { + if ( + itr->a_BlockPos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) && + itr->a_SourcePos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) && + (m_Chunk->GetBlock(a_RelSourceX, a_RelSourceY, a_RelSourceZ) == E_BLOCK_REDSTONE_WIRE) + ) + { + BLOCKTYPE Block; + NIBBLETYPE Meta; + Neighbour->GetBlockTypeMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Block, Meta); + + if (Block == E_BLOCK_REDSTONE_WIRE) + { + if (Meta < a_PowerLevel) + { + m_PoweredBlocks->erase(itr); // Powering source with higher power level, allow it + break; + } + else + { + // Powered wires try to power their source - don't let them! + return; + } + } + } + } + + sPoweredBlocks RC; + RC.a_BlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + RC.a_SourcePos = Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ); + RC.a_PowerLevel = a_PowerLevel; + Powered.emplace_back(RC); + Neighbour->SetIsRedstoneDirty(true); + m_Chunk->SetIsRedstoneDirty(true); +} + + + + + +void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( + int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, + int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, + int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, + BLOCKTYPE a_MiddleBlock, unsigned char a_PowerLevel + ) +{ + if (!IsViableMiddleBlock(a_MiddleBlock)) + { + return; + } + + cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); + m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelMiddleX, a_RelMiddleZ); + if ((Neighbour == nullptr) || !Neighbour->IsValid()) + { + return; + } + + auto & Linked = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_LinkedBlocks; + for (auto & itr : Linked) // Check linked powered list + { + if ( + itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) && + itr.a_MiddlePos.Equals(Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ)) && + itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) + ) + { + // Check for duplicates, update power level, don't add a new listing + itr.a_PowerLevel = a_PowerLevel; + return; + } + } + + sLinkedPoweredBlocks RC; + RC.a_BlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + RC.a_MiddlePos = Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ); + RC.a_SourcePos = Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ); + RC.a_PowerLevel = a_PowerLevel; + Linked.emplace_back(RC); + Neighbour->SetIsRedstoneDirty(true); + m_Chunk->SetIsRedstoneDirty(true); +} + + + + + +void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered) +{ + for (auto itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr) + { + if (!itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } + + if (itr->WasLastStatePowered != WasLastStatePowered) + { + // If power states different, update listing + itr->WasLastStatePowered = WasLastStatePowered; + return; + } + else + { + // If states the same, just ignore + return; + } + } + + // We have arrive here; no block must be in list - add one + sSimulatedPlayerToggleableList RC; + RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + RC.WasLastStatePowered = WasLastStatePowered; + m_SimulatedPlayerToggleableBlocks->emplace_back(RC); +} + + + + + +bool cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn) +{ + for (auto itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr) + { + if (itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry + { + return false; + } + + // Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit + itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description + itr->a_ElapsedTicks = 0; + itr->ShouldPowerOn = ShouldPowerOn; + return false; + } + } + + // Self not in list, add self to list + sRepeatersDelayList RC; + RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + + // Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.) + // Multiply by 2 because in MCS, 1 redstone tick = 1 world tick, but in Vanilla, 1 redstone tick = 2 world ticks, and we need to maintain compatibility + RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; + + RC.a_ElapsedTicks = 0; + RC.ShouldPowerOn = ShouldPowerOn; + m_RepeatersDelayList->emplace_back(RC); + return true; +} + + + + + +void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk, bool a_IsFirstCall) +{ + if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid + { + if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) + { + return; + } + } + + std::vector<Vector3i> BlocksPotentiallyUnpowered; + + auto Data = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); + Data->m_PoweredBlocks.erase(std::remove_if(Data->m_PoweredBlocks.begin(), Data->m_PoweredBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelSourceX, a_RelSourceY, a_RelSourceZ](const sPoweredBlocks & itr) + { + if (itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ))) + { + BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos); + a_Chunk->SetIsRedstoneDirty(true); + return true; + } + return false; + } + ), Data->m_PoweredBlocks.end()); + + Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelSourceX, a_RelSourceY, a_RelSourceZ](const sLinkedPoweredBlocks & itr) + { + if (itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ))) + { + BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos); + a_Chunk->SetIsRedstoneDirty(true); + return true; + } + return false; + } + ), Data->m_LinkedBlocks.end()); + + if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) + { + // +- 2 to accomodate linked powered blocks + SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX - 2, a_RelSourceZ), false); + SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX + 2, a_RelSourceZ), false); + SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX, a_RelSourceZ - 2), false); + SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX, a_RelSourceZ + 2), false); + } + + for (const auto & itr : BlocksPotentiallyUnpowered) + { + if (!AreCoordsPowered(itr.x, itr.y, itr.z)) + { + SetSourceUnpowered(itr.x, itr.y, itr.z, a_Chunk->GetRelNeighborChunk(itr.x, itr.z)); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::SetInvalidMiddleBlock(int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, cChunk * a_Chunk, bool a_IsFirstCall) +{ + if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid + { + if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) + { + return; + } + } + + std::vector<Vector3i> BlocksPotentiallyUnpowered; + auto Data = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); + + Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ](const sLinkedPoweredBlocks & itr) + { + if (itr.a_MiddlePos.Equals(Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ))) + { + BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos); + a_Chunk->SetIsRedstoneDirty(true); + return true; + } + return false; + } + ), Data->m_LinkedBlocks.end()); + + if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ)) + { + // +- 2 to accomodate linked powered blocks + SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX - 2, a_RelMiddleZ), false); + SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX + 2, a_RelMiddleZ), false); + SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX, a_RelMiddleZ - 2), false); + SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX, a_RelMiddleZ + 2), false); + } + + for (const auto & itr : BlocksPotentiallyUnpowered) + { + if (!AreCoordsPowered(itr.x, itr.y, itr.z)) + { + SetSourceUnpowered(itr.x, itr.y, itr.z, a_Chunk->GetRelNeighborChunk(itr.x, itr.z)); + } + } +} + + + + + +cIncrementalRedstoneSimulator::eRedstoneDirection cIncrementalRedstoneSimulator::GetWireDirection(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int Dir = REDSTONE_NONE; + + BLOCKTYPE NegX = 0; + if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, NegX)) + { + if (IsPotentialSource(NegX)) + { + Dir |= (REDSTONE_X_POS); + } + } + + BLOCKTYPE PosX = 0; + if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, PosX)) + { + if (IsPotentialSource(PosX)) + { + Dir |= (REDSTONE_X_NEG); + } + } + + BLOCKTYPE NegZ = 0; + if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, NegZ)) + { + if (IsPotentialSource(NegZ)) + { + if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_POS; + } + } + + BLOCKTYPE PosZ = 0; + if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, PosZ)) + { + if (IsPotentialSource(PosZ)) + { + if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_NEG; + } + } + return (eRedstoneDirection)Dir; +} + + + + + +bool cIncrementalRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) +{ + // Extract the ON bit from metadata and return if true if it is set: + return ((a_BlockMeta & 0x8) == 0x8); +} + + + + diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 5bd1ad95d..3d2962c08 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -2,8 +2,396 @@ #pragma once #include "RedstoneSimulator.h" +#include "BlockEntities/RedstonePoweredEntity.h" class cWorld; class cChunk; -cRedstoneSimulator<cChunk, cWorld> * MakeIncrementalRedstoneSimulator(cWorld & a_World); + + + + +typedef cItemCallback<cRedstonePoweredEntity> cRedstonePoweredCallback; + + + +class cIncrementalRedstoneSimulator : + public cRedstoneSimulator +{ + typedef cRedstoneSimulator super; +public: + + cIncrementalRedstoneSimulator(cWorld & a_World) + : cRedstoneSimulator(a_World) + { + } + + virtual cRedstoneSimulatorChunkData * CreateChunkData() override + { + return new cIncrementalRedstoneSimulatorChunkData; + } + + virtual void Simulate(float a_Dt) override { UNUSED(a_Dt); } // not used + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); } + virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + + enum eRedstoneDirection + { + REDSTONE_NONE = 0, + REDSTONE_X_POS = 0x1, + REDSTONE_X_NEG = 0x2, + REDSTONE_Z_POS = 0x4, + REDSTONE_Z_NEG = 0x8, + }; + eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ); + +private: + +#define MAX_POWER_LEVEL 15 + + struct sPoweredBlocks // Define structure of the directly powered blocks list + { + Vector3i a_BlockPos; // Position of powered block + Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos + unsigned char a_PowerLevel; + }; + + struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side) + { + Vector3i a_BlockPos; + Vector3i a_MiddlePos; // Position of block that is betwixt a source and the destination + Vector3i a_SourcePos; + unsigned char a_PowerLevel; + }; + + struct sSimulatedPlayerToggleableList // Define structure of the list containing simulate-on-update blocks (such as trapdoors that respond once to a block update, and can be toggled by a player) + { + Vector3i a_RelBlockPos; + bool WasLastStatePowered; // Was the last state powered or not? Determines whether a source update has happened and if I should resimulate + }; + + struct sRepeatersDelayList // Define structure of list containing repeaters' delay states + { + Vector3i a_RelBlockPos; + unsigned char a_DelayTicks; // For how many ticks should the repeater delay + unsigned char a_ElapsedTicks; // How much of the previous has been elapsed? + bool ShouldPowerOn; // What happens when the delay time is fulfilled? + }; + + class cIncrementalRedstoneSimulatorChunkData : + public cRedstoneSimulatorChunkData + { + public: + /// Per-chunk data for the simulator, specified individual chunks to simulate + cCoordWithBlockAndBoolVector m_ChunkData; + cCoordWithBlockAndBoolVector m_QueuedChunkData; + std::vector<sPoweredBlocks> m_PoweredBlocks; + std::vector<sLinkedPoweredBlocks> m_LinkedBlocks; + std::vector<sSimulatedPlayerToggleableList> m_SimulatedPlayerToggleableBlocks; + std::vector<sRepeatersDelayList> m_RepeatersDelayList; + }; + +public: + + typedef std::vector <sPoweredBlocks> PoweredBlocksList; + typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList; + typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList; + typedef std::vector <sRepeatersDelayList> RepeatersDelayList; + +private: + + cIncrementalRedstoneSimulatorChunkData * m_RedstoneSimulatorChunkData; + PoweredBlocksList * m_PoweredBlocks; + LinkedBlocksList * m_LinkedPoweredBlocks; + SimulatedPlayerToggleableList * m_SimulatedPlayerToggleableBlocks; + RepeatersDelayList * m_RepeatersDelayList; + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override { RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); } + void RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk = nullptr); + cChunk * m_Chunk; + + // We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly + // In addition to being non-performant, it would stop the player from actually breaking said device + + /* ====== SOURCES ====== */ + /** Handles the redstone torch */ + void HandleRedstoneTorch(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); + /** Handles the redstone block */ + void HandleRedstoneBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles levers */ + void HandleRedstoneLever(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles buttons */ + void HandleRedstoneButton(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles daylight sensors */ + void HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles pressure plates */ + void HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType); + /** Handles tripwire hooks + Performs correct meta and power setting for self by going in the direction it faces and looking for a continous line of tripwire bounded by another oppositely facing hook + If this line is complete, it verifies that at least on wire reports an entity is on top (via its meta), and performs its task + */ + void HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles trapped chests */ + void HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /* ==================== */ + + /* ====== CARRIERS ====== */ + /** Handles redstone wire */ + void HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles repeaters */ + void HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); + /* ====================== */ + + /* ====== DEVICES ====== */ + /** Handles pistons */ + void HandlePiston(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles dispensers and droppers */ + void HandleDropSpenser(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles TNT (exploding) */ + void HandleTNT(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles redstone lamps */ + void HandleRedstoneLamp(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); + /** Handles doords */ + void HandleDoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles command blocks */ + void HandleCommandBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles activator, detector, and powered rails */ + void HandleRail(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType); + /** Handles trapdoors */ + void HandleTrapdoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles fence gates */ + void HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles noteblocks */ + void HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles tripwires */ + void HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /* ===================== */ + + /* ====== Helper functions ====== */ + /** Marks a block as powered */ + void SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL); + /** Marks a block as being powered through another block */ + void SetBlockLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, BLOCKTYPE a_MiddeBlock, unsigned char a_PowerLevel = MAX_POWER_LEVEL); + /** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */ + void SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered); + /** Marks the second block in a direction as linked powered */ + void SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel = MAX_POWER_LEVEL); + /** Marks all blocks immediately surrounding a coordinate as powered */ + void SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL); + /** Queues a repeater to be powered or unpowered and returns if the m_RepeatersDelayList iterators were invalidated */ + bool QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn); + /** Removes a block from the Powered and LinkedPowered lists + Used for variable sources such as tripwire hooks, daylight sensors, and trapped chests + */ + void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk, bool a_IsFirstCall = true); + void SetInvalidMiddleBlock(int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, cChunk * a_Chunk, bool a_IsFirstCall = true); + + /** Returns if a coordinate is powered or linked powered */ + bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } + /** Returns if a coordinate is in the directly powered blocks list */ + bool AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, cChunk * a_Chunk); + /** Returns if a coordinate is in the indirectly powered blocks list */ + bool AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */ + bool AreCoordsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool IsCurrentStatePowered); + /** Returns if a repeater is powered by testing for power sources behind the repeater */ + bool IsRepeaterPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta); + /** Returns if a repeater is locked */ + bool IsRepeaterLocked(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta); + /** Returns if a piston is powered */ + bool IsPistonPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta); + /** Returns if a wire is powered + The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire */ + bool IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel); + /** Handles delayed updates to repeaters **/ + void HandleRedstoneRepeaterDelays(void); + + /** Returns if lever metadata marks it as emitting power */ + bool IsLeverOn(NIBBLETYPE a_BlockMeta); + /** Returns if button metadata marks it as emitting power */ + bool IsButtonOn(NIBBLETYPE a_BlockMeta) { return IsLeverOn(a_BlockMeta); } + /* ============================== */ + + /* ====== Misc Functions ====== */ + /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */ + inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return cBlockInfo::FullyOccupiesVoxel(Block); } + + /** Returns if a block is a mechanism (something that accepts power and does something) + Used by torches to determine if they power a block whilst not standing on the ground + */ + inline static bool IsMechanism(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_ACACIA_DOOR: + case E_BLOCK_ACACIA_FENCE_GATE: + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_BIRCH_DOOR: + case E_BLOCK_BIRCH_FENCE_GATE: + case E_BLOCK_COMMAND_BLOCK: + case E_BLOCK_DARK_OAK_DOOR: + case E_BLOCK_DARK_OAK_FENCE_GATE: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_FENCE_GATE: + case E_BLOCK_HOPPER: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_IRON_TRAPDOOR: + case E_BLOCK_JUNGLE_DOOR: + case E_BLOCK_JUNGLE_FENCE_GATE: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_PISTON: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_SPRUCE_DOOR: + case E_BLOCK_SPRUCE_FENCE_GATE: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_TNT: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_WOODEN_DOOR: + { + return true; + } + default: return false; + } + } + + /** Returns if a block has the potential to output power */ + inline static bool IsPotentialSource(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_DAYLIGHT_SENSOR: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_LEVER: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_TRAPPED_CHEST: + { + return true; + } + default: return false; + } + } + + /** Returns if a block is any sort of redstone device */ + inline static bool IsRedstone(BLOCKTYPE Block) + { + switch (Block) + { + // All redstone devices, please alpha sort + case E_BLOCK_ACACIA_DOOR: + case E_BLOCK_ACACIA_FENCE_GATE: + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_BIRCH_DOOR: + case E_BLOCK_BIRCH_FENCE_GATE: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_COMMAND_BLOCK: + case E_BLOCK_DARK_OAK_DOOR: + case E_BLOCK_DARK_OAK_FENCE_GATE: + case E_BLOCK_DAYLIGHT_SENSOR: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_FENCE_GATE: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_HOPPER: + case E_BLOCK_INACTIVE_COMPARATOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_IRON_TRAPDOOR: + case E_BLOCK_JUNGLE_DOOR: + case E_BLOCK_JUNGLE_FENCE_GATE: + case E_BLOCK_LEVER: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_SPRUCE_DOOR: + case E_BLOCK_SPRUCE_FENCE_GATE: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_TNT: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_TRAPPED_CHEST: + case E_BLOCK_TRIPWIRE_HOOK: + case E_BLOCK_TRIPWIRE: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_PISTON: + { + return true; + } + default: return false; + } + } + + inline static bool AreCoordsOnChunkBoundary(int a_BlockX, int a_BlockY, int a_BlockZ) + { + return ( // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks + ((a_BlockX % cChunkDef::Width) <= 1) || + ((a_BlockX % cChunkDef::Width) >= 14) || + ((a_BlockZ % cChunkDef::Width) <= 1) || + ((a_BlockZ % cChunkDef::Width) >= 14) + ); + } + + inline static Vector3i AdjustRelativeCoords(const Vector3i & a_RelPosition) + { + if ( + (a_RelPosition.x >= 0) && (a_RelPosition.x < cChunkDef::Width) && + (a_RelPosition.z >= 0) && (a_RelPosition.z < cChunkDef::Width) + ) + { + return a_RelPosition; + } + + Vector3i RelPos = a_RelPosition; + + // Request for a different chunk, calculate chunk offset: + while (RelPos.x >= cChunkDef::Width) + { + RelPos.x -= cChunkDef::Width; + } + while (RelPos.x < 0) + { + RelPos.x += cChunkDef::Width; + } + while (RelPos.z >= cChunkDef::Width) + { + RelPos.z -= cChunkDef::Width; + } + while (RelPos.z < 0) + { + RelPos.z += cChunkDef::Width; + } + + return RelPos; + } +}; + + + + diff --git a/src/Simulator/IncrementalRedstoneSimulator.inc b/src/Simulator/IncrementalRedstoneSimulator.inc deleted file mode 100644 index 2d03584e2..000000000 --- a/src/Simulator/IncrementalRedstoneSimulator.inc +++ /dev/null @@ -1,2597 +0,0 @@ - -#include "IncrementalRedstoneSimulator.h" -#include "BoundingBox.h" -#include "BlockEntities/RedstonePoweredEntity.h" -#include "Blocks/ChunkInterface.h" -#include "RedstoneSimulator.h" - - -typedef cItemCallback<cEntity> cEntityCallback; - - - - - -typedef cItemCallback<cRedstonePoweredEntity> cRedstonePoweredCallback; - - -template<class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -class cIncrementalRedstoneSimulator : - public cRedstoneSimulator<ChunkType, WorldType> -{ - typedef cRedstoneSimulator<ChunkType, WorldType> super; -public: - - cIncrementalRedstoneSimulator(WorldType & a_World) - : cRedstoneSimulator<ChunkType, WorldType>(a_World) - { - } - ~cIncrementalRedstoneSimulator(); - - virtual cRedstoneSimulatorChunkData * CreateChunkData() override - { - return new cIncrementalRedstoneSimulatorChunkData; - } - - virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, ChunkType * a_Chunk) override; - virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); } - virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) override; - - enum eRedstoneDirection - { - REDSTONE_NONE = 0, - REDSTONE_X_POS = 0x1, - REDSTONE_X_NEG = 0x2, - REDSTONE_Z_POS = 0x4, - REDSTONE_Z_NEG = 0x8, - }; - eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ); - -private: - - #define MAX_POWER_LEVEL 15 - - struct sPoweredBlocks // Define structure of the directly powered blocks list - { - Vector3i a_BlockPos; // Position of powered block - Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos - unsigned char a_PowerLevel; - }; - - struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side) - { - Vector3i a_BlockPos; - Vector3i a_MiddlePos; // Position of block that is betwixt a source and the destination - Vector3i a_SourcePos; - unsigned char a_PowerLevel; - }; - - struct sSimulatedPlayerToggleableList // Define structure of the list containing simulate-on-update blocks (such as trapdoors that respond once to a block update, and can be toggled by a player) - { - Vector3i a_RelBlockPos; - bool WasLastStatePowered; // Was the last state powered or not? Determines whether a source update has happened and if I should resimulate - }; - - struct sRepeatersDelayList // Define structure of list containing repeaters' delay states - { - Vector3i a_RelBlockPos; - unsigned char a_DelayTicks; // For how many ticks should the repeater delay - unsigned char a_ElapsedTicks; // How much of the previous has been elapsed? - bool ShouldPowerOn; // What happens when the delay time is fulfilled? - }; - - class cIncrementalRedstoneSimulatorChunkData : - public cRedstoneSimulatorChunkData - { - public: - /// Per-chunk data for the simulator, specified individual chunks to simulate - cCoordWithBlockAndBoolVector m_ChunkData; - cCoordWithBlockAndBoolVector m_QueuedChunkData; - std::vector<sPoweredBlocks> m_PoweredBlocks; - std::vector<sLinkedPoweredBlocks> m_LinkedBlocks; - std::vector<sSimulatedPlayerToggleableList> m_SimulatedPlayerToggleableBlocks; - std::vector<sRepeatersDelayList> m_RepeatersDelayList; - }; - -public: - - typedef std::vector <sPoweredBlocks> PoweredBlocksList; - typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList; - typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList; - typedef std::vector <sRepeatersDelayList> RepeatersDelayList; - -private: - - cIncrementalRedstoneSimulatorChunkData * m_RedstoneSimulatorChunkData; - PoweredBlocksList * m_PoweredBlocks; - LinkedBlocksList * m_LinkedPoweredBlocks; - SimulatedPlayerToggleableList * m_SimulatedPlayerToggleableBlocks; - RepeatersDelayList * m_RepeatersDelayList; - - virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) override { RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); } - void RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk, ChunkType * a_OtherChunk = NULL); - ChunkType * m_Chunk; - - // We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly - // In addition to being non-performant, it would stop the player from actually breaking said device - - /* ====== SOURCES ====== */ - /** Handles the redstone torch */ - void HandleRedstoneTorch(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); - /** Handles the redstone block */ - void HandleRedstoneBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles levers */ - void HandleRedstoneLever(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles buttons */ - void HandleRedstoneButton(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles daylight sensors */ - void HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles pressure plates */ - void HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType); - /** Handles tripwire hooks - Performs correct meta and power setting for self by going in the direction it faces and looking for a continous line of tripwire bounded by another oppositely facing hook - If this line is complete, it verifies that at least on wire reports an entity is on top (via its meta), and performs its task - */ - void HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles trapped chests */ - void HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /* ==================== */ - - /* ====== CARRIERS ====== */ - /** Handles redstone wire */ - void HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles repeaters */ - void HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); - /* ====================== */ - - /* ====== DEVICES ====== */ - /** Handles pistons */ - void HandlePiston(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles dispensers and droppers */ - void HandleDropSpenser(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles TNT (exploding) */ - void HandleTNT(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles redstone lamps */ - void HandleRedstoneLamp(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); - /** Handles doords */ - void HandleDoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles command blocks */ - void HandleCommandBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles activator, detector, and powered rails */ - void HandleRail(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType); - /** Handles trapdoors */ - void HandleTrapdoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles fence gates */ - void HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles noteblocks */ - void HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Handles tripwires */ - void HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /* ===================== */ - - /* ====== Helper functions ====== */ - /** Marks a block as powered */ - void SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL); - /** Marks a block as being powered through another block */ - void SetBlockLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, BLOCKTYPE a_MiddeBlock, unsigned char a_PowerLevel = MAX_POWER_LEVEL); - /** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */ - void SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered); - /** Marks the second block in a direction as linked powered */ - void SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel = MAX_POWER_LEVEL); - /** Marks all blocks immediately surrounding a coordinate as powered */ - void SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL); - /** Queues a repeater to be powered or unpowered and returns if the m_RepeatersDelayList iterators were invalidated */ - bool QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn); - /** Removes a block from the Powered and LinkedPowered lists - Used for variable sources such as tripwire hooks, daylight sensors, and trapped chests - */ - void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, ChunkType * a_Chunk, bool a_IsFirstCall = true); - - /** Returns if a coordinate is powered or linked powered */ - bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } - /** Returns if a coordinate is in the directly powered blocks list */ - bool AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, ChunkType * a_Chunk); - /** Returns if a coordinate is in the indirectly powered blocks list */ - bool AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); - /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */ - bool AreCoordsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool IsCurrentStatePowered); - /** Returns if a repeater is powered by testing for power sources behind the repeater */ - bool IsRepeaterPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta); - /** Returns if a repeater is locked */ - bool IsRepeaterLocked(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta); - /** Returns if a piston is powered */ - bool IsPistonPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta); - /** Returns if a wire is powered - The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire */ - bool IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel); - /** Handles delayed updates to repeaters **/ - void HandleRedstoneRepeaterDelays(void); - - /** Returns if lever metadata marks it as emitting power */ - bool IsLeverOn(NIBBLETYPE a_BlockMeta); - /** Returns if button metadata marks it as emitting power */ - bool IsButtonOn(NIBBLETYPE a_BlockMeta) { return IsLeverOn(a_BlockMeta); } - /* ============================== */ - - /* ====== Misc Functions ====== */ - /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */ - inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return cBlockInfo::FullyOccupiesVoxel(Block); } - - /** Returns if a block is a mechanism (something that accepts power and does something) - Used by torches to determine if they power a block whilst not standing on the ground - */ - inline static bool IsMechanism(BLOCKTYPE Block) - { - switch (Block) - { - case E_BLOCK_ACACIA_DOOR: - case E_BLOCK_ACACIA_FENCE_GATE: - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_BIRCH_DOOR: - case E_BLOCK_BIRCH_FENCE_GATE: - case E_BLOCK_COMMAND_BLOCK: - case E_BLOCK_DARK_OAK_DOOR: - case E_BLOCK_DARK_OAK_FENCE_GATE: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_FENCE_GATE: - case E_BLOCK_HOPPER: - case E_BLOCK_IRON_DOOR: - case E_BLOCK_IRON_TRAPDOOR: - case E_BLOCK_JUNGLE_DOOR: - case E_BLOCK_JUNGLE_FENCE_GATE: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_PISTON: - case E_BLOCK_POWERED_RAIL: - case E_BLOCK_REDSTONE_LAMP_OFF: - case E_BLOCK_REDSTONE_LAMP_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_WIRE: - case E_BLOCK_SPRUCE_DOOR: - case E_BLOCK_SPRUCE_FENCE_GATE: - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_TNT: - case E_BLOCK_TRAPDOOR: - case E_BLOCK_WOODEN_DOOR: - { - return true; - } - default: return false; - } - } - - /** Returns if a block has the potential to output power */ - inline static bool IsPotentialSource(BLOCKTYPE Block) - { - switch (Block) - { - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_DAYLIGHT_SENSOR: - case E_BLOCK_WOODEN_BUTTON: - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_REDSTONE_WIRE: - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_LEVER: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_BLOCK_OF_REDSTONE: - case E_BLOCK_ACTIVE_COMPARATOR: - case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_STONE_PRESSURE_PLATE: - case E_BLOCK_WOODEN_PRESSURE_PLATE: - case E_BLOCK_TRAPPED_CHEST: - { - return true; - } - default: return false; - } - } - - /** Returns if a block is any sort of redstone device */ - inline static bool IsRedstone(BLOCKTYPE Block) - { - switch (Block) - { - // All redstone devices, please alpha sort - case E_BLOCK_ACACIA_DOOR: - case E_BLOCK_ACACIA_FENCE_GATE: - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_ACTIVE_COMPARATOR: - case E_BLOCK_BIRCH_DOOR: - case E_BLOCK_BIRCH_FENCE_GATE: - case E_BLOCK_BLOCK_OF_REDSTONE: - case E_BLOCK_COMMAND_BLOCK: - case E_BLOCK_DARK_OAK_DOOR: - case E_BLOCK_DARK_OAK_FENCE_GATE: - case E_BLOCK_DAYLIGHT_SENSOR: - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_FENCE_GATE: - case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_HOPPER: - case E_BLOCK_INACTIVE_COMPARATOR: - case E_BLOCK_IRON_DOOR: - case E_BLOCK_IRON_TRAPDOOR: - case E_BLOCK_JUNGLE_DOOR: - case E_BLOCK_JUNGLE_FENCE_GATE: - case E_BLOCK_LEVER: - case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_POWERED_RAIL: - case E_BLOCK_REDSTONE_LAMP_OFF: - case E_BLOCK_REDSTONE_LAMP_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_REDSTONE_WIRE: - case E_BLOCK_SPRUCE_DOOR: - case E_BLOCK_SPRUCE_FENCE_GATE: - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_STONE_PRESSURE_PLATE: - case E_BLOCK_TNT: - case E_BLOCK_TRAPDOOR: - case E_BLOCK_TRAPPED_CHEST: - case E_BLOCK_TRIPWIRE_HOOK: - case E_BLOCK_TRIPWIRE: - case E_BLOCK_WOODEN_BUTTON: - case E_BLOCK_WOODEN_DOOR: - case E_BLOCK_WOODEN_PRESSURE_PLATE: - case E_BLOCK_PISTON: - { - return true; - } - default: return false; - } - } - - inline static bool AreCoordsOnChunkBoundary(int a_BlockX, int a_BlockY, int a_BlockZ) - { - return ( // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks - ((a_BlockX % cChunkDef::Width) <= 1) || - ((a_BlockX % cChunkDef::Width) >= 14) || - ((a_BlockZ % cChunkDef::Width) <= 1) || - ((a_BlockZ % cChunkDef::Width) >= 14) - ); - } -}; - - - - - -template<class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::~cIncrementalRedstoneSimulator() -{ -} - - - - -template<class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk, ChunkType * a_OtherChunk) -{ - if ((a_Chunk == NULL) || !a_Chunk->IsValid()) - { - return; - } - else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height)) - { - return; - } - - // We may be called with coordinates in a chunk that is not the first chunk parameter - // In that case, the actual chunk (which the coordinates are in), will be passed as the second parameter - // Use that Chunk pointer to get a relative position - - int RelX = 0; - int RelZ = 0; - BLOCKTYPE Block; - NIBBLETYPE Meta; - - if (a_OtherChunk != NULL) - { - RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width; - RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width; - a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); - - // If a_OtherChunk is passed (not NULL), it is the chunk that had a block change, and a_Chunk will be the neighbouring chunk of that block - // Because said neighbouring chunk does not know of this change but still needs to update its redstone, we set it to dirty - a_Chunk->SetIsRedstoneDirty(true); - } - else - { - RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; - RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; - a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); - } - - // Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid - // Checking only when a block is changed, as opposed to every tick, also improves performance - - cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData * SimulatorChunkData = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData()); - if (SimulatorChunkData == NULL) - { - return; - } - PoweredBlocksList & PoweredBlocks = SimulatorChunkData->m_PoweredBlocks; - for (typename PoweredBlocksList::iterator itr = PoweredBlocks.begin(); itr != PoweredBlocks.end();) - { - if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - ++itr; - continue; - } - - if (!IsPotentialSource(Block)) - { - LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - itr = PoweredBlocks.erase(itr); - continue; - } - else if ( - // Changeable sources - ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || - ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - ((Block == E_BLOCK_DETECTOR_RAIL) && ((Meta & 0x08) == 0)) || - (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || - ((Block == E_BLOCK_TRIPWIRE_HOOK) && ((Meta & 0x08) == 0)) - ) - { - LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - itr = PoweredBlocks.erase(itr); - continue; - } - ++itr; - } - - LinkedBlocksList & LinkedPoweredBlocks = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks; - // We loop through all values (insteading of breaking out at the first) to make sure everything is gone, as there can be multiple SourceBlock entries for one AddBlock coordinate - for (typename LinkedBlocksList::iterator itr = LinkedPoweredBlocks.begin(); itr != LinkedPoweredBlocks.end();) - { - if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - if (!IsPotentialSource(Block)) - { - LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - itr = LinkedPoweredBlocks.erase(itr); - continue; - } - else if ( - // Things that can send power through a block but which depends on meta - ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || - ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) - ) - { - LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - itr = LinkedPoweredBlocks.erase(itr); - continue; - } - } - else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - if (!IsViableMiddleBlock(Block)) - { - LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - itr = LinkedPoweredBlocks.erase(itr); - continue; - } - } - ++itr; - } - - SimulatedPlayerToggleableList & SimulatedPlayerToggleableBlocks = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_SimulatedPlayerToggleableBlocks; - for (typename SimulatedPlayerToggleableList::iterator itr = SimulatedPlayerToggleableBlocks.begin(); itr != SimulatedPlayerToggleableBlocks.end(); ++itr) - { - if (!itr->a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ))) - { - continue; - } - - if (!IsAllowedBlock(Block)) - { - LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z); - SimulatedPlayerToggleableBlocks.erase(itr); - break; - } - } - - RepeatersDelayList & RepeatersDelayList = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; - for (typename RepeatersDelayList::iterator itr = RepeatersDelayList.begin(); itr != RepeatersDelayList.end(); ++itr) - { - if (!itr->a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ))) - { - continue; - } - - if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF)) - { - RepeatersDelayList.erase(itr); - break; - } - } - - if (a_OtherChunk != NULL) - { - // DO NOT touch our chunk's data structure if we are being called with coordinates from another chunk - this one caused me massive grief :P - return; - } - - cCoordWithBlockAndBoolVector & RedstoneSimulatorChunkData = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_ChunkData; - for (cCoordWithBlockAndBoolVector::iterator itr = RedstoneSimulatorChunkData.begin(); itr != RedstoneSimulatorChunkData.end(); ++itr) - { - if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block - { - if (!IsAllowedBlock(Block)) - { - itr->DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list - } - else - { - itr->DataTwo = false; - itr->Data = Block; // Update block information - } - return; - } - } - - if (!IsAllowedBlock(Block)) - { - return; - } - - cCoordWithBlockAndBoolVector & QueuedData = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData; - for (cCoordWithBlockAndBoolVector::iterator itr = QueuedData.begin(); itr != QueuedData.end(); ++itr) - { - if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) - { - // Can't have duplicates in here either, in case something adds the block again before the structure can written to the main chunk data - return; - } - } - QueuedData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, ChunkType * a_Chunk) -{ - m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); - if (m_RedstoneSimulatorChunkData == NULL) - { - m_RedstoneSimulatorChunkData = new cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData(); - a_Chunk->SetRedstoneSimulatorData(m_RedstoneSimulatorChunkData); - } - if (m_RedstoneSimulatorChunkData->m_ChunkData.empty() && ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData.empty()) - { - return; - } - - m_RedstoneSimulatorChunkData->m_ChunkData.insert( - m_RedstoneSimulatorChunkData->m_ChunkData.end(), - m_RedstoneSimulatorChunkData ->m_QueuedChunkData.begin(), - m_RedstoneSimulatorChunkData ->m_QueuedChunkData.end()); - - m_RedstoneSimulatorChunkData->m_QueuedChunkData.clear(); - - m_PoweredBlocks = &m_RedstoneSimulatorChunkData->m_PoweredBlocks; - m_RepeatersDelayList = &m_RedstoneSimulatorChunkData->m_RepeatersDelayList; - m_SimulatedPlayerToggleableBlocks = &m_RedstoneSimulatorChunkData->m_SimulatedPlayerToggleableBlocks; - m_LinkedPoweredBlocks = &m_RedstoneSimulatorChunkData->m_LinkedBlocks; - m_Chunk = a_Chunk; - bool ShouldUpdateSimulateOnceBlocks = false; - - if (a_Chunk->IsRedstoneDirty()) - { - // Simulate the majority of devices only if something (blockwise or power-wise) has changed - // Make sure to allow the chunk to resimulate after the initial run if there was a power change (ShouldUpdateSimulateOnceBlocks helps to do this) - a_Chunk->SetIsRedstoneDirty(false); - ShouldUpdateSimulateOnceBlocks = true; - } - - HandleRedstoneRepeaterDelays(); - - for (cCoordWithBlockAndBoolVector::iterator dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.begin(); dataitr != m_RedstoneSimulatorChunkData->m_ChunkData.end();) - { - if (dataitr->DataTwo) - { - dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.erase(dataitr); - continue; - } - - switch (dataitr->Data) - { - case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRIPWIRE: HandleTripwire(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRIPWIRE_HOOK: HandleTripwireHook(dataitr->x, dataitr->y, dataitr->z); break; - - case E_BLOCK_WOODEN_PRESSURE_PLATE: - case E_BLOCK_STONE_PRESSURE_PLATE: - case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: - { - HandlePressurePlate(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); - break; - } - default: break; - } - - if (ShouldUpdateSimulateOnceBlocks) - { - switch (dataitr->Data) - { - case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_IRON_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRAPPED_CHEST: HandleTrappedChest(dataitr->x, dataitr->y, dataitr->z); break; - - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_POWERED_RAIL: - { - HandleRail(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); - break; - } - case E_BLOCK_ACACIA_DOOR: - case E_BLOCK_BIRCH_DOOR: - case E_BLOCK_DARK_OAK_DOOR: - case E_BLOCK_JUNGLE_DOOR: - case E_BLOCK_SPRUCE_DOOR: - case E_BLOCK_WOODEN_DOOR: - case E_BLOCK_IRON_DOOR: - { - HandleDoor(dataitr->x, dataitr->y, dataitr->z); - break; - } - case E_BLOCK_ACACIA_FENCE_GATE: - case E_BLOCK_BIRCH_FENCE_GATE: - case E_BLOCK_DARK_OAK_FENCE_GATE: - case E_BLOCK_FENCE_GATE: - case E_BLOCK_JUNGLE_FENCE_GATE: - case E_BLOCK_SPRUCE_FENCE_GATE: - { - HandleFenceGate(dataitr->x, dataitr->y, dataitr->z); - break; - } - case E_BLOCK_REDSTONE_LAMP_OFF: - case E_BLOCK_REDSTONE_LAMP_ON: - { - HandleRedstoneLamp(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); - break; - } - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - { - HandleDropSpenser(dataitr->x, dataitr->y, dataitr->z); - break; - } - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - HandlePiston(dataitr->x, dataitr->y, dataitr->z); - break; - } - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - { - HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); - break; - } - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - { - HandleRedstoneTorch(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); - break; - } - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_WOODEN_BUTTON: - { - HandleRedstoneButton(dataitr->x, dataitr->y, dataitr->z); - break; - } - default: break; - } - } - ++dataitr; - } -} - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) -{ - if (AreCoordsOnChunkBoundary(a_BlockX, a_BlockY, a_BlockZ)) - { - // On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk) - AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); - - // Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position - // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordinates are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk); - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk); - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk); - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 2), a_Chunk); - - return; - } - - // Not on boundary, just alert this chunk for speed - AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneTorch(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState) -{ - static const struct // Define which directions the torch can power - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0}, - {-1, 0, 0}, - { 0, 0, 1}, - { 0, 0, -1}, - { 0, 1, 0}, - } ; - - if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON) - { - // Check if the block the torch is on is powered - int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ; - AddFaceDirection(X, Y, Z, GetHandlerCompileTime<E_BLOCK_TORCH>::type::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on - - ChunkType * Neighbour = m_Chunk->GetRelNeighborChunk(X, Z); - if ((Neighbour == NULL) || !Neighbour->IsValid()) - { - return; - } - - if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour)) - { - // There was a match, torch goes off - m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); - return; - } - - // Torch still on, make all 4(X, Z) + 1(Y) sides powered - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) - { - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type)) - { - continue; - } - if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last) - { - if ( - IsMechanism(Type) && // Is it a mechanism? Not block/other torch etc. - (!Vector3i(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on - ) - { - SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ); - } - } - else - { - // Top side, power whatever is there, including blocks - SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ); - // Power all blocks surrounding block above torch - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YP); - } - } - - if (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) != 0x5) // Is torch standing on ground? If NOT (i.e. on wall), power block beneath - { - BLOCKTYPE Type = m_Chunk->GetBlock(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ); - - if (IsMechanism(Type)) // Still can't make a normal block powered though! - { - SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); - } - } - } - else - { - // Check if the block the torch is on is powered - int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ; - AddFaceDirection(X, Y, Z, GetHandlerCompileTime<E_BLOCK_TORCH>::type::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on - - ChunkType * Neighbour = m_Chunk->GetRelNeighborChunk(X, Z); - if ((Neighbour == NULL) || !Neighbour->IsValid()) - { - return; - } - - // See if off state torch can be turned on again - if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour)) - { - return; // Something matches, torch still powered - } - - // Block torch on not powered, can be turned on again! - m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); // Set self as powered -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneLever(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - if (IsLeverOn(Meta)) - { - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - - eBlockFace Dir = GetHandlerCompileTime<E_BLOCK_LEVER>::type::BlockMetaDataToBlockFace(Meta); - - Dir = ReverseBlockFace(Dir); - - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Dir); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - NIBBLETYPE MetaData = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - - if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) - { - if ((MetaData & 0x4) == 0) - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); - } - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); - } - } - else - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) - { - if ((MetaData & 0x4) != 0) - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & ~0x04); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); - } - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneButton(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - if (IsButtonOn(Meta)) - { - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - - eBlockFace Dir = GetHandlerCompileTime<E_BLOCK_STONE_BUTTON>::type::BlockMetaDataToBlockFace(Meta); - Dir = ReverseBlockFace(Dir); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Dir); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - static const struct // Define which directions the wire can receive power from - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0}, /* Wires on same level start */ - {-1, 0, 0}, - { 0, 0, 1}, - { 0, 0, -1}, /* Wires on same level stop */ - { 1, 1, 0}, /* Wires one higher, surrounding self start */ - {-1, 1, 0}, - { 0, 1, 1}, - { 0, 1, -1}, /* Wires one higher, surrounding self stop */ - { 1, -1, 0}, /* Wires one lower, surrounding self start */ - {-1, -1, 0}, - { 0, -1, 1}, - { 0, -1, -1}, /* Wires one lower, surrounding self stop */ - } ; - - static const struct // Define which directions the wire will check for repeater prescence - { - int x, y, z; - } gSideCoords[] = - { - { 1, 0, 0 }, - {-1, 0, 0 }, - { 0, 0, 1 }, - { 0, 0, -1 }, - { 0, 1, 0 }, - }; - - // Check to see if directly beside a power source - unsigned char MyPower; - if (!IsWirePowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower)) - { - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0); - this->m_World.WakeUpSimulators(BlockX, a_RelBlockY, BlockZ); - return; - } - - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - - if (MyPower < 1) - { - return; - } - - MyPower--; - - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power - { - if ((i >= 4) && (i <= 7)) // If we are currently checking for wire surrounding ourself one block above... - { - BLOCKTYPE Type = 0; - if (a_RelBlockY + 1 >= cChunkDef::Height) - { - continue; - } - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, Type)) - { - continue; - } - if (cBlockInfo::IsSolid(Type)) // If there is something solid above us (wire cut off)... - { - continue; // We don't receive power from that wire - } - } - else if ((i >= 8) && (i <= 11)) // See above, but this is for wire below us - { - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY, a_RelBlockZ + gCrossCoords[i].z, Type)) - { - continue; - } - if (cBlockInfo::IsSolid(Type)) - { - continue; - } - } - - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type)) - { - continue; - } - if (Type == E_BLOCK_REDSTONE_WIRE) - { - SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - } - } - - for (size_t i = 0; i < ARRAYCOUNT(gSideCoords); i++) // Look for repeaters immediately surrounding self and try to power them - { - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, Type)) - { - continue; - } - if (Type == E_BLOCK_REDSTONE_REPEATER_OFF) - { - SetBlockPowered(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - } - } - - // Wire still powered, power blocks beneath - SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, MyPower); - - switch (GetWireDirection(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - case REDSTONE_NONE: - { - SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower); - break; - } - case REDSTONE_X_POS: - { - SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower); - break; - } - case REDSTONE_X_NEG: - { - SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower); - break; - } - case REDSTONE_Z_POS: - { - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower); - break; - } - case REDSTONE_Z_NEG: - { - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower); - break; - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState) -{ - /* Repeater Orientation Mini Guide: - =================================== - - | - | Z Axis - V - - X Axis ----> - - Repeater directions, values from a WorldType::GetBlockMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) lookup: - - East (Right) (X+): 0x1 - West (Left) (X-): 0x3 - North (Up) (Z-): 0x2 - South (Down) (Z+): 0x0 - // TODO: Add E_META_XXX enum entries for all meta values and update project with them - - Sun rises from East (X+) - - */ - - // Create a variable holding my meta to avoid multiple lookups. - NIBBLETYPE a_Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - bool IsOn = (a_MyState == E_BLOCK_REDSTONE_REPEATER_ON); - - if (!IsRepeaterLocked(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta)) // If we're locked, change nothing. Otherwise: - { - bool IsSelfPowered = IsRepeaterPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta); - if (IsSelfPowered && !IsOn) // Queue a power change if powered, but not on and not locked. - { - QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, true); - } - else if (!IsSelfPowered && IsOn) // Queue a power change if unpowered, on, and not locked. - { - QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false); - } - } -} - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneRepeaterDelays() -{ - for (typename RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end();) - { - if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? - { - int RelBlockX = itr->a_RelBlockPos.x; - int RelBlockY = itr->a_RelBlockPos.y; - int RelBlockZ = itr->a_RelBlockPos.z; - BLOCKTYPE Block; - NIBBLETYPE Meta; - m_Chunk->GetBlockTypeMeta(RelBlockX, RelBlockY, RelBlockZ, Block, Meta); - if (itr->ShouldPowerOn) - { - if (Block != E_BLOCK_REDSTONE_REPEATER_ON) // For performance - { - m_Chunk->SetBlock(itr->a_RelBlockPos, E_BLOCK_REDSTONE_REPEATER_ON, Meta); - } - - switch (Meta & 0x3) // We only want the direction (bottom) bits - { - case 0x0: - { - SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ - 1, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZM); - break; - } - case 0x1: - { - SetBlockPowered(RelBlockX + 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XP); - break; - } - case 0x2: - { - SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ + 1, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZP); - break; - } - case 0x3: - { - SetBlockPowered(RelBlockX - 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XM); - break; - } - } - } - else if (Block != E_BLOCK_REDSTONE_REPEATER_OFF) - { - m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); - } - itr = m_RepeatersDelayList->erase(itr); - } - else - { - LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); - itr->a_ElapsedTicks++; - itr++; - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandlePiston(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - if (IsPistonPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7)) // We only want the bottom three bits (4th controls extended-ness) - { - GetHandlerCompileTime<E_BLOCK_PISTON>::type::ExtendPiston(BlockX, a_RelBlockY, BlockZ, &this->m_World); - } - else - { - GetHandlerCompileTime<E_BLOCK_PISTON>::type::RetractPiston(BlockX, a_RelBlockY, BlockZ, &this->m_World); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleDropSpenser(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - class cSetPowerToDropSpenser : - public cRedstonePoweredCallback - { - bool m_IsPowered; - public: - cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} - - virtual bool Item(cRedstonePoweredEntity * a_DropSpenser) override - { - a_DropSpenser->SetRedstonePower(m_IsPowered); - return false; - } - } DrSpSP (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); - - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, DrSpSP); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneLamp(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState) -{ - if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF) - { - if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0); - } - } - else - { - if (!AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0); - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTNT(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - m_Chunk->BroadcastSoundEffect("game.tnt.primed", (double)BlockX, (double)a_RelBlockY, (double)BlockZ, 0.5f, 0.6f); - m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_AIR, 0); - this->m_World.SpawnPrimedTNT(BlockX + 0.5, a_RelBlockY + 0.5, BlockZ + 0.5); // 80 ticks to boom - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleDoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - typedef typename GetHandlerCompileTime<E_BLOCK_WOODEN_DOOR>::type DoorHandler; - - if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) - { - cChunkInterface ChunkInterface(this->m_World.GetChunkMap()); - if (!DoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ)) - { - DoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, true); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); - } - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); - } - } - else - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) - { - cChunkInterface ChunkInterface(this->m_World.GetChunkMap()); - if (DoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ)) - { - DoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, false); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); - } - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleCommandBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - class cSetPowerToCommandBlock : - public cRedstonePoweredCallback - { - bool m_IsPowered; - public: - cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} - - virtual bool Item(cRedstonePoweredEntity * a_CommandBlock) override - { - a_CommandBlock->SetRedstonePower(m_IsPowered); - return false; - } - } CmdBlockSP (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); - - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, CmdBlockSP); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRail(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType) -{ - switch (a_MyType) - { - case E_BLOCK_DETECTOR_RAIL: - { - if ((m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x08) == 0x08) - { - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_MyType); - } - break; - } - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_POWERED_RAIL: - { - if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0x08); - } - else - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x07); - } - break; - } - default: LOGD("Unhandled type of rail in %s", __FUNCTION__); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTrapdoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) - { - this->m_World.SetTrapdoorOpen(BlockX, a_RelBlockY, BlockZ, true); - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); - } - } - else - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) - { - this->m_World.SetTrapdoorOpen(BlockX, a_RelBlockY, BlockZ, false); - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - bool m_bAreCoordsPowered = AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - - if (m_bAreCoordsPowered) - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) - { - class cSetPowerToNoteBlock : - public cRedstonePoweredCallback - { - public: - cSetPowerToNoteBlock() {} - - virtual bool Item(cRedstonePoweredEntity * a_NoteBlock) override - { - a_NoteBlock->SetRedstonePower(true); - return false; - } - } NoteBlockSP; - - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, NoteBlockSP); - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); - } - } - else - { - if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) - { - SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX, BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); - - if (!this->m_World.IsChunkLighted(ChunkX, ChunkZ)) - { - this->m_World.QueueLightChunk(ChunkX, ChunkZ); - } - else - { - if (m_Chunk->GetTimeAlteredLight(this->m_World.GetBlockSkyLight(BlockX, a_RelBlockY + 1, BlockZ)) > 8) - { - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - } - else - { - WakeUp(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - switch (a_MyType) - { - case E_BLOCK_STONE_PRESSURE_PLATE: - { - // MCS feature - stone pressure plates can only be triggered by players :D - cPlayer * a_Player = this->m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.5f, false); - - if (a_Player != NULL) - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1); - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); - } - else - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } - break; - } - case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: - { - class cPressurePlateCallback : - public cEntityCallback - { - public: - cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : - m_NumberOfEntities(0), - m_X(a_BlockX), - m_Y(a_BlockY), - m_Z(a_BlockZ) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - Vector3f EntityPos = a_Entity->GetPosition(); - Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); - double Distance = (EntityPos - BlockPos).Length(); - - if (Distance <= 0.5) - { - m_NumberOfEntities++; - } - return false; - } - - bool GetPowerLevel(unsigned char & a_PowerLevel) const - { - a_PowerLevel = std::min(m_NumberOfEntities, MAX_POWER_LEVEL); - return (a_PowerLevel > 0); - } - - protected: - int m_NumberOfEntities; - - int m_X; - int m_Y; - int m_Z; - }; - - cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); - this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); - - unsigned char Power; - NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - if (PressurePlateCallback.GetPowerLevel(Power)) - { - if (Meta == E_META_PRESSURE_PLATE_RAISED) - { - m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); - } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); - } - else - { - if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) - { - m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); - } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } - - break; - } - case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: - { - class cPressurePlateCallback : - public cEntityCallback - { - public: - cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : - m_NumberOfEntities(0), - m_X(a_BlockX), - m_Y(a_BlockY), - m_Z(a_BlockZ) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - Vector3f EntityPos = a_Entity->GetPosition(); - Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); - double Distance = (EntityPos - BlockPos).Length(); - - if (Distance <= 0.5) - { - m_NumberOfEntities++; - } - return false; - } - - bool GetPowerLevel(unsigned char & a_PowerLevel) const - { - a_PowerLevel = std::min((int)ceil(m_NumberOfEntities / 10.f), MAX_POWER_LEVEL); - return (a_PowerLevel > 0); - } - - protected: - int m_NumberOfEntities; - - int m_X; - int m_Y; - int m_Z; - }; - - cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); - this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); - - unsigned char Power; - NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - if (PressurePlateCallback.GetPowerLevel(Power)) - { - if (Meta == E_META_PRESSURE_PLATE_RAISED) - { - m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); - } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); - } - else - { - if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) - { - m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); - } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } - - break; - } - case E_BLOCK_WOODEN_PRESSURE_PLATE: - { - class cPressurePlateCallback : - public cEntityCallback - { - public: - cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : - m_FoundEntity(false), - m_X(a_BlockX), - m_Y(a_BlockY), - m_Z(a_BlockZ) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - Vector3f EntityPos = a_Entity->GetPosition(); - Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); - double Distance = (EntityPos - BlockPos).Length(); - - if (Distance <= 0.5) - { - m_FoundEntity = true; - return true; // Break out, we only need to know for plates that at least one entity is on top - } - return false; - } - - bool FoundEntity(void) const - { - return m_FoundEntity; - } - - protected: - bool m_FoundEntity; - - int m_X; - int m_Y; - int m_Z; - } ; - - cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); - this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); - - NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - if (PressurePlateCallback.FoundEntity()) - { - if (Meta == E_META_PRESSURE_PLATE_RAISED) - { - m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); - } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); - } - else - { - if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) - { - m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); - } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } - break; - } - default: - { - LOGD("Unimplemented pressure plate type %s in cRedstoneSimulator", ItemToFullString(cItem(a_MyType)).c_str()); - break; - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; - int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; - int RelX = a_RelBlockX, RelZ = a_RelBlockZ; - bool FoundActivated = false; - eBlockFace FaceToGoTowards = GetHandlerCompileTime<E_BLOCK_TRIPWIRE_HOOK>::type::MetadataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); - - for (int i = 0; i < 40; ++i) // Tripwires can be connected up to 40 blocks - { - BLOCKTYPE Type; - NIBBLETYPE Meta; - - AddFaceDirection(RelX, a_RelBlockY, RelZ, FaceToGoTowards); - m_Chunk->UnboundedRelGetBlock(RelX, a_RelBlockY, RelZ, Type, Meta); - - if (Type == E_BLOCK_TRIPWIRE) - { - if (Meta == 0x1) - { - FoundActivated = true; - } - } - else if (Type == E_BLOCK_TRIPWIRE_HOOK) - { - if (ReverseBlockFace( GetHandlerCompileTime<E_BLOCK_TRIPWIRE_HOOK>::type::MetadataToDirection(Meta)) == FaceToGoTowards) - { - // Other hook facing in opposite direction - circuit completed! - break; - } - else - { - // Tripwire hook not connected at all, AND away all the power state bits - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - return; - } - } - else - { - // Tripwire hook not connected at all, AND away all the power state bits - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - return; - } - } - - if (FoundActivated) - { - // Connected and activated, set the 3rd and 4th highest bits - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0xC); - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - } - else - { - // Connected but not activated, AND away the highest bit - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7) | 0x4); - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } -} - -template <class ChestType> -class cGetTrappedChestPlayers : - public cItemCallback<ChestType> -{ -public: - cGetTrappedChestPlayers(void) : - m_NumberOfPlayers(0) - { - } - - virtual ~cGetTrappedChestPlayers() - { - } - - virtual bool Item(ChestType * a_Chest) override - { - ASSERT(a_Chest->GetBlockType() == E_BLOCK_TRAPPED_CHEST); - m_NumberOfPlayers = a_Chest->GetNumberOfPlayers(); - return (m_NumberOfPlayers <= 0); - } - - unsigned char GetPowerLevel(void) const - { - return std::min(m_NumberOfPlayers, MAX_POWER_LEVEL); - } - -private: - int m_NumberOfPlayers; - -}; - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - cGetTrappedChestPlayers<ChestType> GTCP; - - int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; - int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; - if (m_Chunk->DoWithChestAt(BlockX, a_RelBlockY, BlockZ, GTCP)) - { - SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, GTCP.GetPowerLevel()); - } - else - { - SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; - int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; - - class cTripwireCallback : - public cEntityCallback - { - public: - cTripwireCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : - m_FoundEntity(false), - m_X(a_BlockX), - m_Y(a_BlockY), - m_Z(a_BlockZ) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - cBoundingBox bbWire(m_X, m_X + 1, m_Y, m_Y + 0.1, m_Z, m_Z + 1); - cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); - - if (bbEntity.DoesIntersect(bbWire)) - { - m_FoundEntity = true; - return true; // One entity is sufficient to trigger the wire - } - return false; - } - - bool FoundEntity(void) const - { - return m_FoundEntity; - } - - protected: - bool m_FoundEntity; - - int m_X; - int m_Y; - int m_Z; - }; - - cTripwireCallback TripwireCallback(BlockX, a_RelBlockY, BlockZ); - this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), TripwireCallback); - - if (TripwireCallback.FoundEntity()) - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1); - } - else - { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, ChunkType * a_Chunk) -{ - // Torches want to access neighbour's data when on a wall, hence the extra chunk parameter - - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - for (typename PoweredBlocksList::const_iterator itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.begin(); itr != ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.end(); ++itr) // Check powered list - { - if (itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) - { - return true; - } - } - return false; -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list - { - if (itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) - { - return true; - } - } - return false; -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsRepeaterPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta) -{ - // Repeaters cannot be powered by any face except their back; verify that this is true for a source - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - - for (typename PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; } - - switch (a_Meta & 0x3) - { - case 0x0: - { - // Flip the coords to check the back of the repeater - if (itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ + 1))) { return true; } - break; - } - case 0x1: - { - if (itr->a_SourcePos.Equals(Vector3i(BlockX - 1, a_RelBlockY, BlockZ))) { return true; } - break; - } - case 0x2: - { - if (itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ - 1))) { return true; } - break; - } - case 0x3: - { - if (itr->a_SourcePos.Equals(Vector3i(BlockX + 1, a_RelBlockY, BlockZ))) { return true; } - break; - } - } - } - - for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; } - - switch (a_Meta & 0x3) - { - case 0x0: - { - if (itr->a_MiddlePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ + 1))) { return true; } - break; - } - case 0x1: - { - if (itr->a_MiddlePos.Equals(Vector3i(BlockX - 1, a_RelBlockY, BlockZ))) { return true; } - break; - } - case 0x2: - { - if (itr->a_MiddlePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ - 1))) { return true; } - break; - } - case 0x3: - { - if (itr->a_MiddlePos.Equals(Vector3i(BlockX + 1, a_RelBlockY, BlockZ))) { return true; } - break; - } - } - } - return false; // Couldn't find power source behind repeater -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsRepeaterLocked(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta) -{ - switch (a_Meta & 0x3) // We only want the 'direction' part of our metadata - { - // If the repeater is looking up or down (If parallel to the Z axis) - case 0x0: - case 0x2: - { - // Check if eastern(right) neighbor is a powered on repeater who is facing us - BLOCKTYPE Block = 0; - NIBBLETYPE OtherRepeaterDir = 0; - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) // Is right neighbor a powered repeater? - { - if ((OtherRepeaterDir & 0x03) == 0x3) - { - return true; - } // If so, I am latched/locked - } - - // Check if western(left) neighbor is a powered on repeater who is facing us - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) - { - if ((OtherRepeaterDir & 0x03) == 0x1) - { - return true; - } // If so, I am latched/locked - } - - break; - } - - // If the repeater is looking left or right (If parallel to the x axis) - case 0x1: - case 0x3: - { - // Check if southern(down) neighbor is a powered on repeater who is facing us - BLOCKTYPE Block = 0; - NIBBLETYPE OtherRepeaterDir = 0; - - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) - { - if ((OtherRepeaterDir & 0x30 ) == 0x00) - { - return true; - } // If so, am latched/locked - } - - // Check if northern(up) neighbor is a powered on repeater who is facing us - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) - { - if ((OtherRepeaterDir & 0x03) == 0x02) - { - return true; - } // If so, I am latched/locked - } - - break; - } - } - - return false; // None of the checks succeeded, I am not a locked repeater -} - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsPistonPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta) -{ - // Pistons cannot be powered through their front face; this function verifies that a source meets this requirement - - eBlockFace Face = GetHandlerCompileTime<E_BLOCK_PISTON>::type::MetaDataToDirection(a_Meta); - int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; - int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; - - for (typename PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; } - - AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face); - - if (!itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) - { - return true; - } - - AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face, true); - } - - for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; } - - AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face); - - if (!itr->a_MiddlePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) - { - return true; - } - - AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face, true); - } - return false; // Source was in front of the piston's front face -} - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel) -{ - a_PowerLevel = 0; - int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; - int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; - - for (typename PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) // Check powered list - { - if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) - { - continue; - } - a_PowerLevel = std::max(itr->a_PowerLevel, a_PowerLevel); // Get the highest power level (a_PowerLevel is initialised already and there CAN be multiple levels for one block) - } - - for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list - { - if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) - { - continue; - } - - BLOCKTYPE Type = E_BLOCK_AIR; - int RelSourceX = itr->a_SourcePos.x - m_Chunk->GetPosX() * cChunkDef::Width; - int RelSourceZ = itr->a_SourcePos.z - m_Chunk->GetPosZ() * cChunkDef::Width; - if (!m_Chunk->UnboundedRelGetBlockType(RelSourceX, itr->a_SourcePos.y, RelSourceZ, Type) || (Type == E_BLOCK_REDSTONE_WIRE)) - { - continue; - } - a_PowerLevel = std::max(itr->a_PowerLevel, a_PowerLevel); - } - - return (a_PowerLevel != 0); // Answer the inital question: is the wire powered? -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::AreCoordsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool IsCurrentStatePowered) -{ - for (typename SimulatedPlayerToggleableList::const_iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr) - { - if (itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - if (itr->WasLastStatePowered != IsCurrentStatePowered) // Was the last power state different to the current? - { - return false; // It was, coordinates are no longer simulated - } - else - { - return true; // It wasn't, don't resimulate block, and allow players to toggle - } - } - } - return false; // Block wasn't even in the list, not simulated -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel) -{ - BLOCKTYPE MiddleBlock = 0; - switch (a_Direction) - { - case BLOCK_FACE_XM: - { - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, MiddleBlock)) - { - return; - } - - SetBlockLinkedPowered(a_RelBlockX - 2, a_RelBlockY, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - - break; - } - case BLOCK_FACE_XP: - { - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, MiddleBlock)) - { - return; - } - - SetBlockLinkedPowered(a_RelBlockX + 2, a_RelBlockY, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - - break; - } - case BLOCK_FACE_YM: - { - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, MiddleBlock)) - { - return; - } - - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 2, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - - break; - } - case BLOCK_FACE_YP: - { - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, MiddleBlock)) - { - return; - } - - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 2, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - - break; - } - case BLOCK_FACE_ZM: - { - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, MiddleBlock)) - { - return; - } - - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 2, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - - break; - } - case BLOCK_FACE_ZP: - { - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, MiddleBlock)) - { - return; - } - - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 2, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel); - - break; - } - default: - { - ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); // Zombies, that wasn't supposed to happen... - break; - } - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel) -{ - static const struct - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0 }, - { -1, 0, 0 }, - { 0, 0, 1 }, - { 0, 0, -1 }, - { 0, 1, 0 }, - { 0, -1, 0 } - }; - - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through struct to power all directions - { - SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_PowerLevel); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - int SourceX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelSourceX; - int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ; - - ChunkType * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); // Adjust coordinates for the later call using these values - if ((Neighbour == NULL) || !Neighbour->IsValid()) - { - return; - } - - PoweredBlocksList & Powered = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_PoweredBlocks; // We need to insert the value into the chunk who owns the block position - for (typename PoweredBlocksList::iterator itr = Powered.begin(); itr != Powered.end(); ++itr) - { - if ( - itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) && - itr->a_SourcePos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ)) - ) - { - // Check for duplicates, update power level, don't add a new listing - itr->a_PowerLevel = a_PowerLevel; - return; - } - } - - // No need to get neighbouring chunk as we can guarantee that when something is powering us, the entry will be in our chunk - // TODO: on C++11 support, change this to a llama function pased to a std::remove_if - for (typename PoweredBlocksList::iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) - { - if ( - itr->a_BlockPos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ)) && - itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) && - (m_Chunk->GetBlock(a_RelSourceX, a_RelSourceY, a_RelSourceZ) == E_BLOCK_REDSTONE_WIRE) - ) - { - BLOCKTYPE Block; - NIBBLETYPE Meta; - Neighbour->GetBlockTypeMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Block, Meta); - - if (Block == E_BLOCK_REDSTONE_WIRE) - { - if (Meta < a_PowerLevel) - { - m_PoweredBlocks->erase(itr); // Powering source with higher power level, allow it - break; - } - else - { - // Powered wires try to power their source - don't let them! - return; - } - } - } - } - - sPoweredBlocks RC; - RC.a_BlockPos = Vector3i(BlockX, a_RelBlockY, BlockZ); - RC.a_SourcePos = Vector3i(SourceX, a_RelSourceY, SourceZ); - RC.a_PowerLevel = a_PowerLevel; - Powered.push_back(RC); - Neighbour->SetIsRedstoneDirty(true); - m_Chunk->SetIsRedstoneDirty(true); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetBlockLinkedPowered( - int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, - int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, - int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, - BLOCKTYPE a_MiddleBlock, unsigned char a_PowerLevel - ) -{ - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - int MiddleX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelMiddleX; - int MiddleZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelMiddleZ; - int SourceX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelSourceX; - int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ; - - if (!IsViableMiddleBlock(a_MiddleBlock)) - { - return; - } - - ChunkType * Neighbour = m_Chunk->GetNeighborChunk(BlockX, BlockZ); - if ((Neighbour == NULL) || !Neighbour->IsValid()) - { - return; - } - - LinkedBlocksList & Linked = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_LinkedBlocks; - for (typename LinkedBlocksList::iterator itr = Linked.begin(); itr != Linked.end(); ++itr) // Check linked powered list - { - if ( - itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) && - itr->a_MiddlePos.Equals(Vector3i(MiddleX, a_RelMiddleY, MiddleZ)) && - itr->a_SourcePos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ)) - ) - { - // Check for duplicates, update power level, don't add a new listing - itr->a_PowerLevel = a_PowerLevel; - return; - } - } - - sLinkedPoweredBlocks RC; - RC.a_BlockPos = Vector3i(BlockX, a_RelBlockY, BlockZ); - RC.a_MiddlePos = Vector3i(MiddleX, a_RelMiddleY, MiddleZ); - RC.a_SourcePos = Vector3i(SourceX, a_RelSourceY, SourceZ); - RC.a_PowerLevel = a_PowerLevel; - Linked.push_back(RC); - Neighbour->SetIsRedstoneDirty(true); - m_Chunk->SetIsRedstoneDirty(true); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered) -{ - for (typename SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr) - { - if (!itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - continue; - } - - if (itr->WasLastStatePowered != WasLastStatePowered) - { - // If power states different, update listing - itr->WasLastStatePowered = WasLastStatePowered; - return; - } - else - { - // If states the same, just ignore - return; - } - } - - // We have arrive here; no block must be in list - add one - sSimulatedPlayerToggleableList RC; - RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - RC.WasLastStatePowered = WasLastStatePowered; - m_SimulatedPlayerToggleableBlocks->push_back(RC); -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn) -{ - for (typename RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr) - { - if (itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry - { - return false; - } - - // Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit - itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description - itr->a_ElapsedTicks = 0; - itr->ShouldPowerOn = ShouldPowerOn; - return false; - } - } - - // Self not in list, add self to list - sRepeatersDelayList RC; - RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - - // Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.) - // Multiply by 2 because in MCS, 1 redstone tick = 1 world tick, but in Vanilla, 1 redstone tick = 2 world ticks, and we need to maintain compatibility - RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; - - RC.a_ElapsedTicks = 0; - RC.ShouldPowerOn = ShouldPowerOn; - m_RepeatersDelayList->push_back(RC); - return true; -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetSourceUnpowered(int a_SourceX, int a_SourceY, int a_SourceZ, ChunkType * a_Chunk, bool a_IsFirstCall) -{ - if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid - { - if ((a_Chunk == NULL) || !a_Chunk->IsValid()) - { - return; - } - } - // TODO: on C++11 support, change both of these to llama functions pased to a std::remove_if - - for (typename PoweredBlocksList::iterator itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.begin(); itr != ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.end();) - { - if (itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ))) - { - itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - continue; - } - ++itr; - } - for (typename LinkedBlocksList::iterator itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks.begin(); itr != ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks.end();) - { - if (itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ))) - { - itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks.erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - continue; - } - ++itr; - } - - if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_SourceX, a_SourceY, a_SourceZ)) - { - // +- 2 to accomodate linked powered blocks - SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX - 2, a_SourceZ), false); - SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX + 2, a_SourceZ), false); - SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX, a_SourceZ - 2), false); - SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX, a_SourceZ + 2), false); - } -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -typename cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::eRedstoneDirection cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::GetWireDirection(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) -{ - int Dir = REDSTONE_NONE; - - BLOCKTYPE NegX = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, NegX)) - { - if (IsPotentialSource(NegX)) - { - Dir |= (REDSTONE_X_POS); - } - } - - BLOCKTYPE PosX = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, PosX)) - { - if (IsPotentialSource(PosX)) - { - Dir |= (REDSTONE_X_NEG); - } - } - - BLOCKTYPE NegZ = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, NegZ)) - { - if (IsPotentialSource(NegZ)) - { - if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_POS; - } - } - - BLOCKTYPE PosZ = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, PosZ)) - { - if (IsPotentialSource(PosZ)) - { - if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_NEG; - } - } - return (eRedstoneDirection)Dir; -} - - - - -template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType> -bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsLeverOn(NIBBLETYPE a_BlockMeta) -{ - // Extract the ON bit from metadata and return if true if it is set: - return ((a_BlockMeta & 0x8) == 0x8); -} - - - - diff --git a/src/Simulator/NoopRedstoneSimulator.h b/src/Simulator/NoopRedstoneSimulator.h index 832b26dfc..4e05529f5 100644 --- a/src/Simulator/NoopRedstoneSimulator.h +++ b/src/Simulator/NoopRedstoneSimulator.h @@ -8,9 +8,9 @@ class cRedstoneNoopSimulator : - public cRedstoneSimulator<cChunk, cWorld> + public cRedstoneSimulator { - typedef cRedstoneSimulator<cChunk, cWorld> super; + typedef cRedstoneSimulator super; public: cRedstoneNoopSimulator(cWorld & a_World) : diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h index 6104d39b4..b0ad08aa4 100644 --- a/src/Simulator/RedstoneSimulator.h +++ b/src/Simulator/RedstoneSimulator.h @@ -13,14 +13,14 @@ public: inline cRedstoneSimulatorChunkData::~cRedstoneSimulatorChunkData() {} -template <class ChunkType, class WorldType> + class cRedstoneSimulator : - public cSimulator<ChunkType, WorldType> + public cSimulator { - typedef cSimulator<ChunkType, WorldType> super; + typedef cSimulator super; public: - cRedstoneSimulator(WorldType & a_World) : + cRedstoneSimulator(cWorld & a_World) : super(a_World) { } diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h index feb82b4d5..93b1de8e2 100644 --- a/src/Simulator/SandSimulator.h +++ b/src/Simulator/SandSimulator.h @@ -11,7 +11,7 @@ typedef cCoordWithIntList cSandSimulatorChunkData; /// Despite the class name, this simulator takes care of all blocks that fall when suspended in the air. class cSandSimulator : - public cSimulator<cChunk, cWorld> + public cSimulator { public: cSandSimulator(cWorld & a_World, cIniFile & a_IniFile); diff --git a/src/Simulator/Simulator.cpp b/src/Simulator/Simulator.cpp index 29a1132ad..0175fd67d 100644 --- a/src/Simulator/Simulator.cpp +++ b/src/Simulator/Simulator.cpp @@ -6,13 +6,35 @@ #include "../Defines.h" #include "../Chunk.h" -#include "Simulator.inc" - #ifdef __clang__ #pragma clang diagnostic ignored "-Wweak-template-vtables" #endif // __clang__ -template class cSimulator<cChunk, cWorld>; + + +#include "Simulator.h" + + + + + +void cSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); + AddBlock(a_BlockX - 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 1, a_BlockZ)); + AddBlock(a_BlockX + 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 1, a_BlockZ)); + AddBlock(a_BlockX, a_BlockY, a_BlockZ - 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 1)); + AddBlock(a_BlockX, a_BlockY, a_BlockZ + 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 1)); + if (a_BlockY > 0) + { + AddBlock(a_BlockX, a_BlockY - 1, a_BlockZ, a_Chunk); + } + if (a_BlockY < cChunkDef::Height - 1) + { + AddBlock(a_BlockX, a_BlockY + 1, a_BlockZ, a_Chunk); + } +} + diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index 7cc2f1344..f28a07e35 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -3,24 +3,28 @@ #include "../Vector3.h" +class cWorld; +class cChunk; -template <class ChunkType, class WorldType> class cSimulator { public: - cSimulator(WorldType & a_World); - virtual ~cSimulator(); + cSimulator(cWorld & a_World) + : m_World(a_World) + { + } + + virtual ~cSimulator() {} /// Called in each tick, a_Dt is the time passed since the last tick, in msec virtual void Simulate(float a_Dt) = 0; /// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available - virtual void SimulateChunk(float a_Dt, int a_ChunkX, - int a_ChunkZ, ChunkType * a_Chunk) + virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { UNUSED(a_Dt); UNUSED(a_ChunkX); @@ -29,7 +33,7 @@ public: } /// Called when a block changes - virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk); + virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) = 0; @@ -37,9 +41,9 @@ protected: friend class cChunk; // Calls AddBlock() in its WakeUpSimulators() function, to speed things up /// Called to simulate a new block - virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) = 0; + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) = 0; - WorldType & m_World; + cWorld & m_World; } ; diff --git a/src/Simulator/Simulator.inc b/src/Simulator/Simulator.inc deleted file mode 100644 index 511a6b4c2..000000000 --- a/src/Simulator/Simulator.inc +++ /dev/null @@ -1,45 +0,0 @@ - - -#include "Simulator.h" - - - - -template <class ChunkType, class WorldType> -cSimulator<ChunkType, WorldType>::cSimulator(WorldType & a_World) - : m_World(a_World) -{ -} - - - - -template <class ChunkType, class WorldType> -cSimulator<ChunkType, WorldType>::~cSimulator() -{ -} - - - - -template <class ChunkType, class WorldType> -void cSimulator<ChunkType, WorldType>::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) -{ - AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); - AddBlock(a_BlockX - 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 1, a_BlockZ)); - AddBlock(a_BlockX + 1, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 1, a_BlockZ)); - AddBlock(a_BlockX, a_BlockY, a_BlockZ - 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 1)); - AddBlock(a_BlockX, a_BlockY, a_BlockZ + 1, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 1)); - if (a_BlockY > 0) - { - AddBlock(a_BlockX, a_BlockY - 1, a_BlockZ, a_Chunk); - } - if (a_BlockY < cChunkDef::Height - 1) - { - AddBlock(a_BlockX, a_BlockY + 1, a_BlockZ, a_Chunk); - } -} - - - - diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp index dafdcd239..918bac7a1 100644 --- a/src/Simulator/SimulatorManager.cpp +++ b/src/Simulator/SimulatorManager.cpp @@ -70,7 +70,7 @@ void cSimulatorManager::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk -void cSimulatorManager::RegisterSimulator(cSimulator<cChunk, cWorld> * a_Simulator, int a_Rate) +void cSimulatorManager::RegisterSimulator(cSimulator * a_Simulator, int a_Rate) { m_Simulators.push_back(std::make_pair(a_Simulator, a_Rate)); } diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h index 185141764..31a709316 100644 --- a/src/Simulator/SimulatorManager.h +++ b/src/Simulator/SimulatorManager.h @@ -37,10 +37,10 @@ public: void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); - void RegisterSimulator(cSimulator<cChunk, cWorld> * a_Simulator, int a_Rate); // Takes ownership of the simulator object! + void RegisterSimulator(cSimulator * a_Simulator, int a_Rate); // Takes ownership of the simulator object! protected: - typedef std::vector <std::pair<cSimulator<cChunk, cWorld> *, int> > cSimulators; + typedef std::vector <std::pair<cSimulator *, int> > cSimulators; cWorld & m_World; cSimulators m_Simulators; diff --git a/src/World.cpp b/src/World.cpp index 99701e11b..b3d34b48c 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1520,7 +1520,21 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy } return true; } - + + case E_BLOCK_COCOA_POD: + { + NIBBLETYPE TypeMeta = BlockMeta & 0x03; + int GrowState = BlockMeta >> 2; + + if (GrowState < 2) + { + GrowState++; + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, (NIBBLETYPE) (GrowState << 2 | TypeMeta)); + BroadcastSoundParticleEffect(2005, a_BlockX, a_BlockY, a_BlockZ, 0); + } + return true; + } + case E_BLOCK_CROPS: { if (a_IsByBonemeal && !m_IsCropsBonemealable) @@ -3280,7 +3294,6 @@ int cWorld::SpawnMobFinalize(cMonster * a_Monster) return -1; } - BroadcastSpawnEntity(*a_Monster); cPluginManager::Get()->CallHookSpawnedMonster(*this, *a_Monster); return a_Monster->GetUniqueID(); @@ -3373,7 +3386,7 @@ void cWorld::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicke -cRedstoneSimulator<cChunk, cWorld> * cWorld::InitializeRedstoneSimulator(cIniFile & a_IniFile) +cRedstoneSimulator * cWorld::InitializeRedstoneSimulator(cIniFile & a_IniFile) { AString SimulatorName = a_IniFile.GetValueSet("Physics", "RedstoneSimulator", "Incremental"); @@ -3383,11 +3396,11 @@ cRedstoneSimulator<cChunk, cWorld> * cWorld::InitializeRedstoneSimulator(cIniFil SimulatorName = "Incremental"; } - cRedstoneSimulator<cChunk, cWorld> * res = nullptr; + cRedstoneSimulator * res = nullptr; if (NoCaseCompare(SimulatorName, "Incremental") == 0) { - res = MakeIncrementalRedstoneSimulator(*this); + res = new cIncrementalRedstoneSimulator(*this); } else if (NoCaseCompare(SimulatorName, "noop") == 0) { diff --git a/src/World.h b/src/World.h index 6f28ba534..4f24280a4 100644 --- a/src/World.h +++ b/src/World.h @@ -34,7 +34,6 @@ class cFireSimulator; class cFluidSimulator; class cSandSimulator; -template <class ChunkType, class WorldType> class cRedstoneSimulator; class cItem; class cPlayer; @@ -518,7 +517,7 @@ public: inline cFluidSimulator * GetWaterSimulator(void) { return m_WaterSimulator; } inline cFluidSimulator * GetLavaSimulator (void) { return m_LavaSimulator; } - inline cRedstoneSimulator<cChunk, cWorld> * GetRedstoneSimulator(void) { return m_RedstoneSimulator; } + inline cRedstoneSimulator * GetRedstoneSimulator(void) { return m_RedstoneSimulator; } /** Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true */ bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp @@ -935,7 +934,7 @@ private: cFluidSimulator * m_WaterSimulator; cFluidSimulator * m_LavaSimulator; std::unique_ptr<cFireSimulator> m_FireSimulator; - cRedstoneSimulator<cChunk, cWorld> * m_RedstoneSimulator; + cRedstoneSimulator * m_RedstoneSimulator; cCriticalSection m_CSPlayers; cPlayerList m_Players; @@ -1079,7 +1078,7 @@ private: cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock); /** Creates a new redstone simulator.*/ - cRedstoneSimulator<cChunk, cWorld> * InitializeRedstoneSimulator(cIniFile & a_IniFile); + cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile); /** Adds the players queued in the m_PlayersToAdd queue into the m_Players list. Assumes it is called from the Tick thread. */ |