[LuaGUI] CraftingWidget support added.

This commit is contained in:
Quentin Bazin 2019-01-07 03:55:37 +01:00
parent 7c7af38097
commit 3875b73a1d
21 changed files with 180 additions and 257 deletions

28
Notes
View File

@ -32,19 +32,27 @@ gui:show()
```
## HUD
```lua
local hud = openminer:player():hud()
local hud = LuaHUD.new()
hud:hotbar {
pos = {x = 42, y = 84},
texture = "default_hotbar.png",
slots = 8,
}
openminer:player():set_hud(hud)
```
## Player inventory
• `openminer:player():inventory()`
• `openminer:player():inventory():add_stack(name, count=1)`
```lua
local player_inv = openminer:player():inventory()
player_inv:add_stack(name, count = 1)
```
## World
• `openminer:get_block(pos)`
• `openminer:get_meta(pos)`
• `openminer:get_inventory(pos)`
```lua
openminer:get_block(pos)
openminer:get_meta(pos)
openminer:get_inventory(pos)
```
* * *
# Old notes

5
TODO
View File

@ -23,10 +23,11 @@ TODO
# GUI
• DONE: Add a LuaGUI class to handle GUI from Lua
WIP: Add more GUI elements
DONE: Add more GUI elements
◦ DONE: `InventoryWidget`
TODO: `CraftingWidget`
◦ DONE: `CraftingWidget`
◦ DONE: `Image`
• TODO: Implement GUI elements requested by `Furnace` (`FurnaceWidget` first, then its components)
# Main menu

View File

@ -1,41 +0,0 @@
/*
* =====================================================================================
*
* Filename: WorkbenchWidget.hpp
*
* Description:
*
* Created: 20/06/2018 23:22:08
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#ifndef WORKBENCHWIDGET_HPP_
#define WORKBENCHWIDGET_HPP_
#include "CraftingWidget.hpp"
class WorkbenchWidget : public Widget {
public:
WorkbenchWidget(Inventory &playerInventory, Inventory &craftingInventory, Widget *parent = nullptr);
void onEvent(const SDL_Event &event) override;
void update() override;
private:
void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
gk::Image m_background;
Inventory &m_playerInventory;
InventoryWidget m_playerInventoryWidget{this};
InventoryWidget m_hotbarInventoryWidget{this};
CraftingWidget m_craftingWidget;
MouseItemWidget m_mouseItemWidget{this};
};
#endif // WORKBENCHWIDGET_HPP_

View File

@ -14,20 +14,26 @@
#ifndef LUAGUI_HPP_
#define LUAGUI_HPP_
#include <list>
#include "LuaWidgetDef.hpp"
// This class is meant to be used ONLY in Lua
class LuaGUI {
public:
void addImage(const sol::table &table);
void addButton(const sol::table &table);
void addInventory(const sol::table &table);
void addTextButton(const sol::table &table);
void addInventoryWidget(const sol::table &table);
void addCraftingWidget(const sol::table &table);
void show();
std::vector<LuaWidgetDef::Image> images;
std::vector<LuaWidgetDef::Button> buttons;
std::vector<LuaWidgetDef::InventoryList> inventoryLists;
static void initUsertype(sol::state &lua);
std::list<LuaWidgetDef::Image> imageList;
std::list<LuaWidgetDef::TextButton> textButtonList;
std::list<LuaWidgetDef::InventoryWidget> inventoryWidgetList;
std::list<LuaWidgetDef::CraftingWidget> craftingWidgetList;
};
#endif // LUAGUI_HPP_

View File

@ -30,6 +30,8 @@ class LuaMod {
const std::string &id() const { return m_id; }
static void initUsertype(sol::state &lua);
private:
std::string m_id;
};

View File

@ -18,6 +18,7 @@
#include <gk/core/IntTypes.hpp>
#include <gk/core/Rect.hpp>
#include <gk/core/Vector3.hpp>
namespace LuaWidgetDef {
@ -33,12 +34,12 @@ struct Image : public Widget {
gk::FloatRect clipRect;
};
struct Button : public Widget {
struct TextButton : public Widget {
std::string text;
sol::function on_click;
};
struct InventoryList : public Widget {
struct InventoryWidget : public Widget {
std::string player;
std::string inventory;
@ -46,7 +47,14 @@ struct InventoryList : public Widget {
float height = 0;
u16 offset = 0;
u16 size = 0;
u16 count = 0;
};
struct CraftingWidget : public Widget {
gk::Vector3i block;
u16 offset = 0;
u16 count = 9;
};
} // namespace LuaWidgetDef

View File

@ -33,7 +33,6 @@ class ScriptEngine {
static void setInstance(ScriptEngine *instance) { s_instance = instance; }
private:
static bool openWorkbench(const glm::ivec3 &position, Player &player, World &world);
static bool openFurnace(const glm::ivec3 &position, Player &player, World &world);
static void updateFurnace(const glm::ivec3 &position, Player &player, Chunk &chunk, World &world);

View File

@ -29,6 +29,8 @@ class GameState : public gk::ApplicationState {
public:
GameState();
void testLuaAPI();
void onEvent(const SDL_Event &event) override;
void update() override;

View File

@ -21,7 +21,7 @@
#include <gk/gl/Shader.hpp>
#include "Config.hpp"
#include "WorkbenchWidget.hpp"
#include "Widget.hpp"
class InventoryState : public gk::ApplicationState {
public:

View File

@ -1,26 +0,0 @@
/*
* =====================================================================================
*
* Filename: BlockWorkbench.hpp
*
* Description:
*
* Created: 27/06/2018 04:43:18
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#ifndef BLOCKWORKBENCH_HPP_
#define BLOCKWORKBENCH_HPP_
#include "Block.hpp"
class BlockWorkbench : public Block {
public:
BlockWorkbench();
bool onBlockActivated(const glm::ivec3 &position, Player &player, World &world) const override;
};
#endif // BLOCKWORKBENCH_HPP_

View File

@ -102,10 +102,10 @@ mod:block {
texture = 77,
on_block_activated = function(pos, player, world)
-- open_workbench(pos, player, world)
local gui = LuaGUI.new()
local pos = {
-- FIXME: Replace this by gui:set_size() and gui:set_centered()
local gui_pos = {
x = SCREEN_WIDTH / GUI_SCALE / 2.0 - 176 / 2.0,
y = SCREEN_HEIGHT / GUI_SCALE / 2.0 - 166 / 2.0
}
@ -114,15 +114,15 @@ mod:block {
name = "btn_hello",
pos = {x = 0, y = 0},
text = "Hello",
text = "Test button",
on_click = function(self)
print("hello")
print("Test button pressed")
end,
}
gui:inventory {
name = "inv_main",
pos = {x = pos.x + 7, y = pos.y + 83},
pos = {x = gui_pos.x + 7, y = gui_pos.y + 83},
player = "player",
inventory = "main",
@ -133,7 +133,7 @@ mod:block {
gui:inventory {
name = "inv_hotbar",
pos = {x = pos.x + 7, y = pos.y + 141},
pos = {x = gui_pos.x + 7, y = gui_pos.y + 141},
player = "player",
inventory = "main",
@ -142,17 +142,18 @@ mod:block {
count = 9,
}
-- gui:crafting {
-- name = "inv_crafting",
-- pos = {x = 176, y = 166},
gui:crafting {
name = "inv_crafting",
pos = {x = gui_pos.x, y = gui_pos.y},
-- player = "player",
-- inventory = "main",
-- }
block = {x = pos.x, y = pos.y, z = pos.z},
offset = 0,
}
gui:image {
name = "img_background",
pos = pos,
pos = gui_pos,
texture = "texture-workbench",
clip = {x = 0, y = 0, width = 176, height = 166},

View File

@ -1,5 +1,6 @@
print("Hello from Lua!")
-- FIXME
SCREEN_WIDTH = 1600
SCREEN_HEIGHT = 1050
GUI_SCALE = 3

View File

@ -38,27 +38,28 @@ void Application::init() {
m_resourceHandler.add<gk::Font>("font-default", "resources/fonts/default.ttf");
Registry::setInstance(m_registry);
m_scriptEngine.init();
m_stateStack.push<GameState>();
}
void Application::initOpenGL() {
// Enable transparency
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// glBlendFunc(GL_ZERO, GL_SRC_COLOR);
// glBlendFunc(GL_ONE, GL_ONE);
// glBlendEquation(GL_FUNC_ADD);
// glEnable(GL_ALPHA_TEST);
// Enable depth and hide backside of faces
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1, 1);
// Set best quality for mipmaps
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
glClearColor(0.196078, 0.6, 0.8, 1.0); // Skyblue
// Set clear color to skyblue
glClearColor(0.196078, 0.6, 0.8, 1.0);
}

View File

@ -16,8 +16,6 @@
void InventoryWidget::init(Inventory &inventory, unsigned int offset, unsigned int size) {
m_itemWidgets.clear();
// for (u16 y = 0 ; y < inventory.height() ; ++y) {
// for (u16 x = 0 ; x < inventory.width() ; ++x) {
for (u16 i = 0 ; i < (size > 0 ? size : inventory.width() * inventory.height()) ; ++i) {
ItemWidget &widget = m_itemWidgets.emplace_back(inventory, (i + offset) % inventory.width(), (i + offset) / inventory.width(), this);
widget.update();

View File

@ -1,62 +0,0 @@
/*
* =====================================================================================
*
* Filename: WorkbenchWidget.cpp
*
* Description:
*
* Created: 20/06/2018 23:22:29
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#include "WorkbenchWidget.hpp"
WorkbenchWidget::WorkbenchWidget(Inventory &playerInventory, Inventory &craftingInventory, Widget *parent)
: Widget(176, 166, parent), m_playerInventory(playerInventory), m_craftingWidget(craftingInventory, this)
{
m_background.load("texture-workbench");
m_background.setClipRect(0, 0, 176, 166);
m_playerInventoryWidget.init(m_playerInventory, 9, 9 * 3);
m_playerInventoryWidget.setPosition(7, 83, 0);
m_hotbarInventoryWidget.init(m_playerInventory, 0, 9);
m_hotbarInventoryWidget.setPosition(7, 141, 0);
}
void WorkbenchWidget::onEvent(const SDL_Event &event) {
m_craftingWidget.onMouseEvent(event, m_mouseItemWidget);
m_playerInventoryWidget.onMouseEvent(event, m_mouseItemWidget);
m_hotbarInventoryWidget.onMouseEvent(event, m_mouseItemWidget);
m_mouseItemWidget.onEvent(event);
}
void WorkbenchWidget::update() {
m_craftingWidget.update();
const ItemWidget *currentItemWidget = nullptr;
if ((currentItemWidget = m_playerInventoryWidget.currentItemWidget())
|| (currentItemWidget = m_hotbarInventoryWidget.currentItemWidget())
|| (currentItemWidget = m_craftingWidget.currentItemWidget()))
m_mouseItemWidget.update(currentItemWidget);
else
m_mouseItemWidget.update(nullptr);
}
void WorkbenchWidget::draw(gk::RenderTarget &target, gk::RenderStates states) const {
states.transform *= getTransform();
target.draw(m_background, states);
target.draw(m_craftingWidget, states);
target.draw(m_playerInventoryWidget, states);
target.draw(m_hotbarInventoryWidget, states);
target.draw(m_mouseItemWidget, states);
}

View File

@ -36,10 +36,10 @@ void LuaGUI::addImage(const sol::table &table) {
clipRect.height = clipRectTable.value()["height"];
}
images.emplace_back(LuaWidgetDef::Image{{name, x, y}, texture, clipRect});
imageList.emplace_back(LuaWidgetDef::Image{{name, x, y}, texture, clipRect});
}
void LuaGUI::addButton(const sol::table &table) {
void LuaGUI::addTextButton(const sol::table &table) {
// FIXME: Duplicated below
float x = 0, y = 0;
sol::optional<sol::table> pos = table["pos"];
@ -51,10 +51,10 @@ void LuaGUI::addButton(const sol::table &table) {
std::string text = table["text"].get<std::string>();
sol::function on_click = table["on_click"].get<sol::function>();
buttons.emplace_back(LuaWidgetDef::Button{{name, x, y}, text, on_click});
textButtonList.emplace_back(LuaWidgetDef::TextButton{{name, x, y}, text, on_click});
}
void LuaGUI::addInventory(const sol::table &table) {
void LuaGUI::addInventoryWidget(const sol::table &table) {
// FIXME: Duplicated above
float x = 0, y = 0;
sol::optional<sol::table> pos = table["pos"];
@ -76,12 +76,47 @@ void LuaGUI::addInventory(const sol::table &table) {
height = size.value()["y"];
}
inventoryLists.emplace_back(LuaWidgetDef::InventoryList{{name, x, y},
inventoryWidgetList.emplace_back(LuaWidgetDef::InventoryWidget{{name, x, y},
player, inventory, width, height, offset, count});
}
void LuaGUI::addCraftingWidget(const sol::table &table) {
// FIXME: Duplicated above
float x = 0, y = 0;
sol::optional<sol::table> pos = table["pos"];
std::string name = table["name"].get<std::string>();
if (pos != sol::nullopt) {
x = pos.value()["x"];
y = pos.value()["y"];
}
u16 offset = table["offset"].get<u16>();
u16 count = table["count"].get_or<u16>(9);
gk::Vector3i block;
sol::optional<sol::table> blockTable = table["block"];
if (blockTable != sol::nullopt) {
block.x = blockTable.value()["x"];
block.y = blockTable.value()["y"];
block.z = blockTable.value()["z"];
}
craftingWidgetList.emplace_back(LuaWidgetDef::CraftingWidget{{name, x, y},
block, offset, count});
}
void LuaGUI::show() {
auto &stateStack = gk::ApplicationStateStack::getInstance();
stateStack.push<LuaGUIState>(*this, &stateStack.top());
}
void LuaGUI::initUsertype(sol::state &lua) {
lua.new_usertype<LuaGUI>("LuaGUI",
"image", &LuaGUI::addImage,
"button", &LuaGUI::addTextButton,
"inventory", &LuaGUI::addInventoryWidget,
"crafting", &LuaGUI::addCraftingWidget,
"show", &LuaGUI::show
);
}

View File

@ -97,3 +97,14 @@ void LuaMod::registerSmeltingRecipe(const sol::table &table) {
Registry::getInstance().registerRecipe<SmeltingRecipe>(input, output);
}
void LuaMod::initUsertype(sol::state &lua) {
lua.new_usertype<LuaMod>("LuaMod",
sol::constructors<LuaMod(std::string)>(),
"id", &LuaMod::id,
"block", &LuaMod::registerBlock,
"item", &LuaMod::registerItem,
"crafting_recipe", &LuaMod::registerCraftingRecipe,
"smelting_recipe", &LuaMod::registerSmeltingRecipe
);
}

View File

@ -17,13 +17,6 @@
#include "Registry.hpp"
#include "ScriptEngine.hpp"
#include "CraftingRecipe.hpp"
#include "SmeltingRecipe.hpp"
#include "BlockFurnace.hpp"
#include "BlockWater.hpp"
#include "BlockWorkbench.hpp"
ScriptEngine *ScriptEngine::s_instance = nullptr;
void ScriptEngine::init() {
@ -33,7 +26,6 @@ void ScriptEngine::init() {
// FIXME: Remove these lines when they're not needed anymore
m_lua["registry"] = &Registry::getInstance();
m_lua["open_workbench"] = &openWorkbench;
m_lua["open_furnace"] = &openFurnace;
m_lua["update_furnace"] = &updateFurnace;
@ -41,46 +33,31 @@ void ScriptEngine::init() {
m_lua.safe_script_file("mods/test.lua");
}
// FIXME: Compiling this functions is REALLY long
// It's probably better to split it into respective classes files
void ScriptEngine::initUsertypes() {
m_lua.new_usertype<Player>("Player",
"inventory", &Player::inventory);
m_lua.new_usertype<Inventory>("Inventory",
"add_stack", &Inventory::addStack
"add_stack", &Inventory::addStack
);
m_lua.new_usertype<LuaMod>("LuaMod",
sol::constructors<LuaMod(std::string)>(),
"id", &LuaMod::id,
"block", &LuaMod::registerBlock,
"item", &LuaMod::registerItem,
"crafting_recipe", &LuaMod::registerCraftingRecipe,
"smelting_recipe", &LuaMod::registerSmeltingRecipe
m_lua.new_usertype<glm::ivec3>("ivec3",
"x", &glm::ivec3::x,
"y", &glm::ivec3::y,
"z", &glm::ivec3::z
);
m_lua.new_usertype<LuaGUI>("LuaGUI",
"image", &LuaGUI::addImage,
"button", &LuaGUI::addButton,
"inventory", &LuaGUI::addInventory,
"show", &LuaGUI::show
);
LuaMod::initUsertype(m_lua);
LuaGUI::initUsertype(m_lua);
}
// FIXME: All the code below will be removed once these blocks are fully implemented in Lua
#include "BlockFurnace.hpp"
#include "BlockWorkbench.hpp"
#include "Chunk.hpp"
#include "Player.hpp"
#include "World.hpp"
bool ScriptEngine::openWorkbench(const glm::ivec3 &position, Player &player, World &world) {
BlockWorkbench block;
return block.onBlockActivated(position, player, world);
}
bool ScriptEngine::openFurnace(const glm::ivec3 &position, Player &player, World &world) {
BlockFurnace block;
return block.onBlockActivated(position, player, world);

View File

@ -34,6 +34,12 @@ GameState::GameState() {
World::setInstance(m_world);
testLuaAPI();
initShaders();
}
void GameState::testLuaAPI() {
try {
auto &lua = ScriptEngine::getInstance().lua();
lua["player"] = &m_player;
@ -42,8 +48,6 @@ GameState::GameState() {
catch (const sol::error &e) {
std::cerr << e.what() << std::endl;
}
initShaders();
}
void GameState::onEvent(const SDL_Event &event) {

View File

@ -12,6 +12,7 @@
* =====================================================================================
*/
#include <gk/core/ApplicationStateStack.hpp>
#include <gk/core/Debug.hpp>
#include <gk/core/Mouse.hpp>
#include <gk/graphics/Color.hpp>
@ -44,14 +45,6 @@ LuaGUIState::LuaGUIState(LuaGUI &gui, gk::ApplicationState *parent) : gk::Applic
}
void LuaGUIState::onEvent(const SDL_Event &event) {
for (auto &it : m_widgets)
it->onEvent(event);
for (auto &it : m_inventoryWidgets)
it.onMouseEvent(event, m_mouseItemWidget, false);
m_mouseItemWidget.onEvent(event);
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) {
gk::Mouse::setCursorGrabbed(true);
gk::Mouse::setCursorVisible(false);
@ -59,6 +52,17 @@ void LuaGUIState::onEvent(const SDL_Event &event) {
m_stateStack->pop();
}
for (auto &it : m_widgets)
it->onEvent(event);
for (auto &it : m_inventoryWidgets)
it.onMouseEvent(event, m_mouseItemWidget, false);
for (auto &it : m_craftingWidgets)
it.onMouseEvent(event, m_mouseItemWidget);
m_mouseItemWidget.onEvent(event);
}
void LuaGUIState::update() {
@ -67,6 +71,22 @@ void LuaGUIState::update() {
for (auto &it : m_widgets)
it->update();
for (auto &it : m_craftingWidgets) {
it.update();
}
const ItemWidget *currentItemWidget = nullptr;
for (auto &it : m_inventoryWidgets) {
if (!currentItemWidget)
currentItemWidget = it.currentItemWidget();
}
for (auto &it : m_craftingWidgets) {
if (!currentItemWidget)
currentItemWidget = it.currentItemWidget();
}
m_mouseItemWidget.update(currentItemWidget);
}
void LuaGUIState::draw(gk::RenderTarget &target, gk::RenderStates states) const {
@ -90,18 +110,21 @@ void LuaGUIState::draw(gk::RenderTarget &target, gk::RenderStates states) const
for (auto &it : m_inventoryWidgets)
target.draw(it, states);
for (auto &it : m_craftingWidgets)
target.draw(it, states);
target.draw(m_mouseItemWidget, states);
}
void LuaGUIState::loadGUI(LuaGUI &gui) {
for (auto &it : gui.images) {
for (auto &it : gui.imageList) {
auto *image = new gk::Image(it.texture);
image->setPosition(it.x, it.y);
image->setClipRect(it.clipRect.x, it.clipRect.y, it.clipRect.width, it.clipRect.height);
m_drawables.emplace_back(image);
}
for (auto &it : gui.buttons) {
for (auto &it : gui.textButtonList) {
auto *button = new TextButton(&m_mainWidget);
button->setPosition(it.x, it.y);
button->setCallback(it.on_click);
@ -109,10 +132,21 @@ void LuaGUIState::loadGUI(LuaGUI &gui) {
m_widgets.emplace_back(button);
}
for (auto &it : gui.inventoryLists) {
for (auto &it : gui.inventoryWidgetList) {
auto &inventoryWidget = m_inventoryWidgets.emplace_back(&m_mainWidget);
inventoryWidget.setPosition(it.x, it.y);
inventoryWidget.init(World::getInstance().getPlayer()->inventory(), it.offset, it.size);
inventoryWidget.init(World::getInstance().getPlayer()->inventory(), it.offset, it.count);
}
for (auto &it : gui.craftingWidgetList) {
BlockData *data = World::getInstance().getBlockData(it.block.x, it.block.y, it.block.z);
if (data) {
auto &craftingWidget = m_craftingWidgets.emplace_back(data->inventory, &m_mainWidget);
craftingWidget.setPosition(it.x, it.y);
}
else {
DEBUG("ERROR: No inventory found at", it.block.x, it.block.y, it.block.z);
}
}
}

View File

@ -1,36 +0,0 @@
/*
* =====================================================================================
*
* Filename: BlockWorkbench.cpp
*
* Description:
*
* Created: 27/06/2018 04:53:03
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#include <gk/core/ApplicationStateStack.hpp>
#include <gk/core/Exception.hpp>
#include "BlockWorkbench.hpp"
#include "InventoryState.hpp"
#include "Player.hpp"
#include "WorkbenchWidget.hpp"
#include "World.hpp"
BlockWorkbench::BlockWorkbench() : Block(BlockType::Workbench, 77, "default:workbench", "Workbench") {
}
bool BlockWorkbench::onBlockActivated(const glm::ivec3 &position, Player &player, World &world) const {
BlockData *data = world.getBlockData(position.x, position.y, position.z);
if (!data)
throw EXCEPTION("BlockWorkbench at (", position.x, position.y, position.z, ") has no inventory");
auto &inventoryState = gk::ApplicationStateStack::getInstance().push<InventoryState>(&gk::ApplicationStateStack::getInstance().top());
inventoryState.setupWidget<WorkbenchWidget>(player.inventory(), data->inventory);
return true;
}