• 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!

[TFS 1.3] Mini Shop with command or modalWindow

Sarah Wesker

ƐƖєgαηт Sуηтαx ❤
Staff member
TFS Developer
Support Team
Joined
Mar 16, 2017
Messages
1,408
Solutions
154
Reaction score
1,958
Location
London
GitHub
MillhioreBT
Twitch
millhiorebt
Extra: Most updated version with new features
shop.gif

Note: Versión custom (Use the Extra)

A small store system with command but that also works with windows.
If you want to open the modalWindow you just have to type: !shop list
If you want to buy with just the command you must type: !shop <nombre del articulo>

The modalWindow will only show 255 items, I haven't added a page system yet, but I think 255 is more than enough.

Lua:
local shop = {}
local modalWindow = ModalWindow(99999, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Close")
modalWindow:setDefaultEscapeButton(2)
modalWindow:setDefaultEnterButton(2)

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000 },
    ["Demon Armor"] = { id=2494, price=3000000 },
    ["Demon Legs"] = { id=2495, price=3000000 },
    ["Banana"] = { id=2676, price=3000000, count=100 } -- item stackable

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("[ %s x%d ]: %d gold coins.", lowerCaseName, math.min(item.count and item.count or 1, 100), item.price))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item witi name %s not found!", param))
        return false
    end

    local money = player:getMoney()
    local bankBalance = player:getBankBalance()
    local totalMoney = money + bankBalance
    if totalMoney < item.price then
        player:sendCancelMessage(string.format("You don't have enough gold coins, You need %d gold coins.", item.price))
        return false
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    local resultMoney = money - item.price
    if resultMoney < 0 then
        player:removeMoney(money)
        player:setBankBalance(bankBalance + resultMoney)
    else
        player:removeMoney(item.price)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%s x%d).\nYou now have %d gold coins and %d on the bank balance.", buyedItem:getName(), buyedItem:getCount(), money, player:getBankBalance()))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        modalWindow:sendToPlayer(player)
        return false
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == 99999 then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
 
Last edited:
A small store system with command but that also works with windows.
If you want to open the modalWindow you just have to type: !shop list
If you want to buy with just the command you must type: !shop <nombre del articulo>

The modalWindow will only show 255 items, I haven't added a page system yet, but I think 255 is more than enough.

Lua:
local shop = {}
local modalWindow = ModalWindow(99999, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Close")
modalWindow:setDefaultEscapeButton(2)
modalWindow:setDefaultEnterButton(2)

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000 },
    ["Demon Armor"] = { id=2494, price=3000000 },
    ["Demon Legs"] = { id=2495, price=3000000 },
    ["Banana"] = { id=2676, price=3000000, count=100 } -- item stackable

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("[ %s x%d ]: %d gold coins.", lowerCaseName, math.min(item.count and item.count or 1, 100), item.price))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item witi name %s not found!", param))
        return false
    end

    local money = player:getMoney()
    local bankBalance = player:getBankBalance()
    local totalMoney = money + bankBalance
    if totalMoney < item.price then
        player:sendCancelMessage(string.format("You don't have enough gold coins, You need %d gold coins.", item.price))
        return false
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    local resultMoney = money - item.price
    if resultMoney < 0 then
        player:removeMoney(money)
        player:setBankBalance(bankBalance + resultMoney)
    else
        player:removeMoney(item.price)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%s x%d).\nYou now have %d gold coins and %d on the bank balance.", buyedItem:getName(), buyedItem:getCount(), money, player:getBankBalance()))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        modalWindow:sendToPlayer(player)
        return false
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == 99999 then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
Hello, I hope you are well, it is possible to make it be purchased with a specific item, example that players instead of paying with crystal coins pay with 2145
 
@tutbarao
Add this to the end of the file, and then add the Action ID 65100 to the object you want to use in the map editor

Lua:
local action = Action()

function action.onUse(player, item, fromPos, target, toPos, isHotkey)
    modalWindow:sendToPlayer(player)
    return true
end

action:aid(65100)
action:register()
 
Last edited by a moderator:
@Sarah Wesker
Thank you for your help. Can you help me again?

How to use an item that the player loads in the Backpack and when he clicks open the store and sell his items?
 
