918
edits
Falterfire (talk | contribs) (moved expansion icon in front of quantity for cooking tables) |
m (Add space for separator for herb itemsources) |
||
(19 intermediate revisions by 4 users not shown) | |||
Line 9: | Line 9: | ||
local Items = require('Module:Items') | local Items = require('Module:Items') | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local ItemSourceTables = require('Module:Items/SourceTables') | |||
function p.getCookedItemsTable(frame) | function p.getCookedItemsTable(frame) | ||
Line 47: | Line 48: | ||
table.insert(resultPart, '\r\n|- class="headerRow-0"') | table.insert(resultPart, '\r\n|- class="headerRow-0"') | ||
table.insert(resultPart, '\r\n!colspan="3" rowspan="2"|Cooked Item!!rowspan="2"|'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level') | table.insert(resultPart, '\r\n!colspan="3" rowspan="2"|Cooked Item!!rowspan="2"|'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level') | ||
table.insert(resultPart, '!!rowspan="2"|Cook Time!!rowspan="2"|XP!!rowspan="2"|XP/s!!colspan="2"|Healing!!colspan="2"|Value!!rowspan="2"|Ingredients') | table.insert(resultPart, '!!rowspan="2"|Cook Time (s)!!rowspan="2"|XP!!rowspan="2"|XP/s!!colspan="2"|Healing!!colspan="2"|Value!!rowspan="2"|Ingredients') | ||
table.insert(resultPart, '\r\n|- class="headerRow-1"') | table.insert(resultPart, '\r\n|- class="headerRow-1"') | ||
table.insert(resultPart, '\r\n!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true})) | table.insert(resultPart, '\r\n!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true})) | ||
Line 61: | Line 62: | ||
table.insert(resultPart, '\r\n|-') | table.insert(resultPart, '\r\n|-') | ||
table.insert(resultPart, '\r\n| | table.insert(resultPart, '\r\n|class="table-img"|'..Icons.Icon({item.name, type='item', notext=true, size='50'})) | ||
table.insert(resultPart, '\r\n| | table.insert(resultPart, '\r\n|class="table-img"| ') | ||
if perfectItem ~= nil then | if perfectItem ~= nil then | ||
table.insert(resultPart, Icons.Icon({perfectItem.name, type='item', notext=true, size='50'})) | table.insert(resultPart, Icons.Icon({perfectItem.name, type='item', notext=true, size='50'})) | ||
Line 73: | Line 74: | ||
table.insert(resultPart, Icons.Icon({item.name, type='item', noicon = true})) | table.insert(resultPart, Icons.Icon({item.name, type='item', noicon = true})) | ||
table.insert(resultPart, '||style="text-align:right"|' .. recipe.level) | table.insert(resultPart, '||style="text-align:right"|' .. recipe.level) | ||
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseInterval .. '"|' .. Shared. | table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseInterval .. '"|' .. Shared.round(recipe.baseInterval / 1000, 2, 0)) | ||
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"|' .. Shared.formatnum(recipe.baseExperience)) | table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"|' .. Shared.formatnum(recipe.baseExperience)) | ||
local xpRate = recipe.baseExperience / (recipe.baseInterval / 1000) | local xpRate = recipe.baseExperience / (recipe.baseInterval / 1000) | ||
Line 94: | Line 95: | ||
local tierSuffix = { 'I', 'II', 'III', 'IV' } | local tierSuffix = { 'I', 'II', 'III', 'IV' } | ||
function p._getPotionDescription(potion) | |||
-- TODO: Temporary fix below for incorrect Traps Potion descriptions. To amend | |||
-- once corrected within game data | |||
if potion.customDescription and not Shared.contains(potion.id, 'melvorTotH:Traps_Potion_') then | |||
return potion.customDescription | |||
elseif type(potion.modifiers) == 'table' and not Shared.tableIsEmpty(potion.modifiers) then | |||
return Constants.getModifiersText(potion.modifiers, false, true) | |||
else | |||
return '' | |||
end | |||
end | |||
function p._getHerblorePotionTable(categoryName) | function p._getHerblorePotionTable(categoryName) | ||
local categoryID = nil | local categoryID = nil | ||
Line 101: | Line 114: | ||
categoryID = 'melvorF:SkillPotions' | categoryID = 'melvorF:SkillPotions' | ||
else | else | ||
return ' | return Shared.printError('No such potion category ' .. (categoryName or 'nil')) | ||
end | end | ||
Line 115: | Line 128: | ||
for i, potion in ipairs(potionArray) do | for i, potion in ipairs(potionArray) do | ||
table.insert(resultPart, '\r\n|-') | table.insert(resultPart, '\r\n|-') | ||
local expIcon = Icons.getExpansionIcon(potion.potionIDs[1]) | |||
table.insert(resultPart, '\r\n|rowspan="4"|'..expIcon..'[['..potion.name..']]') | |||
table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.level) | table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.level) | ||
table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.baseExperience) | table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.baseExperience) | ||
Line 134: | Line 144: | ||
local rowTxt = {} | local rowTxt = {} | ||
local tierPot = Items.getItemByID(potionID) | local tierPot = Items.getItemByID(potionID) | ||
table.insert(rowTxt, Icons.Icon({tierPot.name, type='item', notext=true})) | table.insert(rowTxt, Icons.Icon({tierPot.name, type='item', notext=true})) | ||
table.insert(rowTxt, Icons.Icon({tierPot.name, tierSuffix[j], type = 'item', noicon=true})) | table.insert(rowTxt, Icons.Icon({tierPot.name, tierSuffix[j], type = 'item', noicon=true})) | ||
table.insert(rowTxt, '||style="text-align:right;" data-sort-value="'..tierPot.sellsFor..'"|'..Icons.GP(tierPot.sellsFor)) | table.insert(rowTxt, '||style="text-align:right;" data-sort-value="'..tierPot.sellsFor..'"|'..Icons.GP(tierPot.sellsFor)) | ||
table.insert(rowTxt, '||style="text-align:right;"|'..tierPot.charges..'|| '.. | table.insert(rowTxt, '||style="text-align:right;"|'..tierPot.charges..'|| '..p._getPotionDescription(tierPot)) | ||
table.insert(tierRows, table.concat(rowTxt)) | table.insert(tierRows, table.concat(rowTxt)) | ||
end | end | ||
Line 155: | Line 160: | ||
local category = frame.args ~= nil and frame.args[1] or frame | local category = frame.args ~= nil and frame.args[1] or frame | ||
return p._getHerblorePotionTable(category) | return p._getHerblorePotionTable(category) | ||
end | |||
function p._getHerbloreHerbTable(args) | |||
local allHerbs = {} | |||
local allPotions = GameData.getEntities(SkillData.Herblore.recipes, function() return true end) | |||
-- Finds the herb from a potion along with the level required to make the potion. | |||
local function handlePotion(potion) | |||
local potionCosts = potion.itemCosts | |||
local level = potion.level | |||
if potionCosts == nil or level == nil then | |||
return | |||
end | |||
-- Find if this potion uses a herb, and which herb it is. | |||
for _, ingredient in pairs(potionCosts) do | |||
local ingredientID = ingredient.id | |||
if ingredientID == nil or string.sub(ingredientID, -5) ~= "_Herb" then | |||
return | |||
end | |||
-- Set the lowest level of potion this herb is used in. | |||
local currLevel = allHerbs[ingredientID] or 9999999 | |||
if level < currLevel then | |||
allHerbs[ingredientID] = level | |||
end | |||
end | |||
end | |||
for _, potion in pairs(allPotions) do | |||
handlePotion(potion) | |||
end | |||
local sortedValues = Shared.sortDictionary( | |||
allHerbs, | |||
function (a, b) return a.value < b.value end) | |||
local tbl = mw.html.create("table") | |||
:addClass("wikitable sortable stickyHeader") | |||
-- Add header | |||
tbl :tag("tr"):addClass("headerRow-0") | |||
:tag("th"):wikitext(Icons.Icon({'Herblore', type='skill', notext=true})..' Level') | |||
:tag("th"):wikitext("Herb") | |||
:tag("th"):wikitext("Value") | |||
:tag("th"):wikitext("Herb Sources") | |||
:done() | |||
-- Fill wikitable. | |||
for _, v in pairs(sortedValues) do | |||
local herbItem = Items.getItemByID(v['key']) | |||
local herbLevel = v['value'] | |||
local dlcIcon = Icons.getExpansionIcon(herbItem.id) | |||
-- Add rows | |||
tbl :tag("tr") | |||
:tag("td"):wikitext(herbLevel) | |||
:tag("td"):wikitext(dlcIcon .. Icons.Icon({herbItem.name, type='item'})) | |||
:tag("td"):wikitext(Icons.GP(herbItem.sellsFor)) | |||
:tag('td'):wikitext(ItemSourceTables._getItemSources(herbItem, false, nil, ' ')) | |||
:done() | |||
end | |||
return tostring(tbl) | |||
end | |||
function p.getHerbloreHerbTable(frame) | |||
local args = frame:getParent().args | |||
return p._getHerbloreHerbTable(args) | |||
end | end | ||
Line 162: | Line 237: | ||
local recipe = GameData.getEntityByName(SkillData.Herblore.recipes, potionName) | local recipe = GameData.getEntityByName(SkillData.Herblore.recipes, potionName) | ||
if recipe == nil then | if recipe == nil then | ||
return ' | return Shared.printError('No potion named "' .. potionName .. '" was found') | ||
end | end | ||
local resultPart = {} | local resultPart = {} | ||
table.insert(resultPart, '{| class="wikitable"') | table.insert(resultPart, '{| class="wikitable"') | ||
table.insert(resultPart, '\r\n!Potion!!Tier!!Charges!!Effect') | table.insert(resultPart, '\r\n!colspan=4|[['..potionName..']]') | ||
table.insert(resultPart, '\r\n|-\r\n!Potion!!Tier!!Charges!!Effect') | |||
for i, potionID in ipairs(recipe.potionIDs) do | for i, potionID in ipairs(recipe.potionIDs) do | ||
Line 173: | Line 249: | ||
local potion = Items.getItemByID(potionID) | local potion = Items.getItemByID(potionID) | ||
if potion ~= nil then | if potion ~= nil then | ||
table.insert(resultPart, '\r\n|-') | table.insert(resultPart, '\r\n|-') | ||
table.insert(resultPart, '\r\n| ' .. Icons.Icon({potion.name, type='item', notext=true, size='60'})) | table.insert(resultPart, '\r\n| ' .. Icons.Icon({potion.name, type='item', notext=true, size='60'})) | ||
table.insert(resultPart, '|| ' .. Icons.getExpansionIcon(potion.id) .. Icons.Icon({potion.name, tier, type='item', noicon=true})) | table.insert(resultPart, '|| ' .. Icons.getExpansionIcon(potion.id) .. Icons.Icon({potion.name, tier, type='item', noicon=true})) | ||
table.insert(resultPart, '|| ' .. potion.charges .. '|| ' .. | table.insert(resultPart, '|| ' .. potion.charges .. '|| ' .. p._getPotionDescription(potion)) | ||
end | end | ||
end | end | ||
Line 190: | Line 262: | ||
function p.getRunecraftingTable(frame) | function p.getRunecraftingTable(frame) | ||
local category = frame.args ~= nil and frame.args[1] or frame | local category = frame.args ~= nil and frame.args[1] or frame | ||
return p._getRecipeTable('Runecrafting', category, {' | return p._getRecipeTable('Runecrafting', category, {'ItemImage', 'ItemName', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients', 'SkillXPSec', 'GPSec'}) | ||
end | end | ||
function p.getFletchingTable(frame) | function p.getFletchingTable(frame) | ||
local category = frame.args ~= nil and frame.args[1] or frame | local category = frame.args ~= nil and frame.args[1] or frame | ||
return p._getRecipeTable('Fletching', category, {' | return p._getRecipeTable('Fletching', category, {'ItemImage', 'ItemName', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients'}) | ||
end | end | ||
function p.getCraftingTable(frame) | function p.getCraftingTable(frame) | ||
local category = frame.args ~= nil and frame.args[1] or frame | local category = frame.args ~= nil and frame.args[1] or frame | ||
return p._getRecipeTable('Crafting', category, {' | local columns = {'ItemImage', 'ItemName', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients'} | ||
if category == 'Rings' or category == 'Necklaces' then | |||
table.insert(columns, "Description") | |||
end | |||
return p._getRecipeTable('Crafting', category, columns) | |||
end | |||
function p.getSmithingTable(frame) | |||
local category = frame.args ~= nil and frame.args[1] or frame | |||
local columns = {'ItemImage', 'ItemName', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients'} | |||
if category ~= 'Bars' then | |||
table.insert(columns, 'GPBar') | |||
end | |||
return p._getRecipeTable('Smithing', category, columns) | |||
end | end | ||
Line 211: | Line 296: | ||
-- Validation: Parameters | -- Validation: Parameters | ||
if type(skillName) ~= 'string' then | if type(skillName) ~= 'string' then | ||
return ' | return Shared.printError('skillName must be a string') | ||
elseif not Shared.contains({'string', 'nil'}, type(categoryName)) then | elseif not Shared.contains({'string', 'nil'}, type(categoryName)) then | ||
return ' | return Shared.printError('category must be a string or nil') | ||
elseif type(columnList) ~= 'table' or Shared.tableIsEmpty(columnList) then | elseif type(columnList) ~= 'table' or Shared.tableIsEmpty(columnList) then | ||
return ' | return Shared.printError('columnList must be a table with 1+ elements') | ||
end | end | ||
local supportedSkills = { | local supportedSkills = { | ||
'Smithing', | |||
'Crafting', | 'Crafting', | ||
'Fletching', | 'Fletching', | ||
Line 224: | Line 310: | ||
} | } | ||
if not Shared.contains(supportedSkills, skillName) then | if not Shared.contains(supportedSkills, skillName) then | ||
return ' | return Shared.printError('The ' .. skillName .. ' skill is not supported by this function') | ||
end | end | ||
Line 234: | Line 320: | ||
table.insert(catNames, cat.name) | table.insert(catNames, cat.name) | ||
end | end | ||
return ' | return Shared.printError('No such category ' .. categoryName .. ' for skill ' .. skillName .. ', the following are available: ' .. table.concat(catNames, ', ')) | ||
end | end | ||
local actionInterval = SkillData[skillName].baseInterval | local actionInterval = SkillData[skillName].baseInterval / 1000 | ||
-- Validation: Skill data | -- Validation: Skill data | ||
local recipeKey = 'recipes' | local recipeKey = 'recipes' | ||
if SkillData[skillName] == nil then | if SkillData[skillName] == nil then | ||
return ' | return Shared.printError('Could not locate skill data for ' .. skillName) | ||
elseif SkillData[skillName][recipeKey] == nil then | elseif SkillData[skillName][recipeKey] == nil then | ||
return ' | return Shared.printError('Could not locate recipe data for ' .. skillName) | ||
end | end | ||
-- Validation: Column list | -- Validation: Column list | ||
local columnDef = { | local columnDef = { | ||
["Item"] = {["header"] = ' | ["ItemImage"] = {["header"] = 'Item', ["altRepeat"] = false}, | ||
["ItemName"] = {["header"] = 'Name', ["altRepeat"] = true}, | |||
["SkillLevel"] = {["header"] = Icons.Icon({skillName, type='skill', notext=true}) .. ' Level', ["altRepeat"] = false}, | ["SkillLevel"] = {["header"] = Icons.Icon({skillName, type='skill', notext=true}) .. ' Level', ["altRepeat"] = false}, | ||
["SkillXP"] = {["header"] = 'XP', altRepeat = false}, | ["SkillXP"] = {["header"] = 'XP', ["altRepeat"] = false}, | ||
["GP"] = {["header"] = 'Value', ["altRepeat"] = true}, | ["GP"] = {["header"] = 'Value', ["altRepeat"] = true}, | ||
["Ingredients"] = {["header"] = 'Ingredients', ["altRepeat"] = true}, | ["Ingredients"] = {["header"] = 'Ingredients', ["altRepeat"] = true}, | ||
["SkillXPSec"] = {["header"] = 'XP/s', ["altRepeat"] = false}, | ["SkillXPSec"] = {["header"] = 'XP/s', ["altRepeat"] = false}, | ||
["GPSec"] = {["header"] = 'GP/s', ["altRepeat"] = true} | ["GPSec"] = {["header"] = 'GP/s', ["altRepeat"] = true}, | ||
["GPBar"] = {["header"] = 'Value/Bar', ["altRepeat"] = true }, | |||
["Description"] = {["header"] = "Description", ["altRepeat"] = true} | |||
} | } | ||
-- Build the table header while we're here | -- Build the table header while we're here | ||
local resultPart = {} | local resultPart, barIDList = {}, {} | ||
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|- class="headerRow-0"') | table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|- class="headerRow-0"') | ||
for i, colID in ipairs(columnList) do | for i, colID in ipairs(columnList) do | ||
if columnDef[colID] == nil then | if columnDef[colID] == nil then | ||
return ' | return Shared.printError('Invalid column ' .. colID .. ' requested') | ||
else | else | ||
table.insert(resultPart, '\r\n! ' .. columnDef[colID].header) | table.insert(resultPart, '\r\n! ' .. columnDef[colID].header) | ||
if colID == 'GPBar' then | |||
-- For Smithing, a GP value per bar column is included. If this | |||
-- is requested, then obtain a list of bar item IDs | |||
barIDList = p.getBarItemIDs() | |||
end | |||
end | end | ||
end | end | ||
Line 294: | Line 388: | ||
local rowspanStr = (recipeRow == 1 and costCount > 1 and 'rowspan="' .. costCount .. '" ') or '' | local rowspanStr = (recipeRow == 1 and costCount > 1 and 'rowspan="' .. costCount .. '" ') or '' | ||
local qty = (costDef.quantityMultiplier or 1) * (recipe.baseQuantity or 1) | local qty = (costDef.quantityMultiplier or 1) * (recipe.baseQuantity or 1) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|-') | ||
for j, colID in ipairs(columnList) do | for j, colID in ipairs(columnList) do | ||
local altRepeat = columnDef[colID].altRepeat | local altRepeat = columnDef[colID].altRepeat | ||
Line 301: | Line 395: | ||
if recipeRow == 1 or altRepeat then | if recipeRow == 1 or altRepeat then | ||
local spanStr = (not altRepeat and rowspanStr) or '' | local spanStr = (not altRepeat and rowspanStr) or '' | ||
if colID == ' | if colID == 'ItemImage' then | ||
table.insert(resultPart, '\n|' .. spanStr .. 'class="table-img"| ' .. Icons.Icon({item.name, type='item', size='50', notext=true})) | |||
elseif colID == "ItemName" then | |||
local namePrefix = spanStr | local namePrefix = spanStr | ||
if qty > 1 then | if qty > 1 then | ||
namePrefix = namePrefix .. 'data-sort-value="' .. item.name .. '"' | namePrefix = namePrefix .. 'data-sort-value="' .. item.name .. '"' | ||
end | end | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|'.. (namePrefix ~= '' and namePrefix .. '| ' or ' ') .. Icons.getExpansionIcon(item.id) .. (qty > 1 and '<b>' .. qty .. 'x</b> ' or '') .. Icons.Icon({item.name, type='item', noicon=true})) | ||
elseif colID == 'SkillLevel' then | elseif colID == 'SkillLevel' then | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.level) | ||
elseif colID == 'SkillXP' then | elseif colID == 'SkillXP' then | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|' .. spanStr .. 'data-sort-value="' .. recipe.baseExperience ..'" style="text-align:right"| ' .. Shared.formatnum(recipe.baseExperience)) | ||
elseif colID == 'GP' then | elseif colID == 'GP' then | ||
local val = math.floor(item.sellsFor) | local val = math.floor(item.sellsFor) | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|' .. spanStr .. 'data-sort-value="' .. (val * qty) .. '"| ' .. Icons.GP(val) .. (qty > 1 and ' (x' .. qty .. ')' or '')) | ||
elseif colID == 'Ingredients' then | elseif colID == 'Ingredients' then | ||
local matArray = {} | local matArray = {} | ||
Line 329: | Line 424: | ||
table.insert(matArray, Icons.SC(recipe.scCost)) | table.insert(matArray, Icons.SC(recipe.scCost)) | ||
end | end | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|' .. (spanStr ~= '' and spanStr .. '| ' or ' ') .. table.concat(matArray, ', ')) | ||
elseif colID == 'SkillXPSec' then | elseif colID == 'SkillXPSec' then | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|' .. spanStr .. 'style="text-align:right"| ' .. string.format('%.2f', recipe.baseExperience / actionInterval)) | ||
elseif colID == 'GPSec' then | elseif colID == 'GPSec' then | ||
local val = math.floor(item.sellsFor) * qty / actionInterval | local val = math.floor(item.sellsFor) * qty / actionInterval | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|' .. spanStr .. 'data-sort-value="' .. val .. '"| ' .. Icons.GP(string.format('%.2f', val))) | ||
elseif colID == 'GPBar' then | |||
local barQty = 0 | |||
for k, mat in ipairs(costDef.itemCosts) do | |||
if Shared.contains(barIDList, mat.id) then | |||
barQty = barQty + mat.quantity | |||
end | |||
end | |||
if barQty > 0 then | |||
local barVal = Shared.round(math.floor(item.sellsFor) * qty / barQty, 1, 1) | |||
table.insert(resultPart, '\n|' .. spanStr .. 'data-sort-value="' .. barVal .. '"| ' .. Icons.GP(barVal)) | |||
else | |||
table.insert(resultPart, '\n|' .. spanStr .. 'data-sort-value="0" class="table-na"| N/A') | |||
end | |||
elseif colID == 'Description' then | |||
local descrip = Items._getItemStat(item, 'description') | |||
if descrip == 'No Description' and item.modifiers ~= nil and not Shared.tableIsEmpty(item.modifiers) then | |||
descrip = Constants.getModifiersText(item.modifiers, false) | |||
end | |||
table.insert(resultPart, '\n| '..spanStr..'|'..descrip) | |||
else | else | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n| ') | ||
end | end | ||
end | end | ||
Line 343: | Line 457: | ||
end | end | ||
end | end | ||
table.insert(resultPart, ' | table.insert(resultPart, '\n|}') | ||
return table.concat(resultPart) | return table.concat(resultPart) | ||
end | |||
function p.getBarItemIDs() | |||
local barIDList = {} | |||
for i, recipe in ipairs(SkillData.Smithing.recipes) do | |||
if recipe.categoryID == 'melvorD:Bars' then | |||
table.insert(barIDList, recipe.productID) | |||
end | |||
end | |||
return barIDList | |||
end | end | ||
return p | return p |
edits