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

Multiple shopModules

OperatorMopa

Member
Joined
Feb 10, 2014
Messages
127
Reaction score
6
Hello guys :)
Is there any way to make few shopModules for one npc?
Instead of making 2 npc i wonder if its possible to make a shop which shows different items for knights and different for sorcerers.
 
There is, you'd probably need a function which gets the vocation [getplayervocation(cid)] and if so then shopModule:addSellableItem(...blablablashit).

*Let your imagination flow with lua, try a lot of things out, like everthing you can imagine, lua might bring you further than you think it can.
 
The problem is that once shopModule is created its the same for everyone. This is why i wanted to create 2 modules, and depending on vocation show one of them to the player.
 
Probably not if you create the shopmodule inside an If-Function :)
You can't, shopmodule is created when the npc is loaded not when someone talks with them.

Hello guys :)
Is there any way to make few shopModules for one npc?
Instead of making 2 npc i wonder if its possible to make a shop which shows different items for knights and different for sorcerers.

You could edit the npcsystem and add another parameter to the addBuyableItem and addSellableItem, that would be the vocation of the offer and it would be stored and edit the function ShopModule.requestTrade and add only the items from cid vocation in the table itemWindow.
 
You can't, shopmodule is created when the npc is loaded not when someone talks with them.



You could edit the npcsystem and add another parameter to the addBuyableItem and addSellableItem, that would be the vocation of the offer and it would be stored and edit the function ShopModule.requestTrade and add only the items from cid vocation in the table itemWindow.

I'm sure there is a way to load the shopmodule when you talk to npc and it gets your vocation.
 
It is, but it when you load it once (as knight for example) its binded to npc, and every other vocation have this module
Isnt it the point of this all?

No, sorry, I didn't read correctly, I mean, Not if you say hi to the npc with another vocation.

It will reload after you talk to the npc with another vocationid
 
I'm sure there is a way to load the shopmodule when you talk to npc and it gets your vocation.
Of course there is a way, but you would have to delete all the modules from the npc and set them all again once the player talks with him, thats not the point of the modules. Why are you going to store all those items and keywords and then when another player start the npc delete all those previous stored information to create new ones for this player?

Its pretty simple to add another parameter to addbuyable/sellable item to be the vocation id.
 
The reason i wanted to have different modules was that few of the items would be the same, but with different price. For example rope for knight 20gp, for sorcerer 50gp
 
Hello guys :)
Is there any way to make few shopModules for one npc?
Instead of making 2 npc i wonder if its possible to make a shop which shows different items for knights and different for sorcerers.
Simple way is to use storage values, not everything needs to be complicated.
 
not rly. You only re-create the shoplist.
Code:
  function ShopModule:init(handler)
     self.npcHandler = handler
     self.yesNode = KeywordNode:new(SHOP_YESWORD, ShopModule.onConfirm, {module = self})
     self.noNode = KeywordNode:new(SHOP_NOWORD, ShopModule.onDecline, {module = self})
     self.noText = handler:getMessage(MESSAGE_DECLINE)

     if SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK then
       for i, word in pairs(SHOP_TRADEREQUEST) do
         local obj = {}
         obj[#obj + 1] = word
         obj.callback = SHOP_TRADEREQUEST.callback or ShopModule.messageMatcher
         handler.keywordHandler:addKeyword(obj, ShopModule.requestTrade, {module = self})
       end
     end

     return true
   end

Keywords too.

Code:
    function NpcHandler:addModule(module)
        if self.modules ~= nil then
            self.modules[#self.modules +1] = module
            module:init(self)
        end
    end

Modules too.

You will have to erase the shop module from the npcHandler of that npc and recreate it. And erase the keyword to trade that is pointing to the old module, and remove every where that is referencing the old module, other wise the table wont be collected and bye bye memory is leaking like hell
 
Last edited:
Code:
int NpcScriptInterface::luaOpenShopWindow(lua_State* L)
{
    //openShopWindow(cid, items, onBuy callback, onSell callback)
    int32_t sellCallback;
    if (lua_isfunction(L, -1) == 0) {
        sellCallback = -1;
        lua_pop(L, 1);    // skip it - use default value
    } else {
        sellCallback = popCallback(L);
    }

    int32_t buyCallback;
    if (lua_isfunction(L, -1) == 0) {
        buyCallback = -1;
        lua_pop(L, 1);    // skip it - use default value
    } else {
        buyCallback = popCallback(L);
    }

    if (lua_istable(L, -1) == 0) {
        reportError(__FUNCTION__, "item list is not a table.");
        pushBoolean(L, false);
        return 1;
    }

    std::list<ShopInfo> items;
    lua_pushnil(L);
    while (lua_next(L, -2) != 0) {
        const auto tableIndex = lua_gettop(L);
        ShopInfo item;

        item.itemId = getField<uint32_t>(L, tableIndex, "id");
        item.subType = getField<int32_t>(L, tableIndex, "subType");
        if (item.subType == 0) {
            item.subType = getField<int32_t>(L, tableIndex, "subtype");
            lua_pop(L, 1);
        }

        item.buyPrice = getField<uint32_t>(L, tableIndex, "buy");
        item.sellPrice = getField<uint32_t>(L, tableIndex, "sell");
        item.realName = getFieldString(L, tableIndex, "name");

        items.push_back(item);
        lua_pop(L, 6);
    }
    lua_pop(L, 1);

    Player* player = getPlayer(L, -1);
    if (!player) {
        reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND));
        pushBoolean(L, false);
        return 1;
    }

    //Close any eventual other shop window currently open.
    player->closeShopWindow(false);

    Npc* npc = getScriptEnv()->getNpc();
    if (!npc) {
        reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
        pushBoolean(L, false);
        return 1;
    }

    npc->addShopPlayer(player);
    player->setShopOwner(npc, buyCallback, sellCallback);
    player->openShopWindow(npc, items);

    pushBoolean(L, true);
    return 1;
}