If you want a particular item to have the functionality to open this store so use: action:id(ITEM_ID_HERE) instead of action:aid(65100)
 
Thank you again!

1-What if I want to sell items instead of buying?

2-How to make the player need such a QUEST ID to use the item? If he doesn't have the QUEST ID, it emits a message "you need to complete the quest to use the item"
 
Thank you again!

1-What if I want to sell items instead of buying?

2-How to make the player need such a QUEST ID to use the item? If he doesn't have the QUEST ID, it emits a message "you need to complete the quest to use the item"
Extra: Most updated version with new features

Here is a modified version that you may find useful
data/scripts/file.lua
Lua:
local openShop_With_ActionID = nil
local openShop_With_ItemID = nil
local openShop_StorageID = nil
local openShop_StorageValue = nil
local openShop_MessageFailStorage = "nil"
local mOdAlWiNdOw_iD = 99999 -- Try to keep this ID unique for this window
local shop = {}
local modalWindow = ModalWindow(mOdAlWiNdOw_iD, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Sell")
modalWindow:addButton(3, "Close")
modalWindow:setDefaultEscapeButton(3)
modalWindow:setDefaultEnterButton(3)

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000, sell=1500000 },
    ["Demon Armor"] = { id=2494, price=3000000, sell=1500000 },
    ["Demon Legs"] = { id=2495, price=3000000, sell=1500000 },
    ["Banana"] = { id=2676, price=3000000, sell=1500000, count=100 } -- item stackable

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("[ %s x%d ]: %d gold coins.", lowerCaseName, math.min(item.count and item.count or 1, 100), item.price))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", param))
        return false
    end

    local money = player:getMoney()
    local bankBalance = player:getBankBalance()
    local totalMoney = money + bankBalance
    if totalMoney < item.price then
        player:sendCancelMessage(string.format("You don't have enough gold coins, You need %d gold coins.", item.price))
        return false
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    local resultMoney = money - item.price
    if resultMoney < 0 then
        player:removeMoney(money)
        player:setBankBalance(bankBalance + resultMoney)
    else
        player:removeMoney(item.price)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%s x%d).\nYou now have %d gold coins and %d on the bank balance.", buyedItem:getName(), buyedItem:getCount(), money, player:getBankBalance()))
    return false
end

local function sellItem(player, itemName)
    if not itemName then
        return false
    end

    local item = shop[itemName:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", itemName))
        return false
    end

    local itemCount = item.count or 1
    if not player:removeItem(item.id, itemCount) then
        player:sendCancelMessage(string.format("You do not have %d %s in your inventory.", itemCount, itemName))
        return false
    end

    player:setBankBalance(player:getBankBalance() + item.sell)
    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your sale was successful! (%s x%d).\nYou now have %d gold coins in your bank balance.", itemName, itemCount or 1, player:getBankBalance()))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return false
    elseif param == "sell" then
        return sellItem(player, param:splitTrimmed(",")[2])
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

if openShop_With_ActionID or openShop_With_ItemID then
    local action = Action()
    function action.onUse(player, item, fromPos, target, toPos, isHotkey)
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return true
    end
    if openShop_With_ItemID then action:id(openShop_With_ItemID) end
    if openShop_With_ActionID then action:aid(openShop_With_ActionID) end
    action:register()
end

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == mOdAlWiNdOw_iD then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        elseif buttonId == 2 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            sellItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
New command added: sell example of use: !shop sell,demon helmet
In the window you will also find a button to sell
 
Last edited:
OO great. thank you

Can you ask me one last question? I didn't find it anywhere

I am using the latest version of Otservbr, and I am looking to use the tournament coins but I don't know how to do it, I would like to make the players win "X" tournament coins every "X" time.
Post automatically merged:

Here is a modified version that you may find useful
data/scripts/file.lua
Lua:
local openShop_With_ActionID = nil
local openShop_With_ItemID = nil
local openShop_StorageID = nil
local openShop_StorageValue = nil
local openShop_MessageFailStorage = "nil"
local mOdAlWiNdOw_iD = 99999 -- Try to keep this ID unique for this window
local shop = {}
local modalWindow = ModalWindow(mOdAlWiNdOw_iD, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Sell")
modalWindow:addButton(3, "Close")
modalWindow:setDefaultEscapeButton(3)
modalWindow:setDefaultEnterButton(3)

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000, sell=1500000 },
    ["Demon Armor"] = { id=2494, price=3000000, sell=1500000 },
    ["Demon Legs"] = { id=2495, price=3000000, sell=1500000 },
    ["Banana"] = { id=2676, price=3000000, sell=1500000, count=100 } -- item stackable

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("[ %s x%d ]: %d gold coins.", lowerCaseName, math.min(item.count and item.count or 1, 100), item.price))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", param))
        return false
    end

    local money = player:getMoney()
    local bankBalance = player:getBankBalance()
    local totalMoney = money + bankBalance
    if totalMoney < item.price then
        player:sendCancelMessage(string.format("You don't have enough gold coins, You need %d gold coins.", item.price))
        return false
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    local resultMoney = money - item.price
    if resultMoney < 0 then
        player:removeMoney(money)
        player:setBankBalance(bankBalance + resultMoney)
    else
        player:removeMoney(item.price)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%s x%d).\nYou now have %d gold coins and %d on the bank balance.", buyedItem:getName(), buyedItem:getCount(), money, player:getBankBalance()))
    return false
