From 31a8d017d98bfee2e130cecb82f75911efba1c80 Mon Sep 17 00:00:00 2001 From: Zach DeCook Date: Mon, 10 Jun 2019 19:28:02 -0400 Subject: [PATCH] * Sponge: Add water absorbtion (#4333) Closes #1411 --- Server/Plugins/APIDump/APIDesc.lua | 8 ++ src/BlockID.h | 4 + src/Blocks/BlockHandler.cpp | 2 + src/Blocks/BlockSponge.h | 157 +++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 src/Blocks/BlockSponge.h diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index a3ebde9bb..2bd47cbf5 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -16582,6 +16582,14 @@ end { Notes = "A flag in the metadata of heads that indicates that the head is a zombie head.", }, + E_META_SPONGE_DRY = + { + Notes = "A flag in the metadata of sponges that indicates that the sponge is dry.", + }, + E_META_SPONGE_WET = + { + Notes = "A flag in the metadata of sponges that indicates that the sponge is wet.", + }, esBed = { Notes = "A bed explosion. The SourceData param is the {{Vector3i|position}} of the bed.", diff --git a/src/BlockID.h b/src/BlockID.h index d7b362c41..733155a50 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -806,6 +806,10 @@ enum ENUM_BLOCK_META : NIBBLETYPE E_META_SNOW_LAYER_SEVEN = 6, E_META_SNOW_LAYER_EIGHT = 7, + // E_BLOCK_SPONGE metas: + E_META_SPONGE_DRY = 0, + E_META_SPONGE_WET = 1, + // E_BLOCK_STAINED_CLAY metas: E_META_STAINED_CLAY_WHITE = 0, E_META_STAINED_CLAY_ORANGE = 1, diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 923d02a80..61427cae6 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -77,6 +77,7 @@ #include "BlockSlab.h" #include "BlockSlime.h" #include "BlockSnow.h" +#include "BlockSponge.h" #include "BlockStairs.h" #include "BlockStems.h" #include "BlockStone.h" @@ -317,6 +318,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_SIGN_POST: return new cBlockSignPostHandler (a_BlockType); case E_BLOCK_SNOW: return new cBlockSnowHandler (a_BlockType); case E_BLOCK_SLIME_BLOCK: return new cBlockSlimeHandler (a_BlockType); + case E_BLOCK_SPONGE: return new cBlockSpongeHandler (a_BlockType); case E_BLOCK_SPRUCE_DOOR: return new cBlockDoorHandler (a_BlockType); case E_BLOCK_SPRUCE_FENCE: return new cBlockFenceHandler (a_BlockType); case E_BLOCK_SPRUCE_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType); diff --git a/src/Blocks/BlockSponge.h b/src/Blocks/BlockSponge.h new file mode 100644 index 000000000..24bc25388 --- /dev/null +++ b/src/Blocks/BlockSponge.h @@ -0,0 +1,157 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockSpongeHandler : + public cBlockHandler +{ +public: + cBlockSpongeHandler(BLOCKTYPE a_BlockType): + cBlockHandler(a_BlockType) + { + } + + + + + + virtual void Check(cChunkInterface & a_ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk) override + { + if (GetSoaked(Vector3i(a_RelX, a_RelY, a_RelZ), a_Chunk)) + { + return; + } + cBlockHandler::Check(a_ChunkInterface, a_PluginInterface, a_RelX, a_RelY, a_RelZ, a_Chunk); + } + + + + + + /** Check blocks around the sponge to see if they are water. + If a dry sponge is touching water, soak up up to 65 blocks of water, + with a taxicab distance of 7, and turn the sponge into a wet sponge. + Returns TRUE if the block was changed. */ + bool GetSoaked(Vector3i a_Rel, cChunk & a_Chunk) + { + static const std::array WaterCheck + { + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + { 0, -1, 0}, + } + }; + struct sSeed + { + sSeed(Vector3i pos, int d) + { + m_Pos = pos; + m_Depth = d; + } + Vector3i m_Pos; + int m_Depth; + }; + + + // Check if this is a dry sponge next to a water block. + NIBBLETYPE TargetMeta = a_Chunk.GetMeta(a_Rel.x, a_Rel.y, a_Rel.z); + if (TargetMeta != E_META_SPONGE_DRY) + { + return false; + } + + bool ShouldSoak = std::any_of(WaterCheck.cbegin(), WaterCheck.cend(), [a_Rel, & a_Chunk](Vector3i a_Offset) + { + return IsWet(a_Rel + a_Offset, a_Chunk); + } + ); + + // Early return if the sponge isn't touching any water. + if (! ShouldSoak) + { + return false; + } + + + // Use a queue to hold blocks that we want to check, so our search is breadth-first. + std::queue Seeds; + int count = 0; + // Only go 7 blocks away from the center block. + const int maxDepth = 7; + // Start with the 6 blocks around the sponge. + for (unsigned int i = 0; i < 6; i++) + { + Seeds.emplace(a_Rel + WaterCheck[i], maxDepth - 1); + } + + + // Keep checking blocks that are touching water blocks, or until 65 have been soaked up. + while (!Seeds.empty() && count < 65) + { + sSeed seed = Seeds.front(); + Vector3i checkRel = seed.m_Pos; + if (IsWet(checkRel, a_Chunk)) + { + count++; + DryUp(checkRel, a_Chunk); + if (seed.m_Depth > 0) + { + // If this block was water, and we haven't yet gone too far away, + // add itÅ› neighbors to the queue to check. + for (unsigned int i = 0; i < 6; i++) + { + Seeds.emplace(checkRel + WaterCheck[i], seed.m_Depth - 1); + } + } + } + Seeds.pop(); + } + a_Chunk.SetBlock(a_Rel.x, a_Rel.y, a_Rel.z, E_BLOCK_SPONGE, E_META_SPONGE_WET); + return true; + } + + + + + + static void DryUp(Vector3i a_Rel, cChunk & a_Chunk) + { + // TODO: support evaporating waterlogged blocks. + a_Chunk.UnboundedRelSetBlock(a_Rel.x, a_Rel.y, a_Rel.z, E_BLOCK_AIR, 0); + } + + + + + + static bool IsWet(Vector3i a_Rel, cChunk & a_Chunk) + { + // TODO: support detecting waterlogged blocks. + BLOCKTYPE Type; + return(a_Chunk.UnboundedRelGetBlockType(a_Rel.x, a_Rel.y, a_Rel.z, Type) && IsBlockWater(Type)); + } + + + + + + virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override + { + UNUSED(a_Meta); + return 18; + } + + + + + +};