Module:Skills/Gathering: Difference between revisions

Update for v1.3
(Fix Mining pickaxe requirement issue)
(Update for v1.3)
Line 7: Line 7:
local SkillData = GameData.skillData
local SkillData = GameData.skillData
local Common = require('Module:Common')
local Common = require('Module:Common')
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')
Line 13: Line 13:
local ItemSourceTables = require('Module:Items/SourceTables')
local ItemSourceTables = require('Module:Items/SourceTables')


function p.getRecipeRequirements(skillName, recipe)
local function lootValueText(lootValue)
local reqText = {}
local returnPart = {}
if recipe.level ~= nil then
for _, currencyDefn in ipairs(GameData.rawData.currencies) do
table.insert(reqText, Icons._SkillReq(skillName, recipe.level, false))
-- Guarantee order by iterating through currency game data definition
end
local currID = currencyDefn.id
if recipe.totalMasteryRequired ~= nil then
local val = Shared.round(lootValue[currID], 2, 2)
table.insert(reqText, Shared.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'}))
if val ~= nil then
end
table.insert(returnPart, Icons._Currency(currID, val))
local reqsData = {}
if type(recipe.requirements) == 'table' then
reqsData = Shared.shallowClone(recipe.requirements)
end
if recipe.shopItemPurchased ~= nil then
-- Mining requirements are stored differently than other skills like
-- Woodcutting, standardize here
table.insert(reqsData, {
["type"] = 'ShopPurchase',
["purchaseID"] = recipe.shopItemPurchased,
["count"] = 1
})
end
if not Shared.tableIsEmpty(reqsData) then
local reqs = Common.getRequirementString(reqsData)
if reqs ~= nil then
table.insert(reqText, reqs)
end
end
end
end
return table.concat(reqText, '<br/>')
return table.concat(returnPart, ', ')
end
end


function p.getTreesTable(frame)
function p.getTreesTable(frame)
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Woodcutting'
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2"|Tree!!colspan="2"|Logs!!Requirements')
table.insert(resultPart, '\n!colspan="2"|Tree!!colspan="2"|Logs!!Requirements')
table.insert(resultPart, '!!XP!!Cut Time!!XP/s!!GP/s')
table.insert(resultPart, '!!XP!!Cut Time!!XP/s!!Price/s')


local trees = Shared.shallowClone(SkillData.Woodcutting.trees)
local trees = GameData.getEntities(SkillData.Woodcutting.trees,
table.sort(trees, function(a, b) return a.level < b.level end)
function(tree)
return Skills.getRecipeRealm(tree) == realm.id
end
)
table.sort(trees, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)
for i, tree in ipairs(trees) do
for i, tree in ipairs(trees) do
local level = Skills.getRecipeLevel(skillID, tree)
local baseXP = tree.baseAbyssalExperience or tree.baseExperience
local baseInt = tree.baseInterval
local reqText = Skills.getRecipeRequirementText(SkillData.Woodcutting.name, tree)
local log = Items.getItemByID(tree.productId)
local log = Items.getItemByID(tree.productId)
local sellCurrency = log.sellsForCurrency or 'melvorD:GP'
local XPSec = baseXP / (baseInt / 1000)
local currSec = Shared.round(log.sellsFor / (baseInt / 1000), 2, 2)
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..tree.name..'"| '..Icons.Icon({log.name, img=tree.name, type='tree', notext=true, size=50}))
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..tree.name..'"| '..Icons.Icon({log.name, img=tree.name, type='tree', notext=true, size=50}))
Line 59: Line 62:
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..log.name..'"| '..Icons.Icon({log.name, type='item', notext=true, size=50}))
table.insert(resultPart, '\n|class="table-img" data-sort-value="'..log.name..'"| '..Icons.Icon({log.name, type='item', notext=true, size=50}))
table.insert(resultPart, '\n| '..Icons.Icon({log.name, type='item', noicon=true}))
table.insert(resultPart, '\n| '..Icons.Icon({log.name, type='item', noicon=true}))
table.insert(resultPart, '\n|data-sort-value="' .. tree.level .. '"| ' .. p.getRecipeRequirements(SkillData.Woodcutting.name, tree))
table.insert(resultPart, '\n|data-sort-value="' .. level .. '"| ' .. reqText)
table.insert(resultPart, '\n|style="text-align:right"| '..tree.baseExperience)
table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.formatnum(baseXP))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..tree.baseInterval..'"| '..Shared.timeString(tree.baseInterval/1000, true))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..baseInt..'"| '..Shared.timeString(baseInt / 1000, true))
local XPs = tree.baseExperience / (tree.baseInterval / 1000)
table.insert(resultPart, '\n|style="text-align:right"| '..Shared.round(XPSec, 2, 2))
local GPs = log.sellsFor / (tree.baseInterval / 1000)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..currSec..'"| '..Icons._Currency(sellCurrency, currSec))
table.insert(resultPart, '\n|style="text-align:right"| '..Shared.round(XPs, 2, 2))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="'..GPs..'"| '..Icons.GP(Shared.round(GPs, 2, 2)))
end
end


Line 73: Line 74:


function p.getSpecialFishingTable(frame)
function p.getSpecialFishingTable(frame)
local totalWt, lootValue = 0, 0
local args = frame.args ~= nil and frame.args or frame
local itemArray = Shared.shallowClone(SkillData.Fishing.specialItems)
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
 
