Module:ModifierTables: Difference between revisions

From Melvor Idle
(Allowed for calling multiple modifiers at once and fixed various bugs)
(30 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 Pets right now
--This includes Agility, Equipment, Pets, Prayers, and Constellations right now


local p = {}
local p = {}
Line 8: Line 8:
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')
Line 14: Line 16:
--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
--Sometimes nil modifier sets will get here, which is fine. Just return 0 immediately
  if modifiers == nil then
if modifiers == nil then
    return 0
return 0
  end
end
 
  --Make sure we have the skillID and not the name
  if skill == '' then
    skill = nil
  elseif type(skill) == 'string' then
    skill = Constants.getSkillID(skill)
  end


  --By default, attempt to add the increased and decreased prefixes to the modifier
--Make sure we have the skillID and not the name
  --But if getOpposites is false, only look for an exact match
if skill == '' then
  local increaseMod, decreaseMod = '', ''
skill = nil
  if getOpposites == nil or getOpposites then
elseif type(skill) == 'string' then
    increaseMod = 'increased'..modifier
skill = Constants.getSkillID(skill)
    decreaseMod = 'decreased'..modifier
end
  else
    increaseMod = modifier
  end


  local increaseVal, decreaseVal = 0, 0
--By default, attempt to add the increased and decreased prefixes to the modifier
  if modifiers[increaseMod] ~= nil and modifiers[increaseMod] ~= nil then
--But if getOpposites is false, only look for an exact match
    if type(modifiers[increaseMod]) == 'table' then
local increaseMod, decreaseMod = '', ''
      for i, subVal in Shared.skpairs(modifiers[increaseMod]) do
if getOpposites == nil or getOpposites then
        if subVal[1] == skill then
increaseMod = 'increased'..modifier
          increaseVal = subVal[2]
decreaseMod = 'decreased'..modifier
        end
else
      end
increaseMod = modifier
    else
end
      increaseVal = modifiers[increaseMod]
    end
  end


  if modifiers[decreaseMod] ~= nil and modifiers[decreaseMod] ~= nil then
local increaseVal, decreaseVal = 0, 0
    if type(modifiers[decreaseMod]) == 'table' then
if modifiers[increaseMod] ~= nil and modifiers[increaseMod] ~= nil then
      for i, subVal in Shared.skpairs(modifiers[decreaseMod]) do
if type(modifiers[increaseMod]) == 'table' then
        if subVal[1] == skill then
for i, subVal in Shared.skpairs(modifiers[increaseMod]) do
          decreaseVal = subVal[2]
if subVal[1] == skill then
        end
increaseVal = subVal[2]
      end
elseif skill == nil or skill == '' then
    else
increaseVal = increaseVal + subVal[2]
      decreaseVal = modifiers[decreaseMod]
end
    end
end
  end
else
increaseVal = modifiers[increaseMod]
end
end


  return increaseVal - decreaseVal
if modifiers[decreaseMod] ~= nil and modifiers[decreaseMod] ~= nil then
if type(modifiers[decreaseMod]) == 'table' then
for i, subVal in Shared.skpairs(modifiers[decreaseMod]) do
if subVal[1] == skill then
decreaseVal = subVal[2]
elseif skill == nil or skill == '' then
decreaseVal = decreaseVal + subVal[2]
end
end
else
decreaseVal = modifiers[decreaseMod]
end
end
 
return increaseVal - decreaseVal
end
end


function p.getItemsWithModifier(modifiers, skill, getOpposites)
function p.getItemsWithModifier(modifiers, skill, getOpposites)
  if type(modifiers) == 'string' then
if type(modifiers) == 'string' then
    modifiers = {modifiers}
modifiers = {modifiers}
  end
end
  local itemList = Items.getItems(
local itemList = Items.getItems(
        function(item)
function(item)
          local result = false
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
          for i, mod in Shared.skpairs(modifiers) do
return false
            if p.getModifierValue(item.modifiers, mod, skill, getOpposites) ~= 0 then
end
              result = true
for i, mod in Shared.skpairs(modifiers) do
              break
if p.getModifierValue(item.modifiers, mod, skill, getOpposites) ~= 0 then
            end
return true
          end
end
          return result
end
        end)
return false
  return itemList  
end)
return itemList
end
end


function p.getObstaclesWithModifier(modifiers, skill, getOpposites)
function p.getObstaclesWithModifier(modifiers, skill, getOpposites)
  if type(modifiers) == 'string' then
if type(modifiers) == 'string' then
    modifiers = {modifiers}
modifiers = {modifiers}
  end
end
  local obstList = Agility.getObstacles(
local obstList = Agility.getObstacles(
        function(obst)
function(obst)
          local result = false
for i, mod in Shared.skpairs(modifiers) do
          for i, mod in Shared.skpairs(modifiers) do
if p.getModifierValue(obst.modifiers, mod, skill, getOpposites) ~= 0 then
            if p.getModifierValue(obst.modifiers, mod, skill, getOpposites) ~= 0 then
return true
              result = true
end
              break
end
            end
return false
          end
end)
          return result
return obstList
        end)
end
  return obstList
 
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 Shared.skpairs(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
if type(modifiers) == 'string' then
    modifiers = {modifiers}
modifiers = {modifiers}
  end
end
  local petList = Pets.getPets(
local petList = Pets.getPets(
        function(pet)
function(pet)
          local result = false
for i, mod in Shared.skpairs(modifiers) do
          for i, mod in Shared.skpairs(modifiers) do
if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then
            if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then
return true
              result = true
end
              break
end
            end
return false
          end
end)
          return result
return petList
        end)
end
  return petList
 
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
end


function p.getUpgradesWithModifier(modifiers, skill, getOpposites)
function p.getUpgradesWithModifier(modifiers, skill, getOpposites)
  if type(modifiers) == 'string' then
if type(modifiers) == 'string' then
    modifiers = {modifiers}
modifiers = {modifiers}
  end
end
  local upgradeList = Shop.getPurchases(
local upgradeList = Shop.getPurchases(
        function(purchase)
function(category, purchase)
          local result = false
if category == 'GolbinRaid' then
          for i, mod in Shared.skpairs(modifiers) do
return false
            if p.getModifierValue(purchase.contains.modifiers, mod, skill, getOpposites) ~= 0 then
end
              result = true
for i, mod in Shared.skpairs(modifiers) do
              break
if p.getModifierValue(purchase.contains.modifiers, mod, skill, getOpposites) ~= 0 then
            end
return true
          end
end
          return result
end
        end)
return false
return upgradeList
end)
return upgradeList
end
end


function p._getModifierTable(modifiers, skill, columnName, getOpposites)
function p._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods)
  local modifierNames = {}
local modifierNames = {}
  if type(modifiers) == 'string' then
if type(modifiers) == 'string' then
    modifiers = {modifiers}
modifiers = {modifiers}
  end
end
  for i, modifier in pairs(modifiers) do
for i, modifier in pairs(modifiers) do
    if getOpposites then
if getOpposites then
      table.insert(modifierNames, 'increased'..modifier)
table.insert(modifierNames, 'increased'..modifier)
      table.insert(modifierNames, 'decreased'..modifier)
table.insert(modifierNames, 'decreased'..modifier)
    else
else
      table.insert(modifierNames, modifier)
table.insert(modifierNames, modifier)
    end
end
  end
end
 
local hasOtherModifiers = false
local modifierCount = Shared.tableCount(modifiers)
 
if skill == '' then
skill = nil
elseif type(skill) == 'string' then
skill = Constants.getSkillID(skill)
end
 
local getModText =
function(modifiers)
local modTextArray = { ["visible"] = {}, ["overflow"] = {} }
local otherModCount = 0
local mainModText = {}
for modName, modValue in Shared.skpairs(modifiers) do
if Shared.contains(modifierNames, modName) then
if type(modValue) == 'table' and type(modValue[1]) == 'table' then
for j, subVal in ipairs(modValue) do
if subVal[1] == skill or skill == nil or skill == '' 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
else
table.insert(mainModText, Constants._getModifierText(modName, modValue))
end
else
modValue = (type(modValue) == 'table' and type(modValue[1]) == 'table' and modValue or {modValue})
for j, subValue in ipairs(modValue) do
otherModCount = otherModCount + 1
local key = ((maxOtherMods == nil or otherModCount <= maxOtherMods) and 'visible') or 'overflow'
table.insert(modTextArray[key], Constants._getModifierText(modName, subValue))
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 Shared.skpairs(itemList) do
local row = {}
row.name = item.name
row.icon = Icons.Icon({item.name, type='item'})
row.type = 'Item'
--For equipment, show the slot they go in
if item.validSlots ~= nil then
row.type = row.type..' ('..table.concat(item.validSlots, ', ')..')'
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.type = '[[Pets|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)
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 '..tostring(tonumber(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)


  local hasOtherModifiers = false
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
hasOtherModifiers = true
end


  if skill == '' then
table.insert(tableArray, row)
    skill = nil
end
  elseif type(skill) == 'string' then
    skill = Constants.getSkillID(skill)
  end


  local getModText =  
local pillarList = p.getPillarsWithModifier(modifiers, skill, getOpposites)
    function(modifiers)
for i, pillar in Shared.skpairs(pillarList) do
      local modTextArray = {}
local row = {}
      local mainModText = {}
row.name = pillar.name
      for modName, modValue in Shared.skpairs(modifiers) do
row.icon = Icons.Icon({'Agility', pillar.name, type='skill'})
        if Shared.contains(modifierNames, modName) then
row.type = '[[Agility#Passive Pillars|Agility Pillar]]'
          if type(modValue) == 'table' then
local totalVal = 0
            for j, subVal in Shared.skpairs(modValue) do
for i, mod in pairs(modifiers) do
              if subVal[1] == skill then
totalVal = totalVal + p.getModifierValue(pillar.modifiers, mod, skill, getOpposites)
                table.insert(mainModText, Constants._getModifierText(modName, subVal))
end
              else
row.val = totalVal
                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/>')
row.modifierText, row.otherModifiers = getModText(pillar.modifiers)
    end


  local tableArray = {}
if not hasOtherModifiers and string.len(row.otherModifiers) > 0 then
  --Going through each type of thing to add to the array
hasOtherModifiers = true
  local itemList = p.getItemsWithModifier(modifiers, skill, getOpposites)
end
  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)