end

local function sellItem(player, itemName)
    if not itemName then
        return false
    end

    local item = shop[itemName:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", itemName))
        return false
    end

    local itemCount = item.count or 1
    if not player:removeItem(item.id, itemCount) then
        player:sendCancelMessage(string.format("You do not have %d %s in your inventory.", itemCount, itemName))
        return false
    end

    player:setBankBalance(player:getBankBalance() + item.sell)
    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your sale was successful! (%s x%d).\nYou now have %d gold coins in your bank balance.", itemName, itemCount or 1, player:getBankBalance()))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return false
    elseif param == "sell" then
        return sellItem(player, param:splitTrimmed(",")[2])
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

if openShop_With_ActionID or openShop_With_ItemID then
    local action = Action()
    function action.onUse(player, item, fromPos, target, toPos, isHotkey)
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return true
    end
    if openShop_With_ItemID then action:id(openShop_With_ItemID) end
    if openShop_With_ActionID then action:aid(openShop_With_ActionID) end
    action:register()
end

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == mOdAlWiNdOw_iD then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        elseif buttonId == 2 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            sellItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
New command added: sell example of use: !shop sell,demon helmet
In the window you will also find a button to sell
It is displaying this error in the distro when making the purchase and sale of the item. And also when selling the sale is carried out, but the gold is not delivered. After trying to buy the item a second time, the server crashed with no error messages in the distro

[error] [ProtocolGame::sendTextMessage] - Message type is wrong, missing or invalid for player with name GOD, on position ( 32360 / 32223 / 7 )
 
Last edited:
I don't know what distribution you are actually using but this is for TFS 1.3+
anyway you can try changing this: MESSAGE_STATUS_CONSOLE_BLUE to this: MESSAGE_INFO_DESCR
if you don't want to change this then you can also add this MESSAGE_STATUS_CONSOLE_BLUE = MESSAGE_INFO_DESCR in compat.lua

next time don't forget to mention your version and engine, so people can help you fast.
1654025151128.png
 
I don't know what distribution you are actually using but this is for TFS 1.3+
anyway you can try changing this: MESSAGE_STATUS_CONSOLE_BLUE to this: MESSAGE_INFO_DESCR
if you don't want to change this then you can also add this MESSAGE_STATUS_CONSOLE_BLUE = MESSAGE_INFO_DESCR in compat.lua

next time don't forget to mention your version and engine, so people can help you fast.
View attachment 68231
OO great. thank you
I'm using canary otservbr 1.3.1

Can you ask me one last question? I didn't find it anywhere

I am using the latest version of Otservbr, and I am looking to use the tournament coins but I don't know how to do it, I would like to make the players win "X" tournament coins every "X" time.
 
I don't know what distribution you are actually using but this is for TFS 1.3+
anyway you can try changing this: MESSAGE_STATUS_CONSOLE_BLUE to this: MESSAGE_INFO_DESCR
if you don't want to change this then you can also add this MESSAGE_STATUS_CONSOLE_BLUE = MESSAGE_INFO_DESCR in compat.lua

next time don't forget to mention your version and engine, so people can help you fast.
View attachment 68231
Is it possible that instead of using crystal coins I use another coin or item? to buy
 
How can I use it and that the cost is charged for the value of a storage? in my case storage 43000, for example I want storage 43000 to have a value of 1000 and that x amount of value be deducted from that storage.
 
This is a modified version to add two new features requested by @VagosClubTM and @nefinoo
Also add some small changes in the way of displaying prices with gold coins.
data/scripts/file.lua
Lua:
local openShop_With_ActionID = nil
local openShop_With_ItemID = nil
local openShop_StorageID = nil
local openShop_StorageValue = nil
local openShop_MessageFailStorage = "nil"
local mOdAlWiNdOw_iD = 99999 -- Try to keep this ID unique for this window
local shop = {}
local modalWindow = ModalWindow(mOdAlWiNdOw_iD, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Sell")
modalWindow:addButton(3, "Close")
modalWindow:setDefaultEscapeButton(3)
modalWindow:setDefaultEnterButton(3)

local function getMoneyString(gp)
    if gp < 1000 then
        return string.format("%dgp", gp)
    elseif gp < 1000000 then
        local k = gp / 1000
        gp = gp % 1000
        return string.format("%dk%s", k, (gp > 0 and string.format(" %dgp", gp) or ""))
    end
    local kk = gp / 1000000
    gp = gp % 1000000
    local k = gp / 1000
    gp = gp % 1000
    return string.format("%dkk%s", kk, (k > 0 and string.format(" %dk%s", k, (gp > 0 and string.format(" %dgp", gp) or "")) or (gp > 0 and string.format(" %dgp", gp) or "")))
end

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000, sell=1500000 },
    ["Demon Armor"] = { id=2494, price=3000000, sell=1500000 },
    ["Demon Legs"] = { id=2495, price=3000000, sell=1500000 },
    ["Banana"] = { id=2676, price=3000000, sell=1500000, count=100 },
    ["Pear"] = { id=2673, price=20, sell=15, count=25, currency=2674 },
    ["Blood Herb"] = { id=2798, price=80, sell=40, count=6, storagePay=78545, moneyName="$dollar" }

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("%d %s %s %s", math.min(item.count and item.count or 1, 100), lowerCaseName, string.char(215), (item.storagePay and string.format("%d %s", item.price, item.moneyName) or (item.currency and string.format("%d %s(s)", item.price, ItemType(item.currency):getName()) or getMoneyString(item.price))) ))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", param))
        return false
    end

    local money = 0
    if item.storagePay then
        money = player:getStorageValue(item.storagePay)
        if money < item.price then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You don't have enough %s, You need %d %s.", item.moneyName, item.price, item.moneyName))
            return false
        end
    elseif item.currency then
        money = player:getItemCount(item.currency)
        if money < item.price then
            local moneyName = ItemType(item.currency):getName()
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You don't have enough %s, You need %d %s.", moneyName, item.price, moneyName))
            return false
        end
    else
        money = player:getMoney()
        local bankBalance = player:getBankBalance()
        local totalMoney = money + bankBalance
        if totalMoney < item.price then
            player:sendCancelMessage(string.format("You don't have enough gold coins, You need %s", getMoneyString(item.price)))
            return false
        end
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    if item.storagePay then
        player:setStorageValue(item.storagePay, money - item.price)
    elseif item.currency then
        player:removeItem(item.currency, item.price)
    else
        local resultMoney = money - item.price
        if resultMoney < 0 then
            player:removeMoney(money)
            player:setBankBalance(bankBalance + resultMoney)
        else
            player:removeMoney(item.price)
        end
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%d %s).\n%s", math.min(item.count and item.count or 1, 100), buyedItem:getName(), (item.storagePay and string.format("You pay with %d %s.", item.price, item.moneyName) or (item.currency and string.format("You pay with %d %s.", item.price, ItemType(item.currency):getName()) or string.format("You pay with %s.", getMoneyString(item.price))))))
    return false
