Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions data/modules/scripts/gamestore/gamestore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,60 @@ GameStore.Categories = {
},
},
},

{
icons = { "Category_Bundles.png" },
name = "Bundles",
rookgaard = true,
description = "Special bundles with multiple items for a discounted price!",
offers = {
{
icons = { "bundle_starter_pack.png" },
name = "Starter Pack",
price = 900,
state = GameStore.States.STATE_NEW,
home = true,
description = "An essential package for every new adventurer! Includes premium days, a swift mount, a stylish outfit, and potions to begin your journey.",
type = GameStore.OfferTypes.OFFER_TYPE_BUNDLE,
contents = {
{
-- Item 1: 30 Premium Time Days
name = "30 Days of Premium Time",
type = GameStore.OfferTypes.OFFER_TYPE_PREMIUM,
id = 3030,
},
{
-- Item 2: Bloodcurl Mount
name = "Bloodcurl",
type = GameStore.OfferTypes.OFFER_TYPE_MOUNT,
id = 92,
},
{
-- Item 3: Full Pumpkin Mummy Outfit
name = "Full Pumpkin Mummy Outfit",
type = GameStore.OfferTypes.OFFER_TYPE_OUTFIT,
sexId = { female = 1128, male = 1127 },
addon = 3,
},
{
-- Item 4: 100x Great Mana Potion
name = "100x Great Mana Potion",
type = GameStore.OfferTypes.OFFER_TYPE_STACKABLE,
itemtype = 238,
count = 100,
},
{
-- Item 5: 100x Great Health Potion
name = "100x Great Health Potion",
type = GameStore.OfferTypes.OFFER_TYPE_STACKABLE,
itemtype = 239,
count = 100,
},
},
},
},
},

