diff --git a/res/content/base/scripts/hud.lua b/res/content/base/scripts/hud.lua index 0ec427c11..3620566e5 100644 --- a/res/content/base/scripts/hud.lua +++ b/res/content/base/scripts/hud.lua @@ -3,29 +3,55 @@ local base_util = require "util" local DROP_FORCE = 8 local DROP_INIT_VEL = {0, 3, 0} +---@param mode integer 0 - whole stack, 1 - single item, 2 - dupe whole stack +function drop_from_slot(invid, slot, mode) + local pid = hud.get_player() + if mode == 2 and !player.is_infinite_items(pid) then + return + end + local itemid, itemcount = inventory.get(invid, slot) + if itemid == 0 then + return + end + + local drop_itemcount = 1 + if mode == 0 then + drop_itemcount = itemcount + end + if mode == 2 then + drop_itemcount = item.stack_size(itemid) + else + inventory.set(invid, slot, itemid, itemcount - drop_itemcount) + end + + local data = inventory.get_all_data(invid, slot) + local pvel = { player.get_vel(pid) } + local ppos = vec3.add({ player.get_pos(pid) }, { 0, 0.7, 0 }) + local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE) + local drop = base_util.drop(ppos, itemid, drop_itemcount, data, 1.5) + if not drop then + return + end + local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL)) + drop.rigidbody:set_vel(velocity) +end + +function on_inventory_clicked_outside(exc_invid, exc_slot, mode) + drop_from_slot(exc_invid, exc_slot, mode) +end + function on_hud_open() - input.add_callback("player.drop", function () + input.add_callback("player.drop", function() if hud.is_paused() or hud.is_inventory_open() then return end local pid = hud.get_player() local invid, slot = player.get_inventory(pid) - local itemid, itemcount = inventory.get(invid, slot) - if itemid == 0 then - return + local mode = 1 + if input.is_pressed("key:left-ctrl") or input.is_pressed("key:right-ctrl") then + mode = 0 end - local data = inventory.get_all_data(invid, slot) - inventory.set(invid, slot, itemid, itemcount-1) - - local pvel = {player.get_vel(pid)} - local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0}) - local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE) - local drop = base_util.drop(ppos, itemid, 1, data, 1.5) - if not drop then - return - end - local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL)) - drop.rigidbody:set_vel(velocity) + drop_from_slot(invid, slot, mode) end) rules.create("do-loot-non-player", true) end diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 8bfe3a341..fb4320113 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -14,7 +14,6 @@ #include "frontend/UiDocument.hpp" #include "frontend/locale.hpp" #include "graphics/core/Batch2D.hpp" -#include "graphics/core/LineBatch.hpp" #include "graphics/core/Shader.hpp" #include "graphics/core/Font.hpp" #include "graphics/core/DrawContext.hpp" @@ -179,6 +178,8 @@ void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) { } it = mouseOver.erase(it); } + + bool focusHappened = false; if (input.jclicked(Mousecode::BUTTON_1)) { if (pressed == nullptr && this->hover) { @@ -196,10 +197,10 @@ void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) { if (focus != pressed) { focus = pressed; focus->onFocus(); - return; + focusHappened = true; } } - if (this->hover == nullptr && focus) { + if (this->hover == nullptr && focus && !focusHappened) { focus->defocus(); focus = nullptr; } @@ -208,13 +209,44 @@ void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) { pressed = nullptr; } - if (hover) { + if (hover && !focusHappened) { for (Mousecode code : MOUSECODES_ALL) { if (input.jclicked(code)) { hover->clicked(code); } } } + performClickOutside(frame, delta, cursorPos); +} + +void GUI::performClickOutside(Frame& frame, float delta, glm::vec2 cursorPos) { + auto nodes = frame.getNodes(); + std::vector> activeNodes; + + for (const auto& node : nodes) { + if (node && node->isVisible() && node->isInteractive()) { + activeNodes.push_back(node); + } + } + + for (Mousecode code : {Mousecode::BUTTON_1, Mousecode::BUTTON_2, Mousecode::BUTTON_3}) { + if (input.jclicked(code)) { + bool isOverAnyNode = false; + for (const auto& node : activeNodes) { + if (node && node->isInside(cursorPos)) { + isOverAnyNode = true; + break; + } + } + if (!isOverAnyNode) { + for (const auto& node : activeNodes) { + if (node) { + node->clickedOutside(code); + } + } + } + } + } } void GUI::actFocused() { diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index 97fc0ed37..9b65089c1 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -2,7 +2,6 @@ #include "delegates.hpp" -#include #include #include #include @@ -93,6 +92,7 @@ namespace gui { bool debug = false; void actMouse(Frame& frame, float delta, const CursorState& cursor); + void performClickOutside(Frame& frame, float delta, glm::vec2 cursorPos); void actFocused(); void updateTooltip(float delta); void resetTooltip(); diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index 144d80397..09b131cba 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -1,5 +1,7 @@ #include "InventoryView.hpp" +#include + #include #include @@ -19,6 +21,7 @@ #include "items/Inventories.hpp" #include "items/Inventory.hpp" #include "items/ItemDef.hpp" +#include "items/ItemStack.hpp" #include "logic/scripting/scripting.hpp" #include "maths/voxmaths.hpp" #include "objects/Player.hpp" @@ -610,6 +613,33 @@ void InventoryView::setPos(const glm::vec2& pos) { Container::setPos(pos - origin); } +void InventoryView::clickedOutside(Mousecode button) { + // root is the id of the main inventory + if (getId() != "root") { + return; + } + int mode; + if (button == Mousecode::BUTTON_1) { + mode = 0; + } else if (button == Mousecode::BUTTON_2) { + mode = 1; + } else if (button == Mousecode::BUTTON_3) { + mode = 2; + } else { + return; + }; + auto exchangeSlot = std::dynamic_pointer_cast( + gui.get(SlotView::EXCHANGE_SLOT_NAME)); + if (exchangeSlot == nullptr) { + return; + } + ItemStack& grabbed = exchangeSlot->getStack(); + if (grabbed.isEmpty()) { + return; + } + scripting::on_inventory_clicked_outside(exchangeSlot->getInventoryId(), 0, mode); +} + void InventoryView::setOrigin(const glm::vec2& origin) { this->origin = origin; } diff --git a/src/graphics/ui/elements/InventoryView.hpp b/src/graphics/ui/elements/InventoryView.hpp index 243d8b6b5..a0d3c76e9 100644 --- a/src/graphics/ui/elements/InventoryView.hpp +++ b/src/graphics/ui/elements/InventoryView.hpp @@ -143,6 +143,8 @@ namespace gui { void setSelected(int index); + void clickedOutside(Mousecode button) override; + void bind( const std::shared_ptr& inventory, const Content* content diff --git a/src/graphics/ui/elements/UINode.hpp b/src/graphics/ui/elements/UINode.hpp index fb699a737..bd2254567 100644 --- a/src/graphics/ui/elements/UINode.hpp +++ b/src/graphics/ui/elements/UINode.hpp @@ -207,6 +207,9 @@ namespace gui { virtual void onFocus(); virtual void doubleClick(int x, int y); virtual void click(int x, int y); + virtual void clickedOutside(Mousecode button) { + // Empty for all UINodes, except InventoryView + } virtual void clicked(Mousecode button); virtual void mouseMove(int x, int y) {}; virtual void mouseRelease(int x, int y); diff --git a/src/logic/scripting/scripting.hpp b/src/logic/scripting/scripting.hpp index 0044213cd..a4f6adc83 100644 --- a/src/logic/scripting/scripting.hpp +++ b/src/logic/scripting/scripting.hpp @@ -109,6 +109,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 action, int mode); + void on_inventory_clicked_outside(int exchange_invid, int exchange_slot, int mode); 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 59b928d21..92ba4e4ca 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -110,6 +110,22 @@ void scripting::on_inventory_interact(int invid, int slot, int action, int mode) } } +void scripting::on_inventory_clicked_outside(int exchange_invid, int exchange_slot, int mode) { + auto L = lua::get_main_state(); + for (auto& pack : content_control->getAllContentPacks()) { + lua::emit_event( + L, + pack.id + ":.inventoryclickedoutside", + [&](lua::State* L) { + lua::pushinteger(L, exchange_invid); + lua::pushinteger(L, exchange_slot); + lua::pushinteger(L, mode); + return 3; + } + ); + } +} + void scripting::load_hud_script( const scriptenv& senv, const std::string& packid, @@ -127,6 +143,7 @@ void scripting::load_hud_script( register_event(env, "on_hud_render", packid + ":.hudrender"); register_event(env, "on_hud_close", packid + ":.hudclose"); register_event(env, "on_inventory_interact", packid + ":.inventoryinteract"); + register_event(env, "on_inventory_clicked_outside", packid + ":.inventoryclickedoutside"); } gui::PageLoaderFunc scripting::create_page_loader() {