Editing Module:ItemData/nav

Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
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 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)"
}
-- The card navboxes put every item on a single page, so their cards opt into
-- the cheaper ItemBox rendering via lowGraphics. This swaps the paper texture
-- from the normal-mode multiply (which blends two images and is re-rasterised
-- every scrolled frame) to a transparent-paper overlay that is a single
-- cacheable image, the one change that carried the navbox scroll cost. The wear
-- and icon masks stay at full detail, since a single-image mask caches fine.
-- See [[Template:ItemBox]]. Only the ItemBox branch of write_wrapped_item_list
-- uses this; the ItemIcon lists are unaffected.
local LOW_GRAPHICS = "|lowGraphics=true"
-- Cached items grouped by slot to prevent redundant full-table evaluations
local cached_slots = nil
local function get_items_by_slot(slot)
    if not cached_slots then
        cached_slots = { table = {}, Weapon = {}, Armor = {}, Tech = {} }
        for item_key, item_data in pairs(items_data) do
            local this_slot = item_data["Slot"]
            if this_slot and cached_slots[this_slot] then
                table.insert(cached_slots[this_slot], { key = item_key, data = item_data })
            end
        end
    end
    return cached_slots[slot] or {}
end


-- With debug_mode on, it outputs unprocessed wikitext
--With debug_mode on, it outputs unprocessed wikitext
--With debug_mode off/unspecified, it processes the wikitext
local function process_debug_mode(wikitext, debug_mode)
local function process_debug_mode(wikitext, debug_mode)
    if debug_mode == 'true' then
if debug_mode == 'true' then
        return wikitext
return wikitext
    elseif debug_mode == 'false' or debug_mode == nil then
elseif debug_mode == 'false' or debug_mode == nil then
        return mw.getCurrentFrame():preprocess(wikitext)
return mw.getCurrentFrame():preprocess(wikitext)
    else
else
        return "debug_mode must be 'true' or 'false'"
return "debug_mode must be 'true' or 'false'"
    end
end
end
end


-- Writes list of items of a certain slot within the min and max soul bounds
--Writes list of items of a certain slot within the min and max soul bounds
local function write_wrapped_item_list(slot, min_souls, max_souls, template, sep, filter_mode)
-- Each item is wrapped and separated. For example of wrapping/separator, see
    if slot ~= 'Weapon' and slot ~= 'Armor' and slot ~= 'Tech' then
-- get_item_nav_bulletpoints. 'sep' should not be combined with 'right_wrap',
        return 'slot must be Weapon, Armor (Vitality), or Tech (Spirit)'
-- as the trailing separator should also be removed from the string
    end
local function write_wrapped_item_list(slot, min_souls, max_souls, left_wrap, right_wrap, sep)
   
if slot ~= 'Weapon' and slot ~= 'Armor' and slot ~= 'Tech' then
    local min_souls = tonumber(min_souls)
return 'slot must be Weapon, Armor (Vitality), or Tech (Spirit)'
    local max_souls = tonumber(max_souls)
end
    if min_souls == nil or max_souls == nil then return 'Min/Max souls must be numerical' end
   
local min_souls = tonumber(min_souls)
    filter_mode = filter_mode or "all"
local max_souls = tonumber(max_souls)
   
if min_souls == nil or max_souls == nil then return 'Min/Max souls must be numerical' end
    -- Lifted out of the loop: Language code doesn't change per item
    local lang_code = lang_module.get_lang_code()
--Retrieve all items that fit the bounds
   
local items = {}
    local items = {}
for item_key, item_data in pairs(items_data) do
    local slot_items = get_items_by_slot(slot) -- Drastically reduces search pool size
--future proofing; Disabled will be renamed to IsDisabled soon
   
if item_data['Disabled'] == nil and item_data['IsDisabled'] ~= nil then
    for _, item_entry in ipairs(slot_items) do
return "REMINDER: 'Disabled' was renamed to 'IsDisabled'"
        local item_key = item_entry.key
end
        local item_data = item_entry.data
       