end

local function sellItem(player, itemName)
    if not itemName then
        return false
    end

    local item = shop[itemName:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", itemName))
        return false
    end

    local itemCount = item.count or 1
    if not player:removeItem(item.id, itemCount) then
        player:sendCancelMessage(string.format("You do not have %d %s in your inventory.", itemCount, itemName))
        return false
    end

    if item.storagePay then
        player:setStorageValue(item.storagePay, player:getStorageValue(item.storagePay) + item.sell)
    elseif item.currency then
        player:addItem(item.currency, item.sell)
    else
        player:setBankBalance(player:getBankBalance() + item.sell)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your sale was successful! (%d %s).\n%s", math.min(item.count and item.count or 1, 100), itemName, (item.storagePay and string.format("You receive %d %s.", item.sell, item.moneyName) or (item.currency and string.format("You receive %d %s.", item.sell, ItemType(item.currency):getName()) or string.format("You receive %s to your bank balance.", getMoneyString(item.sell))))))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return false
    elseif param == "sell" then
        return sellItem(player, param:splitTrimmed(",")[2])
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

if openShop_With_ActionID or openShop_With_ItemID then
    local action = Action()
    function action.onUse(player, item, fromPos, target, toPos, isHotkey)
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return true
    end
    if openShop_With_ItemID then action:id(openShop_With_ItemID) end
    if openShop_With_ActionID then action:aid(openShop_With_ActionID) end
    action:register()
end

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == mOdAlWiNdOw_iD then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        elseif buttonId == 2 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            sellItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
Items that are paid for with other types of physical currency in the game? (Yes is possible!)
Example:
Lua:
["Pear"] = { id=2673, price=20, sell=15, count=25, currency=2674 },
-- You can buy 25 pear x 20 red apple
-- You can sell 25 pear x 15 red apple

Items that are paid with virtual currencies? (Yes, it is possible to pay with storages)
Example:
Lua:
["Blood Herb"] = { id=2798, price=80, sell=40, count=6, storagePay=78545, moneyName="$dollar" }
-- You can buy 6 blood herb x 80 $dollar
-- You can sell 6 blood herb x 40 $dollar
Note: The name of the virtual currency is declared on each item separately regardless of whether it is the same storage.
example: , moneyName="$dollar" }

