2015-06-26 18:24:51 -04:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "RedstoneHandler.h"
|
2020-12-21 08:54:04 -05:00
|
|
|
#include "Registries/BlockStates.h"
|
2015-06-26 18:24:51 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-19 16:14:40 -04:00
|
|
|
namespace RedstoneWireHandler
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
/** A unified representation of wire direction. */
|
|
|
|
enum class TemporaryDirection
|
|
|
|
{
|
|
|
|
Up,
|
|
|
|
Side
|
|
|
|
};
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
/** Invokes Callback with the wire's left, front, and right direction state corresponding to Offset.
|
|
|
|
Returns a new block constructed from the directions that the callback may have modified. */
|
2020-07-26 09:15:00 -04:00
|
|
|
template <class OffsetCallback>
|
2021-01-21 17:51:55 -05:00
|
|
|
static BlockState DoWithDirectionState(const Vector3i Offset, BlockState Block, OffsetCallback Callback)
|
2020-08-08 13:22:16 -04:00
|
|
|
{
|
|
|
|
auto North = Block::RedstoneWire::North(Block);
|
|
|
|
auto South = Block::RedstoneWire::South(Block);
|
|
|
|
auto West = Block::RedstoneWire::West(Block);
|
|
|
|
auto East = Block::RedstoneWire::East(Block);
|
|
|
|
|
|
|
|
if (Offset.x == -1)
|
|
|
|
{
|
|
|
|
Callback(South, West, North);
|
|
|
|
}
|
|
|
|
else if (Offset.x == 1)
|
|
|
|
{
|
|
|
|
Callback(North, East, South);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Offset.z == -1)
|
|
|
|
{
|
|
|
|
Callback(West, North, East);
|
|
|
|
}
|
|
|
|
else if (Offset.z == 1)
|
|
|
|
{
|
|
|
|
Callback(East, South, West);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Block::RedstoneWire::RedstoneWire(East, North, 0, South, West);
|
|
|
|
}
|
|
|
|
|
2020-08-19 16:14:40 -04:00
|
|
|
/** Adjusts a given wire block so that the direction represented by Offset has state Direction. */
|
2021-01-21 17:51:55 -05:00
|
|
|
static void SetDirectionState(const Vector3i Offset, BlockState & Block, TemporaryDirection Direction)
|
2020-08-19 16:14:40 -04:00
|
|
|
{
|
|
|
|
Block = DoWithDirectionState(Offset, Block, [Direction](auto, auto & Front, auto)
|
|
|
|
{
|
|
|
|
using FrontState = std::remove_reference_t<decltype(Front)>;
|
|
|
|
switch (Direction)
|
|
|
|
{
|
|
|
|
case TemporaryDirection::Up:
|
|
|
|
{
|
|
|
|
Front = FrontState::Up;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TemporaryDirection::Side:
|
|
|
|
{
|
|
|
|
Front = FrontState::Side;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-01-21 17:51:55 -05:00
|
|
|
static bool IsDirectlyConnectingMechanism(BLOCKTYPE a_Block, NIBBLETYPE a_BlockMeta, const Vector3i a_Offset)
|
2020-08-19 16:14:40 -04:00
|
|
|
{
|
|
|
|
switch (a_Block)
|
|
|
|
{
|
|
|
|
case E_BLOCK_REDSTONE_REPEATER_ON:
|
|
|
|
case E_BLOCK_REDSTONE_REPEATER_OFF:
|
|
|
|
{
|
|
|
|
a_BlockMeta &= E_META_REDSTONE_REPEATER_FACING_MASK;
|
|
|
|
if ((a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XP) || (a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XM))
|
|
|
|
{
|
|
|
|
// Wire connects to repeater if repeater is aligned along X
|
|
|
|
// and wire is in front or behind it (#4639)
|
|
|
|
return a_Offset.x != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return a_Offset.z != 0;
|
|
|
|
}
|
|
|
|
case E_BLOCK_ACTIVE_COMPARATOR:
|
|
|
|
case E_BLOCK_BLOCK_OF_REDSTONE:
|
2020-09-21 15:39:58 -04:00
|
|
|
case E_BLOCK_INACTIVE_COMPARATOR:
|
|
|
|
case E_BLOCK_LEVER:
|
2020-08-19 16:14:40 -04:00
|
|
|
case E_BLOCK_REDSTONE_TORCH_OFF:
|
|
|
|
case E_BLOCK_REDSTONE_TORCH_ON:
|
2020-09-21 15:39:58 -04:00
|
|
|
case E_BLOCK_REDSTONE_WIRE:
|
|
|
|
case E_BLOCK_STONE_BUTTON:
|
|
|
|
case E_BLOCK_WOODEN_BUTTON: return true;
|
2020-08-19 16:14:40 -04:00
|
|
|
default: return false;
|
|
|
|
}
|
|
|
|
}
|
2020-08-08 13:22:16 -04:00
|
|
|
|
|
|
|
/** Temporary. Discovers a wire's connection state, including terracing, storing the block inside redstone chunk data.
|
|
|
|
TODO: once the server supports block states this should go in the block handler, with data saved in the world. */
|
2021-01-21 17:51:55 -05:00
|
|
|
static void SetWireState(const cChunk & Chunk, const Vector3i Position)
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
auto Block = Block::RedstoneWire::RedstoneWire();
|
|
|
|
const auto YPTerraceBlock = Chunk.GetBlock(Position + OffsetYP);
|
2020-05-07 16:21:57 -04:00
|
|
|
const bool IsYPTerracingBlocked = cBlockInfo::IsSolid(YPTerraceBlock) && !cBlockInfo::IsTransparent(YPTerraceBlock);
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
// Loop through laterals, discovering terracing connections:
|
2020-10-05 06:27:14 -04:00
|
|
|
for (const auto & Offset : RelativeLaterals)
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
auto Adjacent = Position + Offset;
|
|
|
|
auto NeighbourChunk = Chunk.GetRelNeighborChunkAdjustCoords(Adjacent);
|
|
|
|
|
|
|
|
if ((NeighbourChunk == nullptr) || !NeighbourChunk->IsValid())
|
2020-07-26 09:15:00 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
continue;
|
2020-07-26 09:15:00 -04:00
|
|
|
}
|
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
BLOCKTYPE LateralBlock;
|
|
|
|
NIBBLETYPE LateralMeta;
|
|
|
|
NeighbourChunk->GetBlockTypeMeta(Adjacent, LateralBlock, LateralMeta);
|
2020-07-26 09:15:00 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
if (IsDirectlyConnectingMechanism(LateralBlock, LateralMeta, Offset))
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
// Any direct connections on a lateral means the wire has side connection in that direction:
|
|
|
|
SetDirectionState(Offset, Block, TemporaryDirection::Side);
|
|
|
|
|
|
|
|
// Temporary: this case will eventually be handled when wires are placed, with the state saved as blocks
|
|
|
|
// When a neighbour wire was loaded into its chunk, its neighbour chunks may not have loaded yet
|
|
|
|
// This function is called during chunk load (through AddBlock). Attempt to tell it its new state:
|
|
|
|
if ((NeighbourChunk != &Chunk) && (LateralBlock == E_BLOCK_REDSTONE_WIRE))
|
2020-07-26 09:15:00 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent)->second;
|
|
|
|
SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Side);
|
2020-07-26 09:15:00 -04:00
|
|
|
}
|
2020-08-08 13:22:16 -04:00
|
|
|
|
|
|
|
continue;
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
2020-05-07 16:21:57 -04:00
|
|
|
|
2015-06-26 18:24:51 -04:00
|
|
|
if (
|
2020-08-08 13:22:16 -04:00
|
|
|
!IsYPTerracingBlocked && // A block above us blocks all YP terracing, so the check is static in the loop
|
|
|
|
(Adjacent.y < (cChunkDef::Height - 1)) &&
|
|
|
|
(NeighbourChunk->GetBlock(Adjacent + OffsetYP) == E_BLOCK_REDSTONE_WIRE) // Only terrace YP with another wire
|
2015-06-26 18:24:51 -04:00
|
|
|
)
|
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
SetDirectionState(Offset, Block, TemporaryDirection::Up);
|
|
|
|
|
|
|
|
if (NeighbourChunk != &Chunk)
|
2020-07-26 09:15:00 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent + OffsetYP)->second;
|
|
|
|
SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Side);
|
2020-07-26 09:15:00 -04:00
|
|
|
}
|
2020-08-08 13:22:16 -04:00
|
|
|
|
|
|
|
continue;
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
if (
|
|
|
|
// IsYMTerracingBlocked (i.e. check block above lower terracing position, a.k.a. just the plain adjacent)
|
|
|
|
(!cBlockInfo::IsSolid(LateralBlock) || cBlockInfo::IsTransparent(LateralBlock)) &&
|
2021-03-05 08:03:55 -05:00
|
|
|
(Adjacent.y > 0) &&
|
2020-08-08 13:22:16 -04:00
|
|
|
(NeighbourChunk->GetBlock(Adjacent + OffsetYM) == E_BLOCK_REDSTONE_WIRE) // Only terrace YM with another wire
|
|
|
|
)
|
|
|
|
{
|
|
|
|
SetDirectionState(Offset, Block, TemporaryDirection::Side);
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
if (NeighbourChunk != &Chunk)
|
|
|
|
{
|
|
|
|
auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent + OffsetYM)->second;
|
|
|
|
SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Up);
|
|
|
|
}
|
|
|
|
}
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
auto & States = DataForChunk(Chunk).WireStates;
|
|
|
|
const auto FindResult = States.find(Position);
|
|
|
|
if (FindResult != States.end())
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
if (Block != FindResult->second)
|
|
|
|
{
|
|
|
|
FindResult->second = Block;
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
// TODO: when state is stored as the block, the block handler updating via SetBlock will do this automatically
|
|
|
|
// When a wire changes connection state, it needs to update its neighbours:
|
|
|
|
Chunk.GetWorld()->WakeUpSimulators(cChunkDef::RelativeToAbsolute(Position, Chunk.GetPos()));
|
|
|
|
}
|
2020-07-26 09:15:00 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
return;
|
2020-07-26 09:15:00 -04:00
|
|
|
}
|
|
|
|
|
2020-12-21 08:54:04 -05:00
|
|
|
DataForChunk(Chunk).WireStates.emplace(Position, Block);
|
2020-08-08 13:22:16 -04:00
|
|
|
}
|
2020-07-26 09:15:00 -04:00
|
|
|
|
2021-01-21 17:51:55 -05:00
|
|
|
static PowerLevel GetPowerDeliveredToPosition(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, Vector3i a_QueryPosition, BLOCKTYPE a_QueryBlockType, bool IsLinked)
|
2020-08-08 13:22:16 -04:00
|
|
|
{
|
|
|
|
// Starts off as the wire's meta value, modified appropriately and returned
|
|
|
|
auto Power = a_Chunk.GetMeta(a_Position);
|
|
|
|
const auto QueryOffset = a_QueryPosition - a_Position;
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
if (
|
|
|
|
(QueryOffset == OffsetYP) || // Wires do not power things above them
|
|
|
|
(IsLinked && (a_QueryBlockType == E_BLOCK_REDSTONE_WIRE)) // Nor do they link power other wires
|
2020-07-26 09:15:00 -04:00
|
|
|
)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2020-05-07 16:21:57 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
if (QueryOffset == OffsetYM)
|
2020-07-26 09:15:00 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
// Wires always deliver power to the block underneath
|
|
|
|
return Power;
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
const auto & Data = DataForChunk(a_Chunk);
|
|
|
|
const auto Block = Data.WireStates.find(a_Position)->second;
|
|
|
|
|
|
|
|
DoWithDirectionState(QueryOffset, Block, [a_QueryBlockType, &Power](const auto Left, const auto Front, const auto Right)
|
|
|
|
{
|
|
|
|
using LeftState = std::remove_reference_t<decltype(Left)>;
|
|
|
|
using FrontState = std::remove_reference_t<decltype(Front)>;
|
|
|
|
using RightState = std::remove_reference_t<decltype(Right)>;
|
|
|
|
|
|
|
|
// Wires always deliver power to any directly connecting mechanisms:
|
|
|
|
if (Front != FrontState::None)
|
|
|
|
{
|
|
|
|
if ((a_QueryBlockType == E_BLOCK_REDSTONE_WIRE) && (Power != 0))
|
|
|
|
{
|
|
|
|
// For mechanisms, wire of power one will still power them
|
|
|
|
// But for wire-to-wire connections, power level decreases by 1:
|
|
|
|
Power--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Okay, we do not directly connect to the wire.
|
|
|
|
1. If there are no DC mechanisms at all, the wire powers all laterals. Great, left and right are both None.
|
|
|
|
2. If there is one DC mechanism, the wire "goes straight" along the axis of the wire and mechanism.
|
|
|
|
The only possible way for us to be powered is for us to be on the opposite end, with the wire pointing towards us.
|
|
|
|
Check that left and right are both None.
|
|
|
|
3. If there is more than one DC, no non-DCs are powered. Left, right, cannot both be None.
|
|
|
|
*/
|
|
|
|
if ((Left == LeftState::None) && (Right == RightState::None))
|
|
|
|
{
|
|
|
|
// Case 1
|
|
|
|
// Case 2
|
|
|
|
return;
|
|
|
|
}
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
// Case 3
|
|
|
|
Power = 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
return Power;
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
|
|
|
|
2021-01-21 17:51:55 -05:00
|
|
|
static void Update(cChunk & a_Chunk, cChunk & CurrentlyTicking, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, const PowerLevel Power)
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-20 15:04:28 -04:00
|
|
|
// LOGD("Evaluating dusty the wire (%d %d %d) %i", a_Position.x, a_Position.y, a_Position.z, Power);
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-20 15:04:28 -04:00
|
|
|
if (a_Meta == Power)
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
return;
|
|
|
|
}
|
2020-07-26 09:15:00 -04:00
|
|
|
|
2020-08-20 15:04:28 -04:00
|
|
|
a_Chunk.SetMeta(a_Position, Power);
|
2015-06-26 18:24:51 -04:00
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
// Notify all positions, sans YP, to update:
|
2020-10-05 06:27:14 -04:00
|
|
|
for (const auto & Offset : RelativeAdjacents)
|
2020-08-08 13:22:16 -04:00
|
|
|
{
|
|
|
|
if (Offset == OffsetYP)
|
2020-07-26 09:15:00 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateAdjustedRelative(a_Chunk, CurrentlyTicking, a_Position, Offset);
|
2020-07-26 09:15:00 -04:00
|
|
|
}
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
|
|
|
|
2021-01-21 17:51:55 -05:00
|
|
|
static void ForValidSourcePositions(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, ForEachSourceCallback & Callback)
|
2015-06-26 18:24:51 -04:00
|
|
|
{
|
|
|
|
UNUSED(a_BlockType);
|
|
|
|
UNUSED(a_Meta);
|
|
|
|
|
2020-07-26 09:15:00 -04:00
|
|
|
Callback(a_Position + OffsetYP);
|
|
|
|
Callback(a_Position + OffsetYM);
|
|
|
|
|
2020-08-08 13:22:16 -04:00
|
|
|
const auto & Data = DataForChunk(a_Chunk);
|
|
|
|
const auto Block = Data.WireStates.find(a_Position)->second;
|
|
|
|
|
|
|
|
// Figure out, based on our pre-computed block, where we connect to:
|
2020-10-05 06:27:14 -04:00
|
|
|
for (const auto & Offset : RelativeLaterals)
|
2020-07-26 09:15:00 -04:00
|
|
|
{
|
2020-08-08 13:22:16 -04:00
|
|
|
const auto Relative = a_Position + Offset;
|
|
|
|
Callback(Relative);
|
|
|
|
|
|
|
|
DoWithDirectionState(Offset, Block, [&a_Chunk, &Callback, Relative](auto, const auto Front, auto)
|
|
|
|
{
|
|
|
|
using FrontState = std::remove_reference_t<decltype(Front)>;
|
|
|
|
|
|
|
|
if (Front == FrontState::Up)
|
|
|
|
{
|
|
|
|
Callback(Relative + OffsetYP);
|
|
|
|
}
|
|
|
|
else if (Front == FrontState::Side)
|
|
|
|
{
|
|
|
|
// Alas, no way to distinguish side lateral and side diagonal
|
|
|
|
// Have to do a manual check to only accept power from YM diagonal if there's a wire there
|
|
|
|
|
|
|
|
const auto YMDiagonalPosition = Relative + OffsetYM;
|
|
|
|
if (
|
2020-10-05 06:27:14 -04:00
|
|
|
BLOCKTYPE QueryBlock;
|
2020-08-08 13:22:16 -04:00
|
|
|
cChunkDef::IsValidHeight(YMDiagonalPosition.y) &&
|
2020-10-05 06:27:14 -04:00
|
|
|
a_Chunk.UnboundedRelGetBlockType(YMDiagonalPosition, QueryBlock) &&
|
|
|
|
(QueryBlock == E_BLOCK_REDSTONE_WIRE)
|
2020-08-08 13:22:16 -04:00
|
|
|
)
|
|
|
|
{
|
|
|
|
Callback(YMDiagonalPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-06-26 18:24:51 -04:00
|
|
|
}
|
|
|
|
};
|