Module:ItemTables: Difference between revisions

add fire rate and stamina, include inclusions of different stat names, cleanup. WORKS NOW BUT ALSO BREAKS, invocations need to be reversed from internal to friendly, to friendly to internal
capacitor move slow
 
(116 intermediate revisions by 10 users not shown)
Line 1: Line 1:
local p = {};
local p = {};
local data = mw.loadJsonData("Data:ItemData.json")
local get_cost = require("Module:ItemData")._get_cost
local get_cost = require("Module:ItemData")._get_cost
local get_type = require("Module:ItemData")._get_type
local get_type = require("Module:ItemData")._get_type
local commas = require("Module:Addcommas")
local GameData = require("Module:GameData")


-- Adds commas delimiter to the thousands place
-- Translate internal attribute names to plain text for module calls
function Format(amount)
local friendly_to_internal = {}
    local formatted = amount
friendly_to_internal["ammo"] = { "BonusClipSizePercent", "BonusClipSize", "ActiveReloadPercent" }
    while true do
friendly_to_internal["weapon damage"] = { "BaseAttackDamagePercent", "CloseRangeBonusWeaponPower", "LongRangeBonusWeaponPower", "AttackDamageWhenShielded" }
        formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
friendly_to_internal["spirit damage"] = { "ProcBonusMagicDamage" }
        if (k == 0) then
friendly_to_internal["bullet shield health"] = { "BulletShieldMaxHealth", "BulletShieldOnCast", "SaviorBulletShieldHealth", "FlyingBulletShield", "VexBarrierBulletMaxHealth" }
            break
friendly_to_internal["spirit shield health"] = { "TechShieldMaxHealth", "TechShieldOnCast", "SaviorMagicShieldHealth", "FlyingTechShield", "VexBarrierTechMaxHealth" }
        end
friendly_to_internal["barrier"] = { "CombatBarrier", "GuardianWardCombatBarrier", "VexBarrierCombatBarrier" }
     end
friendly_to_internal["bullet resist"] = { "BulletResist", "LocalBulletArmorReduction", "ReturnFireBulletResist", "BulletResistPerStack" }
     return formatted
friendly_to_internal["bullet resist reduction"] = { "BulletArmorReduction", "BulletResistReduction" }
end
friendly_to_internal["spirit resist"] = { "TechResist" }
friendly_to_internal["spirit resist reduction"] = { "TechArmorDamageReduction", "MagicResistReduction" }
friendly_to_internal["bonus health"] = { "BonusHealth" }
friendly_to_internal["bonus base health"] = { "BonusBaseHealth", "MaxHealthLossPercent" }
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", "ActivatedFireRate" }
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", "InvisMoveSpeedMod" }
friendly_to_internal["movement slow"] = { "SlowPercent", "MovementSpeedSlow", "MaxSlowPercent" }
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", "AmbushBonusMeleeDamage" }
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", "FervorStatusResistancePercent" }
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", "AirMoveIncreasePercent" }
friendly_to_internal["damage reduction"] = { "OutgoingDamagePenaltyPercent", "DeathImmunityDamageReduction" }
friendly_to_internal["gravity scale"] = { "GravityScale" }
 
-- Override dead/hidden stats that exist in data but don't actually work in‑game.
-- Keys are item Names, values are tables of { [internalStatName] = replacementValue }
local deadStatOverrides = {
    ["Witchmail"] = { CooldownReduction = "0" },
    ["Refresher"] = { BulletResist = "0", TechResist = "0" },
    ["Glass Cannon"] = { SlowPercent = "0", MovementSpeedSlow = "0" },
     ["Majestic Leap"] = { SlowPercent = "0", MovementSpeedSlow = "0" },
     ["Improved Spirit"] = { BonusSprintSpeed = "0"},
}


--- 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 34: Line 83:
end
end


-- Helper to resolve a stat value, applying any dead‑stat override if defined.
local function resolveStatValue(itemName, t)
    if deadStatOverrides[itemName.Name] and deadStatOverrides[itemName.Name][t] ~= nil then
        return deadStatOverrides[itemName.Name][t]
    end
    return itemName[t]
end


