1
0
cuberite-2a/source/Simulator/RedstoneSimulator.cpp
Tiger Wang 3640f2c482 Redstone fixes [SEE DESC]
Fixed pistons not correctly powering down
Fixed dispensers not correctly powering down
Fixed droppers not correctly power down
Fixed TNT not correctly powering up
Fixed redstone lamps not correctly powering up
Fixed redstone lamps not correctly powering down
2013-08-25 11:45:47 +01:00

1179 lines
26 KiB
C++

#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);
}