Module:ModifierTables: Difference between revisions

From Melvor Idle
(Allowed for calling multiple modifiers at once and fixed various bugs)
(Update for v1.3)
 
(56 intermediate revisions by 4 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 Pets right now
--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 Modifiers = require('Module:Modifiers')
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 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.getItemsWithModifier(modifierNames, skill)
  --Sometimes nil modifier sets will get here, which is fine. Just return 0 immediately
local itemList = Items.getItems(
  if modifiers == nil then
function(item)
    return 0
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
  end
return false
 
elseif item.modifiers == nil or Shared.tableIsEmpty(item.modifiers) then
  --Make sure we have the skillID and not the name
return false
  if skill == '' then
end
    skill = nil
local mods = Modifiers.getMatchingModifiers(item.modifiers, modifierNames, skill)
  elseif type(skill) == 'string' then
return not Shared.tableIsEmpty(mods.matched)
    skill = Constants.getSkillID(skill)
end)
  end
return itemList
end


  --By default, attempt to add the increased and decreased prefixes to the modifier
function p.getObstaclesWithModifier(modifierNames, skill)
  --But if getOpposites is false, only look for an exact match
local obstList = Agility.getObstacles(
  local increaseMod, decreaseMod = '', ''
function(obst)
  if getOpposites == nil or getOpposites then
if obst.modifiers ~= nil then
    increaseMod = 'increased'..modifier
local mods = Modifiers.getMatchingModifiers(obst.modifiers, modifierNames, skill)
    decreaseMod = 'decreased'..modifier
return not Shared.tableIsEmpty(mods.matched)
  else
end
    increaseMod = modifier
return false
  end
end)
return obstList
end


  local increaseVal, decreaseVal = 0, 0
function p.getConstellationsWithModifier(modifierNames, skill)
  if modifiers[increaseMod] ~= nil and modifiers[increaseMod] ~= nil then
local consList = Skills.getConstellations(
    if type(modifiers[increaseMod]) == 'table' then
function(cons)
      for i, subVal in Shared.skpairs(modifiers[increaseMod]) do
local consMods = Skills._getConstellationModifiers(cons)
        if subVal[1] == skill then
for modType, modsForType in pairs(consMods) do
          increaseVal = subVal[2]
local mods = Modifiers.getMatchingModifiers(modsForType, modifierNames, skill)
        end
if not Shared.tableIsEmpty(mods.matched) then
      end
return true
    else
end
      increaseVal = modifiers[increaseMod]
end
    end
return false
  end
end)
return consList
end


  if modifiers[decreaseMod] ~= nil and modifiers[decreaseMod] ~= nil then
function p.getPillarsWithModifier(modifierNames, skill)
    if type(modifiers[decreaseMod]) == 'table' then
local pillarList = Agility.getPillars(
      for i, subVal in Shared.skpairs(modifiers[decreaseMod]) do
function(pillar)
        if subVal[1] == skill then
if pillar.modifiers ~= nil then
          decreaseVal = subVal[2]
local mods = Modifiers.getMatchingModifiers(pillar.modifiers, modifierNames, skill)
        end
return not Shared.tableIsEmpty(mods.matched)
      end
end
    else
return false
      decreaseVal = modifiers[decreaseMod]
end)
    end
return pillarList
  end
end


  return increaseVal - decreaseVal
function p.getPetsWithModifier(modifierNames, skill)
local petList = Pets.getPets(
function(pet)
if pet.modifiers ~= nil then
local mods = Modifiers.getMatchingModifiers(pet.modifiers, modifierNames, skill)
return not Shared.tableIsEmpty(mods.matched)
end
return false
end)
return petList
end
end


function p.getItemsWithModifier(modifiers, skill, getOpposites)
function p.getPrayersWithModifier(modifierNames, skill)
  if type(modifiers) == 'string' then
local prayerList = Prayer.getPrayers(
    modifiers = {modifiers}
function(prayer)
  end
if prayer.modifiers ~= nil then
  local itemList = Items.getItems(
local mods = Modifiers.getMatchingModifiers(prayer.modifiers, modifierNames, skill)
        function(item)
return not Shared.tableIsEmpty(mods.matched)
          local result = false
end
          for i, mod in Shared.skpairs(modifiers) do
return false
            if p.getModifierValue(item.modifiers, mod, skill, getOpposites) ~= 0 then
end)
              result = true
return prayerList
              break
            end
          end
          return result
        end)
  return itemList   
end
end


function p.getObstaclesWithModifier(modifiers, skill, getOpposites)
function p.getUpgradesWithModifier(modifierNames, skill)
  if type(modifiers) == 'string' then
local upgradeList = Shop.getPurchases(
    modifiers = {modifiers}
function(purchase)
  end
if purchase.category == 'melvorD:GolbinRaid'
  local obstList = Agility.getObstacles(
or purchase.contains == nil
        function(obst)
or purchase.contains.modifiers == nil
          local result = false
or Shared.tableIsEmpty(purchase.contains.modifiers) then
          for i, mod in Shared.skpairs(modifiers) do
return false
            if p.getModifierValue(obst.modifiers, mod, skill, getOpposites) ~= 0 then
end
              result = true
local mods = Modifiers.getMatchingModifiers(purchase.contains.modifiers, modifierNames, skill)
              break
return not Shared.tableIsEmpty(mods.matched)
            end
end)
          end
return upgradeList
          return result
        end)
  return obstList
end
end


function p.getPetsWithModifier(modifiers, skill, getOpposites)
function p.getPOIsWithModifier(modifierNames, skill)
  if type(modifiers) == 'string' then
local POIList = Cartography.getPointsOfInterest(
    modifiers = {modifiers}
function(POI)
  end
if POI.activeModifiers == nil then
  local petList = Pets.getPets(
return false
        function(pet)
end
          local result = false
local mods = Modifiers.getMatchingModifiers(POI.activeModifiers, modifierNames, skill)
          for i, mod in Shared.skpairs(modifiers) do
return not Shared.tableIsEmpty(mods.matched)
            if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then
end)
              result = true
              break
return POIList
            end
          end
          return result
        end)
  return petList
end
end


function p.getUpgradesWithModifier(modifiers, skill, getOpposites)
function p.getCartoMasteryBonusesWithModifier(modifierNames, skill)
  if type(modifiers) == 'string' then
local bonusList = Cartography.getMasteryBonuses(
    modifiers = {modifiers}
function(bonus)
  end
if bonus.modifiers == nil or Shared.tableIsEmpty(bonus.modifiers) then
  local upgradeList = Shop.getPurchases(
return false
        function(purchase)
end
          local result = false
local mods = Modifiers.getMatchingModifiers(bonus.modifiers, modifierNames, skill)
          for i, mod in Shared.skpairs(modifiers) do
return not Shared.tableIsEmpty(mods.matched)
            if p.getModifierValue(purchase.contains.modifiers, mod, skill, getOpposites) ~= 0 then
end)
              result = true
 
              break
return bonusList
            end
          end
          return result
        end)
return upgradeList
end
end


function p._getModifierTable(modifiers, skill, columnName, getOpposites)
function p._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods, forceMagnitudeSort)
  local modifierNames = {}