local totalWt, lootValue = 0, {}
local realmSpecials = GameData.getEntityByProperty(SkillData.Fishing.specialItems, 'realmID', realm.id)
if realmSpecials == nil then
return ''
end
local itemArray = realmSpecials.drops
for i, itemDef in ipairs(itemArray) do
for i, itemDef in ipairs(itemArray) do
totalWt = totalWt + itemDef.weight
totalWt = totalWt + itemDef.weight
Line 88: Line 100:
if item ~= nil then
if item ~= nil then
local dropChance = itemDef.weight / totalWt * 100
local dropChance = itemDef.weight / totalWt * 100
local currID = item.sellsForCurrency or 'melvorD:GP'
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
table.insert(resultPart, '\r\n|-\r\n|class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true}))
table.insert(resultPart, '\r\n|-\r\n|class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Items.getValueText(item))
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. itemDef.weight .. '"| ' .. Shared.fraction(itemDef.weight, totalWt))
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. itemDef.weight .. '"| ' .. Shared.fraction(itemDef.weight, totalWt))
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. string.format(fmt, dropChance) .. '%')
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. string.format(fmt, dropChance) .. '%')
lootValue = lootValue + (dropChance / 100 * item.sellsFor)
if lootValue[currID] == nil then
lootValue[currID] = 0
end
lootValue[currID] = lootValue[currID] + (dropChance / 100 * item.sellsFor)
end
end
end
end
table.insert(resultPart, '\r\n|}\r\nThe average value of a roll on the special fishing loot table is ' .. Icons.GP(Shared.round(lootValue, 2, 0)))
table.insert(resultPart, '\r\n|}\r\nThe average value of a roll on the special fishing loot table is ' .. lootValueText(lootValue))
return table.concat(resultPart)
return table.concat(resultPart)
end
end
Line 121: Line 137:
table.insert(resultPart, '\r\n|class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true, size=50}))
table.insert(resultPart, '\r\n|class="table-img"| ' .. Icons.Icon({item.name, type='item', notext=true, size=50}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Icons.GP(math.floor(item.sellsFor)))
table.insert(resultPart, '\r\n|data-sort-value="' .. item.sellsFor .. '"| ' .. Items.getValueText(item))
end
end
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
Line 128: Line 144:


function p.getMiningOresTable(frame)
function p.getMiningOresTable(frame)
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Mining'
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
Line 134: Line 158:
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value')
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value')


local mineData = Shared.shallowClone(SkillData.Mining.rockData)
local mineData = GameData.getEntities(SkillData.Mining.rockData,
table.sort(mineData, function(a, b) return a.level < b.level end)
function(obj)
 
return Skills.getRecipeRealm(obj) == realm.id
end
)
table.sort(mineData, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)
for i, oreData in ipairs(mineData) do
for i, oreData in ipairs(mineData) do
local level = Skills.getRecipeLevel(skillID, oreData)
local baseXP = oreData.baseAbyssalExperience or oreData.baseExperience
local reqText = Skills.getRecipeRequirementText(SkillData.Mining.name, oreData)
local ore = Items.getItemByID(oreData.productId)
local ore = Items.getItemByID(oreData.productId)
local respawnStyle, respawnSort, respawnText = 'class="table-na"', 0, 'N/A'
local respawnStyle, respawnSort, respawnText = 'class="table-na"', 0, 'N/A'
Line 144: Line 174:
respawnSort = oreData.baseRespawnInterval / 1000
respawnSort = oreData.baseRespawnInterval / 1000
respawnText = Shared.timeString(respawnSort, true)
respawnText = Shared.timeString(respawnSort, true)
end
local categoryName = ''
local category = GameData.getEntityByID(SkillData.Mining.categories, oreData.category)
if category ~= nil and category.name ~= nil then
categoryName = category.name
end
end
local rockName = Icons.Icon({oreData.name, type='rock', noicon=true, nolink=true})
local rockName = Icons.Icon({oreData.name, type='rock', noicon=true, nolink=true})
Line 152: Line 187:
table.insert(resultPart, '\n|class="table-img" data-sort-value="' .. ore.name .. '"| '..Icons.Icon({ore.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n|class="table-img" data-sort-value="' .. ore.name .. '"| '..Icons.Icon({ore.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n| ' .. qtyText .. Icons.Icon({ore.name, type='item', noicon=true}))
table.insert(resultPart, '\n| ' .. qtyText .. Icons.Icon({ore.name, type='item', noicon=true}))
table.insert(resultPart, '\n| ' .. oreData.type)
table.insert(resultPart, '\n| ' .. categoryName)
table.insert(resultPart, '\n|data-sort-value="' .. oreData.level ..'"| '..p.getRecipeRequirements(SkillData.Mining.name, oreData))
table.insert(resultPart, '\n|data-sort-value="' .. level ..'"| ' .. reqText)
table.insert(resultPart, '\n|style="text-align:right"| '..oreData.baseExperience)
table.insert(resultPart, '\n|style="text-align:right"| '..Shared.formatnum(baseXP))
table.insert(resultPart, '\n|' .. respawnStyle .. ' data-sort-value="'..respawnSort..'"| ' .. respawnText)
table.insert(resultPart, '\n|' .. respawnStyle .. ' data-sort-value="'..respawnSort..'"| ' .. respawnText)
table.insert(resultPart, '\n|data-sort-value="'..ore.sellsFor..'"| '..Icons.GP(ore.sellsFor))
table.insert(resultPart, '\n|data-sort-value="'..ore.sellsFor..'"| '..Items.getValueText(ore))
end
end


Line 169: Line 204:
local validTypes = {
local validTypes = {
["Normal"] = 'randomGems',
["Normal"] = 'randomGems',
["Superior"] = 'randomSuperiorGems'
["Superior"] = 'randomSuperiorGems',
["Abyssal"] = 'randomAbyssalGems'
}
}
local gemDataKey = validTypes[gemType]
local gemDataKey = validTypes[gemType]
Line 194: Line 230:
table.insert(resultPart, '\n| data-sort-value="'..gemItem.name..'"|'..Icons.getExpansionIcon(gemItem.id) .. Icons.Icon({gemItem.name, type='item', noicon=true}))
table.insert(resultPart, '\n| data-sort-value="'..gemItem.name..'"|'..Icons.getExpansionIcon(gemItem.id) .. Icons.Icon({gemItem.name, type='item', noicon=true}))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. gemPct .. '" | ' .. string.format("%.1f%%", gemPct))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. gemPct .. '" | ' .. string.format("%.1f%%", gemPct))
table.insert(resultPart, '\n|data-sort-value="' .. gemItem.sellsFor .. '"| ' .. Icons.GP(gemItem.sellsFor))
table.insert(resultPart, '\n|data-sort-value="' .. gemItem.sellsFor .. '"| ' .. Items.getValueText(gemItem))
end
end


Line 207: Line 243:


function p.getFishTable(frame)
function p.getFishTable(frame)
local recipeList = Shared.shallowClone(SkillData.Fishing.fish)
local args = frame.args ~= nil and frame.args or frame
table.sort(recipeList, function(a, b) return a.level < b.level end)
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Fishing'
 
local recipeList = GameData.getEntities(SkillData.Fishing.fish,
function(obj)
return Skills.getRecipeRealm(obj) == realm.id
end
)
table.sort(recipeList, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)


-- Determine cooking levels for all fish
-- Determine cooking levels for all fish
local cookReq = {}
local cookRecipes = {}
for i, recipe in ipairs(SkillData.Cooking.recipes) do
for i, recipe in ipairs(SkillData.Cooking.recipes) do
-- This assumes that each raw fish only appears in a single recipe, which is a bit rubbish
-- This assumes that each raw fish only appears in a single recipe, which is a bit rubbish
-- but currently holds
-- but currently holds
for j, mat in ipairs(recipe.itemCosts) do
for j, mat in ipairs(recipe.itemCosts) do
if cookReq[mat.id] == nil then
if cookRecipes[mat.id] == nil then
cookReq[mat.id] = recipe.level
cookRecipes[mat.id] = recipe
end
end
end
end
Line 225: Line 273:
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan="2" rowspan="2"|Fish\n!rowspan="2"|' .. Icons.Icon({'Fishing', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\n!colspan="2" rowspan="2"|Fish\n!rowspan="2"|Requirements')
table.insert(resultPart, '\n!colspan="3"|Catch Time\n!rowspan="2"|XP\n!rowspan="2"|Value\n!rowspan="2"|XP/s\n!rowspan="2"|GP/s')
table.insert(resultPart, '\n!colspan="3"|Catch Time\n!rowspan="2"|XP\n!rowspan="2"|Value\n!rowspan="2"|XP/s\n!rowspan="2"|Price/s')
table.insert(resultPart, '\n!rowspan="2"|' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\n!rowspan="2"|' .. Icons.Icon({'Cooking', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\n|- class="headerRow-1"\n!Min\n!Max\n!Avg')
table.insert(resultPart, '\n|- class="headerRow-1"\n!Min\n!Max\n!Avg')
Line 236: Line 284:
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000
local timeSortVal = (recipe.baseMinInterval + recipe.baseMaxInterval) / 2000
local timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000)
local timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000)
local XPs = recipe.baseExperience / timeSortVal
local level = Skills.getRecipeLevel(skillID, recipe)
local GPs = fish.sellsFor / timeSortVal
local reqText = Skills.getRecipeRequirementText(skillID, recipe)
local cookSortVal = cookReq[recipe.productId] or 0
local baseXP = recipe.baseAbyssalExperience or recipe.baseExperience
local cookStyle = (cookReq[recipe.productId] ~= nil and 'style="text-align:right" ' or 'class="table-na" ')
local XPSec = baseXP / timeSortVal
local cookStr = cookReq[recipe.productId] or 'N/A'
local sellCurrency = fish.sellsForCurrency or 'melvorD:GP'
local currPerSec = Shared.round(fish.sellsFor / timeSortVal, 2, 2)
local cookRecipe = cookRecipes[recipe.productId]
local cookLevel, cookStyle, cookStr = 0, 'class="table-na" ', 'N/A'
if cookRecipe ~= nil then
cookLevel = Skills.getRecipeLevel('Cooking', cookRecipe)
cookStyle = 'style="text-align:right" '
cookStr = Skills.getRecipeRequirementText('Cooking', cookRecipe)
end
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|class="table-img"| ' .. Icons.Icon({fish.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n|class="table-img"| ' .. Icons.Icon({fish.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n|data-sort-value="'..fish.name..'"|'..Icons.getExpansionIcon(fish.id) .. Icons.Icon({fish.name, type='item', noicon=true}))
table.insert(resultPart, '\n|data-sort-value="'..fish.name..'"|'..Icons.getExpansionIcon(fish.id) .. Icons.Icon({fish.name, type='item', noicon=true}))
table.insert(resultPart, '\n|style="text-align:right"| ' .. recipe.level)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. level .. '"| ' .. reqText)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMin .. '"| ' .. string.format("%.1fs", timeMin))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMin .. '"| ' .. string.format("%.1fs", timeMin))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMax .. '"| ' .. string.format("%.1fs", timeMax))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMax .. '"| ' .. string.format("%.1fs", timeMax))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeAvg .. '"| ' .. string.format("%.1fs", timeAvg))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeAvg .. '"| ' .. string.format("%.1fs", timeAvg))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"| ' .. Shared.formatnum(recipe.baseExperience))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. baseXP .. '"| ' .. Shared.formatnum(baseXP))
table.insert(resultPart, '\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor))
table.insert(resultPart, '\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Items.getValueText(fish))
table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.round(XPs, 2, 2))
table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.round(XPSec, 2, 2))
table.insert(resultPart, '\n|data-sort-value="' .. GPs .. '"|' .. Icons.GP(Shared.round(GPs, 2, 2)))
table.insert(resultPart, '\n|data-sort-value="' .. currPerSec .. '"|' .. Icons._Currency(sellCurrency, currPerSec))
table.insert(resultPart, '\n|' .. cookStyle .. 'data-sort-value="' .. cookSortVal .. '"| ' .. cookStr)
table.insert(resultPart, '\n|' .. cookStyle .. 'data-sort-value="' .. cookLevel .. '"| ' .. cookStr)
end
end
end
end
Line 260: Line 316:


