Elvarion

init.lua

Jul 19th, 2020
39
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 48.41 KB | None | 0 0
  1. -- Please don't edit those information!
  2. GameStore = {
  3.   ModuleName = "GameStore",
  4.   Developers = { "Slavi Dodo", "Cjaker", "metabob" },
  5.   Version = "0.4",
  6.   LastUpdated = "15-04-2019 17:40PM"
  7. }
  8.  
  9. --== Enums ==--
  10. GameStore.OfferTypes = {
  11.   OFFER_TYPE_NONE = 0, -- (this will disable offer)
  12.   OFFER_TYPE_ITEM = 1,
  13.   OFFER_TYPE_STACKABLE = 2,
  14.   OFFER_TYPE_OUTFIT = 3,
  15.   OFFER_TYPE_OUTFIT_ADDON = 4,
  16.   OFFER_TYPE_MOUNT = 5,
  17.   OFFER_TYPE_NAMECHANGE = 6,
  18.   OFFER_TYPE_SEXCHANGE = 7,
  19.   OFFER_TYPE_PROMOTION = 8,
  20.   OFFER_TYPE_HOUSE = 9,
  21.   OFFER_TYPE_EXPBOOST = 10,
  22.   OFFER_TYPE_PREYSLOT = 11,
  23.   OFFER_TYPE_PREYBONUS = 12,
  24.   OFFER_TYPE_TEMPLE = 13,
  25.   OFFER_TYPE_BLESSINGS = 14,
  26.   OFFER_TYPE_PREMIUM = 15,
  27.   OFFER_TYPE_POUCH = 16,
  28.   OFFER_TYPE_ALLBLESSINGS = 17
  29. }
  30.  
  31. GameStore.ClientOfferTypes = {
  32.   CLIENT_STORE_OFFER_OTHER = 0,
  33.   CLIENT_STORE_OFFER_NAMECHANGE = 1
  34. }
  35.  
  36. GameStore.HistoryTypes = {
  37.   HISTORY_TYPE_NONE = 0,
  38.   HISTORY_TYPE_GIFT = 1,
  39.   HISTORY_TYPE_REFUND = 2
  40. }
  41.  
  42. GameStore.States = {
  43.   STATE_NONE = 0,
  44.   STATE_NEW = 1,
  45.   STATE_SALE = 2,
  46.   STATE_TIMED = 3
  47. }
  48.  
  49. GameStore.StoreErrors = {
  50.   STORE_ERROR_PURCHASE = 0,
  51.   STORE_ERROR_NETWORK = 1,
  52.   STORE_ERROR_HISTORY = 2,
  53.   STORE_ERROR_TRANSFER = 3,
  54.   STORE_ERROR_INFORMATION = 4
  55. }
  56.  
  57. GameStore.ServiceTypes = {
  58.   SERVICE_STANDERD = 0,
  59.   SERVICE_OUTFITS = 3,
  60.   SERVICE_MOUNTS = 4,
  61.   SERVICE_BLESSINGS = 5
  62. }
  63.  
  64. GameStore.SendingPackets = {
  65.   S_CoinBalance = 0xDF, -- 223
  66.   S_StoreError = 0xE0, -- 224
  67.   S_RequestPurchaseData = 0xE1, -- 225
  68.   S_CoinBalanceUpdating = 0xF2, -- 242
  69.   S_OpenStore = 0xFB, -- 251
  70.   S_StoreOffers = 0xFC, -- 252
  71.   S_OpenTransactionHistory = 0xFD, -- 253
  72.   S_CompletePurchase = 0xFE  -- 254
  73. }
  74.  
  75. GameStore.RecivedPackets = {
  76.   C_StoreEvent = 0xE9, -- 233
  77.   C_TransferCoins = 0xEF, -- 239
  78.   C_OpenStore = 0xFA, -- 250
  79.   C_StoreSelectOffer = 0xE8, -- 232
  80.   C_RequestStoreOffers = 0xFB, -- 251
  81.   C_StoreSelectOffer = 0xE8, -- 232
  82.   C_BuyStoreOffer = 0xFC, -- 252
  83.   C_OpenTransactionHistory = 0xFD, -- 253
  84.   C_RequestTransactionHistory = 0xFE, -- 254
  85. }
  86.  
  87. GameStore.ExpBoostValues = {
  88.   [1] = 30,
  89.   [2] = 45,
  90.   [3] = 90,
  91.   [4] = 180,
  92.   [5] = 360
  93. }
  94.  
  95. GameStore.DefaultValues = {
  96.   DEFAULT_VALUE_ENTRIES_PER_PAGE = 26
  97. }
  98.  
  99. GameStore.DefaultDescriptions = {
  100.   OUTFIT      = { "This outfit looks nice. Only high-class people are able to wear it!",
  101.              "An outfit that was created to suit you. We are sure you'll like it.",
  102.              "Legend says only smart people should wear it, otherwise you will burn!" },
  103.   MOUNT       = { "This is a fantastic mount that helps to become faster, try it!",
  104.             "The first rider of this mount became the leader of his country! legends say that." },
  105.   NAMECHANGE  = { "Are you hunted? Tired of that? Get a new name, a new life!",
  106.                  "A new name to suit your needs!" },
  107.   SEXCHANGE   = { "Bored of your character's sex? Get a new sex for him now!!" },
  108.   EXPBOOST    = { "Are you tired of leveling slow? try it!" },
  109.   PREYSLOT    = { "It's hunting season! Activate a prey to gain a bonus when hunting a certain monster. Every character can purchase one Permanent Prey Slot, which enables the activation of an additional prey. \nIf you activate a prey, you can select one monster out of nine. The bonus for your prey will be selected randomly from one of the following: damage boost, damage reduction, bonus XP, improved loot. The bonus value may range from 5% to 50%. Your prey will be active for 2 hours hunting time: the duration of an active prey will only be reduced while you are hunting." },
  110.   PREYBONUS   = { "You activated a prey but do not like the randomly selected bonus? Roll for a new one! Here you can purchase five Prey Bonus Rerolls! \nA Bonus Reroll allows you to get a bonus with a higher value (max. 50%). The bonus for your prey will be selected randomly from one of the following: damage boost, damage reduction, bonus XP, improved loot. The 2 hours hunting time will start anew once you have rolled for a new bonus. Your prey monster will stay the same." },
  111.   TEMPLE      = { "Need a quick way home? Buy this transportation service to get instantly teleported to your home temple. \n\nNote, you cannot use this service while having a battle sign or a protection zone block. Further, the service will not work in no-logout zones or close to your home temple." }
  112. }
  113.  
  114. --==Parsing==--
  115. GameStore.isItsPacket = function(byte)
  116.   for k, v in pairs(GameStore.RecivedPackets) do
  117.     if v == byte then
  118.       return true
  119.     end
  120.   end
  121.   return false
  122. end
  123.  
  124. local function queueSendStoreAlertToUser(message, delay, playerId, storeErrorCode)
  125.   storeErrorCode = storeErrorCode and storeErrorCode or  GameStore.StoreErrors.STORE_ERROR_NETWORK
  126.   addPlayerEvent(sendStoreError, delay, playerId, storeErrorCode, message)
  127. end
  128.  
  129. function onRecvbyte(player, msg, byte)
  130.   if not configManager.getBoolean(STOREMODULES) then return true end
  131.   if player:getVocation():getId() == 0 and not GameStore.haveCategoryRook() then
  132.     return player:sendCancelMessage("Store don't have offers for rookgaard citizen.")
  133.   end
  134.  
  135.   if byte == GameStore.RecivedPackets.C_StoreEvent then
  136.     -- Not Used!
  137.   elseif byte == GameStore.RecivedPackets.C_TransferCoins then
  138.     parseTransferCoins(player:getId(), msg)
  139.   elseif byte == GameStore.RecivedPackets.C_OpenStore then
  140.     parseOpenStore(player:getId(), msg)
  141.   elseif byte == GameStore.RecivedPackets.C_RequestStoreOffers then
  142.     parseRequestStoreOffers(player:getId(), msg)
  143.   elseif byte == GameStore.RecivedPackets.C_StoreSelectOffer then
  144.     parseSendDescription(player:getId(), msg)
  145.   elseif byte == GameStore.RecivedPackets.C_BuyStoreOffer then
  146.     parseBuyStoreOffer(player:getId(), msg)
  147.   elseif byte == GameStore.RecivedPackets.C_OpenTransactionHistory then
  148.     parseOpenTransactionHistory(player:getId(), msg)
  149.   elseif byte == GameStore.RecivedPackets.C_RequestTransactionHistory then
  150.     parseRequestTransactionHistory(player:getId(), msg)
  151.   end
  152.   return true
  153. end
  154.  
  155. function parseSendDescription(playerId, msg)
  156.   local player = Player(playerId)
  157.   if not player then
  158.     return false
  159.   end
  160.   if player:getClient().version < 1180 then
  161.     return false
  162.   end
  163.   local offerId = msg:getU32()
  164.   if offerId then
  165.     addPlayerEvent(sendShowDescription, 350, playerId, offerId)
  166.   end
  167. end
  168.  
  169. function parseTransferCoins(playerId, msg)
  170.   local player = Player(playerId)
  171.   if not player then
  172.     return false
  173.   end
  174.  
  175.   local reciver = msg:getString()
  176.   local amount = msg:getU32()
  177.  
  178.   if (player:getCoinsBalance() < amount) then
  179.     return addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER, "You don't have this amount of coins.")
  180.   end
  181.  
  182.   if reciver:lower() == player:getName():lower() then
  183.     return addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER, "You can't transfer coins to yourself.")
  184.   end
  185.  
  186.   local resultId = db.storeQuery("SELECT `account_id` FROM `players` WHERE `name` = " .. db.escapeString(reciver:lower()) .. "")
  187.   if not resultId then
  188.     return addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER, "We couldn't find that player.")
  189.   end
  190.  
  191.   local accountId = result.getDataInt(resultId, "account_id")
  192.   if accountId == player:getAccountId() then
  193.     return addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER, "You cannot transfer coin to a character in the same account.")
  194.   end
  195.  
  196.   db.query("UPDATE `accounts` SET `coins` = `coins` + " .. amount .. " WHERE `id` = " .. accountId)
  197.   player:removeCoinsBalance(amount)
  198.   addPlayerEvent(sendStorePurchaseSuccessful, 550, playerId, "You have transfered " .. amount .. " coins to " .. reciver .. " successfully")
  199.  
  200.   -- Adding history for both reciver/sender
  201.   GameStore.insertHistory(accountId, GameStore.HistoryTypes.HISTORY_TYPE_NONE, player:getName() .. " transfered you this amount.", amount)
  202.   GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, "You transfered this amount to " .. reciver, -1 * amount) -- negative
  203. end
  204.  
  205. function sendShowDescription(playerId, offerId)
  206.   local player = Player(playerId)
  207.   if not player then
  208.     return false
  209.   end
  210.   local offer = GameStore.getOfferById(offerId)
  211.   local msg = NetworkMessage()
  212.   msg:addByte(0xea)
  213.   msg:addU32(offerId)
  214.   msg:addString(offer.description or "No description to be displayed")
  215.   msg:sendToPlayer(player)
  216. end
  217.  
  218. function parseOpenStore(playerId, msg)
  219.   openStore(playerId)
  220.  
  221.   local serviceType = msg:getByte()
  222.   local category = GameStore.Categories and GameStore.Categories[1] or nil
  223.  
  224.   local servicesName = {
  225.     [GameStore.ServiceTypes.SERVICE_OUTFITS] = "outfits",
  226.     [GameStore.ServiceTypes.SERVICE_MOUNTS] = "mounts",
  227.     [GameStore.ServiceTypes.SERVICE_BLESSINGS] = "blessings"
  228.   }
  229.  
  230.   if servicesName[serviceType] then
  231.     category = GameStore.getCategoryByName(servicesName[serviceType])
  232.   end
  233.  
  234.   if category then
  235.     addPlayerEvent(sendShowStoreOffers, 350, playerId, category)
  236.   end
  237. end
  238.  
  239. function parseRequestStoreOffers(playerId, msg)
  240.   local player = Player(playerId)
  241.   if not player then
  242.     return false
  243.   end
  244.  
  245.   local serviceType = GameStore.ServiceTypes.SERVICE_STANDERD
  246.   if player:getClient().version >= 1092 then
  247.     serviceType = msg:getByte()
  248.   end
  249.  
  250.   local categoryName = msg:getString()
  251.  
  252.   local category = GameStore.getCategoryByName(categoryName)
  253.   if category then
  254.     addPlayerEvent(sendShowStoreOffers, 350, playerId, category)
  255.   end
  256. end
  257.  
  258. function parseBuyStoreOffer(playerId, msg)
  259.   local player = Player(playerId)
  260.   local id = msg:getU32()
  261.   local offer = GameStore.getOfferById(id)
  262.   local productType = msg:getByte()
  263.  
  264.   -- All guarding conditions under which the offer should not be processed must be included here
  265.   if (table.contains(GameStore.OfferTypes, offer.type) == false)                      -- we've got an invalid offer type
  266.       or (not player)                                                                 -- player not found
  267.       or (player:getVocation():getId() == 0) and (not GameStore.haveOfferRook(id))    -- we don't have such offer
  268.       or (not offer)                                                                  -- we could not find the offer
  269.       or (offer.type == GameStore.OfferTypes.OFFER_TYPE_NONE)                         -- offer is disabled
  270.       or (offer.type ~= GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE and
  271.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and
  272.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYBONUS and
  273.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYSLOT and
  274.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_TEMPLE and
  275.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE and
  276.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_POUCH and
  277.           not offer.id) then
  278.     return queueSendStoreAlertToUser("This offer is unavailable [1]", 350, playerId, GameStore.StoreErrors.STORE_ERROR_INFORMATION)
  279.   end
  280.  
  281.   -- At this point the purchase is assumed to be formatted correctly
  282.   local offerPrice = offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and GameStore.ExpBoostValues[player:getStorageValue(51052)] or offer.price
  283.  
  284.   if not player:canRemoveCoins(offerPrice) then
  285.     return queueSendStoreAlertToUser("You don't have enough coins. Your purchase has been cancelled.", 250, playerId)
  286.   end
  287.  
  288.   -- Use pcall to catch unhandled errors and send an alert to the user because the client expects it at all times; (OTClient will unlock UI)
  289.   -- Handled errors are thrown to indicate that the purchase has failed;
  290.   -- Handled errors have a code index and unhandled errors do not
  291.   local pcallOk, pcallError = pcall(function()
  292.     if offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM                 then GameStore.processItemPurchase(player, offer.id, offer.count)
  293.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_POUCH          then GameStore.processItemPurchase(player, offer.id, offer.count)
  294.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BLESSINGS      then GameStore.processSignleBlessingPurchase(player, offer.id)
  295.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_ALLBLESSINGS   then GameStore.processAllBlessingsPurchase(player)
  296.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREMIUM        then GameStore.processPremiumPurchase(player, offer.id)
  297.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_STACKABLE      then GameStore.processStackablePurchase(player, offer.id, offer.count, offer.name, offer.number)
  298.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HOUSE          then GameStore.processHouseRelatedPurchase(player, offer.id, offer.count)
  299.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT         then GameStore.processOutfitPurchase(player, offer.sexId, offer.addon)
  300.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON   then GameStore.processOutfitPurchase(player, offer.sexId, offer.addon)
  301.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT          then GameStore.processMountPurchase(player, offer.id)
  302.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE     then local newName = msg:getString(); GameStore.processNameChangePurchase(player, offer, productType, newName)
  303.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE      then GameStore.processSexChangePurchase(player)
  304.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST       then GameStore.processExpBoostPuchase(player)
  305.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYSLOT       then GameStore.processPreySlotPurchase(player)
  306.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYBONUS      then GameStore.processPreyBonusReroll(player, offer.count)
  307.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_TEMPLE         then GameStore.processTempleTeleportPurchase(player)
  308.       elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PROMOTION      then GameStore.processPromotionPurchase(player, offer.id)
  309.     else
  310.       -- This should never happen by our convention, but just in case the guarding condition is messed up...
  311.       error({code = 0, message = "This offer is unavailable [2]"})
  312.     end
  313.   end)
  314.  
  315.   if not pcallOk then
  316.     local alertMessage = pcallError.code and pcallError.message or "Something went wrong. Your purchase has been cancelled."
  317.  
  318.     if not pcallError.code then -- unhandled error
  319.       -- log some debugging info
  320.       print(string.format("Gamestore: Purchase failed due to an unhandled script error. \n\tStacktrace: %s\n", pcallError))
  321.     end
  322.  
  323.     return queueSendStoreAlertToUser(alertMessage, 500, playerId)
  324.   end
  325.  
  326.   if offer.type == GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE then
  327.     return
  328.   end
  329.  
  330.   player:removeCoinsBalance(offerPrice)
  331.   GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offer.name, (offerPrice) * -1)
  332.  
  333.   local message = string.format("You have purchased %s for %d coins.", offer.name, offerPrice)
  334.   return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message)
  335. end
  336.  
  337. -- Both functions use same formula!
  338. function parseOpenTransactionHistory(playerId, msg)
  339.   local page = 1
  340.   GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE = msg:getByte()
  341.   sendStoreTransactionHistory(playerId, page, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)
  342. end
  343.  
  344. function parseRequestTransactionHistory(playerId, msg)
  345.   local page = msg:getU32()
  346.   sendStoreTransactionHistory(playerId, page + 1, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)
  347. end
  348.  
  349. local function getCategoriesRook()
  350.   local tmpTable, count = {}, 0
  351.   for i, v in pairs(GameStore.Categories) do
  352.     if (v.rookgaard) then
  353.       tmpTable[#tmpTable + 1] = v
  354.       count = count + 1
  355.     end
  356.   end
  357.  
  358.   return tmpTable, count
  359. end
  360.  
  361. --==Sending==--
  362. function openStore(playerId)
  363.   local player = Player(playerId)
  364.   if not player then
  365.     return false
  366.   end
  367.  
  368.   if not GameStore.Categories then
  369.     return false
  370.   end
  371.   local msg = NetworkMessage()
  372.   msg:addByte(GameStore.SendingPackets.S_OpenStore)
  373.   if player:getClient().version < 1180 then
  374.     msg:addByte(0x00)
  375.   end
  376.  
  377.   local GameStoreCategories, GameStoreCount = nil, 0
  378.   if (player:getVocation():getId() == 0) then
  379.     GameStoreCategories, GameStoreCount = getCategoriesRook()
  380.   else
  381.     GameStoreCategories, GameStoreCount = GameStore.Categories, #GameStore.Categories
  382.   end
  383.  
  384.   if (GameStoreCategories) then
  385.     msg:addU16(GameStoreCount)
  386.     for k, category in ipairs(GameStoreCategories) do
  387.       msg:addString(category.name)
  388.       if player:getClient().version < 1180 then
  389.         msg:addString(category.description)
  390.       end
  391.  
  392.       if player:getClient().version >= 1093 then
  393.         msg:addByte(category.state or GameStore.States.STATE_NONE)
  394.       end
  395.  
  396.       msg:addByte(#category.icons)
  397.       for m, icon in ipairs(category.icons) do
  398.         msg:addString(icon)
  399.       end
  400.  
  401.       msg:addString(category.parentCategory)
  402.     end
  403.     msg:sendToPlayer(player)
  404.  
  405.     sendCoinBalanceUpdating(playerId, true)
  406.   end
  407. end
  408.  
  409. function sendShowDescription(playerId, offerId)
  410.   local player = Player(playerId)
  411.   if not player then
  412.     return false
  413.   end
  414.   local offer = GameStore.getOfferById(offerId)
  415.   local msg = NetworkMessage()
  416.   msg:addByte(0xea)
  417.   msg:addU32(offerId)
  418.   msg:addString(offer.description or "No description to be displayed")
  419.   msg:sendToPlayer(player)
  420. end
  421.  
  422. function sendShowStoreOffers(playerId, category)
  423.   local player = Player(playerId)
  424.   if not player then
  425.     return false
  426.   end
  427.  
  428.   local msg = NetworkMessage()
  429.   local haveSaleOffer = 0
  430.   msg:addByte(GameStore.SendingPackets.S_StoreOffers)
  431.  
  432.   msg:addString(category.name)
  433.   if player:getClient().version >= 1180 then
  434.     msg:addU32(0)
  435.     if player:getClient().version >= 1185 then
  436.         msg:addU32(0)
  437.     else
  438.         msg:addU16(0)
  439.     end
  440.   end
  441.   msg:addU16(category.offers and #category.offers or 0x00)
  442.  
  443.   if category.offers then
  444.     for k, offer in ipairs(category.offers) do
  445.         local name = ""
  446.         if offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM and offer.count then
  447.             name = offer.count .. "x "
  448.         end
  449.    
  450.         if offer.type == GameStore.OfferTypes.OFFER_TYPE_STACKABLE and offer.count then
  451.             name = offer.number .. "x "
  452.         end
  453.    
  454.         name = name .. (offer.name or "Something Special")
  455.  
  456.         local newPrice = nil
  457.         local offerPrice = 0
  458.         if (offer.state == GameStore.States.STATE_SALE) then
  459.             local daySub = offer.validUntil - os.date("*t").day
  460.             if (daySub < 0) then
  461.             newPrice = offer.basePrice
  462.             end
  463.         end
  464.  
  465.         xpBoostPrice = nil
  466.         if offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then
  467.             xpBoostPrice = GameStore.ExpBoostValues[player:getStorageValue(51052)]
  468.         end
  469.  
  470.         if xpBoostPrice then
  471.             offerPrice = xpBoostPrice
  472.         else
  473.             offerPrice = newPrice or offer.price or 0xFFFF
  474.         end
  475.  
  476.         local disabled, disabledReason = 0, ""
  477.       if offer.disabled == true or not offer.type then
  478.         disabled = 1
  479.       end
  480.  
  481.       if offer.type ~= GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE and
  482.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and
  483.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYSLOT and
  484.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYBONUS and
  485.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_TEMPLE and
  486.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE and
  487.           offer.type ~= GameStore.OfferTypes.OFFER_TYPE_POUCH and
  488.           not offer.id then
  489.         disabled = 1
  490.       end
  491.  
  492.       if disabled == 1 and offer.disabledReason then
  493.         -- dynamic disable
  494.         disabledReason = offer.disabledReason
  495.       end
  496.  
  497.       if disabled ~= 1 then
  498.         if offer.type == GameStore.OfferTypes.OFFER_TYPE_POUCH then
  499.           local pouch = player:getItemById(26377, true)
  500.           if pouch then
  501.             disabled = 1
  502.             disabledReason = "You already have Gold Pouch."
  503.           end
  504.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BLESSINGS then
  505.           if player:hasBlessing(offer.id) and offer.id < 9 then
  506.             disabled = 1
  507.             disabledReason = "You already have this Bless."
  508.           end
  509.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_ALLBLESSINGS then
  510.           if player:hasBlessing(1) and player:hasBlessing(2) and player:hasBlessing(3) and player:hasBlessing(4) and player:hasBlessing(5) and player:hasBlessing(6) and player:hasBlessing(7) and player:hasBlessing(8) then
  511.             disabled = 1
  512.             disabledReason = "You already have all Blessings."
  513.           end
  514.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then
  515.           local outfitLookType
  516.           if player:getSex() == PLAYERSEX_MALE then
  517.             outfitLookType = offer.sexId.male
  518.           else
  519.             outfitLookType = offer.sexId.female
  520.           end
  521.  
  522.           if outfitLookType then
  523.             if offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT and player:hasOutfit(outfitLookType) then
  524.               disabled = 1
  525.               disabledReason = "You already have this outfit."
  526.             elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then
  527.               if player:hasOutfit(outfitLookType) then
  528.                 if player:hasOutfit(outfitLookType, offer.addon) then
  529.                   disabled = 1
  530.                   disabledReason = "You already have this addon."
  531.                 end
  532.               else
  533.                 disabled = 1
  534.                 disabledReason = "You don't have the outfit, you can't buy the addon."
  535.               end
  536.             end
  537.           else
  538.             disabled = 1
  539.             disabledReason = "The offer is fake."
  540.           end
  541.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then
  542.           local hasMount = player:hasMount(offer.id)
  543.           if hasMount == true then
  544.             disabled = 1
  545.             disabledReason = "You already have this mount."
  546.           end
  547.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PROMOTION then
  548.           if GameStore.canAddPromotionToPlayer(playerId, offer.id).ability == false then
  549.             disabled = 1
  550.             disabledReason = "You can't get this promotion"
  551.           end
  552.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYSLOT then
  553.           local unlockedColumns = player:getStorageValue(STORE_SLOT_STORAGE)
  554.           if (unlockedColumns == 1) then
  555.             disabled = 1
  556.             disabledReason = "You already have 3 slots released."
  557.           end
  558.         elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then
  559.           if (player:getStorageValue(51052) == 6 and (os.time() - player:getStorageValue(51053)) < 86400) then
  560.             disabled = 1
  561.             disabledReason = "You can't buy XP Boost for today."
  562.           end
  563.           if (player:getExpBoostStamina()>0) then
  564.             disabled = 1
  565.             disabledReason = "You can't buy XP Boost while you still have one active."
  566.           end
  567.         end
  568.       end
  569.  
  570.         if player:getClient().version >= 1180 then
  571.             msg:addString(name);
  572.             msg:addByte(0x01);
  573.             msg:addU32(offer.id and offer.id or 0xFFFF);
  574.             msg:addU16(1);
  575.             msg:addU32(offerPrice);
  576.  
  577.             msg:addByte(0x00);
  578.  
  579.             msg:addByte(disabled)
  580.             if disabled == 1 and player:getClient().version >= 1093 then
  581.                 msg:addByte(0x01);
  582.                 msg:addString(disabledReason)
  583.             end
  584.  
  585.             if (offer.state) then
  586.                 if (offer.state == GameStore.States.STATE_SALE) then
  587.                   local daySub = offer.validUntil - os.date("*t").day
  588.                   if (daySub >= 0) then
  589.                     msg:addByte(offer.state)
  590.                     msg:addU32(os.time() + daySub * 86400)
  591.                     msg:addU32(offer.basePrice)
  592.                     haveSaleOffer = 1
  593.                   else
  594.                     msg:addByte(GameStore.States.STATE_NONE)
  595.                   end
  596.                 else
  597.                   msg:addByte(offer.state)
  598.                 end
  599.               else
  600.                 msg:addByte(GameStore.States.STATE_NONE)
  601.               end
  602.             msg:addByte(0x00);
  603.  
  604.             msg:addString(offer.icons[1])
  605.            
  606.             msg:addU16(0);
  607.             msg:addU16(0x01);
  608.             msg:addU16(0x0182);
  609.             msg:addU16(0);
  610.             msg:addU16(0);
  611.             msg:addByte(0x00);
  612.         else
  613.             msg:addU32(offer.id and offer.id or 0xFFFF) -- offerid
  614.             msg:addString(name)
  615.             msg:addString(offer.description or GameStore.getDefaultDescription(offer.type))
  616.             msg:addU32(offerPrice)
  617.  
  618.             if (offer.state) then
  619.                 if (offer.state == GameStore.States.STATE_SALE) then
  620.                   local daySub = offer.validUntil - os.date("*t").day
  621.                   if (daySub >= 0) then
  622.                     msg:addByte(offer.state)
  623.                     msg:addU32(os.time() + daySub * 86400)
  624.                     msg:addU32(offer.basePrice)
  625.                     haveSaleOffer = 1
  626.                   else
  627.                     msg:addByte(GameStore.States.STATE_NONE)
  628.                   end
  629.                 else
  630.                   msg:addByte(offer.state)
  631.                 end
  632.               else
  633.                 msg:addByte(GameStore.States.STATE_NONE)
  634.               end
  635.  
  636.               if table.contains({ CLIENTOS_OTCLIENT_LINUX, CLIENTOS_OTCLIENT_WINDOWS, CLIENTOS_OTCLIENT_MAC }, player:getClient().os) then
  637.                 if disabled == 1 then
  638.                   msg:addByte(0) -- offer type 0 means disabled
  639.                 else
  640.                   msg:addByte(offer.type)
  641.                 end
  642.               else
  643.                 -- supporting the old way
  644.                 msg:addByte(disabled)
  645.               end
  646.               if disabled == 1 and player:getClient().version >= 1093 then
  647.                 msg:addString(disabledReason)
  648.               end
  649.        
  650.               msg:addByte(#offer.icons)
  651.               for k, icon in ipairs(offer.icons) do
  652.                 msg:addString(icon)
  653.               end
  654.        
  655.               msg:addU16(0) -- We still don't support SubOffers!
  656.         end
  657.     end
  658.   end
  659.  
  660.   player:sendButtonIndication(haveSaleOffer, 1)
  661.   msg:sendToPlayer(player)
  662. end
  663.  
  664. function sendStoreTransactionHistory(playerId, page, entriesPerPage)
  665.   local player = Player(playerId)
  666.   if not player then
  667.     return false
  668.   end
  669.  
  670.   local totalEntries = GameStore.retrieveHistoryTotalPages(player:getAccountId())
  671.   local totalPages = math.ceil(totalEntries / entriesPerPage)
  672.   local entries = GameStore.retrieveHistoryEntries(player:getAccountId(), page, entriesPerPage) -- this makes everything easy!
  673.   if #entries == 0 then
  674.     return addPlayerEvent(sendStoreError, 250, playerId, GameStore.StoreErrors.STORE_ERROR_HISTORY, "You don't have any entries yet.")
  675.   end
  676.  
  677.   local msg = NetworkMessage()
  678.   msg:addByte(GameStore.SendingPackets.S_OpenTransactionHistory)
  679.  
  680.   msg:addU32(totalPages > 0 and page - 1 or 0x0) -- current page
  681.   msg:addU32(totalPages > 0 and totalPages or 0x0) -- total page
  682.   msg:addByte(#entries)
  683.  
  684.   for k, entry in ipairs(entries) do
  685.     msg:addU32(entry.time)
  686.     msg:addByte(entry.mode)
  687.     msg:addU32(entry.amount)
  688.     if player:getClient().version >= 1200 then
  689.      msg:addByte(0x0) -- 0 = transferable tibia coin, 1 = normal tibia coin
  690.     end
  691.     msg:addString(entry.description)
  692.   end
  693.  
  694.   msg:sendToPlayer(player)
  695. end
  696.  
  697. function sendStorePurchaseSuccessful(playerId, message)
  698.   local player = Player(playerId)
  699.   if not player then
  700.     return false
  701.   end
  702.  
  703.   local msg = NetworkMessage()
  704.   msg:addByte(GameStore.SendingPackets.S_CompletePurchase)
  705.  
  706.   msg:addByte(0x00)
  707.  
  708.   msg:addString(message)
  709.   msg:addU32(player:getCoinsBalance())
  710.   msg:addU32(player:getCoinsBalance())
  711.  
  712.   msg:sendToPlayer(player)
  713. end
  714.  
  715. function sendStoreError(playerId, errorType, message)
  716.   local player = Player(playerId)
  717.   if not player then
  718.     return false
  719.   end
  720.  
  721.   local msg = NetworkMessage()
  722.   msg:addByte(GameStore.SendingPackets.S_StoreError)
  723.  
  724.   msg:addByte(errorType)
  725.   msg:addString(message)
  726.  
  727.   msg:sendToPlayer(player)
  728. end
  729.  
  730. function sendCoinBalanceUpdating(playerId, updating)
  731.   local player = Player(playerId)
  732.   if not player then
  733.     return false
  734.   end
  735.  
  736.   local msg = NetworkMessage()
  737.   msg:addByte(GameStore.SendingPackets.S_CoinBalanceUpdating)
  738.   msg:addByte(0x00)
  739.   msg:sendToPlayer(player)
  740.  
  741.   if updating == true then
  742.     sendUpdateCoinBalance(playerId)
  743.   end
  744. end
  745.  
  746. function sendUpdateCoinBalance(playerId)
  747.   local player = Player(playerId)
  748.   if not player then
  749.     return false
  750.   end
  751.  
  752.   local msg = NetworkMessage()
  753.   msg:addByte(GameStore.SendingPackets.S_CoinBalanceUpdating)
  754.   msg:addByte(0x01)
  755.  
  756.   msg:addByte(GameStore.SendingPackets.S_CoinBalance)
  757.   msg:addByte(0x01)
  758.  
  759.   msg:addU32(player:getCoinsBalance())
  760.   msg:addU32(player:getCoinsBalance())
  761.  
  762.   msg:sendToPlayer(player)
  763. end
  764.  
  765. function sendRequestPurchaseData(playerId, offerId, type)
  766.   local player = Player(playerId)
  767.   if not player then
  768.     return false
  769.   end
  770.  
  771.   local msg = NetworkMessage()
  772.   msg:addByte(GameStore.SendingPackets.S_RequestPurchaseData)
  773.   msg:addU32(offerId)
  774.   msg:addByte(type)
  775.   msg:sendToPlayer(player)
  776. end
  777.  
  778. --==GameStoreFunctions==--
  779. GameStore.getCategoryByName = function(name)
  780.   for k, category in ipairs(GameStore.Categories) do
  781.     if category.name:lower() == name:lower() then
  782.       return category
  783.     end
  784.   end
  785.   return nil
  786. end
  787.  
  788. GameStore.getOfferById = function(id)
  789.   for Cat_k, category in ipairs(GameStore.Categories) do
  790.     if category.offers then
  791.       for Off_k, offer in ipairs(category.offers) do
  792.         if type(offer.id) == "number" then
  793.           if offer.id == id then
  794.             return offer
  795.           end
  796.         elseif type(offer.id) == "table" then
  797.           for m, offerId in pairs(offer.id) do
  798.             -- in case of outfits we have offer.id = {male = ..., female = ...}
  799.             if offerId == id then
  800.               return offer
  801.             end
  802.           end
  803.         end
  804.  
  805.       end
  806.     end
  807.   end
  808.   return nil
  809. end
  810.  
  811. GameStore.haveCategoryRook = function()
  812.   for Cat_k, category in ipairs(GameStore.Categories) do
  813.     if category.offers and category.rookgaard then
  814.       return true
  815.     end
  816.   end
  817.  
  818.   return false
  819. end
  820.  
  821. GameStore.haveOfferRook = function(id)
  822.   for Cat_k, category in ipairs(GameStore.Categories) do
  823.     if category.offers and category.rookgaard then
  824.       for Off_k, offer in ipairs(category.offers) do
  825.         if offer.id == id then
  826.           return true
  827.         end
  828.       end
  829.     end
  830.   end
  831.   return nil
  832. end
  833.  
  834. GameStore.insertHistory = function(accountId, mode, description, amount)
  835.   return db.query(string.format("INSERT INTO `store_history`(`account_id`, `mode`, `description`, `coin_amount`, `time`) VALUES (%s, %s, %s, %s, %s)", accountId, mode, db.escapeString(description), amount, os.time()))
  836. end
  837.  
  838. GameStore.retrieveHistoryTotalPages = function (accountId)
  839.   local resultId = db.storeQuery("SELECT count(id) as total FROM store_history WHERE account_id = " .. accountId)
  840.   if resultId == false then
  841.     return 0
  842.   end
  843.  
  844.   local totalPages = result.getDataInt(resultId, "total")
  845.   result.free(resultId)
  846.   return totalPages
  847. end
  848.  
  849. GameStore.retrieveHistoryEntries = function(accountId, currentPage, entriesPerPage)
  850.   local entries = {}
  851.   local offset = currentPage > 1 and entriesPerPage * (currentPage - 1) or 0
  852.  
  853.   local resultId = db.storeQuery("SELECT * FROM `store_history` WHERE `account_id` = " .. accountId .. " ORDER BY `time` DESC LIMIT " .. offset .. ", " .. entriesPerPage .. ";")
  854.   if resultId ~= false then
  855.     repeat
  856.       local entry = {
  857.         mode = result.getDataInt(resultId, "mode"),
  858.         description = result.getDataString(resultId, "description"),
  859.         amount = result.getDataInt(resultId, "coin_amount"),
  860.         time = result.getDataInt(resultId, "time"),
  861.       }
  862.       table.insert(entries, entry)
  863.     until not result.next(resultId)
  864.     result.free(resultId)
  865.   end
  866.   return entries
  867. end
  868.  
  869. GameStore.getDefaultDescription = function(offerType)
  870.   local t, descList = GameStore.OfferTypes
  871.   if offerType == t.OFFER_TYPE_OUTFIT or offerType == t.OFFER_TYPE_OUTFIT_ADDON then
  872.     descList = GameStore.DefaultDescriptions.OUTFIT
  873.   elseif offerType == t.OFFER_TYPE_MOUNT then
  874.     descList = GameStore.DefaultDescriptions.MOUNT
  875.   elseif offerType == t.OFFER_TYPE_NAMECHANGE then
  876.     descList = GameStore.DefaultDescriptions.NAMECHANGE
  877.   elseif offerType == t.OFFER_TYPE_SEXCHANGE then
  878.     descList = GameStore.DefaultDescriptions.SEXCHANGE
  879.   elseif offerType == t.OFFER_TYPE_EXPBOOST then
  880.     descList = GameStore.DefaultDescriptions.EXPBOOST
  881.   elseif offerType == t.OFFER_TYPE_PREYSLOT then
  882.     descList = GameStore.DefaultDescriptions.PREYSLOT
  883.   elseif offerType == t.OFFER_TYPE_PREYBONUS then
  884.     descList = GameStore.DefaultDescriptions.PREYBONUS
  885.   elseif offerType == t.OFFER_TYPE_TEMPLE then
  886.     descList = GameStore.DefaultDescriptions.TEMPLE
  887.   else
  888.     return ""
  889.   end
  890.  
  891.   return descList[math.floor(math.random(1, #descList))] or ""
  892. end
  893.  
  894. GameStore.canChangeToName = function(name)
  895.   local result = {
  896.     ability = false
  897.   }
  898.   if name:len() < 3 or name:len() > 14 then
  899.     result.reason = "The length of your new name must be between 3 and 14 characters."
  900.     return result
  901.   end
  902.  
  903.   local match = name:gmatch("%s+")
  904.   local count = 0
  905.   for v in match do
  906.     count = count + 1
  907.   end
  908.  
  909.   local matchtwo = name:match("^%s+")
  910.   if (matchtwo) then
  911.     result.reason = "Your new name can't have whitespace at begin."
  912.     return result
  913.   end
  914.  
  915.   if (count > 1) then
  916.     result.reason = "Your new name have more than 1 whitespace."
  917.     return result
  918.   end
  919.  
  920.   -- just copied from znote aac.
  921.   local words = { "owner", "gamemaster", "hoster", "admin", "staff", "tibia", "account", "god", "anal", "ass", "fuck", "sex", "hitler", "pussy", "dick", "rape", "adm", "cm", "gm", "tutor", "counsellor" }
  922.   local split = name:split(" ")
  923.   for k, word in ipairs(words) do
  924.     for k, nameWord in ipairs(split) do
  925.       if nameWord:lower() == word then
  926.         result.reason = "You can't use word \"" .. word .. "\" in your new name."
  927.         return result
  928.       end
  929.     end
  930.   end
  931.  
  932.   local tmpName = name:gsub("%s+", "")
  933.   for i = 1, #words do
  934.     if (tmpName:lower():find(words[i])) then
  935.       result.reason = "You can't use word \"" .. words[i] .. "\" with whitespace in your new name."
  936.       return result
  937.     end
  938.   end
  939.  
  940.   if MonsterType(name) then
  941.     result.reason = "Your new name \"" .. name .. "\" can't be a monster's name."
  942.     return result
  943.   elseif Npc(name) then
  944.     result.reason = "Your new name \"" .. name .. "\" can't be a npc's name."
  945.     return result
  946.   end
  947.  
  948.   local letters = "{}|_*+-=<>0123456789@#%^&()/*'\\.,:;~!\"$"
  949.   for i = 1, letters:len() do
  950.     local c = letters:sub(i, i)
  951.     for i = 1, name:len() do
  952.       local m = name:sub(i, i)
  953.       if m == c then
  954.         result.reason = "You can't use this letter \"" .. c .. "\" in your new name."
  955.         return result
  956.       end
  957.     end
  958.   end
  959.   result.ability = true
  960.   return result
  961. end
  962.  
  963. GameStore.canAddPromotionToPlayer = function(playerId, promotion, send)
  964.   local player = Player(playerId)
  965.   if not player then
  966.     return false
  967.   end
  968.  
  969.   local result = {
  970.     ability = true
  971.   }
  972.   local vocation = player:getVocation()
  973.   -- Working --
  974.   local vocationCopy, baseVocation = vocation, vocation
  975.   vocation = vocation:getDemotion()
  976.   while vocation do
  977.     baseVocation = vocation
  978.     vocation = vocation:getDemotion()
  979.   end
  980.  
  981.   local baseVocationsCount = GameStore.BaseVocationsCount or 4
  982.  
  983.   local newVocId = (baseVocationsCount * promotion) + baseVocation:getId()
  984.  
  985.   if not Vocation(newVocId) then
  986.     if send then
  987.       addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_NETWORK, "The offer is fake, please report it!")
  988.     end
  989.     result.ability = false
  990.     return result
  991.   end
  992.   -- If promotion is less than player's voc, or player don't have previous promotion
  993.   if newVocId <= vocationCopy:getId() then
  994.     if send then
  995.       addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_NETWORK, "You already have this promotion!")
  996.     end
  997.     result.ability = false
  998.     return result
  999.   end
  1000.  
  1001.   if (newVocId - baseVocationsCount) ~= vocationCopy:getId() then
  1002.     if send then
  1003.       addPlayerEvent(sendStoreError, 350, playerId, GameStore.StoreErrors.STORE_ERROR_NETWORK, "You need higher promotion to get his one.")
  1004.     end
  1005.     result.ability = false
  1006.     return result
  1007.   end
  1008.  
  1009.   result.vocId = newVocId
  1010.   return result
  1011. end
  1012.  
  1013. GameStore.addPromotionToPlayer = function(playerId, promotion)
  1014.   local player = Player(playerId)
  1015.   if not player then
  1016.     return false
  1017.   end
  1018.  
  1019.   local result = GameStore.canAddPromotionToPlayer(player, promotion, true)
  1020.   if result.ability == false then return false end
  1021.  
  1022.   local basics = {
  1023.     health = 185,
  1024.     mana = 40,
  1025.     cap = 500
  1026.   }
  1027.  
  1028.   player:setVocation(result.vocId)
  1029.   local newVoc = player:getVocation()
  1030.   player:setMaxHealth(basics.health + (newVoc:getHealthGain() * player:getLevel()))
  1031.   player:setMaxMana(basics.mana + (newVoc:getManaGain() * player:getLevel()))
  1032.   player:setCapacity(basics.cap + (newVoc:getCapacityGain() * player:getLevel()))
  1033.  
  1034.   player:addHealth(player:getMaxHealth())
  1035.   player:addMana(player:getMaxMana())
  1036.  
  1037.   player:sendTextMessage(MESSAGE_INFO_DESCR, "You have been promoted to " .. newVoc:getName())
  1038.   return true
  1039. end
  1040.  
  1041. --
  1042. -- PURCHASE PROCESSOR FUNCTIONS
  1043. -- Must throw an error when the purchase has not been made. The error must of
  1044. -- take a table {code = ..., message = ...} if the error is handled. When no code
  1045. -- index is present the error is assumed to be unhandled.
  1046.  
  1047. function GameStore.processItemPurchase(player, offerId, offerCount)
  1048.   if player:getFreeCapacity() < ItemType(offerId):getWeight(offerCount) then
  1049.     return error({ code = 0, message = "Please make sure you have free capacity to hold this item."})
  1050.   end
  1051.  
  1052.   local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
  1053.   if inbox and inbox:getEmptySlots() > offerCount then
  1054.     for t = 1, offerCount do
  1055.       inbox:addItem(offerId, offerCount or 1)
  1056.     end
  1057.   else
  1058.     return error({ code = 0, message = "Please make sure you have free slots in your store inbox."})
  1059.   end
  1060. end
  1061.  
  1062. function GameStore.processSignleBlessingPurchase(player, offerId)
  1063.   if not player:hasBlessing(offerId) then
  1064.     player:addBlessing(offerId, 1)
  1065.   else
  1066.     return error({ code = 0, message = "You already have this blessing."})
  1067.   end
  1068. end
  1069.  
  1070. function GameStore.processAllBlessingsPurchase(player)
  1071.   if player:hasBlessing(1) and player:hasBlessing(2) and player:hasBlessing(3) and player:hasBlessing(4) and player:hasBlessing(5) and player:hasBlessing(6) and player:hasBlessing(7) and player:hasBlessing(8) then
  1072.     return error({ code = 0, message = "You already have all blessings."})
  1073.   else
  1074.     player:addBlessing(1, 1)
  1075.     player:addBlessing(2, 1)
  1076.     player:addBlessing(3, 1)
  1077.     player:addBlessing(4, 1)
  1078.     player:addBlessing(5, 1)
  1079.     player:addBlessing(6, 1)
  1080.     player:addBlessing(7, 1)
  1081.     player:addBlessing(8, 1)
  1082.   end
  1083. end
  1084.  
  1085. function GameStore.processPremiumPurchase(player, offerId)
  1086.   player:addPremiumDays(offerId-3000)
  1087.  
  1088.   -- Update Prey Data
  1089.   for slot = CONST_PREY_SLOT_FIRST, CONST_PREY_SLOT_THIRD do
  1090.     player:sendPreyData(slot)
  1091.   end
  1092. end
  1093.  
  1094. function GameStore.processStackablePurchase(player, offerId, offerCount, offerName)
  1095.   local function isKegExerciseItem(itemId)
  1096.     return ((itemId >= ITEM_KEG_START and itemId <= ITEM_KEG_END) or (itemId >= ITEM_EXERCISE_START and itemId <= ITEM_EXERCISE_END) or itemId == 32109)
  1097.   end
  1098.  
  1099.   if isKegExerciseItem(offerId) then
  1100.     if player:getFreeCapacity() < ItemType(offerId):getWeight(1) then
  1101.       return error({code = 0, message = "Please make sure you have free capacity to hold this item."})
  1102.     end
  1103.   else
  1104.     if player:getFreeCapacity() < ItemType(offerId):getWeight(offerCount) then
  1105.       return error({code = 0, message = "Please make sure you have free capacity to hold this item."})  
  1106.     end
  1107.   end
  1108.  
  1109.   local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
  1110.   if inbox and inbox:getEmptySlots() > 0 then
  1111.     if (isKegExerciseItem(offerId)) then
  1112.       if (offerCount >= 500) then
  1113.         local pendingCount = offerCount
  1114.         while (pendingCount > 0) do
  1115.             local pack
  1116.             if (pendingCount > 500) then
  1117.                 pack = 500
  1118.             else
  1119.                 pack = pendingCount
  1120.             end
  1121.             local kegExerciseItem = inbox:addItem(offerId, 1)
  1122.             kegExerciseItem:setAttribute(ITEM_ATTRIBUTE_CHARGES, pack)
  1123.             pendingCount = pendingCount - pack
  1124.         end
  1125.       else
  1126.         local kegExerciseItem = inbox:addItem(offerId, 1)
  1127.         kegExerciseItem:setAttribute(ITEM_ATTRIBUTE_CHARGES, offerCount)
  1128.       end
  1129.     elseif (offerCount > 100) then
  1130.         local pendingCount = offerCount
  1131.         while (pendingCount > 0) do
  1132.             local pack
  1133.             if (pendingCount > 100) then
  1134.                 pack = 100
  1135.             else
  1136.                 pack = pendingCount
  1137.             end
  1138.             inbox:addItem(offerId, pack)
  1139.             pendingCount = pendingCount - pack
  1140.         end
  1141.     else
  1142.       inbox:addItem(offerId, offerCount)
  1143.     end
  1144.   else
  1145.     return error({code = 0, message = "Please make sure you have free slots in your store inbox."})
  1146.   end
  1147. end
  1148.  
  1149. function GameStore.processHouseRelatedPurchase(player, offerId, offerCount)
  1150.     local function isCaskItem(itemId)
  1151.         return (itemId >= ITEM_HEALTH_CASK_START and itemId <= ITEM_HEALTH_CASK_END) or
  1152.         (itemId >= ITEM_MANA_CASK_START and itemId <= ITEM_MANA_CASK_END) or
  1153.         (itemId >= ITEM_SPIRIT_CASK_START and itemId <= ITEM_SPIRIT_CASK_END)
  1154.     end
  1155.  
  1156.     local inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)
  1157.     if inbox and inbox:getEmptySlots() > 0 then
  1158.         local decoKit = inbox:addItem(26054, 1)
  1159.         local function changeKit(kit)
  1160.             local decoItemName = ItemType(offerId):getName()
  1161.             if kit then
  1162.                 kit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, "You bought this item in the Store.\nUnwrap it in your own house to create a <" .. decoItemName .. ">.")
  1163.                 kit:setCustomAttribute("unWrapId", offerId)
  1164.                 print(1)
  1165.            
  1166.                 if isCaskItem(offerId) then
  1167.                     kit:setAttribute(ITEM_ATTRIBUTE_DATE, offerCount)
  1168.                     print(2)
  1169.                 end
  1170.             end
  1171.         end
  1172.         addEvent(function() changeKit(decoKit) end, 250)
  1173.     else
  1174.     return error({code = 0, message = "Please make sure you have free slots in your store inbox."})
  1175.     end
  1176. end
  1177.  
  1178. function GameStore.processOutfitPurchase(player, offerSexIdTable, addon)
  1179.   local looktype
  1180.   local _addon = addon and addon or 0
  1181.  
  1182.   if player:getSex() == PLAYERSEX_MALE then
  1183.     looktype = offerSexIdTable.male
  1184.   elseif player:getSex() == PLAYERSEX_FEMALE then
  1185.     looktype = offerSexIdTable.female
  1186.   end
  1187.  
  1188.   if not looktype then
  1189.     return error({code = 0, message = "This outfit seems not to suit your sex, we are sorry for that!"})
  1190.   elseif (not player:hasOutfit(looktype, 0)) and (_addon == 1 or _addon == 2) then
  1191.     return error({code = 0, message = "You must own the outfit before you can buy its addon."})
  1192.   elseif player:hasOutfit(looktype, _addon) then
  1193.     return error({code = 0, message = "You already own this outfit."})
  1194.   else
  1195.     if not (player:addOutfitAddon(looktype, _addon))  -- TFS call failed
  1196.         or (not player:hasOutfit(looktype, _addon))   -- Additional check; if the looktype doesn't match player sex for example,
  1197.                                                       --   then the TFS check will still pass... bug? (TODO)
  1198.     then
  1199.       error({ code = 0, message = "There has been an issue with your outfit purchase. Your purchase has been cancelled."})
  1200.     end
  1201.   end
  1202. end
  1203.  
  1204. function GameStore.processMountPurchase(player, offerId)
  1205.   player:addMount(offerId)
  1206. end
  1207.  
  1208. function GameStore.processNameChangePurchase(player, offer, productType, newName)
  1209.   local playerId = player:getId()
  1210.   local offerId = offer.id
  1211.  
  1212.   if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE then
  1213.     local tile = Tile(player:getPosition())
  1214.     if (tile) then
  1215.       if (not tile:hasFlag(TILESTATE_PROTECTIONZONE)) then
  1216.         return error({code = 1, message = "You can change name only in Protection Zone."})
  1217.       end
  1218.     end
  1219.  
  1220.     local resultId = db.storeQuery("SELECT * FROM `players` WHERE `name` = " .. db.escapeString(newName) .. "")
  1221.     if resultId ~= false then
  1222.       return error({code = 1, message = "This name is already used, please try again!"})
  1223.     end
  1224.  
  1225.     local result = GameStore.canChangeToName(newName)
  1226.     if not result.ability then
  1227.       return error({code = 1, message = result.reason})
  1228.     end
  1229.  
  1230.     newName = newName:lower():gsub("(%l)(%w*)", function(a, b) return string.upper(a) .. b end)
  1231.     db.query("UPDATE `players` SET `name` = " .. db.escapeString(newName) .. " WHERE `id` = " .. player:getGuid())
  1232.     player:removeCoinsBalance(offer.price)
  1233.     GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offer.name, (offer.price) * -1)
  1234.     message = "You have successfully changed you name, relogin!"
  1235.     addEvent(function()
  1236.       local player = Player(playerId)
  1237.       if not player then
  1238.         return false
  1239.       end
  1240.  
  1241.       player:remove()
  1242.     end, 500)
  1243.     -- If not, we ask him to do!
  1244.   else
  1245.     return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE)
  1246.   end
  1247. end
  1248.  
  1249. function GameStore.processSexChangePurchase(player)
  1250.   player:toggleSex()
  1251. end
  1252.  
  1253.  
  1254. function GameStore.processExpBoostPuchase(player)
  1255.   local currentExpBoostTime = player:getExpBoostStamina()
  1256.  
  1257.   player:setStoreXpBoost(50)
  1258.   player:setExpBoostStamina(currentExpBoostTime + 3600)
  1259.  
  1260.   if (player:getStorageValue(51052) == -1 or player:getStorageValue(51052) == 6) then
  1261.     player:setStorageValue(51052, 1)
  1262.   end
  1263.  
  1264.   player:setStorageValue(51052, player:getStorageValue(51052) + 1)
  1265.   player:setStorageValue(51053, os.time()) -- last bought
  1266. end
  1267.  
  1268. function GameStore.processPreySlotPurchase(player)
  1269.     if player:getStorageValue(STORE_SLOT_STORAGE) < 1 then
  1270.         player:setStorageValue(STORE_SLOT_STORAGE, 1)
  1271.         player:setPreyUnlocked(CONST_PREY_SLOT_THIRD, 2)
  1272.         player:setPreyState(CONST_PREY_SLOT_THIRD, 1)
  1273.        
  1274.         -- Update Prey Data
  1275.         for slot = CONST_PREY_SLOT_FIRST, CONST_PREY_SLOT_THIRD do
  1276.             player:sendPreyData(slot)
  1277.         end
  1278.     end
  1279. end
  1280.  
  1281. function GameStore.processPreyBonusReroll(player, offerCount)
  1282.     player:setPreyBonusRerolls(player:getPreyBonusRerolls() + offerCount)
  1283. end
  1284.  
  1285. function GameStore.processTempleTeleportPurchase(player)
  1286.   if player:getCondition(CONDITION_INFIGHT) or player:isPzLocked() then
  1287.     return error({code = 0, message = "You can't use temple teleport in fight!"})
  1288.   end
  1289.  
  1290.   player:teleportTo(player:getTown():getTemplePosition())
  1291.   player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
  1292.   player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 'You have been teleported to your hometown.')
  1293. end
  1294.  
  1295. function GameStore.processPromotionPurchase(player, offerId)
  1296.   if not GameStore.addPromotionToPlayer(player.id, offerId) then
  1297.     return error({code = 0})
  1298.   end
  1299. end
  1300.  
  1301. --==Player==--
  1302. function Player.getCoinsBalance(self)
  1303.   resultId = db.storeQuery("SELECT `coins` FROM `accounts` WHERE `id` = " .. self:getAccountId())
  1304.   if not resultId then return 0 end
  1305.   return result.getDataInt(resultId, "coins")
  1306. end
  1307.  
  1308. function Player.setCoinsBalance(self, coins)
  1309.   db.query("UPDATE `accounts` SET `coins` = " .. coins .. " WHERE `id` = " .. self:getAccountId())
  1310.   return true
  1311. end
  1312.  
  1313. function Player.canRemoveCoins(self, coins)
  1314.   if self:getCoinsBalance() < coins then
  1315.     return false
  1316.   end
  1317.   return true
  1318. end
  1319.  
  1320. function Player.removeCoinsBalance(self, coins)
  1321.   if self:canRemoveCoins(coins) then
  1322.     return self:setCoinsBalance(self:getCoinsBalance() - coins)
  1323.   end
  1324.  
  1325.   return false
  1326. end
  1327.  
  1328. function Player.addCoinsBalance(self, coins, update)
  1329.   self:setCoinsBalance(self:getCoinsBalance() + coins)
  1330.   if update then sendCoinBalanceUpdating(self, true) end
  1331.   return true
  1332. end
  1333.  
  1334. function Player.sendButtonIndication(self, value1, value2)
  1335.   local msg = NetworkMessage()
  1336.   msg:addByte(0x19)
  1337.   msg:addByte(value1) -- Sale
  1338.   msg:addByte(value2) -- New Item
  1339.   msg:sendToPlayer(self)
  1340. end
  1341.  
  1342. function Player.toggleSex(self)
  1343.   local currentSex = self:getSex()
  1344.   local playerOutfit = self:getOutfit()
  1345.  
  1346.   if currentSex == PLAYERSEX_FEMALE then
  1347.     self:setSex(PLAYERSEX_MALE)
  1348.     playerOutfit.lookType = 128
  1349.   else
  1350.     self:setSex(PLAYERSEX_FEMALE)
  1351.     playerOutfit.lookType = 136
  1352.   end
  1353.   self:setOutfit(playerOutfit)
  1354. end
Add Comment
Please, Sign In to add comment