Anonymous

Module:Skills/Gathering: Difference between revisions

From Melvor Idle
Left align sources column
(getFishTable: Adjust formatting)
(Left align sources column)
 
(20 intermediate revisions by 4 users not shown)
Line 6: Line 6:
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local SkillData = GameData.skillData
local Common = require('Module:Common')
local Common = require('Module:Common')
local Items = require('Module:Items')
local Items = require('Module:Items')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Shop = require('Module:Shop')
local Skills = require('Module:Skills')
local Skills = require('Module:Skills')
local ItemSourceTables = require('Module:Items/SourceTables')
local ItemSourceTables = require('Module:Items/SourceTables')
-- TODO Move tool tables to Module:Shop
function p.getToolTable(toolName, searchString, modifiers, skillID)
local skillName = Constants.getSkillName(skillID)
local toolArray = Shop.getPurchases(
function(purch)
return purch.category == 'melvorD:SkillUpgrades' and string.find(purch.id, searchString) ~= nil
end)
if skillName == nil or Shared.tableIsEmpty(toolArray) then
return ''
end
if modifiers == nil then
modifiers = {}
end
local modTotal = {}
for i, modDef in ipairs(modifiers) do
modTotal[modDef.name] = 0
end
local headerRowSpan = (Shared.tableIsEmpty(toolArray) and 1) or 2
local resultPart = {}
table.insert(resultPart, '{| class="wikitable"')
table.insert(resultPart, '\r\n!rowspan="' .. headerRowSpan .. '" colspan="2"| Name')
table.insert(resultPart, '\r\n!rowspan="' .. headerRowSpan .. '"| ' .. Icons.Icon({skillName, type='skill', notext=true})..' Level')
table.insert(resultPart, '\r\n!rowspan="' .. headerRowSpan .. '"| Cost')
for i, modDef in ipairs(modifiers) do
modTotal[modDef.name] = 0
table.insert(resultPart, '\r\n!colspan="2"| ' .. modDef.header)
end
if headerRowSpan > 1 then
table.insert(resultPart, '\r\n|-' .. string.rep('\r\n!This ' .. toolName .. '\r\n!Total', Shared.tableCount(modifiers)))
end
for i, tool in ipairs(toolArray) do
local toolName = Shop._getPurchaseName(tool)
local toolCost = Shop.getCostString(tool.cost, false)
local toolCostSort = Shop._getPurchaseSortValue(tool)
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="min-width:25px" data-sort-value="' .. toolName .. '"| ' .. Icons.Icon({toolName, type='upgrade', size='50', notext=true}))
table.insert(resultPart, '\r\n| ' .. toolName)
local level = 1
if tool.purchaseRequirements ~= nil and not Shared.tableIsEmpty(tool.purchaseRequirements) then
for i, purchReq in ipairs(tool.purchaseRequirements) do
if purchReq.type == 'SkillLevel' and purchReq.skillID == skillID then
level = purchReq.level
break
end
end
end
table.insert(resultPart, '\r\n|style="text-align:right"| '..level)
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. toolCostSort .. '"| ' .. toolCost)
local cellStart = '\r\n|style="text-align:right"| '
if tool.contains ~= nil and tool.contains.modifiers ~= nil then
for j, modDef in ipairs(modifiers) do
local modName = modDef.name
local modVal = tool.contains.modifiers[modName]
if modVal ~= nil then
if type(modVal) == 'table' and type(modVal[1]) == 'table' and modVal[1].skillID ~= nil then
modVal = modVal[1].value
end
modTotal[modName] = modTotal[modName] + modVal
else
modVal = 0
end
table.insert(resultPart, cellStart .. (modVal == 0 and '' or modDef.sign) .. modVal .. modDef.suffix)
table.insert(resultPart, cellStart .. (modTotal[modName] == 0 and '' or modDef.sign) .. modTotal[modName] .. modDef.suffix)
end
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getAxeTable(frame)
local modifiers = {
{ name = 'decreasedSkillIntervalPercent', header = 'Cut Time Decrease', sign = '-', suffix = '%' },
{ name = 'increasedChanceToDoubleItemsSkill', header = 'Double Items Chance', sign = '+', suffix = '%' },
{ name = 'increasedBirdNestDropRate', header = Icons.Icon({'Bird Nest', 'Drop Chance', type='item', nolink=true}), sign = '+', suffix = '%' },
{ name = 'increasedChanceForAshInWoodcutting', header = Icons.Icon({'Ash', 'Drop Chance', type='item', nolink=true}), sign = '+', suffix = '%' }
}
return p.getToolTable('Axe', '_Axe$', modifiers, 'melvorD:Woodcutting')
end
function p.getPickaxeTable(frame)
local modifiers = {
{ name = 'decreasedSkillIntervalPercent', header = 'Mining Time Decrease', sign = '-', suffix = '%' },
{ name = 'increasedChanceToDoubleOres', header = '2x Ore Chance', sign = '+', suffix = '%' },
{ name = 'increasedChanceForOneExtraOre', header = '+1 Ore Chance', sign = '+', suffix = '%' },
{ name = 'increasedChanceForQualitySuperiorGem', header = 'Superior Gem Chance', sign = '+', suffix = '%' },
{ name = 'increasedMeteoriteOre', header = 'Increased ' .. Icons.Icon({'Meteorite Ore', type='item', notext=true}), sign = '+', suffix = '' }
}
return p.getToolTable('Pickaxe', '_Pickaxe$', modifiers, 'melvorD:Mining')
end
function p.getRodTable(frame)
local modifiers = {
{ name = 'decreasedSkillIntervalPercent', header = 'Catch Time Decrease', sign = '-', suffix = '%' },
{ name = 'increasedChanceForOneExtraFish', header = '+1 Fish Chance', sign = '+', suffix = '%' },
{ name = 'increasedChanceToFindLostChest', header = Icons.Icon({'Lost Chest', type='item', notext=true}) .. ' Chance', sign = '+', suffix = '%' },
{ name = 'increasedFishingCookedChance', header = 'Cooked Fish Chance', sign = '+', suffix = '%' }
}
return p.getToolTable('Rod', '_Rod$', modifiers, 'melvorD:Fishing')
end