function p.getFishingAreasTable(frame)
function p.getFishingAreasTable(frame)
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Fishing'
local result = '{| class="wikitable sortable stickyHeader"'
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n|- class="headerRow-0"'
Line 265: Line 329:
result = result..'\r\n!Junk Chance\r\n!Special Chance'
result = result..'\r\n!Junk Chance\r\n!Special Chance'


for i, area in ipairs(SkillData.Fishing.areas) do
local fishAreas = GameData.getEntities(SkillData.Fishing.areas,
function(obj)
return Skills.getRecipeRealm(obj) == realm.id
end
)
 
for i, area in ipairs(fishAreas) do
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n| style ="text-align: left;" |'..Icons.getExpansionIcon(area.id)..area.name
result = result..'\r\n| style ="text-align: left;" |'..Icons.getExpansionIcon(area.id)..area.name
Line 291: Line 361:
end
end


function p._getThievingGeneralRareTable(npcID)
function p._getThievingGeneralRareTable(npc)
local npcRealm = nil
if npc ~= nil then
npcRealm = Skills.getRecipeRealm(npc)
end
 
local rareTxt = '{|class="wikitable sortable"'
local rareTxt = '{|class="wikitable sortable"'
rareTxt = rareTxt..'\r\n!Item!!Qty'
rareTxt = rareTxt..'\r\n!Item!!Qty'
Line 298: Line 373:
-- If an npcID has been passed and the item is NPC specific, only display
-- If an npcID has been passed and the item is NPC specific, only display
-- the item if it may be obtained while pickpocketing that NPC
-- the item if it may be obtained while pickpocketing that NPC
if npcID == nil or drop.npcs == nil or Shared.contains(drop.npcs, npcID) then
local npcMatch = (
npc == nil
or drop.npcs == nil
or Shared.contains(drop.npcs, npc.id)
)
local realmMatch = (
npcRealm == nil
or drop.realms == nil
or Shared.contains(drop.realms, npcRealm)
)
if npcMatch and realmMatch then
local thisItem = Items.getItemByID(drop.itemID)
local thisItem = Items.getItemByID(drop.itemID)
local odds = drop.chance
local odds = drop.chance
rareTxt = rareTxt..'\r\n|-\r\n|data-sort-value="'..thisItem.name..'"|'..Icons.getExpansionIcon(thisItem.id)..Icons.Icon({thisItem.name, type='item'})
rareTxt = rareTxt..'\r\n|-\r\n|data-sort-value="'..thisItem.name..'"|'..Icons.getExpansionIcon(thisItem.id)..Icons.Icon({thisItem.name, type='item'})
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
rareTxt = rareTxt..'||1||data-sort-value="'..thisItem.sellsFor..'"|'..Items.getValueText(thisItem)
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0))
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.fraction(1, Shared.round2(1/(odds/100), 0))
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%'
rareTxt = rareTxt..'||style="text-align:right" data-sort-value="'..odds..'"|'..Shared.round(odds, 4, 4)..'%'
Line 310: Line 395:
rareTxt = rareTxt..'\r\n|}'
rareTxt = rareTxt..'\r\n|}'
return rareTxt
return rareTxt
end
function p._getThievingNPCCurrencyText(npc)
local currTextPart = {}
for _, currencyDrop in ipairs(npc.currencyDrops) do
table.insert(currTextPart, Icons._Currency(currencyDrop.id, 1, currencyDrop.quantity))
end
return table.concat(currTextPart, ', ')
end
end