table.insert(tableArray, row)
end


    if string.len(row.otherModifiers) > 0 then
local prayerList = p.getPrayersWithModifier(modifiers, skill, getOpposites)
      hasOtherModifiers = true
for i, prayer in ipairs(prayerList) do
    end
local row = {}
row.name = prayer.name
row.icon = Icons.Icon({prayer.name, type='prayer'})
row.type = [[Prayer]]
local totalVal = 0
for i, mod in ipairs(modifiers) do
totalVal = totalVal + p.getModifierValue(prayer.modifiers, mod, skill, getOpposites)
end
row.val = totalVal


    table.insert(tableArray, row)
row.modifierText, row.otherModifiers = getModText(prayer.modifiers)
  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.type = '[[Pets|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


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


    table.insert(tableArray, row)
local upgradeList = p.getUpgradesWithModifier(modifiers, skill, getOpposites)
  end
for i, upgrade in Shared.skpairs(upgradeList) do
  local obstList = p.getObstaclesWithModifier(modifiers, skill, getOpposites)
local row = {}
  for i, obst in Shared.skpairs(obstList) do
row.name = upgrade.name
    local row = {}
row.icon = Icons.Icon({upgrade.name, type='upgrade'})
    row.name = obst.name
row.type = '[[Shop|Upgrade]]'
    row.icon = Icons.Icon({'Agility', obst.name, type='skill'})
