Module:Shop: Difference between revisions
From Melvor Idle
(Generate bank slot cost text in a better supported way) |
(Update for v1.1) |
||
Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local Constants = require('Module:Constants') | |||
local GameData = require('Module:GameData') | |||
local Items = require('Module:Items') | local Items = require('Module:Items') | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
-- Overrides for various items, mostly relating to icon overrides | -- Overrides for various items, mostly relating to icon overrides | ||
local purchOverrides = { | local purchOverrides = { | ||
["Extra Bank Slot"] = { icon = {'Bank Slot', 'upgrade'}, link = 'Bank Slot' | ["Extra Bank Slot"] = { icon = {'Bank Slot', 'upgrade'}, link = 'Bank Slot' }, | ||
-- Golbin Raid items | -- Golbin Raid items | ||
["Reduce Wave Skip Cost"] = { icon = {'Melvor Logo', nil}, link = nil }, | ["Reduce Wave Skip Cost"] = { icon = {'Melvor Logo', nil}, link = nil }, | ||
Line 24: | Line 20: | ||
["Increase Prayer Level"] = { icon = {'Prayer', 'skill'}, link = nil }, | ["Increase Prayer Level"] = { icon = {'Prayer', 'skill'}, link = nil }, | ||
["Increase Prayer Points gained per Wave Completion"] = { icon = {'Prayer', 'skill'}, link = nil }, | ["Increase Prayer Points gained per Wave Completion"] = { icon = {'Prayer', 'skill'}, link = nil }, | ||
["Faster Golbin Spawns"] = { icon = {'Timer', nil}, link = nil | ["Faster Golbin Spawns"] = { icon = {'Timer', nil}, link = nil }, | ||
["Golbin Crate"] = { icon = {'Golbin Crate', 'upgrade'}, link = nil | ["Golbin Crate"] = { icon = {'Golbin Crate', 'upgrade'}, link = nil } | ||
} | } | ||
function p.getPurchase(purchaseName) | function p.getPurchase(purchaseName) | ||
local purchList = p.getPurchases(function(purch) return p._getPurchaseName(purch) == purchaseName end) | |||
if purchList ~= nil and not Shared.tableIsEmpty(purchList) then | |||
return purchList[1] | |||
end | |||
end | end | ||
function p. | function p.getPurchases(checkFunc) | ||
return GameData.getEntities('shopPurchases', checkFunc) | |||
end | end | ||
Line 70: | Line 60: | ||
local purchaseList = {} | local purchaseList = {} | ||
if statName == 'cost' then | if statName == 'cost' then | ||
purchaseList = p.getPurchases(function( | purchaseList = p.getPurchases(function(purch) return p._getPurchaseName(purch) == purchaseName end) | ||
else | else | ||
purchaseList = {p.getPurchase(purchaseName)} | purchaseList = {p.getPurchase(purchaseName)} | ||
end | end | ||
if Shared. | if Shared.tableIsEmpty(purchaseList) then | ||
return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]" | return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]" | ||
else | else | ||
Line 84: | Line 74: | ||
return table.concat(resultPart, ' or ') | return table.concat(resultPart, ' or ') | ||
end | end | ||
end | |||
function p._getPurchaseName(purch) | |||
if purch.customName ~= nil then | |||
return purch.customName | |||
elseif purch.contains ~= nil then | |||
if purch.contains.items ~= nil and not Shared.tableIsEmpty(purch.contains.items) then | |||
local item = Items.getItemByID(purch.contains.items[1].id) | |||
if item ~= nil then | |||
return item.name | |||
end | |||
elseif purch.contains.petID ~= nil then | |||
local pet = GameData.getEntityByID('pets', purch.contains.petID) | |||
if pet ~= nil then | |||
return pet.name | |||
end | |||
end | |||
end | |||
return '' | |||
end | end | ||
Line 89: | Line 98: | ||
local displayInline = (inline ~= nil and inline or false) | local displayInline = (inline ~= nil and inline or false) | ||
local costArray = {} | local costArray = {} | ||
if cost. | local currencies = {'gp', 'slayerCoins', 'raidCoins'} | ||
table.insert( | for i, currency in ipairs(currencies) do | ||
if cost[currency] ~= nil then | |||
local costStr = p.getCurrencyCostString(cost[currency], currency) | |||
if costStr ~= nil then | |||
table.insert(costArray, costStr) | |||
end | |||
end | |||
end | |||
if cost.items ~= nil and not Shared.tableIsEmpty(cost.items) then | |||
local itemArray = {} | |||
for i, itemCost in ipairs(cost.items) do | |||
local item = Items.getItemByID(itemCost.id) | |||
if item ~= nil then | |||
table.insert(itemArray, Icons.Icon({item.name, type="item", notext=(not displayInline and true or nil), qty=itemCost.quantity})) | |||
end | |||
end | |||
if not Shared.tableIsEmpty(itemArray) then | |||
table.insert(costArray, table.concat(itemArray, ', ')) | |||
end | |||
end | end | ||
if not Shared.tableIsEmpty(costArray) then | |||
local sep, lastSep = '<br/>', '<br/>' | |||
if displayInline then | |||
sep = ', ' | |||
lastSep = Shared.tableCount(costArray) > 2 and ', and ' or ' and ' | |||
end | |||
return mw.text.listToText(costArray, sep, lastSep) | |||
end | |||
end | |||
function p.getCurrencyCostString(cost, currency) | |||
local decoratorList = { | |||
["gp"] = Icons.GP, | |||
["slayerCoins"] = Icons.SC, | |||
["raidCoins"] = Icons.RC | |||
} | |||
local decorator = nil | |||
if currency ~= nil then | |||
decorator = decoratorList[currency] | |||
end | |||
if decorator == nil then | |||
decorator = function(cost) return cost end | |||
end | |||
if cost.type == 'BankSlot' then | |||
-- Unusual bit of code that basically evaluates wikitext '<math>C_b</math>*' | |||
return mw.getCurrentFrame():callParserFunction('#tag:math', {'C_b'}) .. '*' | |||
elseif cost.type == 'Linear' and (cost.initial > 0 or cost.scaling > 0) then | |||
return decorator(cost.initial) .. '<br/>+' .. decorator(cost.scaling) .. ' for each purchase' | |||
elseif cost.type == 'Glove' or cost.type == 'Fixed' and cost.cost > 0 then | |||
-- Type Glove exists in game so the Merchan's Permit cost reduction can be applied, | |||
-- it makes no difference here | |||
return decorator(cost.cost) | |||
end | |||
end | end | ||
function p.getRequirementString(reqs) | function p.getRequirementString(reqs) | ||
if reqs == nil or Shared. | if reqs == nil or Shared.tableIsEmpty(reqs) then | ||
return | return 'None' | ||
end | end | ||
local reqArray = {} | local reqArray = {} | ||
for i, req in ipairs(reqs) do | |||
if req.type == 'SkillLevel' then | |||
local skillName = Constants.getSkillName(req.skillID) | |||
if skillName ~= nil then | |||
table.insert(reqArray, Icons._SkillReq(skillName, req.level)) | |||
end | |||
elseif req.type == 'DungeonCompletion' then | |||
local dung = GameData.getEntityByID('dungeons', req.dungeonID) | |||
if dung ~= nil then | |||
local dungStr = 'Complete ' .. Icons.Icon({dung.name, type='dungeon'}) | |||
if req.count > 1 then | |||
dungStr = dungStr .. ' ' .. Shared.formatnum(req.count) .. ' times' | |||
end | |||
table.insert(reqArray, dungStr) | |||
end | |||
elseif req.type == 'SlayerTask' then | |||
table.insert(reqArray, 'Complete ' .. Shared.formatnum(req.count) .. ' ' .. req.tier .. ' Slayer Tasks') | |||
elseif req.type == 'TownshipTask' then | |||
table.insert(reqArray, 'Complete ' .. Shared.formatnum(req.count) .. ' Township Tasks') | |||
elseif req.type == 'TownshipBuilding' then | |||
local tsData = GameData.getSkillData('melvorD:Township') | |||
if tsData ~= nil and tsData.buildings ~= nil then | |||
local building = GameData.getEntityByID(tsData.buildings, req.buildingID) | |||
if building ~= nil then | |||
table.insert(reqArray, 'Have ' .. Shared.formatnum(req.count) .. ' ' .. building.name .. ' actively built in Township') | |||
end | |||
end | |||
elseif req.type == 'Completion' then | |||
local ns = GameData.getEntityByID('namespaces', req.namespace) | |||
if ns ~= nil then | |||
table.insert(reqArray, req.percent .. '% ' .. ns.displayName .. ' Completion') | |||
end | |||
elseif req.type == 'AllSkillLevels' then | |||
local reqText = 'Level ' .. req.level .. ' in all skills' | |||
if req.exceptions ~= nil and not Shared.tableIsEmpty(req.exceptions) then | |||
local exceptSkills = {} | |||
for i, skillID in ipairs(req.exceptions) do | |||
local skillName = Constants.getSkillName(skillID) | |||
if skillName ~= nil then | |||
table.insert(exceptSkills, Icons.Icon({skillName, type='skill'})) | |||
end | |||
end | |||
reqText = reqText .. ' except for ' .. table.concat(exceptSkills, ', ') | |||
end | |||
table.insert(reqArray, reqText) | |||
else | |||
table.insert(reqArray, 'ERROR: Unknown requirement: ' .. (req.type or 'nil') .. '[[Category:Pages with script errors]]') | |||
end | |||
end | |||
if Shared.tableIsEmpty(reqArray) then | |||
return 'None' | |||
else | |||
return table.concat(reqArray, '<br/>') | |||
end | |||
end | end | ||
Line 186: | Line 237: | ||
local containArray = {} | local containArray = {} | ||
local GPTotal = 0 | local GPTotal = 0 | ||
if purchase.contains.items ~= nil and Shared. | if purchase.contains.items ~= nil and not Shared.tableIsEmpty(purchase.contains.items) then | ||
if not asList then | if not asList then | ||
table.insert(containArray, '{| class="wikitable sortable stickyHeader"') | table.insert(containArray, '{| class="wikitable sortable stickyHeader"') | ||
Line 192: | Line 243: | ||
table.insert(containArray, '! colspan="2" | Item !! Quantity !! Price') | table.insert(containArray, '! colspan="2" | Item !! Quantity !! Price') | ||
end | end | ||
for i, itemLine in | for i, itemLine in ipairs(purchase.contains.items) do | ||
local item = Items.getItemByID(itemLine | local item = Items.getItemByID(itemLine.id) | ||
local itemQty = itemLine.quantity | |||
if asList then | if asList then | ||
table.insert(containArray, Icons.Icon({item.name, type='item', qty= | table.insert(containArray, Icons.Icon({item.name, type='item', qty=itemQty})) | ||
else | else | ||
local GPVal = item.sellsFor * | local GPVal = item.sellsFor * itemQty | ||
GPTotal = GPTotal + GPVal | GPTotal = GPTotal + GPVal | ||
table.insert(containArray, '|-\r\n| style="min-width:25px"| ' .. Icons.Icon({item.name, type='item', notext=true, size='25'})) | table.insert(containArray, '|-\r\n| style="min-width:25px"| ' .. Icons.Icon({item.name, type='item', notext=true, size='25'})) | ||
table.insert(containArray, '| ' .. Icons.Icon({item.name, type='item', noicon=true}) .. '\r\n| data-sort-value="' .. | table.insert(containArray, '| ' .. Icons.Icon({item.name, type='item', noicon=true}) .. '\r\n| data-sort-value="' .. itemQty .. '" style="text-align:right" | ' .. Shared.formatnum(itemQty)) | ||
table.insert(containArray, '| data-sort-value="' .. GPVal .. '"| ' .. Icons.GP(GPVal)) | table.insert(containArray, '| data-sort-value="' .. GPVal .. '"| ' .. Icons.GP(GPVal)) | ||
end | end | ||
end | end | ||
end | end | ||
if purchase. | if purchase.itemCharges ~= nil and purchase.itemCharges.quantity > 0 then | ||
local gloveItem = Items.getItemByID(purchase.itemCharges.id) | |||
local chargeQty = purchase.itemCharges.quantity | |||
if gloveItem ~= nil then | |||
if asList then | |||
table.insert(containArray, '+'..Shared.formatnum(chargeQty)..' '..Icons.Icon({gloveItem.name, type='item'})..' Charges') | |||
else | |||
table.insert(containArray, '|-\r\n| style="min-width:25px"| ' .. Icons.Icon({gloveItem.name, type='item', notext=true, size='25'})) | |||
table.insert(containArray, '| ' .. Icons.Icon({gloveItem.name, type='item', noicon=true}) .. ' Charges\r\n| data-sort-value="' .. chargeQty .. '" style="text-align:right" | ' .. Shared.formatnum(chargeQty)) | |||
table.insert(containArray, '| data-sort-value="0"| ' .. Icons.GP(0)) | |||
end | |||
end | |||
end | end | ||
if not asList and Shared. | if not asList and not Shared.tableIsEmpty(containArray) then | ||
table.insert(containArray, '|- class="sortbottom"\r\n! colspan="3"| Total\r\n| ' .. Icons.GP(GPTotal) .. '\r\n|}') | table.insert(containArray, '|- class="sortbottom"\r\n! colspan="3"| Total\r\n| ' .. Icons.GP(GPTotal) .. '\r\n|}') | ||
end | end | ||
Line 237: | Line 293: | ||
function p._getPurchaseBuyLimit(purchase, asList) | function p._getPurchaseBuyLimit(purchase, asList) | ||
if asList == nil then asList = true end | if asList == nil then asList = true end | ||
local defaultLimit = (purchase.defaultBuyLimit == 0 and 'Unlimited') or Shared.formatnum(purchase.defaultBuyLimit) | |||
if purchase.buyLimitOverrides == nil or Shared.tableIsEmpty(purchase.buyLimitOverrides) then | |||
-- Same limit for all game modes | |||
return defaultLimit | |||
else | |||
-- The limit varies depending on game mode | |||
local limitTable = {} | |||
local gamemodeHasIcon = { 'melvorF:Hardcore', 'melvorF:Adventure' } | |||
for i, buyLimit in ipairs(purchase.buyLimitOverrides) do | |||
local gamemode = GameData.getEntityByID('gamemodes', buyLimit.gamemodeID) | |||
if gamemode ~= nil then | |||
local gamemodeText = nil | |||
if Shared.contains(gamemodeHasIcon, gamemode.id) then | |||
gamemodeText = Icons.Icon({gamemode.name, notext=(not asList or nil)}) | |||
else | |||
gamemodeText = '[[Game Mode#' .. gamemode.name .. '|' .. gamemode.name .. ']]' | |||
end | |||
local limitText = (buyLimit.maximum == 0 and 'Unlimited') or Shared.formatnum(buyLimit.maximum) | |||
table.insert(limitTable, limitText .. (asList and ' for ' or ' ') .. gamemodeText) | |||
end | |||
end | |||
table.insert(limitTable, defaultLimit .. (asList and ' for ' or ' ') .. 'All other game modes') | |||
return table.concat(limitTable, (asList and ' or ' or '<br/>')) | |||
end | |||
end | end | ||
Line 286: | Line 335: | ||
function p._getPurchaseIcon(iconArgs) | function p._getPurchaseIcon(iconArgs) | ||
local purchase = iconArgs[1] | local purchase = iconArgs[1] | ||
local override = purchOverrides[ | local purchaseName = p._getPurchaseName(purchase) | ||
local override = purchOverrides[purchaseName] | |||
local purchType = p._getPurchaseType(purchase) | local purchType = p._getPurchaseType(purchase) | ||
-- Amend iconArgs before passing to Icons.Icon() | -- Amend iconArgs before passing to Icons.Icon() | ||
iconArgs[1] = ((override ~= nil and override.icon[1]) or | iconArgs[1] = ((override ~= nil and override.icon[1]) or purchaseName) | ||
if override ~= nil then | if override ~= nil then | ||
iconArgs['type'] = override.icon[2] | iconArgs['type'] = override.icon[2] | ||
Line 319: | Line 369: | ||
for j, curr in ipairs(costCurrencies) do | for j, curr in ipairs(costCurrencies) do | ||
local costAmt = purchase.cost[curr] | local costAmt = purchase.cost[curr] | ||
if costAmt.type == 'BankSlot' then | |||
return -1 | |||
elseif costAmt.type == 'Linear' then | |||
return costAmt.initial | |||
elseif costAmt.type == 'Glove' or costAmt.type == 'Fixed' and costAmt.cost > 0 then | |||
return costAmt.cost | |||
end | |||
end | |||
end | end | ||
Line 382: | Line 436: | ||
end | end | ||
for i, purchase in purchIterator(Purchases) do | for i, purchase in purchIterator(Purchases) do | ||
local purchName = p._getPurchaseName(purchase) | |||
local purchOverride = nil | local purchOverride = nil | ||
if purchOverrides ~= nil then | if purchOverrides ~= nil then | ||
purchOverride = purchOverrides[ | purchOverride = purchOverrides[purchName] | ||
end | end | ||
Line 396: | Line 451: | ||
else | else | ||
purchLink = purchOverride.link .. '|' | purchLink = purchOverride.link .. '|' | ||
end | end | ||
end | end | ||
if iconNoLink == nil or iconNoLink ~= true then purchName = '[[' .. purchLink .. purchName .. ']]' end | if iconNoLink == nil or iconNoLink ~= true then purchName = '[[' .. purchLink .. purchName .. ']]' end | ||
Line 415: | Line 465: | ||
table.insert(resultPart, '| ' .. purchType) | table.insert(resultPart, '| ' .. purchType) | ||
elseif column == 'Description' then | elseif column == 'Description' then | ||
table.insert(resultPart, '| ' .. purchase. | table.insert(resultPart, '| ' .. purchase.customDescription) | ||
elseif column == 'Cost' then | elseif column == 'Cost' then | ||
local cellProp = '|style="text-align:right;"' | local cellProp = '|style="text-align:right;"' | ||
Line 465: | Line 515: | ||
end | end | ||
end | end | ||
local shopCat = GameData.getEntityByName('shopCategories', cat) | |||
if shopCat == nil then | if shopCat == nil then | ||
return 'ERROR: Invalid category '..cat..'[[Category:Pages with script errors]]' | return 'ERROR: Invalid category '..cat..'[[Category:Pages with script errors]]' | ||
else | else | ||
return p._getShopTable( | local catPurchases = p.getPurchases(function(purch) return purch.category == shopCat.id end) | ||
return p._getShopTable(catPurchases, options) | |||
end | end | ||
end | end | ||
function p.getItemCostArray(itemID) | function p.getItemCostArray(itemID) | ||
local purchaseArray = {} | |||
for i, purchase in ipairs(GameData.rawData.shopPurchases) do | |||
if purchase.cost ~= nil and purchase.cost.items ~= nil then | |||
for j, itemCost in ipairs(purchase.cost.items) do | |||
if itemCost.id == itemID then | |||
table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = itemCost.quantity }) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
return purchaseArray | |||
end | end | ||
function p.getItemSourceArray(itemID) | function p.getItemSourceArray(itemID) | ||
local purchaseArray = {} | |||
for i, purchase in ipairs(GameData.rawData.shopPurchases) do | |||
if purchase.contains ~= nil and purchase.contains.items ~= nil then | |||
for j, itemContains in ipairs(purchase.contains.items) do | |||
if itemContains.id == itemID then | |||
table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = itemContains.quantity }) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
return purchaseArray | |||
end | end | ||
Line 531: | Line 558: | ||
result = result..'\r\n!colspan="2"|'..Icons.Icon({'Shop'})..' Purchase' | result = result..'\r\n!colspan="2"|'..Icons.Icon({'Shop'})..' Purchase' | ||
if purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then | if purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then | ||
result = result..' - '..Icons.Icon({purchase | result = result..' - '..Icons.Icon({p._getPurchaseName(purchase), type='item'}) | ||
end | end | ||
Line 551: | Line 578: | ||
local purchaseArray = p.getItemSourceArray(item.id) | local purchaseArray = p.getItemSourceArray(item.id) | ||
for i, purchase in | for i, purchase in ipairs(purchaseArray) do | ||
table.insert(tableArray, p._getPurchaseTable(purchase)) | table.insert(tableArray, p._getPurchaseTable(purchase)) | ||
end | end | ||
Line 569: | Line 596: | ||
function p.getShopMiscUpgradeTable() | function p.getShopMiscUpgradeTable() | ||
local purchList = p.getPurchases(function( | local purchList = p.getPurchases(function(purch) return purch.category == 'melvorD:General' and string.find(purch.id, '^melvorD:Auto_Eat') == nil end) | ||
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' }, purchaseHeader = 'Upgrade' }) | return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' }, purchaseHeader = 'Upgrade' }) | ||
end | end | ||
function p.getShopSkillcapeTable() | function p.getShopSkillcapeTable() | ||
local capeList = p.getPurchases(function( | local capeList = p.getPurchases(function(purch) return purch.category == 'melvorD:Skillcapes' end) | ||
local sortOrderFunc = function(a, b) | local sortOrderFunc = function(a, b) | ||
if a.cost.gp == b.cost.gp then | if a.cost.gp == b.cost.gp then | ||
return a. | return p._getPurchaseName(a) < p._getPurchaseName(b) | ||
else | else | ||
return a.cost.gp < b.cost.gp | return a.cost.gp < b.cost.gp | ||
Line 592: | Line 620: | ||
function p.getAutoEatTable() | function p.getAutoEatTable() | ||
local resultPart = {} | local resultPart = {} | ||
local purchasesAE = p.getPurchases(function( | local purchasesAE = p.getPurchases(function(purch) return purch.category == 'melvorD:General' and string.find(purch.id, '^melvorD:Auto_Eat') ~= nil end) | ||
-- Table header | -- Table header | ||
Line 601: | Line 629: | ||
local mods = {["increasedAutoEatEfficiency"] = 0, ["increasedAutoEatHPLimit"] = 0, ["increasedAutoEatThreshold"] = 0} | local mods = {["increasedAutoEatEfficiency"] = 0, ["increasedAutoEatHPLimit"] = 0, ["increasedAutoEatThreshold"] = 0} | ||
for i, purchase in ipairs(purchasesAE) do | for i, purchase in ipairs(purchasesAE) do | ||
local purchaseName = p._getPurchaseName(purchase) | |||
-- Modifiers must be accumulated as we go | -- Modifiers must be accumulated as we go | ||
for modName, modValue in pairs(mods) do | for modName, modValue in pairs(mods) do | ||
Line 608: | Line 637: | ||
end | end | ||
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. purchaseName .. '"| ' .. Icons.Icon({purchaseName, type='upgrade', size=50, notext=true})) | |||
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. | table.insert(resultPart, '| ' .. Icons.Icon({purchaseName, type='upgrade', noicon=true})) | ||
table.insert(resultPart, '| ' .. Icons.Icon({ | |||
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatThreshold .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatThreshold, 0, 0)) .. '%') | table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatThreshold .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatThreshold, 0, 0)) .. '%') | ||
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatEfficiency .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatEfficiency, 0, 0)) .. '%') | table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatEfficiency .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatEfficiency, 0, 0)) .. '%') | ||
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatHPLimit .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatHPLimit, 0, 0)) .. '%') | table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatHPLimit .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatHPLimit, 0, 0)) .. '%') | ||
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. | table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. p._getPurchaseSortValue(purchase) .. '" | ' .. p.getCostString(purchase.cost, false)) | ||
end | end | ||
table.insert(resultPart, '|}') | table.insert(resultPart, '|}') | ||
Line 627: | Line 655: | ||
local getGodDungeon = | local getGodDungeon = | ||
function(reqs) | function(reqs) | ||
for i, req in ipairs(reqs) do | |||
if req.type == 'DungeonCompletion' and string.find(req.dungeonID, 'God_Dungeon$') ~= nil then | |||
return GameData.getEntityByID('dungeons', req.dungeonID) | |||
end | |||
end | |||
end | |||
local upgradeList = p.getPurchases( | local upgradeList = p.getPurchases( | ||
function( | function(purch) | ||
if | if purch.category == 'melvorD:SkillUpgrades' and purch.purchaseRequirements ~= nil then | ||
return getGodDungeon(purch. | return getGodDungeon(purch.purchaseRequirements) ~= nil | ||
end | end | ||
return false | return false | ||
end) | end) | ||
if Shared. | if Shared.tableIsEmpty(upgradeList) then | ||
return '' | |||
end | |||
-- Table header | -- Table header | ||
Line 651: | Line 680: | ||
-- Rows for each God upgrade | -- Rows for each God upgrade | ||
for i, upgrade in ipairs(upgradeList) do | for i, upgrade in ipairs(upgradeList) do | ||
local dung = getGodDungeon(upgrade. | local upgradeName = p._getPurchaseName(upgrade) | ||
local dung = getGodDungeon(upgrade.purchaseRequirements) | |||
local costSortValue = p._getPurchaseSortValue(upgrade) | local costSortValue = p._getPurchaseSortValue(upgrade) | ||
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. | table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. upgradeName .. '"| ' .. Icons.Icon({upgradeName, type='upgrade', size=50, notext=true})) | ||
table.insert(resultPart, '| ' .. Icons.Icon({ | table.insert(resultPart, '| ' .. Icons.Icon({upgradeName, type='upgrade', noicon=true})) | ||
table.insert(resultPart, '| ' .. upgrade. | table.insert(resultPart, '| ' .. upgrade.customDescription) | ||
table.insert(resultPart, '| data-sort-value="' .. dung.name .. '"| ' .. Icons.Icon({dung.name, type='dungeon'})) | table.insert(resultPart, '| data-sort-value="' .. dung.name .. '"| ' .. Icons.Icon({dung.name, type='dungeon'})) | ||
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. costSortValue .. '"| ' .. p.getCostString(upgrade.cost, false)) | table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. costSortValue .. '"| ' .. p.getCostString(upgrade.cost, false)) | ||
Line 685: | Line 715: | ||
'increasedChancePerfectCookPot', 'increasedChancePerfectCookGlobal'} | 'increasedChancePerfectCookPot', 'increasedChancePerfectCookGlobal'} | ||
local totalBonusVal, totalPerfectChance = 0, 0 | local totalBonusVal, totalPerfectChance = 0, 0 | ||
local utilityList = p.getPurchases(function( | local utilityList = p.getPurchases(function(purch) return purch.category == 'melvorD:SkillUpgrades' and string.find(p._getPurchaseName(purch), category .. '$') ~= nil end) | ||
local resultPart = {} | local resultPart = {} | ||
Line 697: | Line 727: | ||
-- Row for each upgrade | -- Row for each upgrade | ||
for i, utility in ipairs(utilityList) do | for i, utility in ipairs(utilityList) do | ||
local utilityName = p._getPurchaseName(utility) | |||
-- First determine bonus XP/doubling chance and perfect chance | -- First determine bonus XP/doubling chance and perfect chance | ||
local bonusVal, perfectChance = 0, 0 | local bonusVal, perfectChance = 0, 0 | ||
Line 716: | Line 747: | ||
totalBonusVal = totalBonusVal + bonusVal | totalBonusVal = totalBonusVal + bonusVal | ||
totalPerfectChance = totalPerfectChance + perfectChance | totalPerfectChance = totalPerfectChance + perfectChance | ||
table.insert(resultPart, '|-') | table.insert(resultPart, '|-') | ||
table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({ | table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({utilityName, type='upgrade', size='50', notext=true})) | ||
table.insert(resultPart, '|' .. | table.insert(resultPart, '|' .. utilityName) | ||
table.insert(resultPart, '|style="text-align:right"|' .. p.getRequirementString( | table.insert(resultPart, '|style="text-align:right"|' .. p.getRequirementString(utility.purchaseRequirements)) | ||
table.insert(resultPart, '|style="text-align:right"|' .. p.getCostString(utility.cost, false)) | table.insert(resultPart, '|style="text-align:right"|' .. p.getCostString(utility.cost, false)) | ||
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. bonusVal .. '%') | table.insert(resultPart, '|style="text-align:right"|' .. '+' .. bonusVal .. '%') |
Revision as of 15:52, 22 October 2022
Documentation for this module may be created at Module:Shop/doc
local p = {}
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local GameData = require('Module:GameData')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
-- Overrides for various items, mostly relating to icon overrides
local purchOverrides = {
["Extra Bank Slot"] = { icon = {'Bank Slot', 'upgrade'}, link = 'Bank Slot' },
-- Golbin Raid items
["Reduce Wave Skip Cost"] = { icon = {'Melvor Logo', nil}, link = nil },
["Food Bonus"] = { icon = {'Melvor Logo', nil}, link = nil },
["Ammo Gatherer"] = { icon = {'Melvor Logo', nil}, link = nil },
["Rune Pouch"] = { icon = {'Melvor Logo', nil}, link = nil },
["Increase Starting Prayer Points"] = { icon = {'Melvor Logo', nil}, link = nil },
["Unlock Combat Passive Slot"] = { icon = {'Melvor Logo', nil}, link = nil },
["Prayer"] = { icon = {'Prayer', 'skill'}, link = nil },
["Increase Prayer Level"] = { icon = {'Prayer', 'skill'}, link = nil },
["Increase Prayer Points gained per Wave Completion"] = { icon = {'Prayer', 'skill'}, link = nil },
["Faster Golbin Spawns"] = { icon = {'Timer', nil}, link = nil },
["Golbin Crate"] = { icon = {'Golbin Crate', 'upgrade'}, link = nil }
}
function p.getPurchase(purchaseName)
local purchList = p.getPurchases(function(purch) return p._getPurchaseName(purch) == purchaseName end)
if purchList ~= nil and not Shared.tableIsEmpty(purchList) then
return purchList[1]
end
end
function p.getPurchases(checkFunc)
return GameData.getEntities('shopPurchases', checkFunc)
end
function p._getPurchaseStat(purchase, stat, inline)
local displayInline = (inline ~= nil and inline or false)
if stat == 'cost' then
return p.getCostString(purchase.cost, displayInline)
elseif stat == 'requirements' then
return p.getRequirementString(purchase.unlockRequirements)
elseif stat == 'contents' then
return p._getPurchaseContents(purchase, true)
elseif stat == 'type' then
return p._getPurchaseType(purchase)
elseif stat == 'buyLimit' then
return p._getPurchaseBuyLimit(purchase, not displayInline)
else
return purchase[stat]
end
end
function p.getPurchaseStat(frame)
local args = frame.args ~= nil and frame.args or frame
local purchaseName = args[1]
local statName = args[2]
local displayInline = (args['inline'] ~= nil and string.lower(args['inline']) == 'true' or false)
-- Hack for some purchases existing twice with varying costs (e.g. 'Extra Equipment Set')
local purchaseList = {}
if statName == 'cost' then
purchaseList = p.getPurchases(function(purch) return p._getPurchaseName(purch) == purchaseName end)
else
purchaseList = {p.getPurchase(purchaseName)}
end
if Shared.tableIsEmpty(purchaseList) then
return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]"
else
local resultPart = {}
for i, purchase in ipairs(purchaseList) do
table.insert(resultPart, p._getPurchaseStat(purchase, statName, displayInline))
end
return table.concat(resultPart, ' or ')
end
end
function p._getPurchaseName(purch)
if purch.customName ~= nil then
return purch.customName
elseif purch.contains ~= nil then
if purch.contains.items ~= nil and not Shared.tableIsEmpty(purch.contains.items) then
local item = Items.getItemByID(purch.contains.items[1].id)
if item ~= nil then
return item.name
end
elseif purch.contains.petID ~= nil then
local pet = GameData.getEntityByID('pets', purch.contains.petID)
if pet ~= nil then
return pet.name
end
end
end
return ''
end
function p.getCostString(cost, inline)
local displayInline = (inline ~= nil and inline or false)
local costArray = {}
local currencies = {'gp', 'slayerCoins', 'raidCoins'}
for i, currency in ipairs(currencies) do
if cost[currency] ~= nil then
local costStr = p.getCurrencyCostString(cost[currency], currency)
if costStr ~= nil then
table.insert(costArray, costStr)
end
end
end
if cost.items ~= nil and not Shared.tableIsEmpty(cost.items) then
local itemArray = {}
for i, itemCost in ipairs(cost.items) do
local item = Items.getItemByID(itemCost.id)
if item ~= nil then
table.insert(itemArray, Icons.Icon({item.name, type="item", notext=(not displayInline and true or nil), qty=itemCost.quantity}))
end
end
if not Shared.tableIsEmpty(itemArray) then
table.insert(costArray, table.concat(itemArray, ', '))
end
end
if not Shared.tableIsEmpty(costArray) then
local sep, lastSep = '<br/>', '<br/>'
if displayInline then
sep = ', '
lastSep = Shared.tableCount(costArray) > 2 and ', and ' or ' and '
end
return mw.text.listToText(costArray, sep, lastSep)
end
end
function p.getCurrencyCostString(cost, currency)
local decoratorList = {
["gp"] = Icons.GP,
["slayerCoins"] = Icons.SC,
["raidCoins"] = Icons.RC
}
local decorator = nil
if currency ~= nil then
decorator = decoratorList[currency]
end
if decorator == nil then
decorator = function(cost) return cost end
end
if cost.type == 'BankSlot' then
-- Unusual bit of code that basically evaluates wikitext '<math>C_b</math>*'
return mw.getCurrentFrame():callParserFunction('#tag:math', {'C_b'}) .. '*'
elseif cost.type == 'Linear' and (cost.initial > 0 or cost.scaling > 0) then
return decorator(cost.initial) .. '<br/>+' .. decorator(cost.scaling) .. ' for each purchase'
elseif cost.type == 'Glove' or cost.type == 'Fixed' and cost.cost > 0 then
-- Type Glove exists in game so the Merchan's Permit cost reduction can be applied,
-- it makes no difference here
return decorator(cost.cost)
end
end
function p.getRequirementString(reqs)
if reqs == nil or Shared.tableIsEmpty(reqs) then
return 'None'
end
local reqArray = {}
for i, req in ipairs(reqs) do
if req.type == 'SkillLevel' then
local skillName = Constants.getSkillName(req.skillID)
if skillName ~= nil then
table.insert(reqArray, Icons._SkillReq(skillName, req.level))
end
elseif req.type == 'DungeonCompletion' then
local dung = GameData.getEntityByID('dungeons', req.dungeonID)
if dung ~= nil then
local dungStr = 'Complete ' .. Icons.Icon({dung.name, type='dungeon'})
if req.count > 1 then
dungStr = dungStr .. ' ' .. Shared.formatnum(req.count) .. ' times'
end
table.insert(reqArray, dungStr)
end
elseif req.type == 'SlayerTask' then
table.insert(reqArray, 'Complete ' .. Shared.formatnum(req.count) .. ' ' .. req.tier .. ' Slayer Tasks')
elseif req.type == 'TownshipTask' then
table.insert(reqArray, 'Complete ' .. Shared.formatnum(req.count) .. ' Township Tasks')
elseif req.type == 'TownshipBuilding' then
local tsData = GameData.getSkillData('melvorD:Township')
if tsData ~= nil and tsData.buildings ~= nil then
local building = GameData.getEntityByID(tsData.buildings, req.buildingID)
if building ~= nil then
table.insert(reqArray, 'Have ' .. Shared.formatnum(req.count) .. ' ' .. building.name .. ' actively built in Township')
end
end
elseif req.type == 'Completion' then
local ns = GameData.getEntityByID('namespaces', req.namespace)
if ns ~= nil then
table.insert(reqArray, req.percent .. '% ' .. ns.displayName .. ' Completion')
end
elseif req.type == 'AllSkillLevels' then
local reqText = 'Level ' .. req.level .. ' in all skills'
if req.exceptions ~= nil and not Shared.tableIsEmpty(req.exceptions) then
local exceptSkills = {}
for i, skillID in ipairs(req.exceptions) do
local skillName = Constants.getSkillName(skillID)
if skillName ~= nil then
table.insert(exceptSkills, Icons.Icon({skillName, type='skill'}))
end
end
reqText = reqText .. ' except for ' .. table.concat(exceptSkills, ', ')
end
table.insert(reqArray, reqText)
else
table.insert(reqArray, 'ERROR: Unknown requirement: ' .. (req.type or 'nil') .. '[[Category:Pages with script errors]]')
end
end
if Shared.tableIsEmpty(reqArray) then
return 'None'
else
return table.concat(reqArray, '<br/>')
end
end
function p._getPurchaseType(purchase)
if purchase.contains == nil then
return 'Unknown'
elseif purchase.contains.pet ~= nil then
return 'Pet'
elseif purchase.contains.modifiers ~= nil or purchase.contains.items == nil or Shared.tableCount(purchase.contains.items) == 0 then
return 'Upgrade'
elseif purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then
return 'Item Bundle'
else
return 'Item'
end
end
function p._getPurchaseContents(purchase, asList)
if asList == nil then asList = true end
local containArray = {}
local GPTotal = 0
if purchase.contains.items ~= nil and not Shared.tableIsEmpty(purchase.contains.items) then
if not asList then
table.insert(containArray, '{| class="wikitable sortable stickyHeader"')
table.insert(containArray, '|- class="headerRow-0"')
table.insert(containArray, '! colspan="2" | Item !! Quantity !! Price')
end
for i, itemLine in ipairs(purchase.contains.items) do
local item = Items.getItemByID(itemLine.id)
local itemQty = itemLine.quantity
if asList then
table.insert(containArray, Icons.Icon({item.name, type='item', qty=itemQty}))
else
local GPVal = item.sellsFor * itemQty
GPTotal = GPTotal + GPVal
table.insert(containArray, '|-\r\n| style="min-width:25px"| ' .. Icons.Icon({item.name, type='item', notext=true, size='25'}))
table.insert(containArray, '| ' .. Icons.Icon({item.name, type='item', noicon=true}) .. '\r\n| data-sort-value="' .. itemQty .. '" style="text-align:right" | ' .. Shared.formatnum(itemQty))
table.insert(containArray, '| data-sort-value="' .. GPVal .. '"| ' .. Icons.GP(GPVal))
end
end
end
if purchase.itemCharges ~= nil and purchase.itemCharges.quantity > 0 then
local gloveItem = Items.getItemByID(purchase.itemCharges.id)
local chargeQty = purchase.itemCharges.quantity
if gloveItem ~= nil then
if asList then
table.insert(containArray, '+'..Shared.formatnum(chargeQty)..' '..Icons.Icon({gloveItem.name, type='item'})..' Charges')
else
table.insert(containArray, '|-\r\n| style="min-width:25px"| ' .. Icons.Icon({gloveItem.name, type='item', notext=true, size='25'}))
table.insert(containArray, '| ' .. Icons.Icon({gloveItem.name, type='item', noicon=true}) .. ' Charges\r\n| data-sort-value="' .. chargeQty .. '" style="text-align:right" | ' .. Shared.formatnum(chargeQty))
table.insert(containArray, '| data-sort-value="0"| ' .. Icons.GP(0))
end
end
end
if not asList and not Shared.tableIsEmpty(containArray) then
table.insert(containArray, '|- class="sortbottom"\r\n! colspan="3"| Total\r\n| ' .. Icons.GP(GPTotal) .. '\r\n|}')
end
local delim = (asList and '<br/>' or '\r\n')
return table.concat(containArray, delim)
end
function p.getPurchaseContents(frame)
local args = frame.args ~= nil and frame.args or frame
local purchaseName = args[1]
local asList = (args[2] ~= nil and string.upper(args[2]) == 'TRUE')
local purchase = p.getPurchase(purchaseName)
if purchase == nil then
return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]"
else
return p._getPurchaseContents(purchase, asList)
end
end
function p._getPurchaseBuyLimit(purchase, asList)
if asList == nil then asList = true end
local defaultLimit = (purchase.defaultBuyLimit == 0 and 'Unlimited') or Shared.formatnum(purchase.defaultBuyLimit)
if purchase.buyLimitOverrides == nil or Shared.tableIsEmpty(purchase.buyLimitOverrides) then
-- Same limit for all game modes
return defaultLimit
else
-- The limit varies depending on game mode
local limitTable = {}
local gamemodeHasIcon = { 'melvorF:Hardcore', 'melvorF:Adventure' }
for i, buyLimit in ipairs(purchase.buyLimitOverrides) do
local gamemode = GameData.getEntityByID('gamemodes', buyLimit.gamemodeID)
if gamemode ~= nil then
local gamemodeText = nil
if Shared.contains(gamemodeHasIcon, gamemode.id) then
gamemodeText = Icons.Icon({gamemode.name, notext=(not asList or nil)})
else
gamemodeText = '[[Game Mode#' .. gamemode.name .. '|' .. gamemode.name .. ']]'
end
local limitText = (buyLimit.maximum == 0 and 'Unlimited') or Shared.formatnum(buyLimit.maximum)
table.insert(limitTable, limitText .. (asList and ' for ' or ' ') .. gamemodeText)
end
end
table.insert(limitTable, defaultLimit .. (asList and ' for ' or ' ') .. 'All other game modes')
return table.concat(limitTable, (asList and ' or ' or '<br/>'))
end
end
function p.getPurchaseBuyLimit(frame)
local args = frame.args ~= nil and frame.args or frame
local purchaseName = args[1]
local asList = (args[2] ~= nil and string.upper(args[2]) == 'TRUE')
local purchase = p.getPurchase(purchaseName)
if purchase == nil then
return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]"
else
return p._getPurchaseBuyLimit(purchase, asList)
end
end
-- Accept similar arguments to Icons.Icon
function p._getPurchaseIcon(iconArgs)
local purchase = iconArgs[1]
local purchaseName = p._getPurchaseName(purchase)
local override = purchOverrides[purchaseName]
local purchType = p._getPurchaseType(purchase)
-- Amend iconArgs before passing to Icons.Icon()
iconArgs[1] = ((override ~= nil and override.icon[1]) or purchaseName)
if override ~= nil then
iconArgs['type'] = override.icon[2]
if override.link == nil then
iconArgs['nolink'] = true
end
else
iconArgs['type'] = (purchType == 'Item Bundle' and 'item') or string.lower(purchType)
end
return Icons.Icon(iconArgs)
end
function p.getPurchaseIcon(frame)
local args = frame.args ~= nil and frame.args or frame
local purchaseName = args[1]
local purchase = p.getPurchase(purchaseName)
if purchase == nil then
return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]"
else
args[1] = purchase
return p._getPurchaseIcon(args)
end
end
function p._getPurchaseSortValue(purchase)
local costCurrencies = {'gp', 'slayerCoins', 'raidCoins'}
for j, curr in ipairs(costCurrencies) do
local costAmt = purchase.cost[curr]
if costAmt.type == 'BankSlot' then
return -1
elseif costAmt.type == 'Linear' then
return costAmt.initial
elseif costAmt.type == 'Glove' or costAmt.type == 'Fixed' and costAmt.cost > 0 then
return costAmt.cost
end
end
end
function p._getShopTable(Purchases, options)
local availableColumns = { 'Purchase', 'Type', 'Description', 'Cost', 'Requirements', 'Buy Limit' }
local headerPropsDefault = {
["Purchase"] = 'colspan="2"',
["Cost"] = 'style="min-width:100px"'
}
local usedColumns, purchHeader, sortOrder, headerProps = {}, 'Purchase', nil, {}
-- Process options if specified
if options ~= nil and type(options) == 'table' then
-- Custom columns
if options.columns ~= nil and type(options.columns) == 'table' then
for i, column in ipairs(options.columns) do
if Shared.contains(availableColumns, column) then
table.insert(usedColumns, column)
end
end
end
-- Purchase column header text
if options.purchaseHeader ~= nil and type(options.purchaseHeader) == 'string' then
purchHeader = options.purchaseHeader
end
-- Custom sort order
if options.sortOrder ~= nil and type(options.sortOrder) == 'function' then
sortOrder = options.sortOrder
end
-- Header properties
if options.headerProps ~= nil and type(options.headerProps) == 'table' then
headerProps = options.headerProps
end
end
-- Use default columns if no custom columns specified
if Shared.tableCount(usedColumns) == 0 then
usedColumns = availableColumns
end
if Shared.tableCount(headerProps) == 0 then
headerProps = headerPropsDefault
end
-- Begin output generation
local resultPart = {}
-- Generate header
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '|- class="headerRow-0"')
for i, column in ipairs(usedColumns) do
local prop = headerProps[column]
table.insert(resultPart, '!' .. (prop and prop .. '| ' or ' ') .. (column == 'Purchase' and purchHeader or column))
end
local purchIterator = nil
if sortOrder == nil then
purchIterator = Shared.skpairs
else
table.sort(Purchases, sortOrder)
purchIterator = ipairs
end
for i, purchase in purchIterator(Purchases) do
local purchName = p._getPurchaseName(purchase)
local purchOverride = nil
if purchOverrides ~= nil then
purchOverride = purchOverrides[purchName]
end
local purchType = p._getPurchaseType(purchase)
local iconNoLink = nil
local purchLink = ''
local costString = p.getCostString(purchase.cost, false)
if purchOverride ~= nil then
if purchOverride.link == nil then
iconNoLink = true
else
purchLink = purchOverride.link .. '|'
end
end
if iconNoLink == nil or iconNoLink ~= true then purchName = '[[' .. purchLink .. purchName .. ']]' end
table.insert(resultPart, '|-')
for j, column in ipairs(usedColumns) do
if column == 'Purchase' then
table.insert(resultPart, '|style="min-width:25px"|' .. p._getPurchaseIcon({purchase, notext=true, size='50'}))
--table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({iconName, type=iconType, notext=true, nolink=iconNoLink, size='50'}))
table.insert(resultPart, '| ' .. purchName)
elseif column == 'Type' then
table.insert(resultPart, '| ' .. purchType)
elseif column == 'Description' then
table.insert(resultPart, '| ' .. purchase.customDescription)
elseif column == 'Cost' then
local cellProp = '|style="text-align:right;"'
local sortValue = p._getPurchaseSortValue(purchase)
if sortValue ~= nil then cellProp = cellProp .. ' data-sort-value="' .. sortValue .. '"' end
table.insert(resultPart, cellProp .. '| ' .. costString)
elseif column == 'Requirements' then
table.insert(resultPart, '| ' .. p.getRequirementString(purchase.unlockRequirements))
elseif column == 'Buy Limit' then
local buyLimit = p._getPurchaseBuyLimit(purchase, false)
local sortValue = (tonumber(buyLimit) == nil and -1 or buyLimit)
table.insert(resultPart, '| data-sort-value="' .. sortValue .. '"| ' .. buyLimit)
else
-- Shouldn't be reached, but will prevent the resulting table becoming horribly mis-aligned if it ever happens
table.insert(resultPart, '| ')
end
end
end
table.insert(resultPart, '|}')
return table.concat(resultPart, '\r\n')
end
-- getShopTable parameter definition:
-- columns: Comma separated values indicating which columns are to be included & the order
-- in which they are displayed.
-- Values can be any of: Purchase, Type, Description, Cost, Requirements
-- columnProps: Comma separated values indicating formatting to be applied to each column. Each
-- value must be in the format column:property, e.g. Purchase:colspan="2"
-- sortOrder: A function determining the order in which table items appear
-- purchaseHeader: Specifies header text for the Purchase column if not 'Purchase'
function p.getShopTable(frame)
local cat = frame.args ~= nil and frame.args[1] or frame
local options = {}
if frame.args ~= nil then
if frame.args.columns ~= nil then options.columns = Shared.splitString(frame.args.columns, ',') end
if frame.args.purchaseHeader ~= nil then options.purchaseHeader = frame.args.purchaseHeader end
if frame.args.sortOrder ~= nil then options.sortOrder = frame.args.sortOrder end
if frame.args.columnProps ~= nil then
local columnPropValues = Shared.splitString(frame.args.columnProps, ',')
local columnProps = {}
for i, prop in pairs(columnPropValues) do
local propName, propValue = string.match(prop, '^([^:]+):(.*)$')
if propName ~= nil then
columnProps[propName] = propValue
end
end
if Shared.tableCount(columnProps) > 0 then options.headerProps = columnProps end
end
end
local shopCat = GameData.getEntityByName('shopCategories', cat)
if shopCat == nil then
return 'ERROR: Invalid category '..cat..'[[Category:Pages with script errors]]'
else
local catPurchases = p.getPurchases(function(purch) return purch.category == shopCat.id end)
return p._getShopTable(catPurchases, options)
end
end
function p.getItemCostArray(itemID)
local purchaseArray = {}
for i, purchase in ipairs(GameData.rawData.shopPurchases) do
if purchase.cost ~= nil and purchase.cost.items ~= nil then
for j, itemCost in ipairs(purchase.cost.items) do
if itemCost.id == itemID then
table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = itemCost.quantity })
break
end
end
end
end
return purchaseArray
end
function p.getItemSourceArray(itemID)
local purchaseArray = {}
for i, purchase in ipairs(GameData.rawData.shopPurchases) do
if purchase.contains ~= nil and purchase.contains.items ~= nil then
for j, itemContains in ipairs(purchase.contains.items) do
if itemContains.id == itemID then
table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = itemContains.quantity })
break
end
end
end
end
return purchaseArray
end
function p._getPurchaseTable(purchase)
local result = '{| class="wikitable"\r\n|-'
result = result..'\r\n!colspan="2"|'..Icons.Icon({'Shop'})..' Purchase'
if purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then
result = result..' - '..Icons.Icon({p._getPurchaseName(purchase), type='item'})
end
result = result..'\r\n|-\r\n!style="text-align:right;"|Cost'
result = result..'\r\n|'..p.getCostString(purchase.cost, false)
result = result..'\r\n|-\r\n!style="text-align:right;"|Requirements'
result = result..'\r\n|'..p.getRequirementString(purchase.unlockRequirements)
result = result..'\r\n|-\r\n!style="text-align:right;"|Contains'
result = result..'\r\n|style="text-align:right;"|'..p._getPurchaseContents(purchase, true)
result = result..'\r\n|}'
return result
end
function p._getItemShopTable(item)
local tableArray = {}
local purchaseArray = p.getItemSourceArray(item.id)
for i, purchase in ipairs(purchaseArray) do
table.insert(tableArray, p._getPurchaseTable(purchase))
end
return table.concat(tableArray, '\r\n\r\n')
end
function p.getItemShopTable(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = Items.getItem(itemName)
if item == nil then
return "ERROR: No item named "..itemName.." exists in the data module"
end
return p._getItemShopTable(item)
end
function p.getShopMiscUpgradeTable()
local purchList = p.getPurchases(function(purch) return purch.category == 'melvorD:General' and string.find(purch.id, '^melvorD:Auto_Eat') == nil end)
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' }, purchaseHeader = 'Upgrade' })
end
function p.getShopSkillcapeTable()
local capeList = p.getPurchases(function(purch) return purch.category == 'melvorD:Skillcapes' end)
local sortOrderFunc = function(a, b)
if a.cost.gp == b.cost.gp then
return p._getPurchaseName(a) < p._getPurchaseName(b)
else
return a.cost.gp < b.cost.gp
end
end
return p._getShopTable(capeList, {
columns = { 'Purchase', 'Description', 'Cost' },
purchaseHeader = 'Cape',
sortOrder = sortOrderFunc,
headerProps = {["Purchase"] = 'colspan="2" style="width:200px;"', ["Cost"] = 'style=width:120px;'}
})
end
function p.getAutoEatTable()
local resultPart = {}
local purchasesAE = p.getPurchases(function(purch) return purch.category == 'melvorD:General' and string.find(purch.id, '^melvorD:Auto_Eat') ~= nil end)
-- Table header
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '|- class="headerRow-0"')
table.insert(resultPart, '!colspan="2"|Auto Eat Tier!!Minimum Threshold!!Efficiency!!Max Healing!!Cost')
-- Rows for each Auto Eat tier
local mods = {["increasedAutoEatEfficiency"] = 0, ["increasedAutoEatHPLimit"] = 0, ["increasedAutoEatThreshold"] = 0}
for i, purchase in ipairs(purchasesAE) do
local purchaseName = p._getPurchaseName(purchase)
-- Modifiers must be accumulated as we go
for modName, modValue in pairs(mods) do
if purchase.contains.modifiers[modName] ~= nil then
mods[modName] = mods[modName] + purchase.contains.modifiers[modName]
end
end
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. purchaseName .. '"| ' .. Icons.Icon({purchaseName, type='upgrade', size=50, notext=true}))
table.insert(resultPart, '| ' .. Icons.Icon({purchaseName, type='upgrade', noicon=true}))
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatThreshold .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatThreshold, 0, 0)) .. '%')
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatEfficiency .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatEfficiency, 0, 0)) .. '%')
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. mods.increasedAutoEatHPLimit .. '" | ' .. Shared.formatnum(Shared.round(mods.increasedAutoEatHPLimit, 0, 0)) .. '%')
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. p._getPurchaseSortValue(purchase) .. '" | ' .. p.getCostString(purchase.cost, false))
end
table.insert(resultPart, '|}')
return table.concat(resultPart, '\r\n')
end
function p.getGodUpgradeTable()
local resultPart = {}
-- Obtain list of God upgrades: look for skill upgrades which have a dungeon completion
-- requirement for an area whose name ends with 'God Dungeon'
local getGodDungeon =
function(reqs)
for i, req in ipairs(reqs) do
if req.type == 'DungeonCompletion' and string.find(req.dungeonID, 'God_Dungeon$') ~= nil then
return GameData.getEntityByID('dungeons', req.dungeonID)
end
end
end
local upgradeList = p.getPurchases(
function(purch)
if purch.category == 'melvorD:SkillUpgrades' and purch.purchaseRequirements ~= nil then
return getGodDungeon(purch.purchaseRequirements) ~= nil
end
return false
end)
if Shared.tableIsEmpty(upgradeList) then
return ''
end
-- Table header
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '|- class="headerRow-0"')
table.insert(resultPart, '!colspan="2"|God Upgrade!!Effect!!Dungeon!!Cost')
-- Rows for each God upgrade
for i, upgrade in ipairs(upgradeList) do
local upgradeName = p._getPurchaseName(upgrade)
local dung = getGodDungeon(upgrade.purchaseRequirements)
local costSortValue = p._getPurchaseSortValue(upgrade)
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. upgradeName .. '"| ' .. Icons.Icon({upgradeName, type='upgrade', size=50, notext=true}))
table.insert(resultPart, '| ' .. Icons.Icon({upgradeName, type='upgrade', noicon=true}))
table.insert(resultPart, '| ' .. upgrade.customDescription)
table.insert(resultPart, '| data-sort-value="' .. dung.name .. '"| ' .. Icons.Icon({dung.name, type='dungeon'}))
table.insert(resultPart, '| style="text-align:right;" data-sort-value="' .. costSortValue .. '"| ' .. p.getCostString(upgrade.cost, false))
end
table.insert(resultPart, '|}')
return table.concat(resultPart, '\r\n')
end
function p.getCookingUtilityTable(frame)
local category = nil
if frame ~= nil then category = frame.args ~= nil and frame.args[1] or frame end
local validCategories = {'Cooking Fire', 'Furnace', 'Pot'}
if category == nil or not Shared.contains({'Cooking Fire', 'Furnace', 'Pot'}, category) then
return 'ERROR: Invalid category specified. Must be one of the following: ' .. mw.text.listToText(validCategories, ', ', ' or ')
end
local categoryShort = string.match(category, '[^%s]+$')
local bonusSkillID = Constants.getSkillID('Cooking')
local bonusColMod, bonusColName = nil, nil
if category == 'Cooking Fire' then
bonusColMod = 'increasedSkillXP'
bonusColName = 'Bonus ' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' XP'
else
bonusColMod = 'increasedChanceToDoubleItemsSkill'
bonusColName = 'Double Items Chance'
end
local modsPerfectChance = {'increasedChancePerfectCookFire', 'increasedChancePerfectCookFurnace',
'increasedChancePerfectCookPot', 'increasedChancePerfectCookGlobal'}
local totalBonusVal, totalPerfectChance = 0, 0
local utilityList = p.getPurchases(function(purch) return purch.category == 'melvorD:SkillUpgrades' and string.find(p._getPurchaseName(purch), category .. '$') ~= nil end)
local resultPart = {}
-- Table header
table.insert(resultPart, '{| class="wikitable stickyHeader"')
table.insert(resultPart, '|- class="headerRow-0"')
table.insert(resultPart, '!colspan="4"| !!colspan="2"|' .. bonusColName .. '!!colspan="2"|Bonus Perfect Chance')
table.insert(resultPart, '|- class="headerRow-1"')
table.insert(resultPart, '!colspan="2"|Name!!Level!!Cost' .. string.rep('!!This ' .. categoryShort .. '!!Total', 2))
-- Row for each upgrade
for i, utility in ipairs(utilityList) do
local utilityName = p._getPurchaseName(utility)
-- First determine bonus XP/doubling chance and perfect chance
local bonusVal, perfectChance = 0, 0
if type(utility.contains) == 'table' then
if type(utility.contains.modifiers) == 'table' then
for modName, modVal in pairs(utility.contains.modifiers) do
if modName == bonusColMod and type(modVal) == 'table' then
-- Bonus XP/doubling
for skID, skVal in pairs(modVal) do
if skVal[1] == bonusSkillID then bonusVal = bonusVal + skVal[2] end
end
elseif Shared.contains(modsPerfectChance, modName) then
-- Perfect chance
perfectChance = perfectChance + modVal
end
end
end
end
totalBonusVal = totalBonusVal + bonusVal
totalPerfectChance = totalPerfectChance + perfectChance
table.insert(resultPart, '|-')
table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({utilityName, type='upgrade', size='50', notext=true}))
table.insert(resultPart, '|' .. utilityName)
table.insert(resultPart, '|style="text-align:right"|' .. p.getRequirementString(utility.purchaseRequirements))
table.insert(resultPart, '|style="text-align:right"|' .. p.getCostString(utility.cost, false))
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. bonusVal .. '%')
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. totalBonusVal .. '%')
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. perfectChance .. '%')
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. totalPerfectChance .. '%')
end
table.insert(resultPart, '|}')
return table.concat(resultPart, '\r\n')
end
return p