|
|
(30 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
| --Splitting some functions into here to avoid bloating a single file
| |
| local p = {}
| |
|
| |
|
| local Constants = require('Module:Constants')
| |
| local Shared = require('Module:Shared')
| |
| local GameData = require('Module:GameData')
| |
| local SkillData = GameData.skillData
| |
| local Common = require('Module:Common')
| |
| local Modifiers = require('Module:Modifiers')
| |
| local Items = require('Module:Items')
| |
| local Icons = require('Module:Icons')
| |
| local Skills = require('Module:Skills')
| |
| local ItemSourceTables = require('Module:Items/SourceTables')
| |
|
| |
| local function lootValueText(lootValue)
| |
| local returnPart = {}
| |
| for _, currencyDefn in ipairs(GameData.rawData.currencies) do
| |
| -- Guarantee order by iterating through currency game data definition
| |
| local currID = currencyDefn.id
| |
| local val = Shared.round(lootValue[currID], 2, 2)
| |
| if val ~= nil then
| |
| table.insert(returnPart, Icons._Currency(currID, val))
| |
| end
| |
| end
| |
| return table.concat(returnPart, ', ')
| |
| end
| |
|
| |
| 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 html = mw.html.create('table')
| |
| :addClass('wikitable sortable stickyHeader')
| |
|
| |
| html:tag('tr')
| |
| :addClass("headerRow-0")
| |
| :tag('th'):attr('colspan', 2)
| |
| :wikitext('Tree')
| |
| :tag('th'):attr('colspan', 2)
| |
| :wikitext('Logs')
| |
| :tag('th'):wikitext('Requirements')
| |
| :tag('th'):wikitext('[[DLC]]')
| |
| :tag('th'):wikitext('XP')
| |
| :tag('th'):wikitext('Cut Time')
| |
| :tag('th'):wikitext('XP/s')
| |
| :tag('th'):wikitext('Price/s')
| |
|
| |
| local trees = GameData.getEntities(SkillData.Woodcutting.trees,
| |
| 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
| |
| 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 sellCurrency = log.sellsForCurrency or 'melvorD:GP'
| |
| local XPSec = baseXP / (baseInt / 1000)
| |
| local currSec = Shared.round(log.sellsFor / (baseInt / 1000), 2, 2)
| |
|
| |
|
| |
| html:tag('tr')
| |
| :tag('td'):wikitext(Icons.Icon({log.name, img=tree.name, type='tree', notext=true}))
| |
| :addClass('table-img')
| |
| :attr('data-sort-value', tree.name)
| |
| :tag('td'):wikitext(tree.name)
| |
| :tag('td'):wikitext(Icons.Icon({log.name, type='item', notext=true}))
| |
| :addClass('table-img')
| |
| :attr('data-sort-value', log.name)
| |
| :tag('td'):wikitext('[[' .. log.name .. ']]')
| |
| :tag('td'):wikitext(reqText)
| |
| :attr('data-sort-value', level)
| |
| :tag('td'):wikitext(Icons.getDLCColumnIcon(tree.id))
| |
| :attr('data-sort-value', Icons.getExpansionID(tree.id))
| |
| :css('text-align', 'center')
| |
| :tag('td'):wikitext(Shared.formatnum(baseXP))
| |
| :css('text-align', 'right')
| |
| :tag('td'):wikitext(Shared.timeString(baseInt / 1000, true))
| |
| :attr('data-sort-value', baseInt)
| |
| :css('text-align', 'right')
| |
| :tag('td'):wikitext(Shared.round(XPSec, 2, 2))
| |
| :css('text-align', 'right')
| |
| :tag('td'):wikitext(Icons._Currency(sellCurrency, currSec))
| |
| :attr('data-sort-value', currSec)
| |
| :css('text-align', 'right')
| |
| end
| |
|
| |
| return tostring(html)
| |
| end
| |
|
| |
| function p.getSpecialFishingTable(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 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
| |
| totalWt = totalWt + itemDef.weight
| |
| end
| |
| -- Sort the loot table by weight in descending order
| |
| table.sort(itemArray, function(a, b) return a.weight > b.weight end)
| |
|
| |
| local html = mw.html.create('table')
| |
| html:addClass('wikitable sortable stickyHeader')
| |
|
| |
| -- Add header row
| |
| local headerRow = html:tag('tr'):addClass('headerRow-0')
| |
| headerRow:tag('th'):attr('colspan', '2'):wikitext('Item')
| |
| headerRow:tag('th'):wikitext('Value')
| |
| headerRow:tag('th'):attr('colspan', '2'):wikitext('Chance')
| |
|
| |
| -- Add item rows
| |
| for i, itemDef in ipairs(itemArray) do
| |
| local item = Items.getItemByID(itemDef.itemID)
| |
| if item ~= nil then
| |
| 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
| |
| local fmt = (dropChance < 0.10 and '%.2g') or '%.2f'
| |
| local row = html:tag('tr')
| |
|
| |
| row:tag('td'):addClass('table-img'):wikitext(Icons.Icon({item.name, type='item', notext=true}))
| |
| row:tag('td'):wikitext(Icons.Icon({item.name, type='item', noicon=true}))
| |
| row:tag('td')
| |
| :attr('data-sort-value', item.sellsFor)
| |
| :wikitext(Items.getValueText(item))
| |
| row:tag('td')
| |
| :css('text-align', 'right')
| |
| :attr('data-sort-value', itemDef.weight)
| |
| :wikitext(Shared.fraction(itemDef.weight, totalWt))
| |
| row:tag('td')
| |
| :css('text-align', 'right')
| |
| :wikitext(string.format(fmt, dropChance) .. '%')
| |
|
| |
| if lootValue[currID] == nil then
| |
| lootValue[currID] = 0
| |
| end
| |
| lootValue[currID] = lootValue[currID] + (dropChance / 100 * item.sellsFor)
| |
| end
| |
| end
| |
|
| |
| local result = tostring(html)
| |
| local averageValueText = 'The average value of a roll on the special fishing loot table is ' .. lootValueText(lootValue)
| |
| return result .. '\n' .. averageValueText
| |
| end
| |
|
| |
| function p.getFishingJunkTable(frame)
| |
| local html = mw.html.create('table')
| |
| html:addClass('wikitable sortable stickyHeader')
| |
|
| |
| -- Add header row
| |
| local headerRow = html:tag('tr'):addClass('headerRow-0')
| |
| headerRow:tag('th'):attr('colspan', '2'):wikitext('Item')
| |
| headerRow:tag('th'):wikitext('Value')
| |
|
| |
| local itemArray = {}
| |
| for i, itemID in ipairs(SkillData.Fishing.junkItemIDs) do
| |
| local item = Items.getItemByID(itemID)
| |
| if item ~= nil then
| |
| table.insert(itemArray, item)
| |
| end
| |
| end
| |
| table.sort(itemArray, function(a, b) return a.name < b.name end)
| |
|
| |
| -- Add item rows
| |
| for i, item in ipairs(itemArray) do
| |
| local row = html:tag('tr')
| |
| row:tag('td'):addClass('table-img')
| |
| :wikitext(Icons.Icon({item.name, type='item', notext=true}))
| |
| row:tag('td'):wikitext(Icons.Icon({item.name, type='item', noicon=true}))
| |
| row:tag('td')
| |
| :attr('data-sort-value', item.sellsFor)
| |
| :wikitext(Items.getValueText(item))
| |
| end
| |
|
| |
| return tostring(html)
| |
| end
| |
|
| |
| 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 html = mw.html.create('table')
| |
| :addClass("wikitable sortable stickyHeader")
| |
|
| |
| html:tag('tr'):addClass("headerRow-0")
| |
| :tag('th'):wikitext('Rock')
| |
| :attr('colspan', 2)
| |
| :tag('th'):wikitext('Ore')
| |
| :attr('colspan', 2)
| |
| :tag('th'):wikitext('Type')
| |
| :tag('th'):wikitext('Requirements')
| |
| :tag('th'):wikitext('[[DLC]]')
| |
| :tag('th'):wikitext('XP')
| |
| :tag('th'):wikitext('Respawn<br>Time')
| |
| :tag('th'):wikitext('Ore Value')
| |
|
| |
| local mineData = GameData.getEntities(SkillData.Mining.rockData,
| |
| 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
| |
| 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 respawnSort, respawnText = 0, 'N/A'
| |
| if oreData.hasPassiveRegen then
| |
| respawnSort = oreData.baseRespawnInterval / 1000
| |
| 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
| |
| local rockName = Icons.Icon({oreData.name, type='rock', noicon=true, nolink=true})
| |
| local qtyText = (oreData.baseQuantity > 1 and '<b>' .. oreData.baseQuantity .. 'x</b> ') or ''
| |
|
| |
| local row = html:tag('tr')
| |
| row :tag('td'):wikitext(Icons.Icon({oreData.name, type='rock', notext=true, nolink=true}))
| |
| :addClass('table-img')
| |
| :attr('data-sort-value', rockName)
| |
| :tag('td'):wikitext(rockName)
| |
| :tag('td'):wikitext(Icons.Icon({ore.name, type='item', notext=true}))
| |
| :addClass('table-img')
| |
| :attr('data-sort-value', ore.name)
| |
| :tag('td'):wikitext(qtyText .. '[[' .. ore.name .. ']]')
| |
| :tag('td'):wikitext(categoryName)
| |
| :tag('td'):wikitext(reqText)
| |
| :attr('data-sort-value', level)
| |
| :tag('td'):wikitext(Icons.getDLCColumnIcon(oreData.id))
| |
| :attr('data-sort-value', Icons.getExpansionID(oreData.id))
| |
| :css('text-align', 'center')
| |
| :tag('td'):wikitext(Shared.formatnum(baseXP))
| |
| :css('text-align', 'right')
| |
| local respawn =
| |
| row:tag('td'):wikitext(respawnText)
| |
| :attr('data-sort-value', respawnSort)
| |
| row:tag('td'):wikitext(Items.getValueText(ore))
| |
| :attr('data-sort-value', ore.sellsFor)
| |
|
| |
| if respawnText == 'N/A' then
| |
| respawn:addClass('table-na')
| |
| else
| |
| respawn:css('text-align', 'right')
| |
| end
| |
| end
| |
|
| |
| return tostring(html)
| |
| end
| |
|
| |
| function p._getMiningGemsTable(gemType)
| |
| if type(gemType) ~= 'string' then
| |
| gemType = 'Normal'
| |
| end
| |
| local validTypes = {
| |
| ["Normal"] = 'randomGems',
| |
| ["Superior"] = 'randomSuperiorGems',
| |
| ["Abyssal"] = 'randomAbyssalGems'
| |
| }
| |
| local gemDataKey = validTypes[gemType]
| |
| if gemDataKey == nil then
| |
| return Shared.printError('No such gem type "' .. gemType .. '"')
| |
| end
| |
|
| |
| local gemData = GameData.rawData[gemDataKey]
| |
| local totalWeight = 0
| |
| for i, gem in ipairs(gemData) do
| |
| totalWeight = totalWeight + gem.weight
| |
| end
| |
|
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{|class="wikitable sortable"')
| |
| table.insert(resultPart, '\n|- class="headerRow-0"')
| |
| table.insert(resultPart, '\n!colspan=2|Gem!!Gem Chance!!Gem Price')
| |
|
| |
| for i, gem in ipairs(gemData) do
| |
| local gemItem = Items.getItemByID(gem.itemID)
| |
| local gemPct = gem.weight / totalWeight * 100
| |
| table.insert(resultPart, '\n|-\n|class="table-img"| ')
| |
| table.insert(resultPart, Icons.Icon({gemItem.name, type='item', notext=true}))
| |
| table.insert(resultPart, '\n| data-sort-value="'..gemItem.name..'"|'..Icons.getDLCColumnIcon(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|data-sort-value="' .. gemItem.sellsFor .. '"| ' .. Items.getValueText(gemItem))
| |
| end
| |
|
| |
| table.insert(resultPart, '\n|}')
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.getMiningGemsTable(frame)
| |
| local gemType = frame.args ~= nil and frame.args[1] or frame
| |
| return p._getMiningGemsTable(gemType)
| |
| end
| |
|
| |
| function p.getFishTable(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 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
| |
| local cookRecipes = {}
| |
| 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
| |
| -- but currently holds
| |
| for j, mat in ipairs(recipe.itemCosts) do
| |
| if cookRecipes[mat.id] == nil then
| |
| cookRecipes[mat.id] = recipe
| |
| end
| |
| end
| |
| end
| |
|
| |
| local resultPart = {}
| |
| table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
| |
| table.insert(resultPart, '\n|- class="headerRow-0"')
| |
| 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"|Price/s')
| |
| 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
| |
| local fish = Items.getItemByID(recipe.productId)
| |
| 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 timeStr = string.format("%.1fs - %.1fs", recipe.baseMinInterval / 1000, recipe.baseMaxInterval / 1000)
| |
| local level = Skills.getRecipeLevel(skillID, recipe)
| |
| local reqText = Skills.getRecipeRequirementText(skillID, recipe)
| |
| local baseXP = recipe.baseAbyssalExperience or recipe.baseExperience
| |
| local XPSec = baseXP / timeSortVal
| |
| 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|class="table-img"| ' .. Icons.Icon({fish.name, type='item', notext=true}))
| |
| table.insert(resultPart, '\n|data-sort-value="'..fish.name..'"|'..Icons.getDLCColumnIcon(fish.id) .. Icons.Icon({fish.name, type='item', noicon=true}))
| |
| 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="' .. 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="' .. baseXP .. '"| ' .. Shared.formatnum(baseXP))
| |
| table.insert(resultPart, '\n|data-sort-value="' .. fish.sellsFor .. '"| ' .. Items.getValueText(fish))
| |
| table.insert(resultPart, '\n|style="text-align:right"| ' .. Shared.round(XPSec, 2, 2))
| |
| table.insert(resultPart, '\n|data-sort-value="' .. currPerSec .. '"|' .. Icons._Currency(sellCurrency, currPerSec))
| |
| table.insert(resultPart, '\n|' .. cookStyle .. 'data-sort-value="' .. cookLevel .. '"| ' .. cookStr)
| |
| end
| |
| end
| |
| table.insert(resultPart, '\n|}')
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| 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"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!Name\r\n!Fish\r\n!Fish Chance'
| |
| result = result..'\r\n!Junk Chance\r\n!Special Chance'
| |
|
| |
| 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| style ="text-align: left;" |'..Icons.getDLCColumnIcon(area.id)..area.name
| |
|
| |
| local fishArray = {}
| |
| for j, fishID in ipairs(area.fishIDs) do
| |
| local fishItem = Items.getItemByID(fishID)
| |
| if fishItem ~= nil then
| |
| table.insert(fishArray, Icons.Icon({fishItem.name, type='item'}))
| |
| end
| |
| end
| |
| result = result..'\r\n|'..table.concat(fishArray, '<br/>')
| |
|
| |
| result = result..'\r\n| style="text-align:right"|'..area.fishChance..'%'
| |
| result = result..'\r\n| style="text-align:right"|'..area.junkChance..'%'
| |
| result = result..'\r\n| style="text-align:right"|'..area.specialChance..'%'
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| function p.getThievingGeneralRareTable(frame)
| |
| return p._getThievingGeneralRareTable()
| |
| end
| |
|
| |
| function p._getThievingGeneralRareTable(npc)
| |
| local npcRealm = nil
| |
| if npc ~= nil then
| |
| npcRealm = Skills.getRecipeRealm(npc)
| |
| end
| |
|
| |
| local rareTxt = '{|class="wikitable sortable"'
| |
| rareTxt = rareTxt..'\r\n!Item!!Qty'
| |
| rareTxt = rareTxt..'!!Price!!colspan="2"|Chance'
| |
| for i, drop in ipairs(SkillData.Thieving.generalRareItems) do
| |
| -- 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
| |
| 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 odds = drop.chance
| |
|
| |
| rareTxt = rareTxt..'\r\n|-\r\n|data-sort-value="'..thisItem.name..'"|'..Icons.getDLCColumnIcon(thisItem.id)..Icons.Icon({thisItem.name, type='item'})
| |
| 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.round(odds, 4, 4)..'%'
| |
| end
| |
| end
| |
| rareTxt = rareTxt..'\r\n|}'
| |
| 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
| |
|
| |
| function p._getThievingNPCLootTables(npc)
| |
| local result = ''
| |
| local sectionTxt = {}
| |
|
| |
| --Five sections here: Currency, normal loot, area loot, rare loot, and unique item
| |
| --First up, currency:
| |
| table.insert(sectionTxt, 'Successfully pickpocketing the ' .. npc.name .. ' will always give '.. p._getThievingNPCCurrencyText(npc))
| |
|
| |
| --Next up, normal loot:
| |
| --(Skip if no loot)
| |
| if npc.lootTable ~= nil and Shared.tableCount(npc.lootTable) > 0 then
| |
| local normalTxt = {}
| |
| table.insert(normalTxt, '===Possible Common Drops:===\r\nUp to one of these will be received on a successful pickpocket:')
| |
| local totalWt = 0
| |
| local lootChance = SkillData.Thieving.itemChance
| |
| local lootValue = {}
| |
|
| |
| --First loop through to get the total weight so we have it for later
| |
| for i, loot in pairs(npc.lootTable) do
| |
| totalWt = totalWt + loot.weight
| |
| end
| |
|
| |
| table.insert(normalTxt, '\r\n{|class="wikitable sortable"')
| |
| table.insert(normalTxt, '\r\n!Item!!Qty')
| |
| table.insert(normalTxt, '!!Price!!colspan="2"|Chance')
| |
|
| |
| local lootTable = Shared.shallowClone(npc.lootTable)
| |
| --Then sort the loot table by weight
| |
| table.sort(lootTable, function(a, b) return a.weight > b.weight end)
| |
| for i, loot in ipairs(lootTable) do
| |
| local thisItem = Items.getItemByID(loot.itemID)
| |
| if thisItem ~= nil then
| |
| table.insert(normalTxt, '\r\n|-\r\n|'..Icons.Icon({thisItem.name, type='item'}))
| |
| else
| |
| table.insert(normalTxt, '\r\n|-\r\n|Unknown Item[[Category:Pages with script errors]]')
| |
| end
| |
| table.insert(normalTxt, '||style="text-align:right" data-sort-value="'..(loot.minQuantity + loot.maxQuantity)..'"|')
| |
|
| |
| if loot.minQuantity ~= loot.maxQuantity then
| |
| table.insert(normalTxt, Shared.formatnum(loot.minQuantity) .. ' - ' .. Shared.formatnum(loot.maxQuantity))
| |
| else
| |
| table.insert(normalTxt, Shared.formatnum(loot.maxQuantity))
| |
| end
| |
|
| |
| --Adding price columns
| |
| local sellAmount, sellCurrency = nil, nil
| |
| if thisItem == nil then
| |
| table.insert(normalTxt, '||data-sort-value="0"|???')
| |
| else
| |
| sellAmount = thisItem.sellsFor or 0
| |
| sellCurrency = thisItem.sellsForCurrency or 'melvorD:GP'
| |
| table.insert(normalTxt, '||' .. Items.getValueText(thisItem, loot.minQuantity, loot.maxQuantity))
| |
| end
| |
|
| |
| --Getting the drop chance
| |
| local dropChance = (loot.weight / totalWt * lootChance)
| |
| if dropChance < 100 then
| |
| --Show fraction as long as it isn't going to be 1/1
| |
| table.insert(normalTxt, '||style="text-align:right" data-sort-value="'..loot.weight..'"')
| |
| table.insert(normalTxt, '|'..Shared.fraction(loot.weight * lootChance, totalWt * 100))
| |
| table.insert(normalTxt, '||')
| |
| else
| |
| table.insert(normalTxt, '||colspan="2" data-sort-value="'..loot.weight..'"')
| |
| end
| |
| table.insert(normalTxt, 'style="text-align:right"|'..Shared.round(dropChance, 2, 2)..'%')
| |
|
| |
| --Adding to the average loot value based on price & dropchance
| |
| 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
| |
| if Shared.tableCount(npc.lootTable) > 1 then
| |
| table.insert(normalTxt, '\r\n|-class="sortbottom" \r\n!colspan="3"|Total:')
| |
| if lootChance < 100 then
| |
| table.insert(normalTxt, '\r\n|style="text-align:right"|'..Shared.fraction(lootChance, 100)..'||')
| |
| else
| |
| table.insert(normalTxt, '\r\n|colspan="2" ')
| |
| end
| |
| table.insert(normalTxt, 'style="text-align:right"|'..Shared.round(lootChance, 2, 2)..'%')
| |
| end
| |
| table.insert(normalTxt, '\r\n|}')
| |
|
| |
| 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))
| |
| end
| |
|
| |
| --After normal drops, add in rare drops
| |
| local rareTxt = '===Possible Rare Drops:===\r\nAny of these can be received after a successful pickpocket:'
| |
| rareTxt = rareTxt..'\r\n'..p._getThievingGeneralRareTable(npc)
| |
| table.insert(sectionTxt, rareTxt)
| |
|
| |
| local areaTxt = '===Possible Area Unique Drops==='
| |
| areaTxt = areaTxt..'\r\nAny Area Unique Drop is equally likely to be obtained after a successful pickpocket. '
| |
| areaTxt = areaTxt..'\r\nEach Area Unique Drop is rolled for separately, so it is possible to receive multiple Area Unique Drops from a single action. '
| |
| areaTxt = areaTxt..'The chance of receiving an Area Unique drop is tripled if the 95% Thieving Mastery Pool checkpoint is active.'
| |
|
| |
| local area = Skills.getThievingNPCArea(npc)
| |
| areaTxt = areaTxt..'\r\n{|class="wikitable sortable"'
| |
| areaTxt = areaTxt..'\r\n!Item!!Qty'
| |
| areaTxt = areaTxt..'!!Price!!colspan="2"|Chance'
| |
| local dropLines = {}
| |
| for i, drop in ipairs(area.uniqueDrops) do
| |
| local thisItem = Items.getItemByID(drop.id)
| |
| local lineTxt = ''
| |
| 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..'"|'..Items.getValueText(thisItem)
| |
| lineTxt = lineTxt..'||style="text-align:right"|'..Shared.fraction(1, 1/(SkillData.Thieving.baseAreaUniqueChance/100))
| |
| lineTxt = lineTxt..'||'..Shared.round(SkillData.Thieving.baseAreaUniqueChance, 2, 2)..'%'
| |
| dropLines[thisItem.name] = lineTxt
| |
| end
| |
| for i, txt in pairs(dropLines) do
| |
| areaTxt = areaTxt..txt
| |
| end
| |
| areaTxt = areaTxt..'\r\n|}'
| |
| table.insert(sectionTxt, areaTxt)
| |
|
| |
| if npc.uniqueDrop ~= nil and npc.uniqueDrop.id ~= nil then
| |
| local thisItem = Items.getItemByID(npc.uniqueDrop.id)
| |
| if thisItem ~= nil then
| |
| local uniqueTxt = '===Possible NPC Unique Drop==='
| |
| uniqueTxt = uniqueTxt..'\r\nThe chance of receiving the unique drop for an NPC is based on a combination of several factors.'
| |
| uniqueTxt = uniqueTxt..' The unique drop chance for an NPC is included in the tooltip for your Stealth against that NPC.'
| |
| uniqueTxt = uniqueTxt..'\r\nThe unique drop for the '..npc.name..' is '
| |
| if npc.uniqueDrop.quantity > 1 then
| |
| uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item', qty=npc.uniqueDrop.quantity}) .. '.'
| |
| else
| |
| uniqueTxt = uniqueTxt..Icons.Icon({thisItem.name, type='item'}) .. '.'
| |
| end
| |
| table.insert(sectionTxt, uniqueTxt)
| |
| end
| |
| end
| |
|
| |
| return table.concat(sectionTxt, '\r\n')
| |
| end
| |
|
| |
| function p.getThievingNPCLootTables(frame)
| |
| local npcName = frame.args ~= nil and frame.args[1] or frame
| |
| local npc = Skills.getThievingNPC(npcName)
| |
| if npc == nil then
| |
| return Shared.printError('Invalid Thieving NPC "' .. npcName .. '"')
| |
| end
| |
|
| |
| return p._getThievingNPCLootTables(npc)
| |
| end
| |
|
| |
| 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"'
| |
| 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!!Currency!!Unique Drop'
| |
| local npcArray = GameData.getEntities(SkillData.Thieving.npcs,
| |
| 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
| |
| 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|'..Icons.Icon({npc.name, type='thieving', notext=true})
| |
| result = result..'||data-sort-value="'..npc.name..'"|'..Icons.getDLCColumnIcon(npc.id)..Icons.Icon({npc.name, type='thieving', noicon=true})
| |
|
| |
| result = result..'||'..area.name
| |
| result = result..'||data-sort-value="' .. level .. '"| ' .. reqText
| |
| result = result..'||style="text-align:right"|' .. Shared.formatnum(baseXP)
| |
| 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..'||data-sort-value="' .. currSortAmt .. '"|' .. p._getThievingNPCCurrencyText(npc)
| |
| if npc.uniqueDrop ~= nil then
| |
| local uniqueDrop = Items.getItemByID(npc.uniqueDrop.id)
| |
| if npc.uniqueDrop.quantity > 1 then
| |
| result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item', qty = npc.uniqueDrop.quantity})
| |
| else
| |
| result = result..'||data-sort-value="'..uniqueDrop.name..'"|'..Icons.Icon({uniqueDrop.name, type='item'})
| |
| end
| |
| else
| |
| result = result..'|| '
| |
| end
| |
| end
| |
| result = result..'\r\n|}'
| |
|
| |
| return result
| |
| end
| |
|
| |
| 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 = {}
| |
| table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
| |
| table.insert(resultPart, '\r\n|- class="headerRow-0"')
| |
| table.insert(resultPart, '\r\n!Area!!'..Icons.Icon({'Thieving', type='skill', notext=true})..' Level!!NPCs!!Unique Drops')
| |
|
| |
| 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, {}, {}
| |
| -- Build NPC list & determine level for area, this is the minimum
| |
| -- Thieving level required for all NPCs within that area
| |
| if area.npcIDs ~= nil and not Shared.tableIsEmpty(area.npcIDs) then
| |
| for j, npcID in ipairs(area.npcIDs) do
| |
| local npc = Skills.getThievingNPCByID(npcID)
| |
| local level = Skills.getRecipeLevel(skillID, npc)
| |
| if minLevel == nil or level < minLevel then
| |
| minLevel = level
| |
| end
| |
| table.insert(npcList, Icons.Icon({npc.name, type='thieving'}))
| |
| end
| |
| else
| |
| table.insert(npcList, '')
| |
| end
| |
|
| |
| -- Build area unique item list
| |
| if area.uniqueDrops ~= nil and Shared.tableCount(area.uniqueDrops) > 0 then
| |
| for k, drop in ipairs(area.uniqueDrops) do
| |
| local areaItem = Items.getItemByID(drop.id)
| |
| if areaItem == nil then
| |
| table.insert(areaItemList, 'Unknown[[Category:Pages with script errors]]')
| |
| else
| |
| local iconDef = {areaItem.name, type='item'}
| |
| if drop.quantity > 1 then
| |
| iconDef.qty = drop.quantity
| |
| end
| |
| table.insert(areaItemList, Icons.Icon(iconDef))
| |
| end
| |
| end
| |
| else
| |
| table.insert(areaItemList, '')
| |
| end
| |
|
| |
| -- Generate table row
| |
| table.insert(resultPart, '\r\n|-')
| |
| table.insert(resultPart, '\r\n|' .. area.name)
| |
| table.insert(resultPart, '\r\n|' .. Icons._SkillReq('Thieving', minLevel))
| |
| table.insert(resultPart, '\r\n|' .. table.concat(npcList, '<br/>'))
| |
| table.insert(resultPart, '\r\n|' .. table.concat(areaItemList, '<br/>'))
| |
| end
| |
| table.insert(resultPart, '\r\n|}')
| |
|
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p._getFarmingTable(realmID, category)
| |
| local seedList = GameData.getEntities(SkillData.Farming.recipes,
| |
| function(recipe)
| |
| return recipe.categoryID == category.id and Skills.getRecipeRealm(recipe) == realmID
| |
| end)
| |
| if Shared.tableIsEmpty(seedList) then
| |
| return ''
| |
| end
| |
|
| |
| local skillID = 'Farming'
| |
|
| |
| local tbl = mw.html.create()
| |
| local html = tbl:tag("table")
| |
| :addClass("wikitable sortable stickyHeader")
| |
| :tag('tr'):addClass("headerRow-0")
| |
| :tag('th'):attr("colspan", 2):wikitext("Seeds")
| |
| :tag('th'):wikitext(Icons.Icon({'Farming', type='skill', notext=true}) .. '<br>Level')
| |
| :tag('th'):wikitext('[[DLC]]')
| |
| :tag('th'):wikitext('XP')
| |
| :tag('th'):wikitext('Growth Time')
| |
| :tag('th'):wikitext('Seed Value')
| |
|
| |
| if category.id == 'melvorD:Allotment' then
| |
| html:tag('th'):attr("colspan", 2):wikitext("Produce")
| |
| :tag('th'):wikitext('Healing')
| |
| :tag('th'):wikitext('Produce<br>Value')
| |
| else
| |
| html:tag('th'):attr("colspan", 2):wikitext("Produce")
| |
| :tag('th'):wikitext('Produce<br>Value')
| |
| end
| |
| --html = html:tag('th'):wikitext('Seed Sources')
| |
|
| |
| table.sort(seedList, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)
| |
|
| |
| for i, seed in ipairs(seedList) do
| |
| local seedItem = Items.getItemByID(seed.seedCost.id)
| |
| local productItem = Items.getItemByID(seed.productId)
| |
| 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:tag('tr')
| |
| :tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', notext=true}))
| |
| :tag('td'):wikitext('[[' .. seedItem.name .. ']]')
| |
| :tag('td'):wikitext(level)
| |
| :css('text-align', 'center')
| |
| :tag('td'):wikitext(Icons.getDLCColumnIcon(seedItem.id))
| |
| :css('text-align', 'center')
| |
| :attr('data-sort-value', Icons.getExpansionID(seedItem.id))
| |
| :tag('td'):wikitext(Shared.formatnum(baseXP))
| |
| :tag('td'):attr('data-sort-value', (baseInt / 1000))
| |
| :wikitext(Shared.timeString(baseInt / 1000, true))
| |
| :tag('td'):attr('data-sort-value', seedItem.sellsFor)
| |
| :wikitext(Items.getValueText(seedItem))
| |
| :tag('td'):wikitext(Icons.Icon({productItem.name, type='item', notext=true}))
| |
| :tag('td'):wikitext('[[' .. productItem.name .. ']]')
| |
|
| |
| if category.id == 'melvorD:Allotment' then
| |
| html:tag('td'):wikitext(Icons.Icon({'Hitpoints', type='skill', notext=true}))
| |
| :wikitext(' ')
| |
| :wikitext(((productItem.healsFor or 0) * 10))
| |
| end
| |
| html =
| |
| html:tag('td'):attr('data-sort-value', productItem.sellsFor)
| |
| :wikitext(Items.getValueText(productItem))
| |
| --:tag('td'):wikitext(ItemSourceTables._getItemSources(seedItem))
| |
| -- :css('text-align', 'left')
| |
| :done()
| |
| end
| |
| end
| |
|
| |
| return tostring(tbl:done())
| |
| end
| |
|
| |
| function p._getSlimFarmingTable(realmID, category)
| |
| local seedList = GameData.getEntities(SkillData.Farming.recipes,
| |
| function(recipe)
| |
| return recipe.categoryID == category.id and Skills.getRecipeRealm(recipe) == realmID
| |
| end)
| |
| if Shared.tableIsEmpty(seedList) then
| |
| return ''
| |
| end
| |
|
| |
| local skillID = 'Farming'
| |
|
| |
| local tbl = mw.html.create()
| |
| local html = tbl:tag("table")
| |
| :addClass("wikitable sortable stickyHeader")
| |
| :tag('tr'):addClass("headerRow-0")
| |
| :tag('th'):wikitext(Icons.Icon({'Farming', type='skill', notext=true}) .. '<br>Level')
| |
| :tag('th'):attr("colspan", 2):wikitext("Seeds")
| |
| :tag('th'):attr("colspan", 2):wikitext("Produce")
| |
| :tag('th'):wikitext('[[DLC]]')
| |
|
| |
| table.sort(seedList, function(a, b) return Skills.standardRecipeSort(skillID, a, b) end)
| |
|
| |
| for i, seed in ipairs(seedList) do
| |
| local seedItem = Items.getItemByID(seed.seedCost.id)
| |
| local productItem = Items.getItemByID(seed.productId)
| |
| if seedItem ~= nil and productItem ~= nil then
| |
| local level = Skills.getRecipeLevel(skillID, seed)
| |
|
| |
| html =
| |
| html:tag('tr')
| |
| :tag('td'):wikitext(level)
| |
| :css('text-align', 'center')
| |
| :tag('td'):wikitext(Icons.Icon({seedItem.name, type='item', notext=true}))
| |
| :tag('td'):wikitext('[[' .. seedItem.name .. ']]')
| |
| :tag('td'):wikitext(Icons.Icon({productItem.name, type='item', notext=true}))
| |
| :tag('td'):wikitext('[[' .. productItem.name .. ']]')
| |
| :tag('td'):wikitext(Icons.getDLCColumnIcon(seedItem.id))
| |
| :css('text-align', 'center')
| |
| :attr('data-sort-value', Icons.getExpansionID(seedItem.id))
| |
| :done()
| |
| end
| |
| end
| |
|
| |
| return tostring(tbl:done())
| |
| end
| |
|
| |
| function p.getFarmingTable(frame)
| |
| local args = frame.args ~= nil and frame.args or frame
| |
| local realmName = args.realm
| |
| local slim = args.slim == 'true' or args.slim == 'True'
| |
| local realm = Skills.getRealmFromName(realmName)
| |
| if realm == nil then
| |
| return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil'))
| |
| end
| |
| local categoryName = args[1]
| |
|
| |
| local category = GameData.getEntityByName(SkillData.Farming.categories, categoryName)
| |
| if category == nil then
| |
| return Shared.printError('Invalid farming category: ' .. categoryName .. '. Please choose Allotments, Herbs, Trees or Special')
| |
| end
| |
|
| |
| if slim == true then
| |
| return p._getSlimFarmingTable(realm.id, category)
| |
| else
| |
| return p._getFarmingTable(realm.id, category)
| |
| end
| |
| end
| |
|
| |
| function p.getFarmingFoodTable(frame)
| |
| local result = '{| class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n|- class="headerRow-0"'
| |
| result = result..'\r\n!colspan="2"|Crop!!'..Icons.Icon({"Farming", type="skill", notext=true})..' Level'
| |
| result = result..'!!Healing!!Value'
| |
|
| |
| local recipes = GameData.getEntities(SkillData.Farming.recipes,
| |
| function(recipe)
| |
| local product = Items.getItemByID(recipe.productId)
| |
| return product ~= nil and product.healsFor ~= nil and product.healsFor > 0
| |
| end)
| |
| table.sort(recipes, function(a, b) return a.level < b.level end)
| |
|
| |
| for i, recipe in ipairs(recipes) do
| |
| local product = Items.getItemByID(recipe.productId)
| |
| if product ~= nil and product.healsFor ~= nil and product.healsFor > 0 then
| |
| result = result..'\r\n|-'
| |
| result = result..'\r\n|'..Icons.Icon({product.name, type='item', notext='true'})
| |
| result = result..'|| ' .. Icons.getDLCColumnIcon(product.id) .. Icons.Icon({product.name, type='item', noicon=true})
| |
| 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.sellsFor..'"|'..Items.getValueText(product)
| |
| end
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
|
| |
| return result
| |
| end
| |
|
| |
| function p.getFarmingPlotTable(frame)
| |
| local skillID = 'Farming'
| |
| local areaName = frame.args ~= nil and frame.args[1] or frame
| |
| local category = GameData.getEntityByName(SkillData.Farming.categories, areaName)
| |
| if category == nil then
| |
| return Shared.printError('Invalid farming category. Please choose Allotments, Herbs, or Trees')
| |
| end
| |
| local patches = GameData.getEntities(SkillData.Farming.plots,
| |
| function(plot)
| |
| return plot.categoryID == category.id
| |
| end)
| |
| table.sort(patches,
| |
| function(a, b)
| |
| local abyssA, abyssB = a.abyssalLevel or 0, b.abyssalLevel or 0
| |
| if abyssA == abyssB then
| |
| if a.level == b.level then
| |
| return a.id < b.id
| |
| else
| |
| return a.level < b.level
| |
| end
| |
| else
| |
| return abyssA < abyssB
| |
| end
| |
| end)
| |
| if Shared.tableIsEmpty(patches) then
| |
| return ''
| |
| end
| |
|
| |
| local result = '{|class="wikitable sortable stickyHeader"'
| |
| result = result..'\r\n!Plot!!Requirements!!Cost'
| |
|
| |
| 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..'||style="text-align:right;" data-sort-value="' .. level .. '"|'..reqText
| |
| result = result..'||style="text-align:right;" data-sort-value="'..costVal..'"|'..costText
| |
| end
| |
|
| |
| result = result..'\r\n|}'
| |
| return result
| |
| end
| |
|
| |
| 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 = {}
| |
| table.insert(resultPart, '{|class="wikitable sortable stickyHeader"')
| |
| 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, '!!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, string.rep('\n! ' .. Icons.Icon({'Mastery', notext=true}) .. 'Level\n! Modifiers', repCount))
| |
|
| |
| for i, cons in ipairs(recipes) do
| |
| local modData = Skills._getConstellationModifiers(cons)
| |
| local name = cons.name
| |
| local skillIconArray = {}
| |
| for j, skillID in ipairs(cons.skillIDs) do
| |
| table.insert(skillIconArray, Icons.Icon({Constants.getSkillName(skillID), type='skill'}))
| |
| end
| |
| local maxRows = 1
| |
| for _, modTypeData in pairs(modData) do
| |
| maxRows = math.max(maxRows, Shared.tableCount(modTypeData))
| |
| end
| |
| local rowSpan = (maxRows > 1 and 'rowspan="' .. maxRows .. '"') or ''
| |
|
| |
| table.insert(resultPart, '\n|-')
| |
| table.insert(resultPart, '\n|' .. rowSpan .. ' data-sort-value="' .. name .. '" id="'..name..'"| ')
| |
| table.insert(resultPart, Icons.Icon({name, type='constellation', notext=true}))
| |
| table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. Icons.getDLCColumnIcon(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)
| |
| table.insert(resultPart, '\n|' .. rowSpan .. '| ' .. table.concat(skillIconArray, '<br/>'))
| |
|
| |
| -- Generate table text for modifiers
| |
| for row = 1, maxRows, 1 do
| |
| for _, modType in ipairs(modTypes) do
| |
| if modType.inUse then
| |
| local masteryLevel = modType.levels[row]
| |
| local rowModData = modData[modType.modKey][row]
| |
| if masteryLevel == nil or rowModData == nil then
| |
| 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
| |
| if row < maxRows then
| |
| table.insert(resultPart, '\n|-')
| |
| end
| |
| end
| |
| end
| |
| table.insert(resultPart, '\n|}')
| |
|
| |
| return table.concat(resultPart)
| |
| end
| |
|
| |
| function p.buildAstrologyConstellationTable(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
| |
|
| |
| return p._buildAstrologyConstellationTable(realm.id)
| |
| end
| |
|
| |
| return p
| |