2598 lines
98 KiB
C++
2598 lines
98 KiB
C++
|
|
#include "IncrementalRedstoneSimulator.h"
|
|
#include "BoundingBox.h"
|
|
#include "BlockEntities/RedstonePoweredEntity.h"
|
|
#include "Blocks/ChunkInterface.h"
|
|
#include "RedstoneSimulator.h"
|
|
|
|
|
|
typedef cItemCallback<cEntity> cEntityCallback;
|
|
|
|
|
|
|
|
|
|
|
|
typedef cItemCallback<cRedstonePoweredEntity> cRedstonePoweredCallback;
|
|
|
|
|
|
template<class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
class cIncrementalRedstoneSimulator :
|
|
public cRedstoneSimulator<ChunkType, WorldType>
|
|
{
|
|
typedef cRedstoneSimulator<ChunkType, WorldType> super;
|
|
public:
|
|
|
|
cIncrementalRedstoneSimulator(WorldType & a_World)
|
|
: cRedstoneSimulator<ChunkType, WorldType>(a_World)
|
|
{
|
|
}
|
|
~cIncrementalRedstoneSimulator();
|
|
|
|
virtual cRedstoneSimulatorChunkData * CreateChunkData() override
|
|
{
|
|
return new cIncrementalRedstoneSimulatorChunkData;
|
|
}
|
|
|
|
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
|
|
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, ChunkType * a_Chunk) override;
|
|
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); }
|
|
virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) override;
|
|
|
|
enum eRedstoneDirection
|
|
{
|
|
REDSTONE_NONE = 0,
|
|
REDSTONE_X_POS = 0x1,
|
|
REDSTONE_X_NEG = 0x2,
|
|
REDSTONE_Z_POS = 0x4,
|
|
REDSTONE_Z_NEG = 0x8,
|
|
};
|
|
eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ);
|
|
|
|
private:
|
|
|
|
#define MAX_POWER_LEVEL 15
|
|
|
|
struct sPoweredBlocks // Define structure of the directly powered blocks list
|
|
{
|
|
Vector3i a_BlockPos; // Position of powered block
|
|
Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos
|
|
unsigned char a_PowerLevel;
|
|
};
|
|
|
|
struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side)
|
|
{
|
|
Vector3i a_BlockPos;
|
|
Vector3i a_MiddlePos; // Position of block that is betwixt a source and the destination
|
|
Vector3i a_SourcePos;
|
|
unsigned char a_PowerLevel;
|
|
};
|
|
|
|
struct sSimulatedPlayerToggleableList // Define structure of the list containing simulate-on-update blocks (such as trapdoors that respond once to a block update, and can be toggled by a player)
|
|
{
|
|
Vector3i a_RelBlockPos;
|
|
bool WasLastStatePowered; // Was the last state powered or not? Determines whether a source update has happened and if I should resimulate
|
|
};
|
|
|
|
struct sRepeatersDelayList // Define structure of list containing repeaters' delay states
|
|
{
|
|
Vector3i a_RelBlockPos;
|
|
unsigned char a_DelayTicks; // For how many ticks should the repeater delay
|
|
unsigned char a_ElapsedTicks; // How much of the previous has been elapsed?
|
|
bool ShouldPowerOn; // What happens when the delay time is fulfilled?
|
|
};
|
|
|
|
class cIncrementalRedstoneSimulatorChunkData :
|
|
public cRedstoneSimulatorChunkData
|
|
{
|
|
public:
|
|
/// Per-chunk data for the simulator, specified individual chunks to simulate
|
|
cCoordWithBlockAndBoolVector m_ChunkData;
|
|
cCoordWithBlockAndBoolVector m_QueuedChunkData;
|
|
std::vector<sPoweredBlocks> m_PoweredBlocks;
|
|
std::vector<sLinkedPoweredBlocks> m_LinkedBlocks;
|
|
std::vector<sSimulatedPlayerToggleableList> m_SimulatedPlayerToggleableBlocks;
|
|
std::vector<sRepeatersDelayList> m_RepeatersDelayList;
|
|
};
|
|
|
|
public:
|
|
|
|
typedef std::vector <sPoweredBlocks> PoweredBlocksList;
|
|
typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList;
|
|
typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList;
|
|
typedef std::vector <sRepeatersDelayList> RepeatersDelayList;
|
|
|
|
private:
|
|
|
|
cIncrementalRedstoneSimulatorChunkData * m_RedstoneSimulatorChunkData;
|
|
PoweredBlocksList * m_PoweredBlocks;
|
|
LinkedBlocksList * m_LinkedPoweredBlocks;
|
|
SimulatedPlayerToggleableList * m_SimulatedPlayerToggleableBlocks;
|
|
RepeatersDelayList * m_RepeatersDelayList;
|
|
|
|
virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk) override { RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); }
|
|
void RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk, ChunkType * a_OtherChunk = NULL);
|
|
ChunkType * m_Chunk;
|
|
|
|
// We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly
|
|
// In addition to being non-performant, it would stop the player from actually breaking said device
|
|
|
|
/* ====== SOURCES ====== */
|
|
/** Handles the redstone torch */
|
|
void HandleRedstoneTorch(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState);
|
|
/** Handles the redstone block */
|
|
void HandleRedstoneBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles levers */
|
|
void HandleRedstoneLever(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles buttons */
|
|
void HandleRedstoneButton(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles daylight sensors */
|
|
void HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles pressure plates */
|
|
void HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType);
|
|
/** Handles tripwire hooks
|
|
Performs correct meta and power setting for self by going in the direction it faces and looking for a continous line of tripwire bounded by another oppositely facing hook
|
|
If this line is complete, it verifies that at least on wire reports an entity is on top (via its meta), and performs its task
|
|
*/
|
|
void HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles trapped chests */
|
|
void HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/* ==================== */
|
|
|
|
/* ====== CARRIERS ====== */
|
|
/** Handles redstone wire */
|
|
void HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles repeaters */
|
|
void HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState);
|
|
/* ====================== */
|
|
|
|
/* ====== DEVICES ====== */
|
|
/** Handles pistons */
|
|
void HandlePiston(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles dispensers and droppers */
|
|
void HandleDropSpenser(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles TNT (exploding) */
|
|
void HandleTNT(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles redstone lamps */
|
|
void HandleRedstoneLamp(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState);
|
|
/** Handles doords */
|
|
void HandleDoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles command blocks */
|
|
void HandleCommandBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles activator, detector, and powered rails */
|
|
void HandleRail(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType);
|
|
/** Handles trapdoors */
|
|
void HandleTrapdoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles fence gates */
|
|
void HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles noteblocks */
|
|
void HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Handles tripwires */
|
|
void HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/* ===================== */
|
|
|
|
/* ====== Helper functions ====== */
|
|
/** Marks a block as powered */
|
|
void SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL);
|
|
/** Marks a block as being powered through another block */
|
|
void SetBlockLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, BLOCKTYPE a_MiddeBlock, unsigned char a_PowerLevel = MAX_POWER_LEVEL);
|
|
/** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */
|
|
void SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered);
|
|
/** Marks the second block in a direction as linked powered */
|
|
void SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel = MAX_POWER_LEVEL);
|
|
/** Marks all blocks immediately surrounding a coordinate as powered */
|
|
void SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL);
|
|
/** Queues a repeater to be powered or unpowered and returns if the m_RepeatersDelayList iterators were invalidated */
|
|
bool QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn);
|
|
/** Removes a block from the Powered and LinkedPowered lists
|
|
Used for variable sources such as tripwire hooks, daylight sensors, and trapped chests
|
|
*/
|
|
void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, ChunkType * a_Chunk, bool a_IsFirstCall = true);
|
|
|
|
/** Returns if a coordinate is powered or linked powered */
|
|
bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); }
|
|
/** Returns if a coordinate is in the directly powered blocks list */
|
|
bool AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, ChunkType * a_Chunk);
|
|
/** Returns if a coordinate is in the indirectly powered blocks list */
|
|
bool AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ);
|
|
/** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */
|
|
bool AreCoordsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool IsCurrentStatePowered);
|
|
/** Returns if a repeater is powered by testing for power sources behind the repeater */
|
|
bool IsRepeaterPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta);
|
|
/** Returns if a repeater is locked */
|
|
bool IsRepeaterLocked(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta);
|
|
/** Returns if a piston is powered */
|
|
bool IsPistonPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta);
|
|
/** Returns if a wire is powered
|
|
The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire */
|
|
bool IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel);
|
|
/** Handles delayed updates to repeaters **/
|
|
void HandleRedstoneRepeaterDelays(void);
|
|
|
|
/** Returns if lever metadata marks it as emitting power */
|
|
bool IsLeverOn(NIBBLETYPE a_BlockMeta);
|
|
/** Returns if button metadata marks it as emitting power */
|
|
bool IsButtonOn(NIBBLETYPE a_BlockMeta) { return IsLeverOn(a_BlockMeta); }
|
|
/* ============================== */
|
|
|
|
/* ====== Misc Functions ====== */
|
|
/** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */
|
|
inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return cBlockInfo::FullyOccupiesVoxel(Block); }
|
|
|
|
/** Returns if a block is a mechanism (something that accepts power and does something)
|
|
Used by torches to determine if they power a block whilst not standing on the ground
|
|
*/
|
|
inline static bool IsMechanism(BLOCKTYPE Block)
|
|
{
|
|
switch (Block)
|
|
{
|
|
case E_BLOCK_ACACIA_DOOR:
|
|
case E_BLOCK_ACACIA_FENCE_GATE:
|
|
case E_BLOCK_ACTIVATOR_RAIL:
|
|
case E_BLOCK_BIRCH_DOOR:
|
|
case E_BLOCK_BIRCH_FENCE_GATE:
|
|
case E_BLOCK_COMMAND_BLOCK:
|
|
case E_BLOCK_DARK_OAK_DOOR:
|
|
case E_BLOCK_DARK_OAK_FENCE_GATE:
|
|
case E_BLOCK_DISPENSER:
|
|
case E_BLOCK_DROPPER:
|
|
case E_BLOCK_FENCE_GATE:
|
|
case E_BLOCK_HOPPER:
|
|
case E_BLOCK_IRON_DOOR:
|
|
case E_BLOCK_IRON_TRAPDOOR:
|
|
case E_BLOCK_JUNGLE_DOOR:
|
|
case E_BLOCK_JUNGLE_FENCE_GATE:
|
|
case E_BLOCK_NOTE_BLOCK:
|
|
case E_BLOCK_PISTON:
|
|
case E_BLOCK_POWERED_RAIL:
|
|
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_WIRE:
|
|
case E_BLOCK_SPRUCE_DOOR:
|
|
case E_BLOCK_SPRUCE_FENCE_GATE:
|
|
case E_BLOCK_STICKY_PISTON:
|
|
case E_BLOCK_TNT:
|
|
case E_BLOCK_TRAPDOOR:
|
|
case E_BLOCK_WOODEN_DOOR:
|
|
{
|
|
return true;
|
|
}
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
/** Returns if a block has the potential to output power */
|
|
inline static bool IsPotentialSource(BLOCKTYPE Block)
|
|
{
|
|
switch (Block)
|
|
{
|
|
case E_BLOCK_DETECTOR_RAIL:
|
|
case E_BLOCK_DAYLIGHT_SENSOR:
|
|
case E_BLOCK_WOODEN_BUTTON:
|
|
case E_BLOCK_STONE_BUTTON:
|
|
case E_BLOCK_REDSTONE_WIRE:
|
|
case E_BLOCK_REDSTONE_TORCH_ON:
|
|
case E_BLOCK_LEVER:
|
|
case E_BLOCK_REDSTONE_REPEATER_ON:
|
|
case E_BLOCK_BLOCK_OF_REDSTONE:
|
|
case E_BLOCK_ACTIVE_COMPARATOR:
|
|
case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
|
|
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
|
|
case E_BLOCK_STONE_PRESSURE_PLATE:
|
|
case E_BLOCK_WOODEN_PRESSURE_PLATE:
|
|
case E_BLOCK_TRAPPED_CHEST:
|
|
{
|
|
return true;
|
|
}
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
/** Returns if a block is any sort of redstone device */
|
|
inline static bool IsRedstone(BLOCKTYPE Block)
|
|
{
|
|
switch (Block)
|
|
{
|
|
// All redstone devices, please alpha sort
|
|
case E_BLOCK_ACACIA_DOOR:
|
|
case E_BLOCK_ACACIA_FENCE_GATE:
|
|
case E_BLOCK_ACTIVATOR_RAIL:
|
|
case E_BLOCK_ACTIVE_COMPARATOR:
|
|
case E_BLOCK_BIRCH_DOOR:
|
|
case E_BLOCK_BIRCH_FENCE_GATE:
|
|
case E_BLOCK_BLOCK_OF_REDSTONE:
|
|
case E_BLOCK_COMMAND_BLOCK:
|
|
case E_BLOCK_DARK_OAK_DOOR:
|
|
case E_BLOCK_DARK_OAK_FENCE_GATE:
|
|
case E_BLOCK_DAYLIGHT_SENSOR:
|
|
case E_BLOCK_DETECTOR_RAIL:
|
|
case E_BLOCK_DISPENSER:
|
|
case E_BLOCK_DROPPER:
|
|
case E_BLOCK_FENCE_GATE:
|
|
case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
|
|
case E_BLOCK_HOPPER:
|
|
case E_BLOCK_INACTIVE_COMPARATOR:
|
|
case E_BLOCK_IRON_DOOR:
|
|
case E_BLOCK_IRON_TRAPDOOR:
|
|
case E_BLOCK_JUNGLE_DOOR:
|
|
case E_BLOCK_JUNGLE_FENCE_GATE:
|
|
case E_BLOCK_LEVER:
|
|
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
|
|
case E_BLOCK_NOTE_BLOCK:
|
|
case E_BLOCK_POWERED_RAIL:
|
|
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_SPRUCE_DOOR:
|
|
case E_BLOCK_SPRUCE_FENCE_GATE:
|
|
case E_BLOCK_STICKY_PISTON:
|
|
case E_BLOCK_STONE_BUTTON:
|
|
case E_BLOCK_STONE_PRESSURE_PLATE:
|
|
case E_BLOCK_TNT:
|
|
case E_BLOCK_TRAPDOOR:
|
|
case E_BLOCK_TRAPPED_CHEST:
|
|
case E_BLOCK_TRIPWIRE_HOOK:
|
|
case E_BLOCK_TRIPWIRE:
|
|
case E_BLOCK_WOODEN_BUTTON:
|
|
case E_BLOCK_WOODEN_DOOR:
|
|
case E_BLOCK_WOODEN_PRESSURE_PLATE:
|
|
case E_BLOCK_PISTON:
|
|
{
|
|
return true;
|
|
}
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
inline static bool AreCoordsOnChunkBoundary(int a_BlockX, int a_BlockY, int a_BlockZ)
|
|
{
|
|
return ( // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks
|
|
((a_BlockX % cChunkDef::Width) <= 1) ||
|
|
((a_BlockX % cChunkDef::Width) >= 14) ||
|
|
((a_BlockZ % cChunkDef::Width) <= 1) ||
|
|
((a_BlockZ % cChunkDef::Width) >= 14)
|
|
);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::~cIncrementalRedstoneSimulator()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk, ChunkType * a_OtherChunk)
|
|
{
|
|
if ((a_Chunk == NULL) || !a_Chunk->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// We may be called with coordinates in a chunk that is not the first chunk parameter
|
|
// In that case, the actual chunk (which the coordinates are in), will be passed as the second parameter
|
|
// Use that Chunk pointer to get a relative position
|
|
|
|
int RelX = 0;
|
|
int RelZ = 0;
|
|
BLOCKTYPE Block;
|
|
NIBBLETYPE Meta;
|
|
|
|
if (a_OtherChunk != NULL)
|
|
{
|
|
RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width;
|
|
RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width;
|
|
a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
|
|
|
|
// If a_OtherChunk is passed (not NULL), it is the chunk that had a block change, and a_Chunk will be the neighbouring chunk of that block
|
|
// Because said neighbouring chunk does not know of this change but still needs to update its redstone, we set it to dirty
|
|
a_Chunk->SetIsRedstoneDirty(true);
|
|
}
|
|
else
|
|
{
|
|
RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
|
|
RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
|
|
a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
|
|
}
|
|
|
|
// Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid
|
|
// Checking only when a block is changed, as opposed to every tick, also improves performance
|
|
|
|
cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData * SimulatorChunkData = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData());
|
|
if (SimulatorChunkData == NULL)
|
|
{
|
|
return;
|
|
}
|
|
PoweredBlocksList & PoweredBlocks = SimulatorChunkData->m_PoweredBlocks;
|
|
for (typename PoweredBlocksList::iterator itr = PoweredBlocks.begin(); itr != PoweredBlocks.end();)
|
|
{
|
|
if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
|
|
{
|
|
++itr;
|
|
continue;
|
|
}
|
|
|
|
if (!IsPotentialSource(Block))
|
|
{
|
|
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
|
|
itr = PoweredBlocks.erase(itr);
|
|
continue;
|
|
}
|
|
else if (
|
|
// Changeable sources
|
|
((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) ||
|
|
((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
|
|
((Block == E_BLOCK_DETECTOR_RAIL) && ((Meta & 0x08) == 0)) ||
|
|
(((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) ||
|
|
((Block == E_BLOCK_TRIPWIRE_HOOK) && ((Meta & 0x08) == 0))
|
|
)
|
|
{
|
|
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
|
|
itr = PoweredBlocks.erase(itr);
|
|
continue;
|
|
}
|
|
++itr;
|
|
}
|
|
|
|
LinkedBlocksList & LinkedPoweredBlocks = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks;
|
|
// We loop through all values (insteading of breaking out at the first) to make sure everything is gone, as there can be multiple SourceBlock entries for one AddBlock coordinate
|
|
for (typename LinkedBlocksList::iterator itr = LinkedPoweredBlocks.begin(); itr != LinkedPoweredBlocks.end();)
|
|
{
|
|
if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
|
|
{
|
|
if (!IsPotentialSource(Block))
|
|
{
|
|
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
|
|
itr = LinkedPoweredBlocks.erase(itr);
|
|
continue;
|
|
}
|
|
else if (
|
|
// Things that can send power through a block but which depends on meta
|
|
((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) ||
|
|
((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
|
|
(((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta)))
|
|
)
|
|
{
|
|
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
|
|
itr = LinkedPoweredBlocks.erase(itr);
|
|
continue;
|
|
}
|
|
}
|
|
else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
|
|
{
|
|
if (!IsViableMiddleBlock(Block))
|
|
{
|
|
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
|
|
itr = LinkedPoweredBlocks.erase(itr);
|
|
continue;
|
|
}
|
|
}
|
|
++itr;
|
|
}
|
|
|
|
SimulatedPlayerToggleableList & SimulatedPlayerToggleableBlocks = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_SimulatedPlayerToggleableBlocks;
|
|
for (typename SimulatedPlayerToggleableList::iterator itr = SimulatedPlayerToggleableBlocks.begin(); itr != SimulatedPlayerToggleableBlocks.end(); ++itr)
|
|
{
|
|
if (!itr->a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!IsAllowedBlock(Block))
|
|
{
|
|
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z);
|
|
SimulatedPlayerToggleableBlocks.erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RepeatersDelayList & RepeatersDelayList = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList;
|
|
for (typename RepeatersDelayList::iterator itr = RepeatersDelayList.begin(); itr != RepeatersDelayList.end(); ++itr)
|
|
{
|
|
if (!itr->a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF))
|
|
{
|
|
RepeatersDelayList.erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (a_OtherChunk != NULL)
|
|
{
|
|
// DO NOT touch our chunk's data structure if we are being called with coordinates from another chunk - this one caused me massive grief :P
|
|
return;
|
|
}
|
|
|
|
cCoordWithBlockAndBoolVector & RedstoneSimulatorChunkData = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_ChunkData;
|
|
for (cCoordWithBlockAndBoolVector::iterator itr = RedstoneSimulatorChunkData.begin(); itr != RedstoneSimulatorChunkData.end(); ++itr)
|
|
{
|
|
if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block
|
|
{
|
|
if (!IsAllowedBlock(Block))
|
|
{
|
|
itr->DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list
|
|
}
|
|
else
|
|
{
|
|
itr->DataTwo = false;
|
|
itr->Data = Block; // Update block information
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!IsAllowedBlock(Block))
|
|
{
|
|
return;
|
|
}
|
|
|
|
cCoordWithBlockAndBoolVector & QueuedData = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData;
|
|
for (cCoordWithBlockAndBoolVector::iterator itr = QueuedData.begin(); itr != QueuedData.end(); ++itr)
|
|
{
|
|
if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
|
|
{
|
|
// Can't have duplicates in here either, in case something adds the block again before the structure can written to the main chunk data
|
|
return;
|
|
}
|
|
}
|
|
QueuedData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false));
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, ChunkType * a_Chunk)
|
|
{
|
|
m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData();
|
|
if (m_RedstoneSimulatorChunkData == NULL)
|
|
{
|
|
m_RedstoneSimulatorChunkData = new cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData();
|
|
a_Chunk->SetRedstoneSimulatorData(m_RedstoneSimulatorChunkData);
|
|
}
|
|
if (m_RedstoneSimulatorChunkData->m_ChunkData.empty() && ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_RedstoneSimulatorChunkData->m_ChunkData.insert(
|
|
m_RedstoneSimulatorChunkData->m_ChunkData.end(),
|
|
m_RedstoneSimulatorChunkData ->m_QueuedChunkData.begin(),
|
|
m_RedstoneSimulatorChunkData ->m_QueuedChunkData.end());
|
|
|
|
m_RedstoneSimulatorChunkData->m_QueuedChunkData.clear();
|
|
|
|
m_PoweredBlocks = &m_RedstoneSimulatorChunkData->m_PoweredBlocks;
|
|
m_RepeatersDelayList = &m_RedstoneSimulatorChunkData->m_RepeatersDelayList;
|
|
m_SimulatedPlayerToggleableBlocks = &m_RedstoneSimulatorChunkData->m_SimulatedPlayerToggleableBlocks;
|
|
m_LinkedPoweredBlocks = &m_RedstoneSimulatorChunkData->m_LinkedBlocks;
|
|
m_Chunk = a_Chunk;
|
|
bool ShouldUpdateSimulateOnceBlocks = false;
|
|
|
|
if (a_Chunk->IsRedstoneDirty())
|
|
{
|
|
// Simulate the majority of devices only if something (blockwise or power-wise) has changed
|
|
// Make sure to allow the chunk to resimulate after the initial run if there was a power change (ShouldUpdateSimulateOnceBlocks helps to do this)
|
|
a_Chunk->SetIsRedstoneDirty(false);
|
|
ShouldUpdateSimulateOnceBlocks = true;
|
|
}
|
|
|
|
HandleRedstoneRepeaterDelays();
|
|
|
|
for (cCoordWithBlockAndBoolVector::iterator dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.begin(); dataitr != m_RedstoneSimulatorChunkData->m_ChunkData.end();)
|
|
{
|
|
if (dataitr->DataTwo)
|
|
{
|
|
dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.erase(dataitr);
|
|
continue;
|
|
}
|
|
|
|
switch (dataitr->Data)
|
|
{
|
|
case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_TRIPWIRE: HandleTripwire(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_TRIPWIRE_HOOK: HandleTripwireHook(dataitr->x, dataitr->y, dataitr->z); break;
|
|
|
|
case E_BLOCK_WOODEN_PRESSURE_PLATE:
|
|
case E_BLOCK_STONE_PRESSURE_PLATE:
|
|
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
|
|
case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
|
|
{
|
|
HandlePressurePlate(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
if (ShouldUpdateSimulateOnceBlocks)
|
|
{
|
|
switch (dataitr->Data)
|
|
{
|
|
case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_IRON_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break;
|
|
case E_BLOCK_TRAPPED_CHEST: HandleTrappedChest(dataitr->x, dataitr->y, dataitr->z); break;
|
|
|
|
case E_BLOCK_ACTIVATOR_RAIL:
|
|
case E_BLOCK_DETECTOR_RAIL:
|
|
case E_BLOCK_POWERED_RAIL:
|
|
{
|
|
HandleRail(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
|
|
break;
|
|
}
|
|
case E_BLOCK_ACACIA_DOOR:
|
|
case E_BLOCK_BIRCH_DOOR:
|
|
case E_BLOCK_DARK_OAK_DOOR:
|
|
case E_BLOCK_JUNGLE_DOOR:
|
|
case E_BLOCK_SPRUCE_DOOR:
|
|
case E_BLOCK_WOODEN_DOOR:
|
|
case E_BLOCK_IRON_DOOR:
|
|
{
|
|
HandleDoor(dataitr->x, dataitr->y, dataitr->z);
|
|
break;
|
|
}
|
|
case E_BLOCK_ACACIA_FENCE_GATE:
|
|
case E_BLOCK_BIRCH_FENCE_GATE:
|
|
case E_BLOCK_DARK_OAK_FENCE_GATE:
|
|
case E_BLOCK_FENCE_GATE:
|
|
case E_BLOCK_JUNGLE_FENCE_GATE:
|
|
case E_BLOCK_SPRUCE_FENCE_GATE:
|
|
{
|
|
HandleFenceGate(dataitr->x, dataitr->y, dataitr->z);
|
|
break;
|
|
}
|
|
case E_BLOCK_REDSTONE_LAMP_OFF:
|
|
case E_BLOCK_REDSTONE_LAMP_ON:
|
|
{
|
|
HandleRedstoneLamp(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
|
|
break;
|
|
}
|
|
case E_BLOCK_DISPENSER:
|
|
case E_BLOCK_DROPPER:
|
|
{
|
|
HandleDropSpenser(dataitr->x, dataitr->y, dataitr->z);
|
|
break;
|
|
}
|
|
case E_BLOCK_PISTON:
|
|
case E_BLOCK_STICKY_PISTON:
|
|
{
|
|
HandlePiston(dataitr->x, dataitr->y, dataitr->z);
|
|
break;
|
|
}
|
|
case E_BLOCK_REDSTONE_REPEATER_OFF:
|
|
case E_BLOCK_REDSTONE_REPEATER_ON:
|
|
{
|
|
HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
|
|
break;
|
|
}
|
|
case E_BLOCK_REDSTONE_TORCH_OFF:
|
|
case E_BLOCK_REDSTONE_TORCH_ON:
|
|
{
|
|
HandleRedstoneTorch(dataitr->x, dataitr->y, dataitr->z, dataitr->Data);
|
|
break;
|
|
}
|
|
case E_BLOCK_STONE_BUTTON:
|
|
case E_BLOCK_WOODEN_BUTTON:
|
|
{
|
|
HandleRedstoneButton(dataitr->x, dataitr->y, dataitr->z);
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
++dataitr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, ChunkType * a_Chunk)
|
|
{
|
|
if (AreCoordsOnChunkBoundary(a_BlockX, a_BlockY, a_BlockZ))
|
|
{
|
|
// On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk)
|
|
AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
|
|
|
|
// Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position
|
|
// RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordinates are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries
|
|
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk);
|
|
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk);
|
|
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk);
|
|
RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 2), a_Chunk);
|
|
|
|
return;
|
|
}
|
|
|
|
// Not on boundary, just alert this chunk for speed
|
|
AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneTorch(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState)
|
|
{
|
|
static const struct // Define which directions the torch can power
|
|
{
|
|
int x, y, z;
|
|
} gCrossCoords[] =
|
|
{
|
|
{ 1, 0, 0},
|
|
{-1, 0, 0},
|
|
{ 0, 0, 1},
|
|
{ 0, 0, -1},
|
|
{ 0, 1, 0},
|
|
} ;
|
|
|
|
if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON)
|
|
{
|
|
// Check if the block the torch is on is powered
|
|
int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ;
|
|
AddFaceDirection(X, Y, Z, GetHandlerCompileTime<E_BLOCK_TORCH>::type::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on
|
|
|
|
ChunkType * Neighbour = m_Chunk->GetRelNeighborChunk(X, Z);
|
|
if ((Neighbour == NULL) || !Neighbour->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour))
|
|
{
|
|
// There was a match, torch goes off
|
|
m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ));
|
|
return;
|
|
}
|
|
|
|
// Torch still on, make all 4(X, Z) + 1(Y) sides powered
|
|
for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
|
|
{
|
|
BLOCKTYPE Type = 0;
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type))
|
|
{
|
|
continue;
|
|
}
|
|
if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last)
|
|
{
|
|
if (
|
|
IsMechanism(Type) && // Is it a mechanism? Not block/other torch etc.
|
|
(!Vector3i(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on
|
|
)
|
|
{
|
|
SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Top side, power whatever is there, including blocks
|
|
SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
// Power all blocks surrounding block above torch
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YP);
|
|
}
|
|
}
|
|
|
|
if (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) != 0x5) // Is torch standing on ground? If NOT (i.e. on wall), power block beneath
|
|
{
|
|
BLOCKTYPE Type = m_Chunk->GetBlock(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ);
|
|
|
|
if (IsMechanism(Type)) // Still can't make a normal block powered though!
|
|
{
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check if the block the torch is on is powered
|
|
int X = a_RelBlockX; int Y = a_RelBlockY; int Z = a_RelBlockZ;
|
|
AddFaceDirection(X, Y, Z, GetHandlerCompileTime<E_BLOCK_TORCH>::type::MetaDataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)), true); // Inverse true to get the block torch is on
|
|
|
|
ChunkType * Neighbour = m_Chunk->GetRelNeighborChunk(X, Z);
|
|
if ((Neighbour == NULL) || !Neighbour->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// See if off state torch can be turned on again
|
|
if (AreCoordsDirectlyPowered(X, Y, Z, Neighbour))
|
|
{
|
|
return; // Something matches, torch still powered
|
|
}
|
|
|
|
// Block torch on not powered, can be turned on again!
|
|
m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); // Set self as powered
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneLever(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
if (IsLeverOn(Meta))
|
|
{
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
|
|
eBlockFace Dir = GetHandlerCompileTime<E_BLOCK_LEVER>::type::BlockMetaDataToBlockFace(Meta);
|
|
|
|
Dir = ReverseBlockFace(Dir);
|
|
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Dir);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
NIBBLETYPE MetaData = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
|
|
if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true))
|
|
{
|
|
if ((MetaData & 0x4) == 0)
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4);
|
|
m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
|
|
}
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false))
|
|
{
|
|
if ((MetaData & 0x4) != 0)
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & ~0x04);
|
|
m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
|
|
}
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneButton(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
if (IsButtonOn(Meta))
|
|
{
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
|
|
eBlockFace Dir = GetHandlerCompileTime<E_BLOCK_STONE_BUTTON>::type::BlockMetaDataToBlockFace(Meta);
|
|
Dir = ReverseBlockFace(Dir);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Dir);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
static const struct // Define which directions the wire can receive power from
|
|
{
|
|
int x, y, z;
|
|
} gCrossCoords[] =
|
|
{
|
|
{ 1, 0, 0}, /* Wires on same level start */
|
|
{-1, 0, 0},
|
|
{ 0, 0, 1},
|
|
{ 0, 0, -1}, /* Wires on same level stop */
|
|
{ 1, 1, 0}, /* Wires one higher, surrounding self start */
|
|
{-1, 1, 0},
|
|
{ 0, 1, 1},
|
|
{ 0, 1, -1}, /* Wires one higher, surrounding self stop */
|
|
{ 1, -1, 0}, /* Wires one lower, surrounding self start */
|
|
{-1, -1, 0},
|
|
{ 0, -1, 1},
|
|
{ 0, -1, -1}, /* Wires one lower, surrounding self stop */
|
|
} ;
|
|
|
|
static const struct // Define which directions the wire will check for repeater prescence
|
|
{
|
|
int x, y, z;
|
|
} gSideCoords[] =
|
|
{
|
|
{ 1, 0, 0 },
|
|
{-1, 0, 0 },
|
|
{ 0, 0, 1 },
|
|
{ 0, 0, -1 },
|
|
{ 0, 1, 0 },
|
|
};
|
|
|
|
// Check to see if directly beside a power source
|
|
unsigned char MyPower;
|
|
if (!IsWirePowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower))
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0);
|
|
this->m_World.WakeUpSimulators(BlockX, a_RelBlockY, BlockZ);
|
|
return;
|
|
}
|
|
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
|
|
if (MyPower < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MyPower--;
|
|
|
|
for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power
|
|
{
|
|
if ((i >= 4) && (i <= 7)) // If we are currently checking for wire surrounding ourself one block above...
|
|
{
|
|
BLOCKTYPE Type = 0;
|
|
if (a_RelBlockY + 1 >= cChunkDef::Height)
|
|
{
|
|
continue;
|
|
}
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, Type))
|
|
{
|
|
continue;
|
|
}
|
|
if (cBlockInfo::IsSolid(Type)) // If there is something solid above us (wire cut off)...
|
|
{
|
|
continue; // We don't receive power from that wire
|
|
}
|
|
}
|
|
else if ((i >= 8) && (i <= 11)) // See above, but this is for wire below us
|
|
{
|
|
BLOCKTYPE Type = 0;
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY, a_RelBlockZ + gCrossCoords[i].z, Type))
|
|
{
|
|
continue;
|
|
}
|
|
if (cBlockInfo::IsSolid(Type))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
BLOCKTYPE Type = 0;
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type))
|
|
{
|
|
continue;
|
|
}
|
|
if (Type == E_BLOCK_REDSTONE_WIRE)
|
|
{
|
|
SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < ARRAYCOUNT(gSideCoords); i++) // Look for repeaters immediately surrounding self and try to power them
|
|
{
|
|
BLOCKTYPE Type = 0;
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, Type))
|
|
{
|
|
continue;
|
|
}
|
|
if (Type == E_BLOCK_REDSTONE_REPEATER_OFF)
|
|
{
|
|
SetBlockPowered(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
}
|
|
}
|
|
|
|
// Wire still powered, power blocks beneath
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, MyPower);
|
|
|
|
switch (GetWireDirection(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
case REDSTONE_NONE:
|
|
{
|
|
SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower);
|
|
break;
|
|
}
|
|
case REDSTONE_X_POS:
|
|
{
|
|
SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower);
|
|
break;
|
|
}
|
|
case REDSTONE_X_NEG:
|
|
{
|
|
SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower);
|
|
break;
|
|
}
|
|
case REDSTONE_Z_POS:
|
|
{
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower);
|
|
break;
|
|
}
|
|
case REDSTONE_Z_NEG:
|
|
{
|
|
SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState)
|
|
{
|
|
/* Repeater Orientation Mini Guide:
|
|
===================================
|
|
|
|
|
|
|
| Z Axis
|
|
V
|
|
|
|
X Axis ---->
|
|
|
|
Repeater directions, values from a WorldType::GetBlockMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) lookup:
|
|
|
|
East (Right) (X+): 0x1
|
|
West (Left) (X-): 0x3
|
|
North (Up) (Z-): 0x2
|
|
South (Down) (Z+): 0x0
|
|
// TODO: Add E_META_XXX enum entries for all meta values and update project with them
|
|
|
|
Sun rises from East (X+)
|
|
|
|
*/
|
|
|
|
// Create a variable holding my meta to avoid multiple lookups.
|
|
NIBBLETYPE a_Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
bool IsOn = (a_MyState == E_BLOCK_REDSTONE_REPEATER_ON);
|
|
|
|
if (!IsRepeaterLocked(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta)) // If we're locked, change nothing. Otherwise:
|
|
{
|
|
bool IsSelfPowered = IsRepeaterPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta);
|
|
if (IsSelfPowered && !IsOn) // Queue a power change if powered, but not on and not locked.
|
|
{
|
|
QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, true);
|
|
}
|
|
else if (!IsSelfPowered && IsOn) // Queue a power change if unpowered, on, and not locked.
|
|
{
|
|
QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneRepeaterDelays()
|
|
{
|
|
for (typename RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end();)
|
|
{
|
|
if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks?
|
|
{
|
|
int RelBlockX = itr->a_RelBlockPos.x;
|
|
int RelBlockY = itr->a_RelBlockPos.y;
|
|
int RelBlockZ = itr->a_RelBlockPos.z;
|
|
BLOCKTYPE Block;
|
|
NIBBLETYPE Meta;
|
|
m_Chunk->GetBlockTypeMeta(RelBlockX, RelBlockY, RelBlockZ, Block, Meta);
|
|
if (itr->ShouldPowerOn)
|
|
{
|
|
if (Block != E_BLOCK_REDSTONE_REPEATER_ON) // For performance
|
|
{
|
|
m_Chunk->SetBlock(itr->a_RelBlockPos, E_BLOCK_REDSTONE_REPEATER_ON, Meta);
|
|
}
|
|
|
|
switch (Meta & 0x3) // We only want the direction (bottom) bits
|
|
{
|
|
case 0x0:
|
|
{
|
|
SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ - 1, RelBlockX, RelBlockY, RelBlockZ);
|
|
SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZM);
|
|
break;
|
|
}
|
|
case 0x1:
|
|
{
|
|
SetBlockPowered(RelBlockX + 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ);
|
|
SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XP);
|
|
break;
|
|
}
|
|
case 0x2:
|
|
{
|
|
SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ + 1, RelBlockX, RelBlockY, RelBlockZ);
|
|
SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZP);
|
|
break;
|
|
}
|
|
case 0x3:
|
|
{
|
|
SetBlockPowered(RelBlockX - 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ);
|
|
SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XM);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (Block != E_BLOCK_REDSTONE_REPEATER_OFF)
|
|
{
|
|
m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta);
|
|
}
|
|
itr = m_RepeatersDelayList->erase(itr);
|
|
}
|
|
else
|
|
{
|
|
LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks);
|
|
itr->a_ElapsedTicks++;
|
|
itr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandlePiston(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
if (IsPistonPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7)) // We only want the bottom three bits (4th controls extended-ness)
|
|
{
|
|
GetHandlerCompileTime<E_BLOCK_PISTON>::type::ExtendPiston(BlockX, a_RelBlockY, BlockZ, &this->m_World);
|
|
}
|
|
else
|
|
{
|
|
GetHandlerCompileTime<E_BLOCK_PISTON>::type::RetractPiston(BlockX, a_RelBlockY, BlockZ, &this->m_World);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleDropSpenser(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
class cSetPowerToDropSpenser :
|
|
public cRedstonePoweredCallback
|
|
{
|
|
bool m_IsPowered;
|
|
public:
|
|
cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {}
|
|
|
|
virtual bool Item(cRedstonePoweredEntity * a_DropSpenser) override
|
|
{
|
|
a_DropSpenser->SetRedstonePower(m_IsPowered);
|
|
return false;
|
|
}
|
|
} DrSpSP (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ));
|
|
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, DrSpSP);
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRedstoneLamp(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState)
|
|
{
|
|
if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF)
|
|
{
|
|
if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTNT(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("game.tnt.primed", (double)BlockX, (double)a_RelBlockY, (double)BlockZ, 0.5f, 0.6f);
|
|
m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_AIR, 0);
|
|
this->m_World.SpawnPrimedTNT(BlockX + 0.5, a_RelBlockY + 0.5, BlockZ + 0.5); // 80 ticks to boom
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleDoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
typedef typename GetHandlerCompileTime<E_BLOCK_WOODEN_DOOR>::type DoorHandler;
|
|
|
|
if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true))
|
|
{
|
|
cChunkInterface ChunkInterface(this->m_World.GetChunkMap());
|
|
if (!DoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ))
|
|
{
|
|
DoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, true);
|
|
m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
|
|
}
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false))
|
|
{
|
|
cChunkInterface ChunkInterface(this->m_World.GetChunkMap());
|
|
if (DoorHandler::IsOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ))
|
|
{
|
|
DoorHandler::SetOpen(ChunkInterface, BlockX, a_RelBlockY, BlockZ, false);
|
|
m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0);
|
|
}
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleCommandBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
class cSetPowerToCommandBlock :
|
|
public cRedstonePoweredCallback
|
|
{
|
|
bool m_IsPowered;
|
|
public:
|
|
cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {}
|
|
|
|
virtual bool Item(cRedstonePoweredEntity * a_CommandBlock) override
|
|
{
|
|
a_CommandBlock->SetRedstonePower(m_IsPowered);
|
|
return false;
|
|
}
|
|
} CmdBlockSP (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ));
|
|
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, CmdBlockSP);
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleRail(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType)
|
|
{
|
|
switch (a_MyType)
|
|
{
|
|
case E_BLOCK_DETECTOR_RAIL:
|
|
{
|
|
if ((m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x08) == 0x08)
|
|
{
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_MyType);
|
|
}
|
|
break;
|
|
}
|
|
case E_BLOCK_ACTIVATOR_RAIL:
|
|
case E_BLOCK_POWERED_RAIL:
|
|
{
|
|
if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0x08);
|
|
}
|
|
else
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x07);
|
|
}
|
|
break;
|
|
}
|
|
default: LOGD("Unhandled type of rail in %s", __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTrapdoor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ))
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true))
|
|
{
|
|
this->m_World.SetTrapdoorOpen(BlockX, a_RelBlockY, BlockZ, true);
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false))
|
|
{
|
|
this->m_World.SetTrapdoorOpen(BlockX, a_RelBlockY, BlockZ, false);
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
bool m_bAreCoordsPowered = AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
|
|
if (m_bAreCoordsPowered)
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true))
|
|
{
|
|
class cSetPowerToNoteBlock :
|
|
public cRedstonePoweredCallback
|
|
{
|
|
public:
|
|
cSetPowerToNoteBlock() {}
|
|
|
|
virtual bool Item(cRedstonePoweredEntity * a_NoteBlock) override
|
|
{
|
|
a_NoteBlock->SetRedstonePower(true);
|
|
return false;
|
|
}
|
|
} NoteBlockSP;
|
|
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
m_Chunk->DoWithRedstonePoweredEntityAt(BlockX, a_RelBlockY, BlockZ, NoteBlockSP);
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false))
|
|
{
|
|
SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX, BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
int ChunkX, ChunkZ;
|
|
cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
|
|
|
|
if (!this->m_World.IsChunkLighted(ChunkX, ChunkZ))
|
|
{
|
|
this->m_World.QueueLightChunk(ChunkX, ChunkZ);
|
|
}
|
|
else
|
|
{
|
|
if (m_Chunk->GetTimeAlteredLight(this->m_World.GetBlockSkyLight(BlockX, a_RelBlockY + 1, BlockZ)) > 8)
|
|
{
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
}
|
|
else
|
|
{
|
|
WakeUp(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
switch (a_MyType)
|
|
{
|
|
case E_BLOCK_STONE_PRESSURE_PLATE:
|
|
{
|
|
// MCS feature - stone pressure plates can only be triggered by players :D
|
|
cPlayer * a_Player = this->m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.5f, false);
|
|
|
|
if (a_Player != NULL)
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1);
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType);
|
|
}
|
|
else
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
break;
|
|
}
|
|
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
|
|
{
|
|
class cPressurePlateCallback :
|
|
public cEntityCallback
|
|
{
|
|
public:
|
|
cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) :
|
|
m_NumberOfEntities(0),
|
|
m_X(a_BlockX),
|
|
m_Y(a_BlockY),
|
|
m_Z(a_BlockZ)
|
|
{
|
|
}
|
|
|
|
virtual bool Item(cEntity * a_Entity) override
|
|
{
|
|
Vector3f EntityPos = a_Entity->GetPosition();
|
|
Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f);
|
|
double Distance = (EntityPos - BlockPos).Length();
|
|
|
|
if (Distance <= 0.5)
|
|
{
|
|
m_NumberOfEntities++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GetPowerLevel(unsigned char & a_PowerLevel) const
|
|
{
|
|
a_PowerLevel = std::min(m_NumberOfEntities, MAX_POWER_LEVEL);
|
|
return (a_PowerLevel > 0);
|
|
}
|
|
|
|
protected:
|
|
int m_NumberOfEntities;
|
|
|
|
int m_X;
|
|
int m_Y;
|
|
int m_Z;
|
|
};
|
|
|
|
cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ);
|
|
this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback);
|
|
|
|
unsigned char Power;
|
|
NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
if (PressurePlateCallback.GetPowerLevel(Power))
|
|
{
|
|
if (Meta == E_META_PRESSURE_PLATE_RAISED)
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F);
|
|
}
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED);
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType);
|
|
}
|
|
else
|
|
{
|
|
if (Meta == E_META_PRESSURE_PLATE_DEPRESSED)
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F);
|
|
}
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
|
|
{
|
|
class cPressurePlateCallback :
|
|
public cEntityCallback
|
|
{
|
|
public:
|
|
cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) :
|
|
m_NumberOfEntities(0),
|
|
m_X(a_BlockX),
|
|
m_Y(a_BlockY),
|
|
m_Z(a_BlockZ)
|
|
{
|
|
}
|
|
|
|
virtual bool Item(cEntity * a_Entity) override
|
|
{
|
|
Vector3f EntityPos = a_Entity->GetPosition();
|
|
Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f);
|
|
double Distance = (EntityPos - BlockPos).Length();
|
|
|
|
if (Distance <= 0.5)
|
|
{
|
|
m_NumberOfEntities++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GetPowerLevel(unsigned char & a_PowerLevel) const
|
|
{
|
|
a_PowerLevel = std::min((int)ceil(m_NumberOfEntities / 10.f), MAX_POWER_LEVEL);
|
|
return (a_PowerLevel > 0);
|
|
}
|
|
|
|
protected:
|
|
int m_NumberOfEntities;
|
|
|
|
int m_X;
|
|
int m_Y;
|
|
int m_Z;
|
|
};
|
|
|
|
cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ);
|
|
this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback);
|
|
|
|
unsigned char Power;
|
|
NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
if (PressurePlateCallback.GetPowerLevel(Power))
|
|
{
|
|
if (Meta == E_META_PRESSURE_PLATE_RAISED)
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F);
|
|
}
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED);
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType);
|
|
}
|
|
else
|
|
{
|
|
if (Meta == E_META_PRESSURE_PLATE_DEPRESSED)
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F);
|
|
}
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case E_BLOCK_WOODEN_PRESSURE_PLATE:
|
|
{
|
|
class cPressurePlateCallback :
|
|
public cEntityCallback
|
|
{
|
|
public:
|
|
cPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ) :
|
|
m_FoundEntity(false),
|
|
m_X(a_BlockX),
|
|
m_Y(a_BlockY),
|
|
m_Z(a_BlockZ)
|
|
{
|
|
}
|
|
|
|
virtual bool Item(cEntity * a_Entity) override
|
|
{
|
|
Vector3f EntityPos = a_Entity->GetPosition();
|
|
Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f);
|
|
double Distance = (EntityPos - BlockPos).Length();
|
|
|
|
if (Distance <= 0.5)
|
|
{
|
|
m_FoundEntity = true;
|
|
return true; // Break out, we only need to know for plates that at least one entity is on top
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FoundEntity(void) const
|
|
{
|
|
return m_FoundEntity;
|
|
}
|
|
|
|
protected:
|
|
bool m_FoundEntity;
|
|
|
|
int m_X;
|
|
int m_Y;
|
|
int m_Z;
|
|
} ;
|
|
|
|
cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ);
|
|
this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback);
|
|
|
|
NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
if (PressurePlateCallback.FoundEntity())
|
|
{
|
|
if (Meta == E_META_PRESSURE_PLATE_RAISED)
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F);
|
|
}
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED);
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType);
|
|
}
|
|
else
|
|
{
|
|
if (Meta == E_META_PRESSURE_PLATE_DEPRESSED)
|
|
{
|
|
m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F);
|
|
}
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
LOGD("Unimplemented pressure plate type %s in cRedstoneSimulator", ItemToFullString(cItem(a_MyType)).c_str());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX;
|
|
int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ;
|
|
int RelX = a_RelBlockX, RelZ = a_RelBlockZ;
|
|
bool FoundActivated = false;
|
|
eBlockFace FaceToGoTowards = GetHandlerCompileTime<E_BLOCK_TRIPWIRE_HOOK>::type::MetadataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ));
|
|
|
|
for (int i = 0; i < 40; ++i) // Tripwires can be connected up to 40 blocks
|
|
{
|
|
BLOCKTYPE Type;
|
|
NIBBLETYPE Meta;
|
|
|
|
AddFaceDirection(RelX, a_RelBlockY, RelZ, FaceToGoTowards);
|
|
m_Chunk->UnboundedRelGetBlock(RelX, a_RelBlockY, RelZ, Type, Meta);
|
|
|
|
if (Type == E_BLOCK_TRIPWIRE)
|
|
{
|
|
if (Meta == 0x1)
|
|
{
|
|
FoundActivated = true;
|
|
}
|
|
}
|
|
else if (Type == E_BLOCK_TRIPWIRE_HOOK)
|
|
{
|
|
if (ReverseBlockFace( GetHandlerCompileTime<E_BLOCK_TRIPWIRE_HOOK>::type::MetadataToDirection(Meta)) == FaceToGoTowards)
|
|
{
|
|
// Other hook facing in opposite direction - circuit completed!
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Tripwire hook not connected at all, AND away all the power state bits
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Tripwire hook not connected at all, AND away all the power state bits
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (FoundActivated)
|
|
{
|
|
// Connected and activated, set the 3rd and 4th highest bits
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0xC);
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
}
|
|
else
|
|
{
|
|
// Connected but not activated, AND away the highest bit
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7) | 0x4);
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
}
|
|
|
|
template <class ChestType>
|
|
class cGetTrappedChestPlayers :
|
|
public cItemCallback<ChestType>
|
|
{
|
|
public:
|
|
cGetTrappedChestPlayers(void) :
|
|
m_NumberOfPlayers(0)
|
|
{
|
|
}
|
|
|
|
virtual ~cGetTrappedChestPlayers()
|
|
{
|
|
}
|
|
|
|
virtual bool Item(ChestType * a_Chest) override
|
|
{
|
|
ASSERT(a_Chest->GetBlockType() == E_BLOCK_TRAPPED_CHEST);
|
|
m_NumberOfPlayers = a_Chest->GetNumberOfPlayers();
|
|
return (m_NumberOfPlayers <= 0);
|
|
}
|
|
|
|
unsigned char GetPowerLevel(void) const
|
|
{
|
|
return std::min(m_NumberOfPlayers, MAX_POWER_LEVEL);
|
|
}
|
|
|
|
private:
|
|
int m_NumberOfPlayers;
|
|
|
|
};
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
cGetTrappedChestPlayers<ChestType> GTCP;
|
|
|
|
int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX;
|
|
int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ;
|
|
if (m_Chunk->DoWithChestAt(BlockX, a_RelBlockY, BlockZ, GTCP))
|
|
{
|
|
SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, GTCP.GetPowerLevel());
|
|
}
|
|
else
|
|
{
|
|
SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX;
|
|
int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ;
|
|
|
|
class cTripwireCallback :
|
|
public cEntityCallback
|
|
{
|
|
public:
|
|
cTripwireCallback(int a_BlockX, int a_BlockY, int a_BlockZ) :
|
|
m_FoundEntity(false),
|
|
m_X(a_BlockX),
|
|
m_Y(a_BlockY),
|
|
m_Z(a_BlockZ)
|
|
{
|
|
}
|
|
|
|
virtual bool Item(cEntity * a_Entity) override
|
|
{
|
|
cBoundingBox bbWire(m_X, m_X + 1, m_Y, m_Y + 0.1, m_Z, m_Z + 1);
|
|
cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
|
|
|
|
if (bbEntity.DoesIntersect(bbWire))
|
|
{
|
|
m_FoundEntity = true;
|
|
return true; // One entity is sufficient to trigger the wire
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FoundEntity(void) const
|
|
{
|
|
return m_FoundEntity;
|
|
}
|
|
|
|
protected:
|
|
bool m_FoundEntity;
|
|
|
|
int m_X;
|
|
int m_Y;
|
|
int m_Z;
|
|
};
|
|
|
|
cTripwireCallback TripwireCallback(BlockX, a_RelBlockY, BlockZ);
|
|
this->m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), TripwireCallback);
|
|
|
|
if (TripwireCallback.FoundEntity())
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1);
|
|
}
|
|
else
|
|
{
|
|
m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, ChunkType * a_Chunk)
|
|
{
|
|
// Torches want to access neighbour's data when on a wall, hence the extra chunk parameter
|
|
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
for (typename PoweredBlocksList::const_iterator itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.begin(); itr != ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.end(); ++itr) // Check powered list
|
|
{
|
|
if (itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list
|
|
{
|
|
if (itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsRepeaterPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta)
|
|
{
|
|
// Repeaters cannot be powered by any face except their back; verify that this is true for a source
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
|
|
for (typename PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr)
|
|
{
|
|
if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; }
|
|
|
|
switch (a_Meta & 0x3)
|
|
{
|
|
case 0x0:
|
|
{
|
|
// Flip the coords to check the back of the repeater
|
|
if (itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ + 1))) { return true; }
|
|
break;
|
|
}
|
|
case 0x1:
|
|
{
|
|
if (itr->a_SourcePos.Equals(Vector3i(BlockX - 1, a_RelBlockY, BlockZ))) { return true; }
|
|
break;
|
|
}
|
|
case 0x2:
|
|
{
|
|
if (itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ - 1))) { return true; }
|
|
break;
|
|
}
|
|
case 0x3:
|
|
{
|
|
if (itr->a_SourcePos.Equals(Vector3i(BlockX + 1, a_RelBlockY, BlockZ))) { return true; }
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr)
|
|
{
|
|
if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; }
|
|
|
|
switch (a_Meta & 0x3)
|
|
{
|
|
case 0x0:
|
|
{
|
|
if (itr->a_MiddlePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ + 1))) { return true; }
|
|
break;
|
|
}
|
|
case 0x1:
|
|
{
|
|
if (itr->a_MiddlePos.Equals(Vector3i(BlockX - 1, a_RelBlockY, BlockZ))) { return true; }
|
|
break;
|
|
}
|
|
case 0x2:
|
|
{
|
|
if (itr->a_MiddlePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ - 1))) { return true; }
|
|
break;
|
|
}
|
|
case 0x3:
|
|
{
|
|
if (itr->a_MiddlePos.Equals(Vector3i(BlockX + 1, a_RelBlockY, BlockZ))) { return true; }
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false; // Couldn't find power source behind repeater
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsRepeaterLocked(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta)
|
|
{
|
|
switch (a_Meta & 0x3) // We only want the 'direction' part of our metadata
|
|
{
|
|
// If the repeater is looking up or down (If parallel to the Z axis)
|
|
case 0x0:
|
|
case 0x2:
|
|
{
|
|
// Check if eastern(right) neighbor is a powered on repeater who is facing us
|
|
BLOCKTYPE Block = 0;
|
|
NIBBLETYPE OtherRepeaterDir = 0;
|
|
if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) // Is right neighbor a powered repeater?
|
|
{
|
|
if ((OtherRepeaterDir & 0x03) == 0x3)
|
|
{
|
|
return true;
|
|
} // If so, I am latched/locked
|
|
}
|
|
|
|
// Check if western(left) neighbor is a powered on repeater who is facing us
|
|
if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON))
|
|
{
|
|
if ((OtherRepeaterDir & 0x03) == 0x1)
|
|
{
|
|
return true;
|
|
} // If so, I am latched/locked
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// If the repeater is looking left or right (If parallel to the x axis)
|
|
case 0x1:
|
|
case 0x3:
|
|
{
|
|
// Check if southern(down) neighbor is a powered on repeater who is facing us
|
|
BLOCKTYPE Block = 0;
|
|
NIBBLETYPE OtherRepeaterDir = 0;
|
|
|
|
if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON))
|
|
{
|
|
if ((OtherRepeaterDir & 0x30 ) == 0x00)
|
|
{
|
|
return true;
|
|
} // If so, am latched/locked
|
|
}
|
|
|
|
// Check if northern(up) neighbor is a powered on repeater who is facing us
|
|
if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON))
|
|
{
|
|
if ((OtherRepeaterDir & 0x03) == 0x02)
|
|
{
|
|
return true;
|
|
} // If so, I am latched/locked
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false; // None of the checks succeeded, I am not a locked repeater
|
|
}
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsPistonPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta)
|
|
{
|
|
// Pistons cannot be powered through their front face; this function verifies that a source meets this requirement
|
|
|
|
eBlockFace Face = GetHandlerCompileTime<E_BLOCK_PISTON>::type::MetaDataToDirection(a_Meta);
|
|
int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX;
|
|
int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ;
|
|
|
|
for (typename PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr)
|
|
{
|
|
if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; }
|
|
|
|
AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face);
|
|
|
|
if (!itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face, true);
|
|
}
|
|
|
|
for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr)
|
|
{
|
|
if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ))) { continue; }
|
|
|
|
AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face);
|
|
|
|
if (!itr->a_MiddlePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
AddFaceDirection(BlockX, a_RelBlockY, BlockZ, Face, true);
|
|
}
|
|
return false; // Source was in front of the piston's front face
|
|
}
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel)
|
|
{
|
|
a_PowerLevel = 0;
|
|
int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX;
|
|
int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ;
|
|
|
|
for (typename PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) // Check powered list
|
|
{
|
|
if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)))
|
|
{
|
|
continue;
|
|
}
|
|
a_PowerLevel = std::max(itr->a_PowerLevel, a_PowerLevel); // Get the highest power level (a_PowerLevel is initialised already and there CAN be multiple levels for one block)
|
|
}
|
|
|
|
for (typename LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list
|
|
{
|
|
if (!itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
BLOCKTYPE Type = E_BLOCK_AIR;
|
|
int RelSourceX = itr->a_SourcePos.x - m_Chunk->GetPosX() * cChunkDef::Width;
|
|
int RelSourceZ = itr->a_SourcePos.z - m_Chunk->GetPosZ() * cChunkDef::Width;
|
|
if (!m_Chunk->UnboundedRelGetBlockType(RelSourceX, itr->a_SourcePos.y, RelSourceZ, Type) || (Type == E_BLOCK_REDSTONE_WIRE))
|
|
{
|
|
continue;
|
|
}
|
|
a_PowerLevel = std::max(itr->a_PowerLevel, a_PowerLevel);
|
|
}
|
|
|
|
return (a_PowerLevel != 0); // Answer the inital question: is the wire powered?
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::AreCoordsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool IsCurrentStatePowered)
|
|
{
|
|
for (typename SimulatedPlayerToggleableList::const_iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr)
|
|
{
|
|
if (itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
|
|
{
|
|
if (itr->WasLastStatePowered != IsCurrentStatePowered) // Was the last power state different to the current?
|
|
{
|
|
return false; // It was, coordinates are no longer simulated
|
|
}
|
|
else
|
|
{
|
|
return true; // It wasn't, don't resimulate block, and allow players to toggle
|
|
}
|
|
}
|
|
}
|
|
return false; // Block wasn't even in the list, not simulated
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetDirectionLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, char a_Direction, unsigned char a_PowerLevel)
|
|
{
|
|
BLOCKTYPE MiddleBlock = 0;
|
|
switch (a_Direction)
|
|
{
|
|
case BLOCK_FACE_XM:
|
|
{
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetBlockLinkedPowered(a_RelBlockX - 2, a_RelBlockY, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
|
|
break;
|
|
}
|
|
case BLOCK_FACE_XP:
|
|
{
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetBlockLinkedPowered(a_RelBlockX + 2, a_RelBlockY, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
|
|
break;
|
|
}
|
|
case BLOCK_FACE_YM:
|
|
{
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 2, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
|
|
break;
|
|
}
|
|
case BLOCK_FACE_YP:
|
|
{
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 2, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
|
|
break;
|
|
}
|
|
case BLOCK_FACE_ZM:
|
|
{
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 2, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
|
|
break;
|
|
}
|
|
case BLOCK_FACE_ZP:
|
|
{
|
|
if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 2, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
SetBlockLinkedPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MiddleBlock, a_PowerLevel);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); // Zombies, that wasn't supposed to happen...
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel)
|
|
{
|
|
static const struct
|
|
{
|
|
int x, y, z;
|
|
} gCrossCoords[] =
|
|
{
|
|
{ 1, 0, 0 },
|
|
{ -1, 0, 0 },
|
|
{ 0, 0, 1 },
|
|
{ 0, 0, -1 },
|
|
{ 0, 1, 0 },
|
|
{ 0, -1, 0 }
|
|
};
|
|
|
|
for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through struct to power all directions
|
|
{
|
|
SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_PowerLevel);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
int SourceX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelSourceX;
|
|
int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ;
|
|
|
|
ChunkType * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); // Adjust coordinates for the later call using these values
|
|
if ((Neighbour == NULL) || !Neighbour->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
PoweredBlocksList & Powered = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_PoweredBlocks; // We need to insert the value into the chunk who owns the block position
|
|
for (typename PoweredBlocksList::iterator itr = Powered.begin(); itr != Powered.end(); ++itr)
|
|
{
|
|
if (
|
|
itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) &&
|
|
itr->a_SourcePos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ))
|
|
)
|
|
{
|
|
// Check for duplicates, update power level, don't add a new listing
|
|
itr->a_PowerLevel = a_PowerLevel;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No need to get neighbouring chunk as we can guarantee that when something is powering us, the entry will be in our chunk
|
|
// TODO: on C++11 support, change this to a llama function pased to a std::remove_if
|
|
for (typename PoweredBlocksList::iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr)
|
|
{
|
|
if (
|
|
itr->a_BlockPos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ)) &&
|
|
itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) &&
|
|
(m_Chunk->GetBlock(a_RelSourceX, a_RelSourceY, a_RelSourceZ) == E_BLOCK_REDSTONE_WIRE)
|
|
)
|
|
{
|
|
BLOCKTYPE Block;
|
|
NIBBLETYPE Meta;
|
|
Neighbour->GetBlockTypeMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Block, Meta);
|
|
|
|
if (Block == E_BLOCK_REDSTONE_WIRE)
|
|
{
|
|
if (Meta < a_PowerLevel)
|
|
{
|
|
m_PoweredBlocks->erase(itr); // Powering source with higher power level, allow it
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Powered wires try to power their source - don't let them!
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sPoweredBlocks RC;
|
|
RC.a_BlockPos = Vector3i(BlockX, a_RelBlockY, BlockZ);
|
|
RC.a_SourcePos = Vector3i(SourceX, a_RelSourceY, SourceZ);
|
|
RC.a_PowerLevel = a_PowerLevel;
|
|
Powered.push_back(RC);
|
|
Neighbour->SetIsRedstoneDirty(true);
|
|
m_Chunk->SetIsRedstoneDirty(true);
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetBlockLinkedPowered(
|
|
int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ,
|
|
int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ,
|
|
int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ,
|
|
BLOCKTYPE a_MiddleBlock, unsigned char a_PowerLevel
|
|
)
|
|
{
|
|
int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX;
|
|
int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ;
|
|
int MiddleX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelMiddleX;
|
|
int MiddleZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelMiddleZ;
|
|
int SourceX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelSourceX;
|
|
int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ;
|
|
|
|
if (!IsViableMiddleBlock(a_MiddleBlock))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ChunkType * Neighbour = m_Chunk->GetNeighborChunk(BlockX, BlockZ);
|
|
if ((Neighbour == NULL) || !Neighbour->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
LinkedBlocksList & Linked = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_LinkedBlocks;
|
|
for (typename LinkedBlocksList::iterator itr = Linked.begin(); itr != Linked.end(); ++itr) // Check linked powered list
|
|
{
|
|
if (
|
|
itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) &&
|
|
itr->a_MiddlePos.Equals(Vector3i(MiddleX, a_RelMiddleY, MiddleZ)) &&
|
|
itr->a_SourcePos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ))
|
|
)
|
|
{
|
|
// Check for duplicates, update power level, don't add a new listing
|
|
itr->a_PowerLevel = a_PowerLevel;
|
|
return;
|
|
}
|
|
}
|
|
|
|
sLinkedPoweredBlocks RC;
|
|
RC.a_BlockPos = Vector3i(BlockX, a_RelBlockY, BlockZ);
|
|
RC.a_MiddlePos = Vector3i(MiddleX, a_RelMiddleY, MiddleZ);
|
|
RC.a_SourcePos = Vector3i(SourceX, a_RelSourceY, SourceZ);
|
|
RC.a_PowerLevel = a_PowerLevel;
|
|
Linked.push_back(RC);
|
|
Neighbour->SetIsRedstoneDirty(true);
|
|
m_Chunk->SetIsRedstoneDirty(true);
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered)
|
|
{
|
|
for (typename SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr)
|
|
{
|
|
if (!itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (itr->WasLastStatePowered != WasLastStatePowered)
|
|
{
|
|
// If power states different, update listing
|
|
itr->WasLastStatePowered = WasLastStatePowered;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// If states the same, just ignore
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We have arrive here; no block must be in list - add one
|
|
sSimulatedPlayerToggleableList RC;
|
|
RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
RC.WasLastStatePowered = WasLastStatePowered;
|
|
m_SimulatedPlayerToggleableBlocks->push_back(RC);
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn)
|
|
{
|
|
for (typename RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr)
|
|
{
|
|
if (itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)))
|
|
{
|
|
if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit
|
|
itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description
|
|
itr->a_ElapsedTicks = 0;
|
|
itr->ShouldPowerOn = ShouldPowerOn;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Self not in list, add self to list
|
|
sRepeatersDelayList RC;
|
|
RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
|
|
|
|
// Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.)
|
|
// Multiply by 2 because in MCS, 1 redstone tick = 1 world tick, but in Vanilla, 1 redstone tick = 2 world ticks, and we need to maintain compatibility
|
|
RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2;
|
|
|
|
RC.a_ElapsedTicks = 0;
|
|
RC.ShouldPowerOn = ShouldPowerOn;
|
|
m_RepeatersDelayList->push_back(RC);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
void cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::SetSourceUnpowered(int a_SourceX, int a_SourceY, int a_SourceZ, ChunkType * a_Chunk, bool a_IsFirstCall)
|
|
{
|
|
if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid
|
|
{
|
|
if ((a_Chunk == NULL) || !a_Chunk->IsValid())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
// TODO: on C++11 support, change both of these to llama functions pased to a std::remove_if
|
|
|
|
for (typename PoweredBlocksList::iterator itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.begin(); itr != ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.end();)
|
|
{
|
|
if (itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ)))
|
|
{
|
|
itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks.erase(itr);
|
|
a_Chunk->SetIsRedstoneDirty(true);
|
|
continue;
|
|
}
|
|
++itr;
|
|
}
|
|
for (typename LinkedBlocksList::iterator itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks.begin(); itr != ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks.end();)
|
|
{
|
|
if (itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ)))
|
|
{
|
|
itr = ((cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks.erase(itr);
|
|
a_Chunk->SetIsRedstoneDirty(true);
|
|
continue;
|
|
}
|
|
++itr;
|
|
}
|
|
|
|
if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_SourceX, a_SourceY, a_SourceZ))
|
|
{
|
|
// +- 2 to accomodate linked powered blocks
|
|
SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX - 2, a_SourceZ), false);
|
|
SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX + 2, a_SourceZ), false);
|
|
SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX, a_SourceZ - 2), false);
|
|
SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX, a_SourceZ + 2), false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
typename cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::eRedstoneDirection cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::GetWireDirection(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ)
|
|
{
|
|
int Dir = REDSTONE_NONE;
|
|
|
|
BLOCKTYPE NegX = 0;
|
|
if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, NegX))
|
|
{
|
|
if (IsPotentialSource(NegX))
|
|
{
|
|
Dir |= (REDSTONE_X_POS);
|
|
}
|
|
}
|
|
|
|
BLOCKTYPE PosX = 0;
|
|
if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, PosX))
|
|
{
|
|
if (IsPotentialSource(PosX))
|
|
{
|
|
Dir |= (REDSTONE_X_NEG);
|
|
}
|
|
}
|
|
|
|
BLOCKTYPE NegZ = 0;
|
|
if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, NegZ))
|
|
{
|
|
if (IsPotentialSource(NegZ))
|
|
{
|
|
if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner
|
|
{
|
|
Dir ^= REDSTONE_X_POS;
|
|
Dir |= REDSTONE_X_NEG;
|
|
}
|
|
if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner
|
|
{
|
|
Dir ^= REDSTONE_X_NEG;
|
|
Dir |= REDSTONE_X_POS;
|
|
}
|
|
Dir |= REDSTONE_Z_POS;
|
|
}
|
|
}
|
|
|
|
BLOCKTYPE PosZ = 0;
|
|
if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, PosZ))
|
|
{
|
|
if (IsPotentialSource(PosZ))
|
|
{
|
|
if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner
|
|
{
|
|
Dir ^= REDSTONE_X_POS;
|
|
Dir |= REDSTONE_X_NEG;
|
|
}
|
|
if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner
|
|
{
|
|
Dir ^= REDSTONE_X_NEG;
|
|
Dir |= REDSTONE_X_POS;
|
|
}
|
|
Dir |= REDSTONE_Z_NEG;
|
|
}
|
|
}
|
|
return (eRedstoneDirection)Dir;
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class ChunkType, class WorldType, template <BLOCKTYPE block> class GetHandlerCompileTime, class ChestType>
|
|
bool cIncrementalRedstoneSimulator<ChunkType, WorldType, GetHandlerCompileTime, ChestType>::IsLeverOn(NIBBLETYPE a_BlockMeta)
|
|
{
|
|
// Extract the ON bit from metadata and return if true if it is set:
|
|
return ((a_BlockMeta & 0x8) == 0x8);
|
|
}
|
|
|
|
|
|
|
|
|