diff --git a/docs/lua-api-block.md b/docs/lua-api-block.md index 64b9fec1a..623002cea 100644 --- a/docs/lua-api-block.md +++ b/docs/lua-api-block.md @@ -233,6 +233,13 @@ Parameters: - `screen_height` (`u16`): height of the screen - `gui_scale` (`u8`): current scaling setting +### `on_block_destroyed` + +Parameters: + +- `pos` (`ivec3`): position of the block +- `world` (`World`): instance of `ServerWorld` + ### `on_block_placed` Parameters: diff --git a/docs/lua-api-cpp.md b/docs/lua-api-cpp.md index d0ae3f489..5c4fab687 100644 --- a/docs/lua-api-cpp.md +++ b/docs/lua-api-cpp.md @@ -23,6 +23,8 @@ - `void set_string(string attribute, string value)` - `int get_int(string attribute)` - `void set_int(string attribute, int value)` +- `bool get_bool(string attribute)` +- `void set_bool(string attribute, bool value)` ## Chunk diff --git a/mods/default/blocks.lua b/mods/default/blocks.lua index 7f58292b7..4e31cb573 100644 --- a/mods/default/blocks.lua +++ b/mods/default/blocks.lua @@ -132,9 +132,6 @@ mod:block { is_light_source = true } -dofile("mods/default/workbench.lua") -dofile("mods/default/furnace.lua") - mod:block { id = "iron_ore", name = "Iron Ore", @@ -315,3 +312,7 @@ mod:block { end } +dofile("mods/default/blocks/workbench.lua") +dofile("mods/default/blocks/furnace.lua") +dofile("mods/default/blocks/door.lua") + diff --git a/mods/default/blocks/door.lua b/mods/default/blocks/door.lua new file mode 100644 index 000000000..975674d42 --- /dev/null +++ b/mods/default/blocks/door.lua @@ -0,0 +1,112 @@ +-- +-- ===================================================================================== +-- +-- OpenMiner +-- +-- Copyright (C) 2018-2020 Unarelith, Quentin Bazin +-- 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 +-- +-- ===================================================================================== +-- + +function open_door(pos, world) + local data = world:get_block_data(pos.x, pos.y, pos.z) + if not data then return end + + -- FIXME: Add a helper to get rotation from data + local rotation = world:get_data(pos.x, pos.y, pos.z) + + local is_opened = data.meta:get_bool("is_opened") or false + if not is_opened then + world:set_data(pos.x, pos.y, pos.z, (rotation - 1) % 4) + else + world:set_data(pos.x, pos.y, pos.z, (rotation + 1) % 4) + end + + data.meta:set_bool("is_opened", not is_opened) +end + +mod:block { + id = "door_wood_upper", + name = "Wooden Door", + tiles = "door_wood_upper.png", + inventory_image = "door_wood.png", + groups = {ci_ignore = 1}, + + is_rotatable = true, + is_opaque = false, + + draw_type = "boundingbox", + bounding_box = {13 / 16, 0, 0, 3 / 16, 1, 1}, + + on_block_placed = function(pos, world) + world:add_block_data(pos.x, pos.y, pos.z, 0, 0) + end, + + on_block_destroyed = function(pos, world) + local lower_block_id = openminer:registry():get_block_from_string("default:door_wood_lower"):id() + if world:get_block(pos.x, pos.y, pos.z - 1) == lower_block_id then + world:set_block(pos.x, pos.y, pos.z - 1, 0) + end + end, + + on_block_activated = function(pos, 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 +} + +mod:block { + id = "door_wood_lower", + name = "Wooden Door", + tiles = "door_wood_lower.png", + inventory_image = "door_wood.png", + + is_rotatable = true, + is_opaque = false, + + draw_type = "boundingbox", + bounding_box = {13 / 16, 0, 0, 3 / 16, 1, 1}, + + on_block_placed = function(pos, world) + world:add_block_data(pos.x, pos.y, pos.z, 0, 0) + + if world:get_block(pos.x, pos.y, pos.z + 1) == 0 then + local data = world:get_data(pos.x, pos.y, pos.z) + world:set_data(pos.x, pos.y, pos.z + 1, data) + + local upper_block_id = openminer:registry():get_block_from_string("default:door_wood_upper"):id() + world:set_block(pos.x, pos.y, pos.z + 1, upper_block_id) + end + end, + + on_block_destroyed = function(pos, world) + local upper_block_id = openminer:registry():get_block_from_string("default:door_wood_upper"):id() + if world:get_block(pos.x, pos.y, pos.z + 1) == upper_block_id then + world:set_block(pos.x, pos.y, pos.z + 1, 0) + end + end, + + on_block_activated = function(pos, 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 +} + + diff --git a/mods/default/furnace.lua b/mods/default/blocks/furnace.lua similarity index 100% rename from mods/default/furnace.lua rename to mods/default/blocks/furnace.lua diff --git a/mods/default/workbench.lua b/mods/default/blocks/workbench.lua similarity index 100% rename from mods/default/workbench.lua rename to mods/default/blocks/workbench.lua diff --git a/mods/default/textures/blocks/door_wood_lower.png b/mods/default/textures/blocks/door_wood_lower.png new file mode 100644 index 000000000..d77655361 Binary files /dev/null and b/mods/default/textures/blocks/door_wood_lower.png differ diff --git a/mods/default/textures/blocks/door_wood_upper.png b/mods/default/textures/blocks/door_wood_upper.png new file mode 100644 index 000000000..9f62b1abc Binary files /dev/null and b/mods/default/textures/blocks/door_wood_upper.png differ diff --git a/mods/default/textures/items/door_wood.png b/mods/default/textures/items/door_wood.png new file mode 100644 index 000000000..b929d7c50 Binary files /dev/null and b/mods/default/textures/items/door_wood.png differ diff --git a/source/client/gui/ItemWidget.cpp b/source/client/gui/ItemWidget.cpp index 0a8905576..fbd13d50b 100644 --- a/source/client/gui/ItemWidget.cpp +++ b/source/client/gui/ItemWidget.cpp @@ -42,7 +42,7 @@ 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) { + if (block.drawType() != BlockDrawType::XShape && block.inventoryImage().empty()) { m_cube.updateVertexBuffer(block); m_isImage = false; } diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp index 3b7f1c083..53ff24901 100644 --- a/source/client/hud/BlockCursor.cpp +++ b/source/client/hud/BlockCursor.cpp @@ -91,8 +91,6 @@ void BlockCursor::onEvent(const SDL_Event &event, const Hotbar &hotbar) { gk::FloatBox boundingBox = newBlock.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)) { - m_world.setBlock(x, y, z, hotbar.currentItem()); - u32 block = hotbar.currentItem(); if (newBlock.isRotatable()) { u16 data = m_player.getOppositeDirection() & 0x3; @@ -101,6 +99,8 @@ void BlockCursor::onEvent(const SDL_Event &event, const Hotbar &hotbar) { block |= data << 16; } + m_world.setBlock(x, y, z, hotbar.currentItem()); + m_client.sendPlayerPlaceBlock(x, y, z, block); const ItemStack ¤tStack = m_player.inventory().getStack(hotbar.cursorPos(), 0); diff --git a/source/common/world/BlockMetadata.cpp b/source/common/world/BlockMetadata.cpp index 4b6a000c8..7a5ad0d47 100644 --- a/source/common/world/BlockMetadata.cpp +++ b/source/common/world/BlockMetadata.cpp @@ -36,6 +36,10 @@ void BlockMetadata::setInt(const std::string &name, int value) { m_data[name].set(value, BlockMetadataValue::Type::Int); } +void BlockMetadata::setBool(const std::string &name, bool value) { + m_data[name].set(value, BlockMetadataValue::Type::Bool); +} + void BlockMetadata::serialize(sf::Packet &packet) const { packet << u32(m_data.size()); for (auto &it : m_data) { @@ -46,6 +50,9 @@ void BlockMetadata::serialize(sf::Packet &packet) const { else if (it.second.type() == BlockMetadataValue::Type::Int) { packet << it.second.get(); } + else if (it.second.type() == BlockMetadataValue::Type::Bool) { + packet << it.second.get(); + } } } @@ -68,6 +75,11 @@ void BlockMetadata::deserialize(sf::Packet &packet) { packet >> value; m_data[name].set(value, type); } + else if (type == BlockMetadataValue::Type::Bool) { + bool value; + packet >> value; + m_data[name].set(value, type); + } } } diff --git a/source/common/world/BlockMetadata.hpp b/source/common/world/BlockMetadata.hpp index 52c3150a8..6e498abcc 100644 --- a/source/common/world/BlockMetadata.hpp +++ b/source/common/world/BlockMetadata.hpp @@ -43,7 +43,8 @@ class BlockMetadataValue { enum class Type : u8 { Undefined, String, - Int + Int, + Bool }; template @@ -68,6 +69,7 @@ class BlockMetadata : public ISerializable { public: void setString(const std::string &name, const std::string &value); void setInt(const std::string &name, int value); + void setBool(const std::string &name, bool value); template T &get(const std::string &name) { diff --git a/source/common/world/Chunk.cpp b/source/common/world/Chunk.cpp index 059c1b77c..ddd74ef0c 100644 --- a/source/common/world/Chunk.cpp +++ b/source/common/world/Chunk.cpp @@ -68,7 +68,7 @@ void Chunk::setBlock(int x, int y, int z, u16 type) { if(z < 0) { if(m_surroundingChunks[4]) m_surroundingChunks[4]->setBlock(x, y, z + Chunk::height, type); return; } if(z >= Chunk::height) { if(m_surroundingChunks[5]) m_surroundingChunks[5]->setBlock(x, y, z - Chunk::height, type); return; } - if (m_data[z][y][x] == type) return; + if ((m_data[z][y][x] & 0xffff) == type) return; const Block &block = Registry::getInstance().getBlock(type); if (block.canUpdate()) { @@ -89,6 +89,11 @@ void Chunk::setBlock(int x, int y, int z, u16 type) { onBlockPlaced(x, y, z, block); + if (m_data[z][y][x] != 0) { + const Block &oldBlock = Registry::getInstance().getBlock(m_data[z][y][x]); + onBlockDestroyed(x, y, z, oldBlock); + } + setBlockRaw(x, y, z, type); if(x == 0 && m_surroundingChunks[West]) { m_surroundingChunks[West]->m_hasChanged = true; } @@ -121,7 +126,7 @@ void Chunk::setBlockRaw(int x, int y, int z, u16 type) { if(z < 0) { if(m_surroundingChunks[4]) m_surroundingChunks[4]->setBlockRaw(x, y, z + Chunk::height, type); return; } if(z >= Chunk::height) { if(m_surroundingChunks[5]) m_surroundingChunks[5]->setBlockRaw(x, y, z - Chunk::height, type); return; } - if (m_data[z][y][x] == type) return; + if ((m_data[z][y][x] & 0xffff) == type) return; if (type == 0) { auto it = m_blockData.find(gk::Vector3i{x, y, z}); @@ -129,7 +134,8 @@ void Chunk::setBlockRaw(int x, int y, int z, u16 type) { m_blockData.erase(it); } - m_data[z][y][x] = type; + m_data[z][y][x] &= 0xffff0000; + m_data[z][y][x] |= type; m_hasChanged = true; } diff --git a/source/common/world/Chunk.hpp b/source/common/world/Chunk.hpp index 07750f8c1..3885d8720 100644 --- a/source/common/world/Chunk.hpp +++ b/source/common/world/Chunk.hpp @@ -66,6 +66,7 @@ class Chunk : public gk::NonCopyable { void setBlockRaw(int x, int y, int z, u16 block); virtual void onBlockPlaced(int, int, int, const Block &) const {} + virtual void onBlockDestroyed(int, int, int, const Block &) const {} BlockData *getBlockData(int x, int y, int z) const; BlockData *addBlockData(int x, int y, int z, int inventoryWidth = 0, int inventoryHeight = 0); diff --git a/source/server/lua/ScriptEngine.cpp b/source/server/lua/ScriptEngine.cpp index 607b285a6..1f13ce068 100644 --- a/source/server/lua/ScriptEngine.cpp +++ b/source/server/lua/ScriptEngine.cpp @@ -168,7 +168,10 @@ void ScriptEngine::initUsertypes() { "set_string", &BlockMetadata::setString, "get_int", &BlockMetadata::getLuaObject, - "set_int", &BlockMetadata::setInt + "set_int", &BlockMetadata::setInt, + + "get_bool", &BlockMetadata::getLuaObject, + "set_bool", &BlockMetadata::setBool ); m_lua.new_usertype("ClientInfo", diff --git a/source/server/lua/loader/LuaBlockLoader.cpp b/source/server/lua/loader/LuaBlockLoader.cpp index 50e00a25d..682b805e8 100644 --- a/source/server/lua/loader/LuaBlockLoader.cpp +++ b/source/server/lua/loader/LuaBlockLoader.cpp @@ -67,6 +67,7 @@ inline void LuaBlockLoader::loadProperties(ServerBlock &block, const sol::table 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("")); } diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 188c94445..e86caa10a 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -162,8 +162,8 @@ void ServerCommandHandler::setupCallbacks() { packet >> x >> y >> z >> block; ServerWorld &world = getWorldForClient(client.id); - world.setBlock(x, y, z, block & 0xffff); world.setData(x, y, z, block >> 16); + world.setBlock(x, y, z, block & 0xffff); m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockPlaced, glm::ivec3{x, y, z}, m_players.at(client.id), world, client, *this); diff --git a/source/server/world/ServerBlock.cpp b/source/server/world/ServerBlock.cpp index 3dde31c5a..85eccefd9 100644 --- a/source/server/world/ServerBlock.cpp +++ b/source/server/world/ServerBlock.cpp @@ -80,3 +80,14 @@ void ServerBlock::onBlockPlaced(const glm::ivec3 &pos, World &world) const { } } +void ServerBlock::onBlockDestroyed(const glm::ivec3 &pos, World &world) const { + try { + if (m_onBlockDestroyed) { + m_onBlockDestroyed(pos, world); + } + } + catch (const sol::error &e) { + std::cerr << e.what() << std::endl; + } +} + diff --git a/source/server/world/ServerBlock.hpp b/source/server/world/ServerBlock.hpp index 500b9ea9b..30c89d798 100644 --- a/source/server/world/ServerBlock.hpp +++ b/source/server/world/ServerBlock.hpp @@ -41,17 +41,21 @@ class ServerBlock : public Block { void onTick(const glm::ivec3 &, Chunk &, World &, ServerCommandHandler &) const; bool onBlockActivated(const glm::ivec3 &pos, Player &player, World &world, ClientInfo &client, ServerCommandHandler &server, u16 screenWidth, u16 screenHeight, u8 guiScale) const; void onBlockPlaced(const glm::ivec3 &pos, World &world) const; + void onBlockDestroyed(const glm::ivec3 &pos, World &world) const; bool canUpdate() const { return m_onTick.valid(); } - void setOnBlockActivated(const sol::function &function) { m_onBlockActivated = function; m_canBeActivated = m_onBlockActivated.valid(); } - void setOnTick(const sol::function &function) { m_onTick = function; m_canUpdate = m_onTick.valid(); } - void setOnBlockPlaced(const sol::function &function) { m_onBlockPlaced = function; } + void setOnBlockActivated(const sol::protected_function &function) { m_onBlockActivated = function; m_canBeActivated = m_onBlockActivated.valid(); } + void setOnTick(const sol::protected_function &function) { m_onTick = function; m_canUpdate = m_onTick.valid(); } + void setOnBlockPlaced(const sol::protected_function &function) { m_onBlockPlaced = function; } + void setOnBlockDestroyed(const sol::protected_function &function) { m_onBlockDestroyed = function; } private: - sol::unsafe_function m_onBlockActivated; - sol::unsafe_function m_onTick; - sol::unsafe_function m_onBlockPlaced; + sol::protected_function m_onBlockActivated; + sol::protected_function m_onTick; + sol::protected_function m_onBlockPlaced; + sol::protected_function m_onBlockDestroyed; + mutable bool m_onTickEnabled = true; }; diff --git a/source/server/world/ServerChunk.cpp b/source/server/world/ServerChunk.cpp index f4c16cf24..b5988cb8f 100644 --- a/source/server/world/ServerChunk.cpp +++ b/source/server/world/ServerChunk.cpp @@ -42,6 +42,11 @@ void ServerChunk::onBlockPlaced(int x, int y, int z, const Block &block) const { serverBlock.onBlockPlaced(glm::ivec3{x + m_x * CHUNK_WIDTH, y + m_y * CHUNK_DEPTH, z + m_z * CHUNK_HEIGHT}, m_world); } +void ServerChunk::onBlockDestroyed(int x, int y, int z, const Block &block) const { + const ServerBlock &serverBlock = (ServerBlock &)block; + serverBlock.onBlockDestroyed(glm::ivec3{x + m_x * CHUNK_WIDTH, y + m_y * CHUNK_DEPTH, z + m_z * CHUNK_HEIGHT}, m_world); +} + void ServerChunk::tick(World &world, ServerCommandHandler &server) { if (!m_tickingBlocks.empty()) { for (auto &it : m_tickingBlocks) { diff --git a/source/server/world/ServerChunk.hpp b/source/server/world/ServerChunk.hpp index 6c0a2ac6f..614f17bc3 100644 --- a/source/server/world/ServerChunk.hpp +++ b/source/server/world/ServerChunk.hpp @@ -43,6 +43,8 @@ class ServerChunk : public Chunk { void updateLights(); void onBlockPlaced(int x, int y, int z, const Block &block) const; + void onBlockDestroyed(int x, int y, int z, const Block &block) const; + void tick(World &world, ServerCommandHandler &server); bool isSent() const { return m_isSent; } diff --git a/texturepacks/minecraft/blocks/door_wood_lower.png b/texturepacks/minecraft/blocks/door_wood_lower.png new file mode 100644 index 000000000..3a0bae98d Binary files /dev/null and b/texturepacks/minecraft/blocks/door_wood_lower.png differ diff --git a/texturepacks/minecraft/blocks/door_wood_upper.png b/texturepacks/minecraft/blocks/door_wood_upper.png new file mode 100644 index 000000000..93319d5d4 Binary files /dev/null and b/texturepacks/minecraft/blocks/door_wood_upper.png differ diff --git a/texturepacks/minecraft/items/door_wood.png b/texturepacks/minecraft/items/door_wood.png new file mode 100644 index 000000000..6501fa45d Binary files /dev/null and b/texturepacks/minecraft/items/door_wood.png differ