Module:Skills/Artisan: Difference between revisions

Update for v1.1
(_getRecipeTable: Support GP & SC costs)
(Update for v1.1)
Line 2: Line 2:
--Contains function for skills that consume resources (ie smithing, cooking, herblore, etc.)
--Contains function for skills that consume resources (ie smithing, cooking, herblore, etc.)
local p = {}
local p = {}
local SkillData = mw.loadData('Module:Skills/data')


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Items = require('Module:Items')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
Line 12: Line 13:
local category = frame.args ~= nil and frame.args[1] or frame
local category = frame.args ~= nil and frame.args[1] or frame
local categoryMap = {
local categoryMap = {
["Cooking Fire"] = 0,
["Cooking Fire"] = 'melvorD:Fire',
["Furnace"] = 1,
["Furnace"] = 'melvorD:Furnace',
["Pot"] = 2
["Pot"] = 'melvorD:Pot'
}
}
local categoryID = categoryMap[category]
local categoryID = categoryMap[category]
local recipeArray = {}


-- Find recipes for the relevant categories
-- Find recipes for the relevant categories
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
-- Note: Excludes Lemon cake
-- Note: Excludes Lemon cake
local recipeArray = GameData.getEntities(SkillData.Cooking.recipes,
if (categoryID == nil or recipe.category == categoryID) and recipe.masteryID >= 0 then
function(recipe)
table.insert(recipeArray, recipe)
return (categoryID == nil or recipe.category == categoryID) and recipe.noMastery == nil
end
end)
end
table.sort(recipeArray, function(a, b) return a.level < b.level end)
table.sort(recipeArray, function(a, b) return (a.level == b.level and a.masteryID < b.masteryID) or a.level < b.level end)


-- Logic for generating some cells of the table which are consistent for normal & perfect items
-- Logic for generating some cells of the table which are consistent for normal & perfect items
Line 38: Line 37:
local getSaleValueCell = function(item, qty)
local getSaleValueCell = function(item, qty)
if item ~= nil then
if item ~= nil then
return 'data-sort-value="'..(math.floor(item.sellsFor) * qty)..'"|'..Icons.GP(math.floor(item.sellsFor))..(qty > 1 and ' (x'..qty..')' or '')
return 'data-sort-value="'..math.floor(item.sellsFor * qty)..'"|'..Icons.GP(math.floor(item.sellsFor))..(qty > 1 and ' (x'..qty..')' or '')
else
else
return ' '
return ' '
Line 54: Line 53:


for i, recipe in ipairs(recipeArray) do
for i, recipe in ipairs(recipeArray) do
local item = Items.getItemByID(recipe.itemID)
local item = Items.getItemByID(recipe.productID)
local perfectItem = nil
local perfectItem = nil
if recipe.perfectCookID ~= nil then
if recipe.perfectCookID ~= nil then
Line 74: Line 73:
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.timeString(recipe.baseInterval / 1000, true))
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseInterval .. '"|' .. Shared.timeString(recipe.baseInterval / 1000, true))
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseXP .. '"|' .. Shared.formatnum(recipe.baseXP))
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"|' .. Shared.formatnum(recipe.baseExperience))
table.insert(resultPart, '||'..getHealingCell(item, qty)..'||'..getHealingCell(perfectItem, qty))
table.insert(resultPart, '||'..getHealingCell(item, qty)..'||'..getHealingCell(perfectItem, qty))
table.insert(resultPart, '||'..getSaleValueCell(item, qty)..'||'..getSaleValueCell(perfectItem, qty))
table.insert(resultPart, '||'..getSaleValueCell(item, qty)..'||'..getSaleValueCell(perfectItem, qty))
Line 81: Line 80:
local matItem = Items.getItemByID(mat.id)
local matItem = Items.getItemByID(mat.id)
if matItem ~= nil then
if matItem ~= nil then
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.quantity}))
end
end
end
end
Line 92: Line 91:


