From 429117c1f6131df407246a46bc75a724eca0da54 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 28 Sep 2020 22:04:57 +0100 Subject: Doors: check power & toggle correctly * Fixed upper half ignoring its updates * Fixes #4945 * Fixed doors playing sound effects when they didn't actually toggle --- src/Blocks/BlockDoor.h | 20 +++--- .../IncrementalRedstoneSimulator/DoorHandler.h | 75 ++++++++++++++++------ 2 files changed, 63 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 36dc788aa..ee511a290 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -96,6 +96,14 @@ public: } } + /** Returns true iff the door at the specified coords is open. + The coords may point to either the top part or the bottom part of the door. */ + static bool IsOpen(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos) + { + const auto Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockPos); + return (Meta & 0x04) != 0; + } + /** Sets the door to the specified state. If the door is already in that state, does nothing. */ static void SetOpen(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos, bool a_Open) { @@ -237,18 +245,6 @@ private: - /** Returns true iff the door at the specified coords is open. - The coords may point to either the top part or the bottom part of the door. */ - static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos) - { - NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockPos); - return ((Meta & 0x04) != 0); - } - - - - - /** Returns the complete meta composed from the both parts of the door as (TopMeta << 4) | BottomMeta The coords may point to either part of the door. The returned value has bit 3 (0x08) set iff the coords point to the top part of the door. diff --git a/src/Simulator/IncrementalRedstoneSimulator/DoorHandler.h b/src/Simulator/IncrementalRedstoneSimulator/DoorHandler.h index fa5debbbf..00fd8c7eb 100644 --- a/src/Simulator/IncrementalRedstoneSimulator/DoorHandler.h +++ b/src/Simulator/IncrementalRedstoneSimulator/DoorHandler.h @@ -22,42 +22,77 @@ namespace DoorHandler return 0; } + inline void ForValidSourcePositions(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, ForEachSourceCallback & Callback) + { + UNUSED(a_Chunk); + UNUSED(a_BlockType); + UNUSED(a_Meta); + InvokeForAdjustedRelatives(Callback, a_Position, RelativeAdjacents); + } + inline void Update(cChunk & a_Chunk, cChunk &, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, PowerLevel Power) { // LOGD("Evaluating dori the door (%d %d %d)", a_Position.x, a_Position.y, a_Position.z); - if ((a_Meta & 0x8) == 0x8) + NIBBLETYPE TopMeta; + const bool IsTop = (a_Meta & 0x8) == 0x8; + const auto TopPosition = IsTop ? a_Position : a_Position.addedY(1); + + // Figure out the metadata of the top half, which stores the previous redstone power state: + if (IsTop) { - // We're treating the bottom half as the source of truth, so ignore updates to the top: - return; + TopMeta = a_Meta; + } + else + { + if (TopPosition.y == cChunkDef::Height) + { + return; + } + + BLOCKTYPE AboveType; + a_Chunk.GetBlockTypeMeta(TopPosition, AboveType, TopMeta); + if (!cBlockDoorHandler::IsDoorBlockType(AboveType)) + { + return; + } } - const auto TopPosition = a_Position + OffsetYP; - ForEachSourceCallback Callback(a_Chunk, TopPosition, a_BlockType); - RedstoneHandler::ForValidSourcePositions(a_Chunk, TopPosition, a_BlockType, a_Meta, Callback); + const auto OppositeHalfPosition = a_Position + (IsTop ? OffsetYM : OffsetYP); + ForEachSourceCallback Callback(a_Chunk, OppositeHalfPosition, a_BlockType); + ForValidSourcePositions(a_Chunk, OppositeHalfPosition, a_BlockType, a_Meta, Callback); - // Factor in what the upper half is getting: + // Factor in what the other half is getting: Power = std::max(Power, Callback.Power); - cChunkInterface ChunkInterface(a_Chunk.GetWorld()->GetChunkMap()); - // Use redstone data rather than block state so players can override redstone control - const auto Previous = DataForChunk(a_Chunk).ExchangeUpdateOncePowerData(a_Position, Power); - const bool IsOpen = (Previous != 0); const bool ShouldBeOpen = Power != 0; + const bool PreviouslyPowered = (TopMeta & 0x2) == 0x2; + + // Allow players to override redstone control + // don't update if redstone power hasn't changed since we last saw it: + if (ShouldBeOpen == PreviouslyPowered) + { + return; + } + + // Update the previous redstone power: + if (ShouldBeOpen) + { + a_Chunk.SetMeta(TopPosition, TopMeta | 0x2); + } + else + { + a_Chunk.SetMeta(TopPosition, TopMeta & ~0x2); + } + + cChunkInterface ChunkInterface(a_Chunk.GetWorld()->GetChunkMap()); const auto AbsolutePosition = cChunkDef::RelativeToAbsolute(a_Position, a_Chunk.GetPos()); - if (ShouldBeOpen != IsOpen) + // Toggle the door, if it needs to be changed: + if (ShouldBeOpen != cBlockDoorHandler::IsOpen(ChunkInterface, AbsolutePosition)) { cBlockDoorHandler::SetOpen(ChunkInterface, AbsolutePosition, ShouldBeOpen); a_Chunk.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_WOODEN_DOOR_OPEN, AbsolutePosition, 0); } } - - inline void ForValidSourcePositions(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, ForEachSourceCallback & Callback) - { - UNUSED(a_Chunk); - UNUSED(a_BlockType); - UNUSED(a_Meta); - InvokeForAdjustedRelatives(Callback, a_Position, RelativeAdjacents); - } }; -- cgit v1.2.3