local modifierByType = {
  if type(modifiers) == 'string' then
["id"] = {},
    modifiers = {modifiers}
["alias"] = {}
  end
}
  for i, modifier in pairs(modifiers) do
if type(modifiers) == 'string' then
    if getOpposites then
modifiers = {modifiers}
      table.insert(modifierNames, 'increased'..modifier)
end
      table.insert(modifierNames, 'decreased'..modifier)
for i, modifier in pairs(modifiers) do
    else
local isNamespaced = string.find(modifier, ':') ~= nil
      table.insert(modifierNames, modifier)
if isNamespaced then
    end
-- Assume 'modifier' contains a modifier ID
  end
table.insert(modifierByType.id, modifier)
else
-- Assume 'modifier' contains a modifier alias
if getOpposites then
local incModAlias, decModAlias = 'increased' .. modifier, 'decreased' .. modifier
local incMod, decMod = Modifiers.getModifierByAlias(incModAlias), Modifiers.getModifierByAlias(decModAlias)
-- If neither alias resolves to a modifier, then let user know this is invalid
if incMod == nil and decMod == nil then
error('No such modifier alias: ' .. modifier, 2)
end
-- Don't include increased or decreased variants if they don't resolve to a modifier,
-- as doing so will generate an error about an invalid alias later
if incMod ~= nil then
table.insert(modifierByType.alias, incModAlias)
end
if decMod ~= nil then
table.insert(modifierByType.alias, decModAlias)
end
else
table.insert(modifierByType.alias, modifier)
end
end
end
local modifierCriteria = Modifiers.getMatchCriteriaFromIDs(modifierByType.id, modifierByType.alias)
 
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 matchedMods = Modifiers.getMatchingModifiers(modifiers, modifierCriteria, skill)
local mainModText = Modifiers.getModifiersText(matchedMods.matched, true, false, maxOtherMods)
local otherModText = Modifiers.getModifiersText(matchedMods.unmatched, true, false, maxOtherMods)
return mainModText, otherModText, matchedMods
end
 
