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]`.
### *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.

View file

@ -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

View file

@ -247,6 +247,14 @@
Массив из трех целых чисел. Значение по-умолчанию - `[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.set_variant(x: int, y: int, z: int, index: int) -> int
block.set_variant(x: int, y: int, z: int, index: int)
```
> [!WARNING]

View file

@ -121,8 +121,8 @@ template<> void ContentUnitLoader<Block>::loadUnit(
}
def.variants = std::make_unique<Variants>();
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;
@ -220,6 +220,15 @@ template<> void ContentUnitLoader<Block>::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

View file

@ -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;

View file

@ -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(

View file

@ -111,10 +111,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) {

View file

@ -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;
}
}

View file

@ -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<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;
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<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;
chunk->setModifiedAndUnsaved();

View file

@ -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<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;
}
/// @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