Anonymous

Module:Shop: Difference between revisions

From Melvor Idle
19,025 bytes added ,  18 April 2022
m
Fix typo
(getShopSkillcapeTable: Move from Module:Items & enforce min-width for icon column)
m (Fix typo)
(27 intermediate revisions by 2 users not shown)
Line 2: Line 2:


local ShopData = mw.loadData('Module:Shop/data')
local ShopData = mw.loadData('Module:Shop/data')
local ConstantData = mw.loadData('Module:Constants/data')
-- Data instead of Module:CombatAreas to avoid loop when that module attempts to require Module:Shop
local AreaData = require('Module:CombatAreas/data')


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
Line 7: Line 10:
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local Areas = require('Module:CombatAreas')
 
-- Overrides for various items, mostly relating to icon overrides
local purchOverrides = {
["Extra Bank Slot"] = { icon = {'Bank Slot', 'upgrade'}, link = 'Bank Slot', cost = Icons.Icon({'Coins', size = 25, notext = true}) .. ' <span style="font-size:127%; font-family: MathJax_Math; font-style: italic;">C<sub>b</sub></span>*' },
-- 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, incCost = true},
["Golbin Crate"] = { icon = {'Golbin Crate', 'upgrade'}, link = nil, incCost = true}
}
 
function p.getPurchase(purchaseName)
for categoryName, categoryData in pairs(ShopData.Shop) do
for i, purchase in ipairs(categoryData) do
if purchase.name == purchaseName then
return p.processPurchase(categoryName, i - 1)
end
end
end
end


function p.processPurchase(category, purchaseID)
function p.processPurchase(category, purchaseID)
  local purchase = Shared.clone(ShopData.Shop[category][purchaseID + 1])
local purchase = Shared.clone(ShopData.Shop[category][purchaseID + 1])
  purchase.id = purchaseID
purchase.id = purchaseID
  purchase.category = category
purchase.category = category
  return purchase
return purchase
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(cat, purch) return purch.name == purchaseName end)
else
purchaseList = {p.getPurchase(purchaseName)}
end
 
if Shared.tableCount(purchaseList) == 0 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
end


function p.getCostString(cost)
function p.getCostString(cost, inline)
  local costArray = {}
local displayInline = (inline ~= nil and inline or false)
  if cost.gp ~= nil and cost.gp > 0 then
local costArray = {}
    table.insert(costArray, Icons.GP(cost.gp))
if cost.gp ~= nil and cost.gp > 0 then
  end
table.insert(costArray, Icons.GP(cost.gp))
  if cost.slayerCoins ~= nil and cost.slayerCoins > 0 then
end
    table.insert(costArray, Icons.SC(cost.slayerCoins))
if cost.slayerCoins ~= nil and cost.slayerCoins > 0 then
  end
table.insert(costArray, Icons.SC(cost.slayerCoins))
  if cost.raidCoins ~= nil and cost.raidCoins > 0 then
end
    table.insert(costArray, Icons.RC(cost.raidCoins))
if cost.raidCoins ~= nil and cost.raidCoins > 0 then
  end
table.insert(costArray, Icons.RC(cost.raidCoins))
  local itemArray = {}
end
  if cost.items ~= nil then
local itemArray = {}
    for i, itemCost in Shared.skpairs(cost.items) do
if cost.items ~= nil then
      local item = Items.getItemByID(itemCost[1])
for i, itemCost in Shared.skpairs(cost.items) do
      table.insert(itemArray, Icons.Icon({item.name, type="item", notext=true, qty=itemCost[2]}))
local item = Items.getItemByID(itemCost[1])
    end
table.insert(itemArray, Icons.Icon({item.name, type="item", notext=(not displayInline and true or nil), qty=itemCost[2]}))
end


    if Shared.tableCount(itemArray) > 0 then
if Shared.tableCount(itemArray) > 0 then
      table.insert(costArray, table.concat(itemArray, ", "))
table.insert(costArray, table.concat(itemArray, ", "))
    end
end
  end
end


  return table.concat(costArray, "<br/>")
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.getRequirementString(reqs)
function p.getRequirementString(reqs)
  if reqs == nil or Shared.tableCount(reqs) == 0 then
if reqs == nil or Shared.tableCount(reqs) == 0 then
    return "None"
return "None"
  end