local tierSuffix = { 'I', 'II', 'III', 'IV' }
local tierSuffix = { 'I', 'II', 'III', 'IV' }
function p._getHerblorePotionTable(category)
function p._getHerblorePotionTable(categoryName)
if string.upper(category) == 'COMBAT' then
local categoryID = nil
category = 0
if string.upper(categoryName) == 'COMBAT' then
elseif string.upper(category) == 'SKILL' then
categoryID = 'melvorF:CombatPotions'
category = 1
elseif string.upper(categoryName) == 'SKILL' then
elseif type(category) == 'string' then
categoryID = 'melvorF:SkillPotions'
category = tonumber(category)
else
return 'ERROR: No such potion category ' .. (categoryName or 'nil') .. '[[Category:Pages with script errors]]'
end
end


local potionArray = {}
local potionArray = GameData.getEntities(SkillData.Herblore.recipes, function(potion) return potion.categoryID == categoryID end)
for i, potion in Shared.skpairs(SkillData.Herblore.Potions) do
table.sort(potionArray, function(a, b) return a.level < b.level end)
if potion.category == category then
table.insert(potionArray, potion)
end
end


local resultPart = {}
local resultPart = {}
Line 113: Line 109:
table.insert(resultPart, '\r\n!Potion!!'..Icons.Icon({'Herblore', type='skill', notext=true})..' Level')
table.insert(resultPart, '\r\n!Potion!!'..Icons.Icon({'Herblore', type='skill', notext=true})..' Level')
table.insert(resultPart, '!!XP!!Ingredients!!style="width:30px;"|Tier!!Value!!Charges!!Effect')
table.insert(resultPart, '!!XP!!Ingredients!!style="width:30px;"|Tier!!Value!!Charges!!Effect')
table.sort(potionArray, function(a, b) return a.level < b.level end)


for i, potion in ipairs(potionArray) do
for i, potion in ipairs(potionArray) do
Line 124: Line 118:
end
end
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.baseXP)
table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.baseExperience)


local matArray = {}
local matArray = {}
for j, mat in ipairs(potion.itemCosts) do
for j, mat in ipairs(potion.itemCosts) do
local matItem = Items.getItemByID(mat.id)
local matItem = Items.getItemByID(mat.id)
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.quantity}))
end
end
table.insert(resultPart, '||rowspan="4"|'..table.concat(matArray, ', ')..'||')
table.insert(resultPart, '||rowspan="4"|'..table.concat(matArray, ', ')..'||')
Line 137: Line 131:
local rowTxt = {}
local rowTxt = {}
local tierPot = Items.getItemByID(potionID)
local tierPot = Items.getItemByID(potionID)
local potDesc = ''
if type(tierPot.modifiers) == 'table' and not Shared.tableIsEmpty(tierPot.modifiers) then
potDesc = Constants.getModifiersText(tierPot.modifiers, false) or ''
end
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.potionCharges..'||'..tierPot.description)
table.insert(rowTxt, '||style="text-align:right;"|'..tierPot.charges..'|| '..potDesc)
table.insert(tierRows, table.concat(rowTxt))
table.insert(tierRows, table.concat(rowTxt))
end
end
Line 181: Line 179:
elseif not Shared.contains({'string', 'nil'}, type(categoryName)) then
elseif not Shared.contains({'string', 'nil'}, type(categoryName)) then
return 'ERROR: category must be a string or nil'
return 'ERROR: category must be a string or nil'
elseif type(columnList) ~= 'table' or Shared.tableCount(columnList) == 0 then
elseif type(columnList) ~= 'table' or Shared.tableIsEmpty(columnList) then
return 'ERROR: columnList must be a table with 1+ elements'
return 'ERROR: columnList must be a table with 1+ elements'
end
end