1660897358829.png
 
This is a modified version to add two new features requested by @VagosClubTM and @nefinoo
Also add some small changes in the way of displaying prices with gold coins.
data/scripts/file.lua
Lua:
local openShop_With_ActionID = nil
local openShop_With_ItemID = nil
local openShop_StorageID = nil
local openShop_StorageValue = nil
local openShop_MessageFailStorage = "nil"
local mOdAlWiNdOw_iD = 99999 -- Try to keep this ID unique for this window
local shop = {}
local modalWindow = ModalWindow(mOdAlWiNdOw_iD, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Sell")
modalWindow:addButton(3, "Close")
modalWindow:setDefaultEscapeButton(3)
modalWindow:setDefaultEnterButton(3)

local function getMoneyString(gp)
    if gp < 1000 then
        return string.format("%dgp", gp)
    elseif gp < 1000000 then
        local k = gp / 1000
        gp = gp % 1000
        return string.format("%dk%s", k, (gp > 0 and string.format(" %dgp", gp) or ""))
    end
    local kk = gp / 1000000
    gp = gp % 1000000
    local k = gp / 1000
    gp = gp % 1000
    return string.format("%dkk%s", kk, (k > 0 and string.format(" %dk%s", k, (gp > 0 and string.format(" %dgp", gp) or "")) or (gp > 0 and string.format(" %dgp", gp) or "")))
end

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000, sell=1500000 },
    ["Demon Armor"] = { id=2494, price=3000000, sell=1500000 },
    ["Demon Legs"] = { id=2495, price=3000000, sell=1500000 },
    ["Banana"] = { id=2676, price=3000000, sell=1500000, count=100 },
    ["Pear"] = { id=2673, price=20, sell=15, count=25, currency=2674 },
    ["Blood Herb"] = { id=2798, price=80, sell=40, count=6, storagePay=78545, moneyName="$dollar" }

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("%d %s %s %s", math.min(item.count and item.count or 1, 100), lowerCaseName, string.char(215), (item.storagePay and string.format("%d %s", item.price, item.moneyName) or (item.currency and string.format("%d %s(s)", item.price, ItemType(item.currency):getName()) or getMoneyString(item.price))) ))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", param))
        return false
    end

    local money = 0
    if item.storagePay then
        money = player:getStorageValue(item.storagePay)
        if money < item.price then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You don't have enough %s, You need %d %s.", item.moneyName, item.price, item.moneyName))
            return false
        end
    elseif item.currency then
        money = player:getItemCount(item.currency)
        if money < item.price then
            local moneyName = ItemType(item.currency):getName()
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You don't have enough %s, You need %d %s.", moneyName, item.price, moneyName))
            return false
        end
    else
        money = player:getMoney()
        local bankBalance = player:getBankBalance()
        local totalMoney = money + bankBalance
        if totalMoney < item.price then
            player:sendCancelMessage(string.format("You don't have enough gold coins, You need %s", getMoneyString(item.price)))
            return false
        end
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    if item.storagePay then
        player:setStorageValue(item.storagePay, money - item.price)
    elseif item.currency then
        player:removeItem(item.currency, item.price)
    else
        local resultMoney = money - item.price
        if resultMoney < 0 then
            player:removeMoney(money)
            player:setBankBalance(bankBalance + resultMoney)
        else
            player:removeMoney(item.price)
        end
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%d %s).\n%s", math.min(item.count and item.count or 1, 100), buyedItem:getName(), (item.storagePay and string.format("You pay with %d %s.", item.price, item.moneyName) or (item.currency and string.format("You pay with %d %s.", item.price, ItemType(item.currency):getName()) or string.format("You pay with %s.", getMoneyString(item.price))))))
    return false