-- local link_patterns = {}
-- Returns a table of internal properties
-- link_patterns["BonusClipSizePercent"] = { "[Aa]mmo" }
-- @function  friendlyNames
-- link_patterns["CloseRangeBonusWeaponPower"] = { "[Cc]lose [Ww]eapon [Dd]amage" }
-- @param      {string}
-- link_patterns["BaseAttackDamagePercent"] = { "[Ww]eapon [Dd]amage" }
-- @return    {table}
-- link_patterns["BulletLifestealPercent"] = { "[Bb]ullet [Ll]ifesteal" }
local function friendlyNames(desc)
-- link_patterns["BonusHealthRegen"] = { "[Hh]ealth [Rr]egen" }
local keysTable = {}
-- link_patterns["BonusHealth"] = { "[Hh]ealth" }
desc = string.lower(desc or "")
-- link_patterns["BonusFireRate"] = { "[Ff]ire [Rr]ate" }
for link, patterns in pairs(friendly_to_internal) do
--
if (desc == link) then keysTable = patterns end  
-- function FriendlyNames(desc)
end
-- for link, patterns in pairs(link_patterns) do
return keysTable
-- for i, v in ipairs(patterns) do
end
--     local a = string.find(desc, v)
-- if a then desc = link break
--     end
--     end
-- end
-- return desc
-- end


local internal_to_friendly = {}
local unitSuffix = {
internal_to_friendly["ammo"] = { "BonusClipSizePercent", "BonusClipSize" }
["%"] = { "BonusClipSizePercent", "ActiveReloadPercent",
internal_to_friendly["weapon damage"] = { "BaseAttackDamagePercent" }
"BaseAttackDamagePercent", "CloseRangeBonusWeaponPower", "LongRangeBonusWeaponPower", "AttackDamageWhenShielded",
internal_to_friendly["health"] = { "BonusHealth" }
"TechResist", "BulletResist",
internal_to_friendly["health regen"] = { "BonusHealthRegen" }
"BonusFireRate", "FireRateWhenShielded", "FervorFireRate", "FireRateBonus", "AmbushBonusFireRate", "ActiveBonusFireRate", "ActivatedFireRate", "FireRateSlow",
internal_to_friendly["fire rate"] = { "BonusFireRate" }
"LocalBulletArmorReduction", "ReturnFireBulletResist", "BulletArmorReduction", "BulletResistReduction", "BulletResistPerStack",
internal_to_friendly["stamina"] = { "Stamina" }
"TechArmorDamageReduction", "MagicResistReduction",
"BonusBulletSpeedPercent",
"BulletLifestealPercent", "ActiveBonusLifesteal", "AbilityLifestealPercentHero",
"HealLifePercentOutOfCombat",
"ReloadSpeedMultipler",
"MeleeDistanceScale", "BonusHeavyMeleeDamage", "BonusMeleeDamagePercent", "MeleeResistPercent", "AmbushBonusMeleeDamage",
"SlideScale",
"StaminaCooldownReduction",
"SlowPercent", "MovementSpeedSlow", "MaxSlowPercent",
"HealAmpReceivePenaltyPercent", "HealAmpRegenPenaltyPercent",
"ImbuedBonusDuration", "NonImbuedBonusDuration", "BonusAbilityDurationPercent",
"ImbuedCooldownReduction", "CooldownReduction",
"ItemCooldownReduction",
"CooldownReductionOnChargedAbilities",
"StatusResistancePercent",
"TechRangeMultiplier", "ImbuedTechRangeMultiplier",
"BonusAttackRangePercent",
"ShreddersTechAmp", "BonusAmpPerHeadshot", "MagicIncreasePerStack",
"TechDamageReduction",
"OutgoingDamagePenaltyPercent", "DeathImmunityDamageReduction", "TechPowerPercent",
"GroundDashReductionPercent", "AirMoveIncreasePercent",
"GravityScale",
"BonusBaseHealth", "MaxHealthLossPercent"
    },
["m/s"] = { "BonusMoveSpeed", "ActiveBonusMoveSpeed", "BonusSprintSpeed", "FervorMovespeed", "InvisMoveSpeedMod" },
[" HP"] = {"CombatBarrier", "GuardianWardCombatBarrier", "VexBarrierCombatBarrier"}
}


function amicableNames(desc)
local function appendSuffix(internal)
local keysTable = {}
local unitName = ""
desc = string.lower(desc or "")
for key, s in pairs(unitSuffix) do
for link, patterns in pairs(internal_to_friendly) 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


