Module:Sandbox: Difference between revisionsGive feedback
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 53: | Line 53: | ||
local function formatNum(amount) | local function formatNum(amount) | ||
return lang:formatNum(toNumber(amount)) | return lang:formatNum(toNumber(amount)) | ||
end | |||
-- Smart stat display formatter | |||
local function formatStat(stat, value) | |||
local icon_map = { | |||
TechPower = "Spirit icon.png", | |||
WeaponPower = "Weapon Icon.png", | |||
TechResist = "Spirit Resist Icon.png", | |||
BulletResist = "Bullet Resist Icon.png", | |||
AbilityCooldown = "Cooldown Icon.png" | |||
} | |||
local suffix_map = { | |||
Percent = "%", | |||
Speed = "m/s", | |||
Range = "m", | |||
Duration = "s", | |||
Cooldown = "s", | |||
Health = " HP", | |||
Stacks = " stacks", | |||
Damage = " Damage", | |||
FireRate = " Fire Rate", | |||
Lifesteal = "% Lifesteal" | |||
} | |||
-- Special cases with custom formatting | |||
if stat == "AmmoPerSoul" then | |||
return string.format("• %d ammo per Soul", value) | |||
elseif stat == "SpiritPowerPerSoul" then | |||
return string.format("• +%d Spirit Power per Soul", value) | |||
elseif stat == "MaxStacks" then | |||
return string.format("• Max %d stacks", value) | |||
end | |||
-- Automatic formatting based on stat name | |||
local display = "• " | |||
local icon = icon_map[stat] | |||
local base_stat = stat:gsub("Bonus", ""):gsub("Percent", "") | |||
-- Add value | |||
display = display .. tostring(value) | |||
-- Add icon if available | |||
if icon then | |||
display = display .. " [[File:" .. icon .. "|20px]]" | |||
end | |||
-- Add appropriate suffix | |||
for pattern, suffix in pairs(suffix_map) do | |||
if stat:find(pattern) then | |||
return display .. suffix | |||
end | |||
end | |||
-- Default case - show stat name if no special formatting | |||
return display .. " " .. base_stat | |||
end | end | ||
| Line 58: | Line 114: | ||
p.generate_infobox = function(frame) | p.generate_infobox = function(frame) | ||
local item_name = frame.args[1] | local item_name = frame.args[1] | ||
local manual_component = frame.args.component | local manual_component = frame.args.component | ||
if not item_name or item_name == "" then | if not item_name or item_name == "" then | ||
| Line 88: | Line 144: | ||
local color = colors[item_type] or colors.Weapon | local color = colors[item_type] or colors.Weapon | ||
-- Process components | -- Process components | ||
local components_text = "" | local components_text = "" | ||
if item.Components and type(item.Components) == "table" then | if item.Components and type(item.Components) == "table" then | ||
| Line 109: | Line 165: | ||
end | end | ||
-- Process parent items | -- Process parent items | ||
local parent_items_text = "" | local parent_items_text = "" | ||
local parent_items = {} | local parent_items = {} | ||
if manual_component and manual_component ~= "" then | if manual_component and manual_component ~= "" then | ||
table.insert(parent_items, manual_component) | table.insert(parent_items, manual_component) | ||
else | else | ||
parent_items = get_parent_items(item.Name) | parent_items = get_parent_items(item.Name) | ||
end | end | ||
| Line 135: | Line 189: | ||
end | end | ||
-- Process stats | -- Process ALL stats dynamically | ||
local stats = {} | local stats = {} | ||
local | local skip_stats = { | ||
Name = true, Description = true, Cost = true, Tier = true, | |||
Activation = true, Slot = true, Components = true, | |||
TargetTypes = true, ShopFilters = true, IsDisabled = true, | |||
AbilityCastDelay = true, AbilityChannelTime = true, | |||
AbilityPostCastDuration = true, AbilityCharges = true, | |||
AbilityCooldownBetweenCharge = true, ChannelMoveSpeed = true, | |||
AbilityResourceCost = true | |||
} | } | ||
for stat, | for stat, value in pairs(item) do | ||
if not skip_stats[stat] and value ~= nil and value ~= "0" and value ~= 0 then | |||
local num_value = toNumber(value) | |||
local | local formatted = formatStat(stat, num_value) | ||
if formatted then | |||
table.insert(stats, formatted) | |||
end | |||
if | |||
end | end | ||
end | end | ||
-- Sort stats alphabetically for consistent display | |||
table.sort(stats) | |||
-- Calculate shop bonus | -- Calculate shop bonus | ||
Revision as of 19:02, 1 April 2025
Documentation for this module may be created at Module:Sandbox/doc
local p = {};
local data = mw.loadJsonData("Data:ItemData.json")
-- Utility function to safely convert to number
local function toNumber(value)
if type(value) == "number" then
return value
elseif type(value) == "string" then
local cleaned = value:gsub("[^%d.-]", "")
return tonumber(cleaned) or 0
end
return 0
end
-- Normalize strings for comparison
local function normalize(str)
return mw.ustring.lower(mw.text.trim(str or ""))
end
-- Safe item access
local function get_json_item(name)
if not name or type(name) ~= 'string' then return nil end
local targetName = normalize(name)
for _, item in pairs(data) do
if item.Name and normalize(item.Name) == targetName then
if item.IsDisabled ~= true then
return item
end
end
end
return nil
end
-- Find items that use this item as a component
local function get_parent_items(item_name)
local parents = {}
for _, item in pairs(data) do
if item.Components and type(item.Components) == "table" then
for _, comp_id in ipairs(item.Components) do
local component = data[comp_id]
if component and component.Name == item_name then
table.insert(parents, item.Name)
end
end
end
end
return parents
end
-- Format numbers with localization
local lang = mw.language.getContentLanguage()
local function formatNum(amount)
return lang:formatNum(toNumber(amount))
end
-- Smart stat display formatter
local function formatStat(stat, value)
local icon_map = {
TechPower = "Spirit icon.png",
WeaponPower = "Weapon Icon.png",
TechResist = "Spirit Resist Icon.png",
BulletResist = "Bullet Resist Icon.png",
AbilityCooldown = "Cooldown Icon.png"
}
local suffix_map = {
Percent = "%",
Speed = "m/s",
Range = "m",
Duration = "s",
Cooldown = "s",
Health = " HP",
Stacks = " stacks",
Damage = " Damage",
FireRate = " Fire Rate",
Lifesteal = "% Lifesteal"
}
-- Special cases with custom formatting
if stat == "AmmoPerSoul" then
return string.format("• %d ammo per Soul", value)
elseif stat == "SpiritPowerPerSoul" then
return string.format("• +%d Spirit Power per Soul", value)
elseif stat == "MaxStacks" then
return string.format("• Max %d stacks", value)
end
-- Automatic formatting based on stat name
local display = "• "
local icon = icon_map[stat]
local base_stat = stat:gsub("Bonus", ""):gsub("Percent", "")
-- Add value
display = display .. tostring(value)
-- Add icon if available
if icon then
display = display .. " [[File:" .. icon .. "|20px]]"
end
-- Add appropriate suffix
for pattern, suffix in pairs(suffix_map) do
if stat:find(pattern) then
return display .. suffix
end
end
-- Default case - show stat name if no special formatting
return display .. " " .. base_stat
end
-- Main function with improved error handling
p.generate_infobox = function(frame)
local item_name = frame.args[1]
local manual_component = frame.args.component
if not item_name or item_name == "" then
return frame:preprocess("{{Error|No item name specified}}")
end
local item = get_json_item(item_name)
if not item then
return frame:preprocess("{{Error|Item not found: " .. item_name .. "}}")
end
-- Determine item type
local item_type = "Weapon" -- default
if item.Slot then
local slot = normalize(item.Slot)
if slot == "armor" then
item_type = "Vitality"
elseif slot == "tech" then
item_type = "Spirit"
end
end
-- Color definitions
local colors = {
Weapon = {bg = "#80550F", header = "#C97A03", border = "#C97A03"},
Vitality = {bg = "#4D7214", header = "#659818", border = "#659818"},
Spirit = {bg = "#623585", header = "#8B56B4", border = "#8B56B4"}
}
local color = colors[item_type] or colors.Weapon
-- Process components
local components_text = ""
if item.Components and type(item.Components) == "table" then
local valid_components = {}
for _, comp_id in ipairs(item.Components) do
local component = data[comp_id]
if component and component.Name then
table.insert(valid_components, "{{ItemIcon|" .. component.Name .. "}}")
end
end
if #valid_components > 0 then
components_text = string.format(
'|-\n! colspan="4" style="text-align:left; padding:0 12px; font-weight:bold; font-size:16px; font-family:\'Retail Demo Bold\',serif; background-color: %s; color: %s" | COMPONENTS:<br/>%s',
(item_type == "Weapon" and "#9E630C" or item_type == "Vitality" and "#203500" or "#372248"),
(item_type == "Weapon" and "#DCCFC6" or item_type == "Vitality" and "#C7C9C6" or "#C9C7CB"),
table.concat(valid_components, "<br/>")
)
end
end
-- Process parent items
local parent_items_text = ""
local parent_items = {}
if manual_component and manual_component ~= "" then
table.insert(parent_items, manual_component)
else
parent_items = get_parent_items(item.Name)
end
if #parent_items > 0 then
local parent_icons = {}
for _, parent in ipairs(parent_items) do
table.insert(parent_icons, "{{ItemIcon|" .. parent .. "}}")
end
parent_items_text = string.format(
'|-\n! colspan="4" style="text-align:left; padding:0 12px; font-weight:bold; font-size:16px; font-family:\'Retail Demo Bold\',serif; background-color: %s; color: %s" | IS COMPONENT OF:<br/>%s',
(item_type == "Weapon" and "#704A0C" or item_type == "Vitality" and "#436310" or "#552D74"),
(item_type == "Weapon" and "#D1CBC6" or item_type == "Vitality" and "#CACFC7" or "#CCC8D2"),
table.concat(parent_icons, "<br/>")
)
end
-- Process ALL stats dynamically
local stats = {}
local skip_stats = {
Name = true, Description = true, Cost = true, Tier = true,
Activation = true, Slot = true, Components = true,
TargetTypes = true, ShopFilters = true, IsDisabled = true,
AbilityCastDelay = true, AbilityChannelTime = true,
AbilityPostCastDuration = true, AbilityCharges = true,
AbilityCooldownBetweenCharge = true, ChannelMoveSpeed = true,
AbilityResourceCost = true
}
for stat, value in pairs(item) do
if not skip_stats[stat] and value ~= nil and value ~= "0" and value ~= 0 then
local num_value = toNumber(value)
local formatted = formatStat(stat, num_value)
if formatted then
table.insert(stats, formatted)
end
end
end
-- Sort stats alphabetically for consistent display
table.sort(stats)
-- Calculate shop bonus
local tier = toNumber(item.Tier or 1)
local shop_bonus = "+0"
if item_type == "Weapon" then
shop_bonus = string.format("+%d%% Weapon Damage", 6 + (tier-1)*4)
elseif item_type == "Vitality" then
shop_bonus = string.format("+%d%% Base Health", 11 + (tier-1)*3)
else -- Spirit
shop_bonus = string.format("+%d Spirit Power", 4 * tier)
end
-- Build infobox parts
local parts = {
-- Header
string.format(
'<div class="infobox_item" style="float:right; display:inline-block; vertical-align:top;">\n' ..
'{| class="wikitable" style="text-align:left; border-collapse:collapse; width:312px; max-width:100%%; color: #FFEFD7; padding:12px; font-family:\'Retail Demo Regular\',serif; background-color: %s"\n' ..
'! colspan="4" style="width:280px; padding:5px 12px; text-align:center; font-size:24px; font-family:\'Retail Demo Bold\',serif; background-color: %s" | %s',
color.bg, color.header, item.Name
),
-- Image
string.format(
'|-\n| colspan=4 class="infobox-image" style="padding:5px; text-align:center;" | [[File:%s.png|144px]]',
item.Name
),
-- Cost
string.format(
'|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Cost\n' ..
'| colspan=2 style="padding-left:0.5em;" | {{Souls|%s}}',
color.border, formatNum(item.Cost)
),
-- Tier
string.format(
'|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Tier\n' ..
'| colspan=2 style="padding-left:0.5em;" | %s',
color.border, tier
),
-- Shop Bonus
string.format(
'|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Shop Bonus\n' ..
'| colspan=2 style="padding-left:0.5em;" | %s',
color.border, shop_bonus
)
}
-- Add components if they exist
if components_text ~= "" then
table.insert(parts, components_text)
end
-- Add parent items if they exist
if parent_items_text ~= "" then
table.insert(parts, parent_items_text)
end
-- Add description if it exists
if item.Description then
table.insert(parts,
string.format(
'|-\n| colspan="4" style="text-align:left; padding:0 12px; color:#FFEFD7; background-color: %s" | %s',
color.bg, item.Description
)
)
end
-- Add stats if they exist
if #stats > 0 then
table.insert(parts,
'|-\n| ' .. table.concat(stats, "\n|-\n| ")
)
end
-- Close table
table.insert(parts, "|}\n</div>")
-- Process and return
return frame:preprocess(table.concat(parts, "\n"))
end
return p