function p.getRecipeRequirements(skillName, recipe)
function p.getRecipeRequirements(skillName, recipe)
Line 130: Line 21:
table.insert(reqText, Shared.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'}))
table.insert(reqText, Shared.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'}))
end
end
if recipe.shopItemPurchased ~= nil then
if type(recipe.requirements) == 'table' then
local purchReq = Shop.getPurchaseByID(recipe.shopItemPurchased)
local reqs = Common.getRequirementString(recipe.requirements)
if purchReq ~= nil then
if reqs ~= nil then
table.insert(reqText, Shop._getPurchaseIcon({purchReq}))
table.insert(reqText, reqs)
end
end
end
end
Line 146: Line 37:
table.insert(resultPart, '!!XP!!Cut Time!!XP/s!!GP/s')
table.insert(resultPart, '!!XP!!Cut Time!!XP/s!!GP/s')


for i, tree in ipairs(SkillData.Woodcutting.trees) do
local trees = Shared.shallowClone(SkillData.Woodcutting.trees)
table.sort(trees, function(a, b) return a.level < b.level end)
for i, tree in ipairs(trees) do
local log = Items.getItemByID(tree.productId)
local log = Items.getItemByID(tree.productId)
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}))
table.insert(resultPart, '\n|'..tree.name)
table.insert(resultPart, '\n|data-sort-value="'..tree.name..'"|'..Icons.getExpansionIcon(tree.id)..tree.name)
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}))
Line 168: Line 61:
function p.getSpecialFishingTable(frame)
function p.getSpecialFishingTable(frame)
local totalWt, lootValue = 0, 0
local totalWt, lootValue = 0, 0
local itemArray = GameData.getEntities(SkillData.Fishing.specialItems, function(item) return true end)
local itemArray = Shared.shallowClone(SkillData.Fishing.specialItems)
for i, itemDef in ipairs(itemArray) do
for i, itemDef in ipairs(itemArray) do
totalWt = totalWt + itemDef.weight
totalWt = totalWt + itemDef.weight
Line 228: Line 121:
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value')
table.insert(resultPart, '!!XP!!Respawn Time!!Ore Value')