You only need list of items you buy and list of items you sell
And this is how i execute it: shopSystem(player, createShopList(player, npcNameL))
shoplist creation makes itemtable depending what player can buy/sell and for what price.
shopSystem separates the buyable and sellable items to correct format for source to handle
 
Code:
int NpcScriptInterface::luaOpenShopWindow(lua_State* L)
{
    //openShopWindow(cid, items, onBuy callback, onSell callback)
    int32_t sellCallback;
    if (lua_isfunction(L, -1) == 0) {
        sellCallback = -1;
        lua_pop(L, 1);    // skip it - use default value
    } else {
        sellCallback = popCallback(L);
    }

    int32_t buyCallback;
    if (lua_isfunction(L, -1) == 0) {
        buyCallback = -1;
        lua_pop(L, 1);    // skip it - use default value
    } else {
        buyCallback = popCallback(L);
    }

    if (lua_istable(L, -1) == 0) {
        reportError(__FUNCTION__, "item list is not a table.");
        pushBoolean(L, false);
        return 1;
    }

    std::list<ShopInfo> items;
    lua_pushnil(L);
    while (lua_next(L, -2) != 0) {
        const auto tableIndex = lua_gettop(L);
        ShopInfo item;

        item.itemId = getField<uint32_t>(L, tableIndex, "id");
        item.subType = getField<int32_t>(L, tableIndex, "subType");
        if (item.subType == 0) {
            item.subType = getField<int32_t>(L, tableIndex, "subtype");
            lua_pop(L, 1);
        }

        item.buyPrice = getField<uint32_t>(L, tableIndex, "buy");
        item.sellPrice = getField<uint32_t>(L, tableIndex, "sell");
        item.realName = getFieldString(L, tableIndex, "name");

        items.push_back(item);
        lua_pop(L, 6);
    }
    lua_pop(L, 1);

    Player* player = getPlayer(L, -1);
    if (!player) {
        reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND));
        pushBoolean(L, false);
        return 1;
    }

    //Close any eventual other shop window currently open.
    player->closeShopWindow(false);

    Npc* npc = getScriptEnv()->getNpc();
    if (!npc) {
        reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
        pushBoolean(L, false);
        return 1;
    }

    npc->addShopPlayer(player);
    player->setShopOwner(npc, buyCallback, sellCallback);
    player->openShopWindow(npc, items);

    pushBoolean(L, true);
    return 1;
}

You only need list of items you buy and list of items you sell
And this is how i execute it: shopSystem(player, createShopList(player, npcNameL))
shoplist creation makes itemtable depending what player can buy/sell and for what price.
shopSystem separates the buyable and sellable items to correct table format for source to handle

"You could edit the npcsystem and add another parameter to the addBuyableItem and addSellableItem, that would be the vocation of the offer and it would be stored and edit the function ShopModule.requestTrade and add only the items from cid vocation in the table itemWindow."

Thats what I said in my previous post. itemWindow is the "shopList"
Code:
    local itemWindow = {}
     for i = 1, #module.npcHandler.shopItems do
       table.insert(itemWindow, module.npcHandler.shopItems[i])
     end

     if(itemWindow[1] == nil) then
       local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
       local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_NOSHOP), parseInfo)
       module.npcHandler:say(msg, cid)
       return true
     end

     local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) }
     local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_SENDTRADE), parseInfo)
     openShopWindow(cid, itemWindow,
       function(cid, itemid, subType, amount, ignoreCap, inBackpacks) module.npcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks) end,
       function(cid, itemid, subType, amount, ignoreCap, inBackpacks) module.npcHandler:onSell(cid, itemid, subType, amount, ignoreCap, inBackpacks) end)

He would only need to place an if inside that for and check the added vocationid in shopitems (modifying addsellableitem/buyableitem).
 
aah i c. these npchandlers make me confused xD (and don't have them in my server to remind me how much they annoyed me)
 
Thank you for all the answers.
HalfAway idea is the best.

Npc module edits are propably useless in this situation. Why? Becouse shopModule:addBuyableItem is executed when you actually create npc - at this time you can't check what storage values of players will be.
 
Thank you for all the answers.
HalfAway idea is the best.

Npc module edits are propably useless in this situation. Why? Becouse shopModule:addBuyableItem is executed when you actually create npc - at this time you can't check what storage values of players will be.
You would not check the storage values of player at the creation of the npc, you would check in the requestTrade that has cid parameter, the storage would only be stored in the offer and would be checked at every requestTrade. Its way easier than recreating callbacks
 
You would not check the storage values of player at the creation of the npc, you would check in the requestTrade that has cid parameter, the storage would only be stored in the offer and would be checked at every requestTrade. Its way easier than recreating callbacks
I propably understand something wrong.
Ofc i can check storage in requestTrade, but what would it give to me if buyable items are loaded when the npc is created. How can i change the item price in requestTrade ? it just loads shopItems table which is created with npc.

The only problem in @HalfAway idea is that i would have to remake all npcs with 4 sets of price for different vocations :/ its not hard it'll be time-consuming and made in nooby way ;p
 
Last edited:
Back
Top