local tableArray = {}
--Going through each type of thing to add to the array
 
local itemList = p.getItemsWithModifier(modifierCriteria, skill)
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 objMods = nil
row.modifierText, row.otherModifiers, objMods = getModText(item.modifiers)
row.val = Modifiers.getModifierValue(objMods.matched)
 
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
hasOtherModifiers = true
end
 
table.insert(tableArray, row)
end
 
local petList = p.getPetsWithModifier(modifierCriteria, skill)
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 objMods = nil
row.modifierText, row.otherModifiers, objMods = getModText(pet.modifiers)
row.val = Modifiers.getModifierValue(objMods.matched)
 
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
hasOtherModifiers = true
end
 
table.insert(tableArray, row)
end
 
local obstList = p.getObstaclesWithModifier(modifierCriteria, skill)
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 objMods = nil
row.modifierText, row.otherModifiers, objMods = getModText(obst.modifiers)
row.val = Modifiers.getModifierValue(objMods.matched)
 
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
hasOtherModifiers = true
end
 
table.insert(tableArray, row)
end
 
local pillarList = p.getPillarsWithModifier(modifierCriteria, skill)
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 hasOtherModifiers = false
local objMods = nil
row.modifierText, row.otherModifiers, objMods = getModText(pillar.modifiers)
row.val = Modifiers.getModifierValue(objMods.matched)


  if skill == '' then
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
    skill = nil
hasOtherModifiers = true
  elseif type(skill) == 'string' then
end
    skill = Constants.getSkillID(skill)
  end


  local getModText =
table.insert(tableArray, row)
    function(modifiers)
end
      local modTextArray = {}
      local mainModText = {}
      for modName, modValue in Shared.skpairs(modifiers) do
        if Shared.contains(modifierNames, modName) then
          if type(modValue) == 'table' then
            for j, subVal in Shared.skpairs(modValue) do
              if subVal[1] == skill then
                table.insert(mainModText, Constants._getModifierText(modName, subVal))
              else
                table.insert(modTextArray, Constants._getModifierText(modName, subVal))
              end
            end
          else
            table.insert(mainModText, Constants._getModifierText(modName, modValue))
          end
        else
          table.insert(modTextArray, Constants._getModifierText(modName, modValue))
        end
      end


      return table.concat(mainModText, '<br/>'), table.concat(modTextArray, '<br/>')
local prayerList = p.getPrayersWithModifier(modifierCriteria, skill)
    end
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 tableArray = {}
local objMods = nil
  --Going through each type of thing to add to the array
row.modifierText, row.otherModifiers, objMods = getModText(prayer.modifiers)
  local itemList = p.getItemsWithModifier(modifiers, skill, getOpposites)
row.val = Modifiers.getModifierValue(objMods.matched)
  for i, item in Shared.skpairs(itemList) do
    local row = {}
    row.name = item.name
    row.icon = Icons.Icon({item.name, type='item'})
    row.type = 'Item'
    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 not Shared.tableIsEmpty(objMods.unmatched) then
hasOtherModifiers = true
end


    if string.len(row.otherModifiers) > 0 then
table.insert(tableArray, row)
      hasOtherModifiers = true
end
    end


    table.insert(tableArray, row)
local upgradeList = p.getUpgradesWithModifier(modifierCriteria, skill)
  end
