Editing Module:Item NavboxGive feedback
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local items_data = mw.loadJsonData("Data:ItemData.json") | local items_data = mw.loadJsonData("Data:ItemData.json") | ||
local lang_module = require('Module:Lang') | local lang_module = require('Module:Lang') | ||
local generic_module = require('Module:GenericData') | |||
local util_module = require('Module:Utilities') | |||
-- Exceptions if the link is different from the item name | |||
local linkOverrides = { | |||
["Bullet Lifesteal"] = "Bullet Lifesteal (item)", | |||
["Spirit Lifesteal"] = "Spirit Lifesteal (item)" | |||
} | |||
-- Cached items grouped by slot to prevent redundant full-table evaluations | -- Cached items grouped by slot to prevent redundant full-table evaluations | ||
| Line 30: | Line 37: | ||
end | end | ||
-- | -- Writes list of items of a certain slot within the min and max soul bounds | ||
local function | local function write_wrapped_item_list(slot, min_souls, max_souls, template, sep, filter_mode) | ||
if slot ~= 'Weapon' and slot ~= 'Armor' and slot ~= 'Tech' then return ' | if slot ~= 'Weapon' and slot ~= 'Armor' and slot ~= 'Tech' then | ||
return 'slot must be Weapon, Armor (Vitality), or Tech (Spirit)' | |||
end | end | ||
local min_souls = tonumber(min_souls) | |||
local max_souls = tonumber(max_souls) | |||
if min_souls == nil or max_souls == nil then return 'Min/Max souls must be numerical' end | |||
local | |||
filter_mode = filter_mode or "all" | |||
-- | -- Lifted out of the loop: Language code doesn't change per item | ||
local | local lang_code = lang_module.get_lang_code() | ||
local items = {} | local items = {} | ||
local slot_items = get_items_by_slot(slot) | local slot_items = get_items_by_slot(slot) -- Drastically reduces search pool size | ||
for _, item_entry in ipairs(slot_items) do | for _, item_entry in ipairs(slot_items) do | ||
| Line 106: | Line 63: | ||
local should_include = true | local should_include = true | ||
-- Filter logic for Street Brawl items | |||
if filter_mode == "street_brawl_only" and not is_street_brawl then | if filter_mode == "street_brawl_only" and not is_street_brawl then | ||
should_include = false | should_include = false | ||
| Line 115: | Line 73: | ||
if this_cost >= min_souls and this_cost < max_souls then | if this_cost >= min_souls and this_cost < max_souls then | ||
local item_name_english = lang_module.get_string(item_key, "en") | local item_name_english = lang_module.get_string(item_key, "en") | ||
local item_link = linkOverrides[item_name_english] or item_name_english | |||
local item_name_local = lang_module.get_string(item_key, lang_code) | local item_name_local = lang_module.get_string(item_key, lang_code) | ||
local item_template | local item_template | ||
if lang_code == "en" then | if template == "ItemIcon" then | ||
item_template = "{{ItemIcon|" .. item_name_english .. "|l1=" .. | if lang_code == "en" then | ||
item_template = "{{ItemIcon|" .. item_name_english .. "|size=26px}}" | |||
else | |||
item_template = "{{ItemIcon|" .. item_name_english .. "|lang=" .. lang_code .. "|l1=" .. item_name_local .. "|size=26px}}" | |||
end | |||
local search_data = item_name_english | |||
if lang_code ~= "en" and item_name_local then | |||
search_data = search_data .. " " .. item_name_local | |||
end | |||
item_template = '<span class="item-nav-search-wrapper"><span class="item-nav-search-item" data-item-name="' .. search_data .. '">' .. item_template .. '</span>' | |||
elseif template == "ItemBox" then | |||
if lang_code == "en" then | |||
if is_street_brawl then | |||
item_template = "{{ItemBox|itemName=" .. item_name_english .. "|overrideLinkClick=" .. item_link .. "|overrideTier=5}}" | |||
else | |||
item_template = "{{ItemBox|itemName=" .. item_name_english .. "|overrideLinkClick=" .. item_link .. "}}" | |||
end | |||
else | |||
local link_local = item_link .. "/" .. lang_code | |||
if is_street_brawl then | |||
item_template = "{{ItemBox|itemName=" .. item_name_english .. "|overrideLinkClick=" .. link_local .. "|item_price={{#invoke:Lang|get_string|Citadel_ItemDraft_Legendary}}}}" | |||
else | |||
item_template = "{{ItemBox|itemName=" .. item_name_english .. "|overrideLinkClick=" .. link_local .. "}}" | |||
end | |||
end | |||
else | else | ||
return "Invalid template type" | |||
end | end | ||
table.insert(items, item_template) | table.insert(items, item_template) | ||
end | end | ||
| Line 141: | Line 113: | ||
end | end | ||
-- Order list alphabetically | |||
table.sort(items) | table.sort(items) | ||
-- Append closing tags and bullets | -- Append closing tags and bullets | ||
local total_items = #items | if template == "ItemIcon" then | ||
local total_items = #items | |||
for i = 1, total_items do | |||
if i == total_items then | |||
items[i] = items[i] .. '</span>' | |||
else | |||
items[i] = items[i] .. '<span class="item-nav-search-sep"> • </span></span>' | |||
end | |||
end | end | ||
end | end | ||
return table.concat(items, | return table.concat(items, sep) | ||
end | end | ||
-- Generates the full HTML item navbox recursively and efficiently | -- Generates the full HTML item navbox recursively and efficiently | ||
function p.generate_full_navbox(frame) | function p.generate_full_navbox(frame) | ||
local debug_mode | local debug_mode | ||
if type(frame) == "table" and frame.args then | |||
debug_mode = frame.args["debug_mode"] | |||
end | |||
local html = {} | local html = {} | ||
table.insert(html, '<div class="itemnav-container"><templatestyles src="User:Kai/mp/itemnavbox/styles.css"/>\n') | |||
table.insert(html, '<div class="itemnav-container"><templatestyles src=" | |||
-- Maps Headers to Slots and distinct CSS Classes | -- Maps Headers to Slots and distinct CSS Classes | ||
| Line 186: | Line 163: | ||
for _, tier in ipairs(tiers) do | for _, tier in ipairs(tiers) do | ||
local css_class = "itemnav-soul " .. (tier.extra_css or "") .. cat.css | local css_class = "itemnav-soul " .. (tier.extra_css or "") .. cat.css | ||
local items_wikitext = | -- Fetch items purely in lua without invoking preprocessing mid-loop | ||
local items_wikitext = write_wrapped_item_list(cat.slot, tier.min, tier.max, "ItemIcon", '', nil) | |||
table.insert(html, '\n<div class=" | table.insert(html, '\n<div class="imsodonenamingclasses">\n') | ||
table.insert(html, '<div class="' .. css_class .. '">' .. tier.label .. '</div | table.insert(html, '<div class="' .. css_class .. '">' .. tier.label .. '</div><div class="itemnav-cost-type-container">' .. items_wikitext .. '</div>\n') | ||
table.insert(html, '</div>\n') | table.insert(html, '</div>\n') | ||
end | end | ||
| Line 199: | Line 176: | ||
table.insert(html, '\n</div>') | table.insert(html, '\n</div>') | ||
-- Only ONE preprocess call for the entire structure | -- Only ONE preprocess call for the entire structure! | ||
local final_wikitext = table.concat(html) | |||
return process_debug_mode(final_wikitext, debug_mode) | |||
end | end | ||
-- | -- for [[Template:Item Navbox]] | ||
function p. | function p.get_item_nav_bulletpoints(slot, min_souls, max_souls, debug_mode, filter_mode) | ||
if type(slot) == "table" and slot.args then | |||
local frame = slot | |||
slot = frame.args[1] | |||
min_souls = frame.args[2] | |||
max_souls = frame.args[3] | |||
debug_mode = frame.args["debug_mode"] | |||
filter_mode = frame.args["filter"] | |||
end | |||
local item_list = write_wrapped_item_list(slot, min_souls, max_souls, "ItemIcon", '', filter_mode) | |||
return process_debug_mode(item_list, debug_mode) | |||
end | |||
-- for [[Template:Infobox ShopItems]] | |||
function p.get_item_nav_cards(slot, min_souls, max_souls, debug_mode, filter_mode) | |||
if type(slot) == "table" and slot.args then | |||
local frame = slot | |||
slot = frame.args[1] | |||
min_souls = frame.args[2] | |||
max_souls = frame.args[3] | |||
debug_mode = frame.args["debug_mode"] | |||
filter_mode = frame.args["filter"] | |||
end | |||
local item_list = write_wrapped_item_list(slot, min_souls, max_souls, "ItemBox", ' ', filter_mode) | |||
return process_debug_mode(item_list, debug_mode) | |||
end | |||
-- for [[Template:Item Navbox]] subgroup rows | |||
local | function p.write_item_slot_subgroup(frame) | ||
local slot = frame.args[1] | |||
local type_func = frame.args[2] | |||
local debug_mode = frame.args['debug_mode'] | |||
local street_brawl_label = frame.args['street_brawl_label'] or '{{Legendary|{{#invoke:Lang|get_string|Citadel_ItemDraft_Legendary}}}}' | |||
if slot == nil then return "'slot' parameter is required" end | |||
local template_args = { | |||
["groupstyle"] = "background-color:" .. util_module.get_slot_color(slot) .. ";width:10%;min-width:70px;border-radius: 8px 0 0 8px", | |||
["grouppadding"] = "5px", | |||
["listpadding"] = "0 0.25rem", | |||
} | } | ||
local soul_style = "font-size: 12px; text-shadow: 1px 1px rgba(0, 0, 0, 0.3);" | |||
local prices = generic_module.get_item_price_per_tier() | |||
local group_index = 1 | |||
for i, souls in ipairs(prices) do | |||
local min_souls = souls | |||
if min_souls ~= 0 then | |||
local max_souls = prices[i+1] or (min_souls * 10) | |||
local list | |||
if type_func == 'get_item_nav_cards' then | |||
list = p.get_item_nav_cards(slot, min_souls, max_souls, debug_mode, "exclude_street_brawl") | |||
elseif type_func == 'get_item_nav_bulletpoints' then | |||
list = p.get_item_nav_bulletpoints(slot, min_souls, max_souls, debug_mode, "exclude_street_brawl") | |||
else | |||
return "'type' should be get_item_nav_cards or get_item_nav_bulletpoints" | |||
end | |||
if list and list ~= "" then | |||
template_args["group" .. group_index] = frame:expandTemplate{title="Souls", args={[1] = min_souls, ["Shadow"] = soul_style}} | |||
template_args["list" .. group_index] = list | |||
group_index = group_index + 1 | |||
end | |||
end | |||
end | |||
local sb_list | |||
if type_func == 'get_item_nav_cards' then | |||
sb_list = p.get_item_nav_cards(slot, 0, 999999, debug_mode, "street_brawl_only") | |||
elseif type_func == 'get_item_nav_bulletpoints' then | |||
sb_list = p.get_item_nav_bulletpoints(slot, 0, 999999, debug_mode, "street_brawl_only") | |||
end | |||
if sb_list and sb_list ~= "" then | |||
local sb_style = "color:#98ffde; font-family:'Retail Demo', sans-serif; text-wrap: nowrap; text-shadow: 0px 0px 2px #2c312e, 1.3px 1.3px rgba(0, 0, 0, 0.2);padding:5px;" | |||
local sb_link_text = "[[Street Brawl{{if_lang}}|<span style=\"" .. sb_style .. "\"><b>" .. street_brawl_label .. "</b></span>]]" | |||
template_args["group" .. group_index] = frame:preprocess(sb_link_text) | |||
template_args["list" .. group_index] = sb_list | |||
end | |||
return frame:expandTemplate{title="Navbox subgroup", args=template_args} | |||
end | |||
-- for [[Template:Active Items]] | |||
local | function p.generate_active_items_table(frame) | ||
{ | local active_items = {} | ||
for item_key, item_data in pairs(items_data) do | |||
local cost = tonumber(item_data["Cost"]) | |||
if item_data["Name"] and | |||
item_data["IsDisabled"] == false and | |||
cost ~= nil and cost > 0 and | |||
for _, | item_data["Activation"] and | ||
item_data["Activation"] ~= "Passive" then | |||
table.insert(active_items, { | |||
name = item_data["Name"], | |||
key = item_key, | |||
cost = cost, | |||
slot = item_data["Slot"] or "Weapon", | |||
has_active_desc = item_data["ActiveDescription"] ~= nil | |||
}) | |||
end | |||
end | |||
table.sort(active_items, function(a, b) return a.name < b.name end) | |||
-- Optimized string compilation using a structural buffer table | |||
local rows = {} | |||
table.insert(rows, [=[{| class="wikitable mw-collapsible sortable" style="width:100%" | |||
|+{{#invoke:Dictionary|translate|Active Items}} | |||
! colspan="2" | {{#invoke:Lang|get_string|Citadel_HeroBuilds_CategoryNameLabel}} | |||
! {{#invoke:Lang|get_string|Citadel_UserFeedback_TypeLabel}} | |||
! {{Souls|{{#invoke:Dictionary|translate|Cost}}}} | |||
! {{#invoke:Lang|get_string|Citadel_Mod_Tooltip_Active}} | |||
! {{#invoke:Lang|get_string|AbilityCooldown_label}}]=]) | |||
for _, item in ipairs(active_items) do | |||
local item_class, item_type | |||
if item.slot == "Armor" then | |||
item_class = "item-bg-armor" | |||
item_type = "Armor" | |||
elseif item.slot == "Tech" then | |||
item_class = "item-bg-tech" | |||
item_type = "Tech" | |||
else | |||
item_class = "item-bg-weapon" | |||
item_type = "Weapon" | |||
end | end | ||
table.insert( | local row = string.format([=[ | ||
|- | |||
! class="%s" | [[File:%s.png|64x64px]] | |||
! [[%s{{If_lang}}|{{#invoke:Lang|get_string|%s}}]] | |||
| style="text-align:center;font-weight:bold" | {{ItemType|%s|{{#invoke:Lang|get_string|CitadelCategory%s}}}} | |||
| data-sort-value="%d" style="text-align:center;font-size:15px" | {{Souls|%d}} | |||
| {{#invoke:Utilities|process_variables|%s_active_desc|%s}}{{#invoke:Utilities|process_variables|%s_desc|%s}}{{#invoke:Utilities|process_variables|%s_active|%s}} | |||
| style="text-align:center;font-size:16px" | {{#invoke:ItemData|get_prop|%s|AbilityCooldown}}s]=], | |||
item_class, | |||
item.name, | |||
item.name, item.key, | |||
item_type, item_type, | |||
item.cost, item.cost, | |||
item.key, item.name, item.key, item.name, item.key, item.name, | |||
item.name) | |||
table.insert(rows, row) | |||
end | end | ||
table.insert( | table.insert(rows, "\n|}") | ||
return frame:preprocess(table.concat(rows, "\n")) | |||
end | end | ||
return p | return p | ||