end


  local reqArray = {}
local reqArray = {}
  if reqs.slayerTaskCompletion ~= nil then
if reqs.slayerTaskCompletion ~= nil then
    for i, taskReq in Shared.skpairs(reqs.slayerTaskCompletion) do
for i, taskReq in Shared.skpairs(reqs.slayerTaskCompletion) do
      local tierName = Constants.getSlayerTierName(taskReq[1])
local tierName = Constants.getSlayerTierName(taskReq[1])
      table.insert(reqArray, 'Complete '..taskReq[2]..' '..tierName..' Slayer Tasks')
table.insert(reqArray, 'Complete '..taskReq[2]..' '..tierName..' Slayer Tasks')
    end
end
  end
end


  if reqs.dungeonCompletion ~= nil then
if reqs.dungeonCompletion ~= nil then
    for i, dungReq in Shared.skpairs(reqs.dungeonCompletion) do
for i, dungReq in Shared.skpairs(reqs.dungeonCompletion) do
      local dung = Areas.getAreaByID('dungeon', dungReq[1])
local dung = AreaData['dungeons'][dungReq[1] + 1]
      local dungStr = 'Complete '..Icons.Icon({dung.name, type='dungeon'})
local dungStr = 'Complete '..Icons.Icon({dung.name, type='dungeon'})
      if dungReq[2] > 1 then
if dungReq[2] > 1 then
        dungStr = dungStr..' '..dungReq[2]..' times'
dungStr = dungStr..' '..dungReq[2]..' times'
      end
end
      table.insert(reqArray, dungStr)
table.insert(reqArray, dungStr)
    end
end
  end
end


  if reqs.skillLevel ~= nil then
if reqs.skillLevel ~= nil then
    for i, skillReq in Shared.skpairs(reqs.skillLevel) do
for i, skillReq in Shared.skpairs(reqs.skillLevel) do
      local skillName = Constants.getSkillName(skillReq[1])
local skillName = Constants.getSkillName(skillReq[1])
      table.insert(reqArray, Icons._SkillReq(skillName, skillReq[2]))
table.insert(reqArray, Icons._SkillReq(skillName, skillReq[2]))
    end
end
  end
end


  if reqs.shopItemPurchased ~= nil then
if reqs.shopItemPurchased ~= nil then
    for i, shopReq in Shared.skpairs(reqs.shopItemPurchased) do
for i, shopReq in Shared.skpairs(reqs.shopItemPurchased) do
      local purchase = ShopData.Shop[shopReq[1]][shopReq[2] + 1]
local purchase = ShopData.Shop[shopReq[1]][shopReq[2] + 1]
      local isUpgrade = purchase.contains.items == nil or Shared.tableCount(purchase.contains.items) == 0
local isUpgrade = purchase.contains.items == nil or Shared.tableCount(purchase.contains.items) == 0
      table.insert(reqArray, Icons.Icon({purchase.name, type=(isUpgrade and 'upgrade' or 'item')})..' Purchased')
table.insert(reqArray, Icons.Icon({purchase.name, type=(isUpgrade and 'upgrade' or 'item')})..' Purchased')
    end
end
  end
end


  if reqs.completionPercentage ~= nil then
if reqs.completionPercentage ~= nil then
    table.insert(reqArray, tostring(reqs.completionPercentage) .. '% Completion Log')
table.insert(reqArray, tostring(reqs.completionPercentage) .. '% Completion Log')
  end
end


  if reqs.text ~= nil then
if reqs.text ~= nil then
    table.insert(reqArray, reqs.text)
table.insert(reqArray, reqs.text)
  end
end


  return table.concat(reqArray, '<br/>')
return table.concat(reqArray, '<br/>')
end
end


function p._getShopTable(Purchases)
function p._getPurchaseType(purchase)
  local result = '{| class="wikitable sortable stickyHeader"'
if purchase.contains == nil then
  result = result..'\r\n|- class="headerRow-0"'
return 'Unknown'
  result = result..'\r\n!colspan="2"|Purchase!!Type!!Description!!style="min-width:90px"|Cost!!Requirements'
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


  local costCurrencies = {'gp', 'slayerCoins', 'raidCoins'}
function p._getPurchaseContents(purchase, asList)
  for i, purchase in Shared.skpairs(Purchases) do
