Skip to content
This repository has been archived by the owner on May 20, 2023. It is now read-only.

Commit

Permalink
Enable gamestore to offers services/items for tournament coins (#743)
Browse files Browse the repository at this point in the history
• Allows offers tagged with `coinType = GameStore.CointType.Tournament` to correctly charge tournament coins.
• Added correct history for the store transactions following the coin type.
• Removed duplicate charge and history when changing the name or sex of a Hireling.
  • Loading branch information
bosmak authored Aug 10, 2022
1 parent 7a9fbb4 commit 758f136
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 70 deletions.
20 changes: 16 additions & 4 deletions data/items/items.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45051,10 +45051,22 @@
<attribute key="duration" value="200"/>
<attribute key="decayTo" value="31464"/>
</item>
<item id="31466" article="a" name="tournament carpet"/>
<item id="31467" article="a" name="sublime tournament carpet"/>
<item id="31468" article="a" name="tournament carpet"/>
<item id="31469" article="a" name="sublime tournament carpet"/>
<item id="31466" article="a" name="rolled-up tournament carpet">
<attribute key="type" value="carpet"/>
<attribute key="wrapableto" value="23398"/>
</item>
<item id="31467" article="a" name="rolled-up sublime tournament carpet">
<attribute key="type" value="carpet"/>
<attribute key="wrapableto" value="23398"/>
</item>
<item id="31468" article="a" name="tournament carpet">
<attribute key="type" value="carpet"/>
<attribute key="wrapableto" value="23398"/>
</item>
<item id="31469" article="a" name="sublime tournament carpet">
<attribute key="type" value="carpet"/>
<attribute key="wrapableto" value="23398"/>
</item>
<item fromid="31470" toid="31471" article="a" name="tournament accolade"/>
<item fromid="31472" toid="31473" article="a" name="sublime tournament accolade"/>
<item fromid="31474" toid="31477" article="a" name="pillar"/>
Expand Down
8 changes: 7 additions & 1 deletion data/migrations/19.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
function onUpdateDatabase()
return false -- true = There are others migrations file | false = this is the last migration file
Spdlog.info("Updating database to version 20 (Gamestore accepting Tournament Coins)")

db.query("ALTER TABLE `accounts` ADD `tournament_coins` int(11) NOT NULL DEFAULT 0 AFTER `coins`")
db.query("ALTER TABLE `store_history` ADD `coin_type` tinyint(1) NOT NULL DEFAULT 0 AFTER `description`")
db.query("ALTER TABLE `store_history` DROP COLUMN `coins`") -- Not in use anywhere.

return true
end
3 changes: 3 additions & 0 deletions data/migrations/20.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function onUpdateDatabase()
return false -- true = There are others migrations file | false = this is the last migration file
end
154 changes: 114 additions & 40 deletions data/modules/scripts/gamestore/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ function 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_NAMECHANGE] = GameStore.ConfigureOffers.SHOW_CONFIGURE,
[GameStore.OfferTypes.OFFER_TYPE_HIRELING_SEXCHANGE] = GameStore.ConfigureOffers.SHOW_CONFIGURE
}

if not types[type] then
Expand Down Expand Up @@ -261,8 +262,8 @@ function parseTransferCoins(playerId, msg)
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.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, "You transfered this amount to " .. reciver, -1 * amount) -- negative
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)
end

function parseOpenStore(playerId, msg)
Expand Down Expand Up @@ -366,8 +367,9 @@ function parseBuyStoreOffer(playerId, msg)

-- At this point the purchase is assumed to be formatted correctly
local offerPrice = offer.type == GameStore.OfferTypes.OFFER_TYPE_EXPBOOST and GameStore.ExpBoostValues[player:getStorageValue(GameStore.Storages.expBoostCount)] or offer.price

if not player:canRemoveCoins(offerPrice) then
local offerCoinType = offer.coinType
-- Check if offer can be honored
if not player:canPayForOffer(offerPrice, offerCoinType) then
return queueSendStoreAlertToUser("You don't have enough coins. Your purchase has been cancelled.", 250, playerId)
end

Expand All @@ -387,7 +389,7 @@ function parseBuyStoreOffer(playerId, msg)
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 then local newName = msg:getString(); GameStore.processNameChangePurchase(player, offer.id, productType, newName, offer.name, offerPrice)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_NAMECHANGE then local 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)
Expand Down Expand Up @@ -419,10 +421,11 @@ function parseBuyStoreOffer(playerId, msg)