local totalVal = 0
    row.type = '[[Agility#Obstacles|Agility Obstacle]]'
for i, mod in pairs(modifiers) do
    local totalVal = 0
totalVal = totalVal + p.getModifierValue(upgrade.contains.modifiers, mod, skill, getOpposites)
    for i, mod in pairs(modifiers) do
end
      totalVal = totalVal + p.getModifierValue(obst.modifiers, mod, skill, getOpposites)
row.val = totalVal
    end
    row.val = totalVal


    row.modifierText, row.otherModifiers = getModText(obst.modifiers)
row.modifierText, row.otherModifiers = getModText(upgrade.contains.modifiers)


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


    table.insert(tableArray, row)
table.insert(tableArray, row)
  end
end
  local upgradeList = p.getUpgradesWithModifier(modifiers, skill, getOpposites)
  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)
local constellationList = p.getConstellationsWithModifier(modifiers, skill, getOpposites)
for i, cons in ipairs(constellationList) do
local row = {}
row.name = cons.name
row.icon = Icons.Icon({cons.name, type='constellation'})
row.type = '[[Astrology#Constellations|Constellation]]'
row.val = 15 -- Assume highest possible, the range is 1 to 15 inclusive


    if string.len(row.otherModifiers) > 0 then
local modList = Skills._buildAstrologyModifierArray(cons, {1, 15}, true, true, true, true)
      hasOtherModifiers = true
