diff --git a/doc/en/block-properties.md b/doc/en/block-properties.md index 72a4b4c48..f3137dd60 100644 --- a/doc/en/block-properties.md +++ b/doc/en/block-properties.md @@ -241,6 +241,15 @@ Number of block inventory slots. Default - 0 (no inventory). Array of three integers. Default value is `[1, 1, 1]`. +### *grounding-behaviour* + +Defines the behavior of the extended grounded block. Available behavior options: + +- "partial" - at least one segment must be grounded (default) +- "complete" - all segments must be grounded +- "origin" - origin segment must be grounded + + ## Block fields Block fields allow you to write more data unique to a specified voxel than the user bits allow. diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md index 97e8fedc6..50bd7f023 100644 --- a/doc/ru/block-properties.md +++ b/doc/ru/block-properties.md @@ -247,6 +247,14 @@ Массив из трех целых чисел. Значение по-умолчанию - `[1, 1, 1]`. +### Поведение приземленности - *grounding-behaviour* + +Определяет поведение расширенного приземленного блока. Доступные варианты поведения: + +- "partial" - как минимум один сегмент блока должен быть приземленным (по-умолчанию) +- "complete" - все сегменты блока должны быть приземленными +- "origin" - главный сегмент должен быть приземленным + ## Поля блока Поля блоков позволяет записывать больше уникальных для конкретного блока данных, чем это позволяют пользовательские биты. diff --git a/src/content/loading/BlockLoader.cpp b/src/content/loading/BlockLoader.cpp index c4df790c6..d9bb5e536 100644 --- a/src/content/loading/BlockLoader.cpp +++ b/src/content/loading/BlockLoader.cpp @@ -206,6 +206,15 @@ template<> void ContentUnitLoader::loadUnit( def.defaults.model.type = BlockModelType::AABB; def.hitboxes = {AABB(def.size)}; } + + // grounding behaviour + if (root.has("grounding-behaviour")) { + std::string groundingBehaviourName = GroundingBehaviourMeta.getNameString(def.groundingBehaviour); + root.at("grounding-behaviour").get(groundingBehaviourName); + if (!GroundingBehaviourMeta.getItem(groundingBehaviourName, def.groundingBehaviour)) { + logger.error() << "unknown grounding behaviour: " << groundingBehaviourName; + } + } } // primitive properties diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 1e3ad2441..46c27ec7f 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -106,10 +106,17 @@ void BlocksController::updateBlock(int x, int y, int z) { if (vox == nullptr) return; const auto& def = level.content.getIndices()->blocks.require(vox->id); if (def.grounded) { - const auto& vec = get_ground_direction(def, vox->state.rotation); - if (!blocks_agent::is_solid_at(chunks, x + vec.x, y + vec.y, z + vec.z)) { - breakBlock(nullptr, def, x, y, z); - return; + if (def.rt.extended) { + auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); + if (!blocks_agent::check_grounding(chunks, def, vox->state.rotation, origin)) { + breakBlock(nullptr, def, origin.x, origin.y, origin.z); + return; + } + } else { + if (!blocks_agent::check_grounding(chunks, def, vox->state.rotation, {x, y, z})) { + breakBlock(nullptr, def, x, y, z); + return; + } } } if (def.rt.funcsset.update) { diff --git a/src/logic/PlayerController.cpp b/src/logic/PlayerController.cpp index 17c4fe23f..2e6e6e7e2 100644 --- a/src/logic/PlayerController.cpp +++ b/src/logic/PlayerController.cpp @@ -22,6 +22,7 @@ #include "scripting/scripting.hpp" #include "settings.hpp" #include "voxels/Block.hpp" +#include "voxels/blocks_agent.hpp" #include "voxels/Chunks.hpp" #include "voxels/voxel.hpp" #include "window/Camera.hpp" @@ -448,10 +449,7 @@ void PlayerController::processRightClick( return; } if (def.grounded) { - const auto& vec = get_ground_direction(def, state.rotation); - if (!chunks.isSolidBlock( - coord.x + vec.x, coord.y + vec.y, coord.z + vec.z - )) { + if (!blocks_agent::check_grounding(chunks, def, state.rotation, coord)) { return; } } diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index e1a68c28f..5ba3bb267 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -140,6 +140,22 @@ VC_ENUM_METADATA(CullingMode) {"disabled", CullingMode::DISABLED}, VC_ENUM_END +/// @brief Grounding behaviour for extended blocks +enum class GroundingBehaviour : uint8_t { + /// @brief at least one segment must be grounded + PARTIAL, + /// @brief all segments must be grounded + COMPLETE, + /// @brief origin segment must be grounded + ORIGIN +}; + +VC_ENUM_METADATA(GroundingBehaviour) + {"partial", GroundingBehaviour::PARTIAL}, + {"complete", GroundingBehaviour::COMPLETE}, + {"origin", GroundingBehaviour::ORIGIN}, +VC_ENUM_END + /// @brief Common kit of block properties applied to groups of blocks struct BlockMaterial : Serializable { std::string name; @@ -239,6 +255,9 @@ public: /// @brief Explicitly overriding 'solid' property if true assigned bool explictlySolid = false; + /// @brief Grounding behaviour + GroundingBehaviour groundingBehaviour = GroundingBehaviour::PARTIAL; + /// @brief Set of block physical hitboxes std::vector hitboxes {AABB()}; diff --git a/src/voxels/blocks_agent.hpp b/src/voxels/blocks_agent.hpp index 9ffac7f93..a8131e820 100644 --- a/src/voxels/blocks_agent.hpp +++ b/src/voxels/blocks_agent.hpp @@ -494,4 +494,57 @@ inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float return nullptr; } +/// @brief Check block grounding +/// @tparam Storage chunks storage class +/// @param chunks chunks storage +/// @param def block definition +/// @param rotationIndex target block rotation index +/// @param origin target block origin +/// @return true if grounded +template +inline bool check_grounding( + const Storage& chunks, + const Block& def, + uint8_t rotationIndex, + const glm::ivec3& origin +) { + const auto& vec = get_ground_direction(def, rotationIndex); + + if (def.rt.extended) { + const auto& rotation = def.rotations.variants[rotationIndex]; + + if (def.groundingBehaviour == GroundingBehaviour::PARTIAL) { + for (int sz = 0; sz < def.size.z; sz++) { + for (int sx = 0; sx < def.size.x; sx++) { + auto pos = origin; + pos += rotation.axes[0] * sx; + pos += rotation.axes[2] * sz; + if (blocks_agent::is_solid_at(chunks, pos.x + vec.x, pos.y + vec.y, pos.z + vec.z)) { + return true; + } + } + } + return false; + } + else if (def.groundingBehaviour == GroundingBehaviour::COMPLETE) { + for (int sz = 0; sz < def.size.z; sz++) { + for (int sx = 0; sx < def.size.x; sx++) { + auto pos = origin; + pos += rotation.axes[0] * sx; + pos += rotation.axes[2] * sz; + if (!blocks_agent::is_solid_at(chunks, pos.x + vec.x, pos.y + vec.y, pos.z + vec.z)) { + return false; + } + } + } + return true; + } + else if (def.groundingBehaviour == GroundingBehaviour::ORIGIN) { + return blocks_agent::is_solid_at(chunks, origin.x + vec.x, origin.y + vec.y, origin.z + vec.z); + } + } + + return blocks_agent::is_solid_at(chunks, origin.x + vec.x, origin.y + vec.y, origin.z + vec.z); +} + } // blocks_agent