local configure = useOfferConfigure(offer.type)
if configure ~= GameStore.ConfigureOffers.SHOW_CONFIGURE then
player:removeCoinsBalance(offerPrice)
GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offer.name, (offerPrice) * -1)

player:makeCoinTransaction(offer)

local message = string.format("You have purchased %s for %d coins.", offer.name, offerPrice)
sendUpdateCoinBalance(playerId)
sendUpdatedStoreBalances(playerId)
return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message)
end
return true
Expand Down Expand Up @@ -491,7 +494,7 @@ function openStore(playerId)
end

msg:sendToPlayer(player)
sendCoinBalanceUpdating(playerId, true)
sendStoreBalanceUpdating(playerId, true)
end
end

Expand Down Expand Up @@ -840,9 +843,9 @@ function sendStoreTransactionHistory(playerId, page, entriesPerPage)
for k, entry in ipairs(entries) do
msg:addU32(0)
msg:addU32(entry.time)
msg:addByte(entry.mode)
msg:addByte(entry.mode) -- 0 = normal, 1 = gift, 2 = refund
msg:addU32(entry.amount)
msg:addByte(0x0) -- 0 = transferable tibia coin, 1 = normal tibia coin
msg:addByte(entry.type) -- 0 = transferable tibia coin, 1 = normal tibia coin, 2 = tournament coin
msg:addString(entry.description)
msg:addByte(0) -- details
end
Expand Down Expand Up @@ -878,7 +881,7 @@ function sendStoreError(playerId, errorType, message)
msg:sendToPlayer(player)
end

function sendCoinBalanceUpdating(playerId, updating)
function sendStoreBalanceUpdating(playerId, updating)
local player = Player(playerId)
if not player then
return false
Expand All @@ -890,11 +893,11 @@ function sendCoinBalanceUpdating(playerId, updating)
msg:sendToPlayer(player)

if updating == true then
sendUpdateCoinBalance(playerId)
sendUpdatedStoreBalances(playerId)
end
end

function sendUpdateCoinBalance(playerId)
function sendUpdatedStoreBalances(playerId)
local player = Player(playerId)
if not player then
return false
Expand All @@ -907,10 +910,10 @@ function sendUpdateCoinBalance(playerId)
msg:addByte(GameStore.SendingPackets.S_CoinBalance)
msg:addByte(0x01)

msg:addU32(player:getCoinsBalance())
msg:addU32(player:getCoinsBalance())
msg:addU32(player:getCoinsBalance())
msg:addU32(0) -- Tournament Coins
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)
end
Expand Down Expand Up @@ -1043,8 +1046,8 @@ GameStore.haveOfferRook = function(id)
return nil
end

GameStore.insertHistory = function(accountId, mode, description, amount)
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()))
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)
Expand All @@ -1069,6 +1072,7 @@ GameStore.retrieveHistoryEntries = function(accountId, currentPage, entriesPerPa
mode = result.getDataInt(resultId, "mode"),
description = result.getDataString(resultId, "description"),
amount = result.getDataInt(resultId, "coin_amount"),
type = result.getDataInt(resultId, "coin_type"),
time = result.getDataInt(resultId, "time"),
}
table.insert(entries, entry)
Expand Down Expand Up @@ -1400,7 +1404,7 @@ function GameStore.processMountPurchase(player, offerId)
player:addMount(offerId)
end

function GameStore.processNameChangePurchase(player, offerId, productType, newName, offerName, offerPrice)
function GameStore.processNameChangePurchase(player, offer, productType, newName)
local playerId = player:getId()

if productType == GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE then
Expand All @@ -1421,10 +1425,9 @@ function GameStore.processNameChangePurchase(player, offerId, productType, newNa
return error({code = 1, message = result.reason})
end

player:removeCoinsBalance(offerPrice)
GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offerName, (offerPrice) * -1)
player:makeCoinTransaction(offer)

local message = string.format("You have purchased %s for %d coins.", offerName, offerPrice)
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)
Expand All @@ -1440,7 +1443,7 @@ function GameStore.processNameChangePurchase(player, offerId, productType, newNa
end, 1000)
-- If not, we ask him to do!
else
return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE)
return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offer.id, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE)
end
end

Expand Down Expand Up @@ -1512,8 +1515,7 @@ function GameStore.processHirelingPurchase(player, offer, productType, hirelingN
return error({code = 1, message = "Error delivering your hireling lamp, try again later."})
end

player:removeCoinsBalance(offer.price)
GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offer.name .. ' ('.. hirelingName ..')', (offer.price) * -1)
player:makeCoinTransaction(offer, hirelingName)
local message = "You have successfully bought " .. hirelingName
return addPlayerEvent(sendStorePurchaseSuccessful, 650, playerId, message)
-- If not, we ask him to do!
Expand Down Expand Up @@ -1541,7 +1543,7 @@ function GameStore.processHirelingChangeNamePurchase(player, offer, productType,
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)
addPlayerEvent(HandleHirelingNameChange, 550, playerId, offer, newHirelingName)