row.modifierText, row.otherModifiers = getModText(modList)
    end


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


  local result = '{| class="wikitable sortable stickyHeader"'
table.insert(tableArray, row)
  result = result..'\r\n|- class="headerRow-0"'
end
  result = result..'\r\n!Source!!Type!!'..columnName
  if hasOtherModifiers then result = result..'!!Other Modifiers' end


  table.sort(tableArray, function(a, b)
local result = '{| class="wikitable sortable stickyHeader"'
                          if a.val ~= b.val then
result = result..'\r\n|- class="headerRow-0"'
                            return a.val > b.val
result = result..'\r\n!Source!!Type!!'..columnName
                          elseif a.name ~= b.name then
if hasOtherModifiers and displayOtherMods then result = result..'!!Other Modifiers' end
                            return a.name < b.name
                          else
                            return a.type < b.type
                          end
                        end)
  for i, row in Shared.skpairs(tableArray) do
    result = result..'\r\n|-'
    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|}'
--Sort by value if only one modifier was passed in
  return result
--Otherwise sort alphabetically by type, then name
table.sort(tableArray, function(a, b)
if modifierCount == 1 and a.val ~= b.val then
return a.val > b.val
elseif a.type ~= b.type then
return a.type < b.type
else
return a.name < b.name
end
end)
for i, row in Shared.skpairs(tableArray) do
result = result..'\r\n|-'
result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon
result = result..'||'..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
 
if Shared.contains(modifier, ',') then
modifier = Shared.splitString(modifier, ',')
end


  if Shared.contains(modifier, ',') then
if getOpposites ~= nil then
    modifier = Shared.splitString(modifier, ',')
getOpposites = string.upper(getOpposites) ~= 'FALSE'
  end
else
getOpposites = true
end


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


  return p._getModifierTable(modifier, skill, columnName, getOpposites)
return p._getModifierTable(modifier, skill, columnName, getOpposites, displayOtherMods, maxOtherMods)
end
end


return p
return p

Revision as of 14:07, 18 April 2022

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 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')