local mineData = GameData.getEntities(SkillData.Mining.rockData, function(rock) return true end)
local mineData = Shared.shallowClone(SkillData.Mining.rockData)
table.sort(mineData, function(a, b) return a.level < b.level end)
table.sort(mineData, function(a, b) return a.level < b.level end)


Line 243: Line 136:
table.insert(resultPart, '\n|-\n|class="table-img" data-sort-value="' .. rockName .. '"| '..Icons.Icon({oreData.name, type='rock', size='50', notext=true, nolink=true}))
table.insert(resultPart, '\n|-\n|class="table-img" data-sort-value="' .. rockName .. '"| '..Icons.Icon({oreData.name, type='rock', size='50', notext=true, nolink=true}))
table.insert(resultPart, '\n| ' .. rockName)
table.insert(resultPart, '\n| data-sort-value="' ..rockName.. '"|'..Icons.getExpansionIcon(oreData.id) .. rockName)
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}))
Line 267: Line 160:
local gemDataKey = validTypes[gemType]
local gemDataKey = validTypes[gemType]
if gemDataKey == nil then
if gemDataKey == nil then
return 'ERROR: No such gem type "' .. gemType .. '"[[Category:Pages with script errors]]'
return Shared.printError('No such gem type "' .. gemType .. '"')
end
end
Line 277: Line 170:
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{|class="wikitable sortable"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\n!colspan=2|Gem!!Gem Chance!!Gem Price')
table.insert(resultPart, '\n!colspan=2|Gem!!Gem Chance!!Gem Price')
Line 286: Line 179:
table.insert(resultPart, '\n|-\n|class="table-img"| ')
table.insert(resultPart, '\n|-\n|class="table-img"| ')
table.insert(resultPart, Icons.Icon({gemItem.name, type='item', size='50', notext=true}))
table.insert(resultPart, Icons.Icon({gemItem.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\n| ' .. 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 .. '"| ' .. Icons.GP(gemItem.sellsFor))
Line 301: Line 194:


function p.getFishTable(frame)
function p.getFishTable(frame)
local recipeList = GameData.getEntities(SkillData.Fishing.fish, function(fish) return true end)
local recipeList = Shared.shallowClone(SkillData.Fishing.fish)
table.sort(recipeList, function(a, b) return a.level < b.level end)
table.sort(recipeList, function(a, b) return a.level < b.level end)


Line 318: Line 211:
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!Fish\r\n!Name\r\n!' .. Icons.Icon({'Fishing', type='skill', notext=true}) .. ' Level\r\n!Catch Time')
table.insert(resultPart, '\n!colspan="2" rowspan="2"|Fish\n!rowspan="2"|' .. Icons.Icon({'Fishing', type='skill', notext=true}) .. ' Level')
table.insert(resultPart, '\r\n!XP\r\n!Value\r\n!XP/s\r\n!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"|GP/s')
table.insert(resultPart, '\r\n!' .. 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')
for i, recipe in ipairs(recipeList) do
for i, recipe in ipairs(recipeList) do
local fish = Items.getItemByID(recipe.productId)
local fish = Items.getItemByID(recipe.productId)
if fish ~= nil then
if fish ~= nil then
local timeMin, timeMax = recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000
local timeAvg = (timeMin + timeMax) / 2
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)
Line 330: Line 226:
local GPs = fish.sellsFor / timeSortVal
local GPs = fish.sellsFor / timeSortVal
local cookSortVal = cookReq[recipe.productId] or 0
local cookSortVal = cookReq[recipe.productId] or 0
local cookClass = (cookReq[recipe.productId] ~= nil and '' or 'class="table-na" ')
local cookStyle = (cookReq[recipe.productId] ~= nil and 'style="text-align:right" ' or 'class="table-na" ')
local cookStr = cookReq[recipe.productId] or 'N/A'
local cookStr = cookReq[recipe.productId] or 'N/A'
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\n|-')
table.insert(resultPart, '\r\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, '\r\n| ' .. 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, '\r\n|style="text-align:right"| ' .. recipe.level)
table.insert(resultPart, '\n|style="text-align:right"| ' .. recipe.level)
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. timeSortVal .. '"| ' .. timeStr)
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeMin .. '"| ' .. string.format("%.1fs", timeMin))
table.insert(resultPart, '\r\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="' .. timeMax .. '"| ' .. string.format("%.1fs", timeMax))
table.insert(resultPart, '\r\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. timeAvg .. '"| ' .. string.format("%.1fs", timeAvg))
table.insert(resultPart, '\r\n|style="text-align:right"| ' .. Shared.round(XPs, 2, 2))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. recipe.baseExperience .. '"| ' .. Shared.formatnum(recipe.baseExperience))
table.insert(resultPart, '\r\n|data-sort-value="' .. GPs .. '"|' .. Icons.GP(Shared.round(GPs, 2, 2)))
table.insert(resultPart, '\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Icons.GP(fish.sellsFor))
table.insert(resultPart, '\r\n|' .. cookClass .. 'style="text-align:right" data-sort-value="' .. cookSortVal .. '"| ' .. cookStr)
table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.round(XPs, 2, 2))
table.insert(resultPart, '\n|data-sort-value="' .. GPs .. '"|' .. Icons.GP(Shared.round(GPs, 2, 2)))
table.insert(resultPart, '\n|' .. cookStyle .. 'data-sort-value="' .. cookSortVal .. '"| ' .. cookStr)
end
end
end
end
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\n|}')
return table.concat(resultPart)
return table.concat(resultPart)
end
end
Line 356: Line 254:
for i, area in ipairs(SkillData.Fishing.areas) do
for i, area in ipairs(SkillData.Fishing.areas) do
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n| style ="text-align: left;" |'..area.name
result = result..'\r\n| style ="text-align: left;" |'..Icons.getExpansionIcon(area.id)..area.name


local fishArray = {}
local fishArray = {}
Line 391: Line 289:
local odds = drop.chance
local odds = drop.chance
rareTxt = rareTxt..'\r\n|-\r\n|'..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..'"|'..Icons.GP(thisItem.sellsFor)
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))
Line 428: Line 326:
table.insert(normalTxt, '!!Price!!colspan="2"|Chance')
table.insert(normalTxt, '!!Price!!colspan="2"|Chance')