Line 316: Line 409:
local sectionTxt = {}
local sectionTxt = {}


--Five sections here: GP, normal loot, area loot, rare loot, and unique item
--Five sections here: Currency, normal loot, area loot, rare loot, and unique item
--First up, GP:
--First up, currency:
local gpTxt = 'Successfully pickpocketing the '..npc.name..' will always give '..Icons.GP(1, npc.maxGP)
table.insert(sectionTxt, 'Successfully pickpocketing the ' .. npc.name .. ' will always give '.. p._getThievingNPCCurrencyText(npc))
table.insert(sectionTxt, gpTxt)


--Next up, normal loot:
--Next up, normal loot:
Line 328: Line 420:
local totalWt = 0
local totalWt = 0
local lootChance = SkillData.Thieving.itemChance
local lootChance = SkillData.Thieving.itemChance
local lootValue = 0
local lootValue = {}


--First loop through to get the total weight so we have it for later
--First loop through to get the total weight so we have it for later
Line 358: Line 450:


--Adding price columns
--Adding price columns
local itemPrice = 0
local sellAmount, sellCurrency = nil, nil
if thisItem == nil then
if thisItem == nil then
table.insert(normalTxt, '||data-sort-value="0"|???')
table.insert(normalTxt, '||data-sort-value="0"|???')
else
else
itemPrice = thisItem.sellsFor ~= nil and thisItem.sellsFor or 0
sellAmount = thisItem.sellsFor or 0
if itemPrice == 0 or loot.minQuantity == loot.maxQuantity then
sellCurrency = thisItem.sellsForCurrency or 'melvorD:GP'
table.insert(normalTxt, '||'..Icons.GP(itemPrice))
table.insert(normalTxt, '||' .. Items.getValueText(thisItem, loot.minQuantity, loot.maxQuantity))
else
table.insert(normalTxt, '||'..Icons.GP(itemPrice * loot.minQuantity, itemPrice * loot.maxQuantity))
end
end
end


