• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[Help] Can't open store in OTClient

vinivst

New Member
Joined
Mar 15, 2012
Messages
16
Reaction score
0
Hello,

I'm trying to get the store module fully functional and already opened an issue at official's github, but since there isn't the place to ask for help i'm making this thread do ask some questions.

My main concern is that i got all the things working separately but i can't put it all together in the right places. So, i got the UI from Margoh's store module system, i got the server side code from Lofiv's distro, and of course the otclient source code from this commit of @TheSumm

Lets get into what i really need to learn:

1) How can i call in store module a function from otclient source that sends to the server side the information that the Store was opened by player and get the provided information back to the otclient by a parse receipt function that will draw the UI with the especified products from a store's xml located into server side data folder?

2) The same parse receipt function needs to get from server the coin balance and shows in the UI.

3) When a coin transfer is called the otclient needs to communicates with the server again to run the logics and send back if it was successful or not.

Basically i need to know how to communicate the otclient with the server using a module that calls functions in otclient source which will call another function in the server side, and the process to get it all back.

Just to get clear, i'm really newbie to otclient and don't know much, but what's getting me intrigued is what's the OPCodes? And, they can help me in anyway?

Hope someone might give me a hand,

Thank you in advance.
 
Code:
storeButton = modules.client_topmenu.addRightGameToggleButton('storeButton', tr('Open Store'), '/images/topbuttons/store', g_game.openStore)

Add that button to your store module ( init() function ) and make sure to destroy it in terminate().
 
I tried this and nothing happened when i click on the button, but when i press ctrl+u the UI is shown but apparently nothing is received from server. I think that is a "toggle" part missing.

Here's my init() function:

function init()
g_ui.importStyle('storeButton')
storeButton = modules.client_topmenu.addRightGameToggleButton('storeButton', tr('Open Store') .. ' (Ctrl+U)', '/images/topbuttons/store', g_game.openStore)
storeButton:setOn(true)
g_ui.importStyle('ui/acceptwindow')

storeWindow = g_ui.displayUI('store_module')
storeWindow:hide()

g_keyboard.bindKeyPress('Ctrl+U', toggle)
ProtocolGame.registerExtendedOpcode(COINS_OPCODE, coinsBalance)
transferPointsWindow = g_ui.displayUI('transferpoints')
indexList = storeWindow:getChildById('indexList')
indexDescription = storeWindow:getChildById('indexDescription')
imageDesc = storeWindow:getChildById('indexDescription'):getChildById('imageDesc')
titleDesc = storeWindow:getChildById('indexDescription'):getChildById('titleDesc')
description = storeWindow:getChildById('indexDescription'):getChildById('description')
productList = storeWindow:getChildById('productList')
for i = 1, #storeIndex do
local label = g_ui.createWidget('StoreButton', indexList)

label:setId(i)
label:setIcon(storeIndex.imageList)
label:setText(tr(storeIndex.name))
label.index = i

local labelId = storeWindow:getChildById('indexList'):getChildById(i)
labelId.onClick = function(self)
local descriptionImage = storeWindow:getChildById('indexDescription'):getChildById('imageDesc')
descriptionImage:setImageSource(storeIndex.image)

local descriptionTitle = storeWindow:getChildById('indexDescription'):getChildById('titleDesc')
descriptionTitle:setText(storeIndex.name)

local description = storeWindow:getChildById('indexDescription'):getChildById('description')
description:setText(storeIndex.description)

local productsPanel = storeWindow:getChildById('productList')
local children = productsPanel:getChildren()
for k = 1, #children do
children[k]:destroy()
end

for j=1, #storeProducts do
if storeIndex.id == storeProducts[j].category_id then
local productLabel = g_ui.createWidget('ProductButton', productList)


productLabel:setId(j)
productLabel:setTooltip(storeProducts[j].tooltip)

local productLabelTitle = storeWindow:getChildById('productList'):getChildById(j):getChildById('productLabelTitle')
productLabelTitle:setText(short(storeProducts[j].name))

