Home
Random
Log in
Settings
About the Deadlock Wiki
Search
Editing
Module
:
LevelTables
Give feedback
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.
Anti-spam check. Do
not
fill this in!
local p = {}; local Icon = require('Module:Icon') local Souls = require('Module:Souls') -- Loads the necessary data from the wiki's JSON pages. local heroes_data = mw.loadJsonData("Data:HeroData.json") local soul_unlocks_data = mw.loadJsonData("Data:SoulUnlockData.json") -- Helper to safely get stats that may be nested under Weapon local function get_stat_value(data, key) if data[key] ~= nil then return data[key] end if data.Weapon and data.Weapon[key] ~= nil then return data.Weapon[key] end return 0 end -- Defines human-readable names for the internal property keys used in headers. local level_scaling_key_to_name = { BulletDamage = "Bullet Damage", MaxHealth = "Health", TechPower = "Spirit Power", LightMeleeDamage = "Light Melee Damage", HeavyMeleeDamage = "Heavy Melee Damage" } -- A helper function that returns a sort-friendly version of a name by removing leading articles like "The ". local function get_sortable_name(name) if string.sub(name, 1, 4) == "The " then return string.sub(name, 5) end return name end --[[ -- @function p.write_power_increase_table -- @description Generates a responsive and readable table showing hero stat scaling based on boons. -- Includes a compact summary view and an expandable, detailed view for all levels. -- Can optionally wrap the entire output in a theme-aware collapsible section. -- @param {Frame} frame The MediaWiki frame object. -- @param {string} frame.args[1] The internal property name to display (e.g., 'BulletDamage'). -- @param {string} [frame.args.title] An optional title. If provided, the table is wrapped in a collapsible section. -- @return {string} The complete HTML for the table or collapsible section. --]] p.write_power_increase_table = function(frame) -- Use the same smart sorting for this list. local function hero_sort(h1, h2) return get_sortable_name(h1.Name) < get_sortable_name(h2.Name) end -- Read and validate the arguments passed from the #invoke call. local property = frame.args[1] local title = frame.args.title if not property or property == '' then return '<strong class="error">Error: Missing required property argument in #invoke call. Example: {{#invoke:ModuleName|write_power_increase_table|BulletDamage}}</strong>' end local name = level_scaling_key_to_name[property] if not name then return '<strong class="error">Error: Invalid property argument "' .. tostring(property) .. '". Must be one of: BulletDamage, MaxHealth, TechPower, LightMeleeDamage, HeavyMeleeDamage.</strong>' end -- Create an ordered list of soul requirements for each power increase. local pi_souls_list = {} for _, v in ipairs(soul_unlocks_data) do if (v["PowerIncrease"]) then table.insert(pi_souls_list, v["RequiredSouls"]) end end local max_boons = #pi_souls_list -- Define the milestones to display in the summary table. local milestones = {10, 20, max_boons} -- Filter out disabled heroes from the data. local filtered_heroes = {} for _, hero in pairs(heroes_data) do if (not hero["IsDisabled"] and not hero["InDevelopment"] and hero["Name"]) then table.insert(filtered_heroes, hero) end end -- Sort the hero list alphabetically, ignoring "The " for proper sorting. table.sort(filtered_heroes, hero_sort) -- Begin building the main data table. local create_table = mw.html.create("table"):addClass("wikitable sortable") -- Create the header row for the summary table. local header_row = mw.html.create("tr") header_row :tag("th"):wikitext("Hero"):done() :tag("th"):wikitext("Base " .. name):done() :tag("th"):wikitext("Increase per Boon"):done() -- Dynamically create milestone headers with both boon count and soul amount. for _, count in ipairs(milestones) do local header_text = count .. " Boons" if count == max_boons then header_text = header_text .. " (Max)" end local required_souls = pi_souls_list[count] local souls_formatted = string.format("%.1fK", (required_souls + 400) / 1000) local soul_display = Souls._render(souls_formatted) local full_header = header_text .. "<br /><small>(" .. soul_display .. ")</small>" header_row:tag("th"):wikitext(full_header):done() end header_row:tag("th"):wikitext("Full Details"):done() create_table:node(header_row) -- A helper function that generates the HTML for a single hero's row and its collapsible details. local function addHeroRows(hero_data, is_alt_fire) local base, level, suffix local hero_icon_wikitext = Icon._render(hero_data['Name']) -- Determine if this is a main weapon or an alt-fire and set the correct data. if is_alt_fire then base = hero_data.alt_fire.base level = hero_data.alt_fire.level suffix = " (" .. hero_data.alt_fire.suffix .. ")" else base = get_stat_value(hero_data, property) level = (hero_data["LevelScaling"] and hero_data["LevelScaling"][property] or 0) suffix = "" local shots = get_stat_value(hero_data, "BulletsPerShot") if shots == 0 then shots = 1 end local burst = get_stat_value(hero_data, "BulletsPerBurst") if burst == 0 then burst = 1 end local hit_once = get_stat_value(hero_data, "HitOnceAcrossAllBullets") if (property == "BulletDamage" and (shots * burst) > 1) then if (not hit_once) then base = base * shots * burst level = level * shots * burst end if (shots > 1) then suffix = suffix .. " (" .. shots .. " pellets)" end if (burst > 1) then suffix = suffix .. " (" .. burst .. " bullets)" end end end -- Build the main summary row with the hero's info and milestone values. local summary_row = mw.html.create("tr") local hero_cell_content = hero_icon_wikitext .. suffix summary_row:tag("td"):wikitext(hero_cell_content):done() summary_row :tag("td"):wikitext(string.format("%.2f", base)):done() :tag("td"):wikitext("+" .. string.format("%.2f", level)):done() for _, count in ipairs(milestones) do local total = base + level * count summary_row:tag("td"):wikitext(string.format("%.2f", total)):done() end -- Create the main container for the collapsible "Full Details" section. local details_id = "boon-details-" .. hero_data.Name .. "-" .. property .. (is_alt_fire and "-alt" or "") local collapsible_container = mw.html.create("div") :addClass("mw-collapsible mw-collapsed") :attr("id", details_id) :attr("style", "display: flex; flex-direction: column; align-items: flex-end; gap: 5px;") -- Style the "Show All" toggle to look like a theme-aware button. collapsible_container:tag("div") :addClass("mw-collapsible-toggle") :attr("style", "display: inline-block; padding: 2px 8px; border: 1px solid currentColor; border-radius: 1px; text-decoration: none;") :wikitext("Show All") -- Create the container for the expanded content. local content_div = mw.html.create("div"):addClass("mw-collapsible-content") -- Generate the three-column layout by distributing rows into mini-tables. local column1_rows, column2_rows, column3_rows = {}, {}, {} local rows_per_column = math.ceil(max_boons / 3) for i = 1, max_boons do local total = base + level * i local required_souls = pi_souls_list[i] local souls_formatted = string.format("%.1fK", (required_souls + 400) / 1000) local soul_display = Souls._render(souls_formatted) local row_html = string.format("<tr><td>%s</td><td style='text-align:right;'>%.2f</td></tr>", soul_display, total) if i <= rows_per_column then table.insert(column1_rows, row_html) elseif i <= rows_per_column * 2 then table.insert(column2_rows, row_html) else table.insert(column3_rows, row_html) end end local table_style = "border:none; background:transparent; margin:0 10px; min-width:150px;" local table1 = "<table style='" .. table_style .. "'>" .. table.concat(column1_rows) .. "</table>" local table2 = "<table style='" .. table_style .. "'>" .. table.concat(column2_rows) .. "</table>" local table3 = "<table style='" .. table_style .. "'>" .. table.concat(column3_rows) .. "</table>" -- Use a flexbox container to display the mini-tables side-by-side. local flex_container_style = "display:flex; justify-content:space-around; padding:10px 0;" content_div:wikitext("<div style='" .. flex_container_style .. "'>" .. table1 .. table2 .. table3 .. "</div>") collapsible_container:node(content_div) summary_row:tag("td"):node(collapsible_container) create_table:node(summary_row) end -- Iterate through each hero to generate their main row and an alt-fire row if applicable. for _, hero in ipairs(filtered_heroes) do addHeroRows(hero, false) -- Dynamically add alt-fire rows if the data exists if (property == "BulletDamage" and hero.Weapon and hero.Weapon.AltFire) then local alt_fire_data = hero.Weapon.AltFire local alt_fire_base = alt_fire_data.BulletDamage or 0 if alt_fire_base > 0 then -- Get scaling: prefer alt-fire specific key, fall back to main weapon scaling local alt_fire_level = (hero.LevelScaling and (hero.LevelScaling.BulletDamageAltFire or hero.LevelScaling[property])) or 0 -- Apply same pellet/burst logic as main weapon local shots = alt_fire_data.BulletsPerShot or 1 local burst = alt_fire_data.BulletsPerBurst or 1 local hit_once = alt_fire_data.HitOnceAcrossAllBullets or false local suffix = "alt fire" if (shots * burst) > 1 and not hit_once then alt_fire_base = alt_fire_base * shots * burst alt_fire_level = alt_fire_level * shots * burst if shots > 1 then suffix = suffix .. " " .. shots .. " pellets" end if burst > 1 then suffix = suffix .. " " .. burst .. " bullets" end end -- Add AOE indicator (without radius) if alt_fire_data.ExplosionRadius then suffix = suffix .. " AOE" end local alt_fire_hero_data = { Name = hero.Name, alt_fire = { base = alt_fire_base, level = alt_fire_level, suffix = suffix } } addHeroRows(alt_fire_hero_data, true) end end end -- If a title was provided, wrap the completed table in a theme-aware collapsible container. if title and title ~= '' then local wrapper = mw.html.create('table') :addClass('mw-collapsible mw-collapsed wikitable') :css('width', '100%') :css('margin-bottom', '1em') local header_row_wrapper = wrapper:tag('tr') header_row_wrapper:tag('th') :css('text-align', 'center') :wikitext('<span style="font-size: larger; font-weight: bold;">' .. title .. '</span><span class="mw-collapsible-toggle mw-collapsible-toggle-right">[Show]</span>') local content_row_wrapper = wrapper:tag('tr') content_row_wrapper:tag('td') :addClass('mw-collapsible-content') :node(create_table) return tostring(wrapper) else -- If no title is provided, return the table as before. return tostring(create_table) end end --[[ -- @function p.write_multishot_stats -- @description Outputs a wikitext bulleted list of heroes with spreadshot or burst fire weapons. -- @param {Frame} frame The MediaWiki frame object. -- @return {string} A formatted wikitext string. --]] p.write_multishot_stats = function(frame) -- Use the same smart sorting for this list. local function hero_sort(h1, h2) return get_sortable_name(h1.Name) < get_sortable_name(h2.Name) end local spreadshot_heroes = {} local burst_heroes = {} for _, hero in pairs(heroes_data) do local bullets_per_shot = get_stat_value(hero, "BulletsPerShot") local bullets_per_burst = get_stat_value(hero, "BulletsPerBurst") if (bullets_per_shot > 1 and not hero["IsDisabled"] and not hero["InDevelopment"]) then table.insert(spreadshot_heroes, hero) elseif (bullets_per_burst > 1 and not hero["IsDisabled"] and not hero["InDevelopment"]) then table.insert(burst_heroes, hero) end end table.sort(spreadshot_heroes, hero_sort) table.sort(burst_heroes, hero_sort) local multishot_str = "Weapons with multiple projectiles (Spreadshot):\n" for _, hero in ipairs(spreadshot_heroes) do local base = math.floor(get_stat_value(hero, "BulletDamage") * 100) / 100 local level = math.floor(hero["LevelScaling"]["BulletDamage"] * 100) / 100 local shots = get_stat_value(hero, "BulletsPerShot") local hit_once = get_stat_value(hero, "HitOnceAcrossAllBullets") local hero_icon = Icon._render(hero["Name"]) -- Checks for the special property and changes the output text accordingly. local stat_str if hit_once then stat_str = "* " .. hero_icon .. ": " .. shots .. " pellets, " .. base .. " total damage (hits only once), +" .. level .. " total\n" else stat_str = "* " .. hero_icon .. ": " .. shots .. " pellets, " .. base .. "/pellet, +" .. level .. "/pellet\n" end multishot_str = multishot_str .. stat_str end multishot_str = multishot_str .. "Weapons with burst fire (Burst Fire):\n" for _, hero in ipairs(burst_heroes) do local base = math.floor(get_stat_value(hero, "BulletDamage") * 100) / 100 local level = math.floor(hero["LevelScaling"]["BulletDamage"] * 100) / 100 local shots = get_stat_value(hero, "BulletsPerBurst") local hero_icon = Icon._render(hero["Name"]) local stat_str = "* " .. hero_icon .. ": " .. shots .. " rounds, " .. base .. "/bullet, +" .. level .. "/bullet\n" multishot_str = multishot_str .. stat_str end return multishot_str end return p
Summary:
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
Pages included on this page:
Module:Dictionary
(
edit
)
Module:Icon
(
edit
)
Module:Icon/data
(
edit
)
Module:Lang
(
edit
)
Module:LevelTables
(
edit
)
Module:LevelTables/doc
(
edit
)
Module:Souls
(
edit
)
Module:Utilities
(
edit
)
Data:Dictionary
(
edit
)
Data:HeroData.json
(
view source
) (⧼restriction-level-bot+⧽)
Data:LangCodes.json
(
view source
) (⧼restriction-level-bot+⧽)
Data:ResourceLookup.json
(
edit
)
Data:ResourceLookup2.json
(
edit
)
Data:SoulUnlockData.json
(
view source
) (⧼restriction-level-bot+⧽)