Line 383: Line 472:


--Adding to the average loot value based on price & dropchance
--Adding to the average loot value based on price & dropchance
lootValue = lootValue + (dropChance * 0.01 * itemPrice * (loot.minQuantity + loot.maxQuantity) / 2)
if sellAmount ~= nil and sellCurrency ~= nil then
if lootValue[sellCurrency] == nil then
lootValue[sellCurrency] = 0
end
lootValue[sellCurrency] = lootValue[sellCurrency] + (dropChance * 0.01 * sellAmount * (loot.minQuantity + loot.maxQuantity) / 2)
end
end
end
if Shared.tableCount(npc.lootTable) > 1 then
if Shared.tableCount(npc.lootTable) > 1 then
Line 395: Line 489:
end
end
table.insert(normalTxt, '\r\n|}')
table.insert(normalTxt, '\r\n|}')
table.insert(normalTxt, '\r\nThe loot obtained from the average successful pickpocket is worth ' .. Icons.GP(Shared.round(lootValue, 2, 2)) .. ' if sold.')
 
table.insert(normalTxt, '\r\n\r\nIncluding GP, the average successful pickpocket is worth ' .. Icons.GP(Shared.round(lootValue + (1 + npc.maxGP) / 2, 2, 2)) .. '.')
table.insert(normalTxt, '\r\nThe loot obtained from the average successful pickpocket is worth ' .. lootValueText(lootValue) .. ' if sold.')
 
-- Amend lootValue
for _, currencyDrop in ipairs(npc.currencyDrops) do
lootValue[currencyDrop.id] = lootValue[currencyDrop.id] + (1 + currencyDrop.quantity) / 2
end
 
table.insert(normalTxt, '\r\n\r\nIncluding currency, the average successful pickpocket is worth ' .. lootValueText(lootValue) .. '.')
table.insert(sectionTxt, table.concat(normalTxt))
table.insert(sectionTxt, table.concat(normalTxt))
end
end
Line 402: Line 503:
--After normal drops, add in rare drops
--After normal drops, add in rare drops
local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket:'
local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket:'
rareTxt = rareTxt..'\r\n'..p._getThievingGeneralRareTable(npc.id)
rareTxt = rareTxt..'\r\n'..p._getThievingGeneralRareTable(npc)
table.insert(sectionTxt, rareTxt)
table.insert(sectionTxt, rareTxt)


Line 419: Line 520:
local lineTxt = ''
local lineTxt = ''
lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
lineTxt = lineTxt..'\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'})
lineTxt = lineTxt..'||data-sort-value="'..drop.quantity..'"| '..Shared.formatnum(drop.quantity)..'||data-sort-value="'..thisItem.sellsFor..'"|'..Icons.GP(thisItem.sellsFor)
lineTxt = lineTxt..'||data-sort-value="'..drop.quantity..'"| '..Shared.formatnum(drop.quantity)..'||data-sort-value="'..thisItem.sellsFor..'"|'..Items.getValueText(thisItem)
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))
lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))
lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%'
lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%'
Line 459: Line 560:
end
end


function p.getThievingNPCTable()
function p.getThievingNPCTable(frame)
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Thieving'
 
local result = '{| class="wikitable sortable stickyHeader"'
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!GP!!Unique Drop'
result = result..'\r\n!colspan="2"|Name!!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!Experience!!Max Hit!!Perception!!Currency!!Unique Drop'
local npcArray = Shared.shallowClone(SkillData.Thieving.npcs)
local npcArray = GameData.getEntities(SkillData.Thieving.npcs,
table.sort(npcArray, function(a, b) return a.level < b.level end)
function(obj)
return Skills.getRecipeRealm(obj) == realm.id
end
)
table.sort(npcArray, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)
for i, npc in ipairs(npcArray) do
for i, npc in ipairs(npcArray) do
local level = Skills.getRecipeLevel(skillID, npc)
local baseXP = npc.baseAbyssalExperience or npc.baseExperience
local reqText = Skills.getRecipeRequirementText(SkillData.Thieving.name, npc)
local area = Skills.getThievingNPCArea(npc)
local currSortAmt = npc.currencyDrops[1].quantity
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
result = result..'\r\n|'..Icons.Icon({npc.name, type='thieving', size='50', notext=true})
result = result..'||data-sort-value="'..npc.name..'"|'..Icons.getExpansionIcon(npc.id)..Icons.Icon({npc.name, type='thieving', noicon=true})
result = result..'||data-sort-value="'..npc.name..'"|'..Icons.getExpansionIcon(npc.id)..Icons.Icon({npc.name, type='thieving', noicon=true})


