diff options
Diffstat (limited to '')
-rw-r--r-- | source/Simulator/RedstoneSimulator.cpp | 1627 | ||||
-rw-r--r-- | source/Simulator/RedstoneSimulator.h | 217 |
2 files changed, 926 insertions, 918 deletions
diff --git a/source/Simulator/RedstoneSimulator.cpp b/source/Simulator/RedstoneSimulator.cpp index 8526a888e..906961490 100644 --- a/source/Simulator/RedstoneSimulator.cpp +++ b/source/Simulator/RedstoneSimulator.cpp @@ -3,12 +3,10 @@ #include "RedstoneSimulator.h" #include "../BlockEntities/DropSpenserEntity.h" +#include "../Entities/TNTEntity.h" #include "../Blocks/BlockTorch.h" +#include "../Blocks/BlockDoor.h" #include "../Piston.h" -#include "../World.h" -#include "../BlockID.h" -#include "../Chunk.h" -#include "../Entities/TNTEntity.h" @@ -31,1104 +29,1024 @@ cRedstoneSimulator::~cRedstoneSimulator() -void cRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { - if (a_Chunk == NULL) + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) { return; } - int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; - int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; - - // Check if any close neighbor is redstone-related: - int MinY = (a_BlockY > 0) ? -1 : 0; - int MaxY = (a_BlockY < cChunkDef::Height - 1) ? 1 : 0; - for (int y = MinY; y <= MaxY; y++) for (int x = -1; x < 2; x++) for (int z = -1; z < 2; z++) + else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height)) { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_Chunk->UnboundedRelGetBlock(RelX + x, a_BlockY + y, RelZ + z, BlockType, BlockMeta)) - { - continue; - } - switch (BlockType) - { - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - 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_LEVER: - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_WOODEN_BUTTON: - case E_BLOCK_TRIPWIRE_HOOK: - { - m_Blocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); - return; - } - } // switch (BlockType) - } // for y, x, z - neighbors -} - - - + return; + } + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; -void cRedstoneSimulator::Simulate(float a_Dt) -{ - // Toggle torches on/off - while (!m_RefreshTorchesAround.empty()) + if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ))) { - Vector3i pos = m_RefreshTorchesAround.front(); - m_RefreshTorchesAround.pop_front(); - - RefreshTorchesAround(pos); + return; } - // Set repeaters to correct values, and decrement ticks - for (RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end();) + // Check for duplicates: + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) { - if (--itr->Ticks > 0) - { - // Not yet, move to next item in the list - ++itr; - continue; - } - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(itr->Position.x, itr->Position.y, itr->Position.z, BlockType, BlockMeta); - if (itr->bPowerOn && (BlockType == E_BLOCK_REDSTONE_REPEATER_OFF)) - { - m_World.FastSetBlock(itr->Position.x, itr->Position.y, itr->Position.z, E_BLOCK_REDSTONE_REPEATER_ON, BlockMeta); - m_Blocks.push_back(itr->Position); - } - else if (!itr->bPowerOn && (BlockType == E_BLOCK_REDSTONE_REPEATER_ON)) - { - m_World.FastSetBlock(itr->Position.x, itr->Position.y, itr->Position.z, E_BLOCK_REDSTONE_REPEATER_OFF, BlockMeta); - m_Blocks.push_back(itr->Position); - } - - if (itr->bPowerOffNextTime) - { - itr->bPowerOn = false; - itr->bPowerOffNextTime = false; - itr->Ticks = 10; // TODO: Look up actual ticks from block metadata - ++itr; - } - else + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) { - itr = m_SetRepeaters.erase(itr); + return; } } - // Handle changed blocks - { - cCSLock Lock(m_CS); - std::swap(m_Blocks, m_BlocksBuffer); - } - for (BlockList::iterator itr = m_BlocksBuffer.begin(); itr != m_BlocksBuffer.end(); ++itr) - { - HandleChange(*itr); - } - m_BlocksBuffer.clear(); + ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ)); } -void cRedstoneSimulator::RefreshTorchesAround(const Vector3i & a_BlockPos) +void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { - static Vector3i Surroundings [] = { - Vector3i(-1, 0, 0), - Vector3i(1, 0, 0), - Vector3i(0, 0,-1), - Vector3i(0, 0, 1), - Vector3i(0, 1, 0), // Also toggle torch on top - }; - BLOCKTYPE TargetBlockType = E_BLOCK_REDSTONE_TORCH_ON; - BLOCKTYPE TargetRepeaterType = E_BLOCK_REDSTONE_REPEATER_OFF; - if (IsPowered(a_BlockPos, true)) + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + if (ChunkData.empty()) + { + return; + } + + int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; + int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; + + for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;) { - TargetBlockType = E_BLOCK_REDSTONE_TORCH_OFF; - TargetRepeaterType = E_BLOCK_REDSTONE_REPEATER_ON; - //Make TNT Explode when it gets powered. - if (m_World.GetBlock(a_BlockPos) == E_BLOCK_TNT) + BLOCKTYPE BlockType = a_Chunk->GetBlock(dataitr->x, dataitr->y, dataitr->z); + if (!IsAllowedBlock(BlockType)) { - m_World.BroadcastSoundEffect("random.fuse", a_BlockPos.x * 8, a_BlockPos.y * 8, a_BlockPos.z * 8, 0.5f, 0.6f); - m_World.SpawnPrimedTNT(a_BlockPos.x + 0.5, a_BlockPos.y + 0.5, a_BlockPos.z + 0.5, 4); // 4 seconds to boom - m_World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_AIR, 0); + dataitr = ChunkData.erase(dataitr); + continue; } - //Turn a redstone lamp on when it gets powered. - if (m_World.GetBlock(a_BlockPos) == E_BLOCK_REDSTONE_LAMP_OFF) + + // Check to see if PoweredBlocks have invalid items (source is air or an unpowered source) + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end();) { - m_World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_ON, 0); + sPoweredBlocks & Change = *itr; + BLOCKTYPE SourceBlockType = m_World.GetBlock(Change.a_SourcePos); + + if (SourceBlockType != Change.a_SourceBlock) + { + itr = m_PoweredBlocks.erase(itr); + } + else if ( + // Changeable sources + ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (m_World.GetBlockMeta(Change.a_SourcePos) == 0)) || + ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(m_World.GetBlockMeta(Change.a_SourcePos))) || + ((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (m_World.GetBlockMeta(Change.a_SourcePos) & 0x08) == 0x08) || + (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(m_World.GetBlockMeta(Change.a_SourcePos)))) + ) + { + itr = m_PoweredBlocks.erase(itr); + } + else + { + itr++; + } } - //if (m_World.GetBlock(a_BlockPos) == E_BLOCK_DIRT) - //{ - // m_World.FastSetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_STONE, 0); - //} - } - else - { - //Turn a redstone lamp off when it gets powered. - if (m_World.GetBlock(a_BlockPos) == E_BLOCK_REDSTONE_LAMP_ON) + + // Check to see if LinkedPoweredBlocks have invalid items: source, block powered through, or power destination block has changed + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end();) { - m_World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_OFF, 0); + sLinkedPoweredBlocks & Change = *itr; + BLOCKTYPE SourceBlockType = m_World.GetBlock(Change.a_SourcePos); + BLOCKTYPE MiddleBlockType = m_World.GetBlock(Change.a_MiddlePos); + + if (SourceBlockType != Change.a_SourceBlock) + { + itr = m_LinkedPoweredBlocks.erase(itr); + } + else if (MiddleBlockType != Change.a_MiddleBlock) + { + itr = m_LinkedPoweredBlocks.erase(itr); + } + else if ( + // Things that can send power through a block but which depends on meta + ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (m_World.GetBlockMeta(Change.a_SourcePos) == 0)) || + ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(m_World.GetBlockMeta(Change.a_SourcePos))) || + (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(m_World.GetBlockMeta(Change.a_SourcePos)))) + ) + { + itr = m_LinkedPoweredBlocks.erase(itr); + } + else + { + itr++; + } } - //if (m_World.GetBlock(a_BlockPos) == E_BLOCK_STONE) - //{ - // m_World.FastSetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_DIRT, 0); - //} - } - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) - { - Vector3i TorchPos = a_BlockPos + Surroundings[i]; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(TorchPos.x, TorchPos.y, TorchPos.z, BlockType, BlockMeta); + // PoweredBlock list was fine, now to the actual handling + int a_X = BaseX + dataitr->x; + int a_Z = BaseZ + dataitr->z; switch (BlockType) { - case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break; + case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break; + case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break; + case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break; + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: { - if (BlockType != TargetBlockType) - { - if (cBlockTorchHandler::IsAttachedTo(TorchPos, BlockMeta, a_BlockPos)) - { - m_World.FastSetBlock(TorchPos.x, TorchPos.y, TorchPos.z, TargetBlockType, BlockMeta); - m_Blocks.push_back(TorchPos); - } - } + HandleRedstoneTorch(a_X, dataitr->y, a_Z, BlockType); + break; + } + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_WOODEN_BUTTON: + { + HandleRedstoneButton(a_X, dataitr->y, a_Z, BlockType); break; } - case E_BLOCK_REDSTONE_REPEATER_ON: case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: { - if ((BlockType != TargetRepeaterType) && IsRepeaterPointingAway(TorchPos, BlockMeta, a_BlockPos)) - { - SetRepeater(TorchPos, 10, (TargetRepeaterType == E_BLOCK_REDSTONE_REPEATER_ON)); - } + HandleRedstoneRepeater(a_X, dataitr->y, a_Z, BlockType); break; } - } // switch (BlockType) - } // for i - Surroundings[] -} - - - - - -void cRedstoneSimulator::HandleChange(const Vector3i & a_BlockPos) -{ - std::deque< Vector3i > SpreadStack; - - static const Vector3i Surroundings[] = { - Vector3i(1, 0, 0), - Vector3i(1, 1, 0), - Vector3i(1,-1, 0), - Vector3i(-1, 0, 0), - Vector3i(-1, 1, 0), - Vector3i(-1,-1, 0), - Vector3i(0, 0, 1), - Vector3i(0, 1, 1), - Vector3i(0,-1, 1), - Vector3i(0, 0,-1), - Vector3i(0, 1,-1), - Vector3i(0,-1,-1), - Vector3i(0,-1, 0), - }; - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - - // First check whether torch should be on or off - switch (BlockType) - { - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_REDSTONE_TORCH_OFF: - { - static const Vector3i Surroundings [] = { - Vector3i(-1, 0, 0), - Vector3i(1, 0, 0), - Vector3i(0, 0,-1), - Vector3i(0, 0, 1), - Vector3i(0,-1, 0), - }; - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: { - Vector3i pos = a_BlockPos + Surroundings[i]; - BLOCKTYPE OtherBlock = m_World.GetBlock(pos); - if ( - (OtherBlock != E_BLOCK_AIR) && - (OtherBlock != E_BLOCK_REDSTONE_TORCH_ON) && - (OtherBlock != E_BLOCK_REDSTONE_TORCH_OFF) - ) - { - RefreshTorchesAround(pos); - } + HandlePiston(a_X, dataitr->y, a_Z); + break; } - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - break; - } // case "torches" - - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - { - // Check if repeater is powered by a 'powered block' (not wires/torch) - Vector3i Direction = GetRepeaterDirection(BlockMeta); - Vector3i pos = a_BlockPos - Direction; // NOTE: It's minus Direction - BLOCKTYPE OtherBlock = m_World.GetBlock(pos); - if ( - (OtherBlock != E_BLOCK_AIR) && - (OtherBlock != E_BLOCK_REDSTONE_TORCH_ON) && - (OtherBlock != E_BLOCK_REDSTONE_TORCH_OFF) && - (OtherBlock != E_BLOCK_REDSTONE_WIRE) - ) + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: { - RefreshTorchesAround(pos); + HandleRedstoneLamp(a_X, dataitr->y, a_Z, BlockType); + break; } - else + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: { - SetRepeater(a_BlockPos, 10, IsPowered(a_BlockPos, false)); + HandleDropSpenser(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + { + HandleDoor(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + HandleRail(a_X, dataitr->y, a_Z, BlockType); + break; } - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - break; } - } // switch (BlockType) - BlockList Sources; - switch (BlockType) + ++dataitr; + } +} + + + + + +void cRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + static const struct // Define which directions the torch can power { - case E_BLOCK_REDSTONE_TORCH_ON: + 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_BlockX; int Y = a_BlockY; int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on + + if (AreCoordsPowered(X, Y, Z)) { - // If torch is still on, use it as a source - Sources.push_back(a_BlockPos); - break; + // There was a match, torch goes off + // FastSetBlock so the server doesn't fail an assert -_- + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); + return; } - - case E_BLOCK_REDSTONE_REPEATER_ON: + + // Torch still on, make all 4(X, Z) + 1(Y) sides powered + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) { - // Repeater only spreads charge right in front, and up to one block up: - static const Vector3i Surroundings [] = { - Vector3i(0, 0, 0), - Vector3i(0, 1, 0), - }; - Vector3i Direction = GetRepeaterDirection(BlockMeta); - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + BLOCKTYPE Type = m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z); + if (i < ARRAYCOUNT(gCrossCoords) - 1) // Sides of torch, not top (top is last) { - Vector3i pos = a_BlockPos + Direction + Surroundings[i]; - if (PowerBlock(pos, a_BlockPos, 0xf)) + if ( + ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) && // Is it a mechanism or wire? Not block/other torch etc. + (!Vector3i(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on + ) { - SpreadStack.push_back(pos); + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); } } - break; - } // case E_BLOCK_REDSTONE_REPEATER_ON - - case E_BLOCK_LEVER: - { - // Adding lever to the source queue - if (cRedstoneSimulator::IsLeverOn(BlockMeta)) + else { - Sources.push_back(a_BlockPos); + // Top side, power whatever is there, including blocks + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); } - break; - } // case E_BLOCK_LEVER - } // switch (BlockType) + } - // Power all blocks legally connected to the sources - if (BlockType != E_BLOCK_REDSTONE_REPEATER_ON) - { - BlockList NewSources = RemoveCurrent(a_BlockPos); - Sources.insert(Sources.end(), NewSources.begin(), NewSources.end()); - while (!Sources.empty()) + if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0x5) // Is torch standing on ground? If not (i.e. on wall), power block beneath { - Vector3i SourcePos = Sources.back(); - Sources.pop_back(); + BLOCKTYPE Type = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(SourcePos.x, SourcePos.y, SourcePos.z, BlockType, BlockMeta); - switch (BlockType) + if ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) // Still can't make a normal block powered though! { - case E_BLOCK_LEVER: // Treating lever as a torch - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - { - static Vector3i Surroundings [] = { - Vector3i(-1, 0, 0), - Vector3i(1, 0, 0), - Vector3i(0, 0,-1), - Vector3i(0, 0, 1), - }; - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) - { - Vector3i OtherPos = SourcePos + Surroundings[i]; - if (PowerBlock(OtherPos, a_BlockPos, 0xf)) - { - SpreadStack.push_back(OtherPos); // Changed, so add to stack - } - } - break; - } - - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - { - static Vector3i Surroundings [] = { - Vector3i(0, 0, 0), - Vector3i(0, 1, 0), - }; - Vector3i Direction = GetRepeaterDirection(BlockMeta); - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) - { - Vector3i pos = SourcePos + Direction + Surroundings[i]; - if (PowerBlock(pos, a_BlockPos, 0xf)) - { - SpreadStack.push_back(pos); - } - } - break; - } - } // switch (BlockType) - } // while (Sources[]) - } // if (!repeater_on) - - // Do a floodfill - while (!SpreadStack.empty()) + SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + } + } + } + else { - Vector3i pos = SpreadStack.back(); - SpreadStack.pop_back(); - NIBBLETYPE Meta = m_World.GetBlockMeta(pos); + // Check if the block the torch is on is powered + int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + // See if off state torch can be turned on again + if (AreCoordsPowered(X, Y, Z)) { - Vector3i OtherPos = pos + Surroundings[i]; - if (PowerBlock(OtherPos, pos, Meta - 1)) - { - SpreadStack.push_back(OtherPos); // Changed, so add to stack - } + return; // Something matches, torch still powered } + + // Block torch on not powered, can be turned on again! + // FastSetBlock so the server doesn't fail an assert -_- + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); } + return; +} - // Only after a redstone area has been completely simulated the redstone entities can react - while (!m_RefreshPistons.empty()) - { - Vector3i pos = m_RefreshPistons.back(); - m_RefreshPistons.pop_back(); - BLOCKTYPE BlockType = m_World.GetBlock(pos); - switch (BlockType) - { - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - if (IsPowered(pos)) - { - cPiston Piston(&m_World); - Piston.ExtendPiston(pos.x, pos.y, pos.z); - } - else - { - cPiston Piston(&m_World); - Piston.RetractPiston(pos.x, pos.y, pos.z); - } - break; - } - } // switch (BlockType) - } // while (m_RefreshPistons[]) - while (!m_RefreshDropSpensers.empty()) - { - Vector3i pos = m_RefreshDropSpensers.back(); - m_RefreshDropSpensers.pop_back(); - BLOCKTYPE BlockType = m_World.GetBlock(pos); - if ((BlockType == E_BLOCK_DISPENSER) || (BlockType == E_BLOCK_DROPPER)) - { - class cSetPowerToDropSpenser : - public cDropSpenserCallback - { - bool m_IsPowered; - public: - cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} - - virtual bool Item(cDropSpenserEntity * a_DropSpenser) override - { - a_DropSpenser->SetRedstonePower(m_IsPowered); - return false; - } - } DrSpSP(IsPowered(pos)); - m_World.DoWithDropSpenserAt(pos.x, pos.y, pos.z, DrSpSP); - } + +void cRedstoneSimulator::HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + static const struct // Define which directions the redstone block can power + { + int x, y, z; + } gCrossCoords[] = + { + { 0, 0, 0}, // Oh, anomalous redstone. Only block that powers itself + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE); } + return; } -bool cRedstoneSimulator::PowerBlock(const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power) +void cRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ) { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - switch (BlockType) + if (IsLeverOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) { - case E_BLOCK_REDSTONE_WIRE: - { - if (BlockMeta < a_Power) - { - m_World.SetBlockMeta(a_BlockPos, a_Power); - return true; - } - break; - } - - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: + static const struct // Define which directions the redstone lever can power (all sides) { - m_RefreshPistons.push_back(a_BlockPos); - break; - } - - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - { - m_RefreshDropSpensers.push_back(a_BlockPos); - break; - } - - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: + int x, y, z; + } gCrossCoords[] = { - if (IsRepeaterPointingAway(a_BlockPos, BlockMeta, a_FromBlock)) - { - SetRepeater(a_BlockPos, 10, true); - } - break; - } - - case E_BLOCK_REDSTONE_LAMP_OFF: + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) { - m_World.FastSetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_ON, 0); - break; + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER); } - case E_BLOCK_TNT: - { - m_World.BroadcastSoundEffect("random.fuse", a_BlockPos.x * 8, a_BlockPos.y * 8, a_BlockPos.z * 8, 0.5f, 0.6f); - m_World.SpawnPrimedTNT(a_BlockPos.x + 0.5, a_BlockPos.y + 0.5, a_BlockPos.z + 0.5, 4); // 4 seconds to boom - m_World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_AIR, 0); - break; - } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_LEVER); + } + return; +} - default: + + + + +void cRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) +{ + if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) + { + static const struct // Define which directions the redstone button can power (all sides) { - if ( - (BlockType != E_BLOCK_AIR) && - (BlockType != E_BLOCK_REDSTONE_TORCH_ON) && - (BlockType != E_BLOCK_REDSTONE_TORCH_OFF) && - (BlockType != E_BLOCK_LEVER) // Treating lever as a torch, for refreshing - ) - { - if (IsPowered(a_BlockPos, true)) - { - m_RefreshTorchesAround.push_back(a_BlockPos); - } - } - break; + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_BlockType); } - } // switch (BlockType) - return false; + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, a_BlockType); + } } -int cRedstoneSimulator::UnPowerBlock(const Vector3i & a_BlockPos, const Vector3i & a_FromBlock) +void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ) { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if ((a_BlockPos.y < 0) || (a_BlockPos.y >= cChunkDef::Height)) + static const struct // Define which directions the wire can receive power from { - return 0; + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 1, 1, 0}, // From here to end, check for wire placed on sides of blocks + {-1, 1, 0}, + { 0, 1, 1}, + { 0, 1, -1}, + { 1,-1, 0}, + {-1,-1, 0}, + { 0,-1, 1}, + { 0,-1, -1}, + } ; + + // Check to see if directly beside a power source + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power } - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - switch (BlockType) + else { - case E_BLOCK_REDSTONE_WIRE: + NIBBLETYPE MyMeta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + int TimesMetaSmaller = 0, TimesFoundAWire = 0; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power { - if (BlockMeta > 0) + BLOCKTYPE SurroundType; + NIBBLETYPE SurroundMeta; + m_World.GetBlockTypeMeta(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, SurroundType, SurroundMeta); + + if (SurroundType == E_BLOCK_REDSTONE_WIRE) { - m_World.SetBlockMeta(a_BlockPos, 0); - return 1; - } - break; - } - - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - m_RefreshPistons.push_back(a_BlockPos); - break; - } + TimesFoundAWire++; - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - { - m_RefreshDropSpensers.push_back(a_BlockPos); - break; + if (SurroundMeta > 1) // Wires of power 1 or 0 cannot transfer power TO ME, don't bother checking + { + if (SurroundMeta > MyMeta) // Does surrounding wire have a higher power level than self? + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, SurroundMeta - 1); + } + } + + if (SurroundMeta < MyMeta) // Go through all surroundings to see if self power is larger than everyone else's + { + TimesMetaSmaller++; + } + } } - - case E_BLOCK_REDSTONE_TORCH_ON: + + if (TimesMetaSmaller == TimesFoundAWire) { - return 2; - break; - } - - case E_BLOCK_LEVER: + // All surrounding metas were smaller - self must have been a wire that was + // transferring power to other wires around. + // However, self not directly powered anymore, so source must have been removed, + // therefore, self must be set to meta zero + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0); + } + } + + if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0) // A powered wire + { + //SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); // No matter what, block underneath gets powered + + switch (GetWireDirection(a_BlockX, a_BlockY, a_BlockZ)) { - // Check if lever is ON. If it is, report it back as a source - if (cRedstoneSimulator::IsLeverOn(BlockMeta)) + case REDSTONE_NONE: { - return 2; + static const struct // Define which directions the redstone wire can power + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, // Power block in front + { 2, 0, 0}, // Power block in front of that (strongly power) + {-1, 0, 0}, + {-2, 0, 0}, + { 0, 0, 1}, + { 0, 0, 2}, + { 0, 0, -1}, + { 0, 0, -2}, + { 0, 1, 0}, + { 0, 2, 0}, + { 0,-1, 0}, + { 0,-2, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power if block is solid, CURRENTLY all mechanisms are solid + if (g_BlockIsSolid[m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z)]) + { + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + } + break; } - break; - } - - case E_BLOCK_REDSTONE_REPEATER_ON: - { - if ( - IsRepeaterPointingTo(a_BlockPos, BlockMeta, a_FromBlock) || // Repeater is next to wire - IsRepeaterPointingTo(a_BlockPos, BlockMeta, a_FromBlock - Vector3i(0, 1, 0)) // Repeater is below wire - ) + case REDSTONE_X_POS: { - return 2; + if (m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE); + break; } - else if (IsRepeaterPointingAway(a_BlockPos, BlockMeta, a_FromBlock)) + case REDSTONE_X_NEG: { - SetRepeater(a_BlockPos, 10, false); + if (m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE); + break; } - // fall-through: - } - - case E_BLOCK_REDSTONE_REPEATER_OFF: - { - if (IsRepeaterPointingAway(a_BlockPos, BlockMeta, a_FromBlock)) + case REDSTONE_Z_POS: { - SetRepeater(a_BlockPos, 10, false); + if (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) != E_BLOCK_REDSTONE_WIRE) + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE); + break; } - break; - } - - case E_BLOCK_REDSTONE_LAMP_ON: - { - m_World.FastSetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_OFF, 0); - break; - } - - default: - { - if ( - (BlockType != E_BLOCK_AIR) && - (BlockType != E_BLOCK_REDSTONE_TORCH_ON) && - (BlockType != E_BLOCK_REDSTONE_TORCH_OFF) && - (BlockType != E_BLOCK_LEVER) - ) + case REDSTONE_Z_NEG: { - if (!IsPowered(a_BlockPos, true)) + if (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) != E_BLOCK_REDSTONE_WIRE) { - m_RefreshTorchesAround.push_back(a_BlockPos); + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); } + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_WIRE); + break; } - break; } - } // switch (BlockType) - - return 0; + } + return; } -// Removes current from all powered redstone wires until it reaches an energy source. -// Also returns all energy sources it encountered -cRedstoneSimulator::BlockList cRedstoneSimulator::RemoveCurrent(const Vector3i & a_BlockPos) +void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) { - - - std::deque< Vector3i > SpreadStack; - std::deque< Vector3i > FoundSources; - - Vector3i Surroundings[] = { - Vector3i(1, 0, 0), - Vector3i(1, 1, 0), - Vector3i(1,-1, 0), - Vector3i(-1, 0, 0), - Vector3i(-1, 1, 0), - Vector3i(-1,-1, 0), - Vector3i(0, 0, 1), - Vector3i(0, 1, 1), - Vector3i(0,-1, 1), - Vector3i(0, 0,-1), - Vector3i(0, 1,-1), - Vector3i(0,-1,-1), - Vector3i(0,-1, 0), - }; - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - switch (BlockType) + NIBBLETYPE a_Meta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + if (a_MyState == E_BLOCK_REDSTONE_REPEATER_OFF) { - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: + if (IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3)) { - // Repeaters only spread to their front front and 0 or 1 block up - static Vector3i Surroundings [] = { - Vector3i(0, 0, 0), - Vector3i(0, 1, 0), - }; - Vector3i Direction = GetRepeaterDirection(BlockMeta); - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta); + switch (a_Meta & 0x3) // We only want the direction (bottom) bits { - Vector3i pos = a_BlockPos + Direction + Surroundings[i]; - int RetVal = UnPowerBlock(pos, a_BlockPos); - if (RetVal == 1) + case 0x0: { - // Changed, so add to stack - SpreadStack.push_back(pos); + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_REPEATER_ON); + break; } - else if (RetVal == 2) + case 0x1: { - FoundSources.push_back(pos); + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_REPEATER_ON); + break; } - } - break; - } - - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_LEVER: - { - static Vector3i Surroundings [] = { // Torches only spread on the same level - Vector3i(-1, 0, 0), - Vector3i(1, 0, 0), - Vector3i(0, 0,-1), - Vector3i(0, 0, 1), - }; - - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) - { - Vector3i pos = Vector3i(a_BlockPos) + Surroundings[i]; - int RetVal = UnPowerBlock(pos, a_BlockPos); - if (RetVal == 1) + case 0x2: { - SpreadStack.push_back(pos); // Changed, so add to stack + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_REPEATER_ON); + break; } - else if (RetVal == 2) + case 0x3: { - FoundSources.push_back(pos); + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_REPEATER_ON); + break; } } - break; } - - default: + } + else + { + if (!IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3)) { - SpreadStack.push_back(a_BlockPos); - break; + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta); } - } // switch (BlockType) + } + return; +} + + + + + +void cRedstoneSimulator::HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + cPiston Piston(&m_World); + Piston.ExtendPiston(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + cPiston Piston(&m_World); + Piston.RetractPiston(a_BlockX, a_BlockY, a_BlockZ); + } + return; +} + + - while (!SpreadStack.empty()) + +void cRedstoneSimulator::HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + class cSetPowerToDropSpenser : + public cDropSpenserCallback { - Vector3i pos = SpreadStack.back(); - SpreadStack.pop_back(); + bool m_IsPowered; + public: + cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cDropSpenserEntity * a_DropSpenser) override + { + a_DropSpenser->SetRedstonePower(m_IsPowered); + return false; + } + } DrSpSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)); + + m_World.DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, DrSpSP); + return; +} + + - for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + + +void cRedstoneSimulator::HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF) + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { - Vector3i OtherPos = pos + Surroundings[i]; - int RetVal = UnPowerBlock(OtherPos, pos); - if (RetVal == 1) - { - SpreadStack.push_back(OtherPos); // Changed, so add to stack - } - else if (RetVal == 2) - { - FoundSources.push_back(OtherPos); - } + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0); + } + } + else + { + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0); } } + return; +} + + + + - return FoundSources; +void cRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom + m_World.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + } + return; } -bool cRedstoneSimulator::IsPowering(const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire) +void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ) { - BLOCKTYPE PowerBlock; - NIBBLETYPE PowerMeta; - m_World.GetBlockTypeMeta(a_PowerPos.x, a_PowerPos.y, a_PowerPos.z, PowerBlock, PowerMeta); - - // Filter out powering blocks for a_bOnlyByWire - if ( - !a_bOnlyByWire && ( - (PowerBlock == E_BLOCK_REDSTONE_TORCH_ON) || - (PowerBlock == E_BLOCK_LEVER) - ) - ) + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) { - return true; + // Block position is located at top half of door + // Is Y - 1 both within world boundaries, a door block, and the bottom half of a door? + // The bottom half stores the open/closed information + if ( + (a_BlockY - 1 >= 0) && + ((m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) || (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_IRON_DOOR)) && + (m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ & 0x08) == 0) + ) + { + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ) & 0x04) == 0) // Closed door? + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open + { + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + } + } + else // Opened door + { + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so + { + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); + } + } + } } - - switch (PowerBlock) + else { - case E_BLOCK_REDSTONE_REPEATER_ON: + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x04) == 0) // Closed door? { - // A repeater pointing towards block is regarded as wire - if (IsRepeaterPointingTo(a_PowerPos, PowerMeta, a_BlockPos)) + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open { - return true; + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); } - break; } - - case E_BLOCK_REDSTONE_WIRE: + else // Opened door { - if (PowerMeta > 0) + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so { - if (GetWireDirection(a_PowerPos) == a_WireDirection) - { - return true; - } + cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ); } - break; } - } // switch (PowerBlock) - - return false; + } + return; } -bool cRedstoneSimulator::IsPowered(const Vector3i & a_BlockPos, bool a_bOnlyByWire /* = false */) +void cRedstoneSimulator::HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); - if ((BlockType == E_BLOCK_REDSTONE_REPEATER_OFF) || (BlockType == E_BLOCK_REDSTONE_REPEATER_ON)) + switch (a_MyType) { - Vector3i Behind = a_BlockPos - GetRepeaterDirection(BlockMeta); - BLOCKTYPE BehindBlock; - NIBBLETYPE BehindMeta; - m_World.GetBlockTypeMeta(Behind.x, Behind.y, Behind.z, BehindBlock, BehindMeta); - switch (BehindBlock) + case E_BLOCK_DETECTOR_RAIL: { - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_LEVER: + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) { - // _X: TODO: Shouldn't a lever be checked if it is switched on? - return true; + static const struct // Define which directions the rail can power (all sides) + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0,-1, 0}, + } ; + + for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + // Power everything + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_MyType); + } } - case E_BLOCK_REDSTONE_WIRE: + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { - return (BehindMeta > 0); + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x08); } - case E_BLOCK_REDSTONE_REPEATER_ON: + else { - return IsRepeaterPointingTo(Behind, BehindMeta, a_BlockPos); + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x07); } - } // switch (BehindBlock) - return false; - } - - if (IsPowering(Vector3i(a_BlockPos.x - 1, a_BlockPos.y, a_BlockPos.z), a_BlockPos, REDSTONE_X_POS, a_bOnlyByWire)) - { - return true; - } - if (IsPowering(Vector3i(a_BlockPos.x + 1, a_BlockPos.y, a_BlockPos.z), a_BlockPos, REDSTONE_X_NEG, a_bOnlyByWire)) - { - return true; - } - if (IsPowering(Vector3i(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z - 1), a_BlockPos, REDSTONE_Z_POS, a_bOnlyByWire)) - { - return true; - } - if (IsPowering(Vector3i(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z + 1), a_BlockPos, REDSTONE_Z_NEG, a_bOnlyByWire)) - { - return true; - } - - // Only wires can power the bottom block - BLOCKTYPE PosYType; - NIBBLETYPE PosYMeta; - m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y + 1, a_BlockPos.z, PosYType, PosYMeta); - if (PosYType == E_BLOCK_REDSTONE_WIRE) - { - return (PosYMeta > 0); + break; + } } - - return false; } -// Believe me, it works!! TODO: Add repeaters and low/high wires -cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int Dir = REDSTONE_NONE; - BLOCKTYPE NegX = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); - if ( - (NegX == E_BLOCK_REDSTONE_WIRE) || - (NegX == E_BLOCK_REDSTONE_TORCH_ON) || - (NegX == E_BLOCK_LEVER) - ) - { - Dir |= (REDSTONE_X_POS); - } - - BLOCKTYPE PosX = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); - if ( - (PosX == E_BLOCK_REDSTONE_WIRE) || - (PosX == E_BLOCK_REDSTONE_TORCH_ON) || - (PosX == E_BLOCK_LEVER) - ) - { - Dir |= (REDSTONE_X_NEG); - } - - BLOCKTYPE NegZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); - if ( - (NegZ == E_BLOCK_REDSTONE_WIRE) || - (NegZ == E_BLOCK_REDSTONE_TORCH_ON) || - (NegZ == E_BLOCK_LEVER) - ) +bool cRedstoneSimulator::AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list { - 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 + sPoweredBlocks & Change = *itr; + + if (Change.a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; + return true; } - Dir |= REDSTONE_Z_POS; } - - BLOCKTYPE PosZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); - if ( - (PosZ == E_BLOCK_REDSTONE_WIRE) || - (PosZ == E_BLOCK_REDSTONE_TORCH_ON) || - (PosZ == E_BLOCK_LEVER) - ) + + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list { - 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 + sLinkedPoweredBlocks & Change = *itr; + + if (Change.a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; + return true; } - Dir |= REDSTONE_Z_NEG; } - - return (eRedstoneDirection)Dir; + return false; } -bool cRedstoneSimulator::IsRepeaterPointingTo(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos) +bool cRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta) { - switch (a_MetaData & 0x3) + // Check through powered blocks list + for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) { - case 0x0: + sPoweredBlocks & Change = *itr; + + switch (a_Meta) { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0, 1))) + case 0x0: { - return true; + // Flip the coords to check the back of the repeater + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } + break; } - break; - } - - case 0x1: - { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))) + case 0x1: + { + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + case 0x2: { - return true; + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } + break; } - break; - } - - case 0x2: - { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0,-1))) + case 0x3: { - return true; + if (Change.a_SourcePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } + break; } - break; } - - case 0x3: + } + + // Check linked powered list, 'middle' blocks + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + { + sLinkedPoweredBlocks & Change = *itr; + + switch (a_Meta) { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(1, 0, 0))) + case 0x0: { - return true; + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } + break; + } + case 0x1: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + case 0x2: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } + break; + } + case 0x3: + { + if (Change.a_MiddlePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } + break; } - break; } } - return false; + return false; // Couldn't find power source behind repeater } -bool cRedstoneSimulator::IsRepeaterPointingAway(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos) +void cRedstoneSimulator::SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType) { - switch (a_MetaData & 0x3) + switch (a_Direction) { - case 0x0: + case BLOCK_FACE_XM: { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0,-1))) - { - return true; - } + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX - 2, a_BlockY, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); break; } - - case 0x1: + case BLOCK_FACE_XP: { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(1, 0, 0))) - { - return true; - } + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX + 2, a_BlockY, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); break; } - - case 0x2: + case BLOCK_FACE_YM: { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0, 1))) - { - return true; - } + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY - 2, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); break; } - - case 0x3: + case BLOCK_FACE_YP: { - if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))) - { - return true; - } + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY + 2, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_ZM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ - 2, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + case BLOCK_FACE_ZP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); + if (!g_BlockIsSolid[MiddleBlock]) { return; } + + SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ + 2, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + break; + } + default: + { + ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); break; } } - return false; + return; } -NIBBLETYPE cRedstoneSimulator::RepeaterRotationToMetaData(double a_Rotation) +void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock) { - a_Rotation += 90 + 45; // So its not aligned with axis - if (a_Rotation > 360) - { - a_Rotation -= 360; - } - - if ((a_Rotation >= 0) && (a_Rotation < 90)) - { - return 0x1; - } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) - { - return 0x3; - } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) - { - return 0x2; - } - else - { - return 0x0; - } + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { return; } // Check for duplicates + + sPoweredBlocks RC; + RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); + RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); + RC.a_SourceBlock = a_SourceBlock; + m_PoweredBlocks.push_back(RC); + return; } -Vector3i cRedstoneSimulator::GetRepeaterDirection(NIBBLETYPE a_MetaData) +void cRedstoneSimulator::SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, + int a_MiddleX, int a_MiddleY, int a_MiddleZ, + int a_SourceX, int a_SourceY, int a_SourceZ, + BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddleBlock + ) { - switch (a_MetaData & 0x3) - { - case 0x0: return Vector3i(0, 0,-1); - case 0x1: return Vector3i(1, 0, 0); - case 0x2: return Vector3i(0, 0, 1); - case 0x3: return Vector3i(-1, 0, 0); - } - return Vector3i(); + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { return; } // Check for duplicates + + sLinkedPoweredBlocks RC; + RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); + RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ); + RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); + RC.a_SourceBlock = a_SourceBlock; + RC.a_MiddleBlock = a_MiddleBlock; + m_LinkedPoweredBlocks.push_back(RC); + return; } -NIBBLETYPE cRedstoneSimulator::LeverDirectionToMetaData(char a_Dir) +cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ) { - // Determine lever direction: - switch (a_Dir) + int Dir = REDSTONE_NONE; + + BLOCKTYPE NegX = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); + if (IsPotentialSource(NegX)) + { + Dir |= (REDSTONE_X_POS); + } + + BLOCKTYPE PosX = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); + if (IsPotentialSource(PosX)) { - case BLOCK_FACE_TOP: return 0x6; - case BLOCK_FACE_EAST: return 0x1; - case BLOCK_FACE_WEST: return 0x2; - case BLOCK_FACE_SOUTH: return 0x3; - case BLOCK_FACE_NORTH: return 0x4; - case BLOCK_FACE_BOTTOM: return 0x0; - default: return 0x6; + Dir |= (REDSTONE_X_NEG); } -} - - - - - -bool cRedstoneSimulator::IsLeverOn(cWorld * a_World, const Vector3i & a_BlockPos) -{ - // Extract the metadata and ask the lower level: - return IsLeverOn(a_World->GetBlockMeta(a_BlockPos)); + + BLOCKTYPE NegZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); + 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 = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); + 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; } @@ -1145,32 +1063,9 @@ bool cRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) -void cRedstoneSimulator::SetRepeater(const Vector3i & a_Position, int a_Ticks, bool a_bPowerOn) +bool cRedstoneSimulator::IsButtonOn(NIBBLETYPE a_BlockMeta) { - for (RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end(); ++itr) - { - sRepeaterChange & Change = *itr; - if (Change.Position.Equals(a_Position)) - { - if (Change.bPowerOn && !a_bPowerOn) - { - Change.bPowerOffNextTime = true; - } - if (a_bPowerOn) - { - Change.bPowerOffNextTime = false; - } - Change.bPowerOn |= a_bPowerOn; - return; - } - } - - sRepeaterChange RC; - RC.Position = a_Position; - RC.Ticks = a_Ticks; - RC.bPowerOn = a_bPowerOn; - RC.bPowerOffNextTime = false; - m_SetRepeaters.push_back(RC); + return IsLeverOn(a_BlockMeta); } diff --git a/source/Simulator/RedstoneSimulator.h b/source/Simulator/RedstoneSimulator.h index c0d5795c7..d68c6daeb 100644 --- a/source/Simulator/RedstoneSimulator.h +++ b/source/Simulator/RedstoneSimulator.h @@ -3,6 +3,9 @@ #include "Simulator.h" +/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used +typedef cCoordWithIntList cRedstoneSimulatorChunkData; + @@ -12,13 +15,13 @@ class cRedstoneSimulator : { typedef cSimulator super; public: + cRedstoneSimulator(cWorld & a_World); ~cRedstoneSimulator(); - virtual void Simulate( float a_Dt ) override; - virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return true; } - - virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + virtual void Simulate(float a_Dt) override {}; // Not used in this simulator + 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); } enum eRedstoneDirection { @@ -31,56 +34,166 @@ public: eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ); eRedstoneDirection GetWireDirection(const Vector3i & a_Pos) { return GetWireDirection(a_Pos.x, a_Pos.y, a_Pos.z); } - static bool IsRepeaterPointingTo (const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos); - static bool IsRepeaterPointingAway(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos); - static NIBBLETYPE RepeaterRotationToMetaData(double a_Rotation); - static Vector3i GetRepeaterDirection(NIBBLETYPE a_MetaData); - static NIBBLETYPE LeverDirectionToMetaData(char a_Dir); - static bool IsLeverOn(cWorld * a_World, const Vector3i & a_BlockPos); - static bool IsLeverOn(NIBBLETYPE a_BlockMeta); - - private: - struct sRepeaterChange + + struct sPoweredBlocks // Define structure of the directly powered blocks list { - Vector3i Position; - int Ticks; - bool bPowerOn; - bool bPowerOffNextTime; + Vector3i a_BlockPos; // Position of powered block + Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos + BLOCKTYPE a_SourceBlock; // The source block type (for pistons pushing away sources and replacing with non source etc.) }; - typedef std::deque <Vector3i> BlockList; - - typedef std::deque< sRepeaterChange > RepeaterList; - RepeaterList m_SetRepeaters; - - void SetRepeater(const Vector3i & a_Position, int a_Ticks, bool a_bPowerOn); - - virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {} - - void HandleChange( const Vector3i & a_BlockPos ); - BlockList RemoveCurrent( const Vector3i & a_BlockPos ); - - bool PowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power ); - int UnPowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock ); - - bool IsPowered( const Vector3i & a_BlockPos, bool a_bOnlyByWire = false ); - bool IsPowering( const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire ); - - BlockList m_Blocks; - BlockList m_BlocksBuffer; - - BlockList m_RefreshPistons; - BlockList m_RefreshDropSpensers; - - BlockList m_RefreshTorchesAround; + 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; + Vector3i a_SourcePos; + BLOCKTYPE a_SourceBlock; + BLOCKTYPE a_MiddleBlock; + }; - void RefreshTorchesAround( const Vector3i & a_BlockPos ); - - // TODO: The entire simulator is synchronized, no need to lock data structures; remove this - cCriticalSection m_CS; -}; - - - - + typedef std::vector <sPoweredBlocks> PoweredBlocksList; + typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList; + + PoweredBlocksList m_PoweredBlocks; + LinkedBlocksList m_LinkedPoweredBlocks; + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + + // 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 ====== */ + ///<summary>Handles the redstone torch</summary> + void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + ///<summary>Handles the redstone block</summary> + void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles levers</summary> + void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles buttons</summary> + void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + /* ==================== */ + + /* ====== CARRIERS ====== */ + ///<summary>Handles redstone wire</summary> + void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles repeaters</summary> + void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + /* ====================== */ + + /* ====== DEVICES ====== */ + ///<summary>Handles pistons</summary> + void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles dispensers and droppers</summary> + void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles TNT (exploding)</summary> + void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles redstone lamps</summary> + void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + ///<summary>Handles doords</summary> + void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ); + ///<summary>Handles activator, detector, and powered rails</summary> + void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); + /* ===================== */ + + /* ====== Helper functions ====== */ + void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock); + void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock); + void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType); + + bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ); + bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); + + bool IsLeverOn(NIBBLETYPE a_BlockMeta); + bool IsButtonOn(NIBBLETYPE a_BlockMeta); + /* ============================== */ + + inline static bool IsMechanism(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_TNT: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_POWERED_RAIL: + { + return true; + } + default: return false; + } + } + + inline static bool IsPotentialSource(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_LEVER: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_INACTIVE_COMPARATOR: + { + return true; + } + default: return false; + } + } + + inline static bool IsRedstone(BLOCKTYPE Block) + { + switch (Block) + { + // All redstone devices, please alpha sort + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_DISPENSER: + case E_BLOCK_DAYLIGHT_SENSOR: + 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_LEVER: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_NOTE_BLOCK: + 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_STICKY_PISTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_TNT: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_TRIPWIRE_HOOK: + 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; + } + } +};
\ No newline at end of file |