diff options
Diffstat (limited to 'src/Simulator/RedstoneSimulator.cpp')
-rw-r--r-- | src/Simulator/RedstoneSimulator.cpp | 1178 |
1 files changed, 1178 insertions, 0 deletions
diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp new file mode 100644 index 000000000..8526a888e --- /dev/null +++ b/src/Simulator/RedstoneSimulator.cpp @@ -0,0 +1,1178 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "RedstoneSimulator.h" +#include "../BlockEntities/DropSpenserEntity.h" +#include "../Blocks/BlockTorch.h" +#include "../Piston.h" +#include "../World.h" +#include "../BlockID.h" +#include "../Chunk.h" +#include "../Entities/TNTEntity.h" + + + + + +cRedstoneSimulator::cRedstoneSimulator(cWorld & a_World) + : super(a_World) +{ +} + + + + + +cRedstoneSimulator::~cRedstoneSimulator() +{ +} + + + + + +void cRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if (a_Chunk == NULL) + { + 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++) + { + 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 +} + + + + + +void cRedstoneSimulator::Simulate(float a_Dt) +{ + // Toggle torches on/off + while (!m_RefreshTorchesAround.empty()) + { + Vector3i pos = m_RefreshTorchesAround.front(); + m_RefreshTorchesAround.pop_front(); + + RefreshTorchesAround(pos); + } + + // Set repeaters to correct values, and decrement ticks + for (RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end();) + { + 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 + { + itr = m_SetRepeaters.erase(itr); + } + } + + // 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(); +} + + + + + +void cRedstoneSimulator::RefreshTorchesAround(const Vector3i & a_BlockPos) +{ + 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)) + { + 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) + { + 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); + } + //Turn a redstone lamp on when it gets powered. + if (m_World.GetBlock(a_BlockPos) == E_BLOCK_REDSTONE_LAMP_OFF) + { + m_World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_ON, 0); + } + //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) + { + m_World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_OFF, 0); + } + //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); + switch (BlockType) + { + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + { + 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); + } + } + break; + } + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + { + if ((BlockType != TargetRepeaterType) && IsRepeaterPointingAway(TorchPos, BlockMeta, a_BlockPos)) + { + SetRepeater(TorchPos, 10, (TargetRepeaterType == E_BLOCK_REDSTONE_REPEATER_ON)); + } + 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) + { + 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); + } + } + 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) + ) + { + RefreshTorchesAround(pos); + } + else + { + SetRepeater(a_BlockPos, 10, IsPowered(a_BlockPos, false)); + } + m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); + break; + } + } // switch (BlockType) + + BlockList Sources; + switch (BlockType) + { + case E_BLOCK_REDSTONE_TORCH_ON: + { + // If torch is still on, use it as a source + Sources.push_back(a_BlockPos); + break; + } + + case E_BLOCK_REDSTONE_REPEATER_ON: + { + // 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) + { + Vector3i pos = a_BlockPos + Direction + Surroundings[i]; + if (PowerBlock(pos, a_BlockPos, 0xf)) + { + SpreadStack.push_back(pos); + } + } + break; + } // case E_BLOCK_REDSTONE_REPEATER_ON + + case E_BLOCK_LEVER: + { + // Adding lever to the source queue + if (cRedstoneSimulator::IsLeverOn(BlockMeta)) + { + Sources.push_back(a_BlockPos); + } + 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()) + { + Vector3i SourcePos = Sources.back(); + Sources.pop_back(); + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + m_World.GetBlockTypeMeta(SourcePos.x, SourcePos.y, SourcePos.z, BlockType, BlockMeta); + switch (BlockType) + { + 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()) + { + Vector3i pos = SpreadStack.back(); + SpreadStack.pop_back(); + NIBBLETYPE Meta = m_World.GetBlockMeta(pos); + + for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + { + Vector3i OtherPos = pos + Surroundings[i]; + if (PowerBlock(OtherPos, pos, Meta - 1)) + { + SpreadStack.push_back(OtherPos); // Changed, so add to stack + } + } + } + + // 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); + } + } +} + + + + + +bool cRedstoneSimulator::PowerBlock(const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); + switch (BlockType) + { + 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: + { + 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: + { + if (IsRepeaterPointingAway(a_BlockPos, BlockMeta, a_FromBlock)) + { + SetRepeater(a_BlockPos, 10, true); + } + break; + } + + case E_BLOCK_REDSTONE_LAMP_OFF: + { + m_World.FastSetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_REDSTONE_LAMP_ON, 0); + break; + } + + 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; + } + + default: + { + 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; + } + } // switch (BlockType) + + return false; +} + + + + + +int cRedstoneSimulator::UnPowerBlock(const Vector3i & a_BlockPos, const Vector3i & a_FromBlock) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if ((a_BlockPos.y < 0) || (a_BlockPos.y >= cChunkDef::Height)) + { + return 0; + } + m_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, BlockType, BlockMeta); + switch (BlockType) + { + case E_BLOCK_REDSTONE_WIRE: + { + if (BlockMeta > 0) + { + 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; + } + + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + { + m_RefreshDropSpensers.push_back(a_BlockPos); + break; + } + + case E_BLOCK_REDSTONE_TORCH_ON: + { + return 2; + break; + } + + case E_BLOCK_LEVER: + { + // Check if lever is ON. If it is, report it back as a source + if (cRedstoneSimulator::IsLeverOn(BlockMeta)) + { + return 2; + } + 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 + ) + { + return 2; + } + else if (IsRepeaterPointingAway(a_BlockPos, BlockMeta, a_FromBlock)) + { + SetRepeater(a_BlockPos, 10, false); + } + // fall-through: + } + + case E_BLOCK_REDSTONE_REPEATER_OFF: + { + if (IsRepeaterPointingAway(a_BlockPos, BlockMeta, a_FromBlock)) + { + SetRepeater(a_BlockPos, 10, false); + } + 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) + ) + { + if (!IsPowered(a_BlockPos, true)) + { + m_RefreshTorchesAround.push_back(a_BlockPos); + } + } + break; + } + } // switch (BlockType) + + return 0; +} + + + + + +// 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) +{ + + + 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) + { + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + { + // 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) + { + Vector3i pos = a_BlockPos + Direction + Surroundings[i]; + int RetVal = UnPowerBlock(pos, a_BlockPos); + if (RetVal == 1) + { + // Changed, so add to stack + SpreadStack.push_back(pos); + } + else if (RetVal == 2) + { + FoundSources.push_back(pos); + } + } + 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) + { + SpreadStack.push_back(pos); // Changed, so add to stack + } + else if (RetVal == 2) + { + FoundSources.push_back(pos); + } + } + break; + } + + default: + { + SpreadStack.push_back(a_BlockPos); + break; + } + } // switch (BlockType) + + + while (!SpreadStack.empty()) + { + Vector3i pos = SpreadStack.back(); + SpreadStack.pop_back(); + + for (unsigned int i = 0; i < ARRAYCOUNT(Surroundings); ++i) + { + 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); + } + } + } + + return FoundSources; +} + + + + + +bool cRedstoneSimulator::IsPowering(const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire) +{ + 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) + ) + ) + { + return true; + } + + switch (PowerBlock) + { + case E_BLOCK_REDSTONE_REPEATER_ON: + { + // A repeater pointing towards block is regarded as wire + if (IsRepeaterPointingTo(a_PowerPos, PowerMeta, a_BlockPos)) + { + return true; + } + break; + } + + case E_BLOCK_REDSTONE_WIRE: + { + if (PowerMeta > 0) + { + if (GetWireDirection(a_PowerPos) == a_WireDirection) + { + return true; + } + } + break; + } + } // switch (PowerBlock) + + return false; +} + + + + + +bool cRedstoneSimulator::IsPowered(const Vector3i & a_BlockPos, bool a_bOnlyByWire /* = false */) +{ + 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)) + { + 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_REDSTONE_TORCH_ON: + case E_BLOCK_LEVER: + { + // _X: TODO: Shouldn't a lever be checked if it is switched on? + return true; + } + case E_BLOCK_REDSTONE_WIRE: + { + return (BehindMeta > 0); + } + case E_BLOCK_REDSTONE_REPEATER_ON: + { + return IsRepeaterPointingTo(Behind, BehindMeta, a_BlockPos); + } + } // 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); + } + + 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) + ) + { + 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 ( + (PosZ == E_BLOCK_REDSTONE_WIRE) || + (PosZ == E_BLOCK_REDSTONE_TORCH_ON) || + (PosZ == E_BLOCK_LEVER) + ) + { + 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 cRedstoneSimulator::IsRepeaterPointingTo(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos) +{ + switch (a_MetaData & 0x3) + { + case 0x0: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0, 1))) + { + return true; + } + break; + } + + case 0x1: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))) + { + return true; + } + break; + } + + case 0x2: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0,-1))) + { + return true; + } + break; + } + + case 0x3: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(1, 0, 0))) + { + return true; + } + break; + } + } + return false; +} + + + + + +bool cRedstoneSimulator::IsRepeaterPointingAway(const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos) +{ + switch (a_MetaData & 0x3) + { + case 0x0: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0,-1))) + { + return true; + } + break; + } + + case 0x1: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(1, 0, 0))) + { + return true; + } + break; + } + + case 0x2: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(0, 0, 1))) + { + return true; + } + break; + } + + case 0x3: + { + if ((a_RepeaterPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))) + { + return true; + } + break; + } + } + return false; +} + + + + + +NIBBLETYPE cRedstoneSimulator::RepeaterRotationToMetaData(double a_Rotation) +{ + 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; + } +} + + + + + +Vector3i cRedstoneSimulator::GetRepeaterDirection(NIBBLETYPE a_MetaData) +{ + 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(); +} + + + + + +NIBBLETYPE cRedstoneSimulator::LeverDirectionToMetaData(char a_Dir) +{ + // Determine lever direction: + switch (a_Dir) + { + 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; + } +} + + + + + +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)); +} + + + + + +bool cRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) +{ + // Extract the ON bit from metadata and return if true if it is set: + return ((a_BlockMeta & 0x8) == 0x8); +} + + + + + +void cRedstoneSimulator::SetRepeater(const Vector3i & a_Position, int a_Ticks, bool a_bPowerOn) +{ + 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); +} + + + + |