local this_cost = tonumber(item_data["Cost"])
        local this_cost = tonumber(item_data["Cost"])
local this_slot = item_data["Slot"]
        local is_street_brawl = item_data["StreetBrawl"] == true
if item_data["Name"] ~= nil and item_data["Disabled"] == false and this_cost ~= nil and this_slot ~= nil then
        local should_include = true
if slot == this_slot and this_cost>=min_souls and this_cost<max_souls then
       
table.insert(items, lang_module.get_string(item_key))
        -- Filter logic for Street Brawl items
end
        if filter_mode == "street_brawl_only" and not is_street_brawl then
end
            should_include = false
end
        elseif filter_mode == "exclude_street_brawl" and is_street_brawl then
            should_include = false
--Order list alphabetically
        end
table.sort(items) --O(nlogn)
       
        if should_include and item_data["Name"] ~= nil and item_data["IsDisabled"] == false and this_cost ~= nil then
--Add each item to output
            if this_cost >= min_souls and this_cost < max_souls then
-- Each item is surrounded by left and right wrap, and separated by sep
                local item_name_english = lang_module.get_string(item_key, "en")
local ret = ''
                local item_link = linkOverrides[item_name_english] or item_name_english
for index, item_name in ipairs(items) do
                local item_name_local = lang_module.get_string(item_key, lang_code)
ret = ret .. left_wrap .. item_name .. right_wrap .. sep
               
end
                local item_template
                if template == "ItemIcon" then
--Remove the last separator thats trailing after the last item
                    if lang_code == "en" then
ret = string.sub(ret, 1, -(string.len(sep))-1)
                        item_template = "{{ItemIcon|" .. item_name_english .. "|size=26px}}"
                    else
return ret
                        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" .. LOW_GRAPHICS .. "}}"
                        else
                            item_template = "{{ItemBox|itemName=" .. item_name_english .. "|overrideLinkClick=" .. item_link .. LOW_GRAPHICS .. "}}"
                        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}}" .. LOW_GRAPHICS .. "}}"
                        else
                            item_template = "{{ItemBox|itemName=" .. item_name_english .. "|overrideLinkClick=" .. link_local .. LOW_GRAPHICS .. "}}"
                        end
                    end
                else
                    return "Invalid template type"
                end
                table.insert(items, item_template)
            end
        end
    end
   
    -- Order list alphabetically
    table.sort(items)
   
    -- Append closing tags and bullets
    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"> &bull; </span></span>'
            end
        end
    end
   
    return table.concat(items, sep)
end
end


-- for [[Template:Item Navbox]]
-- for [[Template:Item Navbox]]
function p.get_item_nav_bulletpoints(slot, min_souls, max_souls, debug_mode, filter_mode)
function p.get_item_nav_bulletpoints(slot, min_souls, max_souls, debug_mode)
-- If called internally (direct Lua call), args will be passed directly.
    -- If called from wikitext, `slot` will be the `frame` object, and we get args from `frame.args`.
 
    -- Handle the case where it's called via #invoke (i.e., from wikitext)
     if type(slot) == "table" and slot.args then
     if type(slot) == "table" and slot.args then
         local frame = slot
         local frame = slot
Line 151: Line 74:
         max_souls = frame.args[3]
         max_souls = frame.args[3]
         debug_mode = frame.args["debug_mode"]
         debug_mode = frame.args["debug_mode"]
        filter_mode = frame.args["filter"]
     end
     end
   
    local item_list = write_wrapped_item_list(slot, min_souls, max_souls, "ItemIcon", '', filter_mode)
local left_wrap = '{{ItemIcon|'
    return process_debug_mode(item_list, debug_mode)
local right_wrap = '}}'
local sep = ' &bull; '
local item_list = write_wrapped_item_list(slot, min_souls, max_souls, left_wrap, right_wrap, sep)
return process_debug_mode(item_list, debug_mode)
end
end