local supportedSkills = {
'Crafting',
'Fletching',
'Runecrafting'
}
if not Shared.contains(supportedSkills, skillName) then
return 'ERROR: The ' .. skillName .. ' skill is not supported by this function'
end
local categoryMap = {
local categoryMap = {
["Runecrafting"] = {
["Runecrafting"] = {
Line 217: Line 223:
}
}
-- Validation: Category
-- Validation: Category
if categoryMap[skillName] == nil then
local category = GameData.getEntityByName(SkillData[skillName].categories, categoryName)
return 'ERROR: The ' .. skillName .. ' skill is not supported by this function'
if category == nil then
elseif categoryName ~= '' and categoryMap[skillName][categoryName] == nil then
local catNames = {}
local catNames = {}
for catName, catID in pairs(categoryMap[skillName]) do
for i, cat in pairs(SkillData[skillName].categories) do
table.insert(catNames, catName)
table.insert(catNames, cat.name)
end
end
return 'ERROR: No such category ' .. categoryName .. ' for skill ' .. skillName .. ', the following are available: ' .. table.concat(catNames, ', ')
return 'ERROR: No such category ' .. categoryName .. ' for skill ' .. skillName .. ', the following are available: ' .. table.concat(catNames, ', ')
end
end
local categoryID = categoryMap[skillName][categoryName]
local actionInterval = SkillData[skillName].baseInterval
local actionInterval = actionIntervals[skillName]


-- Validation: Skill data
-- Validation: Skill data
local recipeKey = 'Recipes'
local recipeKey = 'recipes'
if SkillData[skillName] == nil then
if SkillData[skillName] == nil then
return 'ERROR: Could not locate skill data for ' .. skillName
return 'ERROR: Could not locate skill data for ' .. skillName
Line 259: Line 263:


-- Determine which recipes to include
-- Determine which recipes to include
local recipeList = {}
local recipeList = GameData.getEntities(SkillData[skillName][recipeKey],
for i, recipe in ipairs(SkillData[skillName][recipeKey]) do
function(recipe)
if categoryID == nil or recipe.category == categoryID then
return category.id == nil or recipe.category == category.id
table.insert(recipeList, recipe)
end)
end
if Shared.tableIsEmpty(recipeList) then
end
if Shared.tableCount(recipeList) == 0 then
return ''
return ''
end
end
table.sort(recipeList, function(a, b) return (a.level == b.level and a.masteryID < b.masteryID) or a.level < b.level end)
table.sort(recipeList, function(a, b) return a.level < b.level end)


-- Build rows based on recipes
-- Build rows based on recipes
for i, recipe in ipairs(recipeList) do
for i, recipe in ipairs(recipeList) do
local item = Items.getItemByID(recipe.itemID)
local item = Items.getItemByID(recipe.productID)
if item ~= nil then
if item ~= nil then
-- Some recipes have alternative costs, so the recipe may require multiple rows
-- Some recipes have alternative costs, so the recipe may require multiple rows
local costList = nil
local costList = nil
if recipe.alternativeCosts ~= nil and Shared.tableCount(recipe.alternativeCosts) > 0 then
if recipe.alternativeCosts ~= nil and not Shared.tableIsEmpty(recipe.alternativeCosts) then
costList = recipe.alternativeCosts
costList = recipe.alternativeCosts
else
else
Line 303: Line 305:
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.level)
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.level)
elseif colID == 'SkillXP' then
elseif colID == 'SkillXP' then
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.baseXP)
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.baseExperience)
elseif colID == 'GP' then
elseif colID == 'GP' then
local val = math.floor(item.sellsFor)
local val = math.floor(item.sellsFor)
Line 312: Line 314:
local matItem = Items.getItemByID(mat.id)
local matItem = Items.getItemByID(mat.id)
if matItem ~= nil then
if matItem ~= nil then
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.quantity}))
end
end
end
end
Line 319: Line 321:
end
end
if recipe.scCost ~= nil and recipe.scCost > 0 then
if recipe.scCost ~= nil and recipe.scCost > 0 then
table.insert(matArray, Icons.SC(recipe.gpCost))
table.insert(matArray, Icons.SC(recipe.scCost))
end
end
table.insert(resultPart, '\r\n|' .. (spanStr ~= '' and spanStr .. '| ' or ' ') .. table.concat(matArray, ', '))
table.insert(resultPart, '\r\n|' .. (spanStr ~= '' and spanStr .. '| ' or ' ') .. table.concat(matArray, ', '))
elseif colID == 'SkillXPSec' then
elseif colID == 'SkillXPSec' then
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. string.format('%.2f', recipe.baseXP / actionInterval))
table.insert(resultPart, '\r\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