Module:ModifierTables: Difference between revisions
From Melvor Idle
Falterfire (talk | contribs) (fixed a potential bug with getModifierTable if no maxOtherMods was sent in) |
(_getModifierTable: Add equipment slot links to type column) |
||
(28 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
--Module that constructs tables for all things that can affect Player Modifiers | --Module that constructs tables for all things that can affect Player Modifiers | ||
--This includes Agility, Equipment, and | --This includes Agility, Equipment, Pets, Prayers, and Constellations right now | ||
local p = {} | local p = {} | ||
Line 6: | Line 6: | ||
local Constants = require('Module:Constants') | local Constants = require('Module:Constants') | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local Common = require('Module:Common') | |||
local Pets = require('Module:Pets') | local Pets = require('Module:Pets') | ||
local Items = require('Module:Items') | local Items = require('Module:Items') | ||
local Skills = require('Module:Skills') | |||
local Agility = require('Module:Skills/Agility') | local Agility = require('Module:Skills/Agility') | ||
local Prayer = require('Module:Prayer') | local Prayer = require('Module:Prayer') | ||
local Shop = require('Module:Shop') | local Shop = require('Module:Shop') | ||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local Cartography = require('Module:Skills/Cartography') | |||
local GameData = require('Module:GameData') | |||
--First up, functions to get all the things in a category that have a given modifier: | --First up, functions to get all the things in a category that have a given modifier: | ||
function p.getModifierValue(modifiers, modifier, skill, getOpposites) | function p.getModifierValue(modifiers, modifier, skill, getOpposites) | ||
--Sometimes nil modifier sets will get here, which is fine. Just return 0 immediately | |||
if modifiers == nil then | |||
return 0 | |||
end | |||
--Make sure we have the skillID and not the name | |||
if skill ~= nil then | |||
if skill == '' then | |||
skill = nil | |||
elseif Constants.getSkillID(skill) ~= nil then | |||
-- skill is a skill name | |||
skill = Constants.getSkillID(skill) | |||
elseif Constants.getSkillName(skill) == nil then | |||
-- skill is neither a skill name or ID | |||
return 0 | |||
end | |||
end | |||
--By default, attempt to add the increased and decreased prefixes to the modifier | |||
--But if getOpposites is false, only look for an exact match | |||
local mods = {} | |||
if getOpposites == nil or getOpposites then | |||
mods.inc = 'increased'..modifier | |||
mods.dec = 'decreased'..modifier | |||
else | |||
mods.inc = modifier | |||
end | |||
local magnitude = { inc = 0, dec = 0 } | |||
for modType, modName in pairs(mods) do | |||
if modifiers[modName] ~= nil then | |||
local valueArray = nil | |||
if type(modifiers[modName]) ~= 'table' then | |||
valueArray = {modifiers[modName]} | |||
else | |||
valueArray = modifiers[modName] | |||
end | |||
for i, subVal in ipairs(valueArray) do | |||
if type(subVal) == 'table' then | |||
if subVal.skillID ~= nil then | |||
-- Modifier value is skill specific | |||
if skill == nil or skill == '' or subVal.skillID == skill then | |||
magnitude[modType] = magnitude[modType] + subVal.value | |||
end | |||
else | |||
-- Modifier value is a table of two numbers representing a range. Take the largest value | |||
magnitude[modType] = magnitude[modType] + (subVal[2] or 0) | |||
end | |||
else | |||
magnitude[modType] = magnitude[modType] + subVal | |||
end | |||
end | |||
end | |||
end | |||
return magnitude.inc - magnitude.dec | |||
end | end | ||
function p.getItemsWithModifier(modifiers, skill, getOpposites) | function p.getItemsWithModifier(modifiers, skill, getOpposites) | ||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local itemList = Items.getItems( | |||
function(item) | |||
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then | |||
return false | |||
elseif item.modifiers == nil or Shared.tableIsEmpty(item.modifiers) then | |||
return false | |||
end | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(item.modifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return itemList | |||
end | end | ||
function p.getObstaclesWithModifier(modifiers, skill, getOpposites) | function p.getObstaclesWithModifier(modifiers, skill, getOpposites) | ||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local obstList = Agility.getObstacles( | |||
function(obst) | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(obst.modifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return obstList | |||
end | |||
function p.getConstellationsWithModifier(modifiers, skill, getOpposites) | |||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local consList = Skills.getConstellations( | |||
function(cons) | |||
local consMods = Skills._buildAstrologyModifierArray(cons, 1, true, true, true, true) | |||
for i, modifier in ipairs(modifiers) do | |||
if p.getModifierValue(consMods, modifier, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return consList | |||
end | end | ||
function p.getPillarsWithModifier(modifiers, skill, getOpposites) | function p.getPillarsWithModifier(modifiers, skill, getOpposites) | ||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local pillarList = Agility.getPillars( | |||
function(pillar) | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(pillar.modifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return pillarList | |||
end | end | ||
function p.getPetsWithModifier(modifiers, skill, getOpposites) | function p.getPetsWithModifier(modifiers, skill, getOpposites) | ||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local petList = Pets.getPets( | |||
function(pet) | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return petList | |||
end | end | ||
Line 158: | Line 182: | ||
function p.getUpgradesWithModifier(modifiers, skill, getOpposites) | function p.getUpgradesWithModifier(modifiers, skill, getOpposites) | ||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local upgradeList = Shop.getPurchases( | |||
function(purchase) | |||
if purchase.category == 'melvorD:GolbinRaid' then | |||
return false | |||
end | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(purchase.contains.modifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return upgradeList | |||
end | |||
function p.getPOIsWithModifier(modifiers, skill, getOpposites) | |||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local POIList = Cartography.getPointsOfInterest( | |||
function(POI) | |||
if POI.activeModifiers == nil then | |||
return false | |||
end | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(POI.activeModifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return POIList | |||
end | |||
function p.getCartoMasteryBonusesWithModifier(modifiers, skill, getOpposites) | |||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
local bonusList = Cartography.getMasteryBonuses( | |||
function(bonus) | |||
if bonus.modifiers == nil or Shared.tableIsEmpty(bonus.modifiers) then | |||
return false | |||
end | |||
for i, mod in ipairs(modifiers) do | |||
if p.getModifierValue(bonus.modifiers, mod, skill, getOpposites) ~= 0 then | |||
return true | |||
end | |||
end | |||
return false | |||
end) | |||
return bonusList | |||
end | end | ||
function p._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods) | function p._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods, forceMagnitudeSort) | ||
local modifierNames = {} | |||
if type(modifiers) == 'string' then | |||
modifiers = {modifiers} | |||
end | |||
for i, modifier in pairs(modifiers) do | |||
if getOpposites then | |||
table.insert(modifierNames, 'increased'..modifier) | |||
table.insert(modifierNames, 'decreased'..modifier) | |||
else | |||
table.insert(modifierNames, modifier) | |||
end | |||
end | |||
local hasOtherModifiers = false | |||
local modifierCount = Shared.tableCount(modifiers) | |||
if skill ~= nil then | |||
if skill == '' then | |||
skill = nil | |||
elseif Constants.getSkillID(skill) ~= nil then | |||
-- skill is a skill name | |||
skill = Constants.getSkillID(skill) | |||
elseif Constants.getSkillName(skill) == nil then | |||
-- skill is neither a skill name or ID | |||
return Shared.printError('Failed to find a skill ID for "' .. skill .. '"') | |||
end | |||
end | |||
local getModText = | |||
function(modifiers) | |||
local modTextArray = { ["visible"] = {}, ["overflow"] = {} } | |||
local otherModCount = 0 | |||
local mainModText = {} | |||
for modName, modValue in Shared.skpairs(modifiers) do | |||
local includedMod = Shared.contains(modifierNames, modName) | |||
local valueArray = nil | |||
if type(modValue) ~= 'table' then | |||
valueArray = {modValue} | |||
else | |||
valueArray = modValue | |||
end | |||
for j, subVal in ipairs(valueArray) do | |||
local includeInMainText = includedMod | |||
if type(subVal) == 'table' and subVal.skillID ~= nil then | |||
-- Modifier value is skill specific | |||
if includeInMainText then | |||
-- If the skill doesn't match then don't include in the main text | |||
includeInMainText = skill == nil or skill == '' or subVal.skillID == skill | |||
end | |||
subVal = {subVal} | |||
end | |||
if includeInMainText then | |||
table.insert(mainModText, Constants._getModifierText(modName, subVal)) | |||
else | |||
otherModCount = otherModCount + 1 | |||
local key = ((maxOtherMods == nil or otherModCount <= maxOtherMods) and 'visible') or 'overflow' | |||
table.insert(modTextArray[key], Constants._getModifierText(modName, subVal)) | |||
end | |||
end | |||
end | end | ||
local overflowModCount = Shared.tableCount(modTextArray['overflow']) | |||
if overflowModCount == 1 then | |||
table.insert(modTextArray['visible'], modTextArray['overflow'][1]) | |||
end | |||
local otherModText = {table.concat(modTextArray['visible'], '<br/>')} | |||
if overflowModCount > 1 then | |||
-- Number of other modifiers for the object exceed the specified maximum | |||
table.insert(otherModText, '<br/><span class="mw-collapsible mw-collapsed" ') | |||
table.insert(otherModText, 'data-expandtext="Show ' .. Shared.formatnum(overflowModCount) .. ' more modifiers", data-collapsetext="Hide">') | |||
table.insert(otherModText, table.concat(modTextArray['overflow'], '<br/>') .. '</span>') | |||
end | |||
return table.concat(mainModText, '<br/>'), table.concat(otherModText) | |||
end | |||
local tableArray = {} | |||
--Going through each type of thing to add to the array | |||
local itemList = p.getItemsWithModifier(modifiers, skill, getOpposites) | |||
for i, item in ipairs(itemList) do | |||
local row = {} | |||
row.name = item.name | |||
row.icon = Icons.Icon({item.name, type='item'}) | |||
row.expIcon = Icons.getExpansionIcon(item.id) | |||
row.type = 'Item' | |||
row.typeText = row.type | |||
--For equipment, show the slot they go in | |||
if item.validSlots ~= nil and not Shared.tableIsEmpty(item.validSlots) then | |||
local rowTypePart = {} | |||
for j, slot in ipairs(item.validSlots) do | |||
table.insert(rowTypePart, Common.getEquipmentSlotLink(slot)) | |||
end | |||
row.typeText = row.typeText .. ' (' .. table.concat(item.validSlots, ', ') .. ')' | |||
row.type = row.type .. ' (' .. table.concat(rowTypePart, ', ') .. ')' | |||
end | |||
local totalVal = 0 | |||
for i, mod in pairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(item.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(item.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | end | ||
local petList = p.getPetsWithModifier(modifiers, skill, getOpposites) | |||
for i, pet in Shared.skpairs(petList) do | |||
local row = {} | |||
row.name = pet.name | |||
row.icon = Icons.Icon({pet.name, type='pet'}) | |||
row.expIcon = Icons.getExpansionIcon(pet.id) | |||
row.type = '[[Pets|Pet]]' | |||
row.typeText = 'Pet' | |||
local totalVal = 0 | |||
for i, mod in pairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(pet.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(pet.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local obstList = p.getObstaclesWithModifier(modifiers, skill, getOpposites) | |||
table.sort(obstList, function(a, b) return a.category < b.category end) | |||
for i, obst in Shared.skpairs(obstList) do | |||
local row = {} | |||
row.name = obst.name | |||
row.icon = Icons.Icon({'Agility%23'..string.gsub(obst.name, ' ', ''), obst.name, type='skill', img='Agility'}) | |||
row.expIcon = Icons.getExpansionIcon(obst.id) | |||
row.type = '[[Agility#Obstacles|Agility Obstacle '..tostring(tonumber(obst.category)+1)..']]' | |||
row.typeText = 'Agility Obstacle '..string.format("%02d", (obst.category + 1)) | |||
local totalVal = 0 | |||
for i, mod in pairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(obst.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(obst.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local pillarList = p.getPillarsWithModifier(modifiers, skill, getOpposites) | |||
for i, pillar in ipairs(pillarList) do | |||
local row = {} | |||
row.name = pillar.name | |||
row.icon = Icons.Icon({'Agility%23'..string.gsub(pillar.name, ' ', ''), pillar.name, type='skill', img='Agility'}) | |||
row.expIcon = Icons.getExpansionIcon(pillar.id) | |||
row.type = '[[Agility#Passive Pillars|Agility Pillar]]' | |||
row.typeText = 'Agility Pillar' | |||
local totalVal = 0 | |||
for i, mod in pairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(pillar.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(pillar.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local prayerList = p.getPrayersWithModifier(modifiers, skill, getOpposites) | |||
for i, prayer in ipairs(prayerList) do | |||
local row = {} | |||
row.name = prayer.name | |||
row.icon = Icons.Icon({prayer.name, type='prayer'}) | |||
row.expIcon = Icons.getExpansionIcon(prayer.id) | |||
row.type = [[Prayer]] | |||
row.typeText = 'Prayer' | |||
local totalVal = 0 | |||
for i, mod in ipairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(prayer.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(prayer.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local upgradeList = p.getUpgradesWithModifier(modifiers, skill, getOpposites) | |||
for i, upgrade in ipairs(upgradeList) do | |||
local row = {} | |||
row.name = Shop._getPurchaseName(upgrade) | |||
row.icon = Icons.Icon({row.name, type='upgrade'}) | |||
row.expIcon = Icons.getExpansionIcon(upgrade.id) | |||
row.type = '[[Shop|Upgrade]]' | |||
row.typeText = 'Upgrade' | |||
local totalVal = 0 | |||
for i, mod in pairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(upgrade.contains.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(upgrade.contains.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local constellationList = p.getConstellationsWithModifier(modifiers, skill, getOpposites) | |||
for i, cons in ipairs(constellationList) do | |||
local modList = Skills._buildAstrologyModifierArray(cons, nil, true, true, true, true) | |||
local row = {} | |||
row.name = cons.name | |||
row.icon = Icons.Icon({cons.name, type='constellation'}) | |||
row.expIcon = Icons.getExpansionIcon(cons.id) | |||
row.type = '[[Astrology#Constellations|Constellation]]' | |||
row.typeText = 'Constellation' | |||
row.modifierText, row.otherModifiers = getModText(modList) | |||
local totalVal = 0 | |||
for i, mod in pairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(modList, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local POIList = p.getPOIsWithModifier(modifiers, skill, getOpposites) | |||
for i, POI in ipairs(POIList) do | |||
local row = {} | |||
row.name = POI.name | |||
row.icon = Icons.Icon({POI.name, type='poi'}) | |||
row.expIcon = Icons.getExpansionIcon(POI.id) | |||
row.type = '[[Cartography|Point of Interest]]' | |||
row.typeText = 'Point of Interest' | |||
local totalVal = 0 | |||
for i, mod in ipairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(POI.activeModifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(POI.activeModifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local cartoBonusList = p.getCartoMasteryBonusesWithModifier(modifiers, skill, getOpposites) | |||
for i, bonus in ipairs(cartoBonusList) do | |||
local row = {} | |||
row.name = Shared.formatnum(bonus.masteredHexes) .. ' Hexes Mastered' | |||
row.icon = Icons.Icon({'Cartography', Shared.formatnum(bonus.masteredHexes) .. ' Hexes Mastered', type='skill'}) | |||
row.expIcon = Icons.getExpansionIcon(bonus.id) | |||
row.type = Icons.Icon({'Cartography', 'Mastery Bonus', section='Mastery Unlocks', type='skill', noicon=true}) | |||
row.typeText = 'Mastery Bonus' | |||
local totalVal = 0 | |||
for i, mod in ipairs(modifiers) do | |||
totalVal = totalVal + p.getModifierValue(bonus.modifiers, mod, skill, getOpposites) | |||
end | |||
row.val = totalVal | |||
row.modifierText, row.otherModifiers = getModText(bonus.modifiers) | |||
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then | |||
hasOtherModifiers = true | |||
end | |||
table.insert(tableArray, row) | |||
end | |||
local result = '{| class="wikitable sortable stickyHeader"' | |||
result = result..'\r\n|- class="headerRow-0"' | |||
result = result..'\r\n!Source!!Type!!'..columnName | |||
if hasOtherModifiers and displayOtherMods then result = result..'!!Other Modifiers' end | |||
--Sort by value if only one modifier was passed in | |||
--Otherwise sort alphabetically by type, then name | |||
table.sort(tableArray, function(a, b) | |||
if (modifierCount == 1 or forceMagnitudeSort) and a.val ~= b.val then | |||
return a.val > b.val | |||
elseif a.typeText ~= b.typeText then | |||
return a.typeText < b.typeText | |||
else | |||
return a.name < b.name | |||
end | |||
end) | |||
for i, row in ipairs(tableArray) do | |||
result = result..'\r\n|-' | |||
result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon | |||
result = result..'|| data-sort-value="'..row.typeText..'" | '..row.expIcon..row.type..'||data-sort-value="'..row.val..'"| '..row.modifierText | |||
if hasOtherModifiers and displayOtherMods then | |||
result = result..'|| '..row.otherModifiers | |||
end | |||
end | |||
result = result..'\r\n|}' | |||
return result | |||
end | end | ||
function p.getModifierTable(frame) | function p.getModifierTable(frame) | ||
local modifier = frame.args ~= nil and frame.args[1] or frame[1] | |||
local skill = frame.args ~= nil and frame.args.skill or frame.skill | |||
local columnName = frame.args ~= nil and frame.args[2] or frame[2] | |||
local getOpposites = frame.args ~= nil and frame.args[3] or frame[3] | |||
local displayOtherMods = frame.args ~= nil and frame.args.displayOtherMods or frame.displayOtherMods | |||
local maxOtherMods = frame.args ~= nil and tonumber(frame.args.maxOtherMods) or 5 | |||
local forceMagnitudeSort = frame.args ~= nil and string.upper(tostring(frame.args.forceMagnitudeSort)) == 'TRUE' or false | |||
if Shared.contains(modifier, ',') then | |||
modifier = Shared.splitString(modifier, ',') | |||
end | |||
if getOpposites ~= nil then | |||
getOpposites = string.upper(getOpposites) ~= 'FALSE' | |||
else | |||
getOpposites = true | |||
end | |||
if displayOtherMods ~= nil then | |||
displayOtherMods = string.upper(displayOtherMods) ~= 'FALSE' | |||
else | |||
displayOtherMods = true | |||
end | |||
return p._getModifierTable(modifier, skill, columnName, getOpposites, displayOtherMods, maxOtherMods, forceMagnitudeSort) | |||
end | |||
-- Function to list all available modifiers for the relevant templates. | |||
function p.getAllModifiers() | |||
local tabl = mw.html.create('table') | |||
:addClass('mw-collapsible mw-collapsed') | |||
tabl:tag('caption') | |||
:css('min-width', '200px') | |||
:tag('b') | |||
:wikitext('All Modifiers List') | |||
-- First, sort modifiers | |||
local modifierNames = {} | |||
for k, _ in pairs(GameData.rawData.modifierData) do | |||
table.insert(modifierNames, tostring(k)) | |||
end | |||
table.sort(modifierNames) | |||
-- Then add modifiers to output table | |||
for _, v in pairs(modifierNames) do | |||
tabl:tag('tr') | |||
:tag('td') | |||
:tag('code') | |||
:wikitext(tostring(v)):done() | |||
:done() | |||
:done() | |||
end | |||
return tostring(tabl) | |||
end | |||
--Function for console testing of modifier tables | |||
function p.getModifierTableTest() | |||
return p.getModifierTable({args = {'MeleeMaxHit', 'GP Boosts'}}) | |||
end | end | ||
return p | return p |
Latest revision as of 13:35, 2 May 2024
Documentation for this module may be created at Module:ModifierTables/doc
--Module that constructs tables for all things that can affect Player Modifiers
--This includes Agility, Equipment, Pets, Prayers, and Constellations right now
local p = {}
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Common = require('Module:Common')
local Pets = require('Module:Pets')
local Items = require('Module:Items')
local Skills = require('Module:Skills')
local Agility = require('Module:Skills/Agility')
local Prayer = require('Module:Prayer')
local Shop = require('Module:Shop')
local Icons = require('Module:Icons')
local Cartography = require('Module:Skills/Cartography')
local GameData = require('Module:GameData')
--First up, functions to get all the things in a category that have a given modifier:
function p.getModifierValue(modifiers, modifier, skill, getOpposites)
--Sometimes nil modifier sets will get here, which is fine. Just return 0 immediately
if modifiers == nil then
return 0
end
--Make sure we have the skillID and not the name
if skill ~= nil then
if skill == '' then
skill = nil
elseif Constants.getSkillID(skill) ~= nil then
-- skill is a skill name
skill = Constants.getSkillID(skill)
elseif Constants.getSkillName(skill) == nil then
-- skill is neither a skill name or ID
return 0
end
end
--By default, attempt to add the increased and decreased prefixes to the modifier
--But if getOpposites is false, only look for an exact match
local mods = {}
if getOpposites == nil or getOpposites then
mods.inc = 'increased'..modifier
mods.dec = 'decreased'..modifier
else
mods.inc = modifier
end
local magnitude = { inc = 0, dec = 0 }
for modType, modName in pairs(mods) do
if modifiers[modName] ~= nil then
local valueArray = nil
if type(modifiers[modName]) ~= 'table' then
valueArray = {modifiers[modName]}
else
valueArray = modifiers[modName]
end
for i, subVal in ipairs(valueArray) do
if type(subVal) == 'table' then
if subVal.skillID ~= nil then
-- Modifier value is skill specific
if skill == nil or skill == '' or subVal.skillID == skill then
magnitude[modType] = magnitude[modType] + subVal.value
end
else
-- Modifier value is a table of two numbers representing a range. Take the largest value
magnitude[modType] = magnitude[modType] + (subVal[2] or 0)
end
else
magnitude[modType] = magnitude[modType] + subVal
end
end
end
end
return magnitude.inc - magnitude.dec
end
function p.getItemsWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local itemList = Items.getItems(
function(item)
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
return false
elseif item.modifiers == nil or Shared.tableIsEmpty(item.modifiers) then
return false
end
for i, mod in ipairs(modifiers) do
if p.getModifierValue(item.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return itemList
end
function p.getObstaclesWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local obstList = Agility.getObstacles(
function(obst)
for i, mod in ipairs(modifiers) do
if p.getModifierValue(obst.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return obstList
end
function p.getConstellationsWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local consList = Skills.getConstellations(
function(cons)
local consMods = Skills._buildAstrologyModifierArray(cons, 1, true, true, true, true)
for i, modifier in ipairs(modifiers) do
if p.getModifierValue(consMods, modifier, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return consList
end
function p.getPillarsWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local pillarList = Agility.getPillars(
function(pillar)
for i, mod in ipairs(modifiers) do
if p.getModifierValue(pillar.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return pillarList
end
function p.getPetsWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local petList = Pets.getPets(
function(pet)
for i, mod in ipairs(modifiers) do
if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return petList
end
function p.getPrayersWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local prayerList = Prayer.getPrayers(
function(prayer)
for i, mod in ipairs(modifiers) do
if p.getModifierValue(prayer.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return prayerList
end
function p.getUpgradesWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local upgradeList = Shop.getPurchases(
function(purchase)
if purchase.category == 'melvorD:GolbinRaid' then
return false
end
for i, mod in ipairs(modifiers) do
if p.getModifierValue(purchase.contains.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return upgradeList
end
function p.getPOIsWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local POIList = Cartography.getPointsOfInterest(
function(POI)
if POI.activeModifiers == nil then
return false
end
for i, mod in ipairs(modifiers) do
if p.getModifierValue(POI.activeModifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return POIList
end
function p.getCartoMasteryBonusesWithModifier(modifiers, skill, getOpposites)
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
local bonusList = Cartography.getMasteryBonuses(
function(bonus)
if bonus.modifiers == nil or Shared.tableIsEmpty(bonus.modifiers) then
return false
end
for i, mod in ipairs(modifiers) do
if p.getModifierValue(bonus.modifiers, mod, skill, getOpposites) ~= 0 then
return true
end
end
return false
end)
return bonusList
end
function p._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods, forceMagnitudeSort)
local modifierNames = {}
if type(modifiers) == 'string' then
modifiers = {modifiers}
end
for i, modifier in pairs(modifiers) do
if getOpposites then
table.insert(modifierNames, 'increased'..modifier)
table.insert(modifierNames, 'decreased'..modifier)
else
table.insert(modifierNames, modifier)
end
end
local hasOtherModifiers = false
local modifierCount = Shared.tableCount(modifiers)
if skill ~= nil then
if skill == '' then
skill = nil
elseif Constants.getSkillID(skill) ~= nil then
-- skill is a skill name
skill = Constants.getSkillID(skill)
elseif Constants.getSkillName(skill) == nil then
-- skill is neither a skill name or ID
return Shared.printError('Failed to find a skill ID for "' .. skill .. '"')
end
end
local getModText =
function(modifiers)
local modTextArray = { ["visible"] = {}, ["overflow"] = {} }
local otherModCount = 0
local mainModText = {}
for modName, modValue in Shared.skpairs(modifiers) do
local includedMod = Shared.contains(modifierNames, modName)
local valueArray = nil
if type(modValue) ~= 'table' then
valueArray = {modValue}
else
valueArray = modValue
end
for j, subVal in ipairs(valueArray) do
local includeInMainText = includedMod
if type(subVal) == 'table' and subVal.skillID ~= nil then
-- Modifier value is skill specific
if includeInMainText then
-- If the skill doesn't match then don't include in the main text
includeInMainText = skill == nil or skill == '' or subVal.skillID == skill
end
subVal = {subVal}
end
if includeInMainText then
table.insert(mainModText, Constants._getModifierText(modName, subVal))
else
otherModCount = otherModCount + 1
local key = ((maxOtherMods == nil or otherModCount <= maxOtherMods) and 'visible') or 'overflow'
table.insert(modTextArray[key], Constants._getModifierText(modName, subVal))
end
end
end
local overflowModCount = Shared.tableCount(modTextArray['overflow'])
if overflowModCount == 1 then
table.insert(modTextArray['visible'], modTextArray['overflow'][1])
end
local otherModText = {table.concat(modTextArray['visible'], '<br/>')}
if overflowModCount > 1 then
-- Number of other modifiers for the object exceed the specified maximum
table.insert(otherModText, '<br/><span class="mw-collapsible mw-collapsed" ')
table.insert(otherModText, 'data-expandtext="Show ' .. Shared.formatnum(overflowModCount) .. ' more modifiers", data-collapsetext="Hide">')
table.insert(otherModText, table.concat(modTextArray['overflow'], '<br/>') .. '</span>')
end
return table.concat(mainModText, '<br/>'), table.concat(otherModText)
end
local tableArray = {}
--Going through each type of thing to add to the array
local itemList = p.getItemsWithModifier(modifiers, skill, getOpposites)
for i, item in ipairs(itemList) do
local row = {}
row.name = item.name
row.icon = Icons.Icon({item.name, type='item'})
row.expIcon = Icons.getExpansionIcon(item.id)
row.type = 'Item'
row.typeText = row.type
--For equipment, show the slot they go in
if item.validSlots ~= nil and not Shared.tableIsEmpty(item.validSlots) then
local rowTypePart = {}
for j, slot in ipairs(item.validSlots) do
table.insert(rowTypePart, Common.getEquipmentSlotLink(slot))
end
row.typeText = row.typeText .. ' (' .. table.concat(item.validSlots, ', ') .. ')'
row.type = row.type .. ' (' .. table.concat(rowTypePart, ', ') .. ')'
end
local totalVal = 0
for i, mod in pairs(modifiers) do
totalVal = totalVal + p.getModifierValue(item.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(item.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local petList = p.getPetsWithModifier(modifiers, skill, getOpposites)
for i, pet in Shared.skpairs(petList) do
local row = {}
row.name = pet.name
row.icon = Icons.Icon({pet.name, type='pet'})
row.expIcon = Icons.getExpansionIcon(pet.id)
row.type = '[[Pets|Pet]]'
row.typeText = 'Pet'
local totalVal = 0
for i, mod in pairs(modifiers) do
totalVal = totalVal + p.getModifierValue(pet.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(pet.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local obstList = p.getObstaclesWithModifier(modifiers, skill, getOpposites)
table.sort(obstList, function(a, b) return a.category < b.category end)
for i, obst in Shared.skpairs(obstList) do
local row = {}
row.name = obst.name
row.icon = Icons.Icon({'Agility%23'..string.gsub(obst.name, ' ', ''), obst.name, type='skill', img='Agility'})
row.expIcon = Icons.getExpansionIcon(obst.id)
row.type = '[[Agility#Obstacles|Agility Obstacle '..tostring(tonumber(obst.category)+1)..']]'
row.typeText = 'Agility Obstacle '..string.format("%02d", (obst.category + 1))
local totalVal = 0
for i, mod in pairs(modifiers) do
totalVal = totalVal + p.getModifierValue(obst.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(obst.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local pillarList = p.getPillarsWithModifier(modifiers, skill, getOpposites)
for i, pillar in ipairs(pillarList) do
local row = {}
row.name = pillar.name
row.icon = Icons.Icon({'Agility%23'..string.gsub(pillar.name, ' ', ''), pillar.name, type='skill', img='Agility'})
row.expIcon = Icons.getExpansionIcon(pillar.id)
row.type = '[[Agility#Passive Pillars|Agility Pillar]]'
row.typeText = 'Agility Pillar'
local totalVal = 0
for i, mod in pairs(modifiers) do
totalVal = totalVal + p.getModifierValue(pillar.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(pillar.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local prayerList = p.getPrayersWithModifier(modifiers, skill, getOpposites)
for i, prayer in ipairs(prayerList) do
local row = {}
row.name = prayer.name
row.icon = Icons.Icon({prayer.name, type='prayer'})
row.expIcon = Icons.getExpansionIcon(prayer.id)
row.type = [[Prayer]]
row.typeText = 'Prayer'
local totalVal = 0
for i, mod in ipairs(modifiers) do
totalVal = totalVal + p.getModifierValue(prayer.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(prayer.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local upgradeList = p.getUpgradesWithModifier(modifiers, skill, getOpposites)
for i, upgrade in ipairs(upgradeList) do
local row = {}
row.name = Shop._getPurchaseName(upgrade)
row.icon = Icons.Icon({row.name, type='upgrade'})
row.expIcon = Icons.getExpansionIcon(upgrade.id)
row.type = '[[Shop|Upgrade]]'
row.typeText = 'Upgrade'
local totalVal = 0
for i, mod in pairs(modifiers) do
totalVal = totalVal + p.getModifierValue(upgrade.contains.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(upgrade.contains.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local constellationList = p.getConstellationsWithModifier(modifiers, skill, getOpposites)
for i, cons in ipairs(constellationList) do
local modList = Skills._buildAstrologyModifierArray(cons, nil, true, true, true, true)
local row = {}
row.name = cons.name
row.icon = Icons.Icon({cons.name, type='constellation'})
row.expIcon = Icons.getExpansionIcon(cons.id)
row.type = '[[Astrology#Constellations|Constellation]]'
row.typeText = 'Constellation'
row.modifierText, row.otherModifiers = getModText(modList)
local totalVal = 0
for i, mod in pairs(modifiers) do
totalVal = totalVal + p.getModifierValue(modList, mod, skill, getOpposites)
end
row.val = totalVal
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local POIList = p.getPOIsWithModifier(modifiers, skill, getOpposites)
for i, POI in ipairs(POIList) do
local row = {}
row.name = POI.name
row.icon = Icons.Icon({POI.name, type='poi'})
row.expIcon = Icons.getExpansionIcon(POI.id)
row.type = '[[Cartography|Point of Interest]]'
row.typeText = 'Point of Interest'
local totalVal = 0
for i, mod in ipairs(modifiers) do
totalVal = totalVal + p.getModifierValue(POI.activeModifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(POI.activeModifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local cartoBonusList = p.getCartoMasteryBonusesWithModifier(modifiers, skill, getOpposites)
for i, bonus in ipairs(cartoBonusList) do
local row = {}
row.name = Shared.formatnum(bonus.masteredHexes) .. ' Hexes Mastered'
row.icon = Icons.Icon({'Cartography', Shared.formatnum(bonus.masteredHexes) .. ' Hexes Mastered', type='skill'})
row.expIcon = Icons.getExpansionIcon(bonus.id)
row.type = Icons.Icon({'Cartography', 'Mastery Bonus', section='Mastery Unlocks', type='skill', noicon=true})
row.typeText = 'Mastery Bonus'
local totalVal = 0
for i, mod in ipairs(modifiers) do
totalVal = totalVal + p.getModifierValue(bonus.modifiers, mod, skill, getOpposites)
end
row.val = totalVal
row.modifierText, row.otherModifiers = getModText(bonus.modifiers)
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end
table.insert(tableArray, row)
end
local result = '{| class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!Source!!Type!!'..columnName
if hasOtherModifiers and displayOtherMods then result = result..'!!Other Modifiers' end
--Sort by value if only one modifier was passed in
--Otherwise sort alphabetically by type, then name
table.sort(tableArray, function(a, b)
if (modifierCount == 1 or forceMagnitudeSort) and a.val ~= b.val then
return a.val > b.val
elseif a.typeText ~= b.typeText then
return a.typeText < b.typeText
else
return a.name < b.name
end
end)
for i, row in ipairs(tableArray) do
result = result..'\r\n|-'
result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon
result = result..'|| data-sort-value="'..row.typeText..'" | '..row.expIcon..row.type..'||data-sort-value="'..row.val..'"| '..row.modifierText
if hasOtherModifiers and displayOtherMods then
result = result..'|| '..row.otherModifiers
end
end
result = result..'\r\n|}'
return result
end
function p.getModifierTable(frame)
local modifier = frame.args ~= nil and frame.args[1] or frame[1]
local skill = frame.args ~= nil and frame.args.skill or frame.skill
local columnName = frame.args ~= nil and frame.args[2] or frame[2]
local getOpposites = frame.args ~= nil and frame.args[3] or frame[3]
local displayOtherMods = frame.args ~= nil and frame.args.displayOtherMods or frame.displayOtherMods
local maxOtherMods = frame.args ~= nil and tonumber(frame.args.maxOtherMods) or 5
local forceMagnitudeSort = frame.args ~= nil and string.upper(tostring(frame.args.forceMagnitudeSort)) == 'TRUE' or false
if Shared.contains(modifier, ',') then
modifier = Shared.splitString(modifier, ',')
end
if getOpposites ~= nil then
getOpposites = string.upper(getOpposites) ~= 'FALSE'
else
getOpposites = true
end
if displayOtherMods ~= nil then
displayOtherMods = string.upper(displayOtherMods) ~= 'FALSE'
else
displayOtherMods = true
end
return p._getModifierTable(modifier, skill, columnName, getOpposites, displayOtherMods, maxOtherMods, forceMagnitudeSort)
end
-- Function to list all available modifiers for the relevant templates.
function p.getAllModifiers()
local tabl = mw.html.create('table')
:addClass('mw-collapsible mw-collapsed')
tabl:tag('caption')
:css('min-width', '200px')
:tag('b')
:wikitext('All Modifiers List')
-- First, sort modifiers
local modifierNames = {}
for k, _ in pairs(GameData.rawData.modifierData) do
table.insert(modifierNames, tostring(k))
end
table.sort(modifierNames)
-- Then add modifiers to output table
for _, v in pairs(modifierNames) do
tabl:tag('tr')
:tag('td')
:tag('code')
:wikitext(tostring(v)):done()
:done()
:done()
end
return tostring(tabl)
end
--Function for console testing of modifier tables
function p.getModifierTableTest()
return p.getModifierTable({args = {'MeleeMaxHit', 'GP Boosts'}})
end
return p