else
return addPlayerEvent(sendRequestPurchaseData, 250, playerId, offerId, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE)
Expand Down Expand Up @@ -1572,6 +1574,8 @@ function GameStore.processHirelingOutfitPurchase(player, offer)
end

--==Player==--

--- Tibia Coins
function Player.getCoinsBalance(self)
resultId = db.storeQuery("SELECT `coins` FROM `accounts` WHERE `id` = " .. self:getAccountId())
if not resultId then return 0 end
Expand Down Expand Up @@ -1600,10 +1604,80 @@ end

function Player.addCoinsBalance(self, coins, update)
self:setCoinsBalance(self:getCoinsBalance() + coins)
if update then sendCoinBalanceUpdating(self, true) end
if update then sendStoreBalanceUpdating(self, true) end
return true
end

--- Tournament Coins
function Player.getTournamentBalance(self)
resultId = db.storeQuery("SELECT `tournament_coins` FROM `accounts` WHERE `id` = " .. self:getAccountId())
if not resultId then
return 0
end
return result.getDataInt(resultId, "tournament_coins")
end

function Player.setTournamentBalance(self, tournament)
db.query("UPDATE `accounts` SET `tournament_coins` = " .. tournament .. " WHERE `id` = " .. self:getAccountId())
return true
end

function Player.canRemoveTournament(self, tournament)
if self:getTournamentBalance() < tournament then
return false
end
return true
end

function Player.removeTournamentBalance(self, tournament)
if self:canRemoveTournament(tournament) then
return self:setTournamentBalance(self:getTournamentBalance() - tournament)
end

return false
end

function Player.addTournamentBalance(self, tournament, update)
self:setTournamentBalance(self:getTournamentBalance() + tournament)
if update then sendStoreBalanceUpdating(self, true) end
return true
end

--- Support Functions
function Player.makeCoinTransaction(self, offer, desc)
local op = true

if desc then
desc = offer.name .. ' (' .. desc ..')'
else
desc = offer.name
end

-- Remove coins
if 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 history
if op then
GameStore.insertHistory(self:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, desc, (offer.price) * -1, offer.coinType)
end

return op
end

function Player.canPayForOffer(self, coins, type)
if type == GameStore.CointType.Tournament then
return self:canRemoveTournament(coins)
else
return self:canRemoveCoins(coins)
end
end

--- Other players functions

function Player.sendButtonIndication(self, value1, value2)
local msg = NetworkMessage()
msg:addByte(0x19)
Expand Down Expand Up @@ -1761,18 +1835,19 @@ function HandleHirelingNameChange(playerId, offer, newHirelingName)
return player:showInfoModal("Error", "Your hireling must be inside his/her lamp.")
end

if not player:removeCoinsBalance(offer.price) then
return player:showInfoModal("Error", "Transaction error")
end
local oldName = hireling.name
hireling.name = newHirelingName

if not player:makeCoinTransaction(data.offer, oldName .. ' to ' .. newHirelingName) then
return player:showInfoModal("Error", "Transaction error")
end

local 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
GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offer.name .. ' ('.. oldName .. '->' .. newHirelingName ..')', (offer.price) * -1)

player:showInfoModal('Info',string.format('%s has been renamed to %s', oldName, newHirelingName))
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})
Expand All @@ -1791,7 +1866,7 @@ function HandleHirelingSexChange(playerId, offer)
return player:showInfoModal("Error", "Your hireling must be inside his/her lamp.")
end

if not player:removeCoinsBalance(data.offer.price) then
if not player:makeCoinTransaction(data.offer, hireling:getName()) then
return player:showInfoModal("Error", "Transaction error")
end

Expand All @@ -1809,9 +1884,8 @@ function HandleHirelingSexChange(playerId, offer)
hireling.sex = changeTo
hireling.looktype = lookType

GameStore.insertHistory(player:getAccountId(), GameStore.HistoryTypes.HISTORY_TYPE_NONE, offer.name .. ' ('.. hireling:getName() ..')', (offer.price) * -1)

player:showInfoModal('Info',string.format('%s sex was changed to %s', hireling:getName(), sexString))
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})
Expand Down
Loading

0 comments on commit 758f136

Please sign in to comment.