Module:ModifierTables: Difference between revisions

From Melvor Idle
(Fix modifier table, was previously horribly broken)
(for reals fixed the problem with modifier table change)
(2 intermediate revisions by the same user not shown)
Line 283: Line 283:
row.name = item.name
row.name = item.name
row.icon = Icons.Icon({item.name, type='item'})
row.icon = Icons.Icon({item.name, type='item'})
row.expIcon = Icons.getExpansionIcon(item.id)
row.type = 'Item'
row.type = 'Item'
--For equipment, show the slot they go in
--For equipment, show the slot they go in
Line 288: Line 289:
row.type = row.type..' ('..table.concat(Shared.clone(item.validSlots), ', ')..')'
row.type = row.type..' ('..table.concat(Shared.clone(item.validSlots), ', ')..')'
end
end
row.typeText = row.type
local totalVal = 0
local totalVal = 0
for i, mod in pairs(modifiers) do
for i, mod in pairs(modifiers) do
Line 308: Line 310:
row.name = pet.name
row.name = pet.name
row.icon = Icons.Icon({pet.name, type='pet'})
row.icon = Icons.Icon({pet.name, type='pet'})
row.expIcon = Icons.getExpansionIcon(pet.id)
row.type = '[[Pets|Pet]]'
row.type = '[[Pets|Pet]]'
row.typeText = 'Pet'
local totalVal = 0
local totalVal = 0
for i, mod in pairs(modifiers) do
for i, mod in pairs(modifiers) do
Line 329: Line 333:
row.name = obst.name
row.name = obst.name
row.icon = Icons.Icon({'Agility', obst.name, type='skill'})
row.icon = Icons.Icon({'Agility', obst.name, type='skill'})
row.expIcon = Icons.getExpansionIcon(obst.id)
row.type = '[[Agility#Obstacles|Agility Obstacle '..tostring(tonumber(obst.category)+1)..']]'
row.type = '[[Agility#Obstacles|Agility Obstacle '..tostring(tonumber(obst.category)+1)..']]'
row.typeText = 'Agility Obstacle '..tostring(tonumber(obst.category)+1)
local totalVal = 0
local totalVal = 0
for i, mod in pairs(modifiers) do
for i, mod in pairs(modifiers) do
Line 350: Line 356:
row.name = pillar.name
row.name = pillar.name
row.icon = Icons.Icon({'Agility', pillar.name, type='skill'})
row.icon = Icons.Icon({'Agility', pillar.name, type='skill'})
row.expIcon = Icons.getExpansionIcon(pillar.id)
row.type = '[[Agility#Passive Pillars|Agility Pillar]]'
row.type = '[[Agility#Passive Pillars|Agility Pillar]]'
row.typeText = 'Agility Pillar'
local totalVal = 0
local totalVal = 0
for i, mod in pairs(modifiers) do
for i, mod in pairs(modifiers) do
Line 371: Line 379:
row.name = prayer.name
row.name = prayer.name
row.icon = Icons.Icon({prayer.name, type='prayer'})
row.icon = Icons.Icon({prayer.name, type='prayer'})
row.expIcon = Icons.getExpansionIcon(prayer.id)
row.type = [[Prayer]]
row.type = [[Prayer]]
row.typeText = 'Prayer'
local totalVal = 0
local totalVal = 0
for i, mod in ipairs(modifiers) do
for i, mod in ipairs(modifiers) do
Line 392: Line 402:
row.name = Shop._getPurchaseName(upgrade)
row.name = Shop._getPurchaseName(upgrade)
row.icon = Icons.Icon({row.name, type='upgrade'})
row.icon = Icons.Icon({row.name, type='upgrade'})
row.expIcon = Icons.getExpansionIcon(upgrade.id)
row.type = '[[Shop|Upgrade]]'
row.type = '[[Shop|Upgrade]]'
row.typeText = 'Upgrade'
local totalVal = 0
local totalVal = 0
for i, mod in pairs(modifiers) do
for i, mod in pairs(modifiers) do
Line 413: Line 425:
row.name = cons.name
row.name = cons.name
row.icon = Icons.Icon({cons.name, type='constellation'})
row.icon = Icons.Icon({cons.name, type='constellation'})
row.expIcon = Icons.getExpansionIcon(cons.id)
row.type = '[[Astrology#Constellations|Constellation]]'
row.type = '[[Astrology#Constellations|Constellation]]'
row.typeText = 'Constellation'
row.val = 15 -- Assume highest possible, the range is 1 to 15 inclusive
row.val = 15 -- Assume highest possible, the range is 1 to 15 inclusive