local lootTable = GameData.getEntities(npc.lootTable, function(loot) return true end)
local lootTable = Shared.shallowClone(npc.lootTable)
--Then sort the loot table by weight
--Then sort the loot table by weight
table.sort(lootTable, function(a, b) return a.weight > b.weight end)
table.sort(lootTable, function(a, b) return a.weight > b.weight end)
Line 490: Line 388:


--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.id)
table.insert(sectionTxt, rareTxt)
table.insert(sectionTxt, rareTxt)
Line 516: Line 414:
areaTxt = areaTxt..txt
areaTxt = areaTxt..txt
end
end
areaTxt = areaTxt..'\r\n|-class="sortbottom" \r\n!colspan="3"|Total:'
areaTxt = areaTxt..'\r\n|style="text-align:right"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))..'||'
areaTxt = areaTxt..'style="text-align:right"|'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%'
areaTxt = areaTxt..'\r\n|}'
areaTxt = areaTxt..'\r\n|}'
table.insert(sectionTxt, areaTxt)
table.insert(sectionTxt, areaTxt)
Line 530: Line 425:
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
if npc.uniqueDrop.quantity > 1 then
if npc.uniqueDrop.quantity > 1 then
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.quantity})
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.quantity}) .. '.'
else
else
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'})
uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'}) .. '.'
end
end
table.insert(sectionTxt, uniqueTxt)
table.insert(sectionTxt, uniqueTxt)
Line 545: Line 440:
local npc = Skills.getThievingNPC(npcName)
local npc = Skills.getThievingNPC(npcName)
if npc == nil then
if npc == nil then
return "ERROR: Invalid Thieving NPC "..npcName.."[[Category:Pages with script errors]]"
return Shared.printError('Invalid Thieving NPC "' .. npcName .. '"')
end
end