-- for [[Template:Infobox ShopItems]]
-- for [[Template:Infobox ShopItems]]
function p.get_item_nav_cards(slot, min_souls, max_souls, debug_mode, filter_mode)
function p.get_item_nav_cards(slot, min_souls, max_souls, debug_mode)
-- If called internally (direct Lua call), args will be passed directly.
    -- If called from wikitext, `slot` will be the `frame` object, and we get args from `frame.args`.
 
    -- Handle the case where it's called via #invoke (i.e., from wikitext)
     if type(slot) == "table" and slot.args then
     if type(slot) == "table" and slot.args then
         local frame = slot
         local frame = slot
Line 166: Line 97:
         max_souls = frame.args[3]
         max_souls = frame.args[3]
         debug_mode = frame.args["debug_mode"]
         debug_mode = frame.args["debug_mode"]
        filter_mode = frame.args["filter"]
     end
     end
   
    local item_list = write_wrapped_item_list(slot, min_souls, max_souls, "ItemBox", ' ', filter_mode)
local left_wrap = '{{ItemBox|item_name='
    return process_debug_mode(item_list, debug_mode)
local right_wrap = '}}'
local sep = ' '
local item_list = write_wrapped_item_list(slot, min_souls, max_souls, left_wrap, right_wrap, sep)
return process_debug_mode(item_list, debug_mode)
end
end


-- for [[Template:Item Navbox]] subgroup rows
function p.write_item_slot_subgroup(frame)
function p.write_item_slot_subgroup(frame)
    local slot = frame.args[1]
local slot = frame.args[1]
    local type_func = frame.args[2]
local type = frame.args[2]
    local debug_mode = frame.args['debug_mode']
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
   
    if slot == nil then return "'slot' parameter is required" end
-- Define base args
   
local template_title = "Navbox subgroup"
    local template_args = {
local template_args = {
        ["groupstyle"] = "background-color:" .. util_module.get_slot_color(slot) .. ";width:10%;min-width:70px;border-radius: 8px 0 0 8px",
["groupstyle"] = "background-color:#8b56b4;width:10%;min-width:70px;border-radius: 8px 0 0 8px",
        ["grouppadding"] = "5px",
["grouppadding"] = "5px",
        ["listpadding"] = "0 0.25rem",
["listpadding"] = "0 0.25rem",
    }
}
   
    local soul_style = "font-size: 12px; text-shadow: 1px 1px rgba(0, 0, 0, 0.3);"
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 prices = generic_module.get_item_price_per_tier()
    local group_index = 1
for i, souls in ipairs(prices) do
   
--Determine lower bound for soul
    for i, souls in ipairs(prices) do
min_souls = souls
        local min_souls = souls
       
--Skip 0 to i1 as no items cost less than 500
        if min_souls ~= 0 then  
if min_souls ~= 0 then  
            local max_souls = prices[i+1] or (min_souls * 10)
           
-- Determine upper bound
            local list
max_souls = prices[i+1]
            if type_func == 'get_item_nav_cards' then
if max_souls == nil then  
                list = p.get_item_nav_cards(slot, min_souls, max_souls, debug_mode, "exclude_street_brawl")
max_souls = min_souls * 10 --essentially have no upper bound
            elseif type_func == 'get_item_nav_bulletpoints' then
end
                list = p.get_item_nav_bulletpoints(slot, min_souls, max_souls, debug_mode, "exclude_street_brawl")
            else
--Create the subgroup
                return "'type' should be get_item_nav_cards or get_item_nav_bulletpoints"
template_args["group" .. (i-1)] = frame:expandTemplate{title="Souls", args={[1] = min_souls, ["Shadow"] = soul_style}}
            end
local list
           
if type=='get_item_nav_cards' then
            if list and list ~= "" then
list = p.get_item_nav_cards(slot, min_souls, max_souls, debug_mode)
                template_args["group" .. group_index] = frame:expandTemplate{title="Souls", args={[1] = min_souls, ["Shadow"] = soul_style}}
elseif type=='get_item_nav_bulletpoints' then
                template_args["list" .. group_index] = list