for i, upgrade in ipairs(upgradeList) do
  local petList = p.getPetsWithModifier(modifiers, skill, getOpposites)
local row = {}
  for i, pet in Shared.skpairs(petList) do
row.name = Shop._getPurchaseName(upgrade)
    local row = {}
row.icon = Icons.Icon({row.name, type='upgrade'})
    row.name = pet.name
row.expIcon = Icons.getExpansionIcon(upgrade.id)
    row.icon = Icons.Icon({pet.name, type='pet'})
row.type = '[[Shop|Upgrade]]'
    row.type = '[[Pets|Pet]]'
row.typeText = 'Upgrade'
    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)
local objMods = nil
row.modifierText, row.otherModifiers, objMods = getModText(upgrade.contains.modifiers)
row.val = Modifiers.getModifierValue(objMods.matched)


    if string.len(row.otherModifiers) > 0 then
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
      hasOtherModifiers = true
hasOtherModifiers = true
    end
end


    table.insert(tableArray, row)
table.insert(tableArray, row)
  end
end
  local obstList = p.getObstaclesWithModifier(modifiers, skill, getOpposites)
  for i, obst in Shared.skpairs(obstList) do
    local row = {}
    row.name = obst.name
    row.icon = Icons.Icon({'Agility', obst.name, type='skill'})
    row.type = '[[Agility#Obstacles|Agility Obstacle]]'
    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)
local constellationList = p.getConstellationsWithModifier(modifierCriteria, skill)
for i, cons in ipairs(constellationList) do
local modListByType = Skills._getConstellationModifiers(cons)
local modList = {}
-- Combine all mods into single list
for modType, modsForType in pairs(modListByType) do
for _, mod in ipairs(modsForType) do
table.insert(modList, mod)
end
end


    if string.len(row.otherModifiers) > 0 then
local row = {}
      hasOtherModifiers = true
row.name = cons.name
    end
row.icon = Icons.Icon({cons.name, type='constellation'})
row.expIcon = Icons.getExpansionIcon(cons.id)
row.type = '[[Astrology#Constellations|Constellation]]'
row.typeText = 'Constellation'


    table.insert(tableArray, row)
local objMods = nil
  end
row.modifierText, row.otherModifiers, objMods = getModText(modList)
  local upgradeList = p.getUpgradesWithModifier(modifiers, skill, getOpposites)
row.val = Modifiers.getModifierValue(objMods.matched)
  for i, upgrade in Shared.skpairs(upgradeList) do
    local row = {}
    row.name = upgrade.name
    row.icon = Icons.Icon({upgrade.name, type='upgrade'})
    row.type = '[[Shop|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 not Shared.tableIsEmpty(objMods.unmatched) then
hasOtherModifiers = true
end


    if string.len(row.otherModifiers) > 0 then
table.insert(tableArray, row)
      hasOtherModifiers = true
end
    end
local POIList = p.getPOIsWithModifier(modifierCriteria, skill)
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'


    table.insert(tableArray, row)
local objMods = nil
  end
row.modifierText, row.otherModifiers, objMods = getModText(POI.activeModifiers)
row.val = Modifiers.getModifierValue(objMods.matched)


  local result = '{| class="wikitable sortable stickyHeader"'
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
  result = result..'\r\n|- class="headerRow-0"'
hasOtherModifiers = true
  result = result..'\r\n!Source!!Type!!'..columnName
end
  if hasOtherModifiers then result = result..'!!Other Modifiers' end


  table.sort(tableArray, function(a, b)
table.insert(tableArray, row)
                          if a.val ~= b.val then
end
                            return a.val > b.val
                          elseif a.name ~= b.name then
local cartoBonusList = p.getCartoMasteryBonusesWithModifier(modifierCriteria, skill)
                            return a.name < b.name
for i, bonus in ipairs(cartoBonusList) do
                          else
local row = {}
                            return a.type < b.type
row.name = Shared.formatnum(bonus.masteredHexes) .. ' Hexes Mastered'
                          end
row.icon = Icons.Icon({'Cartography', Shared.formatnum(bonus.masteredHexes) .. ' Hexes Mastered', type='skill'})
                        end)