Line 555: Line 450:
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!!GP!!Unique Drop'
local npcArray = GameData.getEntities(SkillData.Thieving.npcs, function(npc) return true end)
local npcArray = Shared.shallowClone(SkillData.Thieving.npcs)
table.sort(npcArray, function(a, b) return a.level < b.level end)
table.sort(npcArray, function(a, b) return a.level < b.level end)
for i, npc in ipairs(npcArray) do
for i, npc in ipairs(npcArray) do
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..'||'..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)
local area = Skills.getThievingNPCArea(npc)
Line 640: Line 535:
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
if category == nil then
if category == nil then
return 'ERROR: Invalid farming category. Please choose Allotments, Herbs, or Trees[[Category:Pages with script errors]]'
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
end
end
local seedList = GameData.getEntities(SkillData.Farming.recipes,
local seedList = GameData.getEntities(SkillData.Farming.recipes,
Line 650: Line 545:
end
end


local result = '{|class="wikitable sortable stickyHeader"'
local tbl = mw.html.create()
result = result..'\r\n|- class="headerRow-0"'
local html = tbl:tag("table")
result = result..'\r\n!colspan=2|Seeds!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level'
        :addClass("wikitable sortable stickyHeader")
result = result..'!!XP!!Growth Time!!Seed Value'
    :tag('tr'):addClass("headerRow-0")
        :tag('th'):attr("colspan", 2):wikitext("Seeds")
        :tag('th'):wikitext(Icons.Icon({'Farming', type='skill', notext=true}) .. " Level")
        :tag('th'):wikitext('XP')
        :tag('th'):wikitext('Growth Time')
        :tag('th'):wikitext('Seed Value')
 
if category.id == 'melvorD:Allotment' then
if category.id == 'melvorD:Allotment' then
result = result..'!!colspan="2"|Crop!!Crop Healing!!Crop Value'
html:tag('th'):attr("colspan", 2):wikitext("Crop")
:tag('th'):wikitext('Crop Healing')
:tag('th'):wikitext('Crop Value')
elseif category.id == 'melvorD:Herb' then
elseif category.id == 'melvorD:Herb' then
result = result..'!!colspan="2"|Herb!!Herb Value'
html:tag('th'):attr("colspan", 2):wikitext("Herb")
:tag('th'):wikitext('Herb Value')
elseif category.id == 'melvorD:Tree' then
elseif category.id == 'melvorD:Tree' then
result = result..'!!colspan="2"|Logs!!Log Value'
html:tag('th'):attr("colspan", 2):wikitext("Logs")
:tag('th'):wikitext('Log Value')
end
end
result = result..'!!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 a.level < b.level end)
Line 669: Line 574:
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
result = result..'\r\n|-'
html =  
result = result..'\r\n|'..Icons.Icon({seedItem.name, type='item', size='50', notext=true})..'||[['..seedItem.name..']]'
html:tag('tr')
result = result..'||'..seed.level..'||'..Shared.formatnum(seed.baseExperience)
:tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', size='50', notext=true}))
result = result..'||data-sort-value="'..(seed.baseInterval / 1000)..'"|'..Shared.timeString(seed.baseInterval / 1000, true)
:tag('td'):wikitext(Icons.getExpansionIcon(seedItem.id) .. Icons.Icon({seedItem.name, type='item', noicon=true}))
result = result..'||data-sort-value="'..seedItem.sellsFor..'"|'..Icons.GP(seedItem.sellsFor)
:tag('td'):wikitext(seed.level)
result = result..'||'..Icons.Icon({productItem.name, type='item', size='50', notext=true})..'||[['..productItem.name..']]'
:tag('td'):wikitext(Shared.formatnum(seed.baseExperience))
:tag('td'):attr('data-sort-value', (seed.baseInterval / 1000))
  :wikitext(Shared.timeString(seed.baseInterval / 1000, true))
:tag('td'):attr('data-sort-value', seedItem.sellsFor)
      :wikitext(Icons.GP(seedItem.sellsFor))
: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}))
 