if asList == nil then asList = true end
    result = result..'\r\n|-\r\n|'
local containArray = {}
local GPTotal = 0
if purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 0 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 Shared.skpairs(purchase.contains.items) do
local item = Items.getItemByID(itemLine[1])
if asList then
table.insert(containArray, Icons.Icon({item.name, type='item', qty=itemLine[2]}))
else
local GPVal = item.sellsFor * itemLine[2]
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="' .. itemLine[2] .. '" style="text-align:right" | ' .. Shared.formatnum(itemLine[2]))
table.insert(containArray, '| data-sort-value="' .. GPVal .. '"| ' .. Icons.GP(GPVal))
end
end
end
if purchase.charges ~= nil and purchase.charges > 0 then
if asList then
table.insert(containArray, '+'..purchase.charges..' '..Icons.Icon({purchase.name, type='item'})..' Charges')
else
table.insert(containArray, '|-\r\n| style="min-width:25px"| ' .. Icons.Icon({purchase.name, type='item', notext=true, size='25'}))
table.insert(containArray, '| ' .. Icons.Icon({purchase.name, type='item', noicon=true}) .. ' Charges\r\n| data-sort-value="' .. purchase.charges .. '" style="text-align:right" | ' .. Shared.formatnum(purchase.charges))
table.insert(containArray, '| data-sort-value="0"| ' .. Icons.GP(0))
end
end
if not asList and Shared.tableCount(containArray) > 0 then
table.insert(containArray, '|- class="sortbottom"\r\n! colspan="3"| Total\r\n| ' .. Icons.GP(GPTotal) .. '\r\n|}')
end


    local purchType = 'Item'
local delim = (asList and '<br/>' or '\r\n')
    if purchase.contains.pet ~= nil then
return table.concat(containArray, delim)
      purchType = 'Pet'
end
    elseif purchase.contains.modifiers ~= nil or purchase.contains.items == nil or Shared.tableCount(purchase.contains.items) == 0 then
      purchType = 'Upgrade'
    elseif purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then
      purchType = 'Item Bundle'
    end


    -- Translate certain media strings to icons, mostly for Golbin Raid shop objects
function p.getPurchaseContents(frame)
    local iconOverrides = {
local args = frame.args ~= nil and frame.args or frame
      ["assets/media/main/logo.svg"] = { icon = 'Melvor Logo', type = nil, noLink = true },
local purchaseName = args[1]
      ["assets/media/skills/prayer/prayer.svg"] = { icon = 'Prayer', type = 'skill', noLink = true }
local asList = (args[2] ~= nil and string.upper(args[2]) == 'TRUE')
    }
local purchase = p.getPurchase(purchaseName)
    local iconName, iconType, iconNoLink = purchase.name, (purchType == 'Item Bundle' and 'item' or string.lower(purchType)), nil
    if iconOverrides[purchase.media] ~= nil then
      iconName, iconType, iconNoLink = iconOverrides[purchase.media].icon, iconOverrides[purchase.media].type, iconOverrides[purchase.media].noLink
    end


    local purchName = purchase.name
if purchase == nil then
    if iconNoLink == nil or iconNoLink ~= true then purchName = '[[' .. purchName .. ']]' end
return "ERROR: Couldn't find purchase with name '" .. purchaseName .. "'[[Category:Pages with script errors]]"
    result = result..'style="min-width:25px"|'..Icons.Icon({iconName, type=iconType, notext=true, nolink=iconNoLink, size='50'})
else
    result = result..'||'..purchName..'||'..purchType
return p._getPurchaseContents(purchase, asList)
    result = result..'||'..purchase.description..'||style="text-align:right;"'
end
end


    for j, curr in Shared.skpairs(costCurrencies) do
function p._getPurchaseBuyLimit(purchase, asList)
      local costAmt = purchase.cost[curr]
if asList == nil then asList = true end
      if costAmt ~= nil and costAmt > 0 then
if type(purchase.buyLimit) == 'table' then
        result = result..' data-sort-value="'..costAmt..'"'
local limitTable = {}
        break
local gamemodeHasIcon = { 1, 2 }
      end
-- Populate limitTable for each game mode to be included
    end
for id, modeName in pairs(ConstantData.gamemode) do
    result = result..'|'..p.getCostString(purchase.cost)..'||'..p.getRequirementString(purchase.unlockRequirements)
