Anonymous

Module:Skills/Artisan: Difference between revisions

From Melvor Idle
_getRecipeTable: Support GP & SC costs
(getCookingFireTable: Remove, replaced by getCookingUtilityTable in Module:Shop)
(_getRecipeTable: Support GP & SC costs)
(11 intermediate revisions by 3 users not shown)
Line 11: Line 11:
function p.getCookedItemsTable(frame)
function p.getCookedItemsTable(frame)
local category = frame.args ~= nil and frame.args[1] or frame
local category = frame.args ~= nil and frame.args[1] or frame
local itemArray = nil
local categoryMap = {
["Cooking Fire"] = 0,
if category == "Cooking Fire" then
["Furnace"] = 1,
itemArray = Items.getItems(function(item) return item.cookingCategory == 0 end)
["Pot"] = 2
elseif category == "Furnace" then
}
itemArray = Items.getItems(function(item) return item.cookingCategory == 1 and item.name ~= 'Lemon Cake' end)
local categoryID = categoryMap[category]
elseif category == "Pot"  then
local recipeArray = {}
itemArray = Items.getItems(function(item) return item.cookingCategory == 2 end)
 
else
-- Find recipes for the relevant categories
itemArray = Items.getItems(function(item) return item.cookingCategory ~= nil and item.name ~= 'Lemon Cake' end)
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
-- Note: Excludes Lemon cake
if (categoryID == nil or recipe.category == categoryID) and recipe.masteryID >= 0 then
table.insert(recipeArray, recipe)
end
end
end
table.sort(itemArray, function(a, b) return a.cookingLevel < b.cookingLevel 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
local getHealingCell = function(item)
local getHealingCell = function(item, qty)
if item ~= nil then
if item ~= nil then
return 'style="text-align:right" data-sort-value="'..math.floor(item.healsFor)..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..math.floor(item.healsFor * 10)
return 'data-sort-value="'..(math.floor(item.healsFor) * qty)..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..math.floor(item.healsFor * 10)..(qty > 1 and ' (x'..qty..')' or '')
else
else
return ' '
return ' '
Line 39: Line 43:
end
end
end
end
 
local result = '{| class="wikitable sortable stickyHeader"'
local resultPart = {}
result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
result = result..'\r\n!colspan="3" rowspan="2"|Cooked Item!!rowspan="2"|'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level'
table.insert(resultPart, '\r\n|- class="headerRow-0"')
result = result..'!!rowspan="2"|Cook Time!!rowspan="2"|XP!!colspan="2"|Healing!!colspan="2"|Value!!rowspan="2"|Ingredients'
table.insert(resultPart, '\r\n!colspan="3" rowspan="2"|Cooked Item!!rowspan="2"|'..Icons.Icon({'Cooking', type='skill', notext=true})..' Level')
result = result..'\r\n|- class="headerRow-1"'
table.insert(resultPart, '!!rowspan="2"|Cook Time!!rowspan="2"|XP!!colspan="2"|Healing!!colspan="2"|Value!!rowspan="2"|Ingredients')
result = result..'\r\n!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true}) .. '!!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true})
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}))
for i, item in Shared.skpairs(itemArray) do
table.insert(resultPart, '!!Normal!!' .. Icons.Icon({'Perfect', type='bonus', ext='png', notext=true, nolink=true}))
 
for i, recipe in ipairs(recipeArray) do
local item = Items.getItemByID(recipe.itemID)
local perfectItem = nil
local perfectItem = nil
if item.perfectItem ~= nil then
if recipe.perfectCookID ~= nil then
perfectItem = Items.getItemByID(item.perfectItem)
perfectItem = Items.getItemByID(recipe.perfectCookID)
end
end
local qty = item.cookingQty
local qty = recipe.baseQuantity or 1
if qty == nil then
 