local productLabelImage = storeWindow:getChildById('productList'):getChildById(j):getChildById('productLabelImage')
productLabelImage:setImageSource(storeProducts[j].image)

local productLabelTokenPrice = storeWindow:getChildById('productList'):getChildById(j):getChildById('productLabelTokenPrice')
productLabelTokenPrice:setText(formatNumbers(storeProducts[j].price))

local buyWindow = storeWindow:getChildById('productList'):getChildById(j)
buyWindow.onClick = function(self)
if acceptWindow then
return true
end

local acceptFunc = function()
local balance = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
local unformatted = balance:gsub(',', '')
local balanceInfo = tonumber(unformatted)
if balanceInfo >= storeProducts[j].price then
g_game.talk(COMMAND_BUYITEM .. ' ' .. storeIndex.id .. ', ' .. storeProducts[j].id)
acceptWindow:destroy()
acceptWindow = nil

local balance = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
local unformatted = balance:gsub(',', '')
local balanceInfo = tonumber(unformatted)
local balanceAfterPurchase = balanceInfo - storeProducts[j].price

local balanceLabel = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance')
balanceLabel:setText((formatNumbers(balanceAfterPurchase)))
else
io.popen('start ' .. WEBSITE_GETCOINS)
acceptWindow:destroy()
acceptWindow = nil
end
end
local cancelFunc = function() acceptWindow:destroy() acceptWindow = nil end

transferPointsWindow:setVisible(false)

acceptWindow = displayAcceptBox(tr('Buy ' .. storeProducts[j].name), tr(storeProducts[j].description),
{ { text=tr('Buy'), callback=acceptFunc },
{ text=tr('Cancel'), callback=cancelFunc },
anchor=AnchorHorizontalCenter }, acceptFunc, cancelFunc)
return true
end
end
end
end
end

connect(g_game, {
onGameStart = refresh,
onGameEnd = offline
})
refresh()
end


Also i know that store module gets the products from configs.lua and builds in this part:

function onOpenStore()
storeWindow:show()
storeWindow:raise()
storeWindow:focus()
local descriptionImage = storeWindow:getChildById('indexDescription'):getChildById('imageDesc')
descriptionImage:setImageSource(storeIndex[1].image)

local descriptionTitle = storeWindow:getChildById('indexDescription'):getChildById('titleDesc')
descriptionTitle:setText(storeIndex[1].name)

local description = storeWindow:getChildById('indexDescription'):getChildById('description')
description:setText(storeIndex[1].description)

local productsPanel = storeWindow:getChildById('productList')
local children = productsPanel:getChildren()
for k = 1, #children do
children[k]:destroy()
end

for j=1, #storeProducts do
if storeIndex[1].id == storeProducts[j].category_id then
local productLabel = g_ui.createWidget('ProductButton', productList)

productLabel:setId(j)
productLabel:setTooltip(storeProducts[j].tooltip)
local productLabelTitle = storeWindow:getChildById('productList'):getChildById(j):getChildById('productLabelTitle')
productLabelTitle:setText(short(storeProducts[j].name))

local productLabelImage = storeWindow:getChildById('productList'):getChildById(j):getChildById('productLabelImage')
productLabelImage:setImageSource(storeProducts[j].image)

local productLabelTokenPrice = storeWindow:getChildById('productList'):getChildById(j):getChildById('productLabelTokenPrice')
productLabelTokenPrice:setText(formatNumbers(storeProducts[j].price))

But i don't want it did here since the server side already do this getting via a xml file on server/data/store. How can i get this from server to populate here?

There's the functions that handles the balance and the delivery of the product too:

local buyWindow = storeWindow:getChildById('productList'):getChildById(j)
buyWindow.onClick = function(self)
if acceptWindow then
return true
end

local acceptFunc = function()
local balance = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
local unformatted = balance:gsub(',', '')
local balanceInfo = tonumber(unformatted)
if balanceInfo >= storeProducts[j].price then
g_game.talk(COMMAND_BUYITEM .. ' ' .. storeIndex[1].id .. ', ' .. storeProducts[j].id)
acceptWindow:destroy()
acceptWindow = nil

