diff --git a/modules/game_containers/container.otui b/modules/game_containers/container.otui index 7615e4e4..ccf7920c 100644 --- a/modules/game_containers/container.otui +++ b/modules/game_containers/container.otui @@ -1,3 +1,8 @@ +PageButton < Button + size: 30 18 + margin: 1 + + ContainerWindow < MiniWindow height: 150 @@ -25,6 +30,35 @@ ContainerWindow < MiniWindow $pressed: image-clip: 42 28 14 14 + Panel + id: pagePanel + anchors.left: parent.left + anchors.right: parent.right + anchors.top: miniwindowTopBar.bottom + height: 20 + margin: 2 3 0 3 + background: #00000066 + visible: false + + Label + id: pageLabel + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + margin-top: 2 + text-auto-resize: true + + PageButton + id: prevPageButton + text: < + anchors.top: parent.top + anchors.left: parent.left + + PageButton + id: nextPageButton + text: > + anchors.top: parent.top + anchors.right: parent.right + MiniWindowContents padding-right: 0 layout: diff --git a/modules/game_containers/containers.lua b/modules/game_containers/containers.lua index 7f2aed5d..9265942d 100644 --- a/modules/game_containers/containers.lua +++ b/modules/game_containers/containers.lua @@ -3,9 +3,8 @@ function init() connect(Container, { onOpen = onContainerOpen, onClose = onContainerClose, - onAddItem = onContainerAddItem, - onUpdateItem = onContainerUpdateItem, - onRemoveItem = onContainerRemoveItem }) + onSizeChange = onContainerChangeSize, + onUpdateItem = onContainerUpdateItem }) connect(Game, { onGameEnd = clean() }) reloadContainers() @@ -14,9 +13,8 @@ end function terminate() disconnect(Container, { onOpen = onContainerOpen, onClose = onContainerClose, - onAddItem = onContainerAddItem, - onUpdateItem = onContainerUpdateItem, - onRemoveItem = onContainerRemoveItem }) + onSizeChange = onContainerChangeSize, + onUpdateItem = onContainerUpdateItem }) disconnect(Game, { onGameEnd = clean() }) end @@ -46,6 +44,38 @@ function refreshContainerItems(container) local itemWidget = container.itemsPanel:getChildById('item' .. slot) itemWidget:setItem(container:getItem(slot)) end + + if container:hasPages() then + refreshContainerPages(container) + end +end + +function toggleContainerPages(containerWindow, pages) + containerWindow:getChildById('miniwindowScrollBar'):setMarginTop(pages and 42 or 22) + containerWindow:getChildById('contentsPanel'):setMarginTop(pages and 42 or 22) + containerWindow:getChildById('pagePanel'):setVisible(pages) +end + +function refreshContainerPages(container) + local currentPage = 1 + math.floor(container:getFirstIndex() / container:getCapacity()) + local pages = 1 + math.floor(math.max(0, (container:getSize() - 1)) / container:getCapacity()) + container.window:recursiveGetChildById('pageLabel'):setText(string.format('Page %i of %i', currentPage, pages)) + + local prevPageButton = container.window:recursiveGetChildById('prevPageButton') + if currentPage == 1 then + prevPageButton:setEnabled(false) + else + prevPageButton:setEnabled(true) + prevPageButton.onClick = function() g_game.seekInContainer(container:getId(), container:getFirstIndex() - container:getCapacity()) end + end + + local nextPageButton = container.window:recursiveGetChildById('nextPageButton') + if currentPage >= pages then + nextPageButton:setEnabled(false) + else + nextPageButton:setEnabled(true) + nextPageButton.onClick = function() g_game.seekInContainer(container:getId(), container:getFirstIndex() + container:getCapacity()) end + end end function onContainerOpen(container, previousContainer) @@ -88,11 +118,18 @@ function onContainerOpen(container, previousContainer) itemWidget:setItem(container:getItem(slot)) itemWidget:setMargin(0) itemWidget.position = container:getSlotPosition(slot) + + if not container:isUnlocked() then + itemWidget:setBorderColor('red') + end end container.window = containerWindow container.itemsPanel = containerPanel + toggleContainerPages(containerWindow, container:hasPages()) + refreshContainerPages(container) + local layout = containerPanel:getLayout() local cellSize = layout:getCellSize() containerWindow:setContentMinimumHeight(cellSize.height) @@ -110,7 +147,7 @@ function onContainerClose(container) destroy(container) end -function onContainerAddItem(container, slot, item) +function onContainerChangeSize(container, size) if not container.window then return end refreshContainerItems(container) end @@ -120,8 +157,3 @@ function onContainerUpdateItem(container, slot, item, oldItem) local itemWidget = container.itemsPanel:getChildById('item' .. slot) itemWidget:setItem(item) end - -function onContainerRemoveItem(container, slot, item) - if not container.window then return end - refreshContainerItems(container) -end diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index 85ef18b7..8cbeca96 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -467,6 +467,10 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing) if useThing:isRotateable() then menu:addOption(tr('Rotate'), function() g_game.rotate(useThing) end) end + + if g_game.getFeature(GameBrowseField) and useThing:getPosition().x ~= 0xffff then + menu:addOption(tr('Browse Field'), function() g_game.browseField(useThing:getPosition()) end) + end end if lookThing and not lookThing:isCreature() and not lookThing:isNotMoveable() and lookThing:isPickupable() then diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index 3588cac0..7c5695d6 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -117,6 +117,8 @@ GamePVPMode = 50 GameWritableDate = 51 GameAdditionalVipInfo = 52 GameSpritesAlphaChannel = 56 +GamePremiumExpiration = 57 +GameBrowseField = 58 TextColors = { red = '#f55e5e', --'#c83200' diff --git a/src/client/const.h b/src/client/const.h index 268af7f4..2914199c 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -389,6 +389,7 @@ namespace Otc GameHideNpcNames = 55, GameSpritesAlphaChannel = 56, GamePremiumExpiration = 57, + GameBrowseField = 58, LastGameFeature = 101 }; diff --git a/src/client/container.cpp b/src/client/container.cpp index 4d0e06fd..4c83912e 100644 --- a/src/client/container.cpp +++ b/src/client/container.cpp @@ -57,9 +57,22 @@ void Container::onClose() void Container::onAddItem(const ItemPtr& item, int slot) { - m_items.push_front(item); + slot -= m_firstIndex; + + m_size++; + // indicates that there is a new item on next page + if(m_hasPages && slot > m_capacity) { + callLuaField("onSizeChange", m_size); + return; + } + + if(slot == 0) + m_items.push_front(item); + else + m_items.push_back(item); updateItemsPositions(); + callLuaField("onSizeChange", m_size); callLuaField("onAddItem", slot, item); } @@ -80,6 +93,7 @@ void Container::onAddItems(const std::vector& items) void Container::onUpdateItem(int slot, const ItemPtr& item) { + slot -= m_firstIndex; if(slot < 0 || slot >= (int)m_items.size()) { g_logger.traceError("slot not found"); return; @@ -92,8 +106,15 @@ void Container::onUpdateItem(int slot, const ItemPtr& item) callLuaField("onUpdateItem", slot, item, oldItem); } -void Container::onRemoveItem(int slot) +void Container::onRemoveItem(int slot, const ItemPtr& lastItem) { + slot -= m_firstIndex; + if(m_hasPages && slot >= (int)m_items.size()) { + m_size--; + callLuaField("onSizeChange", m_size); + return; + } + if(slot < 0 || slot >= (int)m_items.size()) { g_logger.traceError("slot not found"); return; @@ -102,8 +123,16 @@ void Container::onRemoveItem(int slot) ItemPtr item = m_items[slot]; m_items.erase(m_items.begin() + slot); + + if(lastItem) { + onAddItem(lastItem, m_firstIndex + m_capacity - 1); + m_size--; + } + m_size--; + updateItemsPositions(); + callLuaField("onSizeChange", m_size); callLuaField("onRemoveItem", slot, item); } diff --git a/src/client/container.h b/src/client/container.h index f141e605..c87e8bdd 100644 --- a/src/client/container.h +++ b/src/client/container.h @@ -57,7 +57,7 @@ protected: void onAddItem(const ItemPtr& item, int slot); void onAddItems(const std::vector& items); void onUpdateItem(int slot, const ItemPtr& item); - void onRemoveItem(int slot); + void onRemoveItem(int slot, const ItemPtr& lastItem); friend class Game; diff --git a/src/client/game.cpp b/src/client/game.cpp index d35308e0..71c6d5d6 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -308,7 +308,9 @@ void Game::processCloseContainer(int containerId) { ContainerPtr container = getContainer(containerId); if(!container) { - g_logger.traceError("container not found"); + /* happens if you close and restart client with container opened + * g_logger.traceError("container not found"); + */ return; } @@ -338,7 +340,7 @@ void Game::processContainerUpdateItem(int containerId, int slot, const ItemPtr& container->onUpdateItem(slot, item); } -void Game::processContainerRemoveItem(int containerId, int slot) +void Game::processContainerRemoveItem(int containerId, int slot, const ItemPtr& lastItem) { ContainerPtr container = getContainer(containerId); if(!container) { @@ -346,7 +348,7 @@ void Game::processContainerRemoveItem(int containerId, int slot) return; } - container->onRemoveItem(slot); + container->onRemoveItem(slot, lastItem); } void Game::processInventoryChange(int slot, const ItemPtr& item) @@ -1404,6 +1406,20 @@ void Game::answerModalDialog(int dialog, int button, int choice) m_protocolGame->sendAnswerModalDialog(dialog, button, choice); } +void Game::browseField(const Position& position) +{ + if(!canPerformGameAction()) + return; + m_protocolGame->sendBrowseField(position); +} + +void Game::seekInContainer(int cid, int index) +{ + if(!canPerformGameAction()) + return; + m_protocolGame->sendSeekInContainer(cid, index); +} + void Game::ping() { if(!m_protocolGame || !m_protocolGame->isConnected()) @@ -1558,6 +1574,10 @@ void Game::setProtocolVersion(int version) enableFeature(Otc::GameThingMarks); } + if(version >= 984) { + enableFeature(Otc::GameBrowseField); + } + if(version >= 1000) { enableFeature(Otc::GamePVPMode); } diff --git a/src/client/game.h b/src/client/game.h index 7fdf582f..4cb35184 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -85,7 +85,7 @@ protected: void processCloseContainer(int containerId); void processContainerAddItem(int containerId, const ItemPtr& item, int slot); void processContainerUpdateItem(int containerId, int slot, const ItemPtr& item); - void processContainerRemoveItem(int containerId, int slot); + void processContainerRemoveItem(int containerId, int slot, const ItemPtr& lastItem); // channel related void processChannelList(const std::vector >& channelList); @@ -258,6 +258,10 @@ public: // >= 970 modal dialog void answerModalDialog(int dialog, int button, int choice); + // >= 984 browse field + void browseField(const Position& position); + void seekInContainer(int cid, int index); + //void reportRuleViolation2(); void ping(); void setPingDelay(int delay) { m_pingDelay = delay; } diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 012ad72d..ebc29073 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -295,6 +295,8 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "disableFeature", &Game::disableFeature, &g_game); g_lua.bindSingletonFunction("g_game", "isGM", &Game::isGM, &g_game); g_lua.bindSingletonFunction("g_game", "answerModalDialog", &Game::answerModalDialog, &g_game); + g_lua.bindSingletonFunction("g_game", "browseField", &Game::browseField, &g_game); + g_lua.bindSingletonFunction("g_game", "seekInContainer", &Game::seekInContainer, &g_game); g_lua.bindSingletonFunction("g_game", "getLastWalkDir", &Game::getLastWalkDir, &g_game); g_lua.registerSingletonClass("g_shaders"); diff --git a/src/client/protocolcodes.h b/src/client/protocolcodes.h index 848eabee..14a83a4e 100644 --- a/src/client/protocolcodes.h +++ b/src/client/protocolcodes.h @@ -228,6 +228,8 @@ namespace Proto { ClientCancelAttackAndFollow = 190, ClientUpdateTile = 201, ClientRefreshContainer = 202, + ClientBrowseField = 203, + ClientSeekInContainer = 204, ClientRequestOutfit = 210, ClientChangeOutfit = 211, ClientMount = 212, // 870 diff --git a/src/client/protocolgame.h b/src/client/protocolgame.h index f6f4a4df..ccbab188 100644 --- a/src/client/protocolgame.h +++ b/src/client/protocolgame.h @@ -111,6 +111,8 @@ public: void sendNewNewRuleViolation(int reason, int action, const std::string& characterName, const std::string& comment, const std::string& translation); void sendRequestItemInfo(int itemId, int subType, int index); void sendAnswerModalDialog(int dialog, int button, int choice); + void sendBrowseField(const Position& position); + void sendSeekInContainer(int cid, int index); // otclient only void sendChangeMapAwareRange(int xrange, int yrange); diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index fd727f19..a4b84014 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -686,16 +686,17 @@ void ProtocolGame::parseContainerRemoveItem(const InputMessagePtr& msg) { int containerId = msg->getU8(); int slot; + ItemPtr lastItem; if(g_game.getFeature(Otc::GameContainerPagination)) { slot = msg->getU16(); int itemId = msg->getU16(); if(itemId != 0) - getItem(msg, itemId); + lastItem = getItem(msg, itemId); } else { slot = msg->getU8(); } - g_game.processContainerRemoveItem(containerId, slot); + g_game.processContainerRemoveItem(containerId, slot, lastItem); } void ProtocolGame::parseAddInventoryItem(const InputMessagePtr& msg) diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 502eb820..15487b09 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -831,6 +831,29 @@ void ProtocolGame::sendAnswerModalDialog(int dialog, int button, int choice) send(msg); } +void ProtocolGame::sendBrowseField(const Position& position) +{ + if(!g_game.getFeature(Otc::GameBrowseField)) + return; + + OutputMessagePtr msg(new OutputMessage); + msg->addU8(Proto::ClientBrowseField); + addPosition(msg, position); + send(msg); +} + +void ProtocolGame::sendSeekInContainer(int cid, int index) +{ + if(!g_game.getFeature(Otc::GameContainerPagination)) + return; + + OutputMessagePtr msg(new OutputMessage); + msg->addU8(Proto::ClientSeekInContainer); + msg->addU8(cid); + msg->addU16(index); + send(msg); +} + void ProtocolGame::sendChangeMapAwareRange(int xrange, int yrange) { if(!g_game.getFeature(Otc::GameChangeMapAwareRange))