[BlockState] Added. First stop of block states.

This commit is contained in:
Quentin Bazin 2020-07-10 16:24:29 +02:00
parent 0af7cd7d0e
commit 7750154b22
40 changed files with 574 additions and 334 deletions

View File

@ -263,6 +263,7 @@ Useful for flora, tallgrass, mushrooms, etc...
Parameters:
- `pos` (`ivec3`): position of the block
- `block` (`ServerBlock`): block definition
- `player` (`ServerPlayer`): player that activated the block
- `world` (`ServerWorld`): instance of the world
- `client` (`Client`): client that activated the block
@ -290,6 +291,7 @@ Parameters:
Parameters:
- `pos` (`ivec3`): position of the block
- `block` (`ServerBlock`): block definition
- `chunk` (`ServerChunk`): current chunk
- `world` (`ServerWorld`): instance of the world

View File

@ -35,8 +35,8 @@ end)
Possible events:
- `BlockPlaced`: `funcion(pos, block, player, world, client, server)`
- `BlockDigged`: `funcion(pos, block, player, world, client, server)`
- `BlockPlaced`: `funcion(pos, block_state, player, world, client, server)`
- `BlockDigged`: `funcion(pos, block_state, player, world, client, server)`
- `BlockActivated`: `function(pos, block, player, world, client, server)`
- `ItemActivated`: `function(pos, block, player, world, client, server)`
- `PlayerConnected`: `function(pos, player, client, server)`

View File

@ -5,13 +5,10 @@
## Block
- `u16 id()`
- `u16 data()`
- `string string_id()`
- `string label()`
- `string mod_name()`
- `bool is_opaque()`
- `ItemStack get_item_drop()`
- `BlockParam param()`
- `Block get_state()`
## BlockData

View File

