Module:Infobox item: Difference between revisionsGive feedback
Changed icon size |
append_label: prefer per-stat StatLinks target over generic attribute-type link (fixes e.g. Debuff Resist → Ability Duration) (with help from vergir-bot LLM) |
||
| (11 intermediate revisions by 4 users not shown) | |||
| Line 15: | Line 15: | ||
local tostring = tostring | local tostring = tostring | ||
local type = type | local type = type | ||
-- Status Effect Icon Map | |||
local STATUS_EFFECT_ICON_MAP = { | |||
StatusEffectDisarmed = {img='Status_Disarm.png', link='Disarm', color='Red'}, | |||
StatusEffectEMP = {img='Status_Silence.png', link='Silence', color='Purple'}, | |||
StatusEffectInvisible= {img='Invisible.png', link='Invisible',color='Green'}, | |||
StatusEffectStun = {img='Status_Stun.png', link='Stun', color='NoColor'}, | |||
StatusEffectSlow = {img='Status_Movement_Slow.png', link='Slow', color='Purple'}, | |||
} | |||
-- Pre-computed constants | -- Pre-computed constants | ||
local STATUS_FMT = "[[{{#invoke:Lang|get_string|Citadel_%s}}]]" | local STATUS_FMT = "[[{{#invoke:Lang|get_string|Citadel_%s}}]]" | ||
.. " [[{{#invoke:Lang|get_string|Citadel_StatusEffect}}]]" | .. " [[{{#invoke:Lang|get_string|Citadel_StatusEffect}}]]" | ||
-- Property keys to completely ignore during processing | |||
local IGNORED_PROP_KEYS = { | |||
BuildUpPerShot = true, | |||
} | |||
-- Label key remapping table (replaces if/elseif chain) | -- Label key remapping table (replaces if/elseif chain) | ||
local LABEL_KEY_REMAP = { | local LABEL_KEY_REMAP = { | ||
DotDuration = "DOTDuration", | DotDuration = "DOTDuration", | ||
} | |||
-- Item name override mapping (old name -> new name) | |||
local ITEM_NAME_OVERRIDES = { | |||
["debuff remover"] = "Dispel Magic", | |||
["backstabber"] = "Stalker", | |||
} | } | ||
| Line 50: | Line 69: | ||
local conditional_label -- lazily initialised | local conditional_label -- lazily initialised | ||
-------------------------------------------------------------------------------- | |||
-- Item name resolution | |||
-------------------------------------------------------------------------------- | |||
local function resolve_item_name(name) | |||
-- Check if there's an override for this name (case-insensitive) | |||
local lowercase_name = name:lower() | |||
local override = ITEM_NAME_OVERRIDES[lowercase_name] | |||
if override then | |||
return override | |||
end | |||
-- If no override, try to find the item in data and return its proper Name | |||
-- First try direct key lookup | |||
if data[name] and not data[name].IsDisabled then | |||
return data[name].Name or name | |||
end | |||
-- Then try case-insensitive search by Name field | |||
local lowercase_search = name:lower() | |||
for _, v in pairs(data) do | |||
if v.Name and v.Name:lower() == lowercase_search and not v.IsDisabled then | |||
return v.Name | |||
end | |||
end | |||
-- Fallback to original name | |||
return name | |||
end | |||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| Line 68: | Line 117: | ||
local function get_json_item(name) | local function get_json_item(name) | ||
local direct = data[ | -- Apply name override before lookup | ||
local resolved_name = resolve_item_name(name) | |||
local direct = data[resolved_name] | |||
if direct and not direct.IsDisabled then return direct end | if direct and not direct.IsDisabled then return direct end | ||
local fallback | local fallback | ||
for _, v in pairs(data) do | for _, v in pairs(data) do | ||
if v.Name == | if v.Name == resolved_name then | ||
if not v.IsDisabled then return v end | if not v.IsDisabled then return v end | ||
fallback = fallback or v | fallback = fallback or v | ||
| Line 125: | Line 177: | ||
local function get_component_of_sorted(item_input) | local function get_component_of_sorted(item_input) | ||
-- Apply name override before processing | |||
local resolved_input = resolve_item_name(item_input) | |||
local target_key | local target_key | ||
if data[ | if data[resolved_input] then | ||
target_key = | target_key = resolved_input | ||
else | else | ||
for k, v in pairs(data) do | for k, v in pairs(data) do | ||
if v.Name == | if v.Name == resolved_input and not v.IsDisabled then | ||
target_key = k | target_key = k | ||
break | break | ||
| Line 181: | Line 236: | ||
if label == "" then return val_str end | if label == "" then return val_str end | ||
-- Resolve link priority: Override > Attribute Map | -- Resolve link priority: Override > JSON Stat Link > Attribute Map Link | ||
local link_target = link_override | local link_target = link_override | ||
if not link_target or link_target == "" then | if not link_target or link_target == "" then | ||
link_target = | link_target = stat_links[prop_key] | ||
end | end | ||
if not link_target or link_target == "" then | if not link_target or link_target == "" then | ||
link_target = | link_target = attr_link | ||
end | end | ||
| Line 216: | Line 271: | ||
if not conditional_label then | if not conditional_label then | ||
conditional_label = format( | conditional_label = format( | ||
'<span style="color:#C0C0C0"> | '<span style="color:#C0C0C0">%s</span>', | ||
lang.get_string("Citadel_Shop_ConditionalAttribute") | lang.get_string("Citadel_Shop_ConditionalAttribute") | ||
) | ) | ||
| Line 251: | Line 306: | ||
icon_str = format("{{Icon/%s|[[File:%s|%spx|link=%s]]}}", color, img, 18, link or "") | icon_str = format("{{Icon/%s|[[File:%s|%spx|link=%s]]}}", color, img, 18, link or "") | ||
end | end | ||
return { | |||
icon = icon_str, | |||
link = link | |||
} | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Status Effect Icon helper | |||
-------------------------------------------------------------------------------- | |||
local function resolve_status_effect_icon(prop_key) | |||
local effect_config = STATUS_EFFECT_ICON_MAP[prop_key] | |||
if not effect_config then return nil end | |||
local color = effect_config.color or "NoColor" | |||
local img = effect_config.img or "GenericProperty.png" | |||
local link = effect_config.link | |||
-- Construct icon using the defined color template | |||
local icon_str = format( | |||
"{{Icon/%s|[[File:%s|18px|link=%s]]}}", | |||
color, img, link or "" | |||
) | |||
return { | return { | ||
| Line 376: | Line 455: | ||
return append_conditional(val_str, allow_conditional, usage_flags) | return append_conditional(val_str, allow_conditional, usage_flags) | ||
end | |||
-------------------------------------------------------------------------------- | |||
-- Status Effect formatting helper | |||
-------------------------------------------------------------------------------- | |||
local function format_status_effect(prop_key) | |||
local lang_str = format("[[{{#invoke:Lang|get_string|Citadel_%s}}]]", prop_key) | |||
.. " [[{{#invoke:Lang|get_string|Citadel_StatusEffect}}]]" | |||
-- Get icon for this status effect | |||
local icon_style = resolve_status_effect_icon(prop_key) | |||
if icon_style and icon_style.icon then | |||
return icon_style.icon .. " " .. lang_str | |||
end | |||
return lang_str | |||
end | end | ||
| Line 403: | Line 499: | ||
for _, prop_obj in ipairs(prop_list or {}) do | for _, prop_obj in ipairs(prop_list or {}) do | ||
local key = prop_obj.Key | local key = prop_obj.Key | ||
if not seen[key] and ( | if not seen[key] and not IGNORED_PROP_KEYS[key] and ( | ||
key:match("^StatusEffect") | key:match("^StatusEffect") | ||
or is_property_in_lang_data(key, prop_obj.LocTokenOverride) | or is_property_in_lang_data(key, prop_obj.LocTokenOverride) | ||
| Line 424: | Line 520: | ||
local item_name = frame.args[1] | local item_name = frame.args[1] | ||
if not item_name then return "Item name missing" end | if not item_name then return "Item name missing" end | ||
-- Resolve the item name (apply override if exists) | |||
local resolved_name = resolve_item_name(item_name) | |||
local item = get_json_item(item_name) | local item = get_json_item(item_name) | ||
| Line 429: | Line 528: | ||
local ItemData = require("Module:ItemData") | local ItemData = require("Module:ItemData") | ||
local lines = { "{{ | local lines = { "{{Infobox item" } | ||
if enhanced then | if enhanced then | ||
insert(lines, "| enhanced = true") | insert(lines, "| enhanced = true") | ||
end | end | ||
insert(lines, "| item_name = " .. | -- Use resolved name for item_name parameter | ||
insert(lines, "| item_name = " .. resolved_name) | |||
if item.StreetBrawl == true then | if item.StreetBrawl == true then | ||
insert(lines, "| street_brawl = true") | insert(lines, "| street_brawl = true") | ||
end | |||
-- Pass through nocat arg to the Infobox item template if provided | |||
local nocat = frame.args.nocat | |||
if nocat and nocat ~= "" then | |||
insert(lines, "| nocat = " .. nocat) | |||
end | end | ||
| Line 449: | Line 555: | ||
if section.Type == "Innate" then | if section.Type == "Innate" then | ||
for _, obj in ipairs(section.Main or {}) do | for _, obj in ipairs(section.Main or {}) do | ||
if is_property_in_lang_data(obj.Key, obj.LocTokenOverride) then | if not IGNORED_PROP_KEYS[obj.Key] and is_property_in_lang_data(obj.Key, obj.LocTokenOverride) then | ||
local val | local val | ||
if enhanced then | if enhanced then | ||
val = format_value_enhanced( | val = format_value_enhanced( | ||
resolved_name, obj.Key, obj.Value, obj.Type, | |||
obj.LocTokenOverride, nil, false, obj.UsageFlags, | obj.LocTokenOverride, nil, false, obj.UsageFlags, | ||
true -- disable_attr | true -- disable_attr | ||
| Line 471: | Line 577: | ||
end | end | ||
for _, obj in ipairs(section.Alt or {}) do | for _, obj in ipairs(section.Alt or {}) do | ||
if is_property_in_lang_data(obj.Key, obj.LocTokenOverride) then | if not IGNORED_PROP_KEYS[obj.Key] and is_property_in_lang_data(obj.Key, obj.LocTokenOverride) then | ||
local val | local val | ||
if enhanced then | if enhanced then | ||
val = format_value_enhanced( | val = format_value_enhanced( | ||
resolved_name, obj.Key, obj.Value, obj.Type, | |||
obj.LocTokenOverride, nil, false, obj.UsageFlags, | obj.LocTokenOverride, nil, false, obj.UsageFlags, | ||
true -- disable_attr | true -- disable_attr | ||
| Line 496: | Line 602: | ||
------------------------------------------------------------------ | ------------------------------------------------------------------ | ||
-- Build formatters once (closures capture | -- Build formatters once (closures capture resolved_name) | ||
------------------------------------------------------------------ | ------------------------------------------------------------------ | ||
local main_formatter, alt_formatter | local main_formatter, alt_formatter | ||
| Line 503: | Line 609: | ||
main_formatter = function(obj) | main_formatter = function(obj) | ||
if obj.Key:match("^StatusEffect") then | if obj.Key:match("^StatusEffect") then | ||
return | return format_status_effect(obj.Key) | ||
end | end | ||
return format_value_enhanced( | return format_value_enhanced( | ||
resolved_name, obj.Key, obj.Value, obj.Type, | |||
obj.LocTokenOverride, false, true, obj.UsageFlags, nil, true | obj.LocTokenOverride, false, true, obj.UsageFlags, nil, true | ||
) | ) | ||
end | end | ||
alt_formatter = function(obj) | alt_formatter = function(obj) | ||
if obj.Key:match("^StatusEffect") then | |||
return format_status_effect(obj.Key) | |||
end | |||
return format_value_enhanced( | return format_value_enhanced( | ||
resolved_name, obj.Key, obj.Value, obj.Type, | |||
obj.LocTokenOverride, nil, false, obj.UsageFlags, | obj.LocTokenOverride, nil, false, obj.UsageFlags, | ||
true -- disable_attr | true -- disable_attr | ||
| Line 520: | Line 629: | ||
main_formatter = function(obj) | main_formatter = function(obj) | ||
if obj.Key:match("^StatusEffect") then | if obj.Key:match("^StatusEffect") then | ||
return | return format_status_effect(obj.Key) | ||
end | end | ||
return format_value( | return format_value( | ||
| Line 528: | Line 637: | ||
end | end | ||
alt_formatter = function(obj) | alt_formatter = function(obj) | ||
if obj.Key:match("^StatusEffect") then | |||
return format_status_effect(obj.Key) | |||
end | |||
return format_value( | return format_value( | ||
obj.Key, obj.Value, obj.Type, obj.LocTokenOverride, | obj.Key, obj.Value, obj.Type, obj.LocTokenOverride, | ||
| Line 555: | Line 667: | ||
insert(lines, format( | insert(lines, format( | ||
"| %s%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}", | "| %s%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}", | ||
prefix, counter, loc, | prefix, counter, loc, resolved_name | ||
)) | )) | ||
end | end | ||
| Line 566: | Line 678: | ||
for _, prop_obj in ipairs(section.Main or {}) do | for _, prop_obj in ipairs(section.Main or {}) do | ||
local key = prop_obj.Key | local key = prop_obj.Key | ||
if not main_seen[key] and ( | if not main_seen[key] and not IGNORED_PROP_KEYS[key] and ( | ||
key:match("^StatusEffect") | key:match("^StatusEffect") | ||
or is_property_in_lang_data(key, prop_obj.LocTokenOverride) | or is_property_in_lang_data(key, prop_obj.LocTokenOverride) | ||
| Line 586: | Line 698: | ||
local key = prop_obj.Key | local key = prop_obj.Key | ||
-- Skip keys already emitted as main stats | -- Skip keys already emitted as main stats | ||
if not main_seen[key] and not alt_seen[key] and ( | if not main_seen[key] and not alt_seen[key] and not IGNORED_PROP_KEYS[key] and ( | ||
key:match("^StatusEffect") | key:match("^StatusEffect") | ||
or is_property_in_lang_data(key, prop_obj.LocTokenOverride) | or is_property_in_lang_data(key, prop_obj.LocTokenOverride) | ||
| Line 600: | Line 712: | ||
-- Cooldown / ChargeUp | -- Cooldown / ChargeUp | ||
local cd = format_section_cooldown(section, | local cd = format_section_cooldown(section, resolved_name, enhanced) | ||
if cd.cooldown then | if cd.cooldown then | ||
insert(lines, format("| %s%d_cooldown = %s", prefix, counter, cd.cooldown)) | insert(lines, format("| %s%d_cooldown = %s", prefix, counter, cd.cooldown)) | ||
| Line 620: | Line 732: | ||
------------------------------------------------------------------ | ------------------------------------------------------------------ | ||
for i = 1, 2 do | for i = 1, 2 do | ||
local comp = ItemData.get_component_name { args = { | local comp = ItemData.get_component_name { args = { resolved_name, i } } | ||
if comp and comp ~= "" then | if comp and comp ~= "" then | ||
insert(lines, format("| component%d_name = %s", i, comp)) | insert(lines, format("| component%d_name = %s", i, comp)) | ||