Line 445: Line 459:
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon
result = result..'\r\n|data-sort-value="'..row.name..'"|'..row.icon
result = result..'||'..row.type..'||data-sort-value="'..row.val..'"| '..row.modifierText
result = result..'|| data-sort-value="'..row.typeText..'" | '..row.expIcon..row.type..'||data-sort-value="'..row.val..'"| '..row.modifierText
if hasOtherModifiers and displayOtherMods then
if hasOtherModifiers and displayOtherMods then
result = result..'|| '..row.otherModifiers
result = result..'|| '..row.otherModifiers
Line 480: Line 494:


return p._getModifierTable(modifier, skill, columnName, getOpposites, displayOtherMods, maxOtherMods)
return p._getModifierTable(modifier, skill, columnName, getOpposites, displayOtherMods, maxOtherMods)
end
--Function for console testing of modifier tables
function p.getModifierTableTest()
return p.getModifierTable({args = {'MeleeMaxHit', 'GP Boosts'}})
end
end


return p
return p

Revision as of 16:09, 8 November 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 ~= nil then
		if skill == '' then
			skill = nil
		elseif Constants.getSkillID(skill) ~= nil then
			-- skill is a skill name
			skill = Constants.getSkillID(skill)
		elseif Constants.getSkillName(skill) == nil then
			-- skill is neither a skill name or ID
			return 0
		end
	end

	--By default, attempt to add the increased and decreased prefixes to the modifier
	--But if getOpposites is false, only look for an exact match
	
	local mods = {}
	if getOpposites == nil or getOpposites then
		mods.inc = 'increased'..modifier
		mods.dec = 'decreased'..modifier
	else
		mods.inc = modifier
	end
	
	local magnitude = { inc = 0, dec = 0 }
	for modType, modName in pairs(mods) do
		if modifiers[modName] ~= nil then
			local valueArray = nil
            if type(modifiers[modName]) ~= 'table' then
                valueArray = {modifiers[modName]}
            else
                valueArray = modifiers[modName]
            end

            for i, subVal in ipairs(valueArray) do
                if type(subVal) == 'table' then
					if  subVal.skillID ~= nil then
						-- Modifier value is skill specific
						if skill == nil or skill == '' or subVal.skillID == skill then
							magnitude[modType] = magnitude[modType] + subVal.value
						end
					else
						-- Modifier value is a table of two numbers representing a range. Take the largest value
						magnitude[modType] = magnitude[modType] + (subVal[2] or 0)
					end
                else
                    magnitude[modType] = magnitude[modType] + subVal
                end
            end
		end
	end

    return magnitude.inc - magnitude.dec
end

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

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

function p.getConstellationsWithModifier(modifiers, skill, getOpposites)
	if type(modifiers) == 'string' then
		modifiers = {modifiers}
	end
	local consList = Skills.getConstellations(
			function(cons)
				local consMods = Skills._buildAstrologyModifierArray(cons, 1, true, true, true, true)
				for i, modifier in ipairs(modifiers) do
					if p.getModifierValue(consMods, modifier, skill, getOpposites) ~= 0 then
						return true
					end
				end
				return false
			end)
	return consList
end

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

