Merge pull request #805 from clasher113/main

bug fix
This commit is contained in:
MihailRis 2026-03-02 01:07:18 +03:00 • committed by GitHub
commit dc27bd1430
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 149 additions and 16 deletions

View file

@ -241,6 +241,15 @@ Number of block inventory slots. Default - 0 (no inventory).
Array of three integers. Default value is `[1, 1, 1]`. 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
Block fields allow you to write more data unique to a specified voxel than the user bits allow. Block fields allow you to write more data unique to a specified voxel than the user bits allow.

View file

@ -67,7 +67,7 @@ block.get_picking_item(id: int) -> int
block.get_variant(x: int, y: int, z: int) -> int block.get_variant(x: int, y: int, z: int) -> int
-- Sets the block variant by index -- 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 -- Checks if an block has specified tag
block.has_tag(id: int, tag: str) -> bool block.has_tag(id: int, tag: str) -> bool

View file

@ -247,6 +247,14 @@
Массив из трех целых чисел. Значение по-умолчанию - `[1, 1, 1]`. Массив из трех целых чисел. Значение по-умолчанию - `[1, 1, 1]`.
### Поведение приземленности - *grounding-behaviour*
Определяет поведение расширенного приземленного блока. Доступные варианты поведения:
- "partial" - как минимум один сегмент блока должен быть приземленным (по-умолчанию)
- "complete" - все сегменты блока должны быть приземленными
- "origin" - главный сегмент должен быть приземленным
## Поля блока ## Поля блока
Поля блоков позволяет записывать больше уникальных для конкретного блока данных, чем это позволяют пользовательские биты. Поля блоков позволяет записывать больше уникальных для конкретного блока данных, чем это позволяют пользовательские биты.

View file

@ -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.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] > [!WARNING]

View file