if (desc == link) then keysTable = patterns end  
-- 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
return keysTable
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)
p.itemPropTable = function(frame)
local TableValues = {}
local requirements = {}
local requirements = {}
local listofKeysTable = {}
local listofKeysTable = {}
local listofItems = ""
local property = frame:getParent().args[1] or mw.title.getCurrentTitle().text
local property = frame.args[1] or mw.title.getCurrentTitle().text
local copyVar = property
local copyVar = property
listofKeysTable = amicableNames(property) -- need to switch this around from value to key, to key to values
 
-- Get internal keys corresponding to the friendly property name
listofKeysTable = friendlyNames(property)  
 
local filteredData = GameData.get_entities(GameData.Dataset.ITEMS, listofKeysTable)
table.sort(filteredData, defaultSort())
 
local createTable = mw.html.create('table')
local createTable = mw.html.create('table')
:addClass('wikitable sortable')
    :addClass('wikitable sortable item-stat-table')
:tag('caption'):wikitext("List of Items"):done()
    :tag('caption'):done()
 
local createTableRow = mw.html.create('tr')
local soulIcon = frame:expandTemplate{ title = 'Souls' }
createTableRow
 
local createTableHeader = mw.html.create('tr')
createTableHeader
:tag('th'):wikitext('Name'):done()
:tag('th'):wikitext('Name'):done()
:tag('th'):wikitext('Cost'):done()
:tag('th'):wikitext(soulIcon .. 'Cost'):done()
: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(createTableRow)
-- query all items
for internalName, itemName in pairs(data) do --itemName is the key. go through whole table. in this case itemName is the item table


-- Track rowspans for items with multiple stats
local rowspanTracker = {}
for _, itemName in ipairs(filteredData) do
local display = frame:expandTemplate{
local display = frame:expandTemplate{
title = 'ItemIcon',
title = 'ItemIcon',
args = {
args = { itemName["Name"] }
itemName["Name"]
}
}
}
local statCount = 0
for _, t in pairs(listofKeysTable) do
local statVal = resolveStatValue(itemName, t)
if(statVal ~= nil and statVal ~= "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
for _, t in pairs(listofKeysTable) do
-- filter out items that: don't have the property, don't have names (have 'null' names), and aren't disabled --//and itemName["Disabled"] == nil
local statValue = resolveStatValue(itemName, t)
if(itemName[t] ~= nil and itemName["Name"] ~= nil) then -- and itemName["Disabled"] == false --// the data field is not setup to filter this yet
if(statValue ~= nil and statValue ~= "0") then
listofItems = listofItems .. itemName["Name"] .. "<br/>"
-- Handles complex stat values from the JSON data.
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')
local tableData = mw.html.create('tr')
tableData
 
:tag('td'):wikitext(display):done()
if firstStat then
:tag('td'):wikitext(get_cost(itemName["Name"])):done()
tableData
:tag('td'):wikitext(get_type(itemName["Name"])):done()
:tag('td'):attr('rowspan', statCount):wikitext(display):done()
:tag('td'):wikitext("+<b>" .. itemName[t].. "</b> " .. copyVar):done()
:tag('td'):attr('rowspan', statCount):wikitext(
(get_cost(itemName["Name"]) == 9999)
and "Legendary"
or 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)
table.insert(requirements, tableData)
-- TableValues = table.insert(TableValues, itemName["Name"]) -- i may want to make a table first, so it includes weapon and cost to make it sortable.
end
end
end
end
end
for _, row in ipairs(requirements) do
createTableRow:node(row)
end
end
--createTable:node(createTableRow)
return tostring(createTable)
--return listofItems -- this is just a string list, same thing as createTable. delete this when line when finished
--return table.concat(TableValues, ", ")


    for _, row in ipairs(requirements) do
        createTable:node(row)
    end
    local edit_url = mw.uri.fullUrl("Module:ItemTables", { action = "edit" })
    local disclaimer = frame:preprocess(
        '<div class="item-stat-table-disclaimer">'
        .. 'This is a dynamic list '
        .. '<span class="item-stat-table-list-name">"' .. property .. '"</span>. '
        .. '[' .. tostring(edit_url) .. ' Edit contents].'
        .. '</div>'
    )
    return disclaimer .. tostring(createTable)
end
end


return p
return p