row.expIcon = Icons.getExpansionIcon(bonus.id)
  for i, row in Shared.skpairs(tableArray) do
row.type = Icons.Icon({'Cartography', 'Mastery Bonus', section='Mastery Unlocks', type='skill', noicon=true})
    result = result..'\r\n|-'
row.typeText = 'Mastery Bonus'
    result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon
    result = result..'||'..row.type..'||data-sort-value="'..row.val..'"|'..row.modifierText
    if hasOtherModifiers then
      result = result..'||'..row.otherModifiers
    end
  end


  result = result..'\r\n|}'
local objMods = nil
  return result
row.modifierText, row.otherModifiers, objMods = getModText(bonus.modifiers)
row.val = Modifiers.getModifierValue(objMods.matched)
 
if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) 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 modifier = frame.args ~= nil and frame.args[1] or frame[1]
  local skill = frame.args ~= nil and frame.args.skill or frame.skill
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 columnName = frame.args ~= nil and frame.args[2] or frame[2]
  local getOpposites = frame.args ~= nil and frame.args[3] or frame[3]
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
if Shared.contains(modifier, ',') then
    modifier = Shared.splitString(modifier, ',')
modifier = Shared.splitString(modifier, ',')
  end
end


  getOpposites = getOpposites ~= nil and string.upper(getOpposites) == 'TRUE' or true
if getOpposites ~= nil then
getOpposites = string.upper(getOpposites) ~= 'FALSE'
else
getOpposites = true
end


  return p._getModifierTable(modifier, skill, columnName, getOpposites)
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 17:55, 18 June 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 Modifiers = require('Module:Modifiers')
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.getItemsWithModifier(modifierNames, skill)
	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
			local mods = Modifiers.getMatchingModifiers(item.modifiers, modifierNames, skill)
			return not Shared.tableIsEmpty(mods.matched)
		end)
	return itemList
end

function p.getObstaclesWithModifier(modifierNames, skill)
	local obstList = Agility.getObstacles(
		function(obst)
			if obst.modifiers ~= nil then
				local mods = Modifiers.getMatchingModifiers(obst.modifiers, modifierNames, skill)
				return not Shared.tableIsEmpty(mods.matched)
			end
			return false
		end)
	return obstList
end

function p.getConstellationsWithModifier(modifierNames, skill)
	local consList = Skills.getConstellations(
			function(cons)
				local consMods = Skills._getConstellationModifiers(cons)
				for modType, modsForType in pairs(consMods) do
					local mods = Modifiers.getMatchingModifiers(modsForType, modifierNames, skill)
					if not Shared.tableIsEmpty(mods.matched) then
						return true
					end
				end
				return false
			end)
	return consList
end

function p.getPillarsWithModifier(modifierNames, skill)
	local pillarList = Agility.getPillars(
		function(pillar)
			if pillar.modifiers ~= nil then
				local mods = Modifiers.getMatchingModifiers(pillar.modifiers, modifierNames, skill)
				return not Shared.tableIsEmpty(mods.matched)
			end
			return false
		end)
	return pillarList
end

function p.getPetsWithModifier(modifierNames, skill)
	local petList = Pets.getPets(
		function(pet)
			if pet.modifiers ~= nil then
				local mods = Modifiers.getMatchingModifiers(pet.modifiers, modifierNames, skill)
				return not Shared.tableIsEmpty(mods.matched)
			end
			return false
		end)
	return petList
end

function p.getPrayersWithModifier(modifierNames, skill)
	local prayerList = Prayer.getPrayers(
		function(prayer)
			if prayer.modifiers ~= nil then
				local mods = Modifiers.getMatchingModifiers(prayer.modifiers, modifierNames, skill)
				return not Shared.tableIsEmpty(mods.matched)
			end
			return false
		end)
	return prayerList
end

function p.getUpgradesWithModifier(modifierNames, skill)
	local upgradeList = Shop.getPurchases(
		function(purchase)
			if purchase.category == 'melvorD:GolbinRaid'
				or purchase.contains == nil
				or purchase.contains.modifiers == nil
				or Shared.tableIsEmpty(purchase.contains.modifiers) then
				return false
			end
			local mods = Modifiers.getMatchingModifiers(purchase.contains.modifiers, modifierNames, skill)
			return not Shared.tableIsEmpty(mods.matched)
		end)
	return upgradeList