-- Cosmetics
{
icons = { "Category_Cosmetics.png" },
Expand Down
112 changes: 111 additions & 1 deletion data/modules/scripts/gamestore/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ GameStore.OfferTypes = {
OFFER_TYPE_HUNTINGSLOT = 25,
OFFER_TYPE_ITEM_BED = 26,
OFFER_TYPE_ITEM_UNIQUE = 27,
OFFER_TYPE_BUNDLE = 28,
}

GameStore.SubActions = {
Expand Down Expand Up @@ -100,6 +101,7 @@ function convertType(type)
[GameStore.OfferTypes.OFFER_TYPE_HIRELING] = GameStore.ConverType.SHOW_HIRELING,
[GameStore.OfferTypes.OFFER_TYPE_ITEM_BED] = GameStore.ConverType.SHOW_NONE,
[GameStore.OfferTypes.OFFER_TYPE_ITEM_UNIQUE] = GameStore.ConverType.SHOW_ITEM,
[GameStore.OfferTypes.OFFER_TYPE_BUNDLE] = GameStore.ConverType.SHOW_NONE,
}

if not types[type] then
Expand Down Expand Up @@ -411,6 +413,69 @@ local function insertPlayerTransactionSummary(player, offer)
player:createTransactionSummary(offer.type, math.max(1, offer.count or 1), id)
end

function GameStore.processBundlePurchase(player, offer)
if not offer.contents or #offer.contents == 0 then
return error({ code = 0, message = "This package is empty and cannot be purchased." })
end

local totalCapacityNeeded = 0

for _, itemOffer in ipairs(offer.contents) do
if itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT and player:hasMount(itemOffer.id) then
return error({ code = 1, message = "You already own the mount '" .. itemOffer.name .. "' of this package." })
end

if itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then
local looktype = player:getSex() == PLAYERSEX_MALE and itemOffer.sexId.male or itemOffer.sexId.female
if looktype and player:hasOutfit(looktype, itemOffer.addon or 3) then
return error({ code = 1, message = "You already own the outfit '" .. itemOffer.name .. "' of this package." })
end
end

if itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_UNIQUE then
if player:getItemById(itemOffer.itemtype, true) then
return error({ code = 1, message = "You already own the unique item '" .. itemOffer.name .. "' of this package." })
end
end

local itemType = ItemType(itemOffer.itemtype or 0)
if itemType and itemType:getId() > 0 and itemType:getWeight() > 0 then
totalCapacityNeeded = totalCapacityNeeded + itemType:getWeight(itemOffer.count or 1)
end
end

if player:getFreeCapacity() < totalCapacityNeeded then
local missingCap = totalCapacityNeeded - player:getFreeCapacity()
return error({ code = 0, message = "You don't have enough capacity (" .. missingCap .. " oz.) for all items in this package." })
end

for _, itemOffer in ipairs(offer.contents) do
if itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM or itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_UNIQUE then
GameStore.processItemPurchase(player, itemOffer.itemtype, itemOffer.count or 1, itemOffer.movable, itemOffer.setOwner)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_INSTANT_REWARD_ACCESS then
GameStore.processInstantRewardAccess(player, itemOffer.count)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_CHARMS then
GameStore.processCharmsPurchase(player)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_BLESSINGS then
GameStore.processSingleBlessingPurchase(player, itemOffer.blessid, itemOffer.count)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_ALLBLESSINGS then
GameStore.processAllBlessingsPurchase(player, itemOffer.count)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_PREMIUM then
GameStore.processPremiumPurchase(player, itemOffer.id)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_STACKABLE then
GameStore.processStackablePurchase(player, itemOffer.itemtype, itemOffer.count, itemOffer.name, itemOffer.movable, itemOffer.setOwner)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_HOUSE or itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_BED then
GameStore.processHouseRelatedPurchase(player, itemOffer)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then
GameStore.processOutfitPurchase(player, itemOffer.sexId, itemOffer.addon)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then
GameStore.processMountPurchase(player, itemOffer.id)
elseif itemOffer.type == GameStore.OfferTypes.OFFER_TYPE_CHARGES then
GameStore.processChargesPurchase(player, itemOffer.itemtype, itemOffer.name, itemOffer.charges, itemOffer.movable, itemOffer.setOwner)
end
end
end

function parseBuyStoreOffer(playerId, msg)
local player = Player(playerId)
local id = msg:getU32()
Expand Down Expand Up @@ -525,6 +590,8 @@ function parseBuyStoreOffer(playerId, msg)
GameStore.processHirelingSkillPurchase(player, offer)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_HIRELING_OUTFIT then
GameStore.processHirelingOutfitPurchase(player, offer)
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BUNDLE then
GameStore.processBundlePurchase(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]" })
Expand Down Expand Up @@ -808,9 +875,52 @@ function Player.canBuyOffer(self, offer)
disabled = 1
disabledReason = "You need to have a hireling."
end
elseif offer.type == GameStore.OfferTypes.OFFER_TYPE_BUNDLE then
if offer.contents and #offer.contents > 0 then
for _, subOffer in ipairs(offer.contents) do
local subOfferDisabled = false
if subOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT or subOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then
local outfitLookType
if self:getSex() == PLAYERSEX_MALE then
outfitLookType = subOffer.sexId.male
else
outfitLookType = subOffer.sexId.female
end

if outfitLookType then
if subOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT and self:hasOutfit(outfitLookType) then
subOfferDisabled = true
elseif subOffer.type == GameStore.OfferTypes.OFFER_TYPE_OUTFIT_ADDON then
if self:hasOutfit(outfitLookType) then
if self:hasOutfit(outfitLookType, subOffer.addon) then
subOfferDisabled = true
end
else
subOfferDisabled = true
end
end
else
subOfferDisabled = true
end
elseif subOffer.type == GameStore.OfferTypes.OFFER_TYPE_MOUNT then
if self:hasMount(subOffer.id) then
subOfferDisabled = true
end
elseif subOffer.type == GameStore.OfferTypes.OFFER_TYPE_ITEM_UNIQUE then
if self:getItemById(subOffer.itemtype, true) then
subOfferDisabled = true
end
end

if subOfferDisabled then
disabled = 1
disabledReason = "You already own an exclusive item from this pack (" .. subOffer.name .. ")."
break
end
end
end
end
end

return { disabled = disabled, disabledReason = disabledReason }
end

Expand Down