diff options
Diffstat (limited to 'src/Blocks/Mixins')
-rw-r--r-- | src/Blocks/Mixins/DirtLikeUnderneath.h | 42 | ||||
-rw-r--r-- | src/Blocks/Mixins/Mixins.h | 262 | ||||
-rw-r--r-- | src/Blocks/Mixins/SolidSurfaceUnderneath.h | 65 |
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; + } +}; |