Module:ItemTables: Difference between revisionsGive feedback
Jump to navigation
Jump to search
Gammaton32 (talk | contribs) No edit summary |
Instead of removing duplicates, keep all stats and use rowspan to merge the item info across multiple rows. Added FervorMovespeed to move speed |
||
| Line 4: | Line 4: | ||
local get_type = require("Module:ItemData")._get_type | local get_type = require("Module:ItemData")._get_type | ||
local commas = require("Module:Addcommas") | local commas = require("Module:Addcommas") | ||
--- Copies original table with its children as deep as possible. Does not handle metatables. (Copy is needed because Lua passes by reference, not value) | --- Copies original table with its children as deep as possible. Does not handle metatables. (Copy is needed because Lua passes by reference, not value) | ||
| Line 73: | Line 46: | ||
friendly_to_internal["bullet lifesteal"] = { "BulletLifestealPercent", "ActiveBonusLifesteal" } | friendly_to_internal["bullet lifesteal"] = { "BulletLifestealPercent", "ActiveBonusLifesteal" } | ||
friendly_to_internal["spirit lifesteal"] = { "AbilityLifestealPercentHero" } | friendly_to_internal["spirit lifesteal"] = { "AbilityLifestealPercentHero" } | ||
friendly_to_internal["move speed"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed" } | friendly_to_internal["move speed"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed", "FervorMovespeed"} | ||
friendly_to_internal["sprint speed"] = { "BonusSprintSpeed" } | friendly_to_internal["sprint speed"] = { "BonusSprintSpeed" } | ||
friendly_to_internal["movement slow"] = { "SlowPercent", "MovementSpeedSlow" } | friendly_to_internal["movement slow"] = { "SlowPercent", "MovementSpeedSlow" } | ||
| Line 138: | Line 111: | ||
"GroundDashReductionPercent" | "GroundDashReductionPercent" | ||
}, | }, | ||
["m/s"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed", "BonusSprintSpeed" }, | ["m/s"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed", "BonusSprintSpeed", "FervorMovespeed"}, | ||
[" HP"] = {"CombatBarrier", "GuardianWardCombatBarrier", "VexBarrierCombatBarrier"} | [" HP"] = {"CombatBarrier", "GuardianWardCombatBarrier", "VexBarrierCombatBarrier"} | ||
} | } | ||
| Line 172: | Line 145: | ||
-- @return {string} The formatted wikitext string. | -- @return {string} The formatted wikitext string. | ||
local function signPrefix(value) | local function signPrefix(value) | ||
if value == nil then | if value == nil then | ||
return "" | return "" | ||
end | end | ||
value = tostring(value) | value = tostring(value) | ||
value = value:gsub("m", "") | value = value:gsub("m", "") | ||
value = tonumber(value) | value = tonumber(value) | ||
if not value then | if not value then | ||
return "" | return "" | ||
end | end | ||
if (value >= 0) then | if (value >= 0) then | ||
return "+<b>" .. value .. "</b>" | return "+<b>" .. value .. "</b>" | ||
| Line 207: | Line 169: | ||
local requirements = {} | local requirements = {} | ||
local listofKeysTable = {} | local listofKeysTable = {} | ||
local property = frame:getParent().args[1] or mw.title.getCurrentTitle().text | local property = frame:getParent().args[1] or mw.title.getCurrentTitle().text | ||
local copyVar = property | local copyVar = property | ||
-- Get internal keys corresponding to the friendly property name | |||
listofKeysTable = friendlyNames(property) | listofKeysTable = friendlyNames(property) | ||
local filteredData = {} | local filteredData = {} | ||
for _, v in pairs(data) do | |||
for _, t in ipairs(listofKeysTable) do | |||
if v[t] ~= nil and v["IsDisabled"] == false and v["Name"] ~= nil and v[t] ~= "0" and v[t] ~= "0m" and v[t] ~= "" then | |||
table.insert(filteredData, v) | |||
break | |||
end | |||
end | |||
end | |||
table.sort(filteredData, defaultSort()) | table.sort(filteredData, defaultSort()) | ||
local createTable = mw.html.create('table') | local createTable = mw.html.create('table') | ||
:addClass('wikitable sortable') | |||
:tag('caption'):wikitext("List of items"):done() | |||
local soulIcon = frame:expandTemplate{ title = 'Souls' } | local soulIcon = frame:expandTemplate{ title = 'Souls' } | ||
local createTableHeader = mw.html.create('tr') | local createTableHeader = mw.html.create('tr') | ||
createTableHeader | createTableHeader | ||
| Line 227: | Line 199: | ||
:tag('th'):wikitext('Category'):done() | :tag('th'):wikitext('Category'):done() | ||
:tag('th'):wikitext('Stat change'):done() | :tag('th'):wikitext('Stat change'):done() | ||
createTable:node(createTableHeader) | createTable:node(createTableHeader) | ||
-- | -- Track rowspans for items with multiple stats | ||
for | local rowspanTracker = {} | ||
for _, itemName in ipairs(filteredData) do | |||
local display = frame:expandTemplate{ | |||
title = 'ItemIcon', | |||
args = { itemName["Name"] } | |||
} | |||
local statCount = 0 | |||
for _, t in pairs(listofKeysTable) do | |||
if(itemName[t] ~= nil and itemName[t] ~= "0" ) then | if(itemName[t] ~= nil and itemName[t] ~= "0") then | ||
statCount = statCount + 1 | |||
end | |||
end | |||
if statCount == 0 then statCount = 1 end | |||
rowspanTracker[itemName["Name"]] = statCount | |||
local firstStat = true | |||
for _, t in pairs(listofKeysTable) do | |||
if(itemName[t] ~= nil and itemName[t] ~= "0") then | |||
-- Handles complex stat values from the JSON data. | -- Handles complex stat values from the JSON data. | ||
-- A stat can be a simple value (e.g., "BonusMoveSpeed": "1") or a table containing | -- A stat can be a simple value (e.g., "BonusMoveSpeed": "1") or a table containing | ||
-- scaling info (e.g., "BonusFireRate": { "Value": 10, "Scale": ... }). | -- scaling info (e.g., "BonusFireRate": { "Value": 10, "Scale": ... }). | ||
-- This code checks if the stat is a table and extracts the base numerical value from its 'Value' key. | -- This code checks if the stat is a table and extracts the base numerical value from its 'Value' key. | ||
local statValue = itemName[t] | |||
if type(statValue) == 'table' and statValue.Value ~= nil then | |||
statValue = statValue.Value | |||
end | |||
copyVar = copyVar:gsub("^%l", string.upper) | |||
local categoryDisplay | |||
if itemName["Slot"] == "Armor" then | |||
categoryDisplay = frame:expandTemplate{ | |||
title = 'ItemType', | |||
args = { "Armor", "Vitality" } | |||
} | |||
elseif itemName["Slot"] == "Tech" then | |||
categoryDisplay = frame:expandTemplate{ | |||
title = 'ItemType', | |||
args = { "Tech", "Spirit" } | |||
} | |||
else | |||
categoryDisplay = frame:expandTemplate{ | |||
title = 'ItemType', | |||
args = { "Weapon", "Weapon" } | |||
} | |||
end | |||
local tableData = mw.html.create('tr') | |||
if firstStat then | |||
tableData | tableData | ||
:tag('td'):wikitext(display):done() | :tag('td'):attr('rowspan', statCount):wikitext(display):done() | ||
:tag('td'):wikitext(commas._add(get_cost(itemName["Name"]))):done() | :tag('td'):attr('rowspan', statCount):wikitext(commas._add(get_cost(itemName["Name"]))):done() | ||
:tag('td'): | :tag('td'):attr('rowspan', statCount):wikitext(categoryDisplay):done() | ||
firstStat = false | |||
end | end | ||
tableData:tag('td'):wikitext(signPrefix(statValue) .. appendSuffix(t) .. " " .. copyVar):done() | |||
table.insert(requirements, tableData) | |||
end | end | ||
end | end | ||
end | |||
for _, row in ipairs(requirements) do | for _, row in ipairs(requirements) do | ||
createTable:node(row) | createTable:node(row) | ||
end | end | ||
return tostring(createTable) | return tostring(createTable) | ||
end | end | ||
return p | return p | ||
Revision as of 17:17, 18 January 2026
Module used to create item tables with Template:Item stat table.
To add a new stat, add the name of the stat as "friendly to internal", followed by the property as listed on Data:ItemData.json, as well as adding the property to the "local unitSuffix" list corresponding to its suffix.
local p = {};
local data = mw.loadJsonData("Data:ItemData.json")
local get_cost = require("Module:ItemData")._get_cost
local get_type = require("Module:ItemData")._get_type
local commas = require("Module:Addcommas")
--- Copies original table with its children as deep as possible. Does not handle metatables. (Copy is needed because Lua passes by reference, not value)
-- @function Deepcopy
-- @param {n-D table}
-- @return {n-D table}
function Deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[Deepcopy(orig_key)] = Deepcopy(orig_value)
end
else
copy = orig
end
return copy
end
local friendly_to_internal = {}
friendly_to_internal["ammo"] = { "BonusClipSizePercent", "BonusClipSize", "ActiveReloadPercent" }
friendly_to_internal["weapon damage"] = { "BaseAttackDamagePercent", "CloseRangeBonusWeaponPower", "LongRangeBonusWeaponPower", "AttackDamageWhenShielded" }
friendly_to_internal["spirit damage"] = { "ProcBonusMagicDamage" }
friendly_to_internal["bullet shield health"] = { "BulletShieldMaxHealth", "BulletShieldOnCast", "SaviorBulletShieldHealth", "FlyingBulletShield", "VexBarrierBulletMaxHealth" }
friendly_to_internal["spirit shield health"] = { "TechShieldMaxHealth", "TechShieldOnCast", "SaviorMagicShieldHealth", "FlyingTechShield", "VexBarrierTechMaxHealth" }
friendly_to_internal["barrier"] = { "CombatBarrier", "GuardianWardCombatBarrier", "VexBarrierCombatBarrier" }
friendly_to_internal["bullet resist"] = { "BulletResist", "LocalBulletArmorReduction", "ReturnFireBulletResist", "BulletResistPerStack" }
friendly_to_internal["bullet resist reduction"] = { "BulletArmorReduction", "BulletResistReduction" }
friendly_to_internal["spirit resist"] = { "TechResist" }
friendly_to_internal["spirit resist reduction"] = { "TechArmorDamageReduction", "MagicResistReduction" }
friendly_to_internal["bonus health"] = { "BonusHealth" }
friendly_to_internal["health regen"] = { "BonusHealthRegen" }
friendly_to_internal["regeneration"] = { "Regeneration" }
friendly_to_internal["regeneration duration"] = { "RegenerationDuration" }
friendly_to_internal["out of combat health regen"] = { "OutOfCombatHealthRegen" }
friendly_to_internal["max health regen"] = { "HealLifePercentOutOfCombat" }
friendly_to_internal["fire rate"] = { "BonusFireRate", "FireRateWhenShielded", "FireRateBonus", "FervorFireRate", "AmbushBonusFireRate", "ActiveBonusFireRate" }
friendly_to_internal["fire rate slow"] = { "FireRateSlow" }
friendly_to_internal["bullet velocity"] = { "BonusBulletSpeedPercent" }
friendly_to_internal["spirit power"] = { "TechPower", "BonusSpirit", "SpiritPower", "BonusSpiritWithMagicShield", "ImbuedTechPower", "AmbushBonusTechPower", "TechPowerPercent" }
friendly_to_internal["bullet lifesteal"] = { "BulletLifestealPercent", "ActiveBonusLifesteal" }
friendly_to_internal["spirit lifesteal"] = { "AbilityLifestealPercentHero" }
friendly_to_internal["move speed"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed", "FervorMovespeed"}
friendly_to_internal["sprint speed"] = { "BonusSprintSpeed" }
friendly_to_internal["movement slow"] = { "SlowPercent", "MovementSpeedSlow" }
friendly_to_internal["reload time"] = { "ReloadSpeedMultipler" }
friendly_to_internal["heavy melee distance"] = { "MeleeDistanceScale" }
friendly_to_internal["bonus heavy damage"] = { "BonusHeavyMeleeDamage" }
friendly_to_internal["bonus melee damage"] = { "BonusMeleeDamagePercent" }
friendly_to_internal["melee resist"] = { "MeleeResistPercent" }
friendly_to_internal["stamina"] = { "Stamina" }
friendly_to_internal["slide distance"] = { "SlideScale" }
friendly_to_internal["stamina recovery"] = { "StaminaCooldownReduction" }
friendly_to_internal["healing reduction"] = { "HealAmpRegenPenaltyPercent" }
friendly_to_internal["ability duration"] = { "ImbuedBonusDuration", "BonusAbilityDurationPercent" }
friendly_to_internal["ability cooldown reduction"] = { "ImbuedCooldownReduction", "CooldownReduction" }
friendly_to_internal["item cooldown reduction"] = { "ItemCooldownReduction" }
friendly_to_internal["charge cooldown reduction"] = { "CooldownReductionOnChargedAbilities" }
friendly_to_internal["debuff resist"] = { "StatusResistancePercent" }
friendly_to_internal["ability range"] = { "TechRangeMultiplier", "ImbuedTechRangeMultiplier"}
friendly_to_internal["falloff range"] = { "BonusAttackRangePercent" }
friendly_to_internal["spirit amp"] = { "ShreddersTechAmp", "MagicIncreasePerStack" }
friendly_to_internal["spirit damage reduction"] = { "TechDamageReduction" }
friendly_to_internal["dash distance"] = { "GroundDashReductionPercent" }
friendly_to_internal["damage reduction"] = { "OutgoingDamagePenaltyPercent", "DeathImmunityDamageReduction" }
-- Returns a table of internal properties
-- @function friendlyNames
-- @param {string}
-- @return {table}
local function friendlyNames(desc)
local keysTable = {}
desc = string.lower(desc or "")
for link, patterns in pairs(friendly_to_internal) do
if (desc == link) then keysTable = patterns end
end
return keysTable
end
local unitSuffix = {
["%"] = { "BonusClipSizePercent", "ActiveReloadPercent",
"BaseAttackDamagePercent", "CloseRangeBonusWeaponPower", "LongRangeBonusWeaponPower", "AttackDamageWhenShielded",
"TechResist", "BulletResist",
"BonusFireRate", "FireRateWhenShielded", "FervorFireRate", "FireRateBonus", "AmbushBonusFireRate", "ActiveBonusFireRate", "FireRateSlow",
"LocalBulletArmorReduction", "ReturnFireBulletResist", "BulletArmorReduction", "BulletResistReduction", "BulletResistPerStack",
"TechArmorDamageReduction", "MagicResistReduction",
"BonusBulletSpeedPercent",
"BulletLifestealPercent", "ActiveBonusLifesteal", "AbilityLifestealPercentHero",
"HealLifePercentOutOfCombat",
"ReloadSpeedMultipler",
"MeleeDistanceScale", "BonusHeavyMeleeDamage", "BonusMeleeDamagePercent", "MeleeResistPercent",
"SlideScale",
"StaminaCooldownReduction",
"SlowPercent", "MovementSpeedSlow",
"HealAmpReceivePenaltyPercent", "HealAmpRegenPenaltyPercent",
"ImbuedBonusDuration", "NonImbuedBonusDuration", "BonusAbilityDurationPercent",
"ImbuedCooldownReduction", "CooldownReduction",
"ItemCooldownReduction",
"CooldownReductionOnChargedAbilities",
"StatusResistancePercent",
"TechRangeMultiplier", "ImbuedTechRangeMultiplier",
"BonusAttackRangePercent",
"ShreddersTechAmp", "BonusAmpPerHeadshot", "MagicIncreasePerStack",
"TechDamageReduction",
"OutgoingDamagePenaltyPercent", "DeathImmunityDamageReduction", "TechPowerPercent",
"GroundDashReductionPercent"
},
["m/s"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed", "BonusSprintSpeed", "FervorMovespeed"},
[" HP"] = {"CombatBarrier", "GuardianWardCombatBarrier", "VexBarrierCombatBarrier"}
}
local function appendSuffix(internal)
local unitName = ""
for key, s in pairs(unitSuffix) do
for _, pattern in pairs(s) do
if (internal == pattern) then return key end
end
end
return unitName
end
local sortByWeapon = {}
sortByWeapon["Weapon"] = 1
sortByWeapon["Armor"] = 10000
sortByWeapon["Tech"] = 100000000
-- Sort the order of items in a preset as it appears when no filtered ordering is applied.
-- Ordered first by slot and cost increasing, then alphabetical within same slot and cost.
function defaultSort()
return function(a, b)
if (get_cost(a["Name"]) == get_cost(b["Name"]) and sortByWeapon[a["Slot"]] == sortByWeapon[b["Slot"]]) then return a["Name"] < b["Name"] end
return get_cost(a["Name"]) * sortByWeapon[a["Slot"]] < get_cost(b["Name"]) * sortByWeapon[b["Slot"]]
end
end
-- Formats a stat value with a leading + or - sign and makes it bold.
-- The function is robust and handles nil, number, string, or table inputs safely.
-- @function signPrefix
-- @param {any} The stat value to format.
-- @return {string} The formatted wikitext string.
local function signPrefix(value)
if value == nil then
return ""
end
value = tostring(value)
value = value:gsub("m", "")
value = tonumber(value)
if not value then
return ""
end
if (value >= 0) then
return "+<b>" .. value .. "</b>"
elseif (value < 0) then
value = math.abs(value)
return "-<b>" .. value .. "</b>"
end
end
-- Outputs a wikitable of items that increase a specified stat. Invoked by {{Item stat table}}
-- @function p.itemPropTable
-- @param {string}
-- @return {string}
p.itemPropTable = function(frame)
local requirements = {}
local listofKeysTable = {}
local property = frame:getParent().args[1] or mw.title.getCurrentTitle().text
local copyVar = property
-- Get internal keys corresponding to the friendly property name
listofKeysTable = friendlyNames(property)
local filteredData = {}
for _, v in pairs(data) do
for _, t in ipairs(listofKeysTable) do
if v[t] ~= nil and v["IsDisabled"] == false and v["Name"] ~= nil and v[t] ~= "0" and v[t] ~= "0m" and v[t] ~= "" then
table.insert(filteredData, v)
break
end
end
end
table.sort(filteredData, defaultSort())
local createTable = mw.html.create('table')
:addClass('wikitable sortable')
:tag('caption'):wikitext("List of items"):done()
local soulIcon = frame:expandTemplate{ title = 'Souls' }
local createTableHeader = mw.html.create('tr')
createTableHeader
:tag('th'):wikitext('Name'):done()
:tag('th'):wikitext(soulIcon .. 'Cost'):done()
:tag('th'):wikitext('Category'):done()
:tag('th'):wikitext('Stat change'):done()
createTable:node(createTableHeader)
-- Track rowspans for items with multiple stats
local rowspanTracker = {}
for _, itemName in ipairs(filteredData) do
local display = frame:expandTemplate{
title = 'ItemIcon',
args = { itemName["Name"] }
}
local statCount = 0
for _, t in pairs(listofKeysTable) do
if(itemName[t] ~= nil and itemName[t] ~= "0") then
statCount = statCount + 1
end
end
if statCount == 0 then statCount = 1 end
rowspanTracker[itemName["Name"]] = statCount
local firstStat = true
for _, t in pairs(listofKeysTable) do
if(itemName[t] ~= nil and itemName[t] ~= "0") then
-- Handles complex stat values from the JSON data.
-- A stat can be a simple value (e.g., "BonusMoveSpeed": "1") or a table containing
-- scaling info (e.g., "BonusFireRate": { "Value": 10, "Scale": ... }).
-- This code checks if the stat is a table and extracts the base numerical value from its 'Value' key.
local statValue = itemName[t]
if type(statValue) == 'table' and statValue.Value ~= nil then
statValue = statValue.Value
end
copyVar = copyVar:gsub("^%l", string.upper)
local categoryDisplay
if itemName["Slot"] == "Armor" then
categoryDisplay = frame:expandTemplate{
title = 'ItemType',
args = { "Armor", "Vitality" }
}
elseif itemName["Slot"] == "Tech" then
categoryDisplay = frame:expandTemplate{
title = 'ItemType',
args = { "Tech", "Spirit" }
}
else
categoryDisplay = frame:expandTemplate{
title = 'ItemType',
args = { "Weapon", "Weapon" }
}
end
local tableData = mw.html.create('tr')
if firstStat then
tableData
:tag('td'):attr('rowspan', statCount):wikitext(display):done()
:tag('td'):attr('rowspan', statCount):wikitext(commas._add(get_cost(itemName["Name"]))):done()
:tag('td'):attr('rowspan', statCount):wikitext(categoryDisplay):done()
firstStat = false
end
tableData:tag('td'):wikitext(signPrefix(statValue) .. appendSuffix(t) .. " " .. copyVar):done()
table.insert(requirements, tableData)
end
end
end
for _, row in ipairs(requirements) do
createTable:node(row)
end
return tostring(createTable)
end
return p