if category.id == 'melvorD:Allotment' then
if category.id == 'melvorD:Allotment' then
result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..((productItem.healsFor or 0) * 10)
html:tag('td'):wikitext(Icons.Icon({'Hitpoints', type='skill', notext=true}))
  :wikitext(' ')
  :wikitext(((productItem.healsFor or 0) * 10))
end
end
result = result..'||data-sort-value="'..productItem.sellsFor..'"|'..Icons.GP(productItem.sellsFor)
html =
result = result..'||'..ItemSourceTables._getItemSources(seedItem)
html:tag('td'):attr('data-sort-value', productItem.sellsFor)
  :wikitext(Icons.GP(productItem.sellsFor))
:tag('td'):wikitext(ItemSourceTables._getItemSources(seedItem))
  :css('text-align', 'left')
:done()
end
end
end
end


result = result..'\r\n|}'
return tostring(tbl:done())
return result
end
end


Line 710: Line 627:
if product ~= nil and product.healsFor ~= nil and product.healsFor > 0 then
if product ~= nil and product.healsFor ~= nil and product.healsFor > 0 then
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({product.name, type='item', notext='true', size='50'})..'||[['..product.name..']]'
result = result..'\r\n|'..Icons.Icon({product.name, type='item', notext='true', size='50'})
result = result..'|| ' .. Icons.getExpansionIcon(product.id) .. Icons.Icon({product.name, type='item', noicon=true})
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)
Line 726: Line 644:
local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
if category == nil then
if category == nil then
return 'ERROR: Invalid farming category. Please choose Allotments, Herbs, or Trees[[Category:Pages with script errors]]'
return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
end
end
local patches = GameData.getEntities(SkillData.Farming.plots,
local patches = GameData.getEntities(SkillData.Farming.plots,
function(plot)
function(plot)
return plot.categoryID == category.id
return plot.categoryID == category.id
end)
table.sort(patches,
function(a, b)
if a.level == b.level then
return a.id < b.id
else
return a.level < b.level
end
end)
end)
if Shared.tableIsEmpty(patches) then
if Shared.tableIsEmpty(patches) then
Line 739: Line 665:
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'


for i, patch in Shared.skpairs(patches) do
for i, patch in ipairs(patches) do
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="' .. patch.level .. '"|'..patch.level
local costText = (patch.gpCost > 0 and Icons.GP(patch.gpCost)) or 'Free'
local costText = Common.getCostString({ items = patch.itemCosts, gp = patch.gpCost }, 'Free')
result = result..'||style="text-align:right;" data-sort-value="'..patch.gpCost..'"|'..costText
result = result..'||style="text-align:right;" data-sort-value="'..patch.gpCost..'"|'..costText
end
end
Line 751: Line 677:


function p._buildAstrologyConstellationTable()
function p._buildAstrologyConstellationTable()
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="2"|Constellation!!'..Icons.Icon({"Astrology", type='skill', notext='true'})..' Level'
table.insert(resultPart, '\n|- class="headerRow-0"')
result = result..'!!XP!!Skills!!Standard Modifiers!!Unique Modifiers'
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, '\n|- class="headerRow-1"')
table.insert(resultPart, string.rep('\n! ' .. Icons.Icon({'Mastery', notext=true}) .. 'Level\n! Modifiers', 2))


