diff --git a/doc/en/scripting/builtins/libhud.md b/doc/en/scripting/builtins/libhud.md index a7e3fa853..1d6264b2d 100644 --- a/doc/en/scripting/builtins/libhud.md +++ b/doc/en/scripting/builtins/libhud.md @@ -54,6 +54,9 @@ hud.close(layoutid: str) -- Get open block inventory ID or 0. hud.get_block_inventory() -> int +-- Gives the ID of the second open inventory (block, virtual inventory...) or 0. +hud.get_second_inventory() -> int + -- Gives the ID of the player that the UI is bound to. hud.get_player() -> int @@ -69,6 +72,9 @@ hud.is_paused() -> bool -- Returns true if inventory is open or overlay is shown. hud.is_inventory_open() -> bool +-- Returns true if the player's inventory is open. +hud.is_player_inventory_open() -> boolean + -- Sets whether to allow pausing. If false, the pause menu will not pause the game. hud.set_allow_pause(flag: bool) diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md index 6ecc5db27..b78c791a5 100644 --- a/doc/en/scripting/events.md +++ b/doc/en/scripting/events.md @@ -221,6 +221,18 @@ function on_hud_close(playerid: int) Called on world close (before saving) +```lua +function on_inventory_interact(invid: int, slot: int, mode: int, action: int) +``` + +Triggered when a player interacts with inventory slots. + +| ACTION / MODE | (0) PRIMARY | (1) SECONDARY | +| ------------- | -------------------------------------------------- | ------------------------------------------------------ | +| (0) PUT | Places all items; if occupied, swaps them. | Places a single item from the cursor into the slot. | +| (1) TAKE | Picks up all items from the slot. | Picks up half of the items from the slot (rounded up). | +| (2) SHARE | Triggers the share event for the inventory layout. | Undefined | + ## *events* library ```lua diff --git a/doc/ru/scripting/builtins/libhud.md b/doc/ru/scripting/builtins/libhud.md index 1e904cc56..bad0e93c6 100644 --- a/doc/ru/scripting/builtins/libhud.md +++ b/doc/ru/scripting/builtins/libhud.md @@ -55,6 +55,9 @@ hud.close(layoutid: string) -- Дает ID инвентаря открытого блока или 0. hud.get_block_inventory() -> int +-- Дает ID второго открытого инвентаря (блок, виртуальный инвентарь...) или 0. +hud.get_second_inventory() -> int + -- Дает ID игрока, к которому привязан пользовательский интерфейс. hud.get_player() -> int @@ -70,6 +73,9 @@ hud.is_paused() -> boolean -- Возвращает true если открыт инвентарь или оверлей. hud.is_inventory_open() -> boolean +-- Возвращает true если открыт инвентарь игрока. +hud.is_player_inventory_open() -> boolean + -- Устанавливает разрешение на паузу. При значении false меню паузы не приостанавливает игру. hud.set_allow_pause(flag: boolean) diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md index 72ff37244..d0cf88a24 100644 --- a/doc/ru/scripting/events.md +++ b/doc/ru/scripting/events.md @@ -230,6 +230,17 @@ function on_hud_close(playerid: int) Вызывается при выходе из мира, перед его сохранением. +```lua +function on_inventory_interact(invid: int, slot: int, mode: int, action: int) +``` + +Вызывается при взаимодействии игрока с любым слотом +| ACTION / MODE | (0) PRIMARY | (1) SECONDARY | +| --------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------- | +| `(0) PUT` | Кладёт все предметы в слот, если другой предмет в слоте - обмен | Кладёт в слот один предмет из слота курсора | +| `(1) TAKE` | Забирает все предметы из слота | Берёт половину предметов из слота (округление вверх) | +| `(2) SHARE` | Вызывает share ивент у макета инвентаря | Не определён | + ## События ECS События **ECS** описаны [тут](ecs.md#события-компонента) diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 6a4afbd89..4366d8c6e 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -688,6 +688,10 @@ bool Hud::isInventoryOpen() const { return inventoryOpen; } +bool Hud::isPlayerInventoryOpen() const { + return inventoryView != nullptr; +} + bool Hud::isPause() const { return pause; } @@ -725,6 +729,16 @@ std::shared_ptr Hud::getBlockInventory() { return blockUI->getInventory(); } +std::shared_ptr Hud::getSecondInventory() { + if (blockUI) { + return blockUI->getInventory(); + } + if (secondInvView) { + return secondInvView->getInventory(); + } + return nullptr; +} + bool Hud::isContentAccess() const { return showContentPanel; } diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp index 4e3e4f3dc..f80c36369 100644 --- a/src/frontend/hud.hpp +++ b/src/frontend/hud.hpp @@ -147,6 +147,8 @@ public: /// @brief Check if inventory mode on bool isInventoryOpen() const; + bool isPlayerInventoryOpen() const; + /// @brief Check if pause mode on bool isPause() const; @@ -203,6 +205,8 @@ public: std::shared_ptr getBlockInventory(); + std::shared_ptr getSecondInventory(); + bool isContentAccess() const; void setContentAccess(bool flag); diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index 49c8bd3b9..6429f710f 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -350,45 +350,68 @@ bool SlotView::isHighlighted() const { void SlotView::performLeftClick(ItemStack& stack, ItemStack& grabbed) { const auto& input = gui.getInput(); + const int mode = PRIMARY; + int action; + if (layout.taking && input.pressed(Keycode::LEFT_SHIFT)) { + action = SHARE; if (layout.shareFunc) { layout.shareFunc(layout.index, stack); } if (layout.updateFunc) { layout.updateFunc(layout.index, stack); } + scripting::on_inventory_interact(inventoryId, layout.index, mode, action); return; } + if (!layout.itemSource && stack.accepts(grabbed) && layout.placing) { + action = PUT; stack.move(grabbed, *content->getIndices()); } else { if (layout.itemSource) { if (grabbed.isEmpty()) { + action = TAKE; grabbed.set(stack); } else { + action = PUT; grabbed.clear(); } } else if (grabbed.isEmpty()) { if (layout.taking) { + action = TAKE; std::swap(grabbed, stack); } } else if (layout.taking && layout.placing) { + action = PUT; std::swap(grabbed, stack); } } + + scripting::on_inventory_interact(inventoryId, layout.index, mode, action); } void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) { + const auto& input = gui.getInput(); + const int mode = SECONDARY; + int action = PUT; + if (layout.rightClick) { layout.rightClick(inventoryId, stack); if (layout.updateFunc) { layout.updateFunc(layout.index, stack); } + scripting::on_inventory_interact(inventoryId, layout.index, mode, PUT); return; } - if (layout.itemSource) return; + if (layout.itemSource) { + scripting::on_inventory_interact(inventoryId, layout.index, mode, TAKE); + return; + } + if (grabbed.isEmpty()) { if (!stack.isEmpty() && layout.taking) { + action = TAKE; grabbed.set(std::move(stack)); int halfremain = stack.getCount() / 2; grabbed.setCount(stack.getCount() - halfremain); @@ -399,13 +422,19 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) { stack = ItemStack(0, 0); } } + scripting::on_inventory_interact(inventoryId, layout.index, mode, action); return; } - auto& stackDef = content->getIndices()->items.require(stack.getItemId()); + if (!layout.placing) { + scripting::on_inventory_interact(inventoryId, layout.index, mode, PUT); return; } + + auto& stackDef = content->getIndices()->items.require(stack.getItemId()); + if (stack.isEmpty()) { + action = PUT; itemcount_t count = grabbed.getCount(); stack.set(std::move(grabbed)); stack.setCount(1); @@ -414,11 +443,13 @@ void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) { } else { grabbed = ItemStack(stack.getItemId(), count - 1); } - } else if (stack.accepts(grabbed) && - stack.getCount() < stackDef.stackSize) { + } else if (stack.accepts(grabbed) && stack.getCount() < stackDef.stackSize) { + action = PUT; stack.setCount(stack.getCount() + 1); grabbed.setCount(grabbed.getCount() - 1); } + + scripting::on_inventory_interact(inventoryId, layout.index, mode, action); } void SlotView::clicked(Mousecode button) { @@ -436,6 +467,7 @@ void SlotView::clicked(Mousecode button) { } else if (button == Mousecode::BUTTON_2) { performRightClick(stack, grabbed); } + if (layout.updateFunc) { layout.updateFunc(layout.index, stack); } diff --git a/src/graphics/ui/elements/InventoryView.hpp b/src/graphics/ui/elements/InventoryView.hpp index b77e7d684..03e328815 100644 --- a/src/graphics/ui/elements/InventoryView.hpp +++ b/src/graphics/ui/elements/InventoryView.hpp @@ -56,6 +56,17 @@ namespace gui { ItemStack stack {}; std::wstring countStr; } cache; + + enum InteractionMode { + PRIMARY = 0, + SECONDARY = 1 + }; + + enum InteractionAction { + PUT = 0, + TAKE = 1, + SHARE = 2, + }; const Content* content = nullptr; SlotLayout layout; diff --git a/src/logic/scripting/lua/libs/libhud.cpp b/src/logic/scripting/lua/libs/libhud.cpp index 7ab5d33c0..7f5c893c5 100644 --- a/src/logic/scripting/lua/libs/libhud.cpp +++ b/src/logic/scripting/lua/libs/libhud.cpp @@ -140,6 +140,15 @@ static int l_get_block_inventory(lua::State* L) { } } +static int l_get_second_inventory(lua::State* L) { + auto inventory = hud->getSecondInventory(); + if (inventory == nullptr) { + return lua::pushinteger(L, 0); + } else { + return lua::pushinteger(L, inventory->getId()); + } +} + static int l_get_player(lua::State* L) { auto player = hud->getPlayer(); return lua::pushinteger(L, player->getId()); @@ -153,6 +162,10 @@ static int l_is_inventory_open(lua::State* L) { return lua::pushboolean(L, hud->isInventoryOpen()); } +static int l_is_player_inventory_open(lua::State* L) { + return lua::pushboolean(L, hud->isPlayerInventoryOpen()); +} + static int l_is_content_access(lua::State* L) { return lua::pushboolean(L, hud->isContentAccess()); } @@ -202,11 +215,13 @@ const luaL_Reg hudlib[] = { {"open_permanent", wrap_hud}, {"show_overlay", wrap_hud}, {"get_block_inventory", wrap_hud}, + {"get_second_inventory", wrap_hud}, {"close", wrap_hud}, {"pause", wrap_hud}, {"resume", wrap_hud}, {"is_paused", wrap_hud}, {"is_inventory_open", wrap_hud}, + {"is_player_inventory_open", wrap_hud}, {"get_player", wrap_hud}, {"_is_content_access", wrap_hud}, {"_set_content_access", wrap_hud}, diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index e1553216d..26bd43ac5 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -108,6 +108,7 @@ namespace scripting { void on_inventory_open(const Player* player, const Inventory& inventory); void on_inventory_closed(const Player* player, const Inventory& inventory); + void on_inventory_interact(int invid, int slot, int mode, int action); void on_player_tick(Player* player, int tps); diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index da72b031b..995c2e7a5 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -93,6 +93,23 @@ void scripting::on_frontend_close() { scripting::post_processing = nullptr; } +void scripting::on_inventory_interact(int invid, int slot, int mode, int action) { + auto L = lua::get_main_state(); + for (auto& pack : content_control->getAllContentPacks()) { + lua::emit_event( + L, + pack.id + ":.inventoryinteract", + [&](lua::State* L) { + lua::pushinteger(L, invid); + lua::pushinteger(L, slot); + lua::pushinteger(L, mode); + lua::pushinteger(L, action); + return 4; + } + ); + } +} + void scripting::load_hud_script( const scriptenv& senv, const std::string& packid, @@ -109,6 +126,7 @@ void scripting::load_hud_script( register_event(env, "on_hud_open", packid + ":.hudopen"); register_event(env, "on_hud_render", packid + ":.hudrender"); register_event(env, "on_hud_close", packid + ":.hudclose"); + register_event(env, "on_inventory_interact", packid + ":.inventoryinteract"); } gui::PageLoaderFunc scripting::create_page_loader() {