local area = Skills.getThievingNPCArea(npc)
result = result..'||'..area.name
result = result..'||'..area.name
result = result..'||data-sort-value="' .. npc.level .. '"| '..Icons._SkillReq('Thieving', npc.level)
result = result..'||data-sort-value="' .. level .. '"| ' .. reqText
result = result..'||style="text-align:right"|'..npc.baseExperience
result = result..'||style="text-align:right"|' .. Shared.formatnum(baseXP)
result = result..'||style="text-align:right"|'..(npc.maxHit * 10)
result = result..'||style="text-align:right"|' .. Shared.formatnum(npc.maxHit * 10)
result = result..'||style="text-align:right" data-sort-value="' .. npc.perception .. '| '..Shared.formatnum(npc.perception)
result = result..'||style="text-align:right" data-sort-value="' .. npc.perception .. '| '..Shared.formatnum(npc.perception)
result = result..'||data-sort-value="' .. npc.maxGP .. '"|'..Icons.GP(1, npc.maxGP)
result = result..'||data-sort-value="' .. currSortAmt .. '"|' .. p._getThievingNPCCurrencyText(npc)
if npc.uniqueDrop ~= nil then
if npc.uniqueDrop ~= nil then
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.id)
local uniqueDrop = Items.getItemByID(npc.uniqueDrop.id)
Line 494: Line 612:


function p.getThievingAreaTable(frame)
function p.getThievingAreaTable(frame)
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local skillID = 'Thieving'
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
Line 499: Line 625:
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')


for i, area in ipairs(SkillData.Thieving.areas) do
local areas = GameData.getEntities(SkillData.Thieving.areas,
function(obj)
return Skills.getRecipeRealm(obj) == realm.id
end
)
 
for i, area in ipairs(areas) do
local minLevel, npcList, areaItemList = nil, {}, {}
local minLevel, npcList, areaItemList = nil, {}, {}
-- Build NPC list & determine level for area, this is the minimum
-- Build NPC list & determine level for area, this is the minimum
Line 506: Line 638:
for j, npcID in ipairs(area.npcIDs) do
for j, npcID in ipairs(area.npcIDs) do
local npc = Skills.getThievingNPCByID(npcID)
local npc = Skills.getThievingNPCByID(npcID)
if minLevel == nil or npc.level < minLevel then
local level = Skills.getRecipeLevel(skillID, npc)
minLevel = npc.level
if minLevel == nil or level < minLevel then
minLevel = level
end
end
table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
Line 545: Line 678:
end
end


function p._getFarmingTable(categoryName)
function p._getFarmingTable(realmID, categoryName)
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
if category == nil then
if category == nil then
Line 552: Line 685:
local seedList = GameData.getEntities(SkillData.Farming.recipes,
local seedList = GameData.getEntities(SkillData.Farming.recipes,
function(recipe)
function(recipe)
return recipe.categoryID == category.id
return recipe.categoryID == category.id and Skills.getRecipeRealm(recipe) == realmID
end)
end)
if Shared.tableIsEmpty(seedList) then
if Shared.tableIsEmpty(seedList) then
return ''
return ''
end
end
local skillID = 'Farming'


local tbl = mw.html.create()
local tbl = mw.html.create()
Line 563: Line 698:
     :tag('tr'):addClass("headerRow-0")
     :tag('tr'):addClass("headerRow-0")
         :tag('th'):attr("colspan", 2):wikitext("Seeds")
         :tag('th'):attr("colspan", 2):wikitext("Seeds")
         :tag('th'):wikitext(Icons.Icon({'Farming', type='skill', notext=true}) .. " Level")
         :tag('th'):wikitext('Requirements')
         :tag('th'):wikitext('XP')
         :tag('th'):wikitext('XP')
         :tag('th'):wikitext('Growth Time')
         :tag('th'):wikitext('Growth Time')
Line 581: Line 716:
html = html:tag('th'):wikitext('Seed Sources')
html = html:tag('th'):wikitext('Seed Sources')


table.sort(seedList, function(a, b) return a.level < b.level end)
table.sort(seedList, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)


for i, seed in ipairs(seedList) do
for i, seed in ipairs(seedList) do
Line 587: Line 722:
local productItem = Items.getItemByID(seed.productId)
local productItem = Items.getItemByID(seed.productId)
if seedItem ~= nil and productItem ~= nil then
if seedItem ~= nil and productItem ~= nil then
local level = Skills.getRecipeLevel(skillID, seed)
local baseXP = seed.baseAbyssalExperience or seed.baseExperience
local baseInt = seed.baseInterval
local reqText = Skills.getRecipeRequirementText(SkillData.Farming.name, seed)
html =  
html =  
html:tag('tr')
html:tag('tr')
:tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', size='50', notext=true}))
:tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', size='50', notext=true}))
:tag('td'):wikitext(Icons.getExpansionIcon(seedItem.id) .. Icons.Icon({seedItem.name, type='item', noicon=true}))
:tag('td'):wikitext(Icons.getExpansionIcon(seedItem.id) .. Icons.Icon({seedItem.name, type='item', noicon=true}))
:tag('td'):wikitext(seed.level)
:tag('td'):wikitext(reqText)
:tag('td'):wikitext(Shared.formatnum(seed.baseExperience))
:tag('td'):wikitext(Shared.formatnum(baseXP))
:tag('td'):attr('data-sort-value', (seed.baseInterval / 1000))
:tag('td'):attr('data-sort-value', (baseInt / 1000))
  :wikitext(Shared.timeString(seed.baseInterval / 1000, true))
  :wikitext(Shared.timeString(baseInt / 1000, true))