end

local function sellItem(player, itemName)
    if not itemName then
        return false
    end

    local item = shop[itemName:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", itemName))
        return false
    end

    local itemCount = item.count or 1
    if not player:removeItem(item.id, itemCount) then
        player:sendCancelMessage(string.format("You do not have %d %s in your inventory.", itemCount, itemName))
        return false
    end

    if item.storagePay then
        player:setStorageValue(item.storagePay, player:getStorageValue(item.storagePay) + item.sell)
    elseif item.currency then
        player:addItem(item.currency, item.sell)
    else
        player:setBankBalance(player:getBankBalance() + item.sell)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your sale was successful! (%d %s).\n%s", math.min(item.count and item.count or 1, 100), itemName, (item.storagePay and string.format("You receive %d %s.", item.sell, item.moneyName) or (item.currency and string.format("You receive %d %s.", item.sell, ItemType(item.currency):getName()) or string.format("You receive %s to your bank balance.", getMoneyString(item.sell))))))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return false
    elseif param == "sell" then
        return sellItem(player, param:splitTrimmed(",")[2])
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

if openShop_With_ActionID or openShop_With_ItemID then
    local action = Action()
    function action.onUse(player, item, fromPos, target, toPos, isHotkey)
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return true
    end
    if openShop_With_ItemID then action:id(openShop_With_ItemID) end
    if openShop_With_ActionID then action:aid(openShop_With_ActionID) end
    action:register()
end

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == mOdAlWiNdOw_iD then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        elseif buttonId == 2 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            sellItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
Items that are paid for with other types of physical currency in the game? (Yes is possible!)
Example:
Lua:
["Pear"] = { id=2673, price=20, sell=15, count=25, currency=2674 },
-- You can buy 25 pear x 20 red apple
-- You can sell 25 pear x 15 red apple

Items that are paid with virtual currencies? (Yes, it is possible to pay with storages)
Example:
Lua:
["Blood Herb"] = { id=2798, price=80, sell=40, count=6, storagePay=78545, moneyName="$dollar" }
-- You can buy 6 blood herb x 80 $dollar
-- You can sell 6 blood herb x 40 $dollar
Note: The name of the virtual currency is declared on each item separately regardless of whether it is the same storage.
example: , moneyName="$dollar" }

View attachment 69963

Perfect. 10/10.
 