if tonumber(id) ~= nil and string.upper(modeName) ~= 'CHAOS' then
  end
local buyLimit = tostring(purchase.buyLimit[id + 1])
if limitTable[buyLimit] == nil then
limitTable[buyLimit] = {}
end
local gamemodeText = '[[Game Mode#' .. modeName .. '|' .. modeName .. ']]'
if Shared.contains(gamemodeHasIcon, id) then
gamemodeText = Icons.Icon({modeName, notext=(not asList or nil)})
end
table.insert(limitTable[buyLimit], gamemodeText)
end
end
local numLimits = Shared.tableCount(limitTable)
local resultPart = {}
for buyLimit, gameModes in Shared.skpairs(limitTable, true) do
local limitText = (buyLimit == '0' and 'Unlimited' or tostring(buyLimit))
if numLimits == 1 then
-- Buy limit is the same for all game modes
return limitText
else
table.insert(resultPart, limitText .. (asList and ' for ' or ' ') .. mw.text.listToText(gameModes, ', ', (asList and ' and ' or ', ')))
end
end
return table.concat(resultPart, (asList and ' or ' or '<br/>'))
end
end


  result = result..'\r\n|}'
function p.getPurchaseBuyLimit(frame)
  return result
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
end


-- Accept similar arguments to Icons.Icon
function p._getPurchaseIcon(iconArgs)
local purchase = iconArgs[1]
local override = purchOverrides[purchase.name]
local purchType = p._getPurchaseType(purchase)
-- Amend iconArgs before passing to Icons.Icon()
iconArgs[1] = ((override ~= nil and override.icon[1]) or purchase.name)
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 ~= nil and costAmt > 0 then
return costAmt
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 purchOverride = nil
if purchOverrides ~= nil then
purchOverride = purchOverrides[purchase.name]
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
if purchOverride.cost ~= nil then costString = purchOverride.cost end
if purchOverride.incCost then
costString = costString .. '<br/>+' .. costString .. ' for each purchase'
end
end
local purchName = purchase.name
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.description)
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)
function p.getShopTable(frame)
  local cat = frame.args ~= nil and frame.args[1] or frame
local cat = frame.args ~= nil and frame.args[1] or frame
  local shopCat = ShopData.Shop[cat]
local options = {}
  if shopCat == nil then
if frame.args ~= nil then
    return 'ERROR: Invalid category '..cat..'[[Category:Pages with script errors]]'
if frame.args.columns ~= nil then options.columns = Shared.splitString(frame.args.columns, ',') end
  else
if frame.args.purchaseHeader ~= nil then options.purchaseHeader = frame.args.purchaseHeader end
    return p._getShopTable(shopCat)
if frame.args.sortOrder ~= nil then options.sortOrder = frame.args.sortOrder end
  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 = ShopData.Shop[cat]
if shopCat == nil then
return 'ERROR: Invalid category '..cat..'[[Category:Pages with script errors]]'
else
return p._getShopTable(shopCat, options)
end
end
end


function p.getItemCostArray(itemID)
function p.getItemCostArray(itemID)
  local purchaseArray = {}
local purchaseArray = {}


  for catName, cat in Shared.skpairs(ShopData.Shop) do
for catName, cat in Shared.skpairs(ShopData.Shop) do
    for j, purchase in Shared.skpairs(cat) do
for j, purchase in Shared.skpairs(cat) do
      if purchase.cost.items ~= nil then
if purchase.cost.items ~= nil then
        for k, costLine in Shared.skpairs(purchase.cost.items) do
for k, costLine in Shared.skpairs(purchase.cost.items) do
          if costLine[1] == itemID then
if costLine[1] == itemID then
            local temp = p.processPurchase(catName, j - 1)
local temp = p.processPurchase(catName, j - 1)
            temp.qty = costLine[2]
temp.qty = costLine[2]
            table.insert(purchaseArray, temp)
table.insert(purchaseArray, temp)
            break
break
          end
end
        end
end
      end
end
    end
end
  end
end


  return purchaseArray
return purchaseArray
end
end


function p.getItemSourceArray(itemID)
function p.getItemSourceArray(itemID)
  local purchaseArray = {}
local purchaseArray = {}


  for catName, cat in Shared.skpairs(ShopData.Shop) do