@ -121,8 +121,8 @@ template<> void ContentUnitLoader<Block>::loadUnit(
} }
def.variants = std::make_unique<Variants>(); def.variants = std::make_unique<Variants>();
def.variants->offset = 0; def.variants->offset = offset;
def.variants->mask = 0xF; def.variants->mask = (1ULL << bitsCount) - 1;
def.variants->variants.push_back(def.defaults); def.variants->variants.push_back(def.defaults);
for (int i = 0; i < variants.size(); i++) { for (int i = 0; i < variants.size(); i++) {
Variant variant = def.defaults; Variant variant = def.defaults;
@ -220,6 +220,15 @@ template<> void ContentUnitLoader<Block>::loadUnit(
def.defaults.model.type = BlockModelType::AABB; def.defaults.model.type = BlockModelType::AABB;
def.hitboxes = {AABB(def.size)}; 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 // primitive properties

View file

@ -70,7 +70,7 @@ void ContentGfxCache::refresh(const Block& def, const Atlas& atlas) {
refreshVariant(def, def.defaults, 0, atlas); refreshVariant(def, def.defaults, 0, atlas);
if (def.variants) { if (def.variants) {
const auto& variants = def.variants->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); refreshVariant(def, variants[i], i, atlas);
} }
def.variants->variants.at(0) = def.defaults; def.variants->variants.at(0) = def.defaults;

View file

@ -92,6 +92,12 @@ void ModelsGenerator::prepareModel(
void ModelsGenerator::prepare(Content& content, Assets& assets) { void ModelsGenerator::prepare(Content& content, Assets& assets) {
for (auto& [name, def] : content.blocks.getDefs()) { for (auto& [name, def] : content.blocks.getDefs()) {
prepareModel(assets, *def, def->defaults, 0); 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()) { for (auto& [name, def] : content.items.getDefs()) {
assets.store( assets.store(

View file

@ -111,10 +111,17 @@ void BlocksController::updateBlock(int x, int y, int z) {
if (vox == nullptr) return; if (vox == nullptr) return;
const auto& def = level.content.getIndices()->blocks.require(vox->id); const auto& def = level.content.getIndices()->blocks.require(vox->id);
if (def.grounded) { if (def.grounded) {
const auto& vec = get_ground_direction(def, vox->state.rotation); if (def.rt.extended) {
if (!blocks_agent::is_solid_at(chunks, x + vec.x, y + vec.y, z + vec.z)) { auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state);
breakBlock(nullptr, def, x, y, z); if (!blocks_agent::check_grounding(chunks, def, vox->state.rotation, origin)) {
return; 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) { if (def.rt.funcsset.update) {

View file

@ -22,6 +22,7 @@
#include "scripting/scripting.hpp" #include "scripting/scripting.hpp"
#include "settings.hpp" #include "settings.hpp"
#include "voxels/Block.hpp" #include "voxels/Block.hpp"
#include "voxels/blocks_agent.hpp"
#include "voxels/Chunks.hpp" #include "voxels/Chunks.hpp"
#include "voxels/voxel.hpp" #include "voxels/voxel.hpp"
#include "window/Camera.hpp" #include "window/Camera.hpp"
@ -448,10 +449,7 @@ void PlayerController::processRightClick(
return; return;
} }
if (def.grounded) { if (def.grounded) {
const auto& vec = get_ground_direction(def, state.rotation); if (!blocks_agent::check_grounding(chunks, def, state.rotation, coord)) {
if (!chunks.isSolidBlock(
coord.x + vec.x, coord.y + vec.y, coord.z + vec.z
)) {
return; return;
} }
} }

View file

@ -246,11 +246,12 @@ static int l_get_user_bits(lua::State* L) {
} }
static int l_get_variant(lua::State* L) { static int l_get_variant(lua::State* L) {
auto& chunks = *level->chunks;
auto x = lua::tointeger(L, 1); auto x = lua::tointeger(L, 1);
auto y = lua::tointeger(L, 2); auto y = lua::tointeger(L, 2);
auto z = lua::tointeger(L, 3); 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) { if (vox == nullptr) {
return lua::pushinteger(L, 0); return lua::pushinteger(L, 0);
} }
@ -258,6 +259,13 @@ static int l_get_variant(lua::State* L) {
if (def.variants == nullptr) { if (def.variants == nullptr) {
return lua::pushinteger(L, 0); 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( return lua::pushinteger(
L, (vox->state.userbits >> def.variants->offset) & def.variants->mask 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) { if (vox == nullptr) {
return 0; return 0;
} }
int ocx = floordiv<CHUNK_W>(origin.x);
int ocz = floordiv<CHUNK_D>(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; vox->state.userbits = (vox->state.userbits & (~mask)) | value;
chunk->setModifiedAndUnsaved(); chunk->setModifiedAndUnsaved();
@ -318,8 +334,8 @@ static int l_set_variant(lua::State* L) {
} }
auto offset = def.variants->offset; auto offset = def.variants->offset;
auto mask = def.variants->mask; auto mask = def.variants->mask << offset;
auto value = (lua::tointeger(L, 4) << offset) & mask; auto value = (lua::tointeger(L, 4) << offset);
if (def.rt.extended) { if (def.rt.extended) {
auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state); 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) { if (vox == nullptr) {
return 0; return 0;
} }
int ocx = floordiv<CHUNK_W>(origin.x);
int ocz = floordiv<CHUNK_D>(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; vox->state.userbits = (vox->state.userbits & (~mask)) | value;
chunk->setModifiedAndUnsaved(); chunk->setModifiedAndUnsaved();

View file

@ -140,6 +140,22 @@ VC_ENUM_METADATA(CullingMode)
{"disabled", CullingMode::DISABLED}, {"disabled", CullingMode::DISABLED},
VC_ENUM_END 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 /// @brief Common kit of block properties applied to groups of blocks
struct BlockMaterial : Serializable { struct BlockMaterial : Serializable {
std::string name; std::string name;
@ -239,6 +255,9 @@ public:
/// @brief Explicitly overriding 'solid' property if true assigned /// @brief Explicitly overriding 'solid' property if true assigned
bool explictlySolid = false; bool explictlySolid = false;
/// @brief Grounding behaviour
GroundingBehaviour groundingBehaviour = GroundingBehaviour::PARTIAL;
/// @brief Set of block physical hitboxes /// @brief Set of block physical hitboxes
std::vector<AABB> hitboxes {AABB()}; std::vector<AABB> hitboxes {AABB()};

View file

@ -494,4 +494,57 @@ inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float
return nullptr; 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 <class Storage>
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 } // blocks_agent