This is a modified version to add two new features requested by @VagosClubTM and @nefinoo
Also add some small changes in the way of displaying prices with gold coins.
data/scripts/file.lua
Lua:
local openShop_With_ActionID = nil
local openShop_With_ItemID = nil
local openShop_StorageID = nil
local openShop_StorageValue = nil
local openShop_MessageFailStorage = "nil"
local mOdAlWiNdOw_iD = 99999 -- Try to keep this ID unique for this window
local shop = {}
local modalWindow = ModalWindow(mOdAlWiNdOw_iD, "~ Shop Item List ~", "List of items available in the shop.")
modalWindow:addButton(1, "Buy")
modalWindow:addButton(2, "Sell")
modalWindow:addButton(3, "Close")
modalWindow:setDefaultEscapeButton(3)
modalWindow:setDefaultEnterButton(3)

local function getMoneyString(gp)
    if gp < 1000 then
        return string.format("%dgp", gp)
    elseif gp < 1000000 then
        local k = gp / 1000
        gp = gp % 1000
        return string.format("%dk%s", k, (gp > 0 and string.format(" %dgp", gp) or ""))
    end
    local kk = gp / 1000000
    gp = gp % 1000000
    local k = gp / 1000
    gp = gp % 1000
    return string.format("%dkk%s", kk, (k > 0 and string.format(" %dk%s", k, (gp > 0 and string.format(" %dgp", gp) or "")) or (gp > 0 and string.format(" %dgp", gp) or "")))
end

local index = 1
for name, item in pairs({
    -- The names are converted to lowercase letters automatically, don't worry.
    -- Note: for stackable items the maximum quantity is 100, and for non-stackable items the maximum quantity is 1
    -- Examples:
    ["Demon Helmet"] = { id=2493, price=3000000, sell=1500000 },
    ["Demon Armor"] = { id=2494, price=3000000, sell=1500000 },
    ["Demon Legs"] = { id=2495, price=3000000, sell=1500000 },
    ["Banana"] = { id=2676, price=3000000, sell=1500000, count=100 },
    ["Pear"] = { id=2673, price=20, sell=15, count=25, currency=2674 },
    ["Blood Herb"] = { id=2798, price=80, sell=40, count=6, storagePay=78545, moneyName="$dollar" }

}) do
    local lowerCaseName = name:lower()
    if index <= 255 then -- This is due to the client's limitations, in the future I may improve this system so that it has pages, but for now that is all
        modalWindow:addChoice(index, string.format("%d %s %s %s", math.min(item.count and item.count or 1, 100), lowerCaseName, string.char(215), (item.storagePay and string.format("%d %s", item.price, item.moneyName) or (item.currency and string.format("%d %s(s)", item.price, ItemType(item.currency):getName()) or getMoneyString(item.price))) ))
        shop[index] = lowerCaseName -- for help to modalWindow
        index = index +1
    end
    shop[lowerCaseName] = item
end

local function buyItem(player, param)
    local item = shop[param:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", param))
        return false
    end

    local money = 0
    if item.storagePay then
        money = player:getStorageValue(item.storagePay)
        if money < item.price then
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You don't have enough %s, You need %d %s.", item.moneyName, item.price, item.moneyName))
            return false
        end
    elseif item.currency then
        money = player:getItemCount(item.currency)
        if money < item.price then
            local moneyName = ItemType(item.currency):getName()
            player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("You don't have enough %s, You need %d %s.", moneyName, item.price, moneyName))
            return false
        end
    else
        money = player:getMoney()
        local bankBalance = player:getBankBalance()
        local totalMoney = money + bankBalance
        if totalMoney < item.price then
            player:sendCancelMessage(string.format("You don't have enough gold coins, You need %s", getMoneyString(item.price)))
            return false
        end
    end

    local buyedItem = Game.createItem(item.id, math.min((item.count and item.count or 1), 100))
    if not buyedItem or not buyedItem:getType():isMovable() then
        print(string.format("Warning: The shop item with ID: %d is invalid.\n%s", item.id, debug.traceback()))
        Item.remove(buyedItem) -- in case the buyedItem is nil, no problem will occur
        return false
    end

    if player:addItemEx(buyedItem) ~= RETURNVALUE_NOERROR then
        player:sendCancelMessage(RETURNVALUE_NOTENOUGHCAPACITY)
        buyedItem:remove()
        return false
    end

    if item.storagePay then
        player:setStorageValue(item.storagePay, money - item.price)
    elseif item.currency then
        player:removeItem(item.currency, item.price)
    else
        local resultMoney = money - item.price
        if resultMoney < 0 then
            player:removeMoney(money)
            player:setBankBalance(bankBalance + resultMoney)
        else
            player:removeMoney(item.price)
        end
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your purchase was successful! (%d %s).\n%s", math.min(item.count and item.count or 1, 100), buyedItem:getName(), (item.storagePay and string.format("You pay with %d %s.", item.price, item.moneyName) or (item.currency and string.format("You pay with %d %s.", item.price, ItemType(item.currency):getName()) or string.format("You pay with %s.", getMoneyString(item.price))))))
    return false
