1098 lines
24 KiB
C++
1098 lines
24 KiB
C++
|
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
|
|
|
#include "RedstoneSimulator.h"
|
|
#include "../DispenserEntity.h"
|
|
#include "../Piston.h"
|
|
#include "../World.h"
|
|
#include "../BlockID.h"
|
|
#include "../Torch.h"
|
|
|
|
|
|
|
|
|
|
|
|
cRedstoneSimulator::cRedstoneSimulator( cWorld* a_World )
|
|
: super(a_World)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cRedstoneSimulator::~cRedstoneSimulator()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cRedstoneSimulator::WakeUp( int a_X, int a_Y, int a_Z )
|
|
{
|
|
cCSLock Lock( m_CS );
|
|
m_Blocks.push_back( Vector3i( a_X, a_Y, a_Z ) );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
//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
|
|
{
|
|
//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 (cTorch::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_RefreshDispensers.empty())
|
|
{
|
|
Vector3i pos = m_RefreshDispensers.back();
|
|
m_RefreshDispensers.pop_back();
|
|
|
|
BLOCKTYPE BlockType = m_World->GetBlock(pos);
|
|
if (BlockType == E_BLOCK_DISPENSER)
|
|
{
|
|
if (IsPowered(pos))
|
|
{
|
|
class cActivateDispenser :
|
|
public cDispenserCallback
|
|
{
|
|
public:
|
|
cActivateDispenser()
|
|
{
|
|
}
|
|
|
|
virtual bool Item(cDispenserEntity * a_Dispenser) override
|
|
{
|
|
a_Dispenser->Activate();
|
|
return false;
|
|
}
|
|
} ;
|
|
cActivateDispenser DispAct;
|
|
m_World->DoWithDispenserAt(pos.x, pos.y, pos.z, DispAct);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
|
{
|
|
m_RefreshDispensers.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;
|
|
}
|
|
|
|
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_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;
|
|
}
|
|
|
|
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(float a_Rotation)
|
|
{
|
|
a_Rotation += 90 + 45; // So its not aligned with axis
|
|
if (a_Rotation > 360.f)
|
|
{
|
|
a_Rotation -= 360.f;
|
|
}
|
|
|
|
if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
|
|
{
|
|
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 );
|
|
}
|
|
|
|
|
|
|
|
|