summaryrefslogtreecommitdiffstats
path: root/src/Blocks/Mixins
diff options
context:
space:
mode:
Diffstat (limited to 'src/Blocks/Mixins')
-rw-r--r--src/Blocks/Mixins/DirtLikeUnderneath.h42
-rw-r--r--src/Blocks/Mixins/Mixins.h262
-rw-r--r--src/Blocks/Mixins/SolidSurfaceUnderneath.h65
3 files changed, 369 insertions, 0 deletions
diff --git a/src/Blocks/Mixins/DirtLikeUnderneath.h b/src/Blocks/Mixins/DirtLikeUnderneath.h
new file mode 100644
index 000000000..909f1601e
--- /dev/null
+++ b/src/Blocks/Mixins/DirtLikeUnderneath.h
@@ -0,0 +1,42 @@
+
+#pragma once
+
+#include "../../Chunk.h"
+
+/** Mixin to ensure the block has a dirt-like block underneath. */
+template <class Base>
+class cDirtLikeUnderneath :
+ public Base
+{
+ using Super = Base;
+public:
+
+ using Super::Super;
+
+ constexpr cDirtLikeUnderneath(BLOCKTYPE a_BlockType):
+ Base(a_BlockType)
+ {
+ }
+
+protected:
+
+ ~cDirtLikeUnderneath() = default;
+
+protected:
+
+ virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
+ {
+ if (!Super::CanBeAt(a_Chunk, a_Position, a_Meta))
+ {
+ return false;
+ }
+
+ const auto BelowPos = a_Position.addedY(-1);
+ if (!cChunkDef::IsValidHeight(BelowPos))
+ {
+ return false;
+ }
+
+ return IsBlockTypeOfDirt(a_Chunk.GetBlock(BelowPos));
+ }
+};
diff --git a/src/Blocks/Mixins/Mixins.h b/src/Blocks/Mixins/Mixins.h
new file mode 100644
index 000000000..d9a233bad
--- /dev/null
+++ b/src/Blocks/Mixins/Mixins.h
@@ -0,0 +1,262 @@
+// Mixins.h
+
+// Provides various mixins for easier cBlockHandler descendant implementations
+
+/* The general use case is to derive a handler from these mixins, providing a suitable base to them:
+class cBlockAir: public cBlockWithNoDrops<cBlockHandler>;
+class cBlockLadder: public cMetaRotator<cClearMetaOnDrop, ...>
+*/
+
+#pragma once
+
+#include "../../Item.h"
+
+
+
+
+
+// MSVC generates warnings for the templated AssertIfNotMatched parameter conditions, so disable it:
+#ifdef _MSC_VER
+ #pragma warning(disable: 4127) // Conditional expression is constant
+#endif
+
+
+
+
+
+/** Mixin to clear the block's meta value when converting to a pickup. */
+template <class Base>
+class cClearMetaOnDrop :
+ public Base
+{
+public:
+
+ constexpr cClearMetaOnDrop(BLOCKTYPE a_BlockType):
+ Base(a_BlockType)
+ {
+ }
+
+protected:
+
+ ~cClearMetaOnDrop() = default;
+
+private:
+
+ virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
+ {
+ // Reset the meta to zero:
+ return cItem(this->m_BlockType);
+ }
+};
+
+
+
+
+
+/** Mixin for rotations and reflections following the standard pattern of "apply mask, then use a switch".
+Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West.
+There is also an aptional parameter AssertIfNotMatched, set this if it is invalid for a block to exist in any other state. */
+template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
+class cMetaRotator :
+ public Base
+{
+public:
+
+ constexpr cMetaRotator(BLOCKTYPE a_BlockType):
+ Base(a_BlockType)
+ {
+ }
+
+protected:
+
+ ~cMetaRotator() = default;
+
+ virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) const override
+ {
+ NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
+ switch (a_Meta & BitMask)
+ {
+ case South: return East | OtherMeta;
+ case East: return North | OtherMeta;
+ case North: return West | OtherMeta;
+ case West: return South | OtherMeta;
+ }
+ if (AssertIfNotMatched)
+ {
+ ASSERT(!"Invalid Meta value");
+ }
+ return a_Meta;
+ }
+
+
+
+
+
+ virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) const override
+ {
+ NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
+ switch (a_Meta & BitMask)
+ {
+ case South: return West | OtherMeta;
+ case West: return North | OtherMeta;
+ case North: return East | OtherMeta;
+ case East: return South | OtherMeta;
+ }
+ if (AssertIfNotMatched)
+ {
+ ASSERT(!"Invalid Meta value");
+ }
+ return a_Meta;
+ }
+
+
+
+
+
+ virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) const override
+ {
+ NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
+ switch (a_Meta & BitMask)
+ {
+ case South: return North | OtherMeta;
+ case North: return South | OtherMeta;
+ }
+ // Not Facing North or South; No change.
+ return a_Meta;
+ }
+
+
+
+
+
+ virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) const override
+ {
+ NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
+ switch (a_Meta & BitMask)
+ {
+ case West: return East | OtherMeta;
+ case East: return West | OtherMeta;
+ }
+ // Not Facing East or West; No change.
+ return a_Meta;
+ }
+};
+
+
+
+
+
+/** Mixin for blocks whose meta on placement depends on the yaw of the player placing the block. BitMask
+selects the direction bits from the block's meta values. */
+template <
+ class Base,
+ NIBBLETYPE BitMask = 0x07,
+ NIBBLETYPE North = 0x02,
+ NIBBLETYPE East = 0x05,
+ NIBBLETYPE South = 0x03,
+ NIBBLETYPE West = 0x04,
+ bool AssertIfNotMatched = false
+>
+class cYawRotator :
+ public cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>
+{
+ using Super = cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>;
+
+public:
+
+ using Super::Super;
+
+
+ /** Converts the rotation value as returned by cPlayer::GetYaw() to the appropriate metadata
+ value for a block placed by a player facing that way. */
+ static NIBBLETYPE YawToMetaData(double a_Rotation)
+ {
+ if ((a_Rotation >= -135) && (a_Rotation < -45))
+ {
+ return East;
+ }
+ else if ((a_Rotation >= -45) && (a_Rotation < 45))
+ {
+ return South;
+ }
+ else if ((a_Rotation >= 45) && (a_Rotation < 135))
+ {
+ return West;
+ }
+ else // degrees jumping from 180 to -180
+ {
+ return North;
+ }
+ }
+
+protected:
+
+ ~cYawRotator() = default;
+};
+
+
+
+
+
+/** Mixin for blocks whose meta on placement depends on the relative position of the player to the block in
+addition to the yaw of the player placing the block. BitMask selects the direction bits from the block's meta values. */
+template <
+ class Base,
+ NIBBLETYPE BitMask = 0x07,
+ NIBBLETYPE North = 0x02,
+ NIBBLETYPE East = 0x05,
+ NIBBLETYPE South = 0x03,
+ NIBBLETYPE West = 0x04,
+ NIBBLETYPE Up = 0x00,
+ NIBBLETYPE Down = 0x01
+>
+class cDisplacementYawRotator:
+ public cYawRotator<Base, BitMask, North, East, South, West>
+{
+ using Super = cYawRotator<Base, BitMask, North, East, South, West>;
+
+public:
+
+ using Super::Super;
+
+
+ /** Converts the placement position, eye position as returned by cPlayer::GetEyePosition(), and
+ rotation value as returned by cPlayer::GetYaw() to the appropriate metadata value for a block placed by a player facing that way. */
+ static NIBBLETYPE DisplacementYawToMetaData(const Vector3d a_PlacePosition, const Vector3d a_EyePosition, const double a_Rotation)
+ {
+ if (
+ const auto Displacement = a_EyePosition - a_PlacePosition.addedXZ(0.5, 0.5);
+ (std::abs(Displacement.x) < 2) && (std::abs(Displacement.z) < 2)
+ )
+ {
+ if (Displacement.y > 2)
+ {
+ return Up;
+ }
+
+ if (Displacement.y < 0)
+ {
+ return Down;
+ }
+ }
+
+ return Super::YawToMetaData(a_Rotation);
+ }
+
+protected:
+
+ ~cDisplacementYawRotator() = default;
+
+
+ virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) const override
+ {
+ NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
+ switch (a_Meta & BitMask)
+ {
+ case Down: return Up | OtherMeta; // Down -> Up
+ case Up: return Down | OtherMeta; // Up -> Down
+ }
+ // Not Facing Up or Down; No change.
+ return a_Meta;
+ }
+};
diff --git a/src/Blocks/Mixins/SolidSurfaceUnderneath.h b/src/Blocks/Mixins/SolidSurfaceUnderneath.h
new file mode 100644
index 000000000..c54c064d6
--- /dev/null
+++ b/src/Blocks/Mixins/SolidSurfaceUnderneath.h
@@ -0,0 +1,65 @@
+
+#pragma once
+
+#include "../../Chunk.h"
+#include "../BlockSlab.h"
+#include "../BlockStairs.h"
+
+/** Mixin to ensure the block has a solid surface underneath. */
+template <class Base>
+class cSolidSurfaceUnderneath :
+ public Base
+{
+ using Super = Base;
+public:
+
+ using Super::Super;
+
+ constexpr cSolidSurfaceUnderneath(BLOCKTYPE a_BlockType):
+ Base(a_BlockType)
+ {
+ }
+
+protected:
+
+ ~cSolidSurfaceUnderneath() = default;
+
+protected:
+
+ virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
+ {
+ if (!Super::CanBeAt(a_Chunk, a_Position, a_Meta))
+ {
+ return false;
+ }
+
+ const auto BelowPos = a_Position.addedY(-1);
+ if (!cChunkDef::IsValidHeight(BelowPos))
+ {
+ return false;
+ }
+
+ BLOCKTYPE BelowBlock;
+ NIBBLETYPE BelowBlockMeta;
+ a_Chunk.GetBlockTypeMeta(BelowPos, BelowBlock, BelowBlockMeta);
+
+ if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
+ {
+ return true;
+ }
+
+ // upside down slabs
+ if (cBlockSlabHandler::IsAnySlabType(BelowBlock))
+ {
+ return BelowBlockMeta & E_META_WOODEN_SLAB_UPSIDE_DOWN;
+ }
+
+ // upside down stairs
+ if (cBlockStairsHandler::IsAnyStairType(BelowBlock))
+ {
+ return BelowBlockMeta & E_BLOCK_STAIRS_UPSIDE_DOWN;
+ }
+
+ return false;
+ }
+};