end

local function sellItem(player, itemName)
    if not itemName then
        return false
    end

    local item = shop[itemName:lower()]
    if not item then
        player:sendCancelMessage(string.format("Item with name %s not found!", itemName))
        return false
    end

    local itemCount = item.count or 1
    if not player:removeItem(item.id, itemCount) then
        player:sendCancelMessage(string.format("You do not have %d %s in your inventory.", itemCount, itemName))
        return false
    end

    if item.storagePay then
        player:setStorageValue(item.storagePay, player:getStorageValue(item.storagePay) + item.sell)
    elseif item.currency then
        player:addItem(item.currency, item.sell)
    else
        player:setBankBalance(player:getBankBalance() + item.sell)
    end

    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Your sale was successful! (%d %s).\n%s", math.min(item.count and item.count or 1, 100), itemName, (item.storagePay and string.format("You receive %d %s.", item.sell, item.moneyName) or (item.currency and string.format("You receive %d %s.", item.sell, ItemType(item.currency):getName()) or string.format("You receive %s to your bank balance.", getMoneyString(item.sell))))))
    return false
end

local talkAction = TalkAction("!shop")
function talkAction.onSay(player, words, param, type)
    if param == "list" then
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return false
    elseif param == "sell" then
        return sellItem(player, param:splitTrimmed(",")[2])
    end
    return buyItem(player, param)
end
talkAction:separator(" ")
talkAction:register()

if openShop_With_ActionID or openShop_With_ItemID then
    local action = Action()
    function action.onUse(player, item, fromPos, target, toPos, isHotkey)
        if openShop_StorageID then
            if player:getStorageValue(openShop_StorageID) ~= openShop_StorageValue then
                player:sendCancelMessage(openShop_MessageFailStorage)
                return false
            end
        end
        modalWindow:sendToPlayer(player)
        return true
    end
    if openShop_With_ItemID then action:id(openShop_With_ItemID) end
    if openShop_With_ActionID then action:aid(openShop_With_ActionID) end
    action:register()
end

local creatureEvent = CreatureEvent("shopModalWindow")
function creatureEvent.onModalWindow(player, modalWindowId, buttonId, choiceId)
    if modalWindowId == mOdAlWiNdOw_iD then
        if buttonId == 1 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            buyItem(player, param)
            return true
        elseif buttonId == 2 then
            local param = shop[choiceId]
            if not param then
                return true
            end
            sellItem(player, param)
            return true
        end
    end
    return true
end
creatureEvent:register()

local creatureEvent = CreatureEvent("shopRegister")
function creatureEvent.onLogin(player)
    player:registerEvent("shopModalWindow")
    return true
end
creatureEvent:register()
Items that are paid for with other types of physical currency in the game? (Yes is possible!)
Example:
Lua:
["Pear"] = { id=2673, price=20, sell=15, count=25, currency=2674 },
-- You can buy 25 pear x 20 red apple
-- You can sell 25 pear x 15 red apple

Items that are paid with virtual currencies? (Yes, it is possible to pay with storages)
Example:
Lua:
["Blood Herb"] = { id=2798, price=80, sell=40, count=6, storagePay=78545, moneyName="$dollar" }
-- You can buy 6 blood herb x 80 $dollar
-- You can sell 6 blood herb x 40 $dollar
Note: The name of the virtual currency is declared on each item separately regardless of whether it is the same storage.
example: , moneyName="$dollar" }

View attachment 69963
Hello, it's great, but I don't know why when I want to buy and press the button, it doesn't take away the items and it doesn't give me anything either, no errors appear on the console.
 
Back
Top