Module:ModifierTables: Difference between revisions

From Melvor Idle
(getModifierTable: support 'maxOtherMods' argument)
(_getModifierTable: Correct issues with incorrect other modifier count & incorrect number of visible modifiers; change sort order)
Line 206: Line 206:
         if Shared.contains(modifierNames, modName) then
         if Shared.contains(modifierNames, modName) then
           if type(modValue) == 'table' then
           if type(modValue) == 'table' then
             for j, subVal in Shared.skpairs(modValue) do
             for j, subVal in ipairs(modValue) do
               if subVal[1] == skill or skill == nil or skill == '' then
               if subVal[1] == skill or skill == nil or skill == '' then
                 table.insert(mainModText, Constants._getModifierText(modName, subVal))
                 table.insert(mainModText, Constants._getModifierText(modName, subVal))
               else
               else
               otherModCount = otherModCount + 1
               otherModCount = otherModCount + 1
               local key = ((maxOtherMods == nil or otherModCount < maxOtherMods) and 'visible') or 'overflow'
               local key = ((maxOtherMods == nil or otherModCount <= maxOtherMods) and 'visible') or 'overflow'
                table.insert(modTextArray[key], Constants._getModifierText(modName, subVal))
              table.insert(modTextArray[key], Constants._getModifierText(modName, subVal))
               end
               end
             end
             end
Line 219: Line 219:
           end
           end
         else
         else
           otherModCount = otherModCount + 1
           modValue = (type(modValue) == 'table' and modValue or {modValue})
          local key = ((maxOtherMods == nil or otherModCount < maxOtherMods) and 'visible') or 'overflow'
          for j, subValue in ipairs(modValue) do
          table.insert(modTextArray[key], Constants._getModifierText(modName, modValue))
            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
       end
       end
Line 227: Line 230:
  local otherModText = {table.concat(modTextArray['visible'], '<br/>')}
  local otherModText = {table.concat(modTextArray['visible'], '<br/>')}
  local overflowModCount = Shared.tableCount(modTextArray['overflow'])
  local overflowModCount = Shared.tableCount(modTextArray['overflow'])
  mw.logObject(modTextArray['overflow'])
  if overflowModCount > 0 then
  if overflowModCount > 0 then
  -- Number of other modifiers for the object exceed the specified maximum
  -- Number of other modifiers for the object exceed the specified maximum
Line 373: Line 375:


   --Sort by value if only one modifier was passed in
   --Sort by value if only one modifier was passed in
   --Otherwise sort alphabetically by name
   --Otherwise sort alphabetically by type, then name
   if modifierCount == 1 then
   table.sort(tableArray, function(a, b)
    table.sort(tableArray, function(a, b)
                          if modifierCount == 1 and a.val ~= b.val then
                            if a.val ~= b.val then
                            return a.val > b.val
                              return a.val > b.val
                          elseif a.type ~= b.type then
                            elseif a.name ~= b.name then
                             return a.type < b.type
                              return a.name < b.name
                           else
                             else
                            return a.name < b.name
                              return a.type < b.type
                          end
                            end
                        end)
                           end)
  else
    table.sort(tableArray, function(a, b) return a.name < b.name end)
  end
   for i, row in Shared.skpairs(tableArray) do
   for i, row in Shared.skpairs(tableArray) do
     result = result..'\r\n|-'
     result = result..'\r\n|-'

Revision as of 01:30, 29 November 2021

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 otherModText = {table.concat(modTextArray['visible'], '<br/>')}
	  local overflowModCount = Shared.tableCount(modTextArray['overflow'])
	  if overflowModCount > 0 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)

  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