diff --git a/modules/game/game.otmod b/modules/game/game.otmod index 0feb7f2b..9810062f 100644 --- a/modules/game/game.otmod +++ b/modules/game/game.otmod @@ -17,8 +17,9 @@ Module - game_skills - game_inventory - game_combatcontrols - - game_battle + - game_containers - game_viplist + - game_battle - game_hotkeys @onLoad: | diff --git a/modules/game/gameinterface.lua b/modules/game/gameinterface.lua index 20f812a8..365a8827 100644 --- a/modules/game/gameinterface.lua +++ b/modules/game/gameinterface.lua @@ -140,10 +140,10 @@ function GameInterface.createThingMenu(menuPosition, lookThing, useThing, creatu if useThing then if useThing:isContainer() then if useThing:getParentContainer() then - menu:addOption('Open', function() g_game.open(useThing, useThing:getContainerId()) end) - menu:addOption('Open in new window', function() g_game.open(useThing, Containers.getFreeContainerId()) end) + menu:addOption('Open', function() g_game.open(useThing, useThing:getParentContainer()) end) + menu:addOption('Open in new window', function() g_game.open(useThing, nil) end) else - menu:addOption('Open', function() g_game.open(useThing, Containers.getFreeContainerId()) end) + menu:addOption('Open', function() g_game.open(useThing, nil) end) end else if useThing:isMultiUse() then @@ -256,10 +256,10 @@ function GameInterface.processMouseAction(menuPosition, mouseButton, autoWalk, l elseif useThing and keyboardModifiers == KeyboardCtrlModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then if useThing:isContainer() then if useThing:getParentContainer() then - g_game.open(useThing, useThing:getContainerId()) + g_game.open(useThing, useThing:getParentContainer()) return true else - g_game.open(useThing, Containers.getFreeContainerId()) + g_game.open(useThing, nil) return true end elseif useThing:isMultiUse() then @@ -281,10 +281,10 @@ function GameInterface.processMouseAction(menuPosition, mouseButton, autoWalk, l return true elseif multiUseThing:isContainer() then if multiUseThing:getParentContainer() then - g_game.open(multiUseThing, multiUseThing:getContainerId()) + g_game.open(multiUseThing, multiUseThing:getParentContainer()) return true else - g_game.open(multiUseThing, Containers.getFreeContainerId()) + g_game.open(multiUseThing, nil) return true end elseif multiUseThing:isMultiUse() then diff --git a/modules/game/images/miniwindow.png b/modules/game/images/miniwindow.png index fa291936..855daa5e 100644 Binary files a/modules/game/images/miniwindow.png and b/modules/game/images/miniwindow.png differ diff --git a/modules/game/images/miniwindowbuttons.png b/modules/game/images/miniwindowbuttons.png index bc065015..450a9780 100644 Binary files a/modules/game/images/miniwindowbuttons.png and b/modules/game/images/miniwindowbuttons.png differ diff --git a/modules/game/styles/items.otui b/modules/game/styles/items.otui index 9cd088bc..0e24d455 100644 --- a/modules/game/styles/items.otui +++ b/modules/game/styles/items.otui @@ -1,5 +1,6 @@ Item < UIItem size: 34 34 + padding: 1 image-source: /core_styles/styles/images/item.png font: verdana-11px-rounded border-color: white diff --git a/modules/game/styles/miniwindow.otui b/modules/game/styles/miniwindow.otui index 4a44541d..b618f6a0 100644 --- a/modules/game/styles/miniwindow.otui +++ b/modules/game/styles/miniwindow.otui @@ -3,7 +3,7 @@ MiniWindow < UIMiniWindow icon-rect: 4 4 16 16 width: 192 height: 200 - text-offset: 26 5 + text-offset: 24 5 text-align: topLeft margin-bottom: 2 move-policy: free updated @@ -82,7 +82,7 @@ MiniWindowContents < ScrollablePanel id: contentsPanel anchors.fill: parent margin-right: 14 - padding: 25 6 6 6 + padding: 24 3 3 5 vertical-scrollbar: miniwindowScrollBar BorderlessGameWindow < UIWindow diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua index d9dba2e8..250f9ad2 100644 --- a/modules/game_battle/battle.lua +++ b/modules/game_battle/battle.lua @@ -36,7 +36,7 @@ table.insert(lifeBarColors, {percentAbove = -1, color = '#4F0000' } ) -- public functions function Battle.init() - battleWindow = displayUI('battle.otui', GameInterface.getRightPanel()) + battleWindow = displayUI('battle.otui', GameInterface.getLeftPanel()) battleButton = TopMenu.addGameToggleButton('battleButton', 'Battle (Ctrl+B)', 'battle.png', Battle.toggle) battleButton:setOn(true) Keyboard.bindKeyDown('Ctrl+B', Battle.toggle) diff --git a/modules/game_containers/container.otui b/modules/game_containers/container.otui index c73a0b55..6d1e44ad 100644 --- a/modules/game_containers/container.otui +++ b/modules/game_containers/container.otui @@ -1,11 +1,35 @@ -MiniWindow - size: 200 221 +ContainerWindow < MiniWindow + height: 150 - layout: - type: grid - cell-size: 34 34 - cell-spacing: 5 - num-columns: 4 - num-lines: 5 + UIItem + id: containerItemWidget + virtual: true + item-id: 3253 + size: 32 32 + anchors.top: parent.top + anchors.left: parent.left + margin-top: -6 + margin-left: -6 + UIButton + id: upButton + anchors.top: minimizeButton.top + anchors.right: minimizeButton.left + margin-right: 3 + size: 14 14 + image-source: /game/images/miniwindowbuttons.png + image-clip: 42 0 14 14 + $hover: + image-clip: 42 14 14 14 + + $pressed: + image-clip: 42 28 14 14 + + MiniWindowContents + padding: 27 6 6 8 + layout: + type: grid + cell-size: 34 34 + flow: true + auto-spacing: true diff --git a/modules/game_containers/containers.lua b/modules/game_containers/containers.lua index 13b7ca5c..ad4636f5 100644 --- a/modules/game_containers/containers.lua +++ b/modules/game_containers/containers.lua @@ -1,138 +1,90 @@ Containers = {} - --- private variables -local containers = {} - --- public functions -function Containers.clean() - containers = {} -end - -function Containers.getFreeContainerId() - for i=0,15 do - if not containers[i] then - return i - end +local function refreshContainerItems(container) + for slot=0,container:getCapacity()-1 do + local itemWidget = container.itemsPanel:getChildById('item' .. slot) + itemWidget:setItem(container:getItem(slot)) end - return 0 end --- hooked events -function Containers.onOpenContainer(containerId, itemId, name, capacity, hasParent, items) - local container = containers[containerId] - if container then - GameInterface.getRightPanel():removeChild(container) +local function onContainerOpen(container, previousContainer) + local containerWindow + if previousContainer then + containerWindow = previousContainer.window + previousContainer.window = nil + previousContainer.itemsPanel = nil + else + containerWindow = createWidget('ContainerWindow', GameInterface.getRightPanel()) + end + containerWindow:setId('container' .. container:getId()) + local containerPanel = containerWindow:getChildById('contentsPanel') + local containerItemWidget = containerWindow:getChildById('containerItemWidget') + containerWindow.onClose = function() + g_game.close(container) + containerWindow:hide() end - container = displayUI('container.otui', GameInterface.getRightPanel()) - name = name:sub(1,1):upper() .. name:sub(2) - container:setText(name) - - -- set icon, itemid - -- closebutton - -- resize - if hasParent then - -- parent button + local upButton = containerWindow:getChildById('upButton') + upButton.onClick = function() + g_game.openParent(container) end + upButton:setVisible(container:hasParent()) - container.itemCount = #items - container.capacity = capacity + local name = container:getName() + name = name:sub(1,1):upper() .. name:sub(2) + containerWindow:setText(name) - for i=1,capacity do - local itemWidget = UIItem.create() - itemWidget:setStyle('Item') - container:addChild(itemWidget) - itemWidget.position = {x=65535, y=containerId+64, z=i-1} + containerItemWidget:setItemId(container:getItemId()) - if i <= #items then - local item = items[i] - item:setPosition(itemWidget.position) - itemWidget:setItem(item) - end + containerPanel:destroyChildren() + for slot=0,container:getCapacity()-1 do + local itemWidget = createWidget('Item', containerPanel) + itemWidget:setId('item' .. slot) + itemWidget:setItem(container:getItem(slot)) + itemWidget.position = container:getSlotPosition(slot) end - containers[containerId] = container + container.window = containerWindow + container.itemsPanel = containerPanel end -function Containers.onCloseContainer(containerId) - local container = containers[containerId] - if container then - GameInterface.getRightPanel():removeChild(container) +local function onContainerClose(container) + local containerWindow = container.window + if containerWindow then + containerWindow:destroy() end - containers[containerId] = nil end -function Containers.onContainerAddItem(containerId, item) - local container = containers[containerId] - if not container or not item or container.itemCount >= container.capacity then return end - - local i = container.itemCount - while i >= 1 do - local itemWidget = container:getChildByIndex(i) - if not itemWidget then return end - - local nextItemWidget = container:getChildByIndex(i+1) - if not nextItemWidget then return end - - local swapItem = itemWidget:getItem() - if swapItem then - swapItem:setPosition(nextItemWidget.position) - nextItemWidget:setItem(swapItem) - end - - i = i - 1 - end - - local itemWidget = container:getChildByIndex(1) - if not itemWidget then return end - item:setPosition(itemWidget.position) - itemWidget:setItem(item) - - container.itemCount = container.itemCount + 1 +local function onContainerAddItem(container, slot, item) + refreshContainerItems(container) end -function Containers.onContainerUpdateItem(containerId, slot, item) - local container = containers[containerId] - if not container then return end - - local itemWidget = container:getChildByIndex(slot + 1) - if not itemWidget then return end +local function onContainerUpdateItem(container, slot, item, oldItem) + local itemWidget = container.itemsPanel:getChildById('item' .. slot) itemWidget:setItem(item) - item:setPosition(itemWidget.position) end -function Containers.onContainerRemoveItem(containerId, slot) - local container = containers[containerId] - if not container then return end - - local itemWidget = container:getChildByIndex(slot+1) - if not itemWidget then return end - itemWidget:setItem(nil) - - for i=slot,container.itemCount-2 do - local itemWidget = container:getChildByIndex(i+1) - if not itemWidget then return end - - local nextItemWidget = container:getChildByIndex(i+2) - if not nextItemWidget then return end +local function onContainerRemoveItem(container, slot, item) + refreshContainerItems(container) +end - local item = nextItemWidget:getItem() - local pos = item:getPosition() - pos.z = pos.z - 1 - item:setPosition(pos) +function Containers.init() + importStyle 'container.otui' - itemWidget:setItem(item) - nextItemWidget:setItem(nil) - end + connect(Container, { onOpen = onContainerOpen, + onClose = onContainerClose, + onAddItem = onContainerAddItem, + onUpdateItem = onContainerUpdateItem, + onRemoveItem = onContainerRemoveItem }) +end - container.itemCount = container.itemCount - 1 +function Containers.terminate() + disconnect(Container, { onOpen = onContainerOpen, + onClose = onContainerClose, + onAddItem = onContainerAddItem, + onUpdateItem = onContainerUpdateItem, + onRemoveItem = onContainerRemoveItem }) end -connect(g_game, { onGameStart = Containers.clean, - onGameEnd = Containers.clean, - onOpenContainer = Containers.onOpenContainer, - onCloseContainer = Containers.onCloseContainer, - onContainerAddItem = Containers.onContainerAddItem, - onContainerUpdateItem = Containers.onContainerUpdateItem, - onContainerRemoveItem = Containers.onContainerRemoveItem }) +function Containers.clean() +end diff --git a/modules/game_containers/containers.otmod b/modules/game_containers/containers.otmod index bd4fa8d4..5654e135 100644 --- a/modules/game_containers/containers.otmod +++ b/modules/game_containers/containers.otmod @@ -4,5 +4,12 @@ Module author: OTClient team website: https://github.com/edubart/otclient + dependecies: + - game + @onLoad: | dofile 'containers' + Containers.init() + + @onUnload: | + Containers.terminate() diff --git a/src/framework/luascript/luavaluecasts.h b/src/framework/luascript/luavaluecasts.h index 976f15c0..fae0751f 100644 --- a/src/framework/luascript/luavaluecasts.h +++ b/src/framework/luascript/luavaluecasts.h @@ -135,6 +135,13 @@ void push_luavalue(const std::deque& vec); template bool luavalue_cast(int index, std::deque& vec); +// map +template +void push_luavalue(const std::map& map); + +template +bool luavalue_cast(int index, std::map& map); + // tuple template void push_luavalue(const std::tuple& tuple); @@ -299,6 +306,34 @@ bool luavalue_cast(int index, std::deque& vec) return false; } +template +void push_luavalue(const std::map& map) +{ + g_lua.newTable(); + for(auto& it : map) { + push_luavalue(it.first); + push_luavalue(it.second); + g_lua.rawSet(); + } +} + +template +bool luavalue_cast(int index, std::map& map) +{ + if(g_lua.isTable(index)) { + g_lua.pushNil(); + while(g_lua.next(index < 0 ? index-1 : index)) { + K key; + V value; + if(luavalue_cast(-1, value) && luavalue_cast(-2, key)) + map[key] = value; + g_lua.pop(); + } + return true; + } + return false; +} + template struct push_tuple_luavalue { template diff --git a/src/framework/ui/uigridlayout.cpp b/src/framework/ui/uigridlayout.cpp index 02c915cf..f16f75d4 100644 --- a/src/framework/ui/uigridlayout.cpp +++ b/src/framework/ui/uigridlayout.cpp @@ -23,12 +23,14 @@ #include "uigridlayout.h" #include "uiwidget.h" +#include + UIGridLayout::UIGridLayout(UIWidgetPtr parentWidget): UILayout(parentWidget) { m_cellSize = Size(16,16); m_cellSpacing = 0; m_numColumns = 1; - m_numLines = 1; + m_numLines = 0; } void UIGridLayout::applyStyle(const OTMLNodePtr& styleNode) @@ -49,6 +51,12 @@ void UIGridLayout::applyStyle(const OTMLNodePtr& styleNode) setNumColumns(node->value()); else if(node->tag() == "num-lines") setNumLines(node->value()); + else if(node->tag() == "fit-children") + setFitChildren(node->value()); + else if(node->tag() == "auto-spacing") + setAutoSpacing(node->value()); + else if(node->tag() == "flow") + setFlow(node->value()); } } @@ -71,24 +79,42 @@ bool UIGridLayout::internalUpdate() Rect clippingRect = parentWidget->getClippingRect(); Point topLeft = clippingRect.topLeft(); + int numColumns = m_numColumns; + if(m_flow && m_cellSize.width() > 0) + numColumns = clippingRect.width() / (m_cellSize.width() + m_cellSpacing); + + int cellSpacing = m_cellSpacing; + if(m_autoSpacing && numColumns > 1) + cellSpacing = (clippingRect.width() - numColumns * m_cellSize.width()) / (numColumns - 1); + int index = 0; + int preferredHeight = 0; for(const UIWidgetPtr& widget : widgets) { if(!widget->isExplicitlyVisible()) continue; - int line = index / m_numColumns; - int column = index % m_numColumns; - Point virtualPos = Point(column * (m_cellSize.width() + m_cellSpacing), line * (m_cellSize.height() + m_cellSpacing)); - Point pos = topLeft + virtualPos; + int line = index / numColumns; + int column = index % numColumns; + Point virtualPos = Point(column * (m_cellSize.width() + cellSpacing), line * (m_cellSize.height() + cellSpacing)); + preferredHeight = virtualPos.y + m_cellSize.height(); + Point pos = topLeft + virtualPos - parentWidget->getVirtualOffset(); if(widget->setRect(Rect(pos, m_cellSize))) changed = true; index++; - if(index >= m_numColumns * m_numLines) + if(m_numLines > 0 && index >= m_numColumns * m_numLines) break; } + preferredHeight += parentWidget->getPaddingTop() + parentWidget->getPaddingBottom(); + + if(m_fitChildren && preferredHeight != parentWidget->getHeight()) { + // must set the preferred height later + g_eventDispatcher.addEvent([=] { + parentWidget->setHeight(preferredHeight); + }); + } return changed; } diff --git a/src/framework/ui/uigridlayout.h b/src/framework/ui/uigridlayout.h index a8d6f523..d8937d19 100644 --- a/src/framework/ui/uigridlayout.h +++ b/src/framework/ui/uigridlayout.h @@ -40,6 +40,9 @@ public: void setCellSpacing(int spacing) { m_cellSpacing = spacing; update(); } void setNumColumns(int columns) { m_numColumns = columns; update(); } void setNumLines(int lines) { m_numLines = lines; update(); } + void setAutoSpacing(bool enable) { m_autoSpacing = enable; update(); } + void setFitChildren(bool enable) { m_fitChildren = enable; update(); } + void setFlow(bool enable) { m_flow = enable; update(); } virtual UIGridLayoutPtr asUIGridLayout() { return nullptr; } @@ -51,6 +54,9 @@ private: int m_cellSpacing; int m_numColumns; int m_numLines; + Boolean m_autoSpacing; + Boolean m_fitChildren; + Boolean m_flow; }; #endif diff --git a/src/framework/ui/uihorizontallayout.cpp b/src/framework/ui/uihorizontallayout.cpp index 22bba0e6..ac741016 100644 --- a/src/framework/ui/uihorizontallayout.cpp +++ b/src/framework/ui/uihorizontallayout.cpp @@ -48,7 +48,7 @@ bool UIHorizontalLayout::internalUpdate() bool changed = false; Rect clippingRect = parentWidget->getClippingRect(); Point pos = (m_alignRight) ? clippingRect.topRight() : clippingRect.topLeft(); - int prefferedWidth = 0; + int preferredWidth = 0; int gap; for(const UIWidgetPtr& widget : widgets) { @@ -59,7 +59,7 @@ bool UIHorizontalLayout::internalUpdate() gap = (m_alignRight) ? -(widget->getMarginRight()+widget->getWidth()) : widget->getMarginLeft(); pos.x += gap; - prefferedWidth += gap; + preferredWidth += gap; if(widget->isFixedSize()) { // center it @@ -77,16 +77,16 @@ bool UIHorizontalLayout::internalUpdate() gap = (m_alignRight) ? -widget->getMarginLeft() : (widget->getWidth() + widget->getMarginRight()); gap += m_spacing; pos.x += gap; - prefferedWidth += gap; + preferredWidth += gap; } - prefferedWidth -= m_spacing; - prefferedWidth += parentWidget->getPaddingLeft() + parentWidget->getPaddingRight(); + preferredWidth -= m_spacing; + preferredWidth += parentWidget->getPaddingLeft() + parentWidget->getPaddingRight(); - if(m_fitChildren && prefferedWidth != parentWidget->getWidth()) { - // must set the preffered width later + if(m_fitChildren && preferredWidth != parentWidget->getWidth()) { + // must set the preferred width later g_eventDispatcher.addEvent([=] { - parentWidget->setWidth(prefferedWidth); + parentWidget->setWidth(preferredWidth); }); } diff --git a/src/framework/ui/uiverticallayout.cpp b/src/framework/ui/uiverticallayout.cpp index 48808259..8acd7663 100644 --- a/src/framework/ui/uiverticallayout.cpp +++ b/src/framework/ui/uiverticallayout.cpp @@ -49,7 +49,7 @@ bool UIVerticalLayout::internalUpdate() Rect clippingRect = parentWidget->getClippingRect(); Point pos = (m_alignBottom) ? clippingRect.bottomLeft() : clippingRect.topLeft(); - int prefferedHeight = 0; + int preferredHeight = 0; int gap; for(const UIWidgetPtr& widget : widgets) { @@ -60,7 +60,7 @@ bool UIVerticalLayout::internalUpdate() gap = (m_alignBottom) ? -(widget->getMarginBottom()+widget->getHeight()) : widget->getMarginTop(); pos.y += gap; - prefferedHeight += gap; + preferredHeight += gap; if(widget->isFixedSize()) { // center it @@ -78,16 +78,16 @@ bool UIVerticalLayout::internalUpdate() gap = (m_alignBottom) ? -widget->getMarginTop() : (widget->getHeight() + widget->getMarginBottom()); gap += m_spacing; pos.y += gap; - prefferedHeight += gap; + preferredHeight += gap; } - prefferedHeight -= m_spacing; - prefferedHeight += parentWidget->getPaddingTop() + parentWidget->getPaddingBottom(); + preferredHeight -= m_spacing; + preferredHeight += parentWidget->getPaddingTop() + parentWidget->getPaddingBottom(); - if(m_fitChildren && prefferedHeight != parentWidget->getHeight()) { - // must set the preffered width later + if(m_fitChildren && preferredHeight != parentWidget->getHeight()) { + // must set the preferred width later g_eventDispatcher.addEvent([=] { - parentWidget->setHeight(prefferedHeight); + parentWidget->setHeight(preferredHeight); }); } diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index ebfeacfd..2f37c4ae 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -1300,10 +1300,10 @@ void UIWidget::onGeometryChange(const Rect& oldRect, const Rect& newRect) updateText(); // move children that is outside the parent rect to inside again - for(const UIWidgetPtr& child : m_children) { - if(!child->isAnchored()) - child->bindRectToParent(); - } + //for(const UIWidgetPtr& child : m_children) { + //if(!child->isAnchored()) + //child->bindRectToParent(); + //} } void UIWidget::onLayoutUpdate() diff --git a/src/otclient/core/container.cpp b/src/otclient/core/container.cpp index df12527a..0df11273 100644 --- a/src/otclient/core/container.cpp +++ b/src/otclient/core/container.cpp @@ -21,3 +21,80 @@ */ #include "container.h" +#include "item.h" + +Container::Container() +{ + m_id = -1; + m_itemId = 0; + m_capacity = 20; + m_name = "Container"; + m_hasParent = false; +} + +void Container::open(const ContainerPtr& previousContainer) +{ + callLuaField("onOpen", previousContainer); +} + +void Container::close() +{ + callLuaField("onClose"); +} + +void Container::addItem(const ItemPtr& item) +{ + m_items.push_front(item); + updateItemsPositions(); + + callLuaField("onAddItem", 0, item); +} + +void Container::addItems(const std::vector& items) +{ + for(const ItemPtr& item : items) + m_items.push_back(item); + updateItemsPositions(); +} + +void Container::updateItem(int slot, const ItemPtr& item) +{ + if(slot < 0 || slot >= (int)m_items.size()) { + logTraceError("slot not found"); + return; + } + + ItemPtr oldItem = m_items[slot]; + m_items[slot] = item; + item->setPosition(getSlotPosition(slot)); + + callLuaField("onUpdateItem", slot, item, oldItem); +} + +void Container::removeItem(int slot) +{ + if(slot < 0 || slot >= (int)m_items.size()) { + logTraceError("slot not found"); + return; + } + + ItemPtr item = m_items[slot]; + m_items.erase(m_items.begin() + slot); + + updateItemsPositions(); + + callLuaField("onRemoveItem", slot, item); +} + +ItemPtr Container::getItem(int slot) +{ + if(slot < 0 || slot >= (int)m_items.size()) + return nullptr; + return m_items[slot]; +} + +void Container::updateItemsPositions() +{ + for(int slot = 0; slot < (int)m_items.size(); ++slot) + m_items[slot]->setPosition(getSlotPosition(slot)); +} diff --git a/src/otclient/core/container.h b/src/otclient/core/container.h index e8fc1010..6381d579 100644 --- a/src/otclient/core/container.h +++ b/src/otclient/core/container.h @@ -32,6 +32,39 @@ class Container : public LuaObject public: Container(); + void open(const ContainerPtr& previousContainer); + void close(); + + void addItem(const ItemPtr& item); + void addItems(const std::vector& items); + void updateItem(int slot, const ItemPtr& item); + void removeItem(int slot); + ItemPtr getItem(int slot); + + Position getSlotPosition(int slot) { return Position(0xffff, m_id | 0x40, slot); } + + void setId(int id) { m_id = id; } + void setCapacity(int capacity) { m_capacity = capacity; } + void setName(std::string name) { m_name = name; } + void setItemId(uint16 itemId) { m_itemId = itemId; } + void setHasParent(bool hasParent) { m_hasParent = hasParent; } + + std::string getName() { return m_name; } + int getId() { return m_id; } + int getCapacity() { return m_capacity; } + int getItemsCount() { return m_items.size(); } + uint16 getItemId() { return m_itemId; } + bool hasParent() { return m_hasParent; } + +private: + void updateItemsPositions(); + + int m_id; + int m_capacity; + uint16 m_itemId; + std::string m_name; + bool m_hasParent; + std::deque m_items; }; #endif diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp index faa66826..8dedf74b 100644 --- a/src/otclient/core/game.cpp +++ b/src/otclient/core/game.cpp @@ -25,6 +25,7 @@ #include "map.h" #include "tile.h" #include "creature.h" +#include "container.h" #include "statictext.h" #include #include @@ -166,30 +167,73 @@ void Game::processCreatureSpeak(const std::string& name, int level, Otc::SpeakTy g_lua.callGlobalField("g_game", "onCreatureSpeak", name, level, type, message, channelId, creaturePos); } -void Game::processOpenContainer(int containerId, int itemId, const std::string& name, int capacity, bool hasParent, const std::vector< ItemPtr >& items) +void Game::processOpenContainer(int containerId, int itemId, const std::string& name, int capacity, bool hasParent, const std::vector& items) { - g_lua.callGlobalField("g_game", "onOpenContainer", containerId, itemId, name, capacity, hasParent, items); + ContainerPtr previousContainer = getContainer(containerId); + ContainerPtr container = ContainerPtr(new Container()); + container->setId(containerId); + container->setCapacity(capacity); + container->setName(name); + container->setItemId(itemId); + container->setHasParent(hasParent); + m_containers[containerId] = container; + container->addItems(items); + + container->open(previousContainer); + if(previousContainer) + previousContainer->close(); } void Game::processCloseContainer(int containerId) { - g_lua.callGlobalField("g_game", "onCloseContainer", containerId); + ContainerPtr container = getContainer(containerId); + if(!container) { + logTraceError("container not found"); + return; + } + + auto it = m_containers.find(container->getId()); + if(it == m_containers.end() || it->second != container) { + logTraceError("invalid container"); + return; + } + + m_containers.erase(it); + + container->close(); } void Game::processContainerAddItem(int containerId, const ItemPtr& item) { - item->setPosition(Position(65535, containerId + 0x40, 0)); - g_lua.callGlobalField("g_game", "onContainerAddItem", containerId, item); + ContainerPtr container = getContainer(containerId); + if(!container) { + logTraceError("container not found"); + return; + } + + container->addItem(item); } void Game::processContainerUpdateItem(int containerId, int slot, const ItemPtr& item) { - g_lua.callGlobalField("g_game", "onContainerUpdateItem", containerId, slot, item); + ContainerPtr container = getContainer(containerId); + if(!container) { + logTraceError("container not found"); + return; + } + + container->updateItem(slot, item); } void Game::processContainerRemoveItem(int containerId, int slot) { - g_lua.callGlobalField("g_game", "onContainerRemoveItem", containerId, slot); + ContainerPtr container = getContainer(containerId); + if(!container) { + logTraceError("container not found"); + return; + } + + container->removeItem(slot); } void Game::processInventoryChange(int slot, const ItemPtr& item) @@ -542,19 +586,37 @@ void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing) m_protocolGame->sendUseItemWith(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toThing->getStackpos()); } -void Game::open(const ItemPtr& item, int containerId) +void Game::open(const ItemPtr& item, const ContainerPtr& previousContainer) { if(!canPerformGameAction() || !item) return; - m_protocolGame->sendUseItem(item->getPosition(), item->getId(), item->getStackpos(), containerId); + int id = 0; + if(!previousContainer) { + // find a free container id + while(m_containers.find(id) != m_containers.end()) + id++; + } else { + id = previousContainer->getId(); + } + + m_protocolGame->sendUseItem(item->getPosition(), item->getId(), item->getStackpos(), id); } -void Game::upContainer(int containerId) +void Game::openParent(const ContainerPtr& container) { if(!canPerformGameAction()) return; - m_protocolGame->sendUpContainer(containerId); + + m_protocolGame->sendUpContainer(container->getId()); +} + +void Game::close(const ContainerPtr& container) +{ + if(!canPerformGameAction()) + return; + + m_protocolGame->sendCloseContainer(container->getId()); } void Game::refreshContainer() diff --git a/src/otclient/core/game.h b/src/otclient/core/game.h index 1ea0ffd4..bd3f0ba2 100644 --- a/src/otclient/core/game.h +++ b/src/otclient/core/game.h @@ -139,8 +139,9 @@ public: void useInventoryItemWith(int itemId, const ThingPtr& toThing); // container related - void open(const ItemPtr& item, int containerId); - void upContainer(int containerId); + void open(const ItemPtr& item, const ContainerPtr& previousContainer); + void openParent(const ContainerPtr& container); + void close(const ContainerPtr& container); void refreshContainer(); // attack/follow related @@ -218,6 +219,7 @@ public: bool isFollowing() { return !!m_followingCreature; } ContainerPtr getContainer(int index) { return m_containers[index]; } + std::map getContainers() { return m_containers; } CreaturePtr getAttackingCreature() { return m_attackingCreature; } CreaturePtr getFollowingCreature() { return m_followingCreature; } int getServerBeat() { return m_serverBeat; } diff --git a/src/otclient/core/thing.cpp b/src/otclient/core/thing.cpp index 73178887..c7d67a5c 100644 --- a/src/otclient/core/thing.cpp +++ b/src/otclient/core/thing.cpp @@ -56,8 +56,10 @@ const TilePtr& Thing::getTile() ContainerPtr Thing::getParentContainer() { - if(m_position.x == 0xFFFF && m_position.y & 0x40) - return g_game.getContainer(m_position.z); + if(m_position.x == 0xffff && m_position.y & 0x40) { + int containerId = m_position.y ^ 0x40; + return g_game.getContainer(containerId); + } return nullptr; } diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index e2de3764..aae3e011 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -95,7 +95,8 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("g_game", "useInventoryItem", std::bind(&Game::useInventoryItem, &g_game, _1)); g_lua.bindClassStaticFunction("g_game", "useInventoryItemWith", std::bind(&Game::useInventoryItemWith, &g_game, _1, _2)); g_lua.bindClassStaticFunction("g_game", "open", std::bind(&Game::open, &g_game, _1, _2)); - g_lua.bindClassStaticFunction("g_game", "upContainer", std::bind(&Game::upContainer, &g_game, _1)); + g_lua.bindClassStaticFunction("g_game", "openParent", std::bind(&Game::openParent, &g_game, _1)); + g_lua.bindClassStaticFunction("g_game", "close", std::bind(&Game::close, &g_game, _1)); g_lua.bindClassStaticFunction("g_game", "refreshContainer", std::bind(&Game::refreshContainer, &g_game)); g_lua.bindClassStaticFunction("g_game", "attack", std::bind(&Game::attack, &g_game, _1)); g_lua.bindClassStaticFunction("g_game", "cancelAttack", std::bind(&Game::cancelAttack, &g_game)); @@ -147,6 +148,8 @@ void OTClient::registerLuaFunctions() g_lua.bindClassStaticFunction("g_game", "isDead", std::bind(&Game::isDead, &g_game)); g_lua.bindClassStaticFunction("g_game", "isAttacking", std::bind(&Game::isAttacking, &g_game)); g_lua.bindClassStaticFunction("g_game", "isFollowing", std::bind(&Game::isFollowing, &g_game)); + g_lua.bindClassStaticFunction("g_game", "getContainer", std::bind(&Game::getContainer, &g_game, _1)); + g_lua.bindClassStaticFunction("g_game", "getContainers", std::bind(&Game::getContainers, &g_game)); g_lua.bindClassStaticFunction("g_game", "getAttackingCreature", std::bind(&Game::getAttackingCreature, &g_game)); g_lua.bindClassStaticFunction("g_game", "getFollowingCreature", std::bind(&Game::getFollowingCreature, &g_game)); g_lua.bindClassStaticFunction("g_game", "getServerBeat", std::bind(&Game::getServerBeat, &g_game)); @@ -165,6 +168,26 @@ void OTClient::registerLuaFunctions() g_lua.registerClass(); g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", []{ return ContainerPtr(new Container); }); + g_lua.bindClassMemberFunction("open", &Container::open); + g_lua.bindClassMemberFunction("close", &Container::close); + g_lua.bindClassMemberFunction("addItem", &Container::addItem); + g_lua.bindClassMemberFunction("addItems", &Container::addItems); + g_lua.bindClassMemberFunction("updateItem", &Container::updateItem); + g_lua.bindClassMemberFunction("removeItem", &Container::removeItem); + g_lua.bindClassMemberFunction("getItem", &Container::getItem); + g_lua.bindClassMemberFunction("getSlotPosition", &Container::getSlotPosition); + g_lua.bindClassMemberFunction("setId", &Container::setId); + g_lua.bindClassMemberFunction("setCapacity", &Container::setCapacity); + g_lua.bindClassMemberFunction("setName", &Container::setName); + g_lua.bindClassMemberFunction("setItemId", &Container::setItemId); + g_lua.bindClassMemberFunction("setHasParent", &Container::setHasParent); + g_lua.bindClassMemberFunction("getName", &Container::getName); + g_lua.bindClassMemberFunction("getId", &Container::getId); + g_lua.bindClassMemberFunction("getCapacity", &Container::getCapacity); + g_lua.bindClassMemberFunction("getItemsCount", &Container::getItemsCount); + g_lua.bindClassMemberFunction("getItemId", &Container::getItemId); + g_lua.bindClassMemberFunction("hasParent", &Container::hasParent); g_lua.registerClass(); g_lua.bindClassMemberFunction("setId", &Thing::setId); diff --git a/src/otclient/ui/uiitem.cpp b/src/otclient/ui/uiitem.cpp index dab5a786..8512b0a5 100644 --- a/src/otclient/ui/uiitem.cpp +++ b/src/otclient/ui/uiitem.cpp @@ -35,18 +35,17 @@ void UIItem::drawSelf() UIWidget::drawSelf(); if(m_item) { - Point topLeft = m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top); + Rect drawRect = getClippingRect(); + float scaleFactor = std::min(drawRect.width() / 32.0f, drawRect.height() / 32.0f); g_painter.setColor(Color::white); - m_item->draw(topLeft, 1, true); + m_item->draw(drawRect.topLeft(), scaleFactor, true); if(m_font && m_item->isStackable() && m_item->getCount() > 1) { std::string count = Fw::tostring(m_item->getCount()); g_painter.setColor(Color(231, 231, 231)); m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight); } - - //m_font->renderText(Fw::unsafeCast(m_item->getId()), m_rect); } }