qty = 1
table.insert(resultPart, '\r\n|-')
end
table.insert(resultPart, '\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext=true, size='50'}))
result = result..'\r\n|-'
table.insert(resultPart, '\r\n|style="min-width:25px"| ')
result = result..'\r\n|style="min-width:25px"|'..Icons.Icon({item.name, type='item', notext='true', size='50'})
result = result..'\r\n|style="min-width:25px"| '
if perfectItem ~= nil then
if perfectItem ~= nil then
result = result..Icons.Icon({perfectItem.name, type='item', notext='true', size='50'})
table.insert(resultPart, Icons.Icon({perfectItem.name, type='item', notext=true, size='50'}))
end
end
result = result..'||'
table.insert(resultPart, '||')
if qty > 1 then
if qty > 1 then
result = result..qty..'x '
table.insert(resultPart, qty..'x ')
end
end
result = result..'[['..item.name..']]'
table.insert(resultPart, Icons.Icon({item.name, type='item', noicon = true}))
result = result..'||style="text-align:right"|'..item.cookingLevel
table.insert(resultPart, '||style="text-align:right"|' .. recipe.level)
result = result..'||style="text-align:right" data-sort-value="' .. item.cookingInterval .. '"|'..Shared.timeString(item.cookingInterval / 1000, true)
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseInterval .. '"|' .. Shared.timeString(recipe.baseInterval / 1000, true))
result = result..'||style="text-align:right"|'..item.cookingXP
table.insert(resultPart, '||style="text-align:right" data-sort-value="' .. recipe.baseXP .. '"|' .. Shared.formatnum(recipe.baseXP))
result = result..'||'..getHealingCell(item)..'||'..getHealingCell(perfectItem)
table.insert(resultPart, '||'..getHealingCell(item, qty)..'||'..getHealingCell(perfectItem, qty))
result = result..'||'..getSaleValueCell(item, qty)..'||'..getSaleValueCell(perfectItem, qty)
table.insert(resultPart, '||'..getSaleValueCell(item, qty)..'||'..getSaleValueCell(perfectItem, qty))
    local matArray = {}
local matArray = {}
    for j, reqSet in pairs(item.recipeRequirements) do
for j, mat in ipairs(recipe.itemCosts) do
    for k, mat in pairs(reqSet) do
local matItem = Items.getItemByID(mat.id)
local matItem = Items.getItemByID(mat.id)
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.qty}))
    end
end
end
end
    result = result..'\r\n|'..table.concat(matArray, ' ')
table.insert(resultPart, '\r\n|'..table.concat(matArray, ' '))
end
end


result = result..'\r\n|}'
table.insert(resultPart, '\r\n|}')
return result
return table.concat(resultPart)
end
end


local tierSuffix = { 'I', 'II', 'III', 'IV' }
local tierSuffix = { 'I', 'II', 'III', 'IV' }
function p._getHerblorePotionTable(category)
function p._getHerblorePotionTable(category)
  if string.upper(category) == 'COMBAT' then
if string.upper(category) == 'COMBAT' then
    category = 0
category = 0
  elseif string.upper(category) == 'SKILL' then
elseif string.upper(category) == 'SKILL' then
    category = 1
category = 1
  elseif type(category) == 'string' then
elseif type(category) == 'string' then
    category = tonumber(category)
category = tonumber(category)
  end
end


  local potionArray = {}
local potionArray = {}
  for i, potion in Shared.skpairs(SkillData.Herblore.ItemData) do
for i, potion in Shared.skpairs(SkillData.Herblore.Potions) do
    if potion.category == category then
if potion.category == category then
      table.insert(potionArray, potion)
table.insert(potionArray, potion)
    end
end
  end
end


  local result = '{|class = "wikitable sortable stickyHeader"'
local resultPart = {}
  result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
  result = result..'\r\n!Potion!!'..Icons.Icon({'Herblore', type='skill', notext=true})..' Level'
table.insert(resultPart, '\r\n|- class="headerRow-0"')
  result = result..'!!XP!!Ingredients!!colspan="2"|Tier!!Value!!Charges!!Effect'
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.sort(potionArray, function(a, b) return a.level < b.level end)
table.sort(potionArray, function(a, b) return a.level < b.level end)


  for i, potion in Shared.skpairs(potionArray) do
for i, potion in ipairs(potionArray) do
    local tierPots = {}
table.insert(resultPart, '\r\n|-')
    for j = 1, 4, 1 do
if potion.name == 'Bird Nests Potion' then
      table.insert(tierPots, Items.getItemByID(potion.itemID[j]))
table.insert(resultPart, '\r\n|rowspan="4"|[[Bird Nest Potion]]')
    end
else
    result = result..'\r\n|-'
table.insert(resultPart, '\r\n|rowspan="4"|[['..potion.name..']]')
    if potion.name == 'Bird Nests Potion' then