:tag('td'):attr('data-sort-value', seedItem.sellsFor)
:tag('td'):attr('data-sort-value', seedItem.sellsFor)
      :wikitext(Icons.GP(seedItem.sellsFor))
      :wikitext(Items.getValueText(seedItem))
:tag('td'):wikitext(Icons.Icon({productItem.name, type='item', size='50', notext=true}))
:tag('td'):wikitext(Icons.Icon({productItem.name, type='item', size='50', notext=true}))
:tag('td'):wikitext(Icons.getExpansionIcon(productItem.id) .. Icons.Icon({productItem.name, type='item', noicon=true}))
:tag('td'):wikitext(Icons.getExpansionIcon(productItem.id) .. Icons.Icon({productItem.name, type='item', noicon=true}))
Line 607: Line 747:
html =
html =
html:tag('td'):attr('data-sort-value', productItem.sellsFor)
html:tag('td'):attr('data-sort-value', productItem.sellsFor)
  :wikitext(Icons.GP(productItem.sellsFor))
  :wikitext(Items.getValueText(productItem))
:tag('td'):wikitext(ItemSourceTables._getItemSources(seedItem))
:tag('td'):wikitext(ItemSourceTables._getItemSources(seedItem))
  :css('text-align', 'left')
  :css('text-align', 'left')
Line 618: Line 758:


function p.getFarmingTable(frame)
function p.getFarmingTable(frame)
local category = frame.args ~= nil and frame.args[1] or frame
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
local category = args[1]


return p._getFarmingTable(category)
return p._getFarmingTable(realm.id, category)
end
end


Line 644: Line 790:
result = result..'||style="text-align:right;"|'..recipe.level
result = result..'||style="text-align:right;"|'..recipe.level
result = result..'||style="text-align:right" data-sort-value="'..product.healsFor..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..(product.healsFor * 10)
result = result..'||style="text-align:right" data-sort-value="'..product.healsFor..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..(product.healsFor * 10)
result = result..'||style="text-align:right" data-sort-value="'..product.sellsFor..'"|'..Icons.GP(product.sellsFor)
result = result..'||style="text-align:right" data-sort-value="'..product.sellsFor..'"|'..Items.getValueText(product)
end
end
end
end
Line 654: Line 800:


function p.getFarmingPlotTable(frame)
function p.getFarmingPlotTable(frame)
local skillID = 'Farming'
local areaName = frame.args ~= nil and frame.args[1] or frame
local areaName = frame.args ~= nil and frame.args[1] or frame
local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
Line 665: Line 812:
table.sort(patches,
table.sort(patches,
function(a, b)
function(a, b)
if a.level == b.level then
local abyssA, abyssB = a.abyssalLevel or 0, b.abyssalLevel or 0
return a.id < b.id
if abyssA == abyssB then
if a.level == b.level then
return a.id < b.id
else
return a.level < b.level
end
else
else
return a.level < b.level
return abyssA < abyssB
end
end
end)
end)
Line 676: Line 828:


local result = '{|class="wikitable sortable stickyHeader"'
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'
result = result..'\r\n!Plot!!Requirements!!Cost'


for i, patch in ipairs(patches) do
for i, patch in ipairs(patches) do
local level = Skills.getRecipeLevel(skillID, patch)
local reqText = Skills.getRecipeRequirementText(skillID, patch)
local costText = Common.getCostString({ items = patch.itemCosts, currencies = patch.currencyCosts }, 'Free')
local costVal = (patch.currencyCosts ~= nil and patch.currencyCosts[1].quantity) or 0
result = result..'\r\n|-\r\n|'..i
result = result..'\r\n|-\r\n|'..i
result = result..'||style="text-align:right;" data-sort-value="' .. patch.level .. '"|'..patch.level
result = result..'||style="text-align:right;" data-sort-value="' .. level .. '"|'..reqText
local costText = Common.getCostString({ items = patch.itemCosts, gp = patch.gpCost }, 'Free')
result = result..'||style="text-align:right;" data-sort-value="'..costVal..'"|'..costText
result = result..'||style="text-align:right;" data-sort-value="'..patch.gpCost..'"|'..costText
end
end


Line 689: Line 845:
end
end


function p._buildAstrologyConstellationTable()
function p._buildAstrologyConstellationTable(realmID)
local modTypes = {
{
["name"] = 'Standard',
["modKey"] = 'standardModifiers',
["levels"] = SkillData.Astrology.standardModifierLevels,
["inUse"] = false
},
{
["name"] = 'Unique',
["modKey"] = 'uniqueModifiers',
["levels"] = SkillData.Astrology.uniqueModifierLevels,
["inUse"] = false
},
{
["name"] = 'Abyssal',
["modKey"] = 'abyssalModifiers',
["levels"] = SkillData.Astrology.abyssalModifierLevels,
["inUse"] = false
},
}
 
local recipes = GameData.getEntities(SkillData.Astrology.recipes,
function(cons)
return Skills.getRecipeRealm(cons) == realmID
end
)
table.sort(recipes,
function(a, b)
return Skills.getRecipeLevel('Astrology', a) < Skills.getRecipeLevel('Astrology', b)
end
)
 