end

function p.getPOIsWithModifier(modifierNames, skill)
	local POIList = Cartography.getPointsOfInterest(
		function(POI)
			if POI.activeModifiers == nil then
				return false
			end
			local mods = Modifiers.getMatchingModifiers(POI.activeModifiers, modifierNames, skill)
			return not Shared.tableIsEmpty(mods.matched)
		end)
		
	return POIList
end

function p.getCartoMasteryBonusesWithModifier(modifierNames, skill)
	local bonusList = Cartography.getMasteryBonuses(
		function(bonus)
			if bonus.modifiers == nil or Shared.tableIsEmpty(bonus.modifiers) then
				return false
			end
			local mods = Modifiers.getMatchingModifiers(bonus.modifiers, modifierNames, skill)
			return not Shared.tableIsEmpty(mods.matched)
		end)

	return bonusList
end

function p._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods, forceMagnitudeSort)
	local modifierByType = {
		["id"] = {},
		["alias"] = {}
	}
	if type(modifiers) == 'string' then
		modifiers = {modifiers}
	end
	for i, modifier in pairs(modifiers) do
		local isNamespaced = string.find(modifier, ':') ~= nil
		if isNamespaced then
			-- Assume 'modifier' contains a modifier ID
			table.insert(modifierByType.id, modifier)
		else
			-- Assume 'modifier' contains a modifier alias
			if getOpposites then
				local incModAlias, decModAlias = 'increased' .. modifier, 'decreased' .. modifier
				local incMod, decMod = Modifiers.getModifierByAlias(incModAlias), Modifiers.getModifierByAlias(decModAlias)
				-- If neither alias resolves to a modifier, then let user know this is invalid
				if incMod == nil and decMod == nil then
					error('No such modifier alias: ' .. modifier, 2)
				end
				-- Don't include increased or decreased variants if they don't resolve to a modifier,
				-- as doing so will generate an error about an invalid alias later
				if incMod ~= nil then
					table.insert(modifierByType.alias, incModAlias)
				end
				if decMod ~= nil then
					table.insert(modifierByType.alias, decModAlias)
				end
			else
				table.insert(modifierByType.alias, modifier)
			end
		end
	end
	local modifierCriteria = Modifiers.getMatchCriteriaFromIDs(modifierByType.id, modifierByType.alias)

	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 matchedMods = Modifiers.getMatchingModifiers(modifiers, modifierCriteria, skill)
			local mainModText = Modifiers.getModifiersText(matchedMods.matched, true, false, maxOtherMods)
			local otherModText = Modifiers.getModifiersText(matchedMods.unmatched, true, false, maxOtherMods)
			return mainModText, otherModText, matchedMods
		end

	local tableArray = {}
	--Going through each type of thing to add to the array

	local itemList = p.getItemsWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(item.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end

	local petList = p.getPetsWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(pet.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end

	local obstList = p.getObstaclesWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(obst.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end

	local pillarList = p.getPillarsWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(pillar.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end

	local prayerList = p.getPrayersWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(prayer.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end

	local upgradeList = p.getUpgradesWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(upgrade.contains.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end

	local constellationList = p.getConstellationsWithModifier(modifierCriteria, skill)
	for i, cons in ipairs(constellationList) do
		local modListByType = Skills._getConstellationModifiers(cons)
		local modList = {}
		-- Combine all mods into single list
		for modType, modsForType in pairs(modListByType) do
			for _, mod in ipairs(modsForType) do
				table.insert(modList, mod)
			end
		end

		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'

		local objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(modList)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end
	
	local POIList = p.getPOIsWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(POI.activeModifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) then
			hasOtherModifiers = true
		end

		table.insert(tableArray, row)
	end
	
	local cartoBonusList = p.getCartoMasteryBonusesWithModifier(modifierCriteria, skill)
	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 objMods = nil
		row.modifierText, row.otherModifiers, objMods = getModText(bonus.modifiers)
		row.val = Modifiers.getModifierValue(objMods.matched)

		if not hasOtherModifiers and not Shared.tableIsEmpty(objMods.unmatched) 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