end
      result = result..'\r\n|rowspan="4"|[[Bird Nest Potion]]'
table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.level)
    else
table.insert(resultPart, '||rowspan="4" style="text-align:right"|'..potion.baseXP)
      result = result..'\r\n|rowspan="4"|[['..potion.name..']]'
    end
    result = result..'||rowspan="4" style="text-align:right"|'..potion.level
    result = result..'||rowspan="4" style="text-align:right"|'..potion.herbloreXP


    local matArray = {}
local matArray = {}
    for j, mat in Shared.skpairs(tierPots[1].herbloreReq) 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.qty}))
    end
end
    result = result..'||rowspan="4"|'..table.concat(matArray, ', ')..'||'
table.insert(resultPart, '||rowspan="4"|'..table.concat(matArray, ', ')..'||')
   
 
    local tierRows = {}
local tierRows = {}
    for j, tierPot in Shared.skpairs(tierPots) do
for j, potionID in ipairs(potion.potionIDs) do
      local rowTxt = Icons.Icon({tierPot.name, type='item', notext=true})
local rowTxt = {}
      rowTxt = rowTxt..'||[['..tierPot.name..'|'..tierSuffix[j]..']]'
local tierPot = Items.getItemByID(potionID)
      rowTxt = rowTxt..'||style="text-align:right;" data-sort-value="'..tierPot.sellsFor..'"|'..Icons.GP(tierPot.sellsFor)
table.insert(rowTxt, Icons.Icon({tierPot.name, type='item', notext=true}))
      rowTxt = rowTxt..'||style="text-align:right;"|'..tierPot.potionCharges..'||'..tierPot.description
table.insert(rowTxt, Icons.Icon({tierPot.name, tierSuffix[j], type = 'item', noicon = true}))
      table.insert(tierRows, rowTxt)
table.insert(rowTxt, '||style="text-align:right;" data-sort-value="'..tierPot.sellsFor..'"|'..Icons.GP(tierPot.sellsFor))
    end
table.insert(rowTxt, '||style="text-align:right;"|'..tierPot.potionCharges..'||'..tierPot.description)
    result = result..table.concat(tierRows, '\r\n|-\r\n|')
table.insert(tierRows, table.concat(rowTxt))
  end
end
table.insert(resultPart, table.concat(tierRows, '\r\n|-\r\n|'))
end


  result = result..'\r\n|}'
table.insert(resultPart, '\r\n|}')
  return result
return table.concat(resultPart)
end
end