--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 == '' then
		skill = nil
	elseif type(skill) == 'string' then
		skill = Constants.getSkillID(skill)
	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 increaseMod, decreaseMod = '', ''
	if getOpposites == nil or getOpposites then
		increaseMod = 'increased'..modifier
		decreaseMod = 'decreased'..modifier
	else
		increaseMod = modifier
	end

	local increaseVal, decreaseVal = 0, 0
	if modifiers[increaseMod] ~= nil and modifiers[increaseMod] ~= nil then
		if type(modifiers[increaseMod]) == 'table' then
			for i, subVal in Shared.skpairs(modifiers[increaseMod]) do
				if subVal[1] == skill then
					increaseVal = subVal[2]
				elseif skill == nil or skill == '' then
					increaseVal = increaseVal + subVal[2]
				end
			end
		else
			increaseVal = modifiers[increaseMod]
		end
	end

	if modifiers[decreaseMod] ~= nil and modifiers[decreaseMod] ~= nil then
		if type(modifiers[decreaseMod]) == 'table' then
			for i, subVal in Shared.skpairs(modifiers[decreaseMod]) do
				if subVal[1] == skill then
					decreaseVal = subVal[2]
				elseif skill == nil or skill == '' then
					decreaseVal = decreaseVal + subVal[2]
				end
			end
		else
			decreaseVal = modifiers[decreaseMod]
		end
	end

	return increaseVal - decreaseVal
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
			end
			for i, mod in Shared.skpairs(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 Shared.skpairs(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 Shared.skpairs(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 Shared.skpairs(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(category, purchase)
			if category == 'GolbinRaid' then
				return false
			end
			for i, mod in Shared.skpairs(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._getModifierTable(modifiers, skill, columnName, getOpposites, displayOtherMods, maxOtherMods)
	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 == '' then
		skill = nil
	elseif type(skill) == 'string' then
		skill = Constants.getSkillID(skill)
	end

	local getModText =
		function(modifiers)
			local modTextArray = { ["visible"] = {}, ["overflow"] = {} }
			local otherModCount = 0
			local mainModText = {}
			for modName, modValue in Shared.skpairs(modifiers) do
				if Shared.contains(modifierNames, modName) then
					if type(modValue) == 'table' and type(modValue[1]) == 'table' then
						for j, subVal in ipairs(modValue) do
							if subVal[1] == skill or skill == nil or skill == '' 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
					else
						table.insert(mainModText, Constants._getModifierText(modName, modValue))
					end
				else
					modValue = (type(modValue) == 'table' and type(modValue[1]) == 'table' and modValue or {modValue})
					for j, subValue in ipairs(modValue) do
						otherModCount = otherModCount + 1
						local key = ((maxOtherMods == nil or otherModCount <= maxOtherMods) and 'visible') or 'overflow'
						table.insert(modTextArray[key], Constants._getModifierText(modName, subValue))
					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 Shared.skpairs(itemList) do
		local row = {}
		row.name = item.name
		row.icon = Icons.Icon({item.name, type='item'})
		row.type = 'Item'
		--For equipment, show the slot they go in
		if item.validSlots ~= nil then
			row.type = row.type..' ('..table.concat(item.validSlots, ', ')..')'
		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.type = '[[Pets|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)
	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 '..tostring(tonumber(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 Shared.skpairs(pillarList) do
		local row = {}
		row.name = pillar.name
		row.icon = Icons.Icon({'Agility', pillar.name, type='skill'})
		row.type = '[[Agility#Passive Pillars|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.type = [[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 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 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 row = {}
		row.name = cons.name
		row.icon = Icons.Icon({cons.name, type='constellation'})
		row.type = '[[Astrology#Constellations|Constellation]]'
		row.val = 15 -- Assume highest possible, the range is 1 to 15 inclusive

		local modList = Skills._buildAstrologyModifierArray(cons, {1, 15}, true, true, true, true)
		row.modifierText, row.otherModifiers = getModText(modList)

		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 and a.val ~= b.val then
				return a.val > b.val
			elseif a.type ~= b.type then
				return a.type < b.type
			else
				return a.name < b.name
			end
		end)
	for i, row in Shared.skpairs(tableArray) do
		result = result..'\r\n|-'
		result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon
		result = result..'||'..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

	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)
end

return p