for catName, cat in Shared.skpairs(ShopData.Shop) do
    for j, purchase in Shared.skpairs(cat) do
for j, purchase in Shared.skpairs(cat) do
      if purchase.contains.items ~= nil and purchase.contains.items ~= nil then
if purchase.contains.items ~= nil and purchase.contains.items ~= nil then
        for k, containsLine in Shared.skpairs(purchase.contains.items) do
for k, containsLine in Shared.skpairs(purchase.contains.items) do
          if containsLine [1] == itemID then
if containsLine [1] == itemID then
            local temp = p.processPurchase(catName, j - 1)
local temp = p.processPurchase(catName, j - 1)
            temp.qty = containsLine[2]
temp.qty = containsLine[2]
            table.insert(purchaseArray, temp)
table.insert(purchaseArray, temp)
            break
break
          end
end
        end
end
      end
end
    end
end
  end
end


  return purchaseArray
return purchaseArray
end
end


function p.getPurchases(checkFunc)
function p.getPurchases(checkFunc)
  local purchaseList = {}
local purchaseList = {}
  for category, purchaseArray in Shared.skpairs(ShopData.Shop) do
for category, purchaseArray in Shared.skpairs(ShopData.Shop) do
    for i, purchase in Shared.skpairs(purchaseArray) do
for i, purchase in Shared.skpairs(purchaseArray) do
      if checkFunc(purchase) then
if checkFunc(category, purchase) then
        table.insert(purchaseList, p.processPurchase(category, i - 1))
table.insert(purchaseList, p.processPurchase(category, i - 1))
      end
end
    end
end
  end
end
  return purchaseList
return purchaseList
end
end


function p._getPurchaseTable(purchase)
function p._getPurchaseTable(purchase)
  local result = '{| class="wikitable"\r\n|-'
local result = '{| class="wikitable"\r\n|-'
  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.name, type='item'})
result = result..' - '..Icons.Icon({purchase.name, type='item'})
  end
end


  result = result..'\r\n|-\r\n!style="text-align:right;"|Cost'
