Module:ModifierTables

From Melvor Idle
Revision as of 15:40, 8 December 2021 by Falterfire (talk | contribs) (fixed a potential bug with getModifierTable if no maxOtherMods was sent in)
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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, and Pets 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 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)
          local result = false
          for i, mod in Shared.skpairs(modifiers) do
            if p.getModifierValue(item.modifiers, mod, skill, getOpposites) ~= 0 then
              result = true
              break
            end
          end
          return result
        end)
  return itemList    
end

function p.getObstaclesWithModifier(modifiers, skill, getOpposites)
  if type(modifiers) == 'string' then
    modifiers = {modifiers}
  end
  local obstList = Agility.getObstacles(
        function(obst)
          local result = false
          for i, mod in Shared.skpairs(modifiers) do
            if p.getModifierValue(obst.modifiers, mod, skill, getOpposites) ~= 0 then
              result = true
              break
            end
          end
          return result
        end)
  return obstList
end

function p.getPillarsWithModifier(modifiers, skill, getOpposites)
  if type(modifiers) == 'string' then
    modifiers = {modifiers}
  end
  local pillarList = Agility.getPillars(
        function(pillar)
          local result = false
          for i, mod in Shared.skpairs(modifiers) do
            if p.getModifierValue(pillar.modifiers, mod, skill, getOpposites) ~= 0 then
              result = true
              break
            end
          end
          return result
        end)
  return pillarList
end

function p.getPetsWithModifier(modifiers, skill, getOpposites)
  if type(modifiers) == 'string' then
    modifiers = {modifiers}
  end
  local petList = Pets.getPets(
        function(pet)
          local result = false
          for i, mod in Shared.skpairs(modifiers) do
            if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then
              result = true
              break
            end
          end
          return result
        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)
          local result = false
          for i, mod in Shared.skpairs(modifiers) do
            if p.getModifierValue(purchase.contains.modifiers, mod, skill, getOpposites) ~= 0 then
              result = true
              break
            end
          end
          return result
        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' 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 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 style="float:left;margin-left:0;margin-right:1em" 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 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 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 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 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 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 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