Editing
Module
:
Sandbox/Monster Domosed/AbilityTable
Give feedback
Jump to navigation
Jump to search
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!
-- Renders sortable wikitables of abilities filtered by a named stat. local Lists = require("Module:AbilityTable/Lists") local Notes = require("Module:AbilityTable/Notes") local Renderers = require("Module:Sandbox/Monster Domosed/AbilityTable/ComplexRenderers") local GameData = require("Module:GameData") local p = {} local extra_columns = { ["barrier"] = { headers = { "Barrier" }, get_cells = Renderers.RenderBarrier, }, ["charges"] = { headers = { "Charges", "Time Between Charges", "Notes" }, get_cells = Renderers.RenderCharges, }, ["melee"] = { headers = { "Base Damage", "Light Melee Scaling", "Notes" }, get_cells = Renderers.RenderMelee, }, ["heavymelee"] = { headers = { "Base Damage", "Heavy Melee Scaling", "Notes" }, get_cells = Renderers.RenderHeavyMelee, }, ["scalingspirit"] = { headers = { "Spirit Scaling", "Notes" }, get_cells = Renderers.RenderSpiritScaling, }, ["healreduce"] = { headers = { "Heal Reduction", "Notes" }, get_cells = Renderers.RenderHealReduce, }, ["moveslow"] = { headers = { "Move Slow", "Notes" }, get_cells = Renderers.RenderMoveSlow, }, ["dashslow"] = { headers = { "Dash Slow", "Notes" }, get_cells = Renderers.RenderDashSlow, }, ["extradebuffresist"] = { headers = { "Debuff Resist" }, get_cells = Renderers.RenderExtraDebuffResist, }, } -- ============================================================ -- Note enrichment -- ============================================================ -- Merges auto-generated and human notes for full-table mode. -- Capitalizes human note, joins with ". ", ensures trailing period. local function enrich_notes_full(extra, human_note) if human_note then human_note = human_note:sub(1,1):upper() .. human_note:sub(2) extra["Notes"] = extra["Notes"] and (extra["Notes"] .. ". " .. human_note) or human_note end if extra["Notes"] and extra["Notes"]:sub(-1) ~= "." then extra["Notes"] = extra["Notes"] .. "." end end -- Merges auto-generated and human notes for compact mode. -- Decapitalizes human note (skips T{N} prefixes like "T1 slow"), -- joins with "; ", strips trailing period. local function enrich_notes_compact(extra, human_note) if human_note then local first = human_note:sub(1,1) if first:match("%u") and not human_note:sub(2,2):match("%d") then human_note = first:lower() .. human_note:sub(2) end if human_note:sub(-1) == "." then human_note = human_note:sub(1, -2) end extra["Notes"] = extra["Notes"] and (extra["Notes"] .. "; " .. human_note) or human_note end end -- ============================================================ -- Ability views -- -- In-game Ability may containe 1 or more records in AbilityData.json -- Example: Vexing Bolt is 2 records (Launch Bolt + Redirect Bolt). -- "Ability View" is a main-record + any relevant sub-ability records. -- ============================================================ local _views = nil -- card key -> { view = record, heroName = ..., abilityNum = ... } local _order = nil -- array of card keys, sorted by hero then slot local function build_views() if _views then return _views, _order end _views, _order = {}, {} local hero_data = mw.loadJsonData("Data:HeroData.json") local active_heroes = {} for _, hero in pairs(hero_data) do if type(hero) == "table" and hero["IsDisabled"] == false and type(hero["Name"]) == "string" and (hero["IsSelectable"] == nil or hero["IsSelectable"] ~= false) then active_heroes[hero["Name"]] = true end end local ability_data = mw.loadJsonData(GameData.Dataset.ABILITIES) local cards = mw.loadJsonData("Data:AbilityCards.json") local card_of = {} for _, hero_entry in pairs(cards) do if type(hero_entry) == "table" then local hero_name = hero_entry["Name"] if hero_name and active_heroes[hero_name] then for slot_str, ability in pairs(hero_entry) do local slot_num = tonumber(slot_str) local key = type(ability) == "table" and ability["Key"] or nil if slot_num and key and ability_data[key] then card_of[key] = { heroName = hero_name, abilityNum = slot_num } end end end end end -- Attach each non-card record to the longest card key it prefixes. local subs = {} for key, record in pairs(ability_data) do if not card_of[key] and record["IsDisabled"] ~= true then local parent = nil for card_key in pairs(card_of) do if key:sub(1, #card_key + 1) == card_key .. "_" and (parent == nil or #card_key > #parent) then parent = card_key end end if parent then subs[parent] = subs[parent] or {} table.insert(subs[parent], record) end end end for key, info in pairs(card_of) do local view = {} for k, v in pairs(ability_data[key]) do view[k] = v end view["_subabilities"] = subs[key] _views[key] = { view = view, heroName = info.heroName, abilityNum = info.abilityNum } table.insert(_order, key) end table.sort(_order, function(a, b) local ia, ib = _views[a], _views[b] if ia.heroName ~= ib.heroName then return ia.heroName < ib.heroName end return ia.abilityNum < ib.abilityNum end) return _views, _order end -- ============================================================ -- Membership -- Pieces of code that help in telling "is ability X a member of list Y". -- main function is: p.is_member() -- ============================================================ -- Memoized name-exclusion set per list (from Lists.exclude_abilities). local _excluded = {} local function excluded_set(stat) if _excluded[stat] then return _excluded[stat] end local set = {} for _, name in ipairs(Lists.exclude_abilities[stat] or {}) do set[name] = true end _excluded[stat] = set return set end -- Per-list rules that can't be expressed as prop lists or exclusions. -- Returns true to keep the ability, false to drop it. local function special_ok(stat, record, ability_num) if stat == "dispelmagic" and ability_num == 4 then return false end return true end -- Public: does ability `ability_key` belong to list `stat`? function p.is_member(stat, ability_key, ability_num) local props = Lists.lists[stat] if not props then return false end local views = build_views() local entry = views[ability_key] local view = entry and entry.view if not view then view = mw.loadJsonData(GameData.Dataset.ABILITIES)[ability_key] if not view then return false end end local name = view["Name"] if name and excluded_set(stat)[name] then return false end ability_num = ability_num or (entry and entry.abilityNum) if not special_ok(stat, view, ability_num) then return false end return GameData.entity_matches(view, props) end -- ============================================================ -- Entry point -- ============================================================ function p.render(frame) local stat = mw.text.trim(frame.args[1] or ""):lower() local internal_keys = Lists.lists[stat] if not internal_keys then return '<span class="error">AbilityTable: unknown list "' .. stat .. '".</span>' end local hide_set = {} local hide_param = mw.text.trim(frame.args["hide"] or "") if hide_param ~= "" then for col in hide_param:gmatch("[^,]+") do hide_set[mw.text.trim(col)] = true end end local compact = mw.text.trim(frame.args["compact"] or "") ~= "" local collapsed = mw.text.trim(frame.args["collapsed"] or ""):lower() local views, order = build_views() local spec = extra_columns[stat] local headers = spec and spec.headers or {} local get_cells = spec and spec.get_cells or function() return {} end local stat_notes = Notes[stat] local filtered = {} for _, key in ipairs(order) do if p.is_member(stat, key) then table.insert(filtered, key) end end if #filtered == 0 then return "No abilities found for list: " .. stat end local lists_url = mw.uri.fullUrl("Module:AbilityTable/Lists") local notes_url = mw.uri.fullUrl("Module:AbilityTable/Notes", { action = "edit" }) local disclaimer = frame:preprocess( '<div class="ability-table-disclaimer">' .. 'This is a dynamic list ' .. '<span class="ability-table-list-name">"' .. stat .. '"</span>. ' .. '[' .. tostring(lists_url) .. ' Edit contents]' .. ' Β· ' .. '[' .. tostring(notes_url) .. ' Edit notes].' .. '</div>' ) if compact then local hero_order = {} local hero_abilities = {} for _, key in ipairs(filtered) do local hero = views[key].heroName if not hero_abilities[hero] then table.insert(hero_order, hero) hero_abilities[hero] = {} end table.insert(hero_abilities[hero], views[key].view) end local items = {} for _, hero in ipairs(hero_order) do local hero_cell = frame:expandTemplate{ title = "HeroIcon", args = { hero } } local ability_parts = {} for _, ability in ipairs(hero_abilities[hero]) do local name = ability["Name"] local ability_cell = frame:expandTemplate{ title = "AbilityIcon", args = { name } } local extra = get_cells(ability) enrich_notes_compact(extra, stat_notes and stat_notes[name]) local note = extra["Notes"] if note then table.insert(ability_parts, ability_cell .. " (" .. note .. ")") else table.insert(ability_parts, ability_cell) end end table.insert(items, "<li>" .. hero_cell .. ": " .. table.concat(ability_parts, ", ") .. "</li>") end return disclaimer .. "<ul>" .. table.concat(items) .. "</ul>" end local has_notes_header = false local final_headers = {} for _, h in ipairs(headers) do if h == "Notes" then has_notes_header = true elseif not hide_set[h] then table.insert(final_headers, h) end end local needs_notes = (has_notes_header or stat_notes ~= nil) and not hide_set["Notes"] if needs_notes then table.insert(final_headers, "Notes") end local table_class = 'wikitable sortable ability-table' if collapsed ~= "" then table_class = table_class .. ' mw-collapsible' if collapsed == "yes" or collapsed == "true" or collapsed == "1" then table_class = table_class .. ' mw-collapsed' end end local out = {} table.insert(out, disclaimer) table.insert(out, '{| class="' .. table_class .. '"') local header_row = '! class="ability-table-col-hero" | Hero' .. ' !! class="ability-table-col-ability" | Ability' for _, h in ipairs(final_headers) do header_row = header_row .. ' !! class="ability-table-col-' .. h:lower():gsub("%s+", "-") .. '" | ' .. h end table.insert(out, header_row) -- Pre-compute how many physical table rows each ability occupies. A cell -- may return an array (e.g. multiple Spirit-scaling instances) β one row -- per element; scalar cells then rowspan across the ability's rows. The -- hero cell must span every row its abilities occupy combined. local hero_rows = {} local info = {} for _, key in ipairs(filtered) do local entry = views[key] local ability = entry.view local extra = get_cells(ability) enrich_notes_full(extra, stat_notes and stat_notes[ability["Name"]]) local row_count = 1 for _, header in ipairs(final_headers) do local v = extra[header] if type(v) == "table" then row_count = math.max(row_count, #v) end end local hero = entry.heroName hero_rows[hero] = (hero_rows[hero] or 0) + row_count info[key] = { ability = ability, hero = hero, extra = extra, row_count = row_count } end local hero_seen = {} for _, key in ipairs(filtered) do local rec = info[key] local ability = rec.ability local hero = rec.hero local extra = rec.extra local R = rec.row_count local hero_total = hero_rows[hero] local ability_cell = frame:expandTemplate{ title = "AbilityIcon", args = { ability["Name"] } } -- One entry per physical row this ability occupies. local rows = {} for i = 1, R do rows[i] = {} end -- Lead cells (hero + ability) live on the ability's first row and span -- its remaining rows on down. if not hero_seen[hero] then local hero_cell = frame:expandTemplate{ title = "HeroIcon", args = { hero } } if hero_total > 1 then table.insert(rows[1], 'rowspan="' .. hero_total .. '" class="ability-table-cell-hero" | ' .. hero_cell) else table.insert(rows[1], 'class="ability-table-cell-hero" | ' .. hero_cell) end hero_seen[hero] = true end if R > 1 then table.insert(rows[1], 'rowspan="' .. R .. '" class="ability-table-cell-ability" | ' .. ability_cell) else table.insert(rows[1], 'class="ability-table-cell-ability" | ' .. ability_cell) end -- Stat columns: arrays emit one cell per row, scalars get rowspanned. for _, header in ipairs(final_headers) do local cls = "ability-table-cell-" .. header:lower():gsub("%s+", "-") local v = extra[header] if type(v) == "table" then for i = 1, R do local content = v[i] ~= nil and tostring(v[i]) or "β" table.insert(rows[i], 'class="' .. cls .. '" | ' .. content) end else local content = v ~= nil and tostring(v) or "β" if R > 1 then table.insert(rows[1], 'rowspan="' .. R .. '" class="' .. cls .. '" | ' .. content) else table.insert(rows[1], 'class="' .. cls .. '" | ' .. content) end end end for i = 1, R do table.insert(out, "|-") table.insert(out, "| " .. table.concat(rows[i], " || ")) end end table.insert(out, "|}") return table.concat(out, "\n") 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
Page included on this page:
Module:Sandbox/Monster Domosed/AbilityTable/doc
(
view source
)
Navigation menu
Personal tools
Create account
Log in
associated-pages
Module
Discussion
English
Views
Read
Edit source
History
Tools
Search
Discord
Discord
Navigation
Main page
About us
Random page
Special pages
Recent changes
Lifesteal
4h ago
Β·
Gammaton32
Lifesteal
4h ago
Β·
Gammaton32
Venator
5h ago
Β·
Sleuth
Graves/Lore
5h ago
Β·
Dinjoralo
Show more...
Interface Admin
Common CSS
Vector CSS
Minerva CSS
Citizen CSS
Brown theme CSS
Gadgets
Gadgets definition
Editing
How to edit
Style guide
All guides
New files
Unresolved feedback
Tools
What links here
Related changes
Page information