function p.getHerblorePotionTable(frame)
function p.getHerblorePotionTable(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._getHerblorePotionTable(category)
return p._getHerblorePotionTable(category)
end
end


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
  local data = nil
return p._getRecipeTable('Runecrafting', category, {'Item', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients', 'SkillXPSec', 'GPSec'})
 
  if    category == "Runes"      then data =
        Items.getItems(function(item) return item.runecraftingCategory == 0 end)
  elseif category == "ComboRunes" then data =
        Items.getItems(function(item) return item.runecraftingCategory == 1 end)
  elseif category == "Weapons"    then data =
        Items.getItems(function(item) return item.runecraftingCategory == 2 and item.runecraftingID end)
  elseif category == "AirGear"    then data =
        Items.getItems(function(item) return item.runecraftingCategory == 3 end)
  elseif category == "WaterGear"  then data =
        Items.getItems(function(item) return item.runecraftingCategory == 4 end)
  elseif category == "EarthGear"  then data =
        Items.getItems(function(item) return item.runecraftingCategory == 5 end)
  elseif category == "FireGear"  then data =
        Items.getItems(function(item) return item.runecraftingCategory == 6 end)
  end
 
  if data == nil then
    return "ERROR: Invalid Runecrafting category name.[[Category:Pages with script errors]]"
  end
 
  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!'..Icons.Icon({'Runecrafting', type='skill', notext=true})..' Level\r\n!Experience'
  result = result..'\r\n!Item Price\r\n!Ingredients\r\n!XP/s\r\n!GP/s'
 
  table.sort(data, function(a, b) return (a.runecraftingLevel == b.runecraftingLevel and a.id < b.id)
                                      or a.runecraftingLevel <  b.runecraftingLevel end)
 
  for i, rune in Shared.skpairs(data) do
    result = result..'\r\n|-'
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({rune.name, type='item', size='50', notext=true})
    result = result..'\r\n| style ="text-align: left;" |[['..rune.name..']]'
    result = result..'\r\n| style="text-align:right"|'..rune.runecraftingLevel
    result = result..'\r\n| style="text-align:right"|'..rune.runecraftingXP
    result = result..'\r\n| style="text-align:right"|'..rune.sellsFor
 
    local matArray = {}
    for j, mat in Shared.skpairs(rune.runecraftReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'\r\n|'..table.concat(matArray, ' ')
 
    local rcCraftTime = 2.00
    local xps = rune.runecraftingXP / rcCraftTime
    local gps = rune.sellsFor / rcCraftTime
    result = result..'\r\n| style="text-align:right"|'..string.format("%.2f", xps)
    result = result..'\r\n| style="text-align:right"|'..string.format("%.2f", gps)
  end
 
  result = result..'\r\n|}'
  return result
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
  local data = nil
return p._getRecipeTable('Fletching', category, {'Item', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients'})
 
  if    category == "Arrows"    then
  data = Items.getItems(function(item) return item.fletchingCategory == 0 end)
  elseif category == "Shortbows" then
  data = Items.getItems(function(item) return item.fletchingCategory == 1 end)
  elseif category == "Longbows"  then
  data = Items.getItems(function(item) return item.fletchingCategory == 2 end)
  elseif category == "Bolts"    then
  data =  Items.getItems(function(item) return item.fletchingCategory == 3 end)
  elseif category == "Crossbows" then
  data = Items.getItems(function(item) return item.fletchingCategory == 4 end)
  elseif category == "Javelins"  then
  data = Items.getItems(function(item) return item.fletchingCategory == 5 end)
  end
 
  if data == nil then
    return "ERROR: Invalid Fletching category name.[[Category:Pages with script errors]]"
  end
 
  local result = '{| class="wikitable sortable stickyHeader"'
  result = result..'\r\n|- class="headerRow-0"'
  result = result..'\r\n!Item\r\n!Name\r\n!Fletching Level\r\n!Experience'
  result = result..'\r\n!Quantity\r\n!Sells For\r\n!Ingredients'
 
  table.sort(data, function(a, b) return (a.fletchingLevel == b.fletchingLevel and a.id < b.id)
                                      or a.fletchingLevel <  b.fletchingLevel end)
 
  for i, fletch in Shared.skpairs(data) do
    result = result..'\r\n|-'
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({fletch.name, type='item', size='50', notext=true})
    result = result..'\r\n| style ="text-align: left;" |[['..fletch.name..']]'
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchingLevel
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchingXP
    result = result..'\r\n| style="text-align:right"|'..fletch.fletchQty
    result = result..'\r\n| style="text-align:right"|'..fletch.sellsFor
 
    local matArray = {}
    for j, mat in Shared.skpairs(fletch.fletchReq) do
      local matItem = Items.getItemByID(mat.id)
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
    end
    result = result..'\r\n|'..table.concat(matArray, ' ')
  end
 
  result = result..'\r\n|}'
  return result
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
  local data = nil
return p._getRecipeTable('Crafting', category, {'Item', 'SkillLevel', 'SkillXP', 'GP', 'Ingredients'})
end


  if    category == "Leather"    then data =
-- Given a skill name, category, and column list, produces a table of recipes for that
        Items.getItems(function(item) return item.tier == "Leather" or item.tier == "Hard Leather" end)
-- skill & category combination. If categoryName is '', all recipes are returned.
  elseif category == "Dragonhide" then data =
-- Note: This only supports a number of skills with consistent recipe data structures, being:
        Items.getItems(function(item) return item.tier == "Dragonhide" and item.craftingLevel ~= nil end)
-- Fletching, Crafting, and Runecrafting
  elseif category == "Rings"      then data =
-- Valid column list options are: Item, SkillLevel, SkillXP, GP, Ingredients, SkillXPSec, GPSec
        Items.getItems(function(item) return item.type == "Ring" and item.craftingLevel ~= nil end)
function p._getRecipeTable(skillName, categoryName, columnList)
  elseif category == "Necklaces"  then data =
-- Validation: Parameters
        Items.getItems(function(item) return item.type == "Amulet" and item.craftingLevel ~= nil end)
if type(skillName) ~= 'string' then
  elseif category == "Bags"   then data =
return 'ERROR: skillName must be a string'
  Items.getItems(function(item) return item.type == 'Bag' and item.craftingLevel ~= nil end)
elseif not Shared.contains({'string', 'nil'}, type(categoryName)) then
  end
return 'ERROR: category must be a string or nil'
elseif type(columnList) ~= 'table' or Shared.tableCount(columnList) == 0 then
return 'ERROR: columnList must be a table with 1+ elements'
end


  if data == nil then
local categoryMap = {
    return "ERROR: Invalid Crafting category name.[[Category:Pages with script errors]]"
["Runecrafting"] = {
  end
["Runes"] = 0,
["ComboRunes"] = 1,
["Weapons"] = 2,
["AirGear"] = 3,
["WaterGear"] = 4,
["EarthGear"] = 5,
["FireGear"] = 6
},
["Fletching"] = {
["Arrows"] = 0,
["Shortbows"] = 1,
["Longbows"] = 2,
["Bolts"] = 3,
["Crossbows"] = 4,
["Javelins"] = 5
},
["Crafting"] = {
["Leather"] = 0,
["Dragonhide"] = 1,
["Rings"] = 2,
["Necklaces"] = 3,
["Bags"] = 4
}
}
local actionIntervals = {
["Runecrafting"] = 2,
["Fletching"]  = 2,
["Crafting"] = 2
}
-- Validation: Category
if categoryMap[skillName] == nil then
return 'ERROR: The ' .. skillName .. ' skill is not supported by this function'
elseif categoryName ~= '' and categoryMap[skillName][categoryName] == nil then
local catNames = {}
for catName, catID in pairs(categoryMap[skillName]) do
table.insert(catNames, catName)
end
return 'ERROR: No such category ' .. categoryName .. ' for skill ' .. skillName .. ', the following are available: ' .. table.concat(catNames, ', ')
end
local categoryID = categoryMap[skillName][categoryName]
local actionInterval = actionIntervals[skillName]


  table.sort(data, function(a, b) return (a.craftingLevel == b.craftingLevel and a.id < b.id)
-- Validation: Skill data
                                      or a.craftingLevel <  b.craftingLevel end)
local recipeKey = 'Recipes'
if SkillData[skillName] == nil then
return 'ERROR: Could not locate skill data for ' .. skillName
elseif SkillData[skillName][recipeKey] == nil then
return 'ERROR: Could not locate recipe data for ' .. skillName
end


  local result = '{| class="wikitable sortable stickyHeader"'
-- Validation: Column list
  result = result..'\r\n|- class="headerRow-0"'
local columnDef = {
  result = result..'\r\n!Item\r\n!Name\r\n!'..Icons.Icon({'Crafting', type='skill', notext=true})..' Level\r\n!Experience'
["Item"] = {["header"] = 'Item\r\n!Name', ["altRepeat"] = true},
  result = result..'\r\n!Item Price\r\n!Ingredients'
["SkillLevel"] = {["header"] = Icons.Icon({skillName, type='skill', notext=true}) .. ' Level', ["altRepeat"] = false},
["SkillXP"] = {["header"] = 'XP', altRepeat = false},
["GP"] = {["header"] = 'Value', ["altRepeat"] = true},
["Ingredients"] = {["header"] = 'Ingredients', ["altRepeat"] = true},
["SkillXPSec"] = {["header"] = 'XP/s', ["altRepeat"] = false},
["GPSec"] = {["header"] = 'GP/s', ["altRepeat"] = true}
}
-- Build the table header while we're here
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|- class="headerRow-0"')
for i, colID in ipairs(columnList) do
if columnDef[colID] == nil then
return 'ERROR: Invalid column ' .. colID .. ' requested'
else
table.insert(resultPart, '\r\n! ' .. columnDef[colID].header)
end
end


  for i, craft in Shared.skpairs(data) do
-- Determine which recipes to include
    result = result..'\r\n|-'
local recipeList = {}
    result = result..'\r\n| style="text-align: left;" | '..Icons.Icon({craft.name, type='item', size='50', notext=true})
for i, recipe in ipairs(SkillData[skillName][recipeKey]) do
    result = result..'\r\n| style ="text-align: left;" |[['..craft.name..']]'
if categoryID == nil or recipe.category == categoryID then
    result = result..'\r\n| style="text-align:right"|'..craft.craftingLevel
table.insert(recipeList, recipe)
    result = result..'\r\n| style="text-align:right"|'..craft.craftingXP
end
    result = result..'\r\n| style="text-align:right"|'..craft.sellsFor
end
if Shared.tableCount(recipeList) == 0 then
return ''
end
table.sort(recipeList, function(a, b) return (a.level == b.level and a.masteryID < b.masteryID) or a.level < b.level end)


    local matArray = {}
-- Build rows based on recipes
    if craft.craftGPCost ~= nil and craft.craftGPCost > 0 then
for i, recipe in ipairs(recipeList) do
    table.insert(matArray, Icons.GP(craft.craftGPCost))
local item = Items.getItemByID(recipe.itemID)
    end
if item ~= nil then
    for j, mat in Shared.skpairs(craft.craftReq) do
-- Some recipes have alternative costs, so the recipe may require multiple rows
      local matItem = Items.getItemByID(mat.id)
local costList = nil
      table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
if recipe.alternativeCosts ~= nil and Shared.tableCount(recipe.alternativeCosts) > 0 then
    end
costList = recipe.alternativeCosts
    result = result..'\r\n|'..table.concat(matArray, ', ')
else
  end
costList = {{["itemCosts"] = recipe.itemCosts}}
 
end
  result = result..'\r\n|}'
local costCount = Shared.tableCount(costList)
  return result
-- Build one row per element within costList
for recipeRow, costDef in ipairs(costList) do
local rowspanStr = (recipeRow == 1 and costCount > 1 and 'rowspan="' .. costCount .. '" ') or ''
local qty = (costDef.quantityMultiplier or 1) * (recipe.baseQuantity or 1)
table.insert(resultPart, '\r\n|-')
for j, colID in ipairs(columnList) do
local altRepeat = columnDef[colID].altRepeat
-- All columns must be generated if this is the very first row for a recipe,
-- for subsequent rows only columns marked as altRepeat = true are generated
if recipeRow == 1 or altRepeat then
local spanStr = (not altRepeat and rowspanStr) or ''
if colID == 'Item' then
local namePrefix = spanStr
if qty > 1 then
namePrefix = namePrefix .. 'data-sort-value="' .. item.name .. '"'
end
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:center;min-width:25px"| ' .. Icons.Icon({item.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\r\n|'.. (namePrefix ~= '' and namePrefix .. '| ' or ' ') .. (qty > 1 and '<b>' .. qty .. 'x</b> ' or '') .. Icons.Icon({item.name, type='item', noicon=true}))
elseif colID == 'SkillLevel' then
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.level)
elseif colID == 'SkillXP' then
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. recipe.baseXP)
elseif colID == 'GP' then
local val = math.floor(item.sellsFor)
table.insert(resultPart, '\r\n|' .. spanStr .. 'data-sort-value="' .. (val * qty) .. '"| ' .. Icons.GP(val) .. (qty > 1 and ' (x' .. qty .. ')' or ''))
elseif colID == 'Ingredients' then
local matArray = {}
for k, mat in ipairs(costDef.itemCosts) do
local matItem = Items.getItemByID(mat.id)
if matItem ~= nil then
table.insert(matArray, Icons.Icon({matItem.name, type='item', notext=true, qty=mat.qty}))
end
end
if recipe.gpCost ~= nil and recipe.gpCost > 0 then
table.insert(matArray, Icons.GP(recipe.gpCost))
end
if recipe.scCost ~= nil and recipe.scCost > 0 then
table.insert(matArray, Icons.SC(recipe.gpCost))
end
table.insert(resultPart, '\r\n|' .. (spanStr ~= '' and spanStr .. '| ' or ' ') .. table.concat(matArray, ', '))
elseif colID == 'SkillXPSec' then
table.insert(resultPart, '\r\n|' .. spanStr .. 'style="text-align:right"| ' .. string.format('%.2f', recipe.baseXP / actionInterval))
elseif colID == 'GPSec' then
local val = math.floor(item.sellsFor) * qty / actionInterval
table.insert(resultPart, '\r\n|' .. spanStr .. 'data-sort-value="' .. val .. '"| ' .. Icons.GP(string.format('%.2f', val)))
else
table.insert(resultPart, '\r\n| ')
end
end
end
end
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
end


return p
return p