Module:Shop: Difference between revisions
From Melvor Idle
(_getPurchaseContents: Ensure GP alignment is left in all cases) |
m (Remove % suffix from sight/survey range) |
||
(79 intermediate revisions by 8 users not shown) | |||
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 Common = require('Module:Common') | |||
local Modifiers = require('Module:Modifiers') | |||
local Items = require('Module:Items') | local Items = require('Module:Items') | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local | local Pets = require('Module:Pets') | ||
local Num = require('Module:Number') | |||
local | |||
function p.getPurchase(purchaseName) | function p.getPurchase(purchaseName) | ||
local purchList = p.getPurchases(function(purch) return Common.getPurchaseName(purch) == purchaseName end) | |||
if purchList ~= nil and not Shared.tableIsEmpty(purchList) then | |||
return purchList[1] | |||
end | end | ||
end | end | ||
function p. | function p.getPurchaseByID(id) | ||
local | return GameData.getEntityByID('shopPurchases', id) | ||
purchase.id = | end | ||
return | -- Accepts a function(purchase, name) and a category. | ||
-- Prevents external modules from having to make GameData and Common calls. | |||
function p.getCategoryPurchases(checkFunc, category) | |||
local shopCat = GameData.getEntityByName('shopCategories', category) | |||
if shopCat == nil then | |||
return Shared.printError('Invalid category ' .. shopCat) | |||
end | |||
-- We make a nested func to resolve the item name first, if required. | |||
local func = | |||
function(purchase) | |||
if purchase.category ~= shopCat.id then | |||
return false | |||
end | |||
local name = Common.getPurchaseName(purchase) | |||
return checkFunc(purchase, name) | |||
end | |||
return GameData.getEntities('shopPurchases', func) | |||
end | |||
function p.getPurchases(checkFunc) | |||
return GameData.getEntities('shopPurchases', checkFunc) | |||
end | end | ||
Line 50: | Line 54: | ||
return p.getCostString(purchase.cost, displayInline) | return p.getCostString(purchase.cost, displayInline) | ||
elseif stat == 'requirements' then | elseif stat == 'requirements' then | ||
return | return Common.getRequirementString(purchase.purchaseRequirements, 'None') | ||
elseif stat == 'contents' then | elseif stat == 'contents' then | ||
return p._getPurchaseContents(purchase, true) | return p._getPurchaseContents(purchase, true) | ||
elseif stat == 'type' then | elseif stat == 'type' then | ||
return p. | return Common.getPurchaseType(purchase) | ||
elseif stat == 'buyLimit' then | |||
return p._getPurchaseBuyLimit(purchase, not displayInline) | |||
elseif stat == 'buyLimitHardcore' then | |||
return p._getPurchaseBuyLimitNumeric(purchase, 'melvorF:Hardcore') | |||
elseif stat == 'description' then | |||
return p._getPurchaseDescription(purchase) | |||
elseif stat =='expansionicon' then | |||
return p._getPurchaseExpansionIcon(purchase) | |||
else | else | ||
return purchase[stat] | return purchase[stat] | ||
Line 62: | Line 74: | ||
function p.getPurchaseStat(frame) | function p.getPurchaseStat(frame) | ||
local args = frame.args ~= nil and frame.args or frame | local args = frame.args ~= nil and frame.args or frame | ||
local purchaseName = args[1] | local purchaseName = Shared.fixPagename(args[1]) | ||
local statName = args[2] | local statName = args[2] | ||
local displayInline = (args['inline'] ~= nil and string.lower(args['inline']) == 'true' or false) | local displayInline = (args['inline'] ~= nil and string.lower(args['inline']) == 'true' or false) | ||
Line 68: | Line 80: | ||
local purchaseList = {} | local purchaseList = {} | ||
if statName == 'cost' then | if statName == 'cost' then | ||
purchaseList = p.getPurchases(function( | purchaseList = p.getPurchases(function(purch) return Common.getPurchaseName(purch) == purchaseName end) | ||
else | else | ||
purchaseList = {p.getPurchase(purchaseName)} | purchaseList = {p.getPurchase(purchaseName)} | ||
end | end | ||
if Shared. | if Shared.tableIsEmpty(purchaseList) then | ||
return " | return Shared.printError("Couldn't find purchase with name '" .. purchaseName .. "'") | ||
else | else | ||
local resultPart = {} | local resultPart = {} | ||
Line 84: | Line 96: | ||
end | end | ||
function p. | function p._getPurchaseExpansionIcon(purch) | ||
if purch.id ~= nil then | |||
return Icons.getExpansionIcon(purch.id) | |||
if | elseif purch.contains ~= nil then | ||
local item = nil | |||
if purch.contains.items ~= nil and not Shared.tableIsEmpty(purch.contains.items) then | |||
return Icons.getExpansionIcon(purch.contains.items[1].id) | |||
elseif purch.contains.itemCharges ~= nil and not Shared.tableIsEmpty(purch.contains.itemCharges) then | |||
return Icons.getExpansionIcon(purch.contains.itemCharges.id) | |||
end | end | ||
if | if purch.contains.petID ~= nil then | ||
return Icons.getExpansionIcon(purch.contains.petID) | |||
end | end | ||
end | end | ||
return '' | |||
end | |||
local | function p._getPurchaseDescription(purch) | ||
if | if purch.customDescription ~= nil then | ||
local templateData = p._getPurchaseTemplateData(purch) | |||
return Shared.applyTemplateData(purch.customDescription, templateData) | |||
elseif purch.contains ~= nil then | |||
local item = nil | |||
if purch.contains.modifiers ~= nil then | |||
return Modifiers.getModifiersText(purch.contains.modifiers, false) | |||
elseif purch.contains.petID ~= nil then | |||
local pet = Pets.getPetByID(purch.contains.petID) | |||
return Pets._getPetEffect(pet) | |||
elseif purch.contains.items ~= nil and Shared.tableCount(purch.contains.items) == 1 then | |||
item = Items.getItemByID(purch.contains.items[1].id) | |||
elseif purch.contains.itemCharges ~= nil then | |||
item = Items.getItemByID(purch.contains.itemCharges.id) | |||
end | |||
if item ~= nil then | |||
if item.customDescription ~= nil then | |||
return item.customDescription | |||
elseif item.modifiers ~= nil then | |||
return Modifiers.getModifiersText(item.modifiers, false) | |||
end | |||
end | |||
end | end | ||
return | return '' | ||
end | end | ||
function p. | function p.getCostString(cost, inline) | ||
local displayInline = (inline ~= nil and inline or false) | |||
local costArray = {} | |||
if cost.currencies ~= nil then | |||
for i, costAmt in ipairs(cost.currencies) do | |||
local | local costStr = p.getCurrencyCostString(costAmt) | ||
if | if costStr ~= nil then | ||
for i, | table.insert(costArray, costStr) | ||
local | end | ||
table.insert( | |||
end | end | ||
end | end | ||
if cost.items ~= nil and not Shared.tableIsEmpty(cost.items) then | |||
if | local itemArray = {} | ||
for i, | for i, itemCost in ipairs(cost.items) do | ||
local | 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 | ||
table.insert( | end | ||
if not Shared.tableIsEmpty(itemArray) then | |||
table.insert(costArray, table.concat(itemArray, ', ')) | |||
end | end | ||
end | end | ||
if | 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 | end | ||
return mw.text.listToText(costArray, sep, lastSep) | |||
end | end | ||
return '' | |||
end | |||
-- Generates description template data. See: shop.js, getDescriptionTemplateData() | |||
function p._getPurchaseTemplateData(purchase) | |||
-- qty is a static value of 1 for Bank slots | |||
local templateData = { qty = 1 } | |||
if purchase.contains ~= nil and purchase.contains.items ~= nil then | |||
for i, itemDef in ipairs(purchase.contains.items) do | |||
templateData['qty' .. i] = itemDef.quantity | |||
end | end | ||
end | end | ||
return templateData | |||
return | |||
end | end | ||
function p. | function p.getCurrencyCostString(cost) | ||
if | if cost.type == 'BankSlot' then | ||
return ' | -- Unusual bit of code that basically evaluates wikitext '<math>C_b</math>*' | ||
elseif | return mw.getCurrentFrame():callParserFunction('#tag:math', {'C_b'}) .. '*' | ||
return ' | elseif cost.type == 'Linear' and (cost.initial > 0 or cost.scaling > 0) then | ||
return Icons._Currency(cost.currency, cost.initial) .. '<br/>+' .. Icons._Currency(cost.currency, cost.scaling) .. ' for each purchase' | |||
elseif cost.type == 'Glove' or cost.type == 'Fixed' and cost.cost > 0 then | |||
elseif | -- Type Glove exists in game so the Merchant's Permit cost reduction can be applied, | ||
-- it makes no difference here | |||
return Icons._Currency(cost.currency, cost.cost) | |||
return | |||
end | end | ||
end | end | ||
Line 184: | Line 205: | ||
local containArray = {} | local containArray = {} | ||
local GPTotal = 0 | local GPTotal = 0 | ||
if purchase.contains.items ~= nil and Shared. | local currency = 'melvorD:GP' | ||
if purchase.contains ~= nil then | |||
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 item.sellsForCurrency ~= nil then | |||
currency = item.sellsForCurrency | |||
end | |||
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| class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true})) | |||
table.insert(containArray, '|data-sort-value="'..item.name..'"|'.. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}) .. '\r\n| data-sort-value="' .. itemQty .. '" style="text-align:right" | ' .. Num.formatnum(itemQty)) | |||
table.insert(containArray, '| data-sort-value="' .. GPVal .. '"| ' .. Icons._Currency(currency, GPVal)) | |||
end | |||
end | |||
end | end | ||
if purchase.contains.itemCharges ~= nil and purchase.contains.itemCharges.quantity > 0 then | |||
local | local gloveItem = Items.getItemByID(purchase.contains.itemCharges.id) | ||
if asList then | local chargeQty = purchase.contains.itemCharges.quantity | ||
if gloveItem ~= nil then | |||
if gloveItem.sellsForCurrency ~= nil then | |||
currency = gloveItem.sellsForCurrency | |||
end | |||
if asList then | |||
table.insert(containArray, ' +'..Num.formatnum(chargeQty)..' '..Icons.Icon({gloveItem.name, type='item'})..' Charges') | |||
else | |||
table.insert(containArray, '|-\r\n| class="table-img"| ' .. Icons.Icon({gloveItem.name, type='item', notext=true})) | |||
table.insert(containArray, '| ' .. Icons.Icon({gloveItem.name, type='item', noicon=true}) .. ' Charges\r\n| data-sort-value="' .. chargeQty .. '" style="text-align:right" | ' .. Num.formatnum(chargeQty)) | |||
table.insert(containArray, '| data-sort-value="0"| ' .. Icons._Currency(currency, 0)) | |||
end | |||
end | end | ||
end | 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._Currency(currency, GPTotal) .. '\r\n|}') | |||
if not asList and Shared. | |||
table.insert(containArray, '|- class="sortbottom"\r\n! colspan="3"| Total\r\n| ' .. Icons. | |||
end | end | ||
Line 222: | Line 257: | ||
function p.getPurchaseContents(frame) | function p.getPurchaseContents(frame) | ||
local args = frame.args ~= nil and frame.args or frame | local args = frame.args ~= nil and frame.args or frame | ||
local purchaseName = args[1] | local purchaseName = Shared.fixPagename(args[1]) | ||
local asList = (args[2] ~= nil and string.upper(args[2]) == 'TRUE') | local asList = (args[2] ~= nil and string.upper(args[2]) == 'TRUE') | ||
local purchase = p.getPurchase(purchaseName) | local purchase = p.getPurchase(purchaseName) | ||
if purchase == nil then | if purchase == nil then | ||
return " | return Shared.printError("Couldn't find purchase with name '" .. purchaseName .. "'") | ||
else | else | ||
return p._getPurchaseContents(purchase, asList) | return p._getPurchaseContents(purchase, asList) | ||
end | end | ||
end | |||
function p._getPurchaseBuyLimitNumeric(purchase, gamemodeID) | |||
local buyLimit = (purchase.defaultBuyLimit > 0 and purchase.defaultBuyLimit) | |||
if not Shared.tableIsEmpty(purchase.buyLimitOverrides) then | |||
local gamemodeLimit = GameData.getEntityByProperty(purchase.buyLimitOverrides, 'gamemodeID', gamemodeID) | |||
if gamemodeLimit ~= nil and gamemodeLimit.maximum ~= nil then | |||
buyLimit = gamemodeLimit.maximum | |||
end | |||
end | |||
return buyLimit | |||
end | end | ||
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 Num.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 limitTable = {} | ||
local gamemodeHasIcon = { | local gamemodeHasIcon = { 'melvorF:Hardcore', 'melvorF:Adventure' } | ||
for i, buyLimit in ipairs(purchase.buyLimitOverrides) do | |||
for | local gamemode = GameData.getEntityByID('gamemodes', buyLimit.gamemodeID) | ||
if gamemode ~= nil then | |||
local gamemodeName = Shared.splitString(gamemode.name, ' ')[1] | |||
local gamemodeText = nil | |||
if Shared.contains(gamemodeHasIcon, gamemode.id) then | |||
gamemodeText = Icons.Icon({gamemodeName, notext=(not asList or nil)}) | |||
local | else | ||
if Shared.contains(gamemodeHasIcon, id) then | gamemodeText = '[[Game Mode#' .. gamemodeName .. '|' .. gamemodeName .. ']]' | ||
gamemodeText = Icons.Icon({ | |||
end | end | ||
local limitText = (buyLimit.maximum == 0 and 'Unlimited') or Num.formatnum(buyLimit.maximum) | |||
table.insert(limitTable, limitText .. (asList and ' for ' or ' ') .. gamemodeText) | |||
table.insert( | |||
end | end | ||
end | end | ||
return table.concat( | table.insert(limitTable, defaultLimit .. (asList and ' for ' or ' ') .. 'All other game modes') | ||
return table.concat(limitTable, (asList and ' or ' or '<br/>')) | |||
end | end | ||
end | end | ||
Line 275: | Line 315: | ||
if purchase == nil then | if purchase == nil then | ||
return " | return Shared.printError("Couldn't find purchase with name '" .. purchaseName .. "'") | ||
else | else | ||
return p._getPurchaseBuyLimit(purchase, asList) | return p._getPurchaseBuyLimit(purchase, asList) | ||
end | end | ||
end | end | ||
function p.getPurchaseIcon(frame) | function p.getPurchaseIcon(frame) | ||
local args = frame.args ~= nil and frame.args or frame | local args = frame.args ~= nil and frame.args or frame | ||
local purchaseName = args[1] | local purchaseName = Shared.fixPagename(args[1]) | ||
local purchase = p.getPurchase(purchaseName) | local purchase = p.getPurchase(purchaseName) | ||
if purchase == nil then | if purchase == nil then | ||
return " | return Shared.printError("Couldn't find purchase with name '" .. tostring(purchaseName) .. "'") | ||
else | else | ||
args[1] = purchase | args[1] = purchase | ||
return | return Common.getPurchaseIcon(args) | ||
end | end | ||
end | end | ||
function p._getPurchaseSortValue(purchase) | function p._getPurchaseSortValue(purchase) | ||
if purchase.cost ~= nil and purchase.cost.currencies ~= nil then | |||
for _, costAmt in ipairs(purchase.cost.currencies) do | |||
-- Find cost for the current currency, if it exists | |||
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 | end | ||
Line 329: | Line 355: | ||
["Cost"] = 'style="min-width:100px"' | ["Cost"] = 'style="min-width:100px"' | ||
} | } | ||
local usedColumns, purchHeader, sortOrder, headerProps = {}, 'Purchase', nil, {} | local usedColumns, purchHeader, sortOrder, headerProps, stickyHeader = {}, 'Purchase', nil, {}, true | ||
-- Process options if specified | -- Process options if specified | ||
Line 352: | Line 378: | ||
if options.headerProps ~= nil and type(options.headerProps) == 'table' then | if options.headerProps ~= nil and type(options.headerProps) == 'table' then | ||
headerProps = options.headerProps | headerProps = options.headerProps | ||
end | |||
-- Sticky header class | |||
if options.stickyHeader ~= nil then | |||
if type(options.stickyHeader) == 'boolean' then | |||
stickyHeader = options.stickyHeader | |||
elseif type(options.stickyHeader) == 'string' and string.lower(options.stickyHeader) == 'false' then | |||
stickyHeader = false | |||
end | |||
end | end | ||
end | end | ||
Line 365: | Line 399: | ||
local resultPart = {} | local resultPart = {} | ||
-- Generate header | -- Generate header | ||
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"') | table.insert(resultPart, '{| class="wikitable sortable' .. (stickyHeader and ' stickyHeader' or '') .. '"') | ||
table.insert(resultPart, '|- class="headerRow-0"') | table.insert(resultPart, '|- class="headerRow-0"') | ||
for i, column in ipairs(usedColumns) do | for i, column in ipairs(usedColumns) do | ||
Line 372: | Line 406: | ||
end | end | ||
if sortOrder == nil then | if sortOrder == nil then | ||
Purchases = GameData.sortByOrderTable(Purchases, GameData.rawData.shopDisplayOrder, true) | |||
else | else | ||
table.sort(Purchases, sortOrder) | table.sort(Purchases, sortOrder) | ||
end | end | ||
for i, purchase in | for i, purchase in ipairs(Purchases) do | ||
local | local purchName = Common.getPurchaseName(purchase) | ||
local purchExpIcon = p._getPurchaseExpansionIcon(purchase) | |||
local purchType = Common.getPurchaseType(purchase) | |||
local | |||
local | |||
local costString = p.getCostString(purchase.cost, false) | local costString = p.getCostString(purchase.cost, false) | ||
table.insert(resultPart, '|-') | table.insert(resultPart, '|-') | ||
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, '| | table.insert(resultPart, '|class="table-img"|' .. Common.getPurchaseIcon({purchase, notext=true})) | ||
table.insert(resultPart, '| data-sort-value="'..purchName..'"|'..purchExpIcon .. Common.getPurchaseIcon({purchase, noicon=true})) | |||
elseif column == 'Type' then | elseif column == 'Type' then | ||
table.insert(resultPart, '| ' .. purchType) | table.insert(resultPart, '| ' .. purchType) | ||
elseif column == 'Description' then | elseif column == 'Description' then | ||
table.insert(resultPart, '| ' .. purchase | table.insert(resultPart, '| ' .. p._getPurchaseDescription(purchase)) | ||
elseif column == 'Cost' then | elseif column == 'Cost' then | ||
local cellProp = '|style="text-align:right;"' | local cellProp = '|style="text-align:right;"' | ||
Line 420: | Line 432: | ||
table.insert(resultPart, cellProp .. '| ' .. costString) | table.insert(resultPart, cellProp .. '| ' .. costString) | ||
elseif column == 'Requirements' then | elseif column == 'Requirements' then | ||
table.insert(resultPart, '| ' .. | table.insert(resultPart, '| ' .. Common.getRequirementString(purchase.purchaseRequirements, 'None')) | ||
elseif column == 'Buy Limit' then | elseif column == 'Buy Limit' then | ||
local buyLimit = p._getPurchaseBuyLimit(purchase, false) | local buyLimit = p._getPurchaseBuyLimit(purchase, false) | ||
Line 433: | Line 445: | ||
table.insert(resultPart, '|}') | table.insert(resultPart, '|}') | ||
return table.concat(resultPart, ' | return table.concat(resultPart, '\n') | ||
end | end | ||
Line 444: | Line 456: | ||
-- sortOrder: A function determining the order in which table items appear | -- sortOrder: A function determining the order in which table items appear | ||
-- purchaseHeader: Specifies header text for the Purchase column if not 'Purchase' | -- purchaseHeader: Specifies header text for the Purchase column if not 'Purchase' | ||
-- stickyHeader: Specifies if the table will have a sticky header or not | |||
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 451: | Line 464: | ||
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.stickyHeader ~= nil then options.stickyHeader = frame.args.stickyHeader end | |||
if frame.args.columnProps ~= nil then | if frame.args.columnProps ~= nil then | ||
local columnPropValues = Shared.splitString(frame.args.columnProps, ',') | local columnPropValues = Shared.splitString(frame.args.columnProps, ',') | ||
Line 463: | Line 477: | ||
end | end | ||
end | end | ||
local shopCat = | local shopCat = GameData.getEntityByName('shopCategories', cat) | ||
if shopCat == nil then | if shopCat == nil then | ||
return ' | return Shared.printError('Invalid category ' .. cat) | ||
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 | ||
Line 473: | Line 488: | ||
function p.getItemCostArray(itemID) | function p.getItemCostArray(itemID) | ||
local purchaseArray = {} | local purchaseArray = {} | ||
for i, purchase in ipairs(GameData.rawData.shopPurchases) do | |||
for | 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 | ||
end | end | ||
end | end | ||
return purchaseArray | return purchaseArray | ||
end | end | ||
Line 494: | Line 503: | ||
function p.getItemSourceArray(itemID) | function p.getItemSourceArray(itemID) | ||
local purchaseArray = {} | local purchaseArray = {} | ||
for i, purchase in ipairs(GameData.rawData.shopPurchases) do | |||
for | if purchase.contains ~= nil then | ||
if purchase.contains.items ~= nil then | |||
for j, itemContains in ipairs(purchase.contains.items) do | |||
for | if itemContains.id == itemID then | ||
if | table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = itemContains.quantity }) | ||
break | break | ||
end | end | ||
end | end | ||
end | |||
if purchase.contains.itemCharges ~= nil and purchase.contains.itemCharges.id == itemID then | |||
table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = 1 }) | |||
end | end | ||
end | end | ||
end | end | ||
return purchaseArray | return purchaseArray | ||
end | end | ||
Line 529: | Line 525: | ||
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..' - '.. | result = result..' - '..p._getPurchaseExpansionIcon(purchase) .. Common.getPurchaseIcon({purchase, type='item'}) | ||
end | end | ||
Line 536: | Line 532: | ||
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|'.. | result = result..'\r\n|'..Common.getRequirementString(purchase.purchaseRequirements, 'None') | ||
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 | result = result..'\r\n|'..p._getPurchaseContents(purchase, true) | ||
result = result..'\r\n|}' | result = result..'\r\n|}' | ||
Line 549: | Line 545: | ||
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.purchase)) | ||
end | end | ||
Line 560: | Line 556: | ||
local item = Items.getItem(itemName) | local item = Items.getItem(itemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named ' .. itemName .. ' exists in the data module') | ||
end | end | ||
Line 567: | Line 563: | ||
function p.getShopMiscUpgradeTable() | function p.getShopMiscUpgradeTable() | ||
local purchList = p.getPurchases(function( | -- All purchases in the general category besides Auto Eat, which is conained within a separate table | ||
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.getShopSkillUpgradeTable() | |||
-- All purchaes in the SkillUpgrades category except tools and any upgrades displayed as | |||
-- tools (e.g. ship upgrades) | |||
local purchList = p.getPurchases( | |||
function(purch) | |||
return purch.category == 'melvorD:SkillUpgrades' | |||
-- Exclude tools, handled by p.getToolTable() | |||
and string.find(purch.id, '_Axe$') == nil | |||
and string.find(purch.id, '_Axe_Coating$') == nil | |||
and string.find(purch.id, '_Pickaxe$') == nil | |||
and string.find(purch.id, '_Pickaxe_Coating$') == nil | |||
and string.find(purch.id, '_Rod$') == nil | |||
and string.find(purch.id, '_Rod_Coating$') == nil | |||
and string.find(purch.id, '_Harvester$') == nil | |||
and string.find(purch.id, 'Fire$') == nil | |||
and string.find(purch.id, 'Furnace$') == nil | |||
and string.find(purch.id, 'Pot$') == nil | |||
and string.find(purch.id, 'Sieve$') == nil | |||
and string.find(purch.id, 'Trowel$') == nil | |||
and string.find(purch.id, 'Brush$') == nil | |||
and string.find(purch.id, 'Shovel$') == nil | |||
and string.find(purch.id, 'ShipUpgrade') == nil | |||
-- Exclude God upgrades, handled by p.getGodUpgradeTable() | |||
and p.getGodUpgradeDungeon(purch) == 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. | function p.getPurchaseDescription(frame) | ||
local | local itemName = frame.args ~= nil and frame.args[1] or frame | ||
local | local purchase = p.getPurchase(itemName) | ||
if purchase == nil then | |||
return '' | |||
else | end | ||
return p._getPurchaseDescription(purchase) | |||
end | |||
function p.isSkillcapePurchase(purch, isSuperior, skillID) | |||
-- Returns true or false depending on whether the purchase is a skillcape or not. | |||
-- If isSuperior is true, then this checks for superior skillcapes, false checks | |||
-- for regular skillcapes, and nil checks for both. | |||
-- If skillID is specified, then the skillcape must also relate to that skill | |||
local checkCategories = (isSuperior == nil and {'melvorTotH:SuperiorSkillcapes', 'melvorD:Skillcapes'}) or (isSuperior and {'melvorTotH:SuperiorSkillcapes'}) or {'melvorD:Skillcapes'} | |||
-- Some skillcapes (such as Archaeology & Cartography) reside outside of the usual categories | |||
local overrideIDs = { | |||
['melvorTotH:SuperiorSkillcapes'] = { | |||
'melvorAoD:Superior_Archaeology_Skillcape', | |||
'melvorAoD:Superior_Cartography_Skillcape', | |||
'melvorAoD:Cape_of_Completion_AoD' | |||
}, | |||
['melvorD:Skillcapes'] = { | |||
'melvorAoD:Archaeology_Skillcape', | |||
'melvorAoD:Cartography_Skillcape', | |||
'melvorItA:Cape_of_Completion_ItA' | |||
} | |||
} | |||
for i, cat in ipairs(checkCategories) do | |||
if purch.category == cat or Shared.contains(overrideIDs[cat], purch.id) then | |||
if skillID == nil then | |||
return true | |||
else | |||
-- Also validate purchase requirements for relevant SkillLevel requirement | |||
local hasReq = false | |||
if type(purch.purchaseRequirements) == 'table' then | |||
for j, req in ipairs(purch.purchaseRequirements) do | |||
if req.type == 'SkillLevel' then | |||
if req.skillID == skillID then | |||
hasReq = true | |||
else | |||
-- The presence of any other skill's requirement indicates | |||
-- this is not a skillcape for skill with ID skillID | |||
return false | |||
end | end | ||
end | |||
end | |||
end | |||
return hasReq | |||
end | |||
end | |||
end | |||
return false | |||
end | |||
function p._getShopSkillcapeTable(showSuperior) | |||
local capeList = p.getPurchases(function(purch) return p.isSkillcapePurchase(purch, showSuperior) end) | |||
local sortOrderFunc = | |||
function(a, b) | |||
local costA, costB = p._getPurchaseSortValue(a), p._getPurchaseSortValue(b) | |||
if costA == costB then | |||
return Common.getPurchaseName(a) < Common.getPurchaseName(b) | |||
else | |||
return costA < costB | |||
end | |||
end | |||
return p._getShopTable(capeList, { | return p._getShopTable(capeList, { | ||
columns = { 'Purchase', 'Description', 'Cost' }, | columns = { 'Purchase', 'Description', 'Cost' }, | ||
purchaseHeader = 'Cape', | purchaseHeader = 'Cape', | ||
sortOrder = sortOrderFunc, | sortOrder = sortOrderFunc, | ||
stickyHeader = false, | |||
headerProps = {["Purchase"] = 'colspan="2" style="width:200px;"', ["Cost"] = 'style=width:120px;'} | headerProps = {["Purchase"] = 'colspan="2" style="width:200px;"', ["Cost"] = 'style=width:120px;'} | ||
}) | }) | ||
end | end | ||
function p. | function p.getShopSkillcapeTable(frame) | ||
local | local capeCategory = frame.args ~= nil and frame.args[1] or frame | ||
local | local showSuperior = string.lower(capeCategory) == 'superior' | ||
return p._getShopSkillcapeTable(showSuperior) | |||
end | |||
function p.getSkillcapeTable(frame) | |||
local skillName = frame.args ~= nil and frame.args[1] or frame | |||
local skillID = Constants.getSkillID(skillName) | |||
if skillID == nil then | |||
return Shared.printError('No such skill "' .. (skillName or 'nil') .. '"') | |||
end | |||
local capeList = p.getPurchases(function(purch) return p.isSkillcapePurchase(purch, nil, skillID) end) | |||
if Shared.tableIsEmpty(capeList) then | |||
return '' | |||
else | |||
capeList = GameData.sortByOrderTable(capeList, GameData.rawData.shopDisplayOrder, true) | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable"\n') | |||
table.insert(resultPart, '!Skillcape!!Name!!Requirements!!Effect') | |||
for i, cape in ipairs(capeList) do | |||
local capeItem = Items.getItemByID(cape.contains.items[1].id) | |||
if capeItem ~= nil then | |||
table.insert(resultPart, '\n|-\n| ' .. Icons.Icon({capeItem.name, type='item', notext=true})) | |||
table.insert(resultPart, '\n| data-sort-value="'..capeItem.name..'"|'..Icons.getExpansionIcon(capeItem.id) .. Icons.Icon({capeItem.name, type='item', noicon=true})) | |||
table.insert(resultPart, '\n| ' .. Common.getRequirementString(cape.purchaseRequirements, 'None')) | |||
table.insert(resultPart, '\n| ' .. p._getPurchaseDescription(cape)) | |||
end | end | ||
end | end | ||
table.insert(resultPart, '\n|}') | |||
return table.concat(resultPart) | |||
end | |||
end | |||
function p.getGodUpgradeDungeon(purch) | |||
-- Identifies skill upgrades which have a dungeon completion requirement for an area | |||
-- whose name ends with 'God Dungeon'. Returns the ID of the dungeon which must be | |||
-- completed before the purchase may be bought if the purchase is a god upgrade | |||
if purch.category == 'melvorD:SkillUpgrades' and type(purch.purchaseRequirements) == 'table' then | |||
for i, req in ipairs(purch.purchaseRequirements) do | |||
if req.type == 'DungeonCompletion' and string.find(req.dungeonID, 'God_Dungeon$') ~= nil then | |||
return req.dungeonID | |||
end | |||
end | |||
end | end | ||
end | end | ||
function p.getGodUpgradeTable() | function p.getGodUpgradeTable() | ||
local resultPart = {} | local resultPart = {} | ||
local upgradeList = p.getPurchases( | local upgradeList = p.getPurchases( | ||
function( | function(purch) | ||
return p.getGodUpgradeDungeon(purch) ~= nil | |||
end) | end) | ||
if Shared. | if Shared.tableIsEmpty(upgradeList) then | ||
return '' | |||
end | |||
-- Table header | -- Table header | ||
Line 649: | Line 743: | ||
-- 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 = | local upgradeName = Common.getPurchaseName(upgrade) | ||
local dung = GameData.getEntityByID('dungeons', p.getGodUpgradeDungeon(upgrade)) | |||
local costSortValue = p._getPurchaseSortValue(upgrade) | local costSortValue = p._getPurchaseSortValue(upgrade) | ||
table.insert(resultPart, '|-\r\n| | table.insert(resultPart, '|-\r\n|class="table-img" data-sort-value="' .. upgradeName .. '"| ' ..p._getPurchaseExpansionIcon(upgrade).. Icons.Icon({upgradeName, type='upgrade', notext=true})) | ||
table.insert(resultPart, '| ' .. Icons.Icon({ | table.insert(resultPart, '| ' .. Icons.Icon({upgradeName, type='upgrade', noicon=true})) | ||
table.insert(resultPart, '| ' .. upgrade | table.insert(resultPart, '| ' .. p._getPurchaseDescription(upgrade)) | ||
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 662: | Line 757: | ||
end | end | ||
function p. | function p.getAoDTable(frame) | ||
-- All purchases in the Atlas of Discovery category except for skillcapes, which are handled | |||
-- by p.getShopSkillcapeTable() | |||
local purchList = p.getPurchases( | |||
function(purch) | |||
return ' | return purch.category == 'melvorAoD:AtlasOfDiscovery' and not p.isSkillcapePurchase(purch) | ||
end | |||
) | |||
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' } }) | |||
end | |||
function p.getItATable(frame) | |||
-- As above for AoD, but for Into the Abyss instead | |||
local purchList = p.getPurchases( | |||
function(purch) | |||
return purch.category == 'melvorItA:IntoTheAbyss' and not p.isSkillcapePurchase(purch) | |||
end | |||
) | |||
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' } }) | |||
end | |||
function p.getToolTable(toolName, searchString, modifiers, skillID) | |||
local skillName = nil | |||
if type(skillID) == 'string' then | |||
skillName = Constants.getSkillName(skillID) | |||
end | |||
local toolArray = p.getPurchases( | |||
function(purch) | |||
return purch.category == 'melvorD:SkillUpgrades' and string.find(purch.id, searchString) ~= nil | |||
end) | |||
if Shared.tableIsEmpty(toolArray) then | |||
return '' | |||
end | |||
if modifiers == nil then | |||
modifiers = {} | |||
end | end | ||
-- Determine match criteria for modifier matches later & initialize | |||
-- accumulators for modifier magnitudes | |||
local | local modTotal, modMatchCriteria = {}, {} | ||
for i, modDef in ipairs(modifiers) do | |||
modTotal[i] = 0 | |||
modMatchCriteria[i] = Modifiers.getMatchCriteriaFromIDs({ modDef.matchRule }) | |||
end | end | ||
local headerRowSpan = (Shared.tableIsEmpty(toolArray) and 1) or 2 | |||
local | |||
local resultPart = {} | local resultPart = {} | ||
table.insert(resultPart, '{| class="wikitable stickyHeader"') | table.insert(resultPart, '{| class="wikitable stickyHeader"') | ||
table.insert(resultPart, '|- class="headerRow-0"') | table.insert(resultPart, '\n|- class="headerRow-0"') | ||
table.insert(resultPart, '!colspan=" | table.insert(resultPart, '\n!rowspan="' .. headerRowSpan .. '" colspan="2"| Name') | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n!rowspan="' .. headerRowSpan .. '"| ' .. (skillName == nil and 'Requirements' or Icons.Icon({skillName, type='skill', notext=true}) .. ' Level')) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n!rowspan="' .. headerRowSpan .. '"| Cost') | ||
for i, modDef in ipairs(modifiers) do | |||
table.insert(resultPart, '\n!colspan="2"| ' .. modDef.header) | |||
end | |||
if headerRowSpan > 1 then | |||
table.insert(resultPart, '\n|- class="headerRow-1"' .. string.rep('\n!This ' .. toolName .. '\n!Total', Shared.tableCount(modifiers))) | |||
end | |||
for i, tool in ipairs(toolArray) do | |||
for i, | local toolName = Common.getPurchaseName(tool) | ||
local toolCost = p.getCostString(tool.cost, false) | |||
local | local toolCostSort = p._getPurchaseSortValue(tool) or 0 | ||
table.insert(resultPart, '\n|-') | |||
if | table.insert(resultPart, '\n|class="table-img" data-sort-value="' .. toolName .. '"| ' .. Icons.Icon({toolName, type='upgrade', notext=true})) | ||
table.insert(resultPart, '\n| data-sort-value="' .. toolName.. '"|' .. Icons.getExpansionIcon(tool.id) .. toolName) | |||
local level, levelStyle = nil, nil | |||
if skillID == nil then | |||
level = 'None' | |||
levelStyle = '|class="table-na"' | |||
else | |||
level = 1 | |||
levelStyle = '|style="text-align:right"' | |||
end | |||
if tool.purchaseRequirements ~= nil and not Shared.tableIsEmpty(tool.purchaseRequirements) then | |||
if skillID == nil then | |||
-- Return all requirements | |||
level = Common.getRequirementString(tool.purchaseRequirements, 'None') | |||
if level ~= 'None' then | |||
levelStyle = '' | |||
end | |||
else | |||
-- Return level requirement for just the specified skill | |||
for i, purchReq in ipairs(tool.purchaseRequirements) do | |||
if (purchReq.type == 'SkillLevel' or purchReq.type == 'AbyssalLevel') and purchReq.skillID == skillID then | |||
level = purchReq.level | |||
break | |||
end | end | ||
end | end | ||
end | end | ||
end | end | ||
table.insert(resultPart, '\n' .. levelStyle .. '| '..level) | |||
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. toolCostSort .. '"| ' .. toolCost) | |||
local resultPrefix = '' | |||
local | local cellStart = '\n|style="text-align:right"' | ||
if | if tool.contains ~= nil and tool.contains.modifiers ~= nil then | ||
local toolMods = tool.contains.modifiers | |||
for j, modDef in ipairs(modifiers) do | |||
local matchedMods = Modifiers.getMatchingModifiers(tool.contains.modifiers, modMatchCriteria[j]) | |||
local modVal = Modifiers.getModifierValue(matchedMods.matched) or 0 | |||
modTotal[j] = modTotal[j] + modVal | |||
local cellStartVal = cellStart .. ((modVal == 0 and ' class="table-na"') or '') | |||
local cellStartTot = cellStart .. ((modTotal[j] == 0 and ' class="table-na"') or '') | |||
table.insert(resultPart, cellStartVal .. '| ' .. (modVal == 0 and '' or modDef.sign) .. modVal .. modDef.suffix) | |||
table.insert(resultPart, cellStartTot .. '| ' .. (modTotal[j] == 0 and '' or modDef.sign) .. modTotal[j] .. modDef.suffix) | |||
end | |||
end | end | ||
end | |||
table.insert(resultPart, '\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getAxeTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'Cut Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting' } | |||
} | |||
}, { | |||
header = 'Double Items Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillItemDoublingChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting' } | |||
} | |||
}, { | |||
header = Icons.Icon({'Bird Nest', 'Drop Chance', type='item', nolink=true}), | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'randomProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorD:Bird_Nest' } | |||
} | |||
}, { | |||
header = Icons.Icon({'Ash', 'Drop Chance', type='item', nolink=true}), | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalRandomSkillItemChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorF:Ash' } | |||
} | |||
} | |||
} | |||
return p.getToolTable('Axe', '_Axe$', modifiers, 'melvorD:Woodcutting') | |||
end | |||
function p.getAxeCoatingTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'AXP Increase', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'abyssalSkillXP', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting' } | |||
} | |||
}, { | |||
header = 'Log Quantity Increase', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'flatAdditionalPrimaryProductQuantity', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting' } | |||
} | |||
}, { | |||
header = Icons.Icon({'Shadow Raven Nest', 'Drop Chance', type='item', nolink=true}), | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'randomProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorItA:Shadow_Raven_Nest' } | |||
} | |||
}, { | |||
header = Icons.Icon({'Shadow Drake Nest', 'Drop Chance', type='item', nolink=true}), | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'randomProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorItA:Shadow_Drake_Nest' } | |||
} | |||
} | |||
} | |||
return p.getToolTable('Coating', '_Axe_Coating$', modifiers, 'melvorD:Woodcutting') | |||
end | |||
function p.getPickaxeTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'Mining Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Mining' } | |||
} | |||
}, { | |||
header = '2x Ore Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillItemDoublingChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Mining' } | |||
} | |||
}, { | |||
header = '+1 Ore Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalPrimaryProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Mining', ["categoryID"] = 'melvorD:Ore' } | |||
} | |||
}, { | |||
header = 'Superior Gem Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'qualitySuperiorGemChance', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = 'Increased ' .. Icons.Icon({'Meteorite Ore', type='item', notext=true}), | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'flatBasePrimaryProductQuantity', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Mining', ["actionID"] = 'melvorTotH:Meteorite_Ore' } | |||
} | |||
} | |||
} | |||
return p.getToolTable('Pickaxe', '_Pickaxe$', modifiers, 'melvorD:Mining') | |||
end | |||
function p.getPickaxeCoatingTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'AXP Increase', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'abyssalSkillXP', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Mining' } | |||
} | |||
}, { | |||
header = 'Rock Quantity Increase', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'flatAdditionalPrimaryProductQuantity', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Mining' } | |||
} | |||
}, { | |||
header = 'Abyssal Gem Vein Location Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'abyssalGemVeinChanceIncrease', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = '+1 Abyssal Gem Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalAbyssalGemChance', | |||
["type"] = 'id' | |||
} | |||
} | |||
} | |||
return p.getToolTable('Coating', '_Pickaxe_Coating$', modifiers, 'melvorD:Mining') | |||
end | |||
function p.getRodTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'Catch Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Fishing' } | |||
} | |||
}, { | |||
header = '+1 Fish Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalPrimaryProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Fishing' } | |||
} | |||
}, { | |||
header = Icons.Icon({'Lost Chest', type='item', notext=true}) .. ' Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalRandomSkillItemChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Fishing', ["itemID"] = 'melvorTotH:Lost_Chest' } | |||
} | |||
}, { | |||
header = 'Cooked Fish Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'fishingCookedChance', | |||
["type"] = 'id' | |||
} | |||
} | |||
} | |||
return p.getToolTable('Rod', '_Rod$', modifiers, 'melvorD:Fishing') | |||
end | |||
function p.getRodCoatingTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'AXP Increase', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'abyssalSkillXP', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Fishing' } | |||
} | |||
}, { | |||
header = 'Fish Quantity Increase', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'flatAdditionalPrimaryProductQuantity', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Fishing' } | |||
} | |||
}, { | |||
header = 'Cooked Fish Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'fishingCookedChance', | |||
["type"] = 'id' | |||
} | |||
} | |||
} | |||
return p.getToolTable('Coating', '_Rod_Coating$', modifiers, 'melvorD:Fishing') | |||
end | |||
function p.getHarvesterTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'Minimum Harvesting Intensity', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'minimumHarvestingIntensity', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = '2x Intensity Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'doubleHarvestingIntensityChance', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = '2x Item Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'flatAdditionalPrimaryProductQuantity', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorItA:Harvesting' } | |||
} | |||
} | |||
} | |||
return p.getToolTable('Harvester', '_Harvester$', modifiers, 'melvorItA:Harvesting') | |||
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 Shared.printError('Invalid category specified. Must be one of the following: ' .. mw.text.listToText(validCategories, ', ', ' or ')) | |||
end | |||
local categoryShort = string.match(category, '[^%s]+$') | |||
local modifiers = { | |||
['Cooking Fire'] = { | |||
{ | |||
header = 'Bonus ' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' XP', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillXP', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = Icons.Icon({'Normal Cooking Fire', type='upgrade', notext=true, nolink=true}) .. ' Perfect Cook Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'perfectCookChance', | |||
["type"] = 'id', | |||
["props"] = { ["categoryID"] = 'melvorD:Fire' } | |||
} | |||
}, { | |||
header = 'Passive Cook Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'passiveCookingInterval', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = '2x Items Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillItemDoublingChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = 'Active Cook Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
} | |||
}, | |||
['Furnace'] = { | |||
{ | |||
header = Icons.Icon({'Basic Furnace', type='upgrade', notext=true, nolink=true}) .. ' Perfect Cook Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'perfectCookChance', | |||
["type"] = 'id', | |||
["props"] = { ["categoryID"] = 'melvorD:Furnace' } | |||
} | |||
}, { | |||
header = '2x Items Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillItemDoublingChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = 'Passive Cook Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'passiveCookingInterval', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = 'Active Cook Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = '+1 Item Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalPrimaryProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
} | |||
}, | |||
['Pot'] = { | |||
{ | |||
header = Icons.Icon({'Basic Pot', type='upgrade', notext=true, nolink=true}) .. ' Perfect Cook Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'perfectCookChance', | |||
["type"] = 'id', | |||
["props"] = { ["categoryID"] = 'melvorD:Pot' } | |||
} | |||
}, { | |||
header = '2x Items Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillItemDoublingChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = 'Passive Cook Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'passiveCookingInterval', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = 'Active Cook Time Decrease', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = '+1 Item Chance', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'additionalPrimaryProductChance', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
}, { | |||
header = 'Increased Cooking ' .. Icons.Icon({'Mastery', nolink=true}) .. ' XP', | |||
sign = '+', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'masteryXP', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorD:Cooking' } | |||
} | |||
} | |||
} | |||
} | |||
return p.getToolTable(categoryShort, categoryShort .. '$', modifiers[category], nil) | |||
end | |||
--Adding table for Ship upgrades for Cartography | |||
function p.getShipTable(frame) | |||
local modifiers = { | |||
{ | |||
header = 'Cartography Interval', | |||
sign = '', | |||
suffix = '%', | |||
matchRule = { | |||
["id"] = 'skillInterval', | |||
["type"] = 'id', | |||
["props"] = { ["skillID"] = 'melvorAoD:Cartography' } | |||
} | |||
}, { | |||
header = 'Increased Sight Range', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'cartographySightRange', | |||
["type"] = 'id' | |||
} | |||
}, { | |||
header = 'Increased Survey Range', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = 'cartographySurveyRange', | |||
["type"] = 'id' | |||
} | |||
} | |||
} | |||
return p.getToolTable('Ship', 'Ship', modifiers, 'melvorAoD:Cartography') | |||
end | |||
function p.getArchToolTable(frame) | |||
local category = nil | |||
if frame ~= nil then category = frame.args ~= nil and frame.args[1] or frame end | |||
local validCategories = {'Sieve', 'Trowel', 'Brush', 'Shovel'} | |||
if category == nil or not Shared.contains(validCategories, category) then | |||
return Shared.printError('Invalid category specified. Must be one of the following: ' .. mw.text.listToText(validCategories, ', ', ' or ')) | |||
end | end | ||
return | local modifiers = { | ||
{ | |||
header = 'Increased ' .. category .. 'Tool Level', | |||
sign = '+', | |||
suffix = '', | |||
matchRule = { | |||
["id"] = string.lower(category) .. 'ToolLevel', | |||
["type"] = 'id' | |||
} | |||
} | |||
} | |||
return p.getToolTable(category, category .. '$', modifiers, 'melvorAoD:Archaeology') | |||
end | |||
-- Below functions included for backwards compatibility | |||
-- TODO: Remove dependency on these functions in all other modules | |||
function p._getPurchaseName(purchase) | |||
return Common.getPurchaseName(purchase) | |||
end | |||
function p._getPurchaseType(purchase) | |||
return Common.getPurchaseType(purchase) | |||
end | |||
function p._getPurchaseIcon(iconArgs) | |||
return Common.getPurchaseIcon(iconArgs) | |||
end | |||
function p.getRequirementString(reqs) | |||
return Common.getRequirementString(reqs, 'None') | |||
end | end | ||
return p | return p |
Latest revision as of 15:50, 17 August 2024
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 Common = require('Module:Common')
local Modifiers = require('Module:Modifiers')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Pets = require('Module:Pets')
local Num = require('Module:Number')
function p.getPurchase(purchaseName)
local purchList = p.getPurchases(function(purch) return Common.getPurchaseName(purch) == purchaseName end)
if purchList ~= nil and not Shared.tableIsEmpty(purchList) then
return purchList[1]
end
end
function p.getPurchaseByID(id)
return GameData.getEntityByID('shopPurchases', id)
end
-- Accepts a function(purchase, name) and a category.
-- Prevents external modules from having to make GameData and Common calls.
function p.getCategoryPurchases(checkFunc, category)
local shopCat = GameData.getEntityByName('shopCategories', category)
if shopCat == nil then
return Shared.printError('Invalid category ' .. shopCat)
end
-- We make a nested func to resolve the item name first, if required.
local func =
function(purchase)
if purchase.category ~= shopCat.id then
return false
end
local name = Common.getPurchaseName(purchase)
return checkFunc(purchase, name)
end
return GameData.getEntities('shopPurchases', func)
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 Common.getRequirementString(purchase.purchaseRequirements, 'None')
elseif stat == 'contents' then
return p._getPurchaseContents(purchase, true)
elseif stat == 'type' then
return Common.getPurchaseType(purchase)
elseif stat == 'buyLimit' then
return p._getPurchaseBuyLimit(purchase, not displayInline)
elseif stat == 'buyLimitHardcore' then
return p._getPurchaseBuyLimitNumeric(purchase, 'melvorF:Hardcore')
elseif stat == 'description' then
return p._getPurchaseDescription(purchase)
elseif stat =='expansionicon' then
return p._getPurchaseExpansionIcon(purchase)
else
return purchase[stat]
end
end
function p.getPurchaseStat(frame)
local args = frame.args ~= nil and frame.args or frame
local purchaseName = Shared.fixPagename(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 Common.getPurchaseName(purch) == purchaseName end)
else
purchaseList = {p.getPurchase(purchaseName)}
end
if Shared.tableIsEmpty(purchaseList) then
return Shared.printError("Couldn't find purchase with name '" .. purchaseName .. "'")
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._getPurchaseExpansionIcon(purch)
if purch.id ~= nil then
return Icons.getExpansionIcon(purch.id)
elseif purch.contains ~= nil then
local item = nil
if purch.contains.items ~= nil and not Shared.tableIsEmpty(purch.contains.items) then
return Icons.getExpansionIcon(purch.contains.items[1].id)
elseif purch.contains.itemCharges ~= nil and not Shared.tableIsEmpty(purch.contains.itemCharges) then
return Icons.getExpansionIcon(purch.contains.itemCharges.id)
end
if purch.contains.petID ~= nil then
return Icons.getExpansionIcon(purch.contains.petID)
end
end
return ''
end
function p._getPurchaseDescription(purch)
if purch.customDescription ~= nil then
local templateData = p._getPurchaseTemplateData(purch)
return Shared.applyTemplateData(purch.customDescription, templateData)
elseif purch.contains ~= nil then
local item = nil
if purch.contains.modifiers ~= nil then
return Modifiers.getModifiersText(purch.contains.modifiers, false)
elseif purch.contains.petID ~= nil then
local pet = Pets.getPetByID(purch.contains.petID)
return Pets._getPetEffect(pet)
elseif purch.contains.items ~= nil and Shared.tableCount(purch.contains.items) == 1 then
item = Items.getItemByID(purch.contains.items[1].id)
elseif purch.contains.itemCharges ~= nil then
item = Items.getItemByID(purch.contains.itemCharges.id)
end
if item ~= nil then
if item.customDescription ~= nil then
return item.customDescription
elseif item.modifiers ~= nil then
return Modifiers.getModifiersText(item.modifiers, false)
end
end
end
return ''
end
function p.getCostString(cost, inline)
local displayInline = (inline ~= nil and inline or false)
local costArray = {}
if cost.currencies ~= nil then
for i, costAmt in ipairs(cost.currencies) do
local costStr = p.getCurrencyCostString(costAmt)
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
return ''
end
-- Generates description template data. See: shop.js, getDescriptionTemplateData()
function p._getPurchaseTemplateData(purchase)
-- qty is a static value of 1 for Bank slots
local templateData = { qty = 1 }
if purchase.contains ~= nil and purchase.contains.items ~= nil then
for i, itemDef in ipairs(purchase.contains.items) do
templateData['qty' .. i] = itemDef.quantity
end
end
return templateData
end
function p.getCurrencyCostString(cost)
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 Icons._Currency(cost.currency, cost.initial) .. '<br/>+' .. Icons._Currency(cost.currency, 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 Merchant's Permit cost reduction can be applied,
-- it makes no difference here
return Icons._Currency(cost.currency, cost.cost)
end
end
function p._getPurchaseContents(purchase, asList)
if asList == nil then asList = true end
local containArray = {}
local GPTotal = 0
local currency = 'melvorD:GP'
if purchase.contains ~= nil then
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 item.sellsForCurrency ~= nil then
currency = item.sellsForCurrency
end
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| class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true}))
table.insert(containArray, '|data-sort-value="'..item.name..'"|'.. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}) .. '\r\n| data-sort-value="' .. itemQty .. '" style="text-align:right" | ' .. Num.formatnum(itemQty))
table.insert(containArray, '| data-sort-value="' .. GPVal .. '"| ' .. Icons._Currency(currency, GPVal))
end
end
end
if purchase.contains.itemCharges ~= nil and purchase.contains.itemCharges.quantity > 0 then
local gloveItem = Items.getItemByID(purchase.contains.itemCharges.id)
local chargeQty = purchase.contains.itemCharges.quantity
if gloveItem ~= nil then
if gloveItem.sellsForCurrency ~= nil then
currency = gloveItem.sellsForCurrency
end
if asList then
table.insert(containArray, ' +'..Num.formatnum(chargeQty)..' '..Icons.Icon({gloveItem.name, type='item'})..' Charges')
else
table.insert(containArray, '|-\r\n| class="table-img"| ' .. Icons.Icon({gloveItem.name, type='item', notext=true}))
table.insert(containArray, '| ' .. Icons.Icon({gloveItem.name, type='item', noicon=true}) .. ' Charges\r\n| data-sort-value="' .. chargeQty .. '" style="text-align:right" | ' .. Num.formatnum(chargeQty))
table.insert(containArray, '| data-sort-value="0"| ' .. Icons._Currency(currency, 0))
end
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._Currency(currency, 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 = Shared.fixPagename(args[1])
local asList = (args[2] ~= nil and string.upper(args[2]) == 'TRUE')
local purchase = p.getPurchase(purchaseName)
if purchase == nil then
return Shared.printError("Couldn't find purchase with name '" .. purchaseName .. "'")
else
return p._getPurchaseContents(purchase, asList)
end
end
function p._getPurchaseBuyLimitNumeric(purchase, gamemodeID)
local buyLimit = (purchase.defaultBuyLimit > 0 and purchase.defaultBuyLimit)
if not Shared.tableIsEmpty(purchase.buyLimitOverrides) then
local gamemodeLimit = GameData.getEntityByProperty(purchase.buyLimitOverrides, 'gamemodeID', gamemodeID)
if gamemodeLimit ~= nil and gamemodeLimit.maximum ~= nil then
buyLimit = gamemodeLimit.maximum
end
end
return buyLimit
end
function p._getPurchaseBuyLimit(purchase, asList)
if asList == nil then asList = true end
local defaultLimit = (purchase.defaultBuyLimit == 0 and 'Unlimited') or Num.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 gamemodeName = Shared.splitString(gamemode.name, ' ')[1]
local gamemodeText = nil
if Shared.contains(gamemodeHasIcon, gamemode.id) then
gamemodeText = Icons.Icon({gamemodeName, notext=(not asList or nil)})
else
gamemodeText = '[[Game Mode#' .. gamemodeName .. '|' .. gamemodeName .. ']]'
end
local limitText = (buyLimit.maximum == 0 and 'Unlimited') or Num.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 Shared.printError("Couldn't find purchase with name '" .. purchaseName .. "'")
else
return p._getPurchaseBuyLimit(purchase, asList)
end
end
function p.getPurchaseIcon(frame)
local args = frame.args ~= nil and frame.args or frame
local purchaseName = Shared.fixPagename(args[1])
local purchase = p.getPurchase(purchaseName)
if purchase == nil then
return Shared.printError("Couldn't find purchase with name '" .. tostring(purchaseName) .. "'")
else
args[1] = purchase
return Common.getPurchaseIcon(args)
end
end
function p._getPurchaseSortValue(purchase)
if purchase.cost ~= nil and purchase.cost.currencies ~= nil then
for _, costAmt in ipairs(purchase.cost.currencies) do
-- Find cost for the current currency, if it exists
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
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, stickyHeader = {}, 'Purchase', nil, {}, true
-- 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
-- Sticky header class
if options.stickyHeader ~= nil then
if type(options.stickyHeader) == 'boolean' then
stickyHeader = options.stickyHeader
elseif type(options.stickyHeader) == 'string' and string.lower(options.stickyHeader) == 'false' then
stickyHeader = false
end
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 and ' stickyHeader' or '') .. '"')
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
if sortOrder == nil then
Purchases = GameData.sortByOrderTable(Purchases, GameData.rawData.shopDisplayOrder, true)
else
table.sort(Purchases, sortOrder)
end
for i, purchase in ipairs(Purchases) do
local purchName = Common.getPurchaseName(purchase)
local purchExpIcon = p._getPurchaseExpansionIcon(purchase)
local purchType = Common.getPurchaseType(purchase)
local costString = p.getCostString(purchase.cost, false)
table.insert(resultPart, '|-')
for j, column in ipairs(usedColumns) do
if column == 'Purchase' then
table.insert(resultPart, '|class="table-img"|' .. Common.getPurchaseIcon({purchase, notext=true}))
table.insert(resultPart, '| data-sort-value="'..purchName..'"|'..purchExpIcon .. Common.getPurchaseIcon({purchase, noicon=true}))
elseif column == 'Type' then
table.insert(resultPart, '| ' .. purchType)
elseif column == 'Description' then
table.insert(resultPart, '| ' .. p._getPurchaseDescription(purchase))
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, '| ' .. Common.getRequirementString(purchase.purchaseRequirements, 'None'))
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, '\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'
-- stickyHeader: Specifies if the table will have a sticky header or not
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.stickyHeader ~= nil then options.stickyHeader = frame.args.stickyHeader 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 Shared.printError('Invalid category ' .. cat)
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 then
if 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
if purchase.contains.itemCharges ~= nil and purchase.contains.itemCharges.id == itemID then
table.insert(purchaseArray, { ["purchase"] = purchase, ["qty"] = 1 })
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..' - '..p._getPurchaseExpansionIcon(purchase) .. Common.getPurchaseIcon({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|'..Common.getRequirementString(purchase.purchaseRequirements, 'None')
result = result..'\r\n|-\r\n!style="text-align:right;"|Contains'
result = result..'\r\n|'..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.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 Shared.printError('No item named ' .. itemName .. ' exists in the data module')
end
return p._getItemShopTable(item)
end
function p.getShopMiscUpgradeTable()
-- All purchases in the general category besides Auto Eat, which is conained within a separate table
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.getShopSkillUpgradeTable()
-- All purchaes in the SkillUpgrades category except tools and any upgrades displayed as
-- tools (e.g. ship upgrades)
local purchList = p.getPurchases(
function(purch)
return purch.category == 'melvorD:SkillUpgrades'
-- Exclude tools, handled by p.getToolTable()
and string.find(purch.id, '_Axe$') == nil
and string.find(purch.id, '_Axe_Coating$') == nil
and string.find(purch.id, '_Pickaxe$') == nil
and string.find(purch.id, '_Pickaxe_Coating$') == nil
and string.find(purch.id, '_Rod$') == nil
and string.find(purch.id, '_Rod_Coating$') == nil
and string.find(purch.id, '_Harvester$') == nil
and string.find(purch.id, 'Fire$') == nil
and string.find(purch.id, 'Furnace$') == nil
and string.find(purch.id, 'Pot$') == nil
and string.find(purch.id, 'Sieve$') == nil
and string.find(purch.id, 'Trowel$') == nil
and string.find(purch.id, 'Brush$') == nil
and string.find(purch.id, 'Shovel$') == nil
and string.find(purch.id, 'ShipUpgrade') == nil
-- Exclude God upgrades, handled by p.getGodUpgradeTable()
and p.getGodUpgradeDungeon(purch) == nil
end
)
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' }, purchaseHeader = 'Upgrade' })
end
function p.getPurchaseDescription(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local purchase = p.getPurchase(itemName)
if purchase == nil then
return ''
end
return p._getPurchaseDescription(purchase)
end
function p.isSkillcapePurchase(purch, isSuperior, skillID)
-- Returns true or false depending on whether the purchase is a skillcape or not.
-- If isSuperior is true, then this checks for superior skillcapes, false checks
-- for regular skillcapes, and nil checks for both.
-- If skillID is specified, then the skillcape must also relate to that skill
local checkCategories = (isSuperior == nil and {'melvorTotH:SuperiorSkillcapes', 'melvorD:Skillcapes'}) or (isSuperior and {'melvorTotH:SuperiorSkillcapes'}) or {'melvorD:Skillcapes'}
-- Some skillcapes (such as Archaeology & Cartography) reside outside of the usual categories
local overrideIDs = {
['melvorTotH:SuperiorSkillcapes'] = {
'melvorAoD:Superior_Archaeology_Skillcape',
'melvorAoD:Superior_Cartography_Skillcape',
'melvorAoD:Cape_of_Completion_AoD'
},
['melvorD:Skillcapes'] = {
'melvorAoD:Archaeology_Skillcape',
'melvorAoD:Cartography_Skillcape',
'melvorItA:Cape_of_Completion_ItA'
}
}
for i, cat in ipairs(checkCategories) do
if purch.category == cat or Shared.contains(overrideIDs[cat], purch.id) then
if skillID == nil then
return true
else
-- Also validate purchase requirements for relevant SkillLevel requirement
local hasReq = false
if type(purch.purchaseRequirements) == 'table' then
for j, req in ipairs(purch.purchaseRequirements) do
if req.type == 'SkillLevel' then
if req.skillID == skillID then
hasReq = true
else
-- The presence of any other skill's requirement indicates
-- this is not a skillcape for skill with ID skillID
return false
end
end
end
end
return hasReq
end
end
end
return false
end
function p._getShopSkillcapeTable(showSuperior)
local capeList = p.getPurchases(function(purch) return p.isSkillcapePurchase(purch, showSuperior) end)
local sortOrderFunc =
function(a, b)
local costA, costB = p._getPurchaseSortValue(a), p._getPurchaseSortValue(b)
if costA == costB then
return Common.getPurchaseName(a) < Common.getPurchaseName(b)
else
return costA < costB
end
end
return p._getShopTable(capeList, {
columns = { 'Purchase', 'Description', 'Cost' },
purchaseHeader = 'Cape',
sortOrder = sortOrderFunc,
stickyHeader = false,
headerProps = {["Purchase"] = 'colspan="2" style="width:200px;"', ["Cost"] = 'style=width:120px;'}
})
end
function p.getShopSkillcapeTable(frame)
local capeCategory = frame.args ~= nil and frame.args[1] or frame
local showSuperior = string.lower(capeCategory) == 'superior'
return p._getShopSkillcapeTable(showSuperior)
end
function p.getSkillcapeTable(frame)
local skillName = frame.args ~= nil and frame.args[1] or frame
local skillID = Constants.getSkillID(skillName)
if skillID == nil then
return Shared.printError('No such skill "' .. (skillName or 'nil') .. '"')
end
local capeList = p.getPurchases(function(purch) return p.isSkillcapePurchase(purch, nil, skillID) end)
if Shared.tableIsEmpty(capeList) then
return ''
else
capeList = GameData.sortByOrderTable(capeList, GameData.rawData.shopDisplayOrder, true)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable"\n')
table.insert(resultPart, '!Skillcape!!Name!!Requirements!!Effect')
for i, cape in ipairs(capeList) do
local capeItem = Items.getItemByID(cape.contains.items[1].id)
if capeItem ~= nil then
table.insert(resultPart, '\n|-\n| ' .. Icons.Icon({capeItem.name, type='item', notext=true}))
table.insert(resultPart, '\n| data-sort-value="'..capeItem.name..'"|'..Icons.getExpansionIcon(capeItem.id) .. Icons.Icon({capeItem.name, type='item', noicon=true}))
table.insert(resultPart, '\n| ' .. Common.getRequirementString(cape.purchaseRequirements, 'None'))
table.insert(resultPart, '\n| ' .. p._getPurchaseDescription(cape))
end
end
table.insert(resultPart, '\n|}')
return table.concat(resultPart)
end
end
function p.getGodUpgradeDungeon(purch)
-- Identifies skill upgrades which have a dungeon completion requirement for an area
-- whose name ends with 'God Dungeon'. Returns the ID of the dungeon which must be
-- completed before the purchase may be bought if the purchase is a god upgrade
if purch.category == 'melvorD:SkillUpgrades' and type(purch.purchaseRequirements) == 'table' then
for i, req in ipairs(purch.purchaseRequirements) do
if req.type == 'DungeonCompletion' and string.find(req.dungeonID, 'God_Dungeon$') ~= nil then
return req.dungeonID
end
end
end
end
function p.getGodUpgradeTable()
local resultPart = {}
local upgradeList = p.getPurchases(
function(purch)
return p.getGodUpgradeDungeon(purch) ~= nil
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 = Common.getPurchaseName(upgrade)
local dung = GameData.getEntityByID('dungeons', p.getGodUpgradeDungeon(upgrade))
local costSortValue = p._getPurchaseSortValue(upgrade)
table.insert(resultPart, '|-\r\n|class="table-img" data-sort-value="' .. upgradeName .. '"| ' ..p._getPurchaseExpansionIcon(upgrade).. Icons.Icon({upgradeName, type='upgrade', notext=true}))
table.insert(resultPart, '| ' .. Icons.Icon({upgradeName, type='upgrade', noicon=true}))
table.insert(resultPart, '| ' .. p._getPurchaseDescription(upgrade))
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.getAoDTable(frame)
-- All purchases in the Atlas of Discovery category except for skillcapes, which are handled
-- by p.getShopSkillcapeTable()
local purchList = p.getPurchases(
function(purch)
return purch.category == 'melvorAoD:AtlasOfDiscovery' and not p.isSkillcapePurchase(purch)
end
)
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' } })
end
function p.getItATable(frame)
-- As above for AoD, but for Into the Abyss instead
local purchList = p.getPurchases(
function(purch)
return purch.category == 'melvorItA:IntoTheAbyss' and not p.isSkillcapePurchase(purch)
end
)
return p._getShopTable(purchList, { columns = { 'Purchase', 'Description', 'Cost', 'Requirements' } })
end
function p.getToolTable(toolName, searchString, modifiers, skillID)
local skillName = nil
if type(skillID) == 'string' then
skillName = Constants.getSkillName(skillID)
end
local toolArray = p.getPurchases(
function(purch)
return purch.category == 'melvorD:SkillUpgrades' and string.find(purch.id, searchString) ~= nil
end)
if Shared.tableIsEmpty(toolArray) then
return ''
end
if modifiers == nil then
modifiers = {}
end
-- Determine match criteria for modifier matches later & initialize
-- accumulators for modifier magnitudes
local modTotal, modMatchCriteria = {}, {}
for i, modDef in ipairs(modifiers) do
modTotal[i] = 0
modMatchCriteria[i] = Modifiers.getMatchCriteriaFromIDs({ modDef.matchRule })
end
local headerRowSpan = (Shared.tableIsEmpty(toolArray) and 1) or 2
local resultPart = {}
table.insert(resultPart, '{| class="wikitable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!rowspan="' .. headerRowSpan .. '" colspan="2"| Name')
table.insert(resultPart, '\n!rowspan="' .. headerRowSpan .. '"| ' .. (skillName == nil and 'Requirements' or Icons.Icon({skillName, type='skill', notext=true}) .. ' Level'))
table.insert(resultPart, '\n!rowspan="' .. headerRowSpan .. '"| Cost')
for i, modDef in ipairs(modifiers) do
table.insert(resultPart, '\n!colspan="2"| ' .. modDef.header)
end
if headerRowSpan > 1 then
table.insert(resultPart, '\n|- class="headerRow-1"' .. string.rep('\n!This ' .. toolName .. '\n!Total', Shared.tableCount(modifiers)))
end
for i, tool in ipairs(toolArray) do
local toolName = Common.getPurchaseName(tool)
local toolCost = p.getCostString(tool.cost, false)
local toolCostSort = p._getPurchaseSortValue(tool) or 0
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img" data-sort-value="' .. toolName .. '"| ' .. Icons.Icon({toolName, type='upgrade', notext=true}))
table.insert(resultPart, '\n| data-sort-value="' .. toolName.. '"|' .. Icons.getExpansionIcon(tool.id) .. toolName)
local level, levelStyle = nil, nil
if skillID == nil then
level = 'None'
levelStyle = '|class="table-na"'
else
level = 1
levelStyle = '|style="text-align:right"'
end
if tool.purchaseRequirements ~= nil and not Shared.tableIsEmpty(tool.purchaseRequirements) then
if skillID == nil then
-- Return all requirements
level = Common.getRequirementString(tool.purchaseRequirements, 'None')
if level ~= 'None' then
levelStyle = ''
end
else
-- Return level requirement for just the specified skill
for i, purchReq in ipairs(tool.purchaseRequirements) do
if (purchReq.type == 'SkillLevel' or purchReq.type == 'AbyssalLevel') and purchReq.skillID == skillID then
level = purchReq.level
break
end
end
end
end
table.insert(resultPart, '\n' .. levelStyle .. '| '..level)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. toolCostSort .. '"| ' .. toolCost)
local resultPrefix = ''
local cellStart = '\n|style="text-align:right"'
if tool.contains ~= nil and tool.contains.modifiers ~= nil then
local toolMods = tool.contains.modifiers
for j, modDef in ipairs(modifiers) do
local matchedMods = Modifiers.getMatchingModifiers(tool.contains.modifiers, modMatchCriteria[j])
local modVal = Modifiers.getModifierValue(matchedMods.matched) or 0
modTotal[j] = modTotal[j] + modVal
local cellStartVal = cellStart .. ((modVal == 0 and ' class="table-na"') or '')
local cellStartTot = cellStart .. ((modTotal[j] == 0 and ' class="table-na"') or '')
table.insert(resultPart, cellStartVal .. '| ' .. (modVal == 0 and '' or modDef.sign) .. modVal .. modDef.suffix)
table.insert(resultPart, cellStartTot .. '| ' .. (modTotal[j] == 0 and '' or modDef.sign) .. modTotal[j] .. modDef.suffix)
end
end
end
table.insert(resultPart, '\n|}')
return table.concat(resultPart)
end
function p.getAxeTable(frame)
local modifiers = {
{
header = 'Cut Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting' }
}
}, {
header = 'Double Items Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'skillItemDoublingChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting' }
}
}, {
header = Icons.Icon({'Bird Nest', 'Drop Chance', type='item', nolink=true}),
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'randomProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorD:Bird_Nest' }
}
}, {
header = Icons.Icon({'Ash', 'Drop Chance', type='item', nolink=true}),
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalRandomSkillItemChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorF:Ash' }
}
}
}
return p.getToolTable('Axe', '_Axe$', modifiers, 'melvorD:Woodcutting')
end
function p.getAxeCoatingTable(frame)
local modifiers = {
{
header = 'AXP Increase',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'abyssalSkillXP',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting' }
}
}, {
header = 'Log Quantity Increase',
sign = '+',
suffix = '',
matchRule = {
["id"] = 'flatAdditionalPrimaryProductQuantity',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting' }
}
}, {
header = Icons.Icon({'Shadow Raven Nest', 'Drop Chance', type='item', nolink=true}),
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'randomProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorItA:Shadow_Raven_Nest' }
}
}, {
header = Icons.Icon({'Shadow Drake Nest', 'Drop Chance', type='item', nolink=true}),
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'randomProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Woodcutting', ["itemID"] = 'melvorItA:Shadow_Drake_Nest' }
}
}
}
return p.getToolTable('Coating', '_Axe_Coating$', modifiers, 'melvorD:Woodcutting')
end
function p.getPickaxeTable(frame)
local modifiers = {
{
header = 'Mining Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Mining' }
}
}, {
header = '2x Ore Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'skillItemDoublingChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Mining' }
}
}, {
header = '+1 Ore Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalPrimaryProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Mining', ["categoryID"] = 'melvorD:Ore' }
}
}, {
header = 'Superior Gem Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'qualitySuperiorGemChance',
["type"] = 'id'
}
}, {
header = 'Increased ' .. Icons.Icon({'Meteorite Ore', type='item', notext=true}),
sign = '+',
suffix = '',
matchRule = {
["id"] = 'flatBasePrimaryProductQuantity',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Mining', ["actionID"] = 'melvorTotH:Meteorite_Ore' }
}
}
}
return p.getToolTable('Pickaxe', '_Pickaxe$', modifiers, 'melvorD:Mining')
end
function p.getPickaxeCoatingTable(frame)
local modifiers = {
{
header = 'AXP Increase',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'abyssalSkillXP',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Mining' }
}
}, {
header = 'Rock Quantity Increase',
sign = '+',
suffix = '',
matchRule = {
["id"] = 'flatAdditionalPrimaryProductQuantity',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Mining' }
}
}, {
header = 'Abyssal Gem Vein Location Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'abyssalGemVeinChanceIncrease',
["type"] = 'id'
}
}, {
header = '+1 Abyssal Gem Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalAbyssalGemChance',
["type"] = 'id'
}
}
}
return p.getToolTable('Coating', '_Pickaxe_Coating$', modifiers, 'melvorD:Mining')
end
function p.getRodTable(frame)
local modifiers = {
{
header = 'Catch Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Fishing' }
}
}, {
header = '+1 Fish Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalPrimaryProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Fishing' }
}
}, {
header = Icons.Icon({'Lost Chest', type='item', notext=true}) .. ' Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalRandomSkillItemChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Fishing', ["itemID"] = 'melvorTotH:Lost_Chest' }
}
}, {
header = 'Cooked Fish Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'fishingCookedChance',
["type"] = 'id'
}
}
}
return p.getToolTable('Rod', '_Rod$', modifiers, 'melvorD:Fishing')
end
function p.getRodCoatingTable(frame)
local modifiers = {
{
header = 'AXP Increase',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'abyssalSkillXP',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Fishing' }
}
}, {
header = 'Fish Quantity Increase',
sign = '+',
suffix = '',
matchRule = {
["id"] = 'flatAdditionalPrimaryProductQuantity',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Fishing' }
}
}, {
header = 'Cooked Fish Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'fishingCookedChance',
["type"] = 'id'
}
}
}
return p.getToolTable('Coating', '_Rod_Coating$', modifiers, 'melvorD:Fishing')
end
function p.getHarvesterTable(frame)
local modifiers = {
{
header = 'Minimum Harvesting Intensity',
sign = '+',
suffix = '',
matchRule = {
["id"] = 'minimumHarvestingIntensity',
["type"] = 'id'
}
}, {
header = '2x Intensity Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'doubleHarvestingIntensityChance',
["type"] = 'id'
}
}, {
header = '2x Item Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'flatAdditionalPrimaryProductQuantity',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorItA:Harvesting' }
}
}
}
return p.getToolTable('Harvester', '_Harvester$', modifiers, 'melvorItA:Harvesting')
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 Shared.printError('Invalid category specified. Must be one of the following: ' .. mw.text.listToText(validCategories, ', ', ' or '))
end
local categoryShort = string.match(category, '[^%s]+$')
local modifiers = {
['Cooking Fire'] = {
{
header = 'Bonus ' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' XP',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'skillXP',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = Icons.Icon({'Normal Cooking Fire', type='upgrade', notext=true, nolink=true}) .. ' Perfect Cook Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'perfectCookChance',
["type"] = 'id',
["props"] = { ["categoryID"] = 'melvorD:Fire' }
}
}, {
header = 'Passive Cook Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'passiveCookingInterval',
["type"] = 'id'
}
}, {
header = '2x Items Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'skillItemDoublingChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = 'Active Cook Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}
},
['Furnace'] = {
{
header = Icons.Icon({'Basic Furnace', type='upgrade', notext=true, nolink=true}) .. ' Perfect Cook Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'perfectCookChance',
["type"] = 'id',
["props"] = { ["categoryID"] = 'melvorD:Furnace' }
}
}, {
header = '2x Items Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'skillItemDoublingChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = 'Passive Cook Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'passiveCookingInterval',
["type"] = 'id'
}
}, {
header = 'Active Cook Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = '+1 Item Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalPrimaryProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}
},
['Pot'] = {
{
header = Icons.Icon({'Basic Pot', type='upgrade', notext=true, nolink=true}) .. ' Perfect Cook Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'perfectCookChance',
["type"] = 'id',
["props"] = { ["categoryID"] = 'melvorD:Pot' }
}
}, {
header = '2x Items Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'skillItemDoublingChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = 'Passive Cook Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'passiveCookingInterval',
["type"] = 'id'
}
}, {
header = 'Active Cook Time Decrease',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = '+1 Item Chance',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'additionalPrimaryProductChance',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}, {
header = 'Increased Cooking ' .. Icons.Icon({'Mastery', nolink=true}) .. ' XP',
sign = '+',
suffix = '%',
matchRule = {
["id"] = 'masteryXP',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorD:Cooking' }
}
}
}
}
return p.getToolTable(categoryShort, categoryShort .. '$', modifiers[category], nil)
end
--Adding table for Ship upgrades for Cartography
function p.getShipTable(frame)
local modifiers = {
{
header = 'Cartography Interval',
sign = '',
suffix = '%',
matchRule = {
["id"] = 'skillInterval',
["type"] = 'id',
["props"] = { ["skillID"] = 'melvorAoD:Cartography' }
}
}, {
header = 'Increased Sight Range',
sign = '+',
suffix = '',
matchRule = {
["id"] = 'cartographySightRange',
["type"] = 'id'
}
}, {
header = 'Increased Survey Range',
sign = '+',
suffix = '',
matchRule = {
["id"] = 'cartographySurveyRange',
["type"] = 'id'
}
}
}
return p.getToolTable('Ship', 'Ship', modifiers, 'melvorAoD:Cartography')
end
function p.getArchToolTable(frame)
local category = nil
if frame ~= nil then category = frame.args ~= nil and frame.args[1] or frame end
local validCategories = {'Sieve', 'Trowel', 'Brush', 'Shovel'}
if category == nil or not Shared.contains(validCategories, category) then
return Shared.printError('Invalid category specified. Must be one of the following: ' .. mw.text.listToText(validCategories, ', ', ' or '))
end
local modifiers = {
{
header = 'Increased ' .. category .. 'Tool Level',
sign = '+',
suffix = '',
matchRule = {
["id"] = string.lower(category) .. 'ToolLevel',
["type"] = 'id'
}
}
}
return p.getToolTable(category, category .. '$', modifiers, 'melvorAoD:Archaeology')
end
-- Below functions included for backwards compatibility
-- TODO: Remove dependency on these functions in all other modules
function p._getPurchaseName(purchase)
return Common.getPurchaseName(purchase)
end
function p._getPurchaseType(purchase)
return Common.getPurchaseType(purchase)
end
function p._getPurchaseIcon(iconArgs)
return Common.getPurchaseIcon(iconArgs)
end
function p.getRequirementString(reqs)
return Common.getRequirementString(reqs, 'None')
end
return p