@ -208,7 +208,7 @@ mod:block {
draw_type = "glass",
is_opaque = false,
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
local dim = (player:dimension() + 1) % 2
local pos = {
x = math.floor(player:x()),
@ -311,7 +311,7 @@ mod:block {
name = "Redstone Lamp",
tiles = "redstone_lamp_off.png",
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
local block = openminer.registry:get_block_from_string("default:redstone_lamp_on")
world:set_block(pos.x, pos.y, pos.z, block:id())
end
@ -327,7 +327,7 @@ mod:block {
ci_ignore = 1
},
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
local block = openminer.registry:get_block_from_string("default:redstone_lamp_off")
world:set_block(pos.x, pos.y, pos.z, block:id())
end
@ -356,11 +356,22 @@ mod:block {
id = "seeds",
name = "Seeds",
tiles = "wheat_stage_0.png",
alt_tiles = "wheat_stage_7.png",
draw_type = "xshape",
inventory_image = "seeds_wheat.png",
hardness = 0,
bounding_box = {0, 0, 0, 1, 1, 1.0 / 16.0},
states = {
[1] = { tiles = "wheat_stage_1.png", bounding_box = {0, 0, 0, 1, 1, 3.0 / 16.0}, },
[2] = { tiles = "wheat_stage_2.png", bounding_box = {0, 0, 0, 1, 1, 5.0 / 16.0}, },
[3] = { tiles = "wheat_stage_3.png", bounding_box = {0, 0, 0, 1, 1, 8.0 / 16.0}, },
[4] = { tiles = "wheat_stage_4.png", bounding_box = {0, 0, 0, 1, 1, 10.0 / 16.0}, },
[5] = { tiles = "wheat_stage_5.png", bounding_box = {0, 0, 0, 1, 1, 12.0 / 16.0}, },
[6] = { tiles = "wheat_stage_6.png", bounding_box = {0, 0, 0, 1, 1, 14.0 / 16.0}, },
[7] = { tiles = "wheat_stage_7.png", bounding_box = {0, 0, 0, 1, 1, 1}, },
},
tick_randomly = true,
tick_probability = 0.01,
@ -368,25 +379,20 @@ mod:block {
world:add_block_data(pos.x, pos.y, pos.z, 0, 0)
end,
on_tick = function(pos, chunk, world)
local data = world:get_block_data(pos.x, pos.y, pos.z)
if not data then return end
local growth_stage = data.meta:get_int("growth_stage") or 0
if growth_stage < 7 then
data.use_alt_tiles = true
data.meta:set_int("growth_stage", 7)
on_tick = function(pos, block, chunk, world)
local block_param = world:get_block_param(pos.x, pos.y, pos.z)
local current_state = block:param():get_param(BlockParam.State, block_param)
if current_state < 7 then
world:set_block_param(pos.x, pos.y, pos.z, current_state + 1)
end
end,
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
local data = world:get_block_data(pos.x, pos.y, pos.z)
if not data then return end
local growth_stage = data.meta:get_int("growth_stage") or 0
if growth_stage >= 7 then
data.use_alt_tiles = false
data.meta:set_int("growth_stage", 0)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
local block_param = world:get_block_param(pos.x, pos.y, pos.z)
local current_state = block:param():get_param(BlockParam.State, block_param)
if current_state >= 7 then
world:set_block_param(pos.x, pos.y, pos.z,
block:param():set_param(BlockParam.State, block_param, 0))
-- FIXME: It should drop the item if 'default:use_item_drops' is enabled
local item_stack = ItemStack.new("default:wheat", 1)

View File

@ -66,7 +66,7 @@ mod:block {
end
end,
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
open_door(pos, world)
open_door({x = pos.x, y = pos.y, z = pos.z - 1}, world)
end

View File

@ -37,7 +37,7 @@ mod:block {
world:add_block_data(pos.x, pos.y, pos.z, 3, 1)
end,
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
local gui = LuaGUI.new()
gui:set_size(176, 166)
@ -169,7 +169,7 @@ mod:block {
gui:show(client)
end,
on_tick = function(pos, chunk, world)
on_tick = function(pos, block, chunk, world)
local data = world:get_block_data(pos.x, pos.y, pos.z)
if not data then return end

View File

@ -35,7 +35,7 @@ mod:block {
world:add_block_data(pos.x, pos.y, pos.z, 3, 3)
end,
on_block_activated = function(pos, player, world, client, server, screen_width, screen_height, gui_scale)
on_block_activated = function(pos, block, player, world, client, server, screen_width, screen_height, gui_scale)
local gui = LuaGUI.new()
gui:set_size(176, 166)

View File

@ -136,11 +136,13 @@ void TextureAtlas::loadFromRegistry(const std::string &texturePack) {
else
path = "texturepacks/" + texturePack + "/blocks/";
const TilesDef &tiles = block->tiles();
for (auto &textureFilename : tiles.textureFilenames())
addFile(path, textureFilename);
for (auto &textureFilename : tiles.altTextureFilenames())
addFile(path, textureFilename);
for (auto &state : block->states()) {
const TilesDef &tiles = state.tiles();
for (auto &textureFilename : tiles.textureFilenames())
addFile(path, textureFilename);
for (auto &textureFilename : tiles.altTextureFilenames())
addFile(path, textureFilename);
}
}
for (auto &item : Registry::getInstance().items()) {

View File

@ -58,9 +58,11 @@ InventoryCube::InventoryCube(float size, bool isEntity)
using namespace BlockGeometry;
void InventoryCube::updateVertexBuffer(const Block &block) {
void InventoryCube::updateVertexBuffer(const Block &block, u8 state) {
if (!block.id()) return;
const BlockState &blockState = block.getState(state);
Vertex vertices[nFaces][nVertsPerFace];
glm::vec3 vertexPos[nVertsPerCube] {
@ -75,7 +77,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) {
glm::vec3{m_size, m_size, m_size},
};
const gk::FloatBox &boundingBox = block.boundingBox();
const gk::FloatBox &boundingBox = blockState.boundingBox();
constexpr s8f faceValue[nFaces]{2, 2, 4, 4, 3, 3};
@ -86,7 +88,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) {
// U0V0 -> 3 2 <- U1V0
// U0V1 -> 0 1 <- U1V1
float U0, V0, U1, V1;
if (block.drawType() == BlockDrawType::Cactus) {
if (blockState.drawType() == BlockDrawType::Cactus) {
U0 = 0.f;
V0 = 0.f;
U1 = 1.f;
@ -101,10 +103,10 @@ void InventoryCube::updateVertexBuffer(const Block &block) {
V1 = (f <= 3) ? 1.f - boundingBox.z : (f == 4) ? boundingBox.y + boundingBox.sizeY : 1.f - boundingBox.y;
}
const gk::FloatRect &blockTexCoords = m_textureAtlas->getTexCoords(block.tiles().getTextureForFace(f));
const gk::FloatRect &blockTexCoords = m_textureAtlas->getTexCoords(blockState.tiles().getTextureForFace(f));
for (u8f v = 0; v < nVertsPerFace; ++v) {
if (block.drawType() == BlockDrawType::Cactus) {
if (blockState.drawType() == BlockDrawType::Cactus) {
vertices[f][v].coord3d[0] = vertexPos[cubeVerts[f][v]].x - boundingBox.x * faceNormals[f][0] * m_size;
vertices[f][v].coord3d[1] = vertexPos[cubeVerts[f][v]].y - boundingBox.y * faceNormals[f][1] * m_size;
vertices[f][v].coord3d[2] = vertexPos[cubeVerts[f][v]].z - boundingBox.z * faceNormals[f][2] * m_size;
@ -121,7 +123,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) {
vertices[f][v].texCoord[0] = gk::qlerp(blockTexCoords.x, blockTexCoords.x + blockTexCoords.sizeX, U);
vertices[f][v].texCoord[1] = gk::qlerp(blockTexCoords.y, blockTexCoords.y + blockTexCoords.sizeY, V);
const gk::Color &colorMultiplier = block.colorMultiplier();
const gk::Color &colorMultiplier = blockState.colorMultiplier();
vertices[f][v].color[0] = colorMultiplier.r;
vertices[f][v].color[1] = colorMultiplier.g;
vertices[f][v].color[2] = colorMultiplier.b;

View File

@ -38,7 +38,7 @@ class InventoryCube : public gk::Drawable, public gk::Transformable {
public:
InventoryCube(float size = 1.0f, bool isEntity = false);
void updateVertexBuffer(const Block &block);
void updateVertexBuffer(const Block &block, u8 state = 0);
float size() const { return m_size; }

View File

@ -42,12 +42,13 @@ ItemWidget::ItemWidget(Inventory &inventory, u16 x, u16 y, Widget *parent)
void ItemWidget::update() {
if (stack().item().isBlock()) {
const Block &block = Registry::getInstance().getBlock(stack().item().id());
if (block.drawType() != BlockDrawType::XShape && block.inventoryImage().empty()) {
const BlockState &blockState = block.getState(0); // FIXME: Get state from item stack
if (blockState.drawType() != BlockDrawType::XShape && blockState.inventoryImage().empty()) {
m_cube.updateVertexBuffer(block);
m_isImage = false;
}
else
updateImage(&block);
updateImage(&blockState);
}
else
updateImage();
@ -56,7 +57,7 @@ void ItemWidget::update() {
m_text.setPosition(16 - 4 - 6 * floor(log10(stack().amount())), 16 - 6, 0);
}
void ItemWidget::updateImage(const Block *block) {
void ItemWidget::updateImage(const BlockState *blockState) {
if (m_image.width() == 0) {
m_image.load(m_textureAtlas.texture());
m_image.setPosition(1, 1, 0);
@ -67,8 +68,8 @@ void ItemWidget::updateImage(const Block *block) {
m_image.setClipRect(clipRect.x, clipRect.y, clipRect.sizeX, clipRect.sizeY);
m_image.setScale(16.0f / clipRect.sizeX, 16.0f / clipRect.sizeY);
if (block)
m_image.setColor(block->colorMultiplier());
if (blockState)
m_image.setColor(blockState->colorMultiplier());
m_isImage = true;
}

View File

@ -34,12 +34,14 @@
#include "Text.hpp"
#include "Widget.hpp"
class BlockState;
class ItemWidget : public Widget {
public:
ItemWidget(Inventory &inventory, u16 x, u16 y, Widget *parent = nullptr);
void update() override;
void updateImage(const Block *block = nullptr);
void updateImage(const BlockState *blockState = nullptr);
const ItemStack &stack() const { return m_inventory.getStack(m_x, m_y); }
void setStack(const std::string &name, unsigned int amount = 1);

View File

@ -95,12 +95,14 @@ void BlockCursor::onEvent(const SDL_Event &event, const Hotbar &hotbar) {
if (face == 5) --z;
// First, we check if the new block is not replacing another block
u32 blockId = m_world.getBlock(x, y, z);
const Block &block = Registry::getInstance().getBlock(blockId);
if (!blockId || block.drawType() == BlockDrawType::Liquid) {
// u32 blockId = m_world.getBlock(x, y, z);
// const Block &block = Registry::getInstance().getBlock(blockId);
const BlockState *blockState = m_world.getBlockState(x, y, z);
if (blockState && (!blockState->block().id() || blockState->drawType() == BlockDrawType::Liquid)) {
// Second, we check if the new block is not inside the player
const Block &newBlock = Registry::getInstance().getBlock(hotbar.currentItem().id());
gk::FloatBox boundingBox = newBlock.boundingBox() + gk::Vector3f(x - m_player.x(), y - m_player.y(), z - m_player.z());
const BlockState &newBlockState = newBlock.getState(0); // FIXME: Get state from item stack
gk::FloatBox boundingBox = newBlockState.boundingBox() + gk::Vector3f(x - m_player.x(), y - m_player.y(), z - m_player.z());
gk::FloatBox playerBoundingBox = m_player.hitbox();
if (!boundingBox.intersects(playerBoundingBox)) {
u32 block = hotbar.currentItem().id();
@ -137,12 +139,14 @@ void BlockCursor::update(const Hotbar &hotbar) {
m_selectedBlock = selectedBlock;
m_currentBlock = &Registry::getInstance().getBlock(m_world.getBlock(m_selectedBlock.x, m_selectedBlock.y, m_selectedBlock.z));
m_currentBlock = m_world.getBlockState(m_selectedBlock.x, m_selectedBlock.y, m_selectedBlock.z);
// if (block.boundingBox().intersects(FloatBox{m_selectedBlock.x, m_selectedBlock.y, m_selectedBlock.z, 1, 1, 1})) {
// selectedBlockChanged = false;
// m_selectedBlock.w = -1;
// }
if (!m_currentBlock) return;
u32 ticks = gk::GameClock::getInstance().getTicks();
if (selectedBlockChanged)
@ -167,7 +171,7 @@ void BlockCursor::update(const Hotbar &hotbar) {
}
}
u8f orientation = m_currentBlock->isRotatable() ? m_world.getData(selectedBlock.x, selectedBlock.y, selectedBlock.z) & 0x1F : 0;
u8f orientation = m_currentBlock->block().isRotatable() ? m_world.getData(selectedBlock.x, selectedBlock.y, selectedBlock.z) & 0x1F : 0;
if (m_selectedBlock.w != -1)
updateVertexBuffer(*m_currentBlock, orientation);
@ -181,11 +185,11 @@ void BlockCursor::update(const Hotbar &hotbar) {
using namespace BlockGeometry;
void BlockCursor::updateVBOCoords(Vertex vertices[nFaces][nVertsPerFace], const Block &block,
void BlockCursor::updateVBOCoords(Vertex vertices[nFaces][nVertsPerFace], const BlockState &blockState,
float face, u8f orientation)
{
glm::vec3 bottomLeft{block.boundingBox().x, block.boundingBox().y, block.boundingBox().z};
glm::vec3 topRight{block.boundingBox().sizeX, block.boundingBox().sizeY, block.boundingBox().sizeZ};
glm::vec3 bottomLeft{blockState.boundingBox().x, blockState.boundingBox().y, blockState.boundingBox().z};
glm::vec3 topRight{blockState.boundingBox().sizeX, blockState.boundingBox().sizeY, blockState.boundingBox().sizeZ};
topRight += bottomLeft;
const glm::mat3 &orientMatrix = orientMatrices[orientation];
@ -220,18 +224,18 @@ void BlockCursor::updateVBOCoords(Vertex vertices[nFaces][nVertsPerFace], const
}
}
void BlockCursor::updateVertexBuffer(const Block &block, u8f orientation) {
void BlockCursor::updateVertexBuffer(const BlockState &blockState, u8f orientation) {
Vertex vertices[nFaces][nVertsPerFace];
updateVBOCoords(vertices, block, -1, orientation);
updateVBOCoords(vertices, blockState, -1, orientation);
gk::VertexBuffer::bind(&m_vbo);
m_vbo.setData(sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
gk::VertexBuffer::bind(nullptr);
}
void BlockCursor::updateAnimationVertexBuffer(const Block &block, u8f orientation, int animationPos) {
void BlockCursor::updateAnimationVertexBuffer(const BlockState &blockState, u8f orientation, int animationPos) {
Vertex vertices[nFaces][nVertsPerFace];
updateVBOCoords(vertices, block, -2, orientation);
updateVBOCoords(vertices, blockState, -2, orientation);
GLfloat color[4] = {1, 1, 1, 0.5};
for (u8f f = 0; f < nFaces; ++f)
@ -301,9 +305,8 @@ glm::ivec4 BlockCursor::findSelectedBlock() const {
s32f bestZ = s32f(floor(position.z));
// Deal with a degenerate case: camera in the middle of a block
u32f blockID = m_world.getBlock(bestX, bestY, bestZ);
const Block &block = Registry::getInstance().getBlock(blockID);
if (blockID && block.drawType() != BlockDrawType::Liquid) {
const BlockState *blockState = m_world.getBlockState(bestX, bestY, bestZ);
if (blockState && blockState->block().id() && blockState->drawType() != BlockDrawType::Liquid) {
// We're inside a node, therefore there's no face, but we still need
// to return a valid block. We use face 6 for that. For rightclicks,
// it should attempt to place the block on the same node clicked, and
@ -320,9 +323,8 @@ glm::ivec4 BlockCursor::findSelectedBlock() const {
if (double(bestX) == position.x && double(bestY) == position.y) {
for (int y = -1; y <= 0; y++) {
for (int x = -1; x <= 0; x++) {
blockID = m_world.getBlock(bestX + x, bestY + y, bestZ);
const Block &block = Registry::getInstance().getBlock(blockID);
if (blockID && block.drawType() != BlockDrawType::Liquid) {
const BlockState *blockState = m_world.getBlockState(bestX, bestY, bestZ);
if (blockState && blockState->block().id() && blockState->drawType() != BlockDrawType::Liquid) {
return glm::ivec4{bestX + x, bestY + y, bestZ, 6};
}
}

View File

@ -45,13 +45,13 @@ class BlockCursor : public gk::Drawable {
void update(const Hotbar &hotbar);
const Block *currentBlock() const { return m_currentBlock; }
const BlockState *currentBlock() const { return m_currentBlock; }
private:
void updateVertexBuffer(const Block &block, const u8f orientation);
void updateAnimationVertexBuffer(const Block &block, const u8f orientation, int animationPos = -1);
void updateVertexBuffer(const BlockState &blockState, const u8f orientation);
void updateAnimationVertexBuffer(const BlockState &blockState, const u8f orientation, int animationPos = -1);
void updateVBOCoords(Vertex vertices[BlockGeometry::nFaces][BlockGeometry::nVertsPerFace],
const Block &block, float face, u8f orientation);
const BlockState &blockState, float face, u8f orientation);
void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
@ -66,7 +66,7 @@ class BlockCursor : public gk::Drawable {
unsigned int m_animationStart = 0;
glm::ivec4 m_selectedBlock{0, 0, 0, -1};
const Block *m_currentBlock = nullptr;
const BlockState *m_currentBlock = nullptr;
const ItemStack *m_currentTool = nullptr;
gk::Texture *m_blockDestroyTexture = nullptr;

View File

@ -40,16 +40,16 @@ void BlockInfoWidget::update() {
m_itemWidget.update();
}
void BlockInfoWidget::setCurrentBlock(const Block *block) {
m_currentBlock = block;
void BlockInfoWidget::setCurrentBlock(const BlockState *blockState) {
m_currentBlock = blockState;
if (!m_currentBlock)
m_isVisible = false;
else {
m_isVisible = true;
m_text.setString(block->label());
m_itemWidget.setStack(block->stringID(), 1);
m_text.setString(blockState->label());
m_itemWidget.setStack(blockState->block().stringID(), 1);
}
}

View File

@ -37,8 +37,8 @@ class BlockInfoWidget : public Widget {
void update() override;
const Block *currentBlock() const { return m_currentBlock; }
void setCurrentBlock(const Block *block);
const BlockState *currentBlock() const { return m_currentBlock; }
void setCurrentBlock(const BlockState *blockState);
private:
void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
@ -51,7 +51,7 @@ class BlockInfoWidget : public Widget {
bool m_isVisible = false;
const Block *m_currentBlock = nullptr;
const BlockState *m_currentBlock = nullptr;
};
#endif // BLOCKINFOWIDGET_HPP_

View File

@ -117,12 +117,12 @@ void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &positi
ny = axis == AXIS_Y ? nodeRow : floor(isect.y);
nz = axis == AXIS_Z ? nodeRow : floor(isect.z);
u32 blockID = world.getBlock(nx, ny, nz);
const Block &block = Registry::getInstance().getBlock(blockID);
const BlockState *blockState = world.getBlockState(nx, ny, nz);
if (!blockState) continue;
u8f orientation = block.isRotatable() ? world.getData(nx, ny, nz) & 0x1F : 0;
u8f orientation = blockState->block().isRotatable() ? world.getData(nx, ny, nz) & 0x1F : 0;
const gk::FloatBox &boundingBox = block.boundingBox();
const gk::FloatBox &boundingBox = blockState->boundingBox();
glm::vec3 localCorner1{boundingBox.x, boundingBox.y, boundingBox.z};
glm::vec3 localCorner2{boundingBox.sizeX, boundingBox.sizeY, boundingBox.sizeZ};
localCorner2 += localCorner1;
@ -136,7 +136,7 @@ void BlockCursorRaycast::rayCastToAxis(const Axis axis, const glm::dvec3 &positi
if (localCorner2.z < localCorner1.z) std::swap(localCorner1.z, localCorner2.z);
}
if (blockID && block.drawType() != BlockDrawType::Liquid) {
if (blockState->block().id() && blockState->drawType() != BlockDrawType::Liquid) {
// Check bounding box; this should loop over all selection boxes
// when they are implemented
const glm::dvec3 cubePos{double(nx), double(ny), double(nz)};

View File

@ -43,22 +43,26 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
for (s8f z = 0 ; z < CHUNK_HEIGHT ; z++) {
for (s8f y = 0 ; y < CHUNK_DEPTH ; y++) {
for (s8f x = 0 ; x < CHUNK_WIDTH ; x++) {
const Block &block = Registry::getInstance().getBlock(chunk.getBlock(x, y, z));
if (!block.id()) continue;
if (!chunk.getBlock(x, y, z)) continue;
u16 blockID = chunk.getBlock(x, y, z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (!blockID) continue;
const gk::FloatBox &boundingBox = block.boundingBox();
u16 blockParam = chunk.getData(x, y, z);
const BlockState &blockState = block.getState(block.param().hasParam(BlockParam::State)
? block.param().getParam(BlockParam::State, blockParam) : 0);
const gk::FloatBox &boundingBox = blockState.boundingBox();
u8f orientation = block.isRotatable()
? block.param().getParam(BlockParam::Rotation, chunk.getData(x, y, z)) : 0;
? block.param().getParam(BlockParam::Rotation, blockParam) : 0;
const glm::mat3 &orientMatrix = orientMatrices[orientation];
if (block.drawType() == BlockDrawType::Solid
|| block.drawType() == BlockDrawType::Leaves
|| block.drawType() == BlockDrawType::Liquid
|| block.drawType() == BlockDrawType::Glass
|| block.drawType() == BlockDrawType::Cactus
|| block.drawType() == BlockDrawType::BoundingBox)
if (blockState.drawType() == BlockDrawType::Solid
|| blockState.drawType() == BlockDrawType::Leaves
|| blockState.drawType() == BlockDrawType::Liquid
|| blockState.drawType() == BlockDrawType::Glass
|| blockState.drawType() == BlockDrawType::Cactus
|| blockState.drawType() == BlockDrawType::BoundingBox)
{
glm::vec3 vertexPos[nVertsPerCube]{
// Order is important. It matches the bit order defined in BlockGeometry::cubeVerts.
@ -72,7 +76,7 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
{boundingBox.x + boundingBox.sizeX, boundingBox.y + boundingBox.sizeY, boundingBox.z + boundingBox.sizeZ},
};
if (block.drawType() == BlockDrawType::Cactus) {
if (blockState.drawType() == BlockDrawType::Cactus) {
// Ignore bounding box, initialize it to full node coordinates
for (u8f i = 0; i < nVertsPerCube; ++i) {
vertexPos[i].x = (i >> 0) & 1;
@ -115,10 +119,10 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
const gk::Vector3i *vFaceNeighbours[nVertsPerFace]{&corner0, &corner1, &corner2, &corner3};
addFace(x, y, z, f, chunk, block, normal, faceVerts, vFaceNeighbours);
addFace(x, y, z, f, chunk, blockState, normal, faceVerts, vFaceNeighbours);
}
}
else if (block.drawType() == BlockDrawType::XShape) {
else if (blockState.drawType() == BlockDrawType::XShape) {
glm::vec3 vertexPos[nVertsPerCube]{
{0, 0, 0},
{1, 0, 0},
@ -135,7 +139,7 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
{&vertexPos[crossVerts[1][0]], &vertexPos[crossVerts[1][1]],
&vertexPos[crossVerts[1][2]], &vertexPos[crossVerts[1][3]]},
};
addCross(x, y, z, chunk, block, faceVertices);
addCross(x, y, z, chunk, blockState, faceVertices);
}
}
}
@ -157,26 +161,25 @@ std::array<std::size_t, ChunkBuilder::layers> ChunkBuilder::buildChunk(const Cli
return verticesCount;
}
inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const Block &block,
inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const BlockState &blockState,
const gk::Vector3i &normal, const glm::vec3 *const vertexPos[nVertsPerFace],
const gk::Vector3i *const neighbourOfs[nVertsPerFace])
{
// Get surrounding block for the face
u16 surroundingBlockID = chunk.getBlock(x + normal.x, y + normal.y, z + normal.z);
const Block *surroundingBlock = &Registry::getInstance().getBlock(surroundingBlockID);
const BlockState *surroundingBlockState = chunk.getBlockState(x + normal.x, y + normal.y, z + normal.z);
// Skip hidden faces
if (surroundingBlock && surroundingBlock->id()
&& ((block.drawType() == BlockDrawType::Solid && surroundingBlock->drawType() == BlockDrawType::Solid && surroundingBlock->isOpaque())
|| (block.id() == surroundingBlock->id() && (block.drawType() == BlockDrawType::Liquid || block.drawType() == BlockDrawType::Glass))
|| (block.drawType() == BlockDrawType::Liquid && surroundingBlock->drawType() == BlockDrawType::Solid)
|| (block.drawType() == BlockDrawType::Cactus && surroundingBlock->id() == block.id() && f > 3)))
if (surroundingBlockState && surroundingBlockState->block().id()
&& ((blockState.drawType() == BlockDrawType::Solid && surroundingBlockState->drawType() == BlockDrawType::Solid && surroundingBlockState->isOpaque())
|| (blockState.block().id() == surroundingBlockState->block().id() && (blockState.drawType() == BlockDrawType::Liquid || blockState.drawType() == BlockDrawType::Glass))
|| (blockState.drawType() == BlockDrawType::Liquid && surroundingBlockState->drawType() == BlockDrawType::Solid)
|| (blockState.drawType() == BlockDrawType::Cactus && surroundingBlockState->block().id() == blockState.block().id() && f > 3)))
return;
const gk::FloatBox &boundingBox = block.boundingBox();
const gk::FloatBox &boundingBox = blockState.boundingBox();
const BlockData *blockData = chunk.getBlockData(x, y, z);
const std::string &texture = block.tiles().getTextureForFace(f, blockData ? blockData->useAltTiles : false);
const std::string &texture = blockState.tiles().getTextureForFace(f, blockData ? blockData->useAltTiles : false);
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(texture);
// Calculate UV's
@ -185,7 +188,7 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
// U0V0 -> 3 2 <- U1V0
// U0V1 -> 0 1 <- U1V1
float U0, V0, U1, V1;
if (block.drawType() == BlockDrawType::Cactus) {
if (blockState.drawType() == BlockDrawType::Cactus) {
U0 = 0.f;
V0 = 0.f;
U1 = 1.f;
@ -203,14 +206,14 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
// Prepare vertex information for VBO
Vertex vertices[nVertsPerFace];
for (s8f v = 0; v < nVertsPerFace; ++v) {
if (block.drawType() == BlockDrawType::Cactus) {
if (blockState.drawType() == BlockDrawType::Cactus) {
vertices[v].coord3d[0] = x + vertexPos[v]->x - boundingBox.x * normal.x;
vertices[v].coord3d[1] = y + vertexPos[v]->y - boundingBox.y * normal.y;
vertices[v].coord3d[2] = z + vertexPos[v]->z - boundingBox.z * normal.z;
}
else {
float blockHeight = vertexPos[v]->z;
if (block.drawType() == BlockDrawType::Liquid && (f != BlockFace::Bottom || !surroundingBlock || !surroundingBlock->id())) {
if (blockState.drawType() == BlockDrawType::Liquid && (f != BlockFace::Bottom || !surroundingBlockState || !surroundingBlockState->block().id())) {
if (f == BlockFace::Bottom)
blockHeight = vertexPos[v]->z - 2.f / 16.f;
else
@ -228,7 +231,7 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
vertices[v].normal[1] = normal.y;
vertices[v].normal[2] = normal.z;
const gk::Color colorMultiplier = block.colorMultiplier();
const gk::Color colorMultiplier = blockState.colorMultiplier();
vertices[v].color[0] = colorMultiplier.r;
vertices[v].color[1] = colorMultiplier.g;
vertices[v].color[2] = colorMultiplier.b;
@ -239,13 +242,13 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
vertices[v].texCoord[0] = gk::qlerp(blockTexCoords.x, blockTexCoords.x + blockTexCoords.sizeX, U);
vertices[v].texCoord[1] = gk::qlerp(blockTexCoords.y, blockTexCoords.y + blockTexCoords.sizeY, V);
if (Config::isSmoothLightingEnabled && block.drawType() != BlockDrawType::Liquid)
if (Config::isSmoothLightingEnabled && blockState.drawType() != BlockDrawType::Liquid)
vertices[v].lightValue[0] = getLightForVertex(Light::Sun, x, y, z, *neighbourOfs[v], normal, chunk);
else
vertices[v].lightValue[0] = chunk.lightmap().getSunlight(x + normal.x, y + normal.y, z + normal.z);
int torchlight = chunk.lightmap().getTorchlight(x, y, z);
if (Config::isSmoothLightingEnabled && torchlight == 0 && block.drawType() != BlockDrawType::Liquid)
if (Config::isSmoothLightingEnabled && torchlight == 0 && blockState.drawType() != BlockDrawType::Liquid)
vertices[v].lightValue[1] = getLightForVertex(Light::Torch, x, y, z, *neighbourOfs[v], normal, chunk);
else
vertices[v].lightValue[1] = chunk.lightmap().getTorchlight(x + normal.x, y + normal.y, z + normal.z);
@ -257,11 +260,11 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
if (!Config::isAmbientOcclusionEnabled || Config::isSmoothLightingEnabled)
vertices[v].ambientOcclusion = 5;
if (block.drawType() == BlockDrawType::Liquid)
if (blockState.drawType() == BlockDrawType::Liquid)
m_vertices[Layer::Liquid].emplace_back(vertices[v]);
else if (block.drawType() == BlockDrawType::Glass)
else if (blockState.drawType() == BlockDrawType::Glass)
m_vertices[Layer::Glass].emplace_back(vertices[v]);
else if (block.colorMultiplier() != gk::Color::White)
else if (blockState.colorMultiplier() != gk::Color::White)
m_vertices[Layer::NoMipMap].emplace_back(vertices[v]);
else
m_vertices[Layer::Solid].emplace_back(vertices[v]);
@ -286,9 +289,9 @@ inline void ChunkBuilder::addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk
}
}
inline void ChunkBuilder::addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const Block &block, const glm::vec3 *const vertexPos[nCrossFaces][nVertsPerFace]) {
inline void ChunkBuilder::addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const BlockState &blockState, const glm::vec3 *const vertexPos[nCrossFaces][nVertsPerFace]) {
const BlockData *blockData = chunk.getBlockData(x, y, z);
const std::string &texture = block.tiles().getTextureForFace(0, blockData ? blockData->useAltTiles : false);
const std::string &texture = blockState.tiles().getTextureForFace(0, blockData ? blockData->useAltTiles : false);
const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(texture);
float faceTexCoords[nVertsPerFace][nCoordsPerUV] = {
@ -310,7 +313,7 @@ inline void ChunkBuilder::addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk
vertices[v].normal[1] = 0;
vertices[v].normal[2] = 0;
const gk::Color colorMultiplier = block.colorMultiplier();
const gk::Color colorMultiplier = blockState.colorMultiplier();
vertices[v].color[0] = colorMultiplier.r;
vertices[v].color[1] = colorMultiplier.g;
vertices[v].color[2] = colorMultiplier.b;
@ -342,18 +345,18 @@ inline u8 ChunkBuilder::getAmbientOcclusion(s8f x, s8f y, s8f z, const gk::Vecto
(normal.z != 0) ? offset.z : 0
};
const Block *blocks[4] = {
&Registry::getInstance().getBlock(chunk.getBlock(x + minOffset.x, y + minOffset.y, z + offset.z)),
&Registry::getInstance().getBlock(chunk.getBlock(x + offset.x, y + minOffset.y, z + minOffset.z)),
&Registry::getInstance().getBlock(chunk.getBlock(x + minOffset.x, y + offset.y, z + minOffset.z)),
&Registry::getInstance().getBlock(chunk.getBlock(x + offset.x, y + offset.y, z + offset.z))
const BlockState *blocks[4] = {
chunk.getBlockState(x + minOffset.x, y + minOffset.y, z + offset.z),
chunk.getBlockState(x + offset.x, y + minOffset.y, z + minOffset.z),
chunk.getBlockState(x + minOffset.x, y + offset.y, z + minOffset.z),
chunk.getBlockState(x + offset.x, y + offset.y, z + offset.z)
};
bool blockPresence[4] = {
blocks[0]->id() != 0 && blocks[0]->isOpaque(),
blocks[1]->id() != 0 && blocks[1]->isOpaque(),
blocks[2]->id() != 0 && blocks[2]->isOpaque(),
blocks[3]->id() != 0 && blocks[3]->isOpaque()
blocks[0] && blocks[0]->block().id() != 0 && blocks[0]->isOpaque(),
blocks[1] && blocks[1]->block().id() != 0 && blocks[1]->isOpaque(),
blocks[2] && blocks[2]->block().id() != 0 && blocks[2]->isOpaque(),
blocks[3] && blocks[3]->block().id() != 0 && blocks[3]->isOpaque()
};
bool side1 = blockPresence[(minOffset.x != 0) ? 2 : 1];

View File

@ -37,7 +37,7 @@
#include "Vertex.hpp"
class Block;
class BlockState;
class ClientChunk;
class TextureAtlas;
@ -58,11 +58,11 @@ class ChunkBuilder {
};
private:
void addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const Block &block,
void addFace(s8f x, s8f y, s8f z, s8f f, const ClientChunk &chunk, const BlockState &blockState,
const gk::Vector3i &normal, const glm::vec3 *const vertexPos[4],
const gk::Vector3i *const neighbourOfs[4]);
void addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const Block &block,
void addCross(s8f x, s8f y, s8f z, const ClientChunk &chunk, const BlockState &blockState,
const glm::vec3 *const vertexPos[2][4]);
enum class Light {

View File

@ -144,12 +144,11 @@ void ClientPlayer::updatePosition(const ClientWorld &world) {
m_velocity.z = 0.f;
// Checking to block at camera position to enable specific effects
u16 blockID = world.getBlock(m_camera.getDPosition().x, m_camera.getDPosition().y, m_camera.getDPosition().z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (block.fogDepth() != 0) {
const BlockState *blockState = world.getBlockState(m_camera.getDPosition().x, m_camera.getDPosition().y, m_camera.getDPosition().z);
if (blockState && blockState->fogDepth() != 0) {
GameConfig::currentScreenEffect = 1;
GameConfig::fogDepth = block.fogDepth();
GameConfig::fogColor = block.fogColor();
GameConfig::fogDepth = blockState->fogDepth();
GameConfig::fogColor = blockState->fogColor();
}
else {
GameConfig::currentScreenEffect = 0;
@ -186,9 +185,8 @@ void ClientPlayer::checkCollisions(const ClientWorld &world) {
}
bool passable(const ClientWorld &world, double x, double y, double z) {
u32 blockID = world.getBlock(floor(x), floor(y), floor(z));
const Block &block = Registry::getInstance().getBlock(blockID);
return !blockID || block.drawType() == BlockDrawType::Liquid || block.drawType() == BlockDrawType::XShape;
const BlockState *blockState = world.getBlockState(floor(x), floor(y), floor(z));
return !blockState || !blockState->block().id() || blockState->drawType() == BlockDrawType::Liquid || blockState->drawType() == BlockDrawType::XShape;
}
void ClientPlayer::testPoint(const ClientWorld &world, double x, double y, double z, glm::vec3 &vel) {

View File

@ -47,10 +47,10 @@
class Registry : public gk::ISerializable {
public:
template<typename T>
auto registerBlock(const TilesDef &tiles, const std::string &stringID, const std::string &label) -> typename std::enable_if<std::is_base_of<Block, T>::value, T&>::type {
auto registerBlock(const std::string &stringID) -> typename std::enable_if<std::is_base_of<Block, T>::value, T&>::type {
u32 id = m_blocks.size();
m_blocksID.emplace(stringID, id);
m_blocks.emplace_back(std::make_unique<T>(id, tiles, stringID, label));
m_blocks.emplace_back(std::make_unique<T>(id, stringID));
return *static_cast<T*>(m_blocks.back().get());
}

View File

@ -67,13 +67,7 @@ void TilesDef::deserialize(sf::Packet &packet) {
packet >> m_textureFilenames >> m_altTextureFilenames;
}
void TilesDef::loadFromLuaTable(const sol::table &table) {
if (table["tiles"].get_type() == sol::type::table) {
m_textureFilenames = table["tiles"].get<std::vector<std::string>>();
}
else
m_textureFilenames.emplace_back(table["tiles"].get<std::string>());
bool TilesDef::loadFromLuaTable(const sol::table &table) {
if (table["alt_tiles"].get_type() != sol::type::none) {
if (table["alt_tiles"].get_type() == sol::type::table) {
m_altTextureFilenames = table["alt_tiles"].get<std::vector<std::string>>();
@ -81,5 +75,17 @@ void TilesDef::loadFromLuaTable(const sol::table &table) {
else
m_altTextureFilenames.emplace_back(table["alt_tiles"].get<std::string>());
}
if (table["tiles"].get_type() == sol::type::table) {
m_textureFilenames = table["tiles"].get<std::vector<std::string>>();
return true;
}
else if (table["tiles"].get_type() == sol::type::string) {
m_textureFilenames.emplace_back(table["tiles"].get<std::string>());
return true;
}
else {
return false;
}
}

View File

@ -56,7 +56,7 @@ class TilesDef : public gk::ISerializable {
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
void loadFromLuaTable(const sol::table &table);
bool loadFromLuaTable(const sol::table &table);
const std::vector<std::string> &textureFilenames() const { return m_textureFilenames; };
const std::vector<std::string> &altTextureFilenames() const { return m_altTextureFilenames; }

View File

@ -29,50 +29,75 @@
#include "Player.hpp"
#include "World.hpp"
Block::Block(u32 id, const TilesDef &tiles, const std::string &stringID, const std::string &label) {
Block::Block(u32 id, const std::string &stringID) {
m_id = id;
m_tiles = tiles;
m_stringID = stringID;
m_label = label;
m_itemDrop = stringID;
m_itemDropAmount = 1;
BlockState &defaultState = addState();
defaultState.itemDrop(stringID);
defaultState.itemDropAmount(1);
}
const TilesDef &Block::tiles(u16 stateID) const {
if (stateID >= m_states.size()) {
gkError() << ("Failed to get tiles for block '" + m_stringID + "': State").c_str() << stateID << "doesn't exist";
return m_states[0].tiles();
}
return m_states[stateID].tiles();
}
void Block::serialize(sf::Packet &packet) const {
packet << u32(m_id) << m_stringID << m_label << u8(m_drawType)
<< m_hardness << m_harvestRequirements << m_itemDrop << m_itemDropAmount << m_tiles
<< m_boundingBox << m_isOpaque << m_isLightSource << m_canUpdate << m_canBeActivated
<< m_colorMultiplier << m_isRotatable << m_inventoryImage << m_groups
<< m_fogDepth << m_fogColor << m_param;
packet << u32(m_id) << m_stringID << m_canUpdate << m_canBeActivated
<< m_isRotatable << m_groups << m_states << m_param;
}
void Block::deserialize(sf::Packet &packet) {
u32 id;
u8 drawType;
packet >> id >> m_stringID >> m_label >> drawType
>> m_hardness >> m_harvestRequirements >> m_itemDrop >> m_itemDropAmount >> m_tiles
>> m_boundingBox >> m_isOpaque >> m_isLightSource >> m_canUpdate >> m_canBeActivated
>> m_colorMultiplier >> m_isRotatable >> m_inventoryImage >> m_groups
>> m_fogDepth >> m_fogColor >> m_param;
packet >> id >> m_stringID >> m_canUpdate >> m_canBeActivated
>> m_isRotatable >> m_groups >> m_states >> m_param;
m_id = id;
m_drawType = BlockDrawType(drawType);
for (auto &it : m_states) {
it.setBlock(this);
it.setDefaultState(&m_states.at(0));
}
}
BlockState &Block::addState() {
if (!m_states.empty())
m_states.emplace_back(m_states.size(), this, &getState(0));
else
m_states.emplace_back(m_states.size(), this, nullptr);
return m_states.back();
}
BlockState &Block::getState(u16 id) {
if (id >= m_states.size()) {
gkError() << "Failed to get state" << id << "in block" << m_stringID;
return m_states.at(0);
}
return m_states.at(id);
}
const BlockState &Block::getState(u16 id) const {
if (id >= m_states.size()) {
gkError() << "Failed to get state" << id << "in block" << m_stringID;
return m_states.at(0);
}
return m_states.at(id);
}
// Please update 'docs/lua-api-cpp.md' if you change this
void Block::initUsertype(sol::state &lua) {
lua.new_usertype<Block>("Block",
"id", &Block::id,
"data", &Block::data,
"string_id", &Block::stringID,
"label", &Block::label,
"mod_name", &Block::modName,
"is_opaque", &Block::isOpaque,
"get_item_drop", &Block::getItemDrop,
"param", (const BlockParam &(Block::*)() const)&Block::param
"param", (const BlockParam &(Block::*)() const)&Block::param,
"get_state", (const BlockState &(Block::*)(u16) const)&Block::getState
);
}

View File

@ -37,6 +37,7 @@
#include <gk/graphics/Color.hpp>
#include "BlockParam.hpp"
#include "BlockState.hpp"
#include "ItemStack.hpp"
#include "TilesDef.hpp"
@ -44,72 +45,28 @@ class Chunk;
class Player;
class World;
enum class BlockDrawType : u8 {
Solid = 0,
XShape = 1,
Leaves = 2,
Liquid = 3,
Glass = 4,
Cactus = 5,
BoundingBox = 6, // FIXME: Temporary
};
class Block : public gk::ISerializable {
public:
Block() = default;
Block(u32 id, const TilesDef &tiles, const std::string &stringID, const std::string &label);
Block(u32 id, const std::string &stringID);
virtual ~Block() = default;
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
u16 id() const { return m_id & 0xffff; }
u16 data() const { return (m_id >> 16) & 0xffff; }
const TilesDef &tiles() const { return m_tiles; }
u16 id() const { return m_id; }
const std::string &stringID() const { return m_stringID; }
const std::string &label() const { return m_label; }
void setLabel(const std::string &label) { m_label = label; }
std::string modName() const { return m_stringID.substr(0, m_stringID.find_first_of(":")); }
bool isOpaque() const { return m_id != 0 && m_isOpaque && m_drawType != BlockDrawType::XShape; }
void setOpaque(bool isOpaque) { m_isOpaque = isOpaque; }
ItemStack getItemDrop() const { return ItemStack{m_itemDrop, m_itemDropAmount}; };
void setItemDrop(const std::string &itemDrop, u16 itemDropAmount = 1) { m_itemDrop = itemDrop; m_itemDropAmount = itemDropAmount; }
u8 harvestRequirements() const { return m_harvestRequirements; }
void setHarvestRequirements(u8 harvestRequirements) { m_harvestRequirements = harvestRequirements; }
float hardness() const { return m_hardness; }
void setHardness(float hardness) { m_hardness = hardness; }
float timeToBreak(u8 harvestCapability, float miningSpeed) const {
return ((m_harvestRequirements & harvestCapability) == m_harvestRequirements) ? 1.5 * m_hardness / miningSpeed : 5 * m_hardness;
}
const gk::FloatBox &boundingBox() const { return m_boundingBox; }
void setBoundingBox(const gk::FloatBox &boundingBox) { m_boundingBox = boundingBox; }
BlockDrawType drawType() const { return m_drawType; }
void setDrawType(BlockDrawType drawType) { m_drawType = drawType; }
const TilesDef &tiles(u16 state) const;
bool canUpdate() const { return m_canUpdate; }
bool canBeActivated() const { return m_canBeActivated; }
bool isLightSource() const { return m_isLightSource; }
void setLightSource(bool isLightSource) { m_isLightSource = isLightSource; }
const gk::Color &colorMultiplier() const { return m_colorMultiplier; }
void setColorMultiplier(const gk::Color &colorMultiplier) { m_colorMultiplier = colorMultiplier; }
bool isRotatable() const { return m_isRotatable; }
void setRotatable(bool isRotatable) { m_isRotatable = isRotatable; }
const std::string &inventoryImage() const { return m_inventoryImage; }
void setInventoryImage(const std::string &inventoryImage) { m_inventoryImage = inventoryImage; }
void addGroup(const std::string &name, u16 value) { m_groups.emplace(name, value); }
bool hasGroup(const std::string &name) const { return m_groups.find(name) != m_groups.end(); }
@ -121,15 +78,14 @@ class Block : public gk::ISerializable {
return it->second;
}
float fogDepth() const { return m_fogDepth; }
void setFogDepth(float fogDepth) { m_fogDepth = fogDepth; }
const gk::Color &fogColor() const { return m_fogColor; }
void setFogColor(const gk::Color &fogColor) { m_fogColor = fogColor; }
const BlockParam &param() const { return m_param; }
BlockParam &param() { return m_param; }
BlockState &addState();
BlockState &getState(u16 id);
const BlockState &getState(u16 id) const;
const std::vector<BlockState> &states() const { return m_states; }
static void initUsertype(sol::state &lua);
protected:
@ -139,36 +95,14 @@ class Block : public gk::ISerializable {
bool m_canBeActivated = false;
private:
u32 m_id = 0;
TilesDef m_tiles;
u16 m_id = 0;
std::string m_stringID;
std::string m_label;
std::string m_itemDrop;
u16 m_itemDropAmount;
u8 m_harvestRequirements = 0;
float m_hardness = 1.0f;
gk::FloatBox m_boundingBox{0, 0, 0, 1, 1, 1};
BlockDrawType m_drawType = BlockDrawType::Solid;
bool m_isOpaque = true;
bool m_isLightSource = false;
gk::Color m_colorMultiplier = gk::Color::White;
bool m_isRotatable = false;
std::string m_inventoryImage;
std::unordered_map<std::string, u16> m_groups;
float m_fogDepth = 0;
gk::Color m_fogColor = gk::Color::White;
std::vector<BlockState> m_states;
BlockParam m_param{*this};
};

View File

@ -42,7 +42,7 @@ void BlockParam::deserialize(sf::Packet &packet) {
void BlockParam::allocateBits(u8 type, u8 size) {
auto it = m_allocatedBits.find(type);
if (it != m_allocatedBits.end()) {
gkWarning() << "Can't allocate param type" << getTypeName(type) << "twice in block" << m_block.stringID();
gkWarning() << "Can't allocate param type" << getTypeName(type) << "twice in block" << m_block->stringID();
}
else if (m_totalSize + size <= 16) {
m_allocatedBits.emplace(type, Param{m_totalSize, size});
@ -52,7 +52,7 @@ void BlockParam::allocateBits(u8 type, u8 size) {
// gkDebug() << "Allocated" << (int)size << "bits for type" << getTypeName(type) << "in block" << m_block.stringID();
}
else {
gkError() << "Failed to allocate bits for param" << getTypeName(type) << "in block" << m_block.stringID();
gkError() << "Failed to allocate bits for param" << getTypeName(type) << "in block" << m_block->stringID();
gkError() << "Reason: Can't allocate more than 16 bits. Allocated bits:";
for (auto &it : m_allocatedBits) {
gkError() << "\t-" << getTypeName(it.first) << "=" << (int)it.second.size;
@ -63,7 +63,7 @@ void BlockParam::allocateBits(u8 type, u8 size) {
u16 BlockParam::getParam(u8 type, u16 data) const {
auto it = m_allocatedBits.find(type);
if (it == m_allocatedBits.end()) {
gkWarning() << "Failed to get param" << getTypeName(type) << "in block" << m_block.stringID();
gkWarning() << "Failed to get param" << getTypeName(type) << "in block" << m_block->stringID();
return 0;
}
@ -73,14 +73,14 @@ u16 BlockParam::getParam(u8 type, u16 data) const {
u16 BlockParam::setParam(u8 type, u16 data, u16 param) {
auto it = m_allocatedBits.find(type);
if (it == m_allocatedBits.end()) {
gkWarning() << "Failed to set param" << getTypeName(type) << "in block" << m_block.stringID();
gkWarning() << "Failed to set param" << getTypeName(type) << "in block" << m_block->stringID();
return 0;
}
u16 mask = ~(~0u << it->second.size) << it->second.offset;
param <<= it->second.offset;
if ((param & ~mask) != 0)
gkWarning() << "Block param overflow for type" << getTypeName(type) << "in block" << m_block.stringID();
gkWarning() << "Block param overflow for type" << getTypeName(type) << "in block" << m_block->stringID();
return (data & ~mask) | (param & mask);
}
@ -88,6 +88,7 @@ u16 BlockParam::setParam(u8 type, u16 data, u16 param) {
std::string BlockParam::getTypeName(u8 type) {
std::array<std::string, Type::Count> names = {
"Rotation",
"State",
};
return names[type];

View File

@ -41,13 +41,14 @@ class Block;
class BlockParam : public gk::ISerializable {
public:
BlockParam(Block &block) : m_block(block) {}
BlockParam(Block &block) : m_block(&block) {}
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
enum Type {
Rotation,
State,
Count
};
@ -56,13 +57,14 @@ class BlockParam : public gk::ISerializable {
u16 getParam(u8 type, u16 data) const;
u16 setParam(u8 type, u16 data, u16 param);
bool hasParam(u8 type) const { return m_allocatedBits.find(type) != m_allocatedBits.end(); }
static std::string getTypeName(u8 type);
static void initUsertype(sol::state &lua);
private:
Block &m_block;
Block *m_block = nullptr;
u8 m_totalSize = 0;
struct Param : public gk::ISerializable {

View File

@ -0,0 +1,58 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#include "BlockState.hpp"
#include "Registry.hpp"
void BlockState::serialize(sf::Packet &packet) const {
packet << m_id << m_label << m_tiles
<< m_itemDrop << m_itemDropAmount
<< m_hardness << m_harvestRequirements
<< m_boundingBox << u8(m_drawType) << m_colorMultiplier
<< m_isOpaque << m_isLightSource
<< m_inventoryImage << m_fogDepth << m_fogColor << m_attrs;
}
void BlockState::deserialize(sf::Packet &packet) {
u8 drawType;
packet >> m_id >> m_label >> m_tiles
>> m_itemDrop >> m_itemDropAmount
>> m_hardness >> m_harvestRequirements
>> m_boundingBox >> drawType >> m_colorMultiplier
>> m_isOpaque >> m_isLightSource
>> m_inventoryImage >> m_fogDepth >> m_fogColor >> m_attrs;
m_drawType = BlockDrawType(drawType);
}
// Please update 'docs/lua-api-cpp.md' if you change this
void BlockState::initUsertype(sol::state &lua) {
lua.new_usertype<BlockState>("BlockState",
"get_item_drop", &BlockState::getItemDrop
);
}

View File

@ -0,0 +1,157 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef BLOCKSTATE_HPP_
#define BLOCKSTATE_HPP_
#include <string>
#include <unordered_map>
#include <gk/core/Box.hpp>
#include <gk/core/IntTypes.hpp>
#include "BlockParam.hpp"
#include "ItemStack.hpp"
#include "TilesDef.hpp"
enum class BlockDrawType : u8 {
Solid = 0,
XShape = 1,
Leaves = 2,
Liquid = 3,
Glass = 4,
Cactus = 5,
BoundingBox = 6, // FIXME: Temporary
};
#define BLOCK_ATTR_GETTER(attrName) \
auto attrName() const -> const std::remove_reference<decltype(m_##attrName)>::type & { \
if (!m_defaultState || m_attrs & BlockAttribute::attr_##attrName) \
return m_##attrName; \
else \
return m_defaultState->m_##attrName; \
}
#define BLOCK_ATTR_SETTER(attrName) \
void attrName(const std::remove_reference<decltype(m_##attrName)>::type &value) { \
m_##attrName = value; \
m_attrs |= BlockAttribute::attr_##attrName; \
}
#define BLOCK_ATTR(type, name) \
private: \
type m_##name; \
public: \
BLOCK_ATTR_GETTER(name) \
BLOCK_ATTR_SETTER(name)
#define BLOCK_ATTR_V(type, name, defaultValue) \
private: \
type m_##name = defaultValue; \
public: \
BLOCK_ATTR_GETTER(name) \
BLOCK_ATTR_SETTER(name)
class BlockState : public gk::ISerializable {
public:
BlockState() = default;
BlockState(u16 id, const Block *block, const BlockState *defaultState = nullptr)
: m_block(block), m_defaultState(defaultState), m_id(id) {}
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
const Block &block() const { return *m_block; }
u16 id() const { return m_id; }
float timeToBreak(u8 harvestCapability, float miningSpeed) const {
return ((m_harvestRequirements & harvestCapability) == m_harvestRequirements) ? 1.5 * m_hardness / miningSpeed : 5 * m_hardness;
}
ItemStack getItemDrop() const { return {m_itemDrop, m_itemDropAmount}; }
void setBlock(const Block *block) { m_block = block; }
void setDefaultState(const BlockState *defaultState) { m_defaultState = defaultState; }
static void initUsertype(sol::state &lua);
private:
const Block *m_block = nullptr;
const BlockState *m_defaultState = nullptr;
u16 m_id = 0;
enum BlockAttribute : u32 {
attr_tiles,
attr_label,
attr_itemDrop,
attr_itemDropAmount,
attr_harvestRequirements,
attr_hardness,
attr_boundingBox,
attr_drawType,
attr_colorMultiplier,
attr_isOpaque,
attr_isLightSource,
attr_inventoryImage,
attr_fogDepth,
attr_fogColor,
attr_param,
};
BLOCK_ATTR(std::string, label);
BLOCK_ATTR(TilesDef, tiles);
BLOCK_ATTR(std::string, itemDrop);
BLOCK_ATTR_V(u16, itemDropAmount, 1);
BLOCK_ATTR_V(u8, harvestRequirements, 0);
BLOCK_ATTR_V(float, hardness, 1.0f);
BLOCK_ATTR_V(gk::FloatBox, boundingBox, (gk::FloatBox{0, 0, 0, 1, 1, 1}));
BLOCK_ATTR_V(BlockDrawType, drawType, BlockDrawType::Solid);
BLOCK_ATTR_V(gk::Color, colorMultiplier, gk::Color::White);
BLOCK_ATTR_V(bool, isOpaque, true);
BLOCK_ATTR_V(bool, isLightSource, false);
BLOCK_ATTR(std::string, inventoryImage);
BLOCK_ATTR_V(float, fogDepth, 0);
BLOCK_ATTR_V(gk::Color, fogColor, gk::Color::White);
u32 m_attrs = 0;
};
#undef BLOCK_ATTR
#undef BLOCK_ATTR_GETTER
#undef BLOCK_ATTR_SETTER
#endif // BLOCKSTATE_HPP_

View File

@ -71,8 +71,12 @@ void Chunk::setBlock(int x, int y, int z, u16 type) {
if ((m_data[z][y][x] & 0xffff) == type) return;
u16 blockParam = getData(x, y, z);
const Block &block = Registry::getInstance().getBlock(type);
if (block.isLightSource())
const BlockState &blockState = block.getState(block.param().hasParam(BlockParam::State)
? block.param().getParam(BlockParam::State, blockParam) : 0);
if (blockState.isLightSource())
m_lightmap.addTorchlight(x, y, z, 14);
else {
m_lightmap.removeTorchlight(x, y, z);
@ -82,8 +86,8 @@ void Chunk::setBlock(int x, int y, int z, u16 type) {
onBlockPlaced(x, y, z, block);
m_world.onBlockPlaced(x + m_x * width, y + m_y * depth, z + m_z * height, block);
if (m_data[z][y][x] != 0) {
const Block &oldBlock = Registry::getInstance().getBlock(m_data[z][y][x]);
if ((m_data[z][y][x] & 0xffff) != 0) {
const Block &oldBlock = Registry::getInstance().getBlock(m_data[z][y][x] & 0xffff);
onBlockDestroyed(x, y, z, oldBlock);
}
@ -133,6 +137,23 @@ void Chunk::setBlockRaw(int x, int y, int z, u16 type) {
m_hasChanged = true;
}
const BlockState *Chunk::getBlockState(int x, int y, int z) const {
if(x < 0) return m_surroundingChunks[0] ? m_surroundingChunks[0]->getBlockState(x + Chunk::width, y, z) : nullptr;
if(x >= Chunk::width) return m_surroundingChunks[1] ? m_surroundingChunks[1]->getBlockState(x - Chunk::width, y, z) : nullptr;
if(y < 0) return m_surroundingChunks[2] ? m_surroundingChunks[2]->getBlockState(x, y + Chunk::depth, z) : nullptr;
if(y >= Chunk::depth) return m_surroundingChunks[3] ? m_surroundingChunks[3]->getBlockState(x, y - Chunk::depth, z) : nullptr;
if(z < 0) return m_surroundingChunks[4] ? m_surroundingChunks[4]->getBlockState(x, y, z + Chunk::height) : nullptr;
if(z >= Chunk::height) return m_surroundingChunks[5] ? m_surroundingChunks[5]->getBlockState(x, y, z - Chunk::height) : nullptr;
u16 blockID = getBlock(x, y, z);
u16 blockParam = getData(x, y, z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (!block.param().hasParam(BlockParam::State))
return &block.getState(0);
return &block.getState(block.param().getParam(BlockParam::State, blockParam));
}
BlockData *Chunk::getBlockData(int x, int y, int z) const {
if(x < 0) return m_surroundingChunks[0] ? m_surroundingChunks[0]->getBlockData(x + CHUNK_WIDTH, y, z) : 0;
if(x >= CHUNK_WIDTH) return m_surroundingChunks[1] ? m_surroundingChunks[1]->getBlockData(x - CHUNK_WIDTH, y, z) : 0;

View File

@ -64,6 +64,8 @@ class Chunk : public gk::NonCopyable {
void setBlockRaw(int x, int y, int z, u16 block);
const BlockState *getBlockState(int x, int y, int z) const;
virtual void onBlockPlaced(int, int, int, const Block &) {}
virtual void onBlockDestroyed(int, int, int, const Block &) {}

View File

@ -129,9 +129,8 @@ bool ChunkLightmap::updateTorchlight() {
m_torchlightBfsQueue.pop();
// If this block is opaque, don't propagate the light
u16 blockID = m_chunk->getBlock(node.x, node.y, node.z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (block.isOpaque() && !block.isLightSource()) {
const BlockState *blockState = m_chunk->getBlockState(node.x, node.y, node.z);
if (blockState && blockState->isOpaque() && !blockState->isLightSource()) {
setTorchlight(node.x, node.y, node.z, 0);
// FIXME: This only reverts an addTorchlight that added light in a non-generated chunk
@ -153,9 +152,8 @@ bool ChunkLightmap::updateTorchlight() {
u8 lightLevel = getTorchlight(node.x, node.y, node.z);
for (const gk::Vector3i &surroundingNode : surroundingNodes) {
if (getTorchlight(surroundingNode.x, surroundingNode.y, surroundingNode.z) + 2 <= lightLevel) {
u16 blockID = m_chunk->getBlock(surroundingNode.x, surroundingNode.y, surroundingNode.z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (!block.isOpaque()) {
const BlockState *blockState = m_chunk->getBlockState(surroundingNode.x, surroundingNode.y, surroundingNode.z);
if (blockState && blockState->isOpaque()) {
addTorchlight(surroundingNode.x, surroundingNode.y, surroundingNode.z, lightLevel - 1);
lightUpdated = true;
@ -205,9 +203,8 @@ bool ChunkLightmap::updateSunlight() {
m_sunlightBfsQueue.pop();
// If this block is opaque, don't propagate the light
u16 blockID = m_chunk->getBlock(node.x, node.y, node.z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (block.isOpaque()) {
const BlockState *blockState = m_chunk->getBlockState(node.x, node.y, node.z);
if (blockState && blockState->isOpaque()) {
setSunlight(node.x, node.y, node.z, 0);
// FIXME: This only reverts an addSunlight that added light in a non-generated chunk
@ -231,10 +228,9 @@ bool ChunkLightmap::updateSunlight() {
u8 neighbourSunlightLevel = getSunlight(surroundingNode.x, surroundingNode.y, surroundingNode.z);
if (neighbourSunlightLevel + 2 <= sunlightLevel
|| (sunlightLevel == 15 && neighbourSunlightLevel != 15 && surroundingNode.z == node.z - 1)) {
u16 blockID = m_chunk->getBlock(surroundingNode.x, surroundingNode.y, surroundingNode.z);
const Block &block = Registry::getInstance().getBlock(blockID);
if (!block.isOpaque()) {
if (sunlightLevel == 15 && surroundingNode.z == node.z - 1 && (!blockID || block.drawType() == BlockDrawType::Glass || block.drawType() == BlockDrawType::XShape)) {
const BlockState *blockState = m_chunk->getBlockState(surroundingNode.x, surroundingNode.y, surroundingNode.z);
if (blockState && blockState->isOpaque()) {
if (sunlightLevel == 15 && surroundingNode.z == node.z - 1 && (!blockState->block().id() || blockState->drawType() == BlockDrawType::Glass || blockState->drawType() == BlockDrawType::XShape)) {
addSunlight(surroundingNode.x, surroundingNode.y, surroundingNode.z, sunlightLevel);
lightUpdated = true;

View File

@ -58,6 +58,14 @@ BlockData *World::addBlockData(int x, int y, int z, int inventoryWidth, int inve
return nullptr;
}
const BlockState *World::getBlockState(int x, int y, int z) const {
Chunk *chunk = getChunkAtBlockPos(x, y, z);
if (chunk)
return chunk->getBlockState(x & (CHUNK_WIDTH - 1), y & (CHUNK_DEPTH - 1), z & (CHUNK_HEIGHT - 1));
return nullptr;
}
u16 World::getBlock(int x, int y, int z) const {
Chunk *chunk = getChunkAtBlockPos(x, y, z);
if (chunk)

View File

@ -41,6 +41,8 @@ class World {
BlockData *getBlockData(int x, int y, int z) const;
BlockData *addBlockData(int x, int y, int z, int inventoryWidth = 0, int inventoryHeight = 0) const;
const BlockState *getBlockState(int x, int y, int z) const;
u16 getBlock(int x, int y, int z) const;
void setBlock(int x, int y, int z, u16 id) const;
u16 getData(int x, int y, int z) const;

View File

@ -117,7 +117,7 @@ bool ServerApplication::init() {
Registry::setInstance(m_registry);
// The id "_:air" is used in CraftingRecipe, update it there if it changes
m_registry.registerBlock<ServerBlock>({}, "_:air", "Air");
m_registry.registerBlock<ServerBlock>("_:air").getState(0).label("Air");
m_registry.registerItem<Item>({}, "_:air", "Air").setIsBlock(true);
m_modLoader.loadMods();

View File

@ -32,54 +32,61 @@
#include "ServerBlock.hpp"
void LuaBlockLoader::loadBlock(const sol::table &table) const {
std::string stringID = m_mod.id() + ":" + table["id"].get<std::string>();
ServerBlock &block = Registry::getInstance().registerBlock<ServerBlock>(stringID);
block.setRotatable(table["is_rotatable"].get_or(false));
BlockState &defaultState = block.getState(0);
loadBlockState(defaultState, table, block);
}
void LuaBlockLoader::loadBlockState(BlockState &state, const sol::table &table, ServerBlock &block) const {
TilesDef tiles;
tiles.loadFromLuaTable(table);
state.tiles(tiles);
std::string stringID = m_mod.id() + ":" + table["id"].get<std::string>();
std::string label = table["name"].get<std::string>();
ServerBlock &block = Registry::getInstance().registerBlock<ServerBlock>(tiles, stringID, label);
loadProperties(block, table);
loadBoundingBox(block, table);
loadDrawType(block, table);
loadItemDrop(block, table);
loadColorMultiplier(block, table);
loadProperties(state, table);
loadBoundingBox(state, table);
loadDrawType(state, table, block);
loadItemDrop(state, table);
loadColorMultiplier(state, table);
Item *item = nullptr;
if (!block.inventoryImage().empty()) {
item = &Registry::getInstance().registerItem<Item>(TilesDef{block.inventoryImage()}, stringID, label);
if (!state.inventoryImage().empty()) {
item = &Registry::getInstance().registerItem<Item>(TilesDef{state.inventoryImage()}, block.stringID(), state.label());
}
else {
item = &Registry::getInstance().registerItem<Item>(block.tiles(), stringID, label);
item = &Registry::getInstance().registerItem<Item>(state.tiles(), block.stringID(), state.label());
}
item->setIsBlock(true);
loadGroups(block, *item, table);
loadGroups(block, table, item);
loadParams(block);
}
inline void LuaBlockLoader::loadProperties(ServerBlock &block, const sol::table &table) const {
block.setHarvestRequirements(table["harvest_requirements"].get_or(0));
block.setHardness(table["hardness"].get_or(1.0f));
block.setOpaque(table["is_opaque"].get_or(true));
block.setLightSource(table["is_light_source"].get_or(false));
block.setOnBlockActivated(table["on_block_activated"]);
block.setOnTick(table["on_tick"]);
block.setOnBlockPlaced(table["on_block_placed"]);
block.setOnBlockDestroyed(table["on_block_destroyed"]);
block.setRotatable(table["is_rotatable"].get_or(false));
block.setInventoryImage(table["inventory_image"].get_or<std::string>(""));
block.setFogDepth(table["fog_depth"].get_or<float>(0));
block.setTickRandomly(table["tick_randomly"].get_or(false));
block.setTickProbability(table["tick_probability"].get_or(0.f));
inline void LuaBlockLoader::loadProperties(BlockState &state, const sol::table &table) const {
state.label(table["name"].get<std::string>());
state.harvestRequirements(table["harvest_requirements"].get_or(0));
state.hardness(table["hardness"].get_or(1.0f));
state.isOpaque(table["is_opaque"].get_or(true));
state.isLightSource(table["is_light_source"].get_or(false));
state.inventoryImage(table["inventory_image"].get_or<std::string>(""));
state.fogDepth(table["fog_depth"].get_or<float>(0));
if (block.fogDepth()) {
// state.onBlockActivated(table["on_block_activated"]);
// state.onTick(table["on_tick"]);
// state.onBlockPlaced(table["on_block_placed"]);
// state.onBlockDestroyed(table["on_block_destroyed"]);
// state.setTickRandomly(table["tick_randomly"].get_or(false));
// state.setTickProbability(table["tick_probability"].get_or(0.f));
if (state.fogDepth()) {
sol::optional<sol::table> fogColor = table["fog_color"];
if (fogColor != sol::nullopt) {
block.setFogColor(gk::Color{
state.fogColor(gk::Color{
fogColor.value().get<u8>(1),
fogColor.value().get<u8>(2),
fogColor.value().get<u8>(3),
@ -88,10 +95,10 @@ inline void LuaBlockLoader::loadProperties(ServerBlock &block, const sol::table
}
}
inline void LuaBlockLoader::loadBoundingBox(ServerBlock &block, const sol::table &table) const {
inline void LuaBlockLoader::loadBoundingBox(BlockState &state, const sol::table &table) const {
sol::optional<sol::table> boundingBox = table["bounding_box"];
if (boundingBox != sol::nullopt) {
block.setBoundingBox(gk::FloatBox{
state.boundingBox(gk::FloatBox{
boundingBox.value().get<float>(1),
boundingBox.value().get<float>(2),
boundingBox.value().get<float>(3),
@ -102,7 +109,7 @@ inline void LuaBlockLoader::loadBoundingBox(ServerBlock &block, const sol::table
}
}
inline void LuaBlockLoader::loadDrawType(ServerBlock &block, const sol::table &table) const {
inline void LuaBlockLoader::loadDrawType(BlockState &state, const sol::table &table, const ServerBlock &block) const {
sol::object drawTypeObject = table["draw_type"];
if (drawTypeObject.valid()) {
if (drawTypeObject.get_type() == sol::type::string) {
@ -117,8 +124,9 @@ inline void LuaBlockLoader::loadDrawType(ServerBlock &block, const sol::table &t
};
auto it = drawTypes.find(drawTypeObject.as<std::string>());
if (it != drawTypes.end())
block.setDrawType(it->second);
if (it != drawTypes.end()) {
state.drawType(it->second);
}
else
gkError() << "In" << block.stringID() << " definition: Block draw type invalid";
}
@ -127,19 +135,18 @@ inline void LuaBlockLoader::loadDrawType(ServerBlock &block, const sol::table &t
}
}
inline void LuaBlockLoader::loadItemDrop(ServerBlock &block, const sol::table &table) const {
inline void LuaBlockLoader::loadItemDrop(BlockState &state, const sol::table &table) const {
sol::optional<sol::table> itemDrop = table["item_drop"];
if (itemDrop != sol::nullopt) {
std::string dropID = itemDrop.value()["id"];
u16 dropAmount = itemDrop.value()["amount"];
block.setItemDrop(dropID, dropAmount);
state.itemDrop(itemDrop.value()["id"]);
state.itemDropAmount(itemDrop.value()["amount"]);
}
}
inline void LuaBlockLoader::loadColorMultiplier(ServerBlock &block, const sol::table &table) const {
inline void LuaBlockLoader::loadColorMultiplier(BlockState &state, const sol::table &table) const {
sol::optional<sol::table> colorMultiplier = table["color_multiplier"];
if (colorMultiplier != sol::nullopt) {
block.setColorMultiplier(gk::Color{
state.colorMultiplier(gk::Color{
colorMultiplier.value().get<u8>(1),
colorMultiplier.value().get<u8>(2),
colorMultiplier.value().get<u8>(3),
@ -148,7 +155,7 @@ inline void LuaBlockLoader::loadColorMultiplier(ServerBlock &block, const sol::t
}
}
inline void LuaBlockLoader::loadGroups(ServerBlock &block, Item &item, const sol::table &table) const {
inline void LuaBlockLoader::loadGroups(ServerBlock &block, const sol::table &table, Item *item) const {
sol::object groupsObject = table["groups"];
if (groupsObject.valid()) {
if (groupsObject.get_type() == sol::type::table) {
@ -158,7 +165,8 @@ inline void LuaBlockLoader::loadGroups(ServerBlock &block, Item &item, const sol
u16 groupValue = groupObject.second.as<u16>();
block.addGroup(groupName, groupValue);
item.addGroup(groupName, groupValue);
if (item)
item->addGroup(groupName, groupValue);
}
}
else

View File

@ -31,6 +31,7 @@
class Item;
class LuaMod;
class BlockState;
class ServerBlock;
class LuaBlockLoader {
@ -40,12 +41,13 @@ class LuaBlockLoader {
void loadBlock(const sol::table &table) const;
private:
void loadProperties(ServerBlock &block, const sol::table &table) const;
void loadBoundingBox(ServerBlock &block, const sol::table &table) const;
void loadDrawType(ServerBlock &block, const sol::table &table) const;
void loadItemDrop(ServerBlock &block, const sol::table &table) const;
void loadColorMultiplier(ServerBlock &block, const sol::table &table) const;
void loadGroups(ServerBlock &block, Item &item, const sol::table &table) const;
void loadBlockState(BlockState &state, const sol::table &table, ServerBlock &block) const;
void loadProperties(BlockState &state, const sol::table &table) const;
void loadBoundingBox(BlockState &state, const sol::table &table) const;
void loadDrawType(BlockState &state, const sol::table &table, const ServerBlock &block) const;
void loadItemDrop(BlockState &state, const sol::table &table) const;
void loadColorMultiplier(BlockState &state, const sol::table &table) const;
void loadGroups(ServerBlock &block, const sol::table &table, Item *item = nullptr) const;
void loadParams(ServerBlock &block) const;
LuaMod &m_mod;

View File

@ -272,9 +272,11 @@ void ServerCommandHandler::setupCallbacks() {
ServerWorld &world = getWorldForClient(client.id);
world.setData(x, y, z, block >> 16);
world.setBlock(x, y, z, block & 0xffff);
const Block &blockDef = Registry::getInstance().getBlock(block & 0xffff);
m_scriptEngine.luaCore().onEvent(LuaEventType::BlockPlaced, glm::ivec3{x, y, z}, blockDef, *player, world, client, *this);
const BlockState *blockState = world.getBlockState(x, y, z);
if (!blockState) return;
m_scriptEngine.luaCore().onEvent(LuaEventType::BlockPlaced, glm::ivec3{x, y, z}, blockState, *player, world, client, *this);
Network::Packet answer;
answer << Network::Command::BlockUpdate << x << y << z << block;
@ -291,10 +293,11 @@ void ServerCommandHandler::setupCallbacks() {
packet >> x >> y >> z;
ServerWorld &world = getWorldForClient(client.id);
const Block &blockDef = Registry::getInstance().getBlock(world.getBlock(x, y, z));
const BlockState *blockState = world.getBlockState(x, y, z);
if (!blockState) return;
world.setBlock(x, y, z, 0);
m_scriptEngine.luaCore().onEvent(LuaEventType::BlockDigged, glm::ivec3{x, y, z}, blockDef, *player, world, client, *this);
m_scriptEngine.luaCore().onEvent(LuaEventType::BlockDigged, glm::ivec3{x, y, z}, blockState, *player, world, client, *this);
Network::Packet answer;
answer << Network::Command::BlockUpdate << x << y << z << u32(0);

View File

@ -35,7 +35,7 @@
void ServerBlock::onTick(const glm::ivec3 &pos, ServerChunk &chunk, ServerWorld &world, ServerCommandHandler &server) const {
if (m_onTickEnabled && m_onTick) {
try {
m_onTick(pos, chunk, world);
m_onTick(pos, *this, chunk, world);
BlockData *blockData = world.getBlockData(pos.x, pos.y, pos.z);
if (blockData) {
@ -59,7 +59,7 @@ void ServerBlock::onTick(const glm::ivec3 &pos, ServerChunk &chunk, ServerWorld
bool ServerBlock::onBlockActivated(const glm::ivec3 &pos, ServerPlayer &player, ServerWorld &world, ClientInfo &client, ServerCommandHandler &server, u16 screenWidth, u16 screenHeight, u8 guiScale) const {
try {
if (m_onBlockActivated) {
m_onBlockActivated(pos, player, world, client, server, screenWidth, screenHeight, guiScale);
m_onBlockActivated(pos, *this, player, world, client, server, screenWidth, screenHeight, guiScale);
// FIXME: Check if data changed before sending
BlockData *blockData = world.getBlockData(pos.x, pos.y, pos.z);

View File

@ -37,8 +37,8 @@ class ServerWorld;
class ServerBlock : public Block {
public:
ServerBlock(u32 id, const TilesDef &tiles, const std::string &name, const std::string &label)
: Block(id, tiles, name, label) {}
ServerBlock(u32 id, const std::string &name)
: Block(id, name) {}
void onTick(const glm::ivec3 &pos, ServerChunk &chunk, ServerWorld &world, ServerCommandHandler &server) const;
bool onBlockActivated(const glm::ivec3 &pos, ServerPlayer &player, ServerWorld &world, ClientInfo &client, ServerCommandHandler &server, u16 screenWidth, u16 screenHeight, u8 guiScale) const;