list = p.get_item_nav_bulletpoints(slot, min_souls, max_souls, debug_mode)
                group_index = group_index + 1
else
            end
return "'type' should be get_item_nav_cards or get_item_nav_bulletpoints"
        end
end
    end
template_args["list" .. (i-1)] = list
   
    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]]
 
-- Displayed when a stat does not apply to an item
local NO_VALUE = "—"
 
-- One expanded inline attribute as produced by [[Module:Lang]]:
-- wrapper span > icon span > spacer span > wikilink
local INLINE_ATTRIBUTE_PATTERN =
    '<span class="no%-blue%-link" style="text%-wrap:nowrap; font%-weight:bold; color:[^;"]*;">' ..
    '<span style="[^"]*">%[%[File:[^%]]*%]%]</span>' ..
    '<span style="color: inherit;"> </span> ' ..
    '%[%[([^|%]]+)|([^%]]*)%]%]</span>'
 
-- Range is taken from the first of these the item has a usable value for
local RANGE_PROPERTIES = { "AbilityCastRange", "Radius", "EndRadius" }
 
-- Rewrites the description markup [[Module:Lang]] produces
local function restyle_description(text)
    text = text:gsub(INLINE_ATTRIBUTE_PATTERN, '[[%1|%2]]')
    -- Attributes with an icon but no link keep their span; follow the site text color
    text = text:gsub('color:#ffefd7;', 'color:var(--color-base);')
    -- Diminished notes ("Cannot be used while Stunned...")
    return (text:gsub('color:#C0C0C0', 'color:var(--color-subtle)'))
end
 
-- Numeric value of a stat, used for sorting
local function stat_sort_value(value)
    if type(value) == "table" then value = value["Value"] end
    if value == nil then return -1 end
    return tonumber(tostring(value):match("[-%d%.]+")) or -1
end
 
-- First range property the item has a usable value for
local function get_range_property(item_data)
    for _, property in ipairs(RANGE_PROPERTIES) do
        local value = item_data[property]
        if value ~= nil and stat_sort_value(value) > 0 then
            return property, value
        end
    end
    return nil, nil
end
 
-- Builds a stat cell, falling back to an em dash when the stat does not apply
local function stat_cell(raw_value, item_name, property, postfix)
    if raw_value == nil or property == nil then
        return '| data-sort-value="-1" style="text-align:center;font-size:16px" | ' .. NO_VALUE
    end
    return string.format(
        '| data-sort-value="%s" style="text-align:center;font-size:16px" | {{#invoke:ItemData|get_prop|%s|%s}}%s',
        stat_sort_value(raw_value), item_name, property, postfix or "")
end
 
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
          item_data["Activation"] and
          item_data["Activation"] ~= "Passive" then
           
            local range_property, range_value = get_range_property(item_data)
           
            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,
                cooldown = item_data["AbilityCooldown"],
                duration = item_data["AbilityDuration"],
                range_property = range_property,
                range_value = range_value
            })
        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}}
! {{#invoke:Lang|get_string|AbilityDuration_label}}
! {{#invoke:Lang|get_string|AbilityCastRange_label}}]=])
   
    for _, item in ipairs(active_items) do
        local bg_color = util_module.get_slot_color(item.slot)
 
local item_type
if item.slot == "Armor" then
    item_type = "Armor"
elseif item.slot == "Tech" then
    item_type = "Tech"
else
    item_type = "Weapon"
end
end
       
end
        local row = string.format([=[
|-
return frame:expandTemplate{title=template_title, args=template_args}
! style="background-color:%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}}
%s
%s
%s]=],
            bg_color,
            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,
            stat_cell(item.cooldown, item.name, "AbilityCooldown", "s"),
            stat_cell(item.duration, item.name, "AbilityDuration", "s"),
            stat_cell(item.range_value, item.name, item.range_property))
           
        table.insert(rows, row)
    end
   
    table.insert(rows, "\n|}")
    return restyle_description(frame:preprocess(table.concat(rows, "\n")))
end
end


return p
return p
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template