Boas Guys, estou tendo problema com a store ingame no Canary 2.6.0 versão 13.10, os itens não chegando no Inbox dos chars, e não mostra nenhum erro no debug do server ou do mysql...
Obs.: sistema de Inbox funcionando 100%
Obs2.: ao comprar o item não aparece nenhuma mensagem e desconta o cap do player sem receber o item.
init.lua:
GameStore ={
ModuleName ="GameStore",
Developers ={"Cjaker","metabob","Rick"},
Version ="1.1",
LastUpdated ="25-07-2020 11:52AM"}--== Enums ==--
GameStore.OfferTypes ={
OFFER_TYPE_NONE =0,
OFFER_TYPE_ITEM =1,
OFFER_TYPE_STACKABLE =2,
OFFER_TYPE_CHARGES =3,
OFFER_TYPE_OUTFIT =4,
OFFER_TYPE_OUTFIT_ADDON =5,
OFFER_TYPE_MOUNT =6,
OFFER_TYPE_NAMECHANGE =7,
OFFER_TYPE_SEXCHANGE =8,
OFFER_TYPE_HOUSE =9,
OFFER_TYPE_EXPBOOST =10,
OFFER_TYPE_PREYSLOT =11,
OFFER_TYPE_PREYBONUS =12,
OFFER_TYPE_TEMPLE =13,
OFFER_TYPE_BLESSINGS =14,
OFFER_TYPE_PREMIUM =15,
OFFER_TYPE_POUNCH =16,
OFFER_TYPE_ALLBLESSINGS =17,
OFFER_TYPE_INSTANT_REWARD_ACCESS =18,
OFFER_TYPE_CHARMS =19,
OFFER_TYPE_HIRELING =20,
OFFER_TYPE_HIRELING_NAMECHANGE =21,
OFFER_TYPE_HIRELING_SEXCHANGE =22,
OFFER_TYPE_HIRELING_SKILL =23,
OFFER_TYPE_HIRELING_OUTFIT =24,
OFFER_TYPE_HUNTINGSLOT =25}
GameStore.SubActions ={
PREY_THIRDSLOT_REAL =0,
PREY_WILDCARD =1,
INSTANT_REWARD =2,
BLESSING_TWIST =3,
BLESSING_SOLITUDE =4,
BLESSING_PHOENIX =5,
BLESSING_SUNS =6,
BLESSING_SPIRITUAL =7,
BLESSING_EMBRACE =8,
BLESSING_HEART =9,
BLESSING_BLOOD =10,
BLESSING_ALL_PVE =11,
BLESSING_ALL_PVP =12,
CHARM_EXPANSION =13,
TASKHUNTING_THIRDSLOT =14,
PREY_THIRDSLOT_REDIRECT =15}
GameStore.ActionType ={
OPEN_HOME =0,
OPEN_PREMIUM_BOOST =1,
OPEN_CATEGORY =2,
OPEN_USEFUL_THINGS =3,
OPEN_OFFER =4,}
GameStore.CointType ={
Coin =0,
Transferable =1,
Tournament =2,}
GameStore.Storages ={
expBoostCount =51052}
GameStore.ConverType ={
SHOW_NONE =0,
SHOW_MOUNT =1,
SHOW_OUTFIT =2,
SHOW_ITEM =3,
SHOW_HIRELING =4}
GameStore.ConfigureOffers ={
SHOW_NORMAL =0,
SHOW_CONFIGURE =1}function convertType(type)local types ={[GameStore.OfferTypes.OFFER_TYPE_OUTFIT]= GameStore.ConverType.SHOW_OUTFIT,[GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON]= GameStore.ConverType.SHOW_OUTFIT,[GameStore.OfferTypes.OFFER_TYPE_MOUNT]= GameStore.ConverType.SHOW_MOUNT,[GameStore.OfferTypes.OFFER_TYPE_ITEM]= GameStore.ConverType.SHOW_ITEM,[GameStore.OfferTypes.OFFER_TYPE_STACKABLE]= GameStore.ConverType.SHOW_ITEM,[GameStore.OfferTypes.OFFER_TYPE_HOUSE]= GameStore.ConverType.SHOW_ITEM,[GameStore.OfferTypes.OFFER_TYPE_CHARGES]= GameStore.ConverType.SHOW_ITEM,[GameStore.OfferTypes.OFFER_TYPE_HIRELING]= GameStore.ConverType.SHOW_HIRELING,}ifnot types[type]thenreturn GameStore.ConverType.SHOW_NONE
endreturn types[type]endfunction useOfferConfigure(type)local types ={[GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE]= GameStore.ConfigureOffers.SHOW_CONFIGURE,[GameStore.OfferTypes.OFFER_TYPE_HIRELING]= GameStore.ConfigureOffers.SHOW_CONFIGURE,[GameStore.OfferTypes.OFFER_TYPE_HIRELING_NAMECHANGE]= GameStore.ConfigureOffers.SHOW_CONFIGURE,[GameStore.OfferTypes.OFFER_TYPE_HIRELING_SEXCHANGE]= GameStore.ConfigureOffers.SHOW_CONFIGURE
}ifnot types[type]thenreturn GameStore.ConfigureOffers.SHOW_NORMAL
endreturn types[type]end
GameStore.ClientOfferTypes ={
CLIENT_STORE_OFFER_OTHER =0,
CLIENT_STORE_OFFER_NAMECHANGE =1,
CLIENT_STORE_OFFER_HIRELING =3,}
GameStore.HistoryTypes ={
HISTORY_TYPE_NONE =0,
HISTORY_TYPE_GIFT =1,
HISTORY_TYPE_REFUND =2}
GameStore.States ={
STATE_NONE =0,
STATE_NEW =1,
STATE_SALE =2,
STATE_TIMED =3}
GameStore.StoreErrors ={
STORE_ERROR_PURCHASE =0,
STORE_ERROR_NETWORK =1,
STORE_ERROR_HISTORY =2,
STORE_ERROR_TRANSFER =3,
STORE_ERROR_INFORMATION =4}
GameStore.ServiceTypes ={
SERVICE_STANDERD =0,
SERVICE_OUTFITS =3,
SERVICE_MOUNTS =4,
SERVICE_BLESSINGS =5}
GameStore.SendingPackets ={
S_CoinBalance =0xDF,-- 223
S_StoreError =0xE0,-- 224
S_RequestPurchaseData =0xE1,-- 225
S_CoinBalanceUpdating =0xF2,-- 242
S_OpenStore =0xFB,-- 251
S_StoreOffers =0xFC,-- 252
S_OpenTransactionHistory =0xFD,-- 253
S_CompletePurchase =0xFE-- 254}
GameStore.RecivedPackets ={
C_StoreEvent =0xE9,-- 233--(transfer-off) C_TransferCoins = 0xEF, -- 239
C_ParseHirelingName =0xEC,-- 236
C_OpenStore =0xFA,-- 250
C_RequestStoreOffers =0xFB,-- 251
C_BuyStoreOffer =0xFC,-- 252
C_OpenTransactionHistory =0xFD,-- 253
C_RequestTransactionHistory =0xFE,-- 254}
GameStore.ExpBoostValues ={[1]=30,[2]=45,[3]=90,[4]=180,[5]=360}
GameStore.DefaultValues ={
DEFAULT_VALUE_ENTRIES_PER_PAGE =26}
GameStore.DefaultDescriptions ={
OUTFIT ={"This outfit looks nice. Only high-class people are able to wear it!","An outfit that was created to suit you. We are sure you'll like it.","Legend says only smart people should wear it, otherwise you will burn!"},
MOUNT ={"This is a fantastic mount that helps to become faster, try it!","The first rider of this mount became the leader of his country! legends say that."},
NAMECHANGE ={"Are you hunted? Tired of that? Get a new name, a new life!","A new name to suit your needs!"},
SEXCHANGE ={"Bored of your character's sex? Get a new sex for him now!!"},
EXPBOOST ={"Are you tired of leveling slow? try it!"},
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."},
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."},
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."}}--==Parsing==--
GameStore.isItsPacket =function(byte)for k, v in pairs(GameStore.RecivedPackets)doif v == byte thenreturntrueendendreturnfalseendlocalfunction queueSendStoreAlertToUser(message, delay, playerId, storeErrorCode)
storeErrorCode = storeErrorCode and storeErrorCode or GameStore.StoreErrors.STORE_ERROR_NETWORK
addPlayerEvent(sendStoreError, delay, playerId, storeErrorCode, message)endfunction onRecvbyte(player, msg, byte)ifnot configManager.getBoolean(STOREMODULES)thenreturntrueendif player:getVocation():getId()==0andnot GameStore.haveCategoryRook()thenreturn player:sendCancelMessage("Store don't have offers for rookgaard citizen.")endlocal exaust = player:getStorageValue(Storage.StoreExaust)local currentTime = os.time()if byte == GameStore.RecivedPackets.C_StoreEvent thenelseif byte == GameStore.RecivedPackets.C_TransferCoins then
parseTransferCoins(player:getId(), msg)elseif byte == GameStore.RecivedPackets.C_OpenStore thenif exaust > currentTime then
player:sendCancelMessage("You are exhausted")returnfalseendlocal num = currentTime +1
player:setStorageValue(Storage.StoreExaust, num)
parseOpenStore(player:getId(), msg)elseif byte == GameStore.RecivedPackets.C_RequestStoreOffers then
parseRequestStoreOffers(player:getId(), msg)elseif byte == GameStore.RecivedPackets.C_BuyStoreOffer then
parseBuyStoreOffer(player:getId(), msg)elseif byte == GameStore.RecivedPackets.C_OpenTransactionHistory then
parseOpenTransactionHistory(player:getId(), msg)elseif byte == GameStore.RecivedPackets.C_RequestTransactionHistory then
parseRequestTransactionHistory(player:getId(), msg)endreturntrueendfunction parseTransferCoins(playerId, msg)local player = Player(playerId)ifnot player thenreturnfalseendlocal reciver = msg:getString()local amount = msg:getU32()if(player:getCoinsBalance()< amount)thenreturn addPlayerEvent(sendStoreError,350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER,"You don't have this amount of coins.")endif reciver:lower()== player:getName():lower()thenreturn addPlayerEvent(sendStoreError,350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER,"You can't transfer coins to yourself.")endlocal resultId = db.storeQuery("SELECT `account_id` FROM `players` WHERE `name` = ".. db.escapeString(reciver:lower()).."")ifnot resultId thenreturn addPlayerEvent(sendStoreError,350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER,"We couldn't find that player.")endlocal accountId = result.getNumber(resultId,"account_id")if accountId == player:getAccountId()thenreturn addPlayerEvent(sendStoreError,350, playerId, GameStore.StoreErrors.STORE_ERROR_TRANSFER,"You cannot transfer coin to a character in the same account.")end
db.query("UPDATE `accounts` SET `coins` = `coins` + ".. amount .." WHERE `id` = ".. accountId)
player:removeCoinsBalance(amount)
addPlayerEvent(sendStorePurchaseSuccessful,550, playerId,"You have transfered ".. amount .." coins to ".. reciver .." successfully")-- Adding history for both reciver/sender
GameStore.insertHistory(accountId, GameStore.HistoryTypes.HISTORY_TYPE_NONE, player:getName().." transfered you this amount.", amount, GameStore.CointType.Coin)
GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE,"You transfered this amount to ".. reciver,-1* amount, GameStore.CointType.Coin)endfunction parseOpenStore(playerId, msg)
openStore(playerId)local category = GameStore.Categories and GameStore.Categories[1]ornilif category then
addPlayerEvent(parseRequestStoreOffers,50, playerId)endendfunction parseRequestStoreOffers(playerId, msg)local player = Player(playerId)ifnot player thenreturnfalseendlocal actionType = msg:getByte()if actionType == GameStore.ActionType.OPEN_CATEGORY thenlocal categoryName = msg:getString()local category = GameStore.getCategoryByName(categoryName)if category then
addPlayerEvent(sendShowStoreOffers,50, playerId, category)endelseif actionType == GameStore.ActionType.OPEN_HOME then
sendHomePage(player:getId())if category then
addPlayerEvent(sendShowStoreOffers,50, playerId,"Home Offers")endelseif actionType == GameStore.ActionType.OPEN_PREMIUM_BOOST thenlocal subAction = msg:getByte()local category =nilif subAction ==0then
category = GameStore.getCategoryByName("Premium Time")else
category = GameStore.getCategoryByName("Boosts")endif category then
addPlayerEvent(sendShowStoreOffers,50, playerId, category)endelseif actionType == GameStore.ActionType.OPEN_USEFUL_THINGS thenlocal subAction = msg:getByte()local offerId = subAction
local category =nilif subAction >= GameStore.SubActions.BLESSING_TWIST and subAction <= GameStore.SubActions.BLESSING_ALL_PVP then
category = GameStore.getCategoryByName("Blessings")else
category = GameStore.getCategoryByName("Useful Things")endif subAction == GameStore.SubActions.PREY_THIRDSLOT_REAL then
offerId = GameStore.SubActions.PREY_THIRDSLOT_REDIRECT
endif category then
addPlayerEvent(sendShowStoreOffers,50, playerId, category, offerId)endelseif actionType == GameStore.ActionType.OPEN_OFFER thenlocal offerId = msg:getU32()local category = GameStore.getCategoryByOffer(offerId)if category then
addPlayerEvent(sendShowStoreOffers,50, playerId, category, offerId)endendendfunction parseBuyStoreOffer(playerId, msg)local player = Player(playerId)local id = msg:getU32()local offer = GameStore.getOfferById(id)local productType = msg:getByte()-- All guarding conditions under which the offer should not be processed must be included hereif(table.contains(GameStore.OfferTypes, offer.type)==false)-- we've got an invalid offer typeor(not player)-- player not foundor(player:getVocation():getId()==0)and(not GameStore.haveOfferRook(id))-- we don't have such offeror(not offer)-- we could not find the offeror(offer.type == GameStore.OfferTypes.OFFER_TYPE_NONE)-- offer is disabledor(offer.type ~= GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYBONUS and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYSLOT and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_TEMPLE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_POUNCH and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING_NAMECHANGE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING_SEXCHANGE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING_SKILL and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT andnot offer.id)thenreturn queueSendStoreAlertToUser("This offer is unavailable [1]",350, playerId, GameStore.StoreErrors.STORE_ERROR_INFORMATION)end-- At this point the purchase is assumed to be formatted correctlylocal offerPrice = offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and GameStore.ExpBoostValues[player:getStorageValue(GameStore.Storages.expBoostCount)]or offer.price
local offerCoinType = offer.coinType
-- Check if offer can be honoredifnot player:canPayForOffer(offerPrice, offerCoinType)thenreturn queueSendStoreAlertToUser("You don't have enough coins. Your purchase has been cancelled.",250, playerId)end-- 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)-- Handled errors are thrown to indicate that the purchase has failed;-- Handled errors have a code index and unhandled errors do notlocal pcallOk, pcallError = pcall(function()if offer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM then GameStore.processItemPurchase(player, offer.itemtype, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_POUNCH then GameStore.processItemPurchase(player, offer.itemtype, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS then GameStore.processInstantRewardAccess(player, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_CHARMS then GameStore.processCharmsPurchase(player)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BLESSINGS then GameStore.processSignleBlessingPurchase(player, offer.blessid, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_ALLBLESSINGS then GameStore.processAllBlessingsPurchase(player, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREMIUM then GameStore.processPremiumPurchase(player, offer.id)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_STACKABLE then GameStore.processStackablePurchase(player, offer.itemtype, offer.count, offer.name)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HOUSE then GameStore.processHouseRelatedPurchase(player, offer.itemtype, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT then GameStore.processOutfitPurchase(player, offer.sexId, offer.addon)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then GameStore.processOutfitPurchase(player, offer.sexId, offer.addon)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then GameStore.processMountPurchase(player, offer.id)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE thenlocal newName = msg:getString(); GameStore.processNameChangePurchase(player, offer, productType, newName)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE then GameStore.processSexChangePurchase(player)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then GameStore.processExpBoostPuchase(player)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYSLOT then GameStore.processPreyThirdSlot(player)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HUNTINGSLOT then GameStore.processTaskHuntingThirdSlot(player)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYBONUS then GameStore.processPreyBonusReroll(player, offer.count)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_TEMPLE then GameStore.processTempleTeleportPurchase(player)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_CHARGES then GameStore.processChargesPurchase(player, offer.itemtype, offer.name, offer.charges)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING thenlocal hirelingName = msg:getString();local sex = msg:getByte(); GameStore.processHirelingPurchase(player, offer, productType, hirelingName, sex)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_NAMECHANGE thenlocal hirelingName = msg:getString(); GameStore.processHirelingChangeNamePurchase(player, offer, productType, hirelingName)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_SEXCHANGE then GameStore.processHirelingChangeSexPurchase(player, offer)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_SKILL then GameStore.processHirelingSkillPurchase(player, offer)elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT then GameStore.processHirelingOutfitPurchase(player, offer)else-- This should never happen by our convention, but just in case the guarding condition is messed up...
error({code =0, message ="This offer is unavailable [2]"})endend)ifnot pcallOk thenlocal alertMessage = pcallError.code and pcallError.message or"Something went wrong. Your purchase has been cancelled."ifnot pcallError.code then-- unhandled error-- log some debugging info
Spdlog.warn("[parseBuyStoreOffer] - Purchase failed due to an unhandled script error. Stacktrace: ".. pcallError)endreturn queueSendStoreAlertToUser(alertMessage,500, playerId)endlocal configure = useOfferConfigure(offer.type)if configure ~= GameStore.ConfigureOffers.SHOW_CONFIGURE then
player:makeCoinTransaction(offer)local message = string.format("You have purchased %s for %d coins.", offer.name, offerPrice)
sendUpdatedStoreBalances(playerId)return addPlayerEvent(sendStorePurchaseSuccessful,650, playerId, message)endreturntrueend-- Both functions use same formula!function parseOpenTransactionHistory(playerId, msg)local page =1
GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE = msg:getByte()
sendStoreTransactionHistory(playerId, page, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)endfunction parseRequestTransactionHistory(playerId, msg)local page = msg:getU32()
sendStoreTransactionHistory(playerId, page +1, GameStore.DefaultValues.DEFAULT_VALUE_ENTRIES_PER_PAGE)endlocalfunction getCategoriesRook()local tmpTable, count ={},0for i, v in pairs(GameStore.Categories)doif(v.rookgaard)then
tmpTable[#tmpTable +1]= v
count = count +1endendreturn tmpTable, count
end--==Sending==--function openStore(playerId)local player = Player(playerId)ifnot player thenreturnfalseendifnot GameStore.Categories thenreturnfalseendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_OpenStore)local GameStoreCategories, GameStoreCount =nil,0if(player:getVocation():getId()==0)then
GameStoreCategories, GameStoreCount = getCategoriesRook()else
GameStoreCategories, GameStoreCount = GameStore.Categories,#GameStore.Categories
endif(GameStoreCategories)then
msg:addU16(GameStoreCount)for k, category in ipairs(GameStoreCategories)do
msg:addString(category.name)
msg:addByte(category.state or GameStore.States.STATE_NONE)
msg:addByte(#category.icons)for m, icon in ipairs(category.icons)do
msg:addString(icon)endif category.parent then
msg:addString(category.parent)else
msg:addU16(0)endend
msg:sendToPlayer(player)
sendStoreBalanceUpdating(playerId,true)endendfunction sendOfferDescription(player, offerId, description)local msg = NetworkMessage()
msg:addByte(0xEA)
msg:addU32(offerId)
msg:addString(description)
msg:sendToPlayer(player)endfunction Player.canBuyOffer(self, offer)local playerId = self:getId()local disabled, disabledReason =0,""if offer.disabled ==trueornot offer.type then
disabled =1endif offer.type ~= GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYSLOT and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_PREYBONUS and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_TEMPLE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_SEXCHANGE and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_POUNCH and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING_SKILL and
offer.type ~= GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT andnot offer.id then
disabled =1endif disabled ==1and offer.disabledReason then-- dynamic disable
disabledReason = offer.disabledReason
endif disabled ~=1thenif offer.type == GameStore.OfferTypes.OFFER_TYPE_POUNCH thenlocal pounch = self:getItemById(23721,true)if pounch then
disabled =1
disabledReason ="You already have Loot Pouch."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BLESSINGS thenif self:getBlessingCount(offer.blessid)>=5then
disabled =1
disabledReason ="You reached the maximum amount for this blessing."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_ALLBLESSINGS thenfor i =1,8doif self:getBlessingCount(i)>=5then
disabled =1
disabledReason ="You already have all Blessings."breakendendelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON thenlocal outfitLookType
if self:getSex()== PLAYERSEX_MALE then
outfitLookType = offer.sexId.male
else
outfitLookType = offer.sexId.female
endif outfitLookType thenif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT and self:hasOutfit(outfitLookType)then
disabled =1
disabledReason ="You already have this outfit."elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON thenif self:hasOutfit(outfitLookType)thenif self:hasOutfit(outfitLookType, offer.addon)then
disabled =1
disabledReason ="You already have this addon."endelse
disabled =1
disabledReason ="You don't have the outfit, you can't buy the addon."endendelse
disabled =1
disabledReason ="The offer is fake."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT thenlocal hasMount = self:hasMount(offer.id)if hasMount ==truethen
disabled =1
disabledReason ="You already have this mount."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS thenif self:getCollectionTokens()>=90then
disabled =1
disabledReason ="You already have maximum of reward tokens."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYBONUS thenif self:getPreyCards()>=50then
disabled =1
disabledReason ="You already have maximum of prey wildcards."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_CHARMS thenif self:charmExpansion()then
disabled =1
disabledReason ="You already have charm expansion."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HUNTINGSLOT thenif self:taskHuntingThirdSlot()then
disabled =1
disabledReason ="You already have 3 slots released."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_PREYSLOT thenif self:preyThirdSlot()then
disabled =1
disabledReason ="You already have 3 slots released."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST thenlocal remainingBoost = self:getExpBoostStamina()if self:getStorageValue(GameStore.Storages.expBoostCount)==6then
disabled =1
disabledReason ="You can't buy XP Boost for today."endif(remainingBoost >0)then
disabled =1
disabledReason ="You already have an active XP boost."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING thenif self:getHirelingsCount()>=10then
disabled =1
disabledReason ="You already have bought the maximum number of allowed hirelings."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_SKILL thenlocal skill =(HIRELING_STORAGE.SKILL + offer.id)if self:hasHirelingSkill(skill)then
disabled =1
disabledReason ="This skill is already unlocked."endif self:getHirelingsCount()<=0then
disabled =1
disabledReason ="You need to have a hireling."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT thenlocal outfit = offer.id - HIRELING_STORAGE.OUTFIT
if self:hasHirelingOutfit(outfit)then
disabled =1
disabledReason ="This hireling outfit is already unlocked."endif self:getHirelingsCount()<=0then
disabled =1
disabledReason ="You need to have a hireling."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_NAMECHANGE thenif self:getHirelingsCount()<=0then
disabled =1
disabledReason ="You need to have a hireling."endelseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_SEXCHANGE thenif self:getHirelingsCount()<=0then
disabled =1
disabledReason ="You need to have a hireling."endendendreturn{disabled = disabled, disabledReason = disabledReason}endfunction sendShowStoreOffers(playerId, category, redirectId)local player = Player(playerId)ifnot player thenreturnfalseendlocal msg = NetworkMessage()local haveSaleOffer =0
msg:addByte(GameStore.SendingPackets.S_StoreOffers)
msg:addString(category.name)
msg:addU32(redirectId or0)
msg:addByte(0)-- Window Type
msg:addByte(0)-- Collections Size
msg:addU16(0)-- Collection Nameifnot category.offers then
msg:addU16(0)-- Disable reasons
msg:addU16(0)-- Offers
msg:sendToPlayer(player)returnendlocal disableReasons ={}local offers ={}local count =0for k, offer in ipairs(category.offers)dolocal name = offer.name or"Something Special"ifnot offers[name]then
offers[name]={}
count = count +1
offers[name].offers ={}
offers[name].state = offer.state
offers[name].id = offer.id
offers[name].type = offer.type
offers[name].icons = offer.icons
offers[name].basePrice = offer.basePrice
offers[name].description = offer.description
if offer.sexId then
offers[name].sexId = offer.sexId
endif offer.itemtype then
offers[name].itemtype = offer.itemtype
endendlocal canBuy = player:canBuyOffer(offer)if(canBuy.disabled ==1)thenfor index, disableTable in ipairs(disableReasons)doif(canBuy.disabledReason == disableTable.reason)then
offer.disabledReadonIndex = index
endendif(offer.disabledReadonIndex ==nil)then
offer.disabledReadonIndex =#disableReasons
table.insert(disableReasons, canBuy.disabledReason)endend
table.insert(offers[name].offers, offer)end-- If player doesn't have hirelingif category.name =="Hirelings"thenif player:getHirelingsCount()<1then
offers["Hireling Name Change"]=nil
offers["Hireling Sex Change"]=nil
offers["Hireling Trader"]=nil
offers["Hireling Steward"]=nil
offers["Hireling Banker"]=nil
offers["Hireling Cook"]=nil
count = count -6endend
msg:addU16(#disableReasons)for _, reason in ipairs(disableReasons)do
msg:addString(reason)end
msg:addU16(count)if count >0thenfor name, offer in pairs(offers)do
msg:addString(name)
msg:addByte(#offer.offers)
sendOfferDescription(player, offer.id and offer.id or0xFFFF, offer.description)for _, off in ipairs(offer.offers)do
xpBoostPrice =nilif offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST then
xpBoostPrice = GameStore.ExpBoostValues[player:getStorageValue(GameStore.Storages.expBoostCount)]end
msg:addU32(off.id)
msg:addU16(off.count)
msg:addU32(xpBoostPrice or off.price)
msg:addByte(off.coinType or0x00)
msg:addByte((off.disabledReadonIndex ~=nil)and1or0)if(off.disabledReadonIndex ~=nil)then
msg:addByte(0x01);
msg:addU16(off.disabledReadonIndex)
off.disabledReadonIndex =nil-- Reseting the table to nil disable reasonendif(off.state)thenif(off.state == GameStore.States.STATE_SALE)thenlocal daySub = off.validUntil - os.date("*t").day
if(daySub >=0)then
msg:addByte(off.state)
msg:addU32(os.time()+ daySub *86400)
msg:addU32(off.basePrice)
haveSaleOffer =1else
msg:addByte(GameStore.States.STATE_NONE)endelse
msg:addByte(off.state)endelse
msg:addByte(GameStore.States.STATE_NONE)endendlocal tryOnType =0local type = convertType(offer.type)
msg:addByte(type);if type == GameStore.ConverType.SHOW_NONE then
msg:addString(offer.icons[1])elseif type == GameStore.ConverType.SHOW_MOUNT thenlocal mount = Mount(offer.id)
msg:addU16(mount:getClientId())
tryOnType =1elseif type == GameStore.ConverType.SHOW_ITEM then
msg:addU16(offer.itemtype)elseif type == GameStore.ConverType.SHOW_OUTFIT then
msg:addU16(player:getSex()== PLAYERSEX_FEMALE and offer.sexId.female or offer.sexId.male)local outfit = player:getOutfit()
msg:addByte(outfit.lookHead)
msg:addByte(outfit.lookBody)
msg:addByte(outfit.lookLegs)
msg:addByte(outfit.lookFeet)
tryOnType =1elseif type == GameStore.ConverType.SHOW_HIRELING thenif player:getSex()== PLAYERSEX_MALE then
msg:addByte(1)else
msg:addByte(2)end
msg:addU16(offer.sexId.male)
msg:addU16(offer.sexId.female)local outfit = player:getOutfit()
msg:addByte(outfit.lookHead)
msg:addByte(outfit.lookBody)
msg:addByte(outfit.lookLegs)
msg:addByte(outfit.lookFeet)end
msg:addByte(tryOnType)-- TryOn Type
msg:addU16(0)-- Collection (to-do)
msg:addU16(0)-- Popularity Score (to-do)
msg:addU32(0)-- State New Until (timestamp)local configure = useOfferConfigure(offer.type)if configure == GameStore.ConfigureOffers.SHOW_CONFIGURE then
msg:addByte(1)else
msg:addByte(0)end
msg:addU16(0)-- Products Capacity (unnused)endend
player:sendButtonIndication(haveSaleOffer,1)
msg:sendToPlayer(player)
msg:delete()endfunction sendStoreTransactionHistory(playerId, page, entriesPerPage)local player = Player(playerId)ifnot player thenreturnfalseendlocal totalEntries = GameStore.retrieveHistoryTotalPages(player:getAccountId())local totalPages = math.ceil(totalEntries / entriesPerPage)local entries = GameStore.retrieveHistoryEntries(player:getAccountId(), page, entriesPerPage)-- this makes everything easy!if#entries ==0thenreturn addPlayerEvent(sendStoreError,250, playerId, GameStore.StoreErrors.STORE_ERROR_HISTORY,"You don't have any entries yet.")endlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_OpenTransactionHistory)
msg:addU32(totalPages >0and page -1or0x0)-- current page
msg:addU32(totalPages >0and totalPages or0x0)-- total page
msg:addByte(#entries)for k, entry in ipairs(entries)do
msg:addU32(0)
msg:addU32(entry.time)
msg:addByte(entry.mode)-- 0 = normal, 1 = gift, 2 = refund
msg:addU32(entry.amount)
msg:addByte(entry.type)-- 0 = transferable tibia coin, 1 = normal tibia coin, 2 = tournament coin
msg:addString(entry.description)
msg:addByte(0)-- detailsend
msg:sendToPlayer(player)endfunction sendStorePurchaseSuccessful(playerId, message)local player = Player(playerId)ifnot player thenreturnfalseendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_CompletePurchase)
msg:addByte(0x00)
msg:addString(message)
msg:sendToPlayer(player)endfunction sendStoreError(playerId, errorType, message)local player = Player(playerId)ifnot player thenreturnfalseendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_StoreError)
msg:addByte(errorType)
msg:addString(message)
msg:sendToPlayer(player)endfunction sendStoreBalanceUpdating(playerId, updating)local player = Player(playerId)ifnot player thenreturnfalseendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_CoinBalanceUpdating)
msg:addByte(0x00)
msg:sendToPlayer(player)if updating ==truethen
sendUpdatedStoreBalances(playerId)endendfunction sendUpdatedStoreBalances(playerId)local player = Player(playerId)ifnot player thenreturnfalseendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_CoinBalanceUpdating)
msg:addByte(0x01)
msg:addByte(GameStore.SendingPackets.S_CoinBalance)
msg:addByte(0x01)
msg:addU32(player:getCoinsBalance())-- Tibia Coins
msg:addU32(player:getCoinsBalance())-- How many are Transferable
msg:addU32(0)-- How many are reserved for a Character Auction
msg:addU32(player:getTournamentBalance())-- Tournament Coins
msg:sendToPlayer(player)endfunction sendRequestPurchaseData(playerId, offerId, type)local player = Player(playerId)ifnot player thenreturnfalseendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_RequestPurchaseData)
msg:addU32(offerId)
msg:addByte(type)
msg:sendToPlayer(player)end--==GameStoreFunctions==--
GameStore.getCategoryByName =function(name)for k, category in ipairs(GameStore.Categories)doif category.name:lower()== name:lower()thenifnot category.offers thenreturn GameStore.getCategoryByName(category.subclasses[1])endreturn category
endendreturnnilend
GameStore.getCategoryByOffer =function(id)for Cat_k, category in ipairs(GameStore.Categories)doif category.offers thenfor Off_k, offer in ipairs(category.offers)doif type(offer.id)=="number"thenif offer.id == id thenifnot category.offers thenreturn GameStore.getCategoryByName(category.subclasses[1])endreturn category
endelseif type(offer.id)=="table"thenfor m, offerId in pairs(offer.id)do-- in case of outfits we have offer.id = {male = ..., female = ...}if offerId == id thenifnot category.offers thenreturn GameStore.getCategoryByName(category.subclasses[1])endreturn category
endendendendendendreturnnilend
GameStore.getOfferById =function(id)for Cat_k, category in ipairs(GameStore.Categories)doif category.offers thenfor Off_k, offer in ipairs(category.offers)doif type(offer.id)=="number"thenif offer.id == id thenreturn offer
endelseif type(offer.id)=="table"and(offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or offer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON)thenfor m, offerId in pairs(offer.id)do-- in case of outfits we have offer.id = {male = ..., female = ...}if offerId == id thenreturn offer
endend-- case multi offerelseif type(offer.id)=="table"thenlocal newoffer = offer
for i =1,#offer.id dolocal offerId = offer.id[i]if offerId == id then
newoffer.id = offerId
newoffer.price = offer.price[i]return newoffer
endendendendendendreturnnilend-- Using for multi offerfunction GameStore.getOffersByName(name)local offers ={}for Cat_k, category in ipairs(GameStore.Categories)doif category.offers thenfor Off_k, offer in ipairs(category.offers)doif offer.name:lower()== name:lower()then
table.insert(offers, offer)endendendendreturn offers
end
GameStore.haveCategoryRook =function()for Cat_k, category in ipairs(GameStore.Categories)doif category.offers and category.rookgaard thenreturntrueendendreturnfalseend
GameStore.haveOfferRook =function(id)for Cat_k, category in ipairs(GameStore.Categories)doif category.offers and category.rookgaard thenfor Off_k, offer in ipairs(category.offers)doif offer.id == id thenreturntrueendendendendreturnnilend
GameStore.insertHistory =function(accountId, mode, description, coinAmount, coinType)return db.query(string.format("INSERT INTO `store_history`(`account_id`, `mode`, `description`, `coin_type`, `coin_amount`, `time`) VALUES (%s, %s, %s, %s, %s, %s)", accountId, mode, db.escapeString(description), coinType, coinAmount, os.time()))end
GameStore.retrieveHistoryTotalPages =function(accountId)local resultId = db.storeQuery("SELECT count(id) as total FROM store_history WHERE account_id = ".. accountId)if resultId ==falsethenreturn0endlocal totalPages = result.getNumber(resultId,"total")
result.free(resultId)return totalPages
end
GameStore.retrieveHistoryEntries =function(accountId, currentPage, entriesPerPage)local entries ={}local offset = currentPage >1and entriesPerPage *(currentPage -1)or0local resultId = db.storeQuery("SELECT * FROM `store_history` WHERE `account_id` = ".. accountId .." ORDER BY `time` DESC LIMIT ".. offset ..", ".. entriesPerPage ..";")if resultId ~=falsethenrepeatlocal entry ={
mode = result.getNumber(resultId,"mode"),
description = result.getString(resultId,"description"),
amount = result.getNumber(resultId,"coin_amount"),
type = result.getNumber(resultId,"coin_type"),
time = result.getNumber(resultId,"time"),}
table.insert(entries, entry)untilnot result.next(resultId)
result.free(resultId)endreturn entries
end
GameStore.getDefaultDescription =function(offerType, count)local t, descList = GameStore.OfferTypes
if offerType == t.OFFER_TYPE_OUTFIT or offerType == t.OFFER_TYPE_OUTFIT_ADDON then
descList = GameStore.DefaultDescriptions.OUTFIT
elseif offerType == t.OFFER_TYPE_MOUNT then
descList = GameStore.DefaultDescriptions.MOUNT
elseif offerType == t.OFFER_TYPE_NAMECHANGE then
descList = GameStore.DefaultDescriptions.NAMECHANGE
elseif offerType == t.OFFER_TYPE_SEXCHANGE then
descList = GameStore.DefaultDescriptions.SEXCHANGE
elseif offerType == t.OFFER_TYPE_EXPBOOST then
descList = GameStore.DefaultDescriptions.EXPBOOST
elseif offerType == t.OFFER_TYPE_PREYSLOT then
descList = GameStore.DefaultDescriptions.PREYSLOT
elseif offerType == t.OFFER_TYPE_PREYBONUS then
descList = GameStore.DefaultDescriptions.PREYBONUS
elseif offerType == t.OFFER_TYPE_TEMPLE then
descList = GameStore.DefaultDescriptions.TEMPLE
endreturn descList[math.floor(math.random(1,#descList))]or""end
GameStore.canUseHirelingName =function(name)local result ={
ability =false}if name:len()<3or name:len()>14then
result.reason ="The length of the hireling name must be between 3 and 14 characters."return result
endlocal match = name:gmatch("%s+")local count =0for v in match do
count = count +1endlocal matchtwo = name:match("^%s+")if(matchtwo)then
result.reason ="The hireling name can't have whitespace at begin."return result
endlocal matchthree = name:match("[^a-zA-Z ]")if(matchthree)then
result.reason ="The hireling name has invalid characters"return result
endif(count >1)then
result.reason ="The hireling name have more than 1 whitespace."return result
end-- just copied from znote aac.local words ={"owner","gamemaster","hoster","admin","staff","tibia","account","god","anal","ass","fuck","sex","hitler","pussy","dick","rape","adm","cm","gm","tutor","counsellor"}local split = name:split(" ")for k, word in ipairs(words)dofor k, nameWord in ipairs(split)doif nameWord:lower()== word then
result.reason ="You can't use word \"".. word .."\" in your hireling name."return result
endendendlocal tmpName = name:gsub("%s+","")for i =1,#words doif(tmpName:lower():find(words[i]))then
result.reason ="You can't use word \"".. words[i].."\" with whitespace in your hireling name."return result
endend
result.ability =truereturn result
end
GameStore.canChangeToName =function(name)local result ={
ability =false}if name:len()<3or name:len()>14then
result.reason ="The length of your new name must be between 3 and 14 characters."return result
endlocal match = name:gmatch("%s+")local count =0for v in match do
count = count +1endlocal matchtwo = name:match("^%s+")if(matchtwo)then
result.reason ="Your new name can't have whitespace at begin."return result
endif(count >1)then
result.reason ="Your new name have more than 1 whitespace."return result
end-- just copied from znote aac.local words ={"owner","gamemaster","hoster","admin","staff","tibia","account","god","anal","ass","fuck","sex","hitler","pussy","dick","rape","adm","cm","gm","tutor","counsellor"}local split = name:split(" ")for k, word in ipairs(words)dofor k, nameWord in ipairs(split)doif nameWord:lower()== word then
result.reason ="You can't use word \"".. word .."\" in your new name."return result
endendendlocal tmpName = name:gsub("%s+","")for i =1,#words doif(tmpName:lower():find(words[i]))then
result.reason ="You can't use word \"".. words[i].."\" with whitespace in your new name."return result
endendif MonsterType(name)then
result.reason ="Your new name \"".. name .."\" can't be a monster's name."return result
elseif Npc(name)then
result.reason ="Your new name \"".. name .."\" can't be a npc's name."return result
endlocal letters ="{}|_*+-=<>0123456789@#%^&()/*'\\.,:;~!\"$"for i =1, letters:len()dolocal c = letters:sub(i, i)for i =1, name:len()dolocal m = name:sub(i, i)if m == c then
result.reason ="You can't use this letter \"".. c .."\" in your new name."return result
endendend
result.ability =truereturn result
end---- PURCHASE PROCESSOR FUNCTIONS-- Must throw an error when the purchase has not been made. The error must of-- take a table {code = ..., message = ...} if the error is handled. When no code-- index is present the error is assumed to be unhandled.function GameStore.processItemPurchase(player, offerId, offerCount)if player:getFreeCapacity()< ItemType(offerId):getWeight(offerCount)thenreturn error({ code =0, message ="Please make sure you have free capacity to hold this item."})endlocal inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)if inbox and inbox:getEmptySlots()> offerCount thenfor t =1, offerCount do
inbox:addItem(offerId, offerCount or1)endelsereturn error({ code =0, message ="Please make sure you have free slots in your store inbox."})endendfunction GameStore.processChargesPurchase(player, itemtype, name, charges)if player:getFreeCapacity()< ItemType(itemtype):getWeight(1)thenreturn error({ code =0, message ="Please make sure you have free capacity to hold this item."})endlocal inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)if inbox and inbox:getEmptySlots()>1then
inbox:addItem(itemtype, charges)elsereturn error({ code =0, message ="Please make sure you have free slots in your store inbox."})endendfunction GameStore.processSignleBlessingPurchase(player, blessId, count)
player:addBlessing(blessId, count)endfunction GameStore.processAllBlessingsPurchase(player, count)
player:addBlessing(1, count)
player:addBlessing(2, count)
player:addBlessing(3, count)
player:addBlessing(4, count)
player:addBlessing(5, count)
player:addBlessing(6, count)
player:addBlessing(7, count)
player:addBlessing(8, count)endfunction GameStore.processInstantRewardAccess(player, offerCount)if player:getCollectionTokens()+ offerCount >=91thenreturn error({code =1, message ="You cannot own more than 90 reward tokens."})end
player:setCollectionTokens(player:getCollectionTokens()+ offerCount)endfunction GameStore.processCharmsPurchase(player)
player:charmExpansion(true)endfunction GameStore.processPremiumPurchase(player, offerId)
player:addPremiumDays(offerId -3000)endfunction GameStore.processStackablePurchase(player, offerId, offerCount, offerName)localfunction isKegItem(itemId)return itemId >= ITEM_KEG_START and itemId <= ITEM_KEG_END
endlocal PARCEL_ID =3504local isKeg = isKegItem(offerId)if isKeg thenif player:getFreeCapacity()< ItemType(offerId):getWeight(1)+ ItemType(PARCEL_ID):getWeight()thenreturn error({code =0, message ="Please make sure you have free capacity to hold this item."})endelseif player:getFreeCapacity()< ItemType(offerId):getWeight(offerCount)+ ItemType(PARCEL_ID):getWeight()thenreturn error({code =0, message ="Please make sure you have free capacity to hold this item."})endlocal inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)if inbox and inbox:getEmptySlots()>0thenif(isKeg and offerCount >500)or offerCount >100thenlocal parcel = inbox:addItem(PARCEL_ID,1)if parcel then
parcel:setAttribute(ITEM_ATTRIBUTE_NAME,''.. offerCount ..'x '.. offerName ..' package.')local pendingCount = offerCount
local limit = isKeg and500or100while(pendingCount >0)dolocal pack
if(pendingCount > limit)then
pack = limit
else
pack = pendingCount
endif isKeg thenlocal kegItem = parcel:addItem(offerId,1)
kegItem:setAttribute(ITEM_ATTRIBUTE_CHARGES, pack)else
parcel:addItem(offerId, pack)end
pendingCount = pendingCount - pack
endendelselocal item = inbox:addItem(offerId, isKeg and1or offerCount)if item and isKeg then
item:setAttribute(ITEM_ATTRIBUTE_CHARGES, offerCount)endendelsereturn error({code =0, message ="Please make sure you have free slots in your store inbox."})endendfunction GameStore.processHouseRelatedPurchase(player, offerId, offerCount)localfunction isCaskItem(itemId)return(itemId >= ITEM_HEALTH_CASK_START and itemId <= ITEM_HEALTH_CASK_END)or(itemId >= ITEM_MANA_CASK_START and itemId <= ITEM_MANA_CASK_END)or(itemId >= ITEM_SPIRIT_CASK_START and itemId <= ITEM_SPIRIT_CASK_END)endlocal inbox = player:getSlotItem(CONST_SLOT_STORE_INBOX)if inbox and inbox:getEmptySlots()>0thenlocal decoKit = inbox:addItem(23398,1)if decoKit then
decoKit:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION,"You bought this item in the Store.\nUnwrap it in your own house to create a <".. ItemType(offerId):getName()..">.")
decoKit:setCustomAttribute("unWrapId", offerId)if isCaskItem(offerId)then
decoKit:setAttribute(ITEM_ATTRIBUTE_DATE, offerCount)endendelsereturn error({code =0, message ="Please make sure you have free slots in your store inbox."})endendfunction GameStore.processOutfitPurchase(player, offerSexIdTable, addon)local looktype
local _addon = addon and addon or0if player:getSex()== PLAYERSEX_MALE then
looktype = offerSexIdTable.male
elseif player:getSex()== PLAYERSEX_FEMALE then
looktype = offerSexIdTable.female
endifnot looktype thenreturn error({code =0, message ="This outfit seems not to suit your sex, we are sorry for that!"})elseif(not player:hasOutfit(looktype,0))and(_addon ==1or _addon ==2)thenreturn error({code =0, message ="You must own the outfit before you can buy its addon."})elseif player:hasOutfit(looktype, _addon)thenreturn error({code =0, message ="You already own this outfit."})elseifnot(player:addOutfitAddon(looktype, _addon))-- TFS call failedor(not player:hasOutfit(looktype, _addon))-- Additional check; if the looktype doesn't match player sex for example,-- then the TFS check will still pass... bug? (TODO)then
error({ code =0, message ="There has been an issue with your outfit purchase. Your purchase has been cancelled."})else
player:addOutfitAddon(offerSexIdTable.male, _addon)
player:addOutfitAddon(offerSexIdTable.female, _addon)endendendfunction GameStore.processMountPurchase(player, offerId)if player:hasMount(offerId)thenreturn error({code =0, message ="You already own this mount."})end
player:addMount(offerId)endfunction GameStore.processNameChangePurchase(player, offer, productType, newName)local playerId = player:getId()if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE thenlocal tile = Tile(player:getPosition())if(tile)thenif(not tile:hasFlag(TILESTATE_PROTECTIONZONE))thenreturn error({code =1, message ="You can change name only in Protection Zone."})endendlocal resultId = db.storeQuery("SELECT * FROM `players` WHERE `name` = ".. db.escapeString(newName).."")if resultId ~=falsethenreturn error({code =1, message ="This name is already used, please try again!"})endlocal result = GameStore.canChangeToName(newName)ifnot result.ability thenreturn error({code =1, message = result.reason})end
player:makeCoinTransaction(offer)local message = string.format("You have purchased %s for %d coins.", offer.name, offer.price)
addPlayerEvent(sendStorePurchaseSuccessful,500, playerId, message)
newName = newName:lower():gsub("(%l)(%w*)",function(a, b)return string.upper(a).. b end)
db.query("UPDATE `players` SET `name` = ".. db.escapeString(newName).." WHERE `id` = ".. player:getGuid())
message ="You have successfully changed you name, relogin!"
addEvent(function()local player = Player(playerId)ifnot player thenreturnfalseend
player:remove()end,1000)-- If not, we ask him to do!elsereturn addPlayerEvent(sendRequestPurchaseData,250, playerId, offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE)endendfunction GameStore.processSexChangePurchase(player)
player:toggleSex()endfunction GameStore.processExpBoostPuchase(player)local currentExpBoostTime = player:getExpBoostStamina()local expBoostCount = player:getStorageValue(GameStore.Storages.expBoostCount)
player:setStoreXpBoost(50)
player:setExpBoostStamina(currentExpBoostTime +3600)if(player:getStorageValue(GameStore.Storages.expBoostCount)==-1or expBoostCount ==6)then
player:setStorageValue(GameStore.Storages.expBoostCount,1)end
player:setStorageValue(GameStore.Storages.expBoostCount, expBoostCount +1)endfunction GameStore.processPreyThirdSlot(player)if player:preyThirdSlot()thenreturn error({code =1, message ="You already have unlocked all prey slots."})end
player:preyThirdSlot(true)endfunction GameStore.processTaskHuntingThirdSlot(player)if player:taskHuntingThirdSlot()thenreturn error({code =1, message ="You already have unlocked all task hunting slots."})end
player:taskHuntingThirdSlot(true)endfunction GameStore.processPreyBonusReroll(player, offerCount)if player:getPreyCards()+ offerCount >=51thenreturn error({code =1, message ="You cannot own more than 50 prey wildcards."})end
player:addPreyCards(offerCount)endfunction GameStore.processTempleTeleportPurchase(player)if player:getCondition(CONDITION_INFIGHT, CONDITIONID_DEFAULT)or player:isPzLocked()thenreturn error({code =0, message ="You can't use temple teleport in fight!"})end
player:teleportTo(player:getTown():getTemplePosition())
player:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE,'You have been teleported to your hometown.')endfunction GameStore.processHirelingPurchase(player, offer, productType, hirelingName, chosenSex)local playerId = player:getId()local offerId = offer.id
if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_HIRELING thenlocal result = GameStore.canUseHirelingName(hirelingName)ifnot result.ability thenreturn error({code =1, message = result.reason})end
hirelingName = hirelingName:lower():gsub("(%l)(%w*)",function(a, b)return string.upper(a).. b end)local hireling = player:addNewHireling(hirelingName, chosenSex)ifnot hireling thenreturn error({code =1, message ="Error delivering your hireling lamp, try again later."})end
player:makeCoinTransaction(offer, hirelingName)local message ="You have successfully bought ".. hirelingName
return addPlayerEvent(sendStorePurchaseSuccessful,650, playerId, message)-- If not, we ask him to do!elseif player:getHirelingsCount()>=10thenreturn error({code =1, message ="You cannot have more than 10 hirelings."})end-- TODO: Use the correct dialog (byte 0xDB) on client 1205+-- for compatibility, request name using the change name dialogreturn addPlayerEvent(sendRequestPurchaseData,250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_HIRELING)endendfunction GameStore.processHirelingChangeNamePurchase(player, offer, productType, newHirelingName)local playerId = player:getId()local offerId = offer.id
if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE thenlocal result = GameStore.canUseHirelingName(newHirelingName)ifnot result.ability thenreturn error({code =1, message = result.reason})end
newHirelingName = newHirelingName:lower():gsub("(%l)(%w*)",function(a, b)return string.upper(a).. b end)local message ='Close the store window to select which hireling should be renamed to '.. newHirelingName
addPlayerEvent(sendStorePurchaseSuccessful,200, playerId, message)
addPlayerEvent(HandleHirelingNameChange,550, playerId, offer, newHirelingName)elsereturn addPlayerEvent(sendRequestPurchaseData,250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE)endendfunction GameStore.processHirelingChangeSexPurchase(player, offer)local playerId = player:getId()local message ='Close the store window to select which hireling should have the sex changed.'
addPlayerEvent(sendStorePurchaseSuccessful,200, playerId, message)
addPlayerEvent(HandleHirelingSexChange,550, playerId, offer)endfunction GameStore.processHirelingSkillPurchase(player, offer)
player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)local skill = offer.id - HIRELING_STORAGE.SKILL
player:enableHirelingSkill(skill)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE,'A new hireling skill has been added to all your hirelings')endfunction GameStore.processHirelingOutfitPurchase(player, offer)
player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN)local outfit = offer.id - HIRELING_STORAGE.OUTFIT
player:enableHirelingOutfit(outfit)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE,'A new hireling outfit has been added to all your hirelings')end--==Player==----- Tibia Coinsfunction Player.getCoinsBalance(self)
resultId = db.storeQuery("SELECT `coins` FROM `accounts` WHERE `id` = ".. self:getAccountId())ifnot resultId thenreturn0endreturn result.getNumber(resultId,"coins")endfunction Player.setCoinsBalance(self, coins)
db.query("UPDATE `accounts` SET `coins` = ".. coins .." WHERE `id` = ".. self:getAccountId())returntrueendfunction Player.canRemoveCoins(self, coins)if self:getCoinsBalance()< coins thenreturnfalseendreturntrueendfunction Player.removeCoinsBalance(self, coins)if self:canRemoveCoins(coins)thenreturn self:setCoinsBalance(self:getCoinsBalance()- coins)endreturnfalseendfunction Player.addCoinsBalance(self, coins, update)
self:setCoinsBalance(self:getCoinsBalance()+ coins)if update then sendStoreBalanceUpdating(self,true)endreturntrueend--- Tournament Coinsfunction Player.getTournamentBalance(self)
resultId = db.storeQuery("SELECT `tournament_coins` FROM `accounts` WHERE `id` = ".. self:getAccountId())ifnot resultId thenreturn0endreturn result.getNumber(resultId,"tournament_coins")endfunction Player.setTournamentBalance(self, tournament)
db.query("UPDATE `accounts` SET `tournament_coins` = ".. tournament .." WHERE `id` = ".. self:getAccountId())returntrueendfunction Player.canRemoveTournament(self, tournament)if self:getTournamentBalance()< tournament thenreturnfalseendreturntrueendfunction Player.removeTournamentBalance(self, tournament)if self:canRemoveTournament(tournament)thenreturn self:setTournamentBalance(self:getTournamentBalance()- tournament)endreturnfalseendfunction Player.addTournamentBalance(self, tournament, update)
self:setTournamentBalance(self:getTournamentBalance()+ tournament)if update then sendStoreBalanceUpdating(self,true)endreturntrueend--- Support Functionsfunction Player.makeCoinTransaction(self, offer, desc)local op =trueif desc then
desc = offer.name ..' ('.. desc ..')'else
desc = offer.name
end-- Remove coinsif offer.coinType == GameStore.CointType.Tournament then
op = self:removeTournamentBalance(offer.price)else
op = self:removeCoinsBalance(offer.price)end-- When the transaction is suscessfull add to the historyif op then
GameStore.insertHistory(self:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, desc,(offer.price)*-1, offer.coinType)endreturn op
endfunction Player.canPayForOffer(self, coins, type)if type == GameStore.CointType.Tournament thenreturn self:canRemoveTournament(coins)elsereturn self:canRemoveCoins(coins)endend--- Other players functionsfunction Player.sendButtonIndication(self, value1, value2)local msg = NetworkMessage()
msg:addByte(0x19)
msg:addByte(value1)-- Sale
msg:addByte(value2)-- New Item
msg:sendToPlayer(self)endfunction Player.toggleSex(self)local currentSex = self:getSex()local playerOutfit = self:getOutfit()
playerOutfit.lookAddons =0if currentSex == PLAYERSEX_FEMALE then
self:setSex(PLAYERSEX_MALE)
playerOutfit.lookType =128else
self:setSex(PLAYERSEX_FEMALE)
playerOutfit.lookType =136end
self:setOutfit(playerOutfit)endlocalfunction getHomeOffers(playerId)local player = Player(playerId)ifnot player thenreturn{}endlocal GameStoreCategories = GameStore.Categories
local offers ={}if(GameStoreCategories)thenfor k, category in ipairs(GameStoreCategories)doif category.offers thenfor _, offer in ipairs(category.offers)doif offer.home then
table.insert(offers, offer)endendendendendreturn offers
endfunction sendHomePage(playerId)local player = Player(playerId)ifnot player thenreturnendlocal msg = NetworkMessage()
msg:addByte(GameStore.SendingPackets.S_StoreOffers)
msg:addString("Home")
msg:addU32(0x0)-- Redirect ID (not used here)
msg:addByte(0x0)-- Window Type
msg:addByte(0x0)-- Collections Size
msg:addU16(0x00)-- Collection Namelocal homeOffers = getHomeOffers(player:getId())local disableReasons ={}for p, offer in pairs(homeOffers)dolocal canBuy = player:canBuyOffer(offer)if(canBuy.disabled ==1)thenfor index, disableTable in ipairs(disableReasons)doif(canBuy.disabledReason == disableTable.reason)then
offer.disabledReadonIndex = index
endendif(offer.disabledReadonIndex ==nil)then
offer.disabledReadonIndex =#disableReasons
table.insert(disableReasons, canBuy.disabledReason)endendend
msg:addU16(#disableReasons)for _, reason in ipairs(disableReasons)do
msg:addString(reason)end
msg:addU16(#homeOffers)-- offersfor p, offer in pairs(homeOffers)do
msg:addString(offer.name)
msg:addByte(0x1)-- ?
msg:addU32(offer.id or0)-- id
msg:addU16(0x1)
msg:addU32(offer.price)
msg:addByte(offer.coinType or0x00)
msg:addByte((offer.disabledReadonIndex ~=nil)and1or0)if(offer.disabledReadonIndex ~=nil)then
msg:addByte(0x01);
msg:addU16(offer.disabledReadonIndex)
offer.disabledReadonIndex =nil-- Reseting the table to nil disable reasonend
msg:addByte(0x00)local type = convertType(offer.type)
msg:addByte(type);if type == GameStore.ConverType.SHOW_NONE then
msg:addString(offer.icons[1])elseif type == GameStore.ConverType.SHOW_MOUNT thenlocal mount = Mount(offer.id)
msg:addU16(mount:getClientId())elseif type == GameStore.ConverType.SHOW_ITEM then
msg:addU16(offer.itemtype)elseif type == GameStore.ConverType.SHOW_OUTFIT then
msg:addU16(player:getSex()== PLAYERSEX_FEMALE and offer.sexId.female or offer.sexId.male)local outfit = player:getOutfit()
msg:addByte(outfit.lookHead)
msg:addByte(outfit.lookBody)
msg:addByte(outfit.lookLegs)
msg:addByte(outfit.lookFeet)end
msg:addByte(0)-- TryOn Type
msg:addU16(0)-- Collection
msg:addU16(0)-- Popularity Score
msg:addU32(0)-- State New Until
msg:addByte(0)-- User Configuration
msg:addU16(0)-- Products Capacityendlocal banner = HomeBanners
msg:addByte(#banner.images)for m, image in ipairs(banner.images)do
msg:addString(image)
msg:addByte(0x04)-- Banner Type (offer)
msg:addU32(0x00)-- Offer Id
msg:addByte(0)
msg:addByte(0)end
msg:addByte(banner.delay)-- Delay to swtich images
msg:sendToPlayer(player)endfunction Player:openStore(serviceName)--exporting the method so other scripts can use to open store
openStore(self:getId())--local serviceType = msg:getByte()local category = GameStore.Categories and GameStore.Categories[1]ornilif serviceName and serviceName:lower()=="home"thenreturn sendHomePage(self:getId())endif serviceName and GameStore.getCategoryByName(serviceName)then
category = GameStore.getCategoryByName(serviceName)endif category then
addPlayerEvent(sendShowStoreOffers,50, playerId, category)endend-- Hireling Helpersfunction HandleHirelingNameChange(playerId, offer, newHirelingName)local player = Player(playerId);local cb =function(playerId, data, hireling)local offer = data.offer
local newHirelingName = data.newHirelingName
local player = Player(playerId);ifnot hireling thenreturn player:showInfoModal("Error","Your must select a hireling.")endif hireling.active >0thenreturn player:showInfoModal("Error","Your hireling must be inside his/her lamp.")endlocal oldName = hireling.name
hireling.name = newHirelingName
ifnot player:makeCoinTransaction(data.offer, oldName ..' to '.. newHirelingName)thenreturn player:showInfoModal("Error","Transaction error")endlocal lamp = player:findHirelingLamp(hireling:getId())if lamp then
lamp:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION,"This mysterious lamp summons your very own personal hireling.\nThis item cannot be traded.\nThis magic lamp is the home of ".. hireling:getName()..".")end
Spdlog.debug(string.format('%s has been renamed to %s', oldName, newHirelingName))
sendUpdatedStoreBalances(playerId)end
player:sendHirelingSelectionModal('Choose a Hireling','Select a hireling below', cb,{offer=offer, newHirelingName=newHirelingName})endfunction HandleHirelingSexChange(playerId, offer)local player = Player(playerId);local cb =function(playerId, data, hireling)local player = Player(playerId);ifnot hireling thenreturn player:showInfoModal("Error","Your must select a hireling.")endif hireling.active >0thenreturn player:showInfoModal("Error","Your hireling must be inside his/her lamp.")endifnot player:makeCoinTransaction(data.offer, hireling:getName())thenreturn player:showInfoModal("Error","Transaction error")endlocal changeTo,sexString,lookType
if hireling.sex == HIRELING_SEX.FEMALE then
changeTo = HIRELING_SEX.MALE
sexString ='male'
lookType = HIRELING_OUTFIT_DEFAULT.male
else
changeTo = HIRELING_SEX.FEMALE
sexString ='female'
lookType = HIRELING_OUTFIT_DEFAULT.female
end
hireling.sex = changeTo
hireling.looktype = lookType
Spdlog.debug(string.format('%s sex was changed to %s', hireling:getName(), sexString))
sendUpdatedStoreBalances(playerId)end
player:sendHirelingSelectionModal('Choose a Hireling','Select a hireling below', cb,{offer=offer})end
Pergunta
McSorriso309 2
Boas Guys, estou tendo problema com a store ingame no Canary 2.6.0 versão 13.10, os itens não chegando no Inbox dos chars, e não mostra nenhum erro no debug do server ou do mysql...
Obs.: sistema de Inbox funcionando 100%
Obs2.: ao comprar o item não aparece nenhuma mensagem e desconta o cap do player sem receber o item.
init.lua:
Resolvido Guys
Editado por McSorriso309testes bug
Link para o comentário
Compartilhar em outros sites
1 resposta a esta questão
Posts Recomendados