function p.getPetsWithModifier(modifiers, skill, getOpposites)
	if type(modifiers) == 'string' then
		modifiers = {modifiers}
	end
	local petList = Pets.getPets(
		function(pet)
			for i, mod in ipairs(modifiers) do
				if p.getModifierValue(pet.modifiers, mod, skill, getOpposites) ~= 0 then
					return true
				end
			end
			return false
		end)
	return petList
end

function p.getPrayersWithModifier(modifiers, skill, getOpposites)
	if type(modifiers) == 'string' then
		modifiers = {modifiers}
	end
	local prayerList = Prayer.getPrayers(
		function(prayer)
			for i, mod in ipairs(modifiers) do
				if p.getModifierValue(prayer.modifiers, mod, skill, getOpposites) ~= 0 then
					return true
				end
			end
			return false
		end)
	return prayerList
end

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

function p._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 ~= 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 'ERROR: Failed to find a skill ID for ' .. skill .. '[[Category:Pages with script errors]]'
		end
	end

	local getModText =
		function(modifiers)
			local modTextArray = { ["visible"] = {}, ["overflow"] = {} }
			local otherModCount = 0
			local mainModText = {}
			for modName, modValue in Shared.skpairs(modifiers) do
                local includedMod = Shared.contains(modifierNames, modName)
                local valueArray = nil
                if type(modValue) ~= 'table' then
                    valueArray = {modValue}
                else
                    valueArray = modValue
                end

                for j, subVal in ipairs(valueArray) do
                    local includeInMainText = includedMod
                    if type(subVal) == 'table' and subVal.skillID ~= nil then
                        -- Modifier value is skill specific
                        if includeInMainText then
                            -- If the skill doesn't match then don't include in the main text
                            includeInMainText = skill == nil or skill == '' or subVal.skillID == skill
                        end
                        subVal = {subVal}
                    end

                    if includeInMainText then
                        table.insert(mainModText, Constants._getModifierText(modName, subVal))
                    else
                        otherModCount = otherModCount + 1
                        local key = ((maxOtherMods == nil or otherModCount <= maxOtherMods) and 'visible') or 'overflow'
                        table.insert(modTextArray[key], Constants._getModifierText(modName, subVal))
                    end
                end
            end

			local overflowModCount = Shared.tableCount(modTextArray['overflow'])
			if overflowModCount == 1 then
				table.insert(modTextArray['visible'], modTextArray['overflow'][1])
			end
			local otherModText = {table.concat(modTextArray['visible'], '<br/>')}

			if overflowModCount > 1 then
				-- Number of other modifiers for the object exceed the specified maximum
				table.insert(otherModText, '<br/><span class="mw-collapsible mw-collapsed" ')
				table.insert(otherModText, 'data-expandtext="Show ' .. Shared.formatnum(overflowModCount) .. ' more modifiers", data-collapsetext="Hide">')
				table.insert(otherModText, table.concat(modTextArray['overflow'], '<br/>') .. '</span>')
			end
			return table.concat(mainModText, '<br/>'), table.concat(otherModText)
		end

	local tableArray = {}
	--Going through each type of thing to add to the array
	local itemList = p.getItemsWithModifier(modifiers, skill, getOpposites)
	for i, item in ipairs(itemList) do
		local row = {}
		row.name = item.name
		row.icon = Icons.Icon({item.name, type='item'})
		row.expIcon = Icons.getExpansionIcon(item.id)
		row.type = 'Item'
		--For equipment, show the slot they go in
		if item.validSlots ~= nil then
			row.type = row.type..' ('..table.concat(Shared.clone(item.validSlots), ', ')..')'
		end
		row.typeText = row.type
		local totalVal = 0
		for i, mod in pairs(modifiers) do
			totalVal = totalVal + p.getModifierValue(item.modifiers, mod, skill, getOpposites)
		end
		row.val = totalVal

		row.modifierText, row.otherModifiers = getModText(item.modifiers)

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

		table.insert(tableArray, row)
	end

	local petList = p.getPetsWithModifier(modifiers, skill, getOpposites)
	for i, pet in Shared.skpairs(petList) do
		local row = {}
		row.name = pet.name
		row.icon = Icons.Icon({pet.name, type='pet'})
		row.expIcon = Icons.getExpansionIcon(pet.id)
		row.type = '[[Pets|Pet]]'
		row.typeText = 'Pet'
		local totalVal = 0
		for i, mod in pairs(modifiers) do
			totalVal = totalVal + p.getModifierValue(pet.modifiers, mod, skill, getOpposites)
		end
		row.val = totalVal

		row.modifierText, row.otherModifiers = getModText(pet.modifiers)

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

		table.insert(tableArray, row)
	end

	local obstList = p.getObstaclesWithModifier(modifiers, skill, getOpposites)
	for i, obst in Shared.skpairs(obstList) do
		local row = {}
		row.name = obst.name
		row.icon = Icons.Icon({'Agility', obst.name, type='skill'})
		row.expIcon = Icons.getExpansionIcon(obst.id)
		row.type = '[[Agility#Obstacles|Agility Obstacle '..tostring(tonumber(obst.category)+1)..']]'
		row.typeText = '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 ipairs(pillarList) do
		local row = {}
		row.name = pillar.name
		row.icon = Icons.Icon({'Agility', pillar.name, type='skill'})
		row.expIcon = Icons.getExpansionIcon(pillar.id)
		row.type = '[[Agility#Passive Pillars|Agility Pillar]]'
		row.typeText = 'Agility Pillar'
		local totalVal = 0
		for i, mod in pairs(modifiers) do
			totalVal = totalVal + p.getModifierValue(pillar.modifiers, mod, skill, getOpposites)
		end
		row.val = totalVal

		row.modifierText, row.otherModifiers = getModText(pillar.modifiers)

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

		table.insert(tableArray, row)
	end

	local prayerList = p.getPrayersWithModifier(modifiers, skill, getOpposites)
	for i, prayer in ipairs(prayerList) do
		local row = {}
		row.name = prayer.name
		row.icon = Icons.Icon({prayer.name, type='prayer'})
		row.expIcon = Icons.getExpansionIcon(prayer.id)
		row.type = [[Prayer]]
		row.typeText = 'Prayer'
		local totalVal = 0
		for i, mod in ipairs(modifiers) do
			totalVal = totalVal + p.getModifierValue(prayer.modifiers, mod, skill, getOpposites)
		end
		row.val = totalVal

		row.modifierText, row.otherModifiers = getModText(prayer.modifiers)

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

		table.insert(tableArray, row)
	end

	local upgradeList = p.getUpgradesWithModifier(modifiers, skill, getOpposites)
	for i, upgrade in ipairs(upgradeList) do
		local row = {}
		row.name = Shop._getPurchaseName(upgrade)
		row.icon = Icons.Icon({row.name, type='upgrade'})
		row.expIcon = Icons.getExpansionIcon(upgrade.id)
		row.type = '[[Shop|Upgrade]]'
		row.typeText = 'Upgrade'
		local totalVal = 0
		for i, mod in pairs(modifiers) do
			totalVal = totalVal + p.getModifierValue(upgrade.contains.modifiers, mod, skill, getOpposites)
		end
		row.val = totalVal

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

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

		table.insert(tableArray, row)
	end

	local constellationList = p.getConstellationsWithModifier(modifiers, skill, getOpposites)
	for i, cons in ipairs(constellationList) do
		local row = {}
		row.name = cons.name
		row.icon = Icons.Icon({cons.name, type='constellation'})
		row.expIcon = Icons.getExpansionIcon(cons.id)
		row.type = '[[Astrology#Constellations|Constellation]]'
		row.typeText = 'Constellation'
		row.val = 15 -- Assume highest possible, the range is 1 to 15 inclusive

		local modList = Skills._buildAstrologyModifierArray(cons, nil, 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 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

	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

--Function for console testing of modifier tables
function p.getModifierTableTest()
	return p.getModifierTable({args = {'MeleeMaxHit', 'GP Boosts'}})
end

return p