summaryrefslogtreecommitdiffstats
path: root/src/Simulator/RedstoneSimulator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Simulator/RedstoneSimulator.cpp')
-rw-r--r--src/Simulator/RedstoneSimulator.cpp1178
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);
+}
+
+
+
+