result = result..'\r\n|-\r\n!style="text-align:right;"|Cost'
  result = result..'\r\n|'..p.getCostString(purchase.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|-\r\n!style="text-align:right;"|Requirements'
  result = result..'\r\n|'..p.getRequirementString(purchase.unlockRequirements)
result = result..'\r\n|'..p.getRequirementString(purchase.unlockRequirements)


  result = result..'\r\n|-\r\n!style="text-align:right;"|Contains'
result = result..'\r\n|-\r\n!style="text-align:right;"|Contains'
  local containArray = {}
result = result..'\r\n|style="text-align:right;"|'..p._getPurchaseContents(purchase, true)
  if purchase.contains.items ~= nil then
    for i, itemLine in Shared.skpairs(purchase.contains.items) do
      local item = Items.getItemByID(itemLine[1])
      table.insert(containArray, Icons.Icon({item.name, type='item', qty=itemLine[2]}))
    end
  end
  if purchase.charges ~= nil and purchase.charges > 0 then
    table.insert(containArray, '+'..purchase.charges..' '..Icons.Icon({purchase.name, type='item'})..' Charges')
  end
  result = result..'\r\n|style="text-align:right;"|'..table.concat(containArray, '<br/>')


  result = result..'\r\n|}'
result = result..'\r\n|}'
  return result
return result
end
end


function p._getItemShopTable(item)
function p._getItemShopTable(item)
  local tableArray = {}
local tableArray = {}
  local purchaseArray = p.getItemSourceArray(item.id)
local purchaseArray = p.getItemSourceArray(item.id)


  for i, purchase in Shared.skpairs(purchaseArray) do
for i, purchase in Shared.skpairs(purchaseArray) do
    table.insert(tableArray, p._getPurchaseTable(purchase))
table.insert(tableArray, p._getPurchaseTable(purchase))
  end
end


  return table.concat(tableArray, '\r\n\r\n')
return table.concat(tableArray, '\r\n\r\n')
end
end


function p.getItemShopTable(frame)
function p.getItemShopTable(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
local itemName = frame.args ~= nil and frame.args[1] or frame
  local item = Items.getItem(itemName)
local item = Items.getItem(itemName)
  if item == nil then
if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module"
return "ERROR: No item named "..itemName.." exists in the data module"
  end
end


  return p._getItemShopTable(item)
return p._getItemShopTable(item)
end
 
function p.getShopMiscUpgradeTable()
local purchList = p.getPurchases(function(cat, purch) return cat == 'General' and string.find(purch.name, '^Auto Eat') == nil end)
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' }, purchaseHeader = 'Upgrade' })
end
end


function p.getShopSkillcapeTable()
function p.getShopSkillcapeTable()
   local result = ''
local capeList = p.getPurchases(function(cat, purch) return cat == 'Skillcapes' end)
  local capeList = Items.getItems(function(item) return Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion' end)
local sortOrderFunc = function(a, b)
if a.cost.gp == b.cost.gp then
return a.name < b.name
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(cat, purch) return string.find(purch.name, '^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
-- 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
 
local costAmt = p._getPurchaseSortValue(purchase)
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. purchase.name .. '"| ' .. Icons.Icon({purchase.name, type='upgrade', size=50, notext=true}))
table.insert(resultPart, '| ' .. Icons.Icon({purchase.name, 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="' .. costAmt .. '" | ' .. Icons.GP(costAmt))
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)
if reqs.dungeonCompletion ~= nil then
for i, areaReq in ipairs(reqs.dungeonCompletion) do
local dung = AreaData['dungeons'][areaReq[1] + 1]
if string.find(dung.name, 'God Dungeon$') ~= nil then return dung end
end
end
end
 
local upgradeList = p.getPurchases(
function(cat, purch)
if cat == 'SkillUpgrades' and purch.unlockRequirements ~= nil then
return getGodDungeon(purch.unlockRequirements) ~= nil
end
return false
end)
if Shared.tableCount(upgradeList) == 0 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 dung = getGodDungeon(upgrade.unlockRequirements)
local costSortValue = p._getPurchaseSortValue(upgrade)
table.insert(resultPart, '|-\r\n|style="min-width:25px; text-align:center;" data-sort-value="' .. upgrade.name .. '"| ' .. Icons.Icon({upgrade.name, type='upgrade', size=50, notext=true}))
table.insert(resultPart, '| ' .. Icons.Icon({upgrade.name, type='upgrade', noicon=true}))
table.insert(resultPart, '| ' .. upgrade.description)
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(cat, purch) return cat == 'SkillUpgrades' and string.find(purch.name, 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
-- 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


  result = result..'\r\n{|class="wikitable sortable stickyHeader"'
-- Mangle unlockRequirements so that it only includes skillLevels
  result = result..'\r\n|- class="headerRow-0"'
local unlockReqs = {}
  result = result..'\r\n!colspan="2" style="width:200px"|Cape'
if type(utility.unlockRequirements) == 'table' then
  result = result..'!!Description!!style="width:120px"|Price'
unlockReqs['skillLevel'] = utility.unlockRequirements.skillLevel
end


  --Sort the table by cost and then name
table.insert(resultPart, '|-')
  table.sort(capeList, function(a, b)  
table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({utility.name, type='upgrade', size='50', notext=true}))
                        if a.buysFor == b.buysFor then
table.insert(resultPart, '|' .. utility.name)
                          return a.name < b.name
table.insert(resultPart, '|style="text-align:right"|' .. p.getRequirementString(unlockReqs))
                        else
table.insert(resultPart, '|style="text-align:right"|' .. p.getCostString(utility.cost, false))
                          return a.sellsFor < b.buysFor
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. bonusVal .. '%')
                        end
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. totalBonusVal .. '%')
                      end)
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. perfectChance .. '%')
  for i, thisItem in pairs(capeList) do
table.insert(resultPart, '|style="text-align:right"|' .. '+' .. totalPerfectChance .. '%')
    result = result..'\r\n|-\r\n|style="min-width:25px"|'..Icons.Icon({thisItem.name, type='item', size='50', notext=true})
end
    result = result..'||[['..thisItem.name..']]'
table.insert(resultPart, '|}')
    result = result..'\r\n||'..thisItem.description
    result = result..'||style="text-align:left" data-sort-value="'..thisItem.buysFor..'"'
    result = result..'|'..Icons.GP(thisItem.buysFor)
  end
  result = result..'\r\n|}'


  return result
return table.concat(resultPart, '\r\n')
end
end


return p
return p