local balance = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
local unformatted = balance:gsub(',', '')
local balanceInfo = tonumber(unformatted)
local balanceAfterPurchase = balanceInfo - storeProducts[j].price

local balanceLabel = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance')
balanceLabel:setText((formatNumbers(balanceAfterPurchase)))
else
io.popen('start ' .. WEBSITE_GETCOINS)
acceptWindow:destroy()
acceptWindow = nil
end
end
 
And finally the functions that handles the transfer of the coins:

function transferPoints()
local value = transferPointsWindow:getChildById('transferPointsValue')
value:setText(tr('0'))
local balanceInfo = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
local balance = transferPointsWindow:getChildById('coinBalance2')
balance:setText(formatNumbers(balanceInfo))
transferPointsWindow:setVisible(true)
transferPointsWindow:focus()
transferPointsWindow:raise()
acceptWindow:destroy()
acceptWindow = nil
end

function transferAccept()
local nickname = transferPointsWindow:getChildById('transferPointsText'):getText()
local value = transferPointsWindow:getChildById('transferPointsValue'):getText()
g_game.talk(COMMAND_TRANSFER .. ' ' .. nickname .. ', '.. value)
local balance = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
local unformatted = balance:gsub(',', '')
local balanceInfo = tonumber(unformatted)
local getValue = tonumber(value)
local balanceAfterSend = balanceInfo - getValue
local balanceLabel = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance')
balanceLabel:setText((formatNumbers(balanceAfterSend)))
transferPointsWindow:setVisible(false)
end

function transferCancel()
transferPointsWindow:setVisible(false)
end

As you can see, i don't want those things here, i just want functions that calls the already done codes in both otclient sources and server side sources. I want that the store_module.lua just handles the communicate between both. Is that possible? How?

PS: Sorry for the double post, but i just can't post in one single post because it exceed the 10000 characters limit.
 
You know that module I just made for older server versions (below version where store got implemented) where TibiaStore does not exist? It has nothing to do with changes provided by Summ and that of Lofi's distro. You need to make a module for that as it does not exist.
 
Hello Margoh,

Yeah, i know that, i'm just using your super beautiful UI to my purposes. :D

By the way, can you help me doing that module?

It just needs to be a module of handle functions that communicates the otclient and the server.

You got any clues?

EDIT: I'm really trying to do that but untill now have no success.

Here's my store_module.lua now:

Code:
dofile('ui/uistoreacceptbox')

local storeWindow = nil
local indexDescription = nil
local indexList = nil
local imageDesc = nil
local titleDesc = nil
local description = nil
local acceptWindow = nil
local transferPointsWindow = nil

local function short(value)
  local text = value
  if string.len(text) >= 13 then
    shorten = string.sub(text, 1, 10)
    return shorten .. "..."
  else
    return text
  end
end

local function formatNumbers(number)
    local ret = number
    while true do 
        ret, k = string.gsub(ret, "^(-?%d+)(%d%d%d)", '%1,%2')
        if k == 0 then
            break
        end
    end
    return ret
end

function init()
  g_ui.importStyle('storeButton')
  storeButton = modules.client_topmenu.addRightGameToggleButton('storeButton', tr('Open Store') .. ' (Ctrl+U)', '/images/topbuttons/store', toggle)
  g_game.openStore()
  teste = g_game.requestStoreOffers(categoryName)
  print(teste)
  storeButton:setOn(true)
  g_ui.importStyle('ui/acceptwindow')

  storeWindow = g_ui.displayUI('store_module')
  storeWindow:hide()

  g_keyboard.bindKeyPress('Ctrl+U', toggle)
 
  transferPointsWindow = g_ui.displayUI('transferpoints')
 
  indexList = storeWindow:getChildById('indexList')
  indexDescription = storeWindow:getChildById('indexDescription')
  imageDesc = storeWindow:getChildById('indexDescription'):getChildById('imageDesc')
  titleDesc = storeWindow:getChildById('indexDescription'):getChildById('titleDesc')
  description = storeWindow:getChildById('indexDescription'):getChildById('description')
 
  productList = storeWindow:getChildById('productList')

  connect(g_game, {
    onGameStart = refresh,
    onGameEnd = offline
  })
 
  refresh()
 