-- Figure out which mod types are to be displayed
for _, recipe in ipairs(recipes) do
for _, modType in ipairs(modTypes) do
if not modType.inUse then
local recipeMods = recipe[modType.modKey]
if recipeMods ~= nil and not Shared.tableIsEmpty(recipeMods) then
modType.inUse = true
end
end
end
end
 
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!rowspan="2" colspan="2"|Constellation!!rowspan="2"| ' .. Icons.Icon({"Astrology", type='skill', notext='true'}) .. ' Level')
table.insert(resultPart, '\n!rowspan="2" colspan="2"|Constellation!!rowspan="2"| ' .. Icons.Icon({"Astrology", type='skill', notext='true'}) .. ' Level')
table.insert(resultPart, '!!rowspan="2"| XP!!rowspan="2"| Skills!!colspan="2"| Standard Stars!!colspan="2"| Unique Stars')
table.insert(resultPart, '!!rowspan="2"| XP!!rowspan="2"| Skills')
 
local repCount = 0
for _, modType in ipairs(modTypes) do
if modType.inUse then
repCount = repCount + 1
table.insert(resultPart, '!!colspan="2"| ' .. modType.name .. ' Stars')
end
end
table.insert(resultPart, '\n|- class="headerRow-1"')
table.insert(resultPart, '\n|- class="headerRow-1"')
table.insert(resultPart, string.rep('\n! ' .. Icons.Icon({'Mastery', notext=true}) .. 'Level\n! Modifiers', 2))
table.insert(resultPart, string.rep('\n! ' .. Icons.Icon({'Mastery', notext=true}) .. 'Level\n! Modifiers', repCount))


local recipes = Shared.shallowClone(SkillData.Astrology.recipes)
table.sort(recipes, function(a, b) return a.level < b.level end)
for i, cons in ipairs(recipes) do
for i, cons in ipairs(recipes) do
-- Generate the list of modifiers first for the purpose of determining the
local modData = Skills._getConstellationModifiers(cons)
-- the number of rows required to display all stars
local name = cons.name
local modTypes = { 'standard', 'unique' }
local skillIconArray = {}
local modsRaw = {
for j, skillID in ipairs(cons.skillIDs) do
["standard"] = Skills._buildAstrologyModifierArray(cons, nil, true, false, false, false),
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
["unique"] = Skills._buildAstrologyModifierArray(cons, nil, false, true, false, false)
end
}
-- Building the list of standard & unique modifiers
local mods = {}
local modLevels = {}
local maxRows = 1
local maxRows = 1
for j, modType in ipairs(modTypes) do
for _, modTypeData in pairs(modData) do
mods[modType] = {}
maxRows = math.max(maxRows, Shared.tableCount(modTypeData))
modLevels[modType] = {}
for k, modifier in ipairs(modsRaw[modType]) do
local masteryLevel = modifier.group
if mods[modType][masteryLevel] == nil then
mods[modType][masteryLevel] = {}
table.insert(modLevels[modType], masteryLevel)
end
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2]
table.insert(mods[modType][masteryLevel], Constants._getModifierText(modifier[1], modMagnitude, false))
end
table.sort(modLevels[modType])
local typeCount = Shared.tableCount(modLevels[modType])
if typeCount > maxRows then
maxRows = typeCount
end
end
end
local name = cons.name
local rowSpan = (maxRows > 1 and 'rowspan="' .. maxRows .. '"') or ''
local rowSpan = (maxRows > 1 and 'rowspan="' .. maxRows .. '"') or ''
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\n|' .. rowSpan .. ' data-sort-value="' .. name .. '" id="'..name..'"| ')
table.insert(resultPart, '\n|' .. rowSpan .. ' data-sort-value="' .. name .. '" id="'..name..'"| ')
Line 739: Line 925:
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.level)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.level)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.baseExperience)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.baseExperience)
local skillIconArray = {}
for j, skillID in ipairs(cons.skillIDs) do
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
end
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. table.concat(skillIconArray, '<br/>'))
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. table.concat(skillIconArray, '<br/>'))


-- Generate table text for standard & unique modifiers
-- Generate table text for modifiers
for row = 1, maxRows, 1 do
for row = 1, maxRows, 1 do
for j, modType in ipairs(modTypes) do
for _, modType in ipairs(modTypes) do
local masteryLevel = (modLevels[modType] ~= nil and modLevels[modType][row]) or nil
if modType.inUse then
if masteryLevel ~= nil then
local masteryLevel = modType.levels[row]
table.insert(resultPart, '\n|style="text-align:right"| ' .. masteryLevel)
local rowModData = modData[modType.modKey][row]
table.insert(resultPart, '\n| ' .. table.concat(mods[modType][masteryLevel], '<br/>'))
if masteryLevel == nil or rowModData == nil then
else
table.insert(resultPart, '\n|colspan="2" class="table-na"| ')
table.insert(resultPart, '\n|colspan="2" class="table-na"| ')
else
table.insert(resultPart, '\n|style="text-align:right"| ' .. masteryLevel)
table.insert(resultPart, '\n| ' .. Modifiers.getModifiersText(rowModData, false, false, 10))
end
end
end
end
end
Line 768: Line 952:


function p.buildAstrologyConstellationTable(frame)
function p.buildAstrologyConstellationTable(frame)
return p._buildAstrologyConstellationTable()
local args = frame.args ~= nil and frame.args or frame
local realmName = args.realm
local realm = Skills.getRealmFromName(realmName)
if realm == nil then
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
end
 
return p._buildAstrologyConstellationTable(realm.id)
end
end


return p
return p