12,714
edits
(_getShopTable: Overhaul to allow for greater control over output format; getShopSkillcapeTable: Use _getShopTable; getShopMiscUpgradeTable: Initial implementation) |
(getShopMiscUpgradeTable: Include requirements column) |
||
(10 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
local ShopData = mw.loadData('Module:Shop/data') | local ShopData = mw.loadData('Module:Shop/data') | ||
-- Data instead of Module:CombatAreas to avoid loop whne 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 9: | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local Constants = require('Module:Constants') | local Constants = require('Module:Constants') | ||
local | |||
-- 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 } | |||
} | |||
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) | ||
Line 16: | Line 42: | ||
end | end | ||
function p.getCostString(cost) | 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) | |||
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 | |||
function p.getCostString(cost, inline) | |||
local displayInline = (inline ~= nil and inline or false) | |||
local costArray = {} | local costArray = {} | ||
if cost.gp ~= nil and cost.gp > 0 then | if cost.gp ~= nil and cost.gp > 0 then | ||
Line 31: | Line 97: | ||
for i, itemCost in Shared.skpairs(cost.items) do | for i, itemCost in Shared.skpairs(cost.items) do | ||
local item = Items.getItemByID(itemCost[1]) | local item = Items.getItemByID(itemCost[1]) | ||
table.insert(itemArray, Icons.Icon({item.name, type="item", notext=true, qty=itemCost[2]})) | table.insert(itemArray, Icons.Icon({item.name, type="item", notext=(not displayInline and true or nil), qty=itemCost[2]})) | ||
end | end | ||
Line 39: | Line 105: | ||
end | end | ||
return | local sep, lastSep = '<br/>', nil | ||
if displayInline then | |||
sep = ', ' | |||
lastSep = Shared.tableCount(costArray) > 2 and ', and ' or ' and ' | |||
end | |||
return Shared.joinList(costArray, sep, lastSep) | |||
end | end | ||
Line 57: | Line 128: | ||
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 = | 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 | ||
Line 90: | Line 161: | ||
return table.concat(reqArray, '<br/>') | return table.concat(reqArray, '<br/>') | ||
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 = {} | |||
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') | |||
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 | |||
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])) | |||
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({item.name, type='item', noicon=true}) .. ' Charges\r\n| data-sort-value="' .. purchase.charges .. '" style="text-align:right" | ' .. Shared.formatnum(purchase.charges)) | |||
end | |||
end | |||
if not asList and Shared.tableCount(containArray) > 0 then table.insert(containArray, '|}') 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 args[2] == 'true') or false) | |||
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 | |||
-- 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 | end | ||
Line 106: | Line 269: | ||
local headerPropsDefault = { | local headerPropsDefault = { | ||
["Purchase"] = 'colspan="2"', | ["Purchase"] = 'colspan="2"', | ||
["Cost"] = 'style="min-width: | ["Cost"] = 'style="min-width:100px"' | ||
} | } | ||
local usedColumns, purchHeader, sortOrder, headerProps = {}, 'Purchase', nil, {} | local usedColumns, purchHeader, sortOrder, headerProps = {}, 'Purchase', nil, {} | ||
Line 141: | Line 304: | ||
end | end | ||
-- Begin output generation | -- Begin output generation | ||
local resultPart = {} | local resultPart = {} | ||
Line 178: | Line 327: | ||
end | end | ||
local purchType = | local purchType = p._getPurchaseType(purchase) | ||
local iconNoLink = nil | local iconNoLink = nil | ||
local purchLink = '' | local purchLink = '' | ||
local costString = p.getCostString(purchase.cost) | local costString = p.getCostString(purchase.cost, false) | ||
if purchOverride ~= nil then | if purchOverride ~= nil then | ||
if purchOverride.link == nil then | if purchOverride.link == nil then | ||
iconNoLink = true | iconNoLink = true | ||
Line 211: | Line 346: | ||
for j, column in ipairs(usedColumns) do | for j, column in ipairs(usedColumns) do | ||
if column == 'Purchase' then | if column == 'Purchase' then | ||
table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({iconName, type=iconType, notext=true, nolink=iconNoLink, size='50'})) | 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) | table.insert(resultPart, '| ' .. purchName) | ||
elseif column == 'Type' then | elseif column == 'Type' then | ||
Line 235: | Line 371: | ||
end | 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 | ||
Line 242: | Line 386: | ||
if frame.args.purchaseHeader ~= nil then options.purchaseHeader = frame.args.purchaseHeader 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.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 | end | ||
local shopCat = ShopData.Shop[cat] | local shopCat = ShopData.Shop[cat] | ||
Line 313: | Line 468: | ||
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' | ||
Line 319: | Line 474: | ||
result = result..'\r\n|-\r\n!style="text-align:right;"|Contains' | 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|style="text-align:right;"|'.. | |||
result = result..'\r\n|}' | result = result..'\r\n|}' | ||
Line 358: | Line 503: | ||
function p.getShopMiscUpgradeTable() | function p.getShopMiscUpgradeTable() | ||
local purchList = p.getPurchases(function(cat, purch) return cat == 'General' and string.find(purch.name, '^Auto Eat') == nil end) | 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' }, purchaseHeader = 'Upgrade' }) | return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' }, purchaseHeader = 'Upgrade' }) | ||
end | end | ||
Line 398: | Line 543: | ||
local costAmt = p._getPurchaseSortValue(purchase) | 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, '|-\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, '| | 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.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)) .. '%') | ||
Line 407: | Line 552: | ||
return table.concat(resultPart, '\r\n') | 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 | |||
-- Mangle unlockRequirements so that it only includes skillLevels | |||
local unlockReqs = {} | |||
if type(utility.unlockRequirements) == 'table' then | |||
unlockReqs['skillLevel'] = utility.unlockRequirements.skillLevel | |||
end | |||
table.insert(resultPart, '|-') | |||
table.insert(resultPart, '|style="min-width:25px"|' .. Icons.Icon({utility.name, type='upgrade', size='50', notext=true})) | |||
table.insert(resultPart, '|' .. utility.name) | |||
table.insert(resultPart, '|style="text-align:right"|' .. p.getRequirementString(unlockReqs)) | |||
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 | end | ||
return p | return p |