end

function terminate()
  storeWindow:hide()
  storeButton:destroy()
  g_keyboard.unbindKeyPress('Ctrl+U')
 
  disconnect(g_game, {
    onGameStart = refresh,
    onGameEnd = offline
  })

end

function coinsBalance(protocol, opcode, buffer)
    local balanceLabel = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance')
    balanceLabel:setText(formatNumbers(buffer))
end

function refresh()
    local player = g_game.getLocalPlayer()
    if not player then
        return
    end
end

function offline()
  storeWindow:hide()
end

function toggle()
  if storeWindow:isVisible() then
    onCloseStore()
  else
    onOpenStore()
  end
end

function onOpenStore()
  storeWindow:show()
  storeWindow:raise()
  storeWindow:focus()
 
  local descriptionImage = storeWindow:getChildById('indexDescription'):getChildById('imageDesc')
  descriptionImage:setImageSource(storeIndex[1].image)
   
  local descriptionTitle = storeWindow:getChildById('indexDescription'):getChildById('titleDesc')
  descriptionTitle:setText(storeIndex[1].name)
   
  local description = storeWindow:getChildById('indexDescription'):getChildById('description')
  description:setText(storeIndex[1].description)

  local productsPanel = storeWindow:getChildById('productList')
  local children = productsPanel:getChildren()
 
        local cancelFunc = function() acceptWindow:destroy() acceptWindow = nil end
       
        transferPointsWindow:setVisible(false)
        return true
      end

function onCloseStore()
  storeWindow:hide()
  transferPointsWindow:setVisible(false)
  if acceptWindow then
    acceptWindow:destroy()
    acceptWindow = nil
  end
end

function transferPoints()
  local value = transferPointsWindow:getChildById('transferPointsValue')
  value:setText(tr('0'))
 
  local balanceInfo = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
  local balance = transferPointsWindow:getChildById('coinBalance2')
  balance:setText(formatNumbers(balanceInfo))
  transferPointsWindow:setVisible(true)
  transferPointsWindow:focus()
  transferPointsWindow:raise()
 
  acceptWindow:destroy()
  acceptWindow = nil
end

function transferAccept()
  local nickname = transferPointsWindow:getChildById('transferPointsText'):getText()
  local value = transferPointsWindow:getChildById('transferPointsValue'):getText()
  g_game.talk(COMMAND_TRANSFER .. ' ' .. nickname .. ', '.. value)
 
  local balance = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance'):getText()
  local unformatted = balance:gsub(',', '')
  local balanceInfo = tonumber(unformatted)
  local getValue = tonumber(value)
  local balanceAfterSend = balanceInfo - getValue
 
  local balanceLabel = storeWindow:getChildById('balanceInfo'):getChildById('coinBalance')
  balanceLabel:setText((formatNumbers(balanceAfterSend)))
  transferPointsWindow:setVisible(false)
end

function transferCancel()
  transferPointsWindow:setVisible(false)
end

function getCoins()
io.popen('start ' .. WEBSITE_GETCOINS)
end

So, what am i trying to do?

I'm trying to make the store_module on init receive from the server the categories and products that are stored on data/store/store.xml with the g_game.requestStoreOffers(categoryName) function. And also, get the coins balance from server but i don't have found a function for that in luafunctions.cpp.

After that, when player clicks on the button the module sends the openStore() function to server and if everything is ok callback to the otclient draws the UI with the categories and products that are already stored.

When player buys something the otclient haves to send the sendBuyStoreOffer() function to the server handles it.

And finally, the transfer coins that will call sendTransferCoins() and the server will also handle it.

Besides the history that i guess should uses sendOpenTransactionHistory() and sendRequestTransactionHistory() functions.

This is what i have in my mind, but i'm not having success to put this on paper.
 
Last edited:
Back
Top