From 0cc8c70a1704bf511635c1c1e19fe707e8477127 Mon Sep 17 00:00:00 2001 From: clasher113 Date: Sat, 28 Feb 2026 15:59:04 +0200 Subject: [PATCH 1/2] fix blocks variants --- doc/en/scripting/builtins/libblock.md | 2 +- doc/ru/scripting/builtins/libblock.md | 2 +- src/content/loading/BlockLoader.cpp | 4 +-- src/frontend/ContentGfxCache.cpp | 2 +- src/graphics/render/ModelsGenerator.cpp | 6 +++++ src/logic/scripting/lua/libs/libblock.cpp | 30 ++++++++++++++++++++--- 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/doc/en/scripting/builtins/libblock.md b/doc/en/scripting/builtins/libblock.md index b2528c364..db407e92a 100644 --- a/doc/en/scripting/builtins/libblock.md +++ b/doc/en/scripting/builtins/libblock.md @@ -67,7 +67,7 @@ block.get_picking_item(id: int) -> int block.get_variant(x: int, y: int, z: int) -> int -- Sets the block variant by index -block.set_variant(x: int, y: int, z: int, index: int) -> int +block.set_variant(x: int, y: int, z: int, index: int) -- Checks if an block has specified tag block.has_tag(id: int, tag: str) -> bool diff --git a/doc/ru/scripting/builtins/libblock.md b/doc/ru/scripting/builtins/libblock.md index a82d2ff66..b7af3e462 100644 --- a/doc/ru/scripting/builtins/libblock.md +++ b/doc/ru/scripting/builtins/libblock.md @@ -46,7 +46,7 @@ block.destruct(x: int, y: int, z: int, playerid: int) block.get_variant(x: int, y: int, z: int) -> int -- Устанавливает вариант блока по индексу -block.set_variant(x: int, y: int, z: int, index: int) -> int +block.set_variant(x: int, y: int, z: int, index: int) ``` > [!WARNING] diff --git a/src/content/loading/BlockLoader.cpp b/src/content/loading/BlockLoader.cpp index 87c10fa67..c4df790c6 100644 --- a/src/content/loading/BlockLoader.cpp +++ b/src/content/loading/BlockLoader.cpp @@ -120,8 +120,8 @@ template<> void ContentUnitLoader::loadUnit( } def.variants = std::make_unique(); - def.variants->offset = 0; - def.variants->mask = 0xF; + def.variants->offset = offset; + def.variants->mask = (1ULL << bitsCount) - 1; def.variants->variants.push_back(def.defaults); for (int i = 0; i < variants.size(); i++) { Variant variant = def.defaults; diff --git a/src/frontend/ContentGfxCache.cpp b/src/frontend/ContentGfxCache.cpp index 20b7cdaa7..b8ce69a5f 100644 --- a/src/frontend/ContentGfxCache.cpp +++ b/src/frontend/ContentGfxCache.cpp @@ -70,7 +70,7 @@ void ContentGfxCache::refresh(const Block& def, const Atlas& atlas) { refreshVariant(def, def.defaults, 0, atlas); if (def.variants) { const auto& variants = def.variants->variants; - for (int i = 1; i < variants.size() - 1; i++) { + for (int i = 1; i < variants.size(); i++) { refreshVariant(def, variants[i], i, atlas); } def.variants->variants.at(0) = def.defaults; diff --git a/src/graphics/render/ModelsGenerator.cpp b/src/graphics/render/ModelsGenerator.cpp index b4f423480..da82680bb 100644 --- a/src/graphics/render/ModelsGenerator.cpp +++ b/src/graphics/render/ModelsGenerator.cpp @@ -92,6 +92,12 @@ void ModelsGenerator::prepareModel( void ModelsGenerator::prepare(Content& content, Assets& assets) { for (auto& [name, def] : content.blocks.getDefs()) { prepareModel(assets, *def, def->defaults, 0); + if (def->variants) { + auto& variants = def->variants->variants; + for (int i = 1; i < variants.size(); i++) { + prepareModel(assets, *def, variants[i], i); + } + } } for (auto& [name, def] : content.items.getDefs()) { assets.store( diff --git a/src/logic/scripting/lua/libs/libblock.cpp b/src/logic/scripting/lua/libs/libblock.cpp index 6a84b4113..2fa2b1281 100644 --- a/src/logic/scripting/lua/libs/libblock.cpp +++ b/src/logic/scripting/lua/libs/libblock.cpp @@ -246,11 +246,12 @@ static int l_get_user_bits(lua::State* L) { } static int l_get_variant(lua::State* L) { + auto& chunks = *level->chunks; auto x = lua::tointeger(L, 1); auto y = lua::tointeger(L, 2); auto z = lua::tointeger(L, 3); - auto vox = blocks_agent::get(*level->chunks, x, y, z); + auto vox = blocks_agent::get(chunks, x, y, z); if (vox == nullptr) { return lua::pushinteger(L, 0); } @@ -258,6 +259,13 @@ static int l_get_variant(lua::State* L) { if (def.variants == nullptr) { return lua::pushinteger(L, 0); } + if (def.rt.extended) { + auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); + vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z); + if (vox == nullptr) { + return lua::pushinteger(L, 0); + } + } return lua::pushinteger( L, (vox->state.userbits >> def.variants->offset) & def.variants->mask ); @@ -290,6 +298,14 @@ static int l_set_user_bits(lua::State* L) { if (vox == nullptr) { return 0; } + int ocx = floordiv(origin.x); + int ocz = floordiv(origin.z); + if (cx != ocx || cz != ocz) { + chunk = blocks_agent::get_chunk(chunks, ocx, ocz); + if (chunk == nullptr) { + return 0; + } + } } vox->state.userbits = (vox->state.userbits & (~mask)) | value; chunk->setModifiedAndUnsaved(); @@ -318,8 +334,8 @@ static int l_set_variant(lua::State* L) { } auto offset = def.variants->offset; - auto mask = def.variants->mask; - auto value = (lua::tointeger(L, 4) << offset) & mask; + auto mask = def.variants->mask << offset; + auto value = (lua::tointeger(L, 4) << offset); if (def.rt.extended) { auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); @@ -327,6 +343,14 @@ static int l_set_variant(lua::State* L) { if (vox == nullptr) { return 0; } + int ocx = floordiv(origin.x); + int ocz = floordiv(origin.z); + if (cx != ocx || cz != ocz) { + chunk = blocks_agent::get_chunk(chunks, ocx, ocz); + if (chunk == nullptr) { + return 0; + } + } } vox->state.userbits = (vox->state.userbits & (~mask)) | value; chunk->setModifiedAndUnsaved(); From 0b7dc14f5512478c7b7df408bab007d06de6f9ae Mon Sep 17 00:00:00 2001 From: clasher113 Date: Sat, 28 Feb 2026 18:31:15 +0200 Subject: [PATCH 2/2] fix extended block grounding (new block property) --- doc/en/block-properties.md | 9 +++++ doc/ru/block-properties.md | 8 +++++ src/content/loading/BlockLoader.cpp | 9 +++++ src/logic/BlocksController.cpp | 15 +++++--- src/logic/PlayerController.cpp | 6 ++-- src/voxels/Block.hpp | 19 +++++++++++ src/voxels/blocks_agent.hpp | 53 +++++++++++++++++++++++++++++ 7 files changed, 111 insertions(+), 8 deletions(-) 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