for i, cons in ipairs(SkillData.Astrology.recipes) do
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
-- Generate the list of modifiers first for the purpose of determining the
-- the number of rows required to display all stars
local modTypes = { 'standard', 'unique' }
local modsRaw = {
["standard"] = Skills._buildAstrologyModifierArray(cons, nil, true, false, false, false),
["unique"] = Skills._buildAstrologyModifierArray(cons, nil, false, true, false, false)
}
-- Building the list of standard & unique modifiers
local mods = {}
local modLevels = {}
local maxRows = 1
for j, modType in ipairs(modTypes) do
mods[modType] = {}
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
local name = cons.name
local name = cons.name
result = result..'\r\n|-'
local rowSpan = (maxRows > 1 and 'rowspan="' .. maxRows .. '"') or ''
result = result..'\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='constellation', size='50', notext=true})..'||'..name
table.insert(resultPart, '\n|-')
result = result..'||'..cons.level..'||'..cons.baseExperience
table.insert(resultPart, '\n|' .. rowSpan .. ' data-sort-value="' .. name .. '" id="'..name..'"| ')
table.insert(resultPart, Icons.Icon({name, type='constellation', size='50', notext=true}))
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. Icons.getExpansionIcon(cons.id) .. name)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.level)
table.insert(resultPart, '\n|' .. rowSpan .. ' style="text-align:right"| ' .. cons.baseExperience)


local skillIconArray = {}
local skillIconArray = {}
Line 766: Line 731:
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
end
end
result = result..'||'..table.concat(skillIconArray, '<br/>')
table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. table.concat(skillIconArray, '<br/>'))


--Adding a function that converts an array of connected bonuses into text [Falterfire 22/10/27]
-- Generate table text for standard & unique modifiers
local groupedModsToText = function(allMods)
for row = 1, maxRows, 1 do
local outArray = {}
for j, modType in ipairs(modTypes) do
for i, group in ipairs(allMods) do
local masteryLevel = (modLevels[modType] ~= nil and modLevels[modType][row]) or nil
local groupTxt = table.concat(group, ' & ')
if masteryLevel ~= nil then
table.insert(outArray, groupTxt)
table.insert(resultPart, '\n|style="text-align:right"| ' .. masteryLevel)
table.insert(resultPart, '\n| ' .. table.concat(mods[modType][masteryLevel], '<br/>'))
else
table.insert(resultPart, '\n|colspan="2" class="table-na"| ')
end
end
return table.concat(outArray, '<br/>')
end
end
 
if row < maxRows then
local standModsRaw = Skills._buildAstrologyModifierArray(cons, nil, true, false, false, false)
table.insert(resultPart, '\n|-')
local standMods = {}
end
--Building the list of Standard modifiers:
for j, modifier in ipairs(standModsRaw) do
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2]
local groupNum = modifier.group
if standMods[groupNum] == nil then standMods[groupNum] = {} end
table.insert(standMods[groupNum], Constants._getModifierText(modifier[1], modMagnitude, false))
end
end
result = result..'|| '..groupedModsToText(standMods)
--Building the list of all Unique Modifiers
local uModsRaw = Skills._buildAstrologyModifierArray(cons, nil, false, true, false, false)
local uMods = {}
for j, modifier in ipairs(uModsRaw) do
local modMagnitude = type(modifier[2]) == 'table' and {modifier[2]} or modifier[2]
local groupNum = modifier.group
if uMods[groupNum] == nil then uMods[groupNum] = {} end
table.insert(uMods[groupNum], Constants._getModifierText(modifier[1], modMagnitude, false))
end
result = result..'|| '..groupedModsToText(uMods)
end
end
result = result..'\r\n|}'
table.insert(resultPart, '\n|}')


return result
return table.concat(resultPart)
end
end


915

edits