-- private variables local SHOP_EXTENTED_OPCODE = 201 shop = nil local shopButton = nil local msgWindow = nil local browsingHistory = false local CATEGORIES = {} local HISTORY = {} local STATUS = {} local AD = {} local selectedOffer = {} local function sendAction(action, data) local protocolGame = g_game.getProtocolGame() if data == nil then data = {} end if protocolGame then protocolGame:sendExtendedOpcode(SHOP_EXTENTED_OPCODE, json.encode({action = action, data = data})) end end -- public functions function init() connect(g_game, { onGameStart = check, onGameEnd = hide }) ProtocolGame.registerExtendedOpcode(SHOP_EXTENTED_OPCODE, onExtendedOpcode) if g_game.isOnline() then check() end end function terminate() disconnect(g_game, { onGameStart = check, onGameEnd = hide }) ProtocolGame.unregisterExtendedOpcode(SHOP_EXTENTED_OPCODE, onExtendedOpcode) if shopButton then shopButton:destroy() shopButton = nil end if shop then disconnect(shop.categories, { onChildFocusChange = changeCategory }) shop:destroy() shop = nil end if msgWindow then msgWindow:destroy() end end function check() if not g_game.getFeature(GameExtendedOpcode) then return end sendAction("init") end function hide() if not shop then return end shop:hide() end function show() if not shop or not shopButton then return end shop:show() shop:raise() shop:focus() end function toggle() if not shop then return end if shop:isVisible() then return hide() end show() check() end function onExtendedOpcode(protocol, code, buffer) if not shop then shop = g_ui.displayUI('shop') shop:hide() shopButton = modules.client_topmenu.addRightGameToggleButton('shopButton', tr('Shop'), '/images/topbuttons/shop', toggle) connect(shop.categories, { onChildFocusChange = changeCategory }) end local json_status, json_data = pcall(function() return json.decode(buffer) end) if not json_status then g_logger.error("SHOP json error: " .. json_data) return false end local action = json_data['action'] local data = json_data['data'] local status = json_data['status'] if not action or not data then return false end if action == 'categories' then processCategories(data) elseif action == 'history' then processHistory(data) elseif action == 'message' then processMessage(data) end if status then processStatus(status) end end function clearOffers() while shop.offers:getChildCount() > 0 do local child = shop.offers:getLastChild() shop.offers:destroyChildren(child) end end function clearCategories() CATEGORIES = {} clearOffers() while shop.categories:getChildCount() > 0 do local child = shop.categories:getLastChild() shop.categories:destroyChildren(child) end end function clearHistory() HISTORY = {} if browsingHistory then clearOffers() end end function processCategories(data) if table.equal(CATEGORIES,data) then return end clearCategories() CATEGORIES = data for i, category in ipairs(data) do addCategory(category) end if not browsingHistory then local firstCategory = shop.categories:getChildByIndex(1) if firstCategory then firstCategory:focus() end end end function processHistory(data) if table.equal(HISTORY,data) then return end HISTORY = data if browsingHistory then showHistory(true) end end function processMessage(data) if msgWindow then msgWindow:destroy() end local title = tr(data["title"]) local msg = data["msg"] msgWindow = displayInfoBox(title, msg) msgWindow:show() msgWindow:raise() msgWindow:focus() msgWindow:raise() end function processStatus(data) if table.equal(STATUS,data) then return end STATUS = data if data['ad'] then processAd(data['ad']) end if data['points'] then shop.infoPanel.points:setText(tr("Points:") .. " " .. data['points']) end if data['buyUrl'] and data['buyUrl']:sub(1, 4):lower() == "http" then shop.infoPanel.buy:show() shop.infoPanel.buy.onMouseRelease = function() scheduleEvent(function() g_platform.openUrl(data['buyUrl']) end, 50) end else shop.infoPanel.buy:hide() end end function processAd(data) if table.equal(AD,data) then return end AD = data if data['image'] and data['image']:sub(1, 4):lower() == "http" then HTTP.downloadImage(data['image'], function(path, err) if err then g_logger.warning("HTTP error: " .. err) return end shop.adPanel:setHeight(shop.infoPanel:getHeight()) shop.adPanel.ad:setText("") shop.adPanel.ad:setImageSource(path) shop.adPanel.ad:setImageFixedRatio(true) shop.adPanel.ad:setImageAutoResize(true) shop.adPanel.ad:setHeight(shop.infoPanel:getHeight()) end) elseif data['text'] and data['text']:len() > 0 then shop.adPanel:setHeight(shop.infoPanel:getHeight()) shop.adPanel.ad:setText(data['text']) shop.adPanel.ad:setHeight(shop.infoPanel:getHeight()) else shop.adPanel:setHeight(0) end if data['url'] and data['url']:sub(1, 4):lower() == "http" then shop.adPanel.ad.onMouseRelease = function() scheduleEvent(function() g_platform.openUrl(data['url']) end, 50) end else shop.adPanel.ad.onMouseRelease = nil end end function addCategory(data) local category if data["type"] == "item" then category = g_ui.createWidget('ShopCategoryItem', shop.categories) category.item:setItemId(data["item"]) category.item:setItemCount(data["count"]) category.item:setShowCount(false) elseif data["type"] == "outfit" then category = g_ui.createWidget('ShopCategoryCreature', shop.categories) category.creature:setOutfit(data["outfit"]) if data["outfit"]["rotating"] then category.creature:setAutoRotating(true) end elseif data["type"] == "image" then category = g_ui.createWidget('ShopCategoryImage', shop.categories) if data["image"]:sub(1, 4):lower() == "http" then HTTP.downloadImage(data['image'], function(path, err) if err then g_logger.warning("HTTP error: " .. err) return end category.image:setImageSource(path) end) else category.image:setImageSource(data["image"]) end else g_logger.error("Invalid shop category type: " .. tostring(data["type"])) return end category:setId("category_" .. shop.categories:getChildCount()) category.name:setText(data["name"]) end function showHistory(force) if browsingHistory and not force then return end sendAction("history") browsingHistory = true clearOffers() shop.categories:focusChild(nil) for i, transaction in ipairs(HISTORY) do addOffer(0, transaction) end end function addOffer(category, data) local offer if data["type"] == "item" then offer = g_ui.createWidget('ShopOfferItem', shop.offers) offer.item:setItemId(data["item"]) offer.item:setItemCount(data["count"]) offer.item:setShowCount(false) elseif data["type"] == "outfit" then offer = g_ui.createWidget('ShopOfferCreature', shop.offers) offer.creature:setOutfit(data["outfit"]) if data["outfit"]["rotating"] then offer.creature:setAutoRotating(true) end elseif data["type"] == "image" then offer = g_ui.createWidget('ShopOfferImage', shop.offers) if data["image"]:sub(1, 4):lower() == "http" then HTTP.downloadImage(data['image'], function(path, err) if err then g_logger.warning("HTTP error: " .. err) return end offer.image:setImageSource(path) end) elseif data["image"] and data["image"]:len() > 1 then offer.image:setImageSource(data["image"]) end else g_logger.error("Invalid shop offer type: " .. tostring(data["type"])) return end offer:setId("offer_" .. category .. "_" .. shop.offers:getChildCount()) offer.title:setText(data["title"] .. " (" .. data["cost"] .. " points)") offer.description:setText(data["description"]) if category ~= 0 then offer.onDoubleClick = buyOffer end end function changeCategory(widget, newCategory) if not newCategory then return end browsingHistory = false local id = tonumber(newCategory:getId():split("_")[2]) clearOffers() for i, offer in ipairs(CATEGORIES[id]["offers"]) do addOffer(id, offer) end end function buyOffer(widget) if not widget then return end local split = widget:getId():split("_") if #split ~= 3 then return end local category = tonumber(split[2]) local offer = tonumber(split[3]) local item = CATEGORIES[category]["offers"][offer] if not item then return end selectedOffer = {category=category, offer=offer, title=item.title, cost=item.cost} scheduleEvent(function() if msgWindow then msgWindow:destroy() end local title = tr("Buying from shop") local msg = "Do you want to buy " .. item.title .. " for " .. item.cost .. " premium points?" msgWindow = displayGeneralBox(title, msg, { { text=tr('Yes'), callback=buyConfirmed }, { text=tr('No'), callback=buyCanceled }, anchor=AnchorHorizontalCenter}, buyConfirmed, buyCanceled) msgWindow:show() msgWindow:raise() msgWindow:focus() msgWindow:raise() end, 50) end function buyConfirmed() msgWindow:destroy() msgWindow = nil sendAction("buy", selectedOffer) end function buyCanceled() msgWindow:destroy() msgWindow = nil selectedOffer = {} end