5,003
edits
(Implement getMasteryTokenTable()) |
(Add Archaeology recipe key) |
||
(64 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
--This module should avoid including skill specific functions which generate | |||
--output for wiki pages, especially those which require() other modules. For | |||
--these functions, consider using the appropriate module from the below list. | |||
--Some skills have their own modules: | --Some skills have their own modules: | ||
--Module:Magic for Magic | --Module:Magic for Magic | ||
--Module:Prayer for Prayer | --Module:Prayer for Prayer | ||
--Module:Agility for Agility | --Module:Skills/Agility for Agility | ||
--Module:Skills/Summoning for Summoning | |||
--Module:Skills/Gathering for Mining, Fishing, Woodcutting | --Module:Skills/Gathering for Mining, Fishing, Woodcutting | ||
--Module:Skills/Artisan for Smithing, Cooking, Herblore, etc. | --Module:Skills/Artisan for Smithing, Cooking, Herblore, etc. | ||
--Also be aware of: | |||
--Module:Navboxes for navigation boxes appearing near the bottom of pages | |||
local p = {} | local p = {} | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local Constants = require('Module:Constants') | |||
local Common = require('Module:Common') | |||
local GameData = require('Module:GameData') | |||
local SkillData = GameData.skillData | |||
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') | ||
local Num = require('Module:Number') | |||
local | -- Given a skill ID, returns the key for that skill's recipe data. | ||
-- If the skill has no recipes (e.g. is a combat skill) then the | |||
-- return value is nil | |||
function p.getSkillRecipeKey(skillID) | |||
-- Convert skillID to local ID if not already | |||
local ns, localSkillID = GameData.getLocalID(skillID) | |||
local recipeIDs = { | |||
["Woodcutting"] = 'trees', | |||
["Fishing"] = 'fish', | |||
["Firemaking"] = 'logs', | |||
["Mining"] = 'rockData', | |||
["Thieving"] = 'npcs', | |||
["Agility"] = 'obstacles', | |||
["Cooking"] = 'recipes', | |||
["Smithing"] = 'recipes', | |||
["Farming"] = 'recipes', | |||
["Summoning"] = 'recipes', | |||
["Fletching"] = 'recipes', | |||
["Crafting"] = 'recipes', | |||
["Runecrafting"] = 'recipes', | |||
["Herblore"] = 'recipes', | |||
["Astrology"] = 'recipes', | |||
["Archaeology"] = 'digSites', | |||
["Harvesting"] = 'veinData' | |||
} | |||
return recipeIDs[localSkillID] | |||
end | |||
function p. | -- Given a skill ID & recipe, returns the skill level requirement for | ||
-- that recipe and a boolean value indicating whether the level if abyssal or not. | |||
-- If the level could not be determined, then the return value is nil, nil | |||
function p.getRecipeLevelRealm(skillID, recipe) | |||
local level, isAbyssal = nil, nil | |||
-- Convert skillID to local ID if not already | |||
local ns, localSkillID = GameData.getLocalID(skillID) | |||
local realmID = p.getRecipeRealm(recipe) | |||
if localSkillID == 'Agility' then | |||
local course = GameData.getEntityByProperty(SkillData.Agility.courses, 'realm', realmID) | |||
isAbyssal = (realmID == 'melvorItA:Abyssal') | |||
-- For Agility, level is derived from obstacle category | |||
if recipe.category ~= nil then | |||
-- Obstacle | |||
local slot = course.obstacleSlots[recipe.category + 1] | |||
if isAbyssal then | |||
level = slot.abyssalLevel | |||
else | |||
level = slot.level | |||
end | |||
elseif recipe.slot ~= nil then | |||
-- Pillar | |||
level = course.pillarSlots[recipe.slot + 1].level | |||
end | |||
elseif recipe.abyssalLevel ~= nil then | |||
level, isAbyssal = recipe.abyssalLevel, true | |||
else | |||
-- For all other skills, the recipe should have a level property | |||
level, isAbyssal = recipe.level, false | |||
end | |||
return level, isAbyssal | |||
end | end | ||
function p. | function p.getRecipeLevel(skillID, recipe) | ||
local level, isAbyssal = p.getRecipeLevelRealm(skillID, recipe) | |||
return level | |||
end | end | ||
function p. | function p.getRecipeRequirementText(skillName, recipe) | ||
local reqText = {} | |||
if recipe.abyssalLevel ~= nil and recipe.abyssalLevel > 0 then | |||
table.insert(reqText, Icons._SkillReq(skillName, recipe.abyssalLevel, false, 'melvorItA:Abyssal')) | |||
elseif recipe.level ~= nil and recipe.level > 0 then | |||
table.insert(reqText, Icons._SkillReq(skillName, recipe.level, false)) | |||
end | |||
if recipe.totalMasteryRequired ~= nil then | |||
table.insert(reqText, Num.formatnum(recipe.totalMasteryRequired) .. ' ' .. Icons.Icon({skillName, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'})) | |||
end | |||
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 | |||
return table.concat(reqText, '<br/>') | |||
end | end | ||
function p. | function p.standardRecipeSort(skillID, recipeA, recipeB) | ||
local levelA, isAbyssalA = p.getRecipeLevelRealm(skillID, recipeA) | |||
local levelB, isAbyssalB = p.getRecipeLevelRealm(skillID, recipeB) | |||
local isAbyssalNumA, isAbyssalNumB = (isAbyssalA and 1) or 0, (isAbyssalB and 1) or 0 | |||
return (isAbyssalA == isAbyssalB and levelA < levelB) or isAbyssalNumA < isAbyssalNumB | |||
end | end | ||
function p.getRecipeRealm(recipe) | |||
return recipe.realm or 'melvorD:Melvor' | |||
end | end | ||
function p. | function p.getRealmFromName(realmName) | ||
local realm = nil | |||
if realmName == nil or realmName == '' then | |||
-- Default realm | |||
realm = GameData.getEntityByID('realms', 'melvorD:Melvor') | |||
else | |||
realm = GameData.getEntityByName('realms', realmName) | |||
end | |||
return realm | |||
end | |||
function p.getMasteryActionCount(skillID, realmID, levelLimit) | |||
local actCount = 0 | |||
local skillNS, skillLocalID = Shared.getLocalID(skillID) | |||
local skillData = SkillData[skillLocalID] | |||
local recipeKey = p.getSkillRecipeKey(skillLocalID) | |||
if recipeKey ~= nil then | |||
local recipeData = skillData[recipeKey] | |||
for i, recipe in ipairs(recipeData) do | |||
if ( | |||
p.getRecipeRealm(recipe) == realmID | |||
and (recipe.noMastery == nil or not recipe.noMastery) | |||
and (levelLimit == nil or p.getRecipeLevel(skillLocalID, recipe) <= levelLimit) | |||
) then | |||
actCount = actCount + 1 | |||
end | |||
end | |||
end | |||
return actCount | |||
end | |||
-- Thieving | |||
function p.getThievingNPCByID(npcID) | |||
return GameData.getEntityByID(SkillData.Thieving.npcs, npcID) | |||
end | end | ||
function p. | function p.getThievingNPC(npcName) | ||
return GameData.getEntityByName(SkillData.Thieving.npcs, npcName) | |||
end | |||
function p.getThievingNPCArea(npc) | |||
for i, area in ipairs(SkillData.Thieving.areas) do | |||
for j, npcID in ipairs(area.npcIDs) do | |||
if npcID == npc.id then | |||
return area | |||
end | |||
end | |||
end | |||
end | |||
function p._getThievingNPCStat(npc, statName) | |||
local result = nil | |||
if statName == 'level' then | |||
result = Icons._SkillReq('Thieving', npc.level) | |||
elseif statName == 'maxHit' then | |||
result = npc.maxHit * 10 | |||
elseif statName == 'area' then | |||
local area = p.getThievingNPCArea(npc) | |||
result = area.name | |||
else | |||
result = npc[statName] | |||
end | |||
if result == nil then | |||
result = '' | |||
end | |||
return result | |||
end | end | ||
function p. | function p.getThievingNPCStat(frame) | ||
local npcName = frame.args ~= nil and frame.args[1] or frame[1] | |||
local statName = frame.args ~= nil and frame.args[2] or frame[2] | |||
local npc = p.getThievingNPC(npcName) | |||
if npc == nil then | |||
return Shared.printError('Invalid Thieving NPC ' .. npcName) | |||
end | |||
return p._getThievingNPCStat(npc, statName) | |||
end | |||
function p.getThievingSourcesForItem(itemID) | |||
local resultArray = {} | |||
local areaNPCs = {} | |||
--First check area unique drops | |||
--If an area drops the item, add all the NPC ids to the list so we can add them later | |||
for i, area in pairs(SkillData.Thieving.areas) do | |||
for j, drop in pairs(area.uniqueDrops) do | |||
if drop.id == itemID then | |||
for k, npcID in ipairs(area.npcIDs) do | |||
areaNPCs[npcID] = { qty = drop.quantity, area = area } | |||
end | |||
break | |||
end | |||
end | |||
end | |||
--Now go through and get drop chances on each NPC if needed | |||
for i, npc in pairs(SkillData.Thieving.npcs) do | |||
local totalWt = 0 | |||
local dropWt = 0 | |||
local dropQty = { min = 0, max = 0 } | |||
for j, drop in ipairs(npc.lootTable) do | |||
totalWt = totalWt + drop.weight | |||
if drop.itemID == itemID then | |||
dropWt = drop.weight | |||
dropQty = { min = drop.minQuantity, max = drop.maxQuantity } | |||
end | |||
end | |||
if dropWt > 0 then | |||
table.insert(resultArray, {npc = npc.name, minQty = dropQty.min, maxQty = dropQty.max, wt = dropWt * SkillData.Thieving.itemChance, totalWt = totalWt * 100, level = npc.level, abyssalLevel = npc.abyssalLevel, npcID = npc.id, type = 'npc'}) | |||
end | |||
--Chance of -1 on unique drops is to indicate variable chance | |||
end | if npc.uniqueDrop ~= nil and npc.uniqueDrop.id == itemID then | ||
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.quantity, maxQty = npc.uniqueDrop.quantity, wt = -1, totalWt = -1, level = npc.level, abyssalLevel = npc.abyssalLevel, npcID = npc.id, type = 'npcUnique'}) | |||
end | |||
local areaNPC = areaNPCs[npc.id] | |||
if areaNPC ~= nil then | |||
table.insert(resultArray, {npc = npc.name, minQty = areaNPC.qty, maxQty = areaNPC.qty, wt = SkillData.Thieving.baseAreaUniqueChance, totalWt = 100, level = npc.level, abyssalLevel = npc.abyssalLevel, npcID = npc.id, area = areaNPC.area, type = 'areaUnique'}) | |||
end | |||
end | |||
for i, drop in ipairs(SkillData.Thieving.generalRareItems) do | |||
if drop.itemID == itemID then | |||
if drop.npcs == nil then | |||
table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Num.round2(1/(drop.chance/100), 0), level = 1, npcID = itemID, type = 'generalRare'}) | |||
else | |||
for j, npcID in ipairs(drop.npcs) do | |||
local npc = p.getThievingNPCByID(npcID) | |||
if npc ~= nil then | |||
table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = 1, wt = 1, totalWt = Num.round2(1/(drop.chance/100), 0), level = npc.level, abyssalLevel = npc.abyssalLevel, npcID = npc.id, type = 'generalRare'}) | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return resultArray | |||
end | end | ||
function p. | -- Astrology | ||
function p.getConstellationByID(constID) | |||
return GameData.getEntityByID(SkillData.Astrology.recipes, constID) | |||
end | |||
function p.getConstellation(constName) | |||
return GameData.getEntityByName(SkillData.Astrology.recipes, constName) | |||
end | |||
function p.getConstellations(checkFunc) | |||
return GameData.getEntities(SkillData.Astrology.recipes, checkFunc) | |||
end | end | ||
function p. | -- Combines Astrology constellation modifiers into an object similar to other entities, | ||
-- and multiplies the values up to their maximum possible amount | |||
function p._getConstellationModifiers(cons) | |||
local result = {} | |||
local modKeys = { 'standardModifiers', 'uniqueModifiers', 'abyssalModifiers' } | |||
for _, keyID in ipairs(modKeys) do | |||
result[keyID] = {} | |||
local mods = cons[keyID] | |||
if mods ~= nil then | |||
for _, mod in ipairs(mods) do | |||
local newModObj = {} | |||
local multValue = mod.maxCount | |||
local subKeys = { 'modifiers', 'enemyModifiers' } | |||
for _, subKey in ipairs(subKeys) do | |||
local modAdj = Shared.clone(mod[subKey]) | |||
if type(modAdj) == 'table' then | |||
for modName, modValueDef in pairs(modAdj) do | |||
if type(modValueDef) == 'table' then | |||
if modValueDef[1] ~= nil then | |||
-- Table of multiple values | |||
for i, subValue in ipairs(modValueDef) do | |||
if type(subValue) == 'table' and subValue.value ~= nil then | |||
subValue.value = subValue.value * multValue | |||
elseif type(subValue) == 'number' then | |||
modValueDef[i] = subValue * multValue | |||
end | |||
end | |||
elseif modValueDef.value ~= nil then | |||
-- Table but with a single value | |||
modValueDef.value = modValueDef.value * multValue | |||
end | |||
elseif type(modValueDef) == 'number' then | |||
-- Single value | |||
modAdj[modName] = modValueDef * multValue | |||
end | |||
end | |||
newModObj[subKey] = modAdj | |||
end | |||
end | |||
table.insert(result[keyID], newModObj) | |||
end | |||
end | |||
end | |||
return result | |||
end | |||
-- Mastery | |||
function p.getMasteryUnlockTable(frame) | |||
local skillName = frame.args ~= nil and frame.args[1] or frame | |||
local skillID = Constants.getSkillID(skillName) | |||
if skillID == nil then | |||
return Shared.printError('Failed to find a skill ID for ' .. skillName) | |||
end | |||
local _, localSkillID = GameData.getLocalID(skillID) | |||
-- Clone so that we can sort by level | |||
local unlockTable = Shared.shallowClone(SkillData[localSkillID].masteryLevelUnlocks) | |||
if unlockTable == nil then | |||
return Shared.printError('Failed to find Mastery Unlock data for ' .. skillName) | |||
end | |||
table.sort(unlockTable, function(a, b) return (a.level == b.level and a.descriptionID < b.descriptionID) or a.level < b.level end) | |||
local result = '{|class="wikitable"\r\n!Level!!Unlock' | |||
for i, unlock in ipairs(unlockTable) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n|'..unlock.level..'||'..unlock.description | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | end | ||
function p. | function p.getMasteryCheckpointTable(frame) | ||
local args = frame.args ~= nil and frame.args or frame | |||
local skillName = args[1] | |||
local realmName = args.realm | |||
local realm = p.getRealmFromName(realmName) | |||
if realm == nil then | |||
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil')) | |||
end | |||
local skillID = Constants.getSkillID(skillName) | |||
if skillID == nil then | |||
return Shared.printError('Failed to find a skill ID for ' .. skillName) | |||
end | |||
local _, localSkillID = GameData.getLocalID(skillID) | |||
local checkpoints = SkillData[localSkillID].masteryPoolBonuses | |||
if checkpoints == nil then | |||
return Shared.printError('Failed to find Mastery checkpoint data for ' .. skillName) | |||
end | |||
local totalPoolXP = p.getMasteryActionCount(localSkillID, realm.id) * 500000 | |||
local result = '{|class="wikitable"\r\n!Pool %!!style="width:100px"|Pool XP!!Bonus' | |||
for i, checkpointData in ipairs(checkpoints) do | |||
if checkpointData.realm == realm.id then | |||
local chkDesc = Modifiers.getModifiersText(checkpointData.modifiers, false, false) | |||
local chkPercent = checkpointData.percent | |||
result = result..'\r\n|-' | |||
result = result..'\r\n|'..chkPercent..'%||' | |||
result = result..Num.formatnum(math.floor(totalPoolXP * chkPercent / 100))..' xp||'..chkDesc | |||
end | |||
end | |||
result = result..'\r\n|-\r\n!colspan="2"|Total Mastery Pool XP' | |||
result = result..'\r\n|'..Num.formatnum(totalPoolXP) | |||
result = result..'\r\n|}' | |||
return result | |||
end | end | ||
function p. | function p.getMasteryTokenTable() | ||
-- Defines which skill levels should be included within the output | |||
local skillLevels = { | |||
{ | |||
["id"] = 'Base', | |||
["level"] = 99, | |||
["description"] = '[[Full Version|Base Game]] (Level 99)' | |||
}, { | |||
["id"] = 'TotH', | |||
["level"] = 120, | |||
["description"] = Icons.TotH() .. ' [[Throne of the Herald Expansion|Throne of the Herald]] (Level 120)' | |||
} | |||
} | |||
local baseTokenChance = 18500 | |||
local masteryActionCount = {} | |||
local CCI_ID = 'melvorD:Clue_Chasers_Insignia' | |||
local CCI = Items.getItemByID(CCI_ID) | |||
if CCI == nil then | |||
return Shared.printError('Failed to find item with ID ' .. CCI_ID) | |||
end | |||
local tokens = Items.getItems(function(item) return item.itemType == 'MasteryToken' end) | |||
local tokenItems = {} | |||
for _, item in ipairs(tokens) do | |||
if item.realm == 'melvorD:Melvor' and item.skill ~= nil then | |||
local skillNS, skillLocalID = Shared.getLocalID(item.skill) | |||
tokenItems[skillLocalID] = item | |||
end | |||
end | |||
-- Iterate over each skill with mastery, determining the number of | |||
-- mastery actions for each | |||
for skillLocalID, skill in pairs(SkillData) do | |||
if skill.masteryPoolBonuses ~= nil then | |||
local actCount = { ["skill"] = skill, ["token"] = tokenItems[skillLocalID] } | |||
for i, levelDef in ipairs(skillLevels) do | |||
actCount[levelDef.id] = p.getMasteryActionCount(skillLocalID, 'melvorD:Melvor', levelDef.level) | |||
end | |||
table.insert(masteryActionCount, actCount) | |||
end | |||
end | |||
end | |||
local firstID = skillLevels[1].id | |||
table.sort(masteryActionCount, | |||
function(a, b) | |||
if a[firstID] == b[firstID] then | |||
return a.skill.name < b.skill.name | |||
else | |||
return a[firstID] > b[firstID] | |||
end | |||
end) | |||
-- Generate output table | |||
local resultPart = {} | |||
local CCIIcon = Icons.Icon({CCI.name, type='item', notext=true}) | |||
local columnPairs = Shared.tableCount(skillLevels) | |||
-- Generate header | |||
table.insert(resultPart, '{| class="wikitable sortable"') | |||
table.insert(resultPart, '\n!rowspan="3"|Token!!rowspan="3"|Skill!!colspan="' .. columnPairs * 2 .. '"|Approximate Mastery Token Chance') | |||
table.insert(resultPart, '\n|-') | |||
for i, levelDef in ipairs(skillLevels) do | |||
table.insert(resultPart, '\n!colspan="2"| ' .. levelDef.description) | |||
end | |||
table.insert(resultPart, '\n|-' .. string.rep('\n!Without ' .. CCIIcon .. '\n!With ' .. CCIIcon, columnPairs)) | |||
for i, rowData in ipairs(masteryActionCount) do | |||
local token = rowData.token | |||
table.insert(resultPart, '\n|-') | |||
local tokenImg = (token == nil and '?') or Icons.Icon({token.name, type='item', notext=true}) | |||
table.insert(resultPart, '\n|style="text-align:center"|' .. tokenImg) | |||
table.insert(resultPart, '\n|' .. Icons.Icon({rowData.skill.name, type='skill'})) | |||
for j, levelDef in ipairs(skillLevels) do | |||
local actCount = rowData[levelDef.id] | |||
local denom, denomCCI = 0, 0 | |||
if actCount > 0 then | |||
denom = math.floor(baseTokenChance / actCount) | |||
denomCCI = Num.round(baseTokenChance / (actCount * (1 + CCI.modifiers.offItemChance / 100)), 0, 0) | |||
end | |||
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. denom .. '"|1/' .. Num.formatnum(denom)) | |||
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. denomCCI .. '"|1/' .. Num.formatnum(denomCCI)) | |||
end | |||
end | |||
table.insert(resultPart, '\n|}') | |||
return table.concat(resultPart) | |||
end | end | ||
function p.getFiremakingTable(frame) | function p.getFiremakingTable(frame) | ||
local args = frame.args ~= nil and frame.args or frame | |||
local realmName = args.realm | |||
local realm = p.getRealmFromName(realmName) | |||
if realm == nil then | |||
return Shared.printError('Failed to find a realm with name ' .. (realmName or 'nil')) | |||
end | |||
local skillID = 'Firemaking' | |||
local tableHtml = mw.html.create('table') | |||
local | :addClass('wikitable sortable stickyHeader') | ||
local headerRow0 = tableHtml:tag('tr'):addClass('headerRow-0') | |||
headerRow0:tag('th'):attr('colspan', '2') | |||
:attr('rowspan', '2') | |||
:wikitext('Logs') | |||
headerRow0:tag('th'):attr('rowspan', '2') | |||
:wikitext(Icons._SkillRealmIcon('Firemaking', realm.id) .. '<br>Level') | |||
headerRow0:tag('th'):attr('rowspan', '2') | |||
:wikitext('[[DLC]]') | |||
headerRow0:tag('th'):attr('rowspan', '2') | |||
:wikitext('Burn<br>Time') | |||
headerRow0:tag('th'):attr('colspan', '2') | |||
:wikitext('Without Bonfire') | |||
headerRow0:tag('th'):attr('colspan', '2') | |||
:wikitext('With Bonfire') | |||
headerRow0:tag('th'):attr('rowspan', '2') | |||
:wikitext('Bonfire<br>Bonus') | |||
headerRow0:tag('th'):attr('rowspan', '2') | |||
:wikitext('Bonfire<br>Time') | |||
local headerRow1 = tableHtml:tag('tr'):addClass('headerRow-1') | |||
headerRow1:tag('th'):wikitext('XP') | |||
headerRow1:tag('th'):wikitext('XP/s') | |||
headerRow1:tag('th'):wikitext('XP') | |||
headerRow1:tag('th'):wikitext('XP/s') | |||
local logsData = GameData.getEntities(SkillData.Firemaking.logs, function(obj) | |||
return p.getRecipeRealm(obj) == realm.id | |||
end) | |||
table.sort(logsData, function(a, b) return p.standardRecipeSort(skillID, a, b) end) | |||
for i, logData in ipairs(logsData) do | |||
local logs = Items.getItemByID(logData.logID) | |||
local name = logs.name | |||
local level = p.getRecipeLevel(skillID, logData) | |||
local baseXP = logData.baseAbyssalExperience or logData.baseExperience | |||
local reqText = p.getRecipeRequirementText(SkillData.Firemaking.name, logData) | |||
local bonfireBonus = logData.bonfireAXPBonus or logData.bonfireXPBonus | |||
local burnTime = logData.baseInterval / 1000 | |||
local bonfireTime = logData.baseBonfireInterval / 1000 | |||
local XPS = baseXP / burnTime | |||
local XP_BF = baseXP * (1 + bonfireBonus / 100) | |||
local XPS_BF = Num.round(XP_BF / burnTime, 2, 2) | |||
XP_BF = Num.round(XP_BF, 2, 0) | |||
local row = tableHtml:tag('tr') | |||
row:tag('td'):attr('data-sort-value', name) | |||
:wikitext(Icons.Icon({name, type='item', notext=true})) | |||
row:tag('td'):wikitext('[[' .. name .. ']]') | |||
row:tag('td'):css('text-align', 'center') | |||
:wikitext(level) | |||
row:tag('td'):css('text-align', 'center') | |||
:attr('data-sort-value', Icons.getExpansionID(logData.logID)) | |||
:wikitext(Icons.getDLCColumnIcon(logData.logID)) | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', burnTime) | |||
:wikitext(Shared.timeString(burnTime, true)) | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', baseXP) | |||
:wikitext(Num.formatnum(baseXP)) | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', XPS) | |||
:wikitext(Num.formatnum(Num.round(XPS, 2, 2))) | |||
if bonfireBonus == 0 then | |||
row:tag('td'):attr('colspan', '4') | |||
:addClass('table-na') | |||
:wikitext('N/A') | |||
else | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', XP_BF) | |||
:wikitext(Num.formatnum(XP_BF)) | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', XPS_BF) | |||
:wikitext(Num.formatnum(XPS_BF, 2, 2)) | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', bonfireBonus) | |||
:wikitext(bonfireBonus .. '%') | |||
row:tag('td'):css('text-align', 'right') | |||
:attr('data-sort-value', bonfireTime) | |||
:wikitext(Shared.timeString(bonfireTime, true)) | |||
end | |||
end | end | ||
return tostring(tableHtml) | |||
end | end | ||
return p | return p |