Module:Test
From Elwiki
Documentation for this module may be created at Module:Test/doc
-- pystart
require('Module:CommonFunctions');
local getArgs = require('Module:Arguments').getArgs
local inspect = require('Module:Inspect').inspect
local p = {}
-- Main process
function p.main(frame)
local args = getArgs(frame)
local out
function inArgs(key)
if args[key] ~= nil then
return true
end
end
local modes = { 'PvE', 'PvP' }
-- Define the schema for the table
local tableSchema = {}
for _, mode in ipairs(modes) do
tableSchema[mode] = {}
end
function forEach(func)
for _, mode in ipairs(modes) do
func(mode)
end
end
function forEachDamageType(func)
for _, damage_type in ipairs({ 'min', 'max' }) do
func(damage_type)
end
end
-- Function to create a new table with the desired schema
function createDamageDataTable()
local newTable = {}
for key, value in pairs(tableSchema) do
if type(value) == "table" then
newTable[key] = {}
end
end
return newTable
end
-- User requested options
local OPTIONS = {
do_table = args[1] == 'true',
character = args[2] or args.char or 'Elsword',
format = args.format ~= 'false',
no_max = args.no_max == 'true',
is_append = args.append ~= nil,
append_index = args.append and tonumber(split(args.append)[1]),
append_name = args.append and split(args.append)[2],
combine_suffix = args.combine_suffix and (' ' .. args.combine_suffix) or '',
combine = (function()
local output = {}
if not args.combine then
return nil
end
for _, passive_key in ipairs(split(args.combine)) do
table.insert(output, tonumber(passive_key))
end
if #output == 0 then
return nil
end
return output
end)(),
perm_buff = {
PvE = args.perm_buff or 1,
PvP = args.pvp_perm_buff or args.perm_buff or 1
},
bug = args.bug == 'true',
dump = args.dump == 'true',
dump_table_data = args.dump_table_data == 'true',
dump_parsed = args.dump_parsed == 'true',
prefix = args.prefix,
use_avg = args.use_avg == 'true',
dmp = args.dmp == 'true' and 3 or args.dmp
}
-- Define a table with parsed damage information of all kind.
local BASIC_DAMAGE = createDamageDataTable()
-- Define a table with trait names and their values to apply.
local TRAITS = {
-- An empty trait so we keep the original values there.
{
key = '',
name = 'Normal',
value = 1
},
{
key = 'enhanced',
name = 'Enhanced (Trait)',
value = args.enhanced ~= nil and 0.8
},
{
key = 'empowered',
name = 'Empowered',
value = args.empowered == 'true' and 1.2 or tonumber(args.empowered) or false
},
{
key = 'useful',
name = 'Useful',
value = (args.hits_useful or args.avg_hits_useful) and (args.useful_penalty or args.useful or 0.8) or false
},
{
key = 'heavy',
name = 'Heavy',
value = args.heavy ~= nil and 1.44
}
}
function eval(s)
return frame:preprocess('{{#expr:' .. s .. '}}')
end
-- A table with user-requested passive skills (empty by default).
local PASSIVES = {}
-- A table with non-numeric arguments to split.
local TO_SPLIT = { 'append', 'awk_alias' }
for k, v in pairs(args) do
if string.find(k, 'passive') then
--[[
Fix up the passives and put them into a separate table.
|passive1=... |passive2=... -> { passive1, passive2 }
--]]
local passive_name = v
local is_custom = string.find(k, '_define') ~= nil
local passive_index = string.match(k, "%d")
local passive_values = split(is_custom and v or
frame:preprocess('{{:' .. passive_name .. '}}{{#arrayprint:' .. passive_name .. '}}'));
if is_custom then
passive_name = passive_values[#passive_values]
passive_values[#passive_values] = nil
end
PASSIVES[tonumber(passive_index)] = {
name = passive_name,
value = passive_values[1],
value_pvp = passive_values[2],
alias = args['alias' .. passive_index] or (passive_index == OPTIONS.append_index and OPTIONS.append_name),
suffix = args['suffix' .. passive_index] and (' ' .. args['suffix' .. passive_index]) or '',
prefix = args['prefix' .. passive_index] and (args['prefix' .. passive_index] .. ' ') or '',
exist = frame:preprocess('{{#ifexist:' .. passive_name .. '|true|false}}') == 'true'
}
elseif not string.find(v, '[a-hj-zA-HJ-Z]+') then
--[[
Change how args are received.
dmg = 500, 700, 800 (string) -> dmg = { 500, 700, 800 } (table)
--]]
local split_values = split(v)
-- Perform automatic math on each value.
for k2, v2 in pairs(split_values) do
if not string.find(v, '[a-zA-Z]+') then
split_values[k2] = eval(v2)
end
end
args[k] = split_values
elseif inArrayHasValue(k, TO_SPLIT) then
args[k] = split(v)
end
end
-- Set basic hit count to 1 for all damage.
for k, v in ipairs(args.dmg) do
if not args.hits then
args.hits = {}
end
if not args.hits[k] then
args.hits[k] = 1
end
end
-- Set basic hit count to 1 for all cancel damage.
if args.cancel_dmg then
for k, v in ipairs(args.cancel_dmg) do
if not args.cancel_hits then
args.cancel_hits = {}
end
if not args.cancel_hits[k] then
args.cancel_hits[k] = 1
end
end
end
-- Store a configuration that will tell the main function how to behave given different inputs.
-- It will always take the first value if available. If not, fall back to the other (recursively).
local BASE_DAMAGE_CONFIG = {
total_damage = {
damage_numbers = { 'dmg' },
hit_counts = { 'hits' },
provided = { 'dmg' }
},
total_damage_awk = {
damage_numbers = { 'awk_dmg', 'dmg' },
hit_counts = { 'awk_hits', 'hits' },
provided = { 'awk_dmg', 'awk_hits' }
},
avg_damage = {
damage_numbers = { 'dmg' },
hit_counts = { 'avg_hits', 'hits' },
provided = { 'avg_hits' }
},
avg_damage_awk = {
damage_numbers = { 'awk_dmg', 'dmg' },
hit_counts = { 'avg_awk_hits', 'awk_hits', 'avg_hits', 'hits' },
provided = { 'avg_awk_hits', args.awk_dmg and 'avg_hits' or nil }
},
-- Store the logic for Useful traits
total_damage_useful = {
damage_numbers = { 'dmg' },
hit_counts = { 'hits_useful', 'hits' },
provided = { 'hits_useful' }
},
total_damage_awk_useful = {
damage_numbers = { 'awk_dmg', 'dmg' },
hit_counts = { 'awk_hits_useful', 'awk_hits', 'hits_useful', 'hits' },
provided = { 'awk_hits_useful' }
},
avg_damage_useful = {
damage_numbers = { 'dmg' },
hit_counts = { 'avg_hits_useful', 'hits_useful', 'avg_hits', 'hits' },
provided = { 'avg_hits_useful' }
},
avg_damage_awk_useful = {
damage_numbers = { 'awk_dmg', 'dmg' },
hit_counts = { 'avg_awk_hits_useful', 'avg_awk_hits', 'hits_useful', 'hits' },
provided = { 'avg_awk_hits_useful' }
},
}
local DAMAGE_CONFIG = {}
function handleCancel()
local processed_keys = {}
for config_key, config_value in pairs(BASE_DAMAGE_CONFIG) do
if not config_key:match('cancel_') then
local new_config_value = {}
for arg_table_key, arg_table in pairs(config_value) do
local new_arg_table = {}
for _, arg in ipairs(arg_table) do
table.insert(new_arg_table, 'cancel_' .. arg)
end
new_config_value[arg_table_key] = new_arg_table
end
local new_key = 'cancel_' .. config_key
DAMAGE_CONFIG[new_key] = new_config_value
processed_keys[new_key] = true
end
end
return processed_keys
end
if args.cancel_dmg then
handleCancel()
DAMAGE_CONFIG = table.fuse(BASE_DAMAGE_CONFIG, DAMAGE_CONFIG)
else
DAMAGE_CONFIG = BASE_DAMAGE_CONFIG
end
-- Helper function to check if a table is not empty
local function isTableNotEmpty(tbl)
return next(tbl) ~= nil
end
-- Function to apply inheritance for a specific damage type and argument
local function applyInheritance(mainArgValues, inheritArg, mainArgValue, inheritValue, mainKeyPrefixed, inheritKeyPrefixed)
if mainArgValue == '' then
return inheritValue
elseif mainArgValue and string.find(mainArgValue, 'i') and inheritValue then
return eval(mainArgValue:gsub('i', inheritValue))
end
return mainArgValue
end
-- Function to apply inheritance for a specific argument key
local function applyInheritanceForKey(args, prefix, damageType, damageTypeIndex, argTable)
local mainKey = argTable[1] .. damageType
local mainKeyPrefixed = prefix .. mainKey
local mainArgValues = args[mainKeyPrefixed]
if mainArgValues then
local i = 1
local cancelDmgLen = args.cancel_dmg and #args.cancel_dmg or 0
while i <= (#args.dmg + cancelDmgLen) do
local mainArgValue = mainArgValues[i]
for ix, inheritKey in ipairs(argTable) do
local inheritArg = (inheritKey .. damageType) ~= mainKey and (args[prefix .. inheritKey .. damageType] or args[inheritKey .. damageType]) or (args[prefix .. inheritKey] or args[inheritKey])
if inheritArg and inheritArg[i] and inheritArg[i] ~= '' and ix ~= 1 then
mainArgValues[i] = applyInheritance(mainArgValues, inheritArg, mainArgValue, inheritArg[i])
break
end
end
i = i + 1
end
end
end
-- Inherits values from args if not provided, but usage suggests that they're meant to be generated.
function inherit(mode)
local prefix = mode == 'PvE' and '' or string.lower(mode .. '_')
for configKey, configValue in pairs(DAMAGE_CONFIG) do
for argTableKey, argTable in pairs(configValue) do
if argTableKey ~= 'provided' and isTableNotEmpty(argTable) then
for damageTypeIndex, damageType in ipairs({ '', '_min', '_max' }) do
applyInheritanceForKey(args, prefix, damageType, damageTypeIndex, argTable)
end
end
end
end
end
forEach(inherit)
local DAMAGE_PARSED = createDamageDataTable()
function parseConfig(mode)
local prefix = mode == 'PvE' and '' or string.lower(mode .. '_')
for config_key, config_value in pairs(DAMAGE_CONFIG) do
for k, v in pairs(config_value) do
local output_value = {}
-- When both min and max are found, we need to break from the loop.
local isValueFound = { min = false, max = false }
for _, v2 in ipairs(v) do -- This array holds the argument names with fallbacks
forEachDamageType(function(damage_type)
-- If there already is a value for this damage type (min or max), do not continue.
if isValueFound[damage_type] == true then
return
end
local arg_from_template =
args[prefix .. v2 .. '_' .. damage_type]
or args[v2 .. '_' .. damage_type]
or args[prefix .. v2]
or args[v2];
if arg_from_template ~= nil then
if k == 'provided' then
output_value = true
-- Do not generate total_damage values at all if the skill can't reach them.
if string.find(config_key, 'total_') and OPTIONS.no_max then
output_value = false
end
else
if type(output_value) ~= "table" then
output_value = {}
end
output_value[damage_type] = arg_from_template
end
-- Mark the value as found.
isValueFound[damage_type] = true
else
if k == 'provided' then
output_value = false
else
output_value[damage_type] = {}
end
end
end)
-- Both values found, we can now break the loop.
if isValueFound.min and isValueFound.max then
break
end
end
if DAMAGE_PARSED[mode][config_key] == nil then
DAMAGE_PARSED[mode][config_key] = {}
end
DAMAGE_PARSED[mode][config_key][k] = output_value
end
end
end
forEach(parseConfig)
-- Detected "count", for skills like Clementine, Enough Mineral, etc.
function doEachDamage()
local WITH_EACH = table.deep_copy(DAMAGE_PARSED)
for mode, mode_content in pairs(DAMAGE_PARSED) do
for damage_key, damage_value in pairs(mode_content) do
if string.find(damage_key, 'total_') then
local new_value = table.deep_copy(damage_value)
forEachDamageType(function(damage_type)
for k, hit_count in ipairs(new_value.hit_counts[damage_type]) do
hit_count = hit_count == '' and 1 or hit_count
new_value.hit_counts[damage_type][k] = hit_count *
((string.find(damage_key, 'awk_') and args.awk_count) and args.awk_count[1] or args.count[1])
end
end)
WITH_EACH[mode][damage_key:gsub("total_", "each_")] = damage_value
WITH_EACH[mode][damage_key] = new_value
end
end
end
return WITH_EACH
end
if args.count then
DAMAGE_PARSED = doEachDamage()
end
function doBasicDamage()
for mode, mode_content in pairs(DAMAGE_PARSED) do
for damage_key, damage_value in pairs(mode_content) do
forEachDamageType(function(damage_type)
local i = 1
local output = 0
-- Check if to even generate the damage.
if damage_value.provided then
-- Loop through damage numbers and multiply them with hits.
for k, damage_number in ipairs(damage_value.damage_numbers[damage_type]) do
local hit_count = damage_value.hit_counts[damage_type][i]
hit_count = hit_count == '' and 1 or hit_count
output = output + (damage_number * hit_count)
i = i + 1
end
-- Write the result to a separate object.
if not BASIC_DAMAGE[mode][damage_key] then
BASIC_DAMAGE[mode][damage_key] = {}
end
BASIC_DAMAGE[mode][damage_key][damage_type] = output
end
end)
end
end
end
-- doBasicDamage()
-- -- Adding missing cancel part damage to full, so that repetition wouldn't be a problem.
-- function addCancelDamage()
-- for mode, mode_content in pairs(BASIC_DAMAGE) do
-- for damage_key, damage_value in pairs(mode_content) do
-- local cancel_candidate = BASIC_DAMAGE[mode]['cancel_' .. damage_key]
-- forEachDamageType(function(damage_type)
-- if not string.find(damage_key, 'cancel_') and cancel_candidate then
-- BASIC_DAMAGE[mode][damage_key][damage_type] = damage_value[damage_type] +
-- cancel_candidate[damage_type]
-- end
-- end)
-- end
-- end
-- end
-- if args.cancel_dmg then
-- addCancelDamage()
-- end
-- local WITH_TRAITS = createDamageDataTable()
-- function doTraits()
-- -- Handle traits here
-- for mode, mode_content in pairs(BASIC_DAMAGE) do
-- for damage_key, damage_value in pairs(mode_content) do
-- for _, trait in pairs(TRAITS) do
-- --[[
-- Suffix all damage values with existing traits.
-- Useful already has the prefix, so only multiply with its value.
-- Also, we don't want other traits to multiply with Useful,
-- so we skip those situations, as impossible in-game.
-- --]]
-- if (trait.value and trait.key ~= 'useful') or (string.find(damage_key, 'useful') and trait.key == 'useful') then
-- forEachDamageType(function(damage_type)
-- local new_key = damage_key ..
-- ((trait.key == 'useful' or trait.key == '') and "" or ('_' .. trait.key));
-- if not WITH_TRAITS[mode][new_key] then
-- WITH_TRAITS[mode][new_key] = {}
-- end
-- WITH_TRAITS[mode][new_key][damage_type] = damage_value[damage_type] * trait.value
-- end)
-- end
-- end
-- end
-- end
-- end
-- doTraits()
-- local WITH_PASSIVES = createDamageDataTable()
-- --[[
-- Generates passives with every possible combinations of all subsets.
-- For example: 3 passives are given, so it will generate the following:
-- (1), (2), (3), (1, 2), (1, 3), (1, 2, 3), (2, 3)
-- ]]
-- function doPassives()
-- for mode, mode_content in pairs(WITH_TRAITS) do
-- for damage_key, damage_value in pairs(mode_content) do
-- forEachDamageType(function(damage_type)
-- local combinations = { {} }
-- for passive_key, passive in pairs(PASSIVES) do
-- local count = #combinations
-- for i = 1, count do
-- local new_combination = { unpack(combinations[i]) }
-- table.insert(new_combination, passive_key)
-- table.insert(combinations, new_combination)
-- end
-- end
-- for _, combination in pairs(combinations) do
-- local passive_multiplier = 1
-- local name_suffix = ''
-- if #combination > 0 then
-- table.sort(combination)
-- for _, passive_key in pairs(combination) do
-- passive_multiplier = passive_multiplier *
-- tonumber(PASSIVES[passive_key][mode == 'PvE' and 'value' or 'value_pvp'])
-- name_suffix = name_suffix .. '_passive' .. passive_key
-- end
-- end
-- local new_damage_key = damage_key .. name_suffix;
-- if not WITH_PASSIVES[mode][new_damage_key] then
-- WITH_PASSIVES[mode][new_damage_key] = {}
-- end
-- WITH_PASSIVES[mode][new_damage_key][damage_type] = damage_value[damage_type] * passive_multiplier
-- end
-- end)
-- end
-- end
-- end
-- doPassives()
-- local RANGE = {
-- min_count = args.range_min_count and args.range_min_count[1] or 1,
-- max_count = args.range_max_count and args.range_max_count[1] or 1,
-- PvE = {
-- min = args.range_min and args.range_min[1] or 1,
-- max = args.range_max and args.range_max[1] or 1
-- },
-- PvP = {
-- min = args.range_min and (args.range_min[2] or args.range_min[1]) or 1,
-- max = args.range_max and (args.range_max[2] or args.range_max[1]) or 1
-- }
-- }
-- local WITH_RANGE = createDamageDataTable()
-- function doDamageBuffRange()
-- -- Handle damage range here
-- for mode, mode_content in pairs(WITH_PASSIVES) do
-- for damage_key, damage_value in pairs(mode_content) do
-- WITH_RANGE[mode][damage_key] = { min = 0, max = 0 }
-- forEachDamageType(function(damage_type)
-- local range_count = RANGE[damage_type .. '_count'];
-- local range_multiplier = RANGE[mode][damage_type];
-- local final_range_multiplier = (1 + ((range_multiplier - 1) * range_count));
-- local perm_buff = OPTIONS.perm_buff[mode];
-- local final_damage_value = damage_value[damage_type] * final_range_multiplier * perm_buff;
-- WITH_RANGE[mode][damage_key][damage_type] = not OPTIONS.format and final_damage_value or
-- formatDamage(final_damage_value)
-- end)
-- end
-- end
-- end
-- doDamageBuffRange()
-- local FINAL_DAMAGE = WITH_RANGE
-- -- Helper function to iterate over traits.
-- function checkTraits(settings)
-- local output
-- if not settings then
-- output = false
-- else
-- output = settings.output or {}
-- end
-- for trait_index, trait in ipairs(TRAITS) do
-- if trait.value ~= false and trait_index ~= 1 then
-- if settings and type(settings.action) == 'function' then
-- settings.action(trait, output, settings)
-- else
-- return true
-- end
-- end
-- end
-- return output
-- end
-- -- Helper function to iterate over passives.
-- function checkPassives(settings)
-- local output = settings.output or {}
-- local PASSIVES_WITH_COMBINED = table.deep_copy(PASSIVES)
-- -- Handle combined passives properly.
-- if OPTIONS.combine then
-- table.insert(PASSIVES_WITH_COMBINED, {
-- is_combined = true
-- })
-- end
-- for passive_index, passive in ipairs(PASSIVES_WITH_COMBINED) do
-- if (not OPTIONS.is_append or (OPTIONS.is_append and OPTIONS.append_index ~= passive_index)) and (not inArrayHasValue(passive_index, OPTIONS.combine or {}) or inArrayHasValue(passive_index, args.display_separated or {})) then
-- if type(settings.action) == 'function' then
-- settings.action(passive, output, passive_index)
-- else
-- return true
-- end
-- end
-- end
-- return output
-- end
-- -- Generate the table
-- local TABLE = mw.html.create('table'):attr({
-- cellpadding = 5,
-- border = 1,
-- style = 'border-collapse: collapse; text-align: center',
-- class = 'colortable-' .. OPTIONS.character
-- })
-- -- Our table structure
-- local TABLE_CONTENT = {
-- {
-- type = 'extra',
-- text = { 'Average' },
-- is_visible = OPTIONS.no_max,
-- no_damage = true
-- },
-- {
-- type = 'passives',
-- text = checkPassives({
-- output = { 'Base' },
-- action = function(passive, output)
-- if passive.is_combined then
-- -- Handling combined passive header name.
-- local combo = {}
-- for _, passive_key in ipairs(OPTIONS.combine) do
-- passive = PASSIVES[passive_key]
-- table.insert(combo,
-- link(passive.name, passive.alias, passive.prefix, passive.suffix, passive.exist))
-- end
-- table.insert(output, table.concat(combo, '/') .. OPTIONS.combine_suffix)
-- else
-- table.insert(output,
-- link(passive.name, passive.alias, passive.prefix, passive.suffix, passive.exist))
-- end
-- end
-- }),
-- keywords = checkPassives({
-- action = function(passive, output, passive_index)
-- if passive.is_combined then
-- -- Handling combined passive damage cells.
-- table.insert(output, sortPassives('passive' .. table.concat(OPTIONS.combine, '_passive')))
-- else
-- table.insert(output, 'passive' .. passive_index)
-- end
-- end
-- }),
-- is_visible = not OPTIONS.no_max or #PASSIVES > 0
-- },
-- {
-- type = 'passive_appended',
-- text = {
-- 'Normal',
-- OPTIONS.is_append and
-- link(PASSIVES[OPTIONS.append_index].name,
-- PASSIVES[OPTIONS.append_index].alias or OPTIONS.append_name or nil,
-- PASSIVES[OPTIONS.append_index].prefix,
-- PASSIVES[OPTIONS.append_index].suffix,
-- PASSIVES[OPTIONS.append_index].exist
-- )
-- },
-- keywords = { OPTIONS.is_append and ('passive' .. OPTIONS.append_index) or nil },
-- is_visible = OPTIONS.is_append or false
-- },
-- {
-- type = 'awakening',
-- text = { 'Regular', (function()
-- if OPTIONS.dmp then
-- return link('Dynamo Point System', 'Dynamo Configuration', nil,
-- OPTIONS.dmp ~= 'false' and ('(' .. OPTIONS.dmp .. ' DMP)'))
-- elseif args.awk_alias then
-- return link(unpack(args.awk_alias))
-- end
-- return link('Awakening Mode')
-- end)()
-- },
-- keywords = { 'awk' },
-- keyword_next_to_main_key = true,
-- is_visible = inArgs('awk_dmg') or inArgs('awk_hits') or inArgs('avg_awk_hits') or false
-- },
-- {
-- type = 'traits',
-- text = checkTraits({
-- output = { 'Normal' },
-- action = function(trait, output)
-- table.insert(output, trait.name)
-- end
-- }),
-- keywords = checkTraits({
-- action = function(trait, output)
-- table.insert(output, trait.key)
-- end
-- }),
-- is_visible = checkTraits()
-- },
-- {
-- type = 'cancel',
-- text = {
-- 'Cancel', 'Full'
-- },
-- keywords = { 'cancel' },
-- keyword_first = true,
-- is_visible = inArgs('cancel_dmg')
-- },
-- {
-- type = 'hit_count',
-- text = {
-- (inArgs('count') and not OPTIONS.use_avg) and
-- (table.concat({ 'Per', args.count_name or 'Group' }, ' ')) or 'Average',
-- 'Max'
-- },
-- keywords = (function()
-- if inArgs('avg_hits') or inArgs('count') then
-- return { (inArgs('count') and not OPTIONS.use_avg) and 'each' or 'avg', 'total' }
-- end
-- return { 'total' }
-- end)(),
-- is_visible = ((inArgs('avg_hits') or inArgs('count')) and not OPTIONS.no_max) or false
-- }
-- }
-- function TABLE:new()
-- return self:tag('tr')
-- end
-- function returnDamageInOrder()
-- local main_key = 'damage'
-- local all_list = {}
-- -- Initialize current list with main key
-- local current_list = { main_key }
-- for i = #TABLE_CONTENT, 1, -1 do
-- local current_row = TABLE_CONTENT[i]
-- local new_list = {}
-- -- Check if it's the first iteration. If so, append phrases.
-- if not current_row.no_damage then
-- if i == #TABLE_CONTENT then
-- for _, keyword in ipairs(current_row.keywords) do
-- if not OPTIONS.no_max or (OPTIONS.no_max and keyword ~= 'total') then
-- local new_key = keyword .. '_' .. main_key
-- table.insert(new_list, new_key)
-- end
-- end
-- elseif current_row.is_visible then
-- -- Append suffix for each keyword in current row
-- for _, keyword in ipairs(current_row.keywords) do
-- -- Iterate through previous keys
-- for _, prev_key in ipairs(all_list) do
-- local new_key = prev_key .. '_' .. keyword
-- -- If needed, move the suffix to the rightmost of main_key.
-- if current_row.keyword_next_to_main_key then
-- new_key = prev_key:gsub(main_key, main_key .. '_' .. keyword)
-- elseif current_row.keyword_first then
-- new_key = keyword .. '_' .. prev_key
-- end
-- table.insert(new_list, new_key)
-- end
-- end
-- end
-- -- Append new_list to all_list
-- for _, new_key in ipairs(new_list) do
-- table.insert(all_list, sortPassives(new_key))
-- end
-- end
-- end
-- -- Sort the list once more, in order to swap the order of cancel & full.
-- if inArgs('cancel_dmg') then
-- local new_list = {}
-- local cancel_counter = 1
-- local full_counter = 2
-- for i, damage_key in ipairs(all_list) do
-- local regex = "^(%w+_)"
-- local prefix = 'cancel_'
-- local match = string.match(damage_key, regex)
-- if (match == prefix) then
-- new_list[i] = damage_key:gsub(prefix, "")
-- else
-- new_list[i] = prefix .. damage_key
-- end
-- end
-- all_list = new_list
-- end
-- return all_list
-- end
-- function doInitialCell(new_row)
-- return new_row:tag('th'):wikitext('Mode')
-- end
-- function doHeaders()
-- local current_multiplier = 0 -- Keeps track of the number of cells to spawn
-- local initial_header_cell -- The leftmost cell that says "Mode"
-- local iterations = 0 -- Keeps track of iterations that successfully rendered something. Required to tell the initial cell how many columns to span.
-- for row_index, row in ipairs(TABLE_CONTENT) do
-- if row.is_visible then
-- local new_row = TABLE:new()
-- local next_multiplier = 0
-- -- Only spawn the initial cell in the first generated row.
-- if iterations == 0 and not initial_header_cell then
-- initial_header_cell = doInitialCell(new_row)
-- end
-- --[[
-- We need to know how the colspan will look like.
-- So the solution is to loop through the table again and check how many cells will be spawned.
-- And also multiply everything, because it is exponential.
-- ]]
-- local colspan_value = 1
-- for k, v in ipairs(TABLE_CONTENT) do
-- if k > row_index and v.is_visible then
-- colspan_value = colspan_value * #v.text
-- end
-- end
-- -- Now we can spawn our header cells depending on what is known.
-- for i = 1, (current_multiplier == 0 and 1 or current_multiplier), 1 do
-- for _, text in ipairs(row.text) do
-- local new_cell = new_row:tag('th')
-- new_cell:attr('colspan', colspan_value):wikitext(text)
-- next_multiplier = next_multiplier + 1
-- end
-- end
-- current_multiplier = next_multiplier
-- iterations = iterations + 1
-- end
-- end
-- -- Apply rowspan of the same value as iteration count.
-- initial_header_cell:attr('rowspan', iterations)
-- end
-- -- Helper function to display ranges.
-- function doRangeText(damage_number)
-- if damage_number and damage_number.min == damage_number.max then
-- damage_number = damage_number.min
-- elseif damage_number then
-- damage_number = damage_number.min ..
-- '<span style="white-space: nowrap;"> ~</span> ' .. damage_number.max
-- end
-- return damage_number
-- end
-- function doContentByMode(mode)
-- local mode_row = TABLE:new()
-- mode_row:tag('td'):wikitext(frame:expandTemplate { title = mode })
-- local damage_entries = returnDamageInOrder()
-- local last_number
-- local last_unique_cell
-- for _, damage_key in ipairs(damage_entries) do
-- if args.dump_names ~= 'true' then
-- local damage_number = FINAL_DAMAGE[mode][damage_key]
-- damage_number = doRangeText(damage_number)
-- if last_number ~= damage_number then
-- -- Display ranges.
-- local new_cell = mode_row:tag('td'):wikitext(damage_number
-- -- Error out if it doesn't exist
-- or frame:expandTemplate {
-- title = 'color',
-- args = { 'red', '#ERROR' }
-- })
-- last_unique_cell = new_cell
-- else
-- last_unique_cell:attr('colspan', (last_unique_cell:getAttr('colspan') or 1) + 1)
-- end
-- last_number = damage_number
-- else
-- mode_row:tag('td'):wikitext(damage_key)
-- end
-- end
-- end
-- function doTable()
-- doHeaders()
-- forEach(doContentByMode)
-- end
-- if OPTIONS.do_table then
-- doTable()
-- end
-- Dump all values if wanted.
if OPTIONS.dump_table_data then
return inspect_dump(frame, TABLE_CONTENT)
elseif OPTIONS.dump then
return inspect_dump(frame, FINAL_DAMAGE)
elseif OPTIONS.dump_parsed then
return inspect_dump(frame, DAMAGE_PARSED)
end
local bug = ''
if OPTIONS.bug then
bug = frame:expandTemplate {
title = 'SkillText',
args = { 'FreeTraining' }
}
end
-- Transform into variables
local variables = doVariables(frame, FINAL_DAMAGE, OPTIONS.prefix)
if out ~= nil then
return inspect_dump(frame, out)
end
return variables .. bug .. (OPTIONS.do_table and tostring(TABLE) or '')
end
return p
-- pyend