|
|
(103 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
| -- pystart
| | require('Module:CommonFunctions') |
| require('Module:CommonFunctions'); | | require('Module:InfoboxProto'); |
| local getArgs = require('Module:Arguments').getArgs | | local getArgs = require('Module:Arguments').getArgs |
| local inspect = require('Module:Inspect').inspect
| |
| local p = {} | | local p = {} |
|
| |
|
| -- Main process | | -- Main process |
| function p.main(frame) | | function p.main(frame) |
| local args = getArgs(frame) | | local args = getArgs(frame); |
| local out | | local infobox = require('Module:InfoboxProto').main(frame, args.title); |
| | | local divclass = args.divclass; |
| function inArgs(key)
| | if divclass == nil then |
| if args[key] ~= nil then
| | divclass = 'custom' |
| 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 | | end |
| | infobox:addClass('infobox-' .. divclass); |
|
| |
|
| -- Inherits values from args if not provided, but usage suggests that they're meant to be generated. | | for k, v in spairs(args) do |
| function inherit(mode)
| | if string.match(k, 'data[0-9]+') ~= nil then |
| local prefix = mode == 'PvE' and '' or string.lower(mode .. '_') | | local i = k:gsub('data', ''); |
| for config_key, config_value in pairs(DAMAGE_CONFIG) do
| | addField('data' .. i, args['label' .. i]) |
| for arg_table_key, arg_table in pairs(config_value) do
| |
| if arg_table_key ~= 'provided' and arg_table then
| |
| -- We only do this for the first (main) key
| |
| local main_key = arg_table[1]
| |
| local main_key_prefixed = prefix .. main_key
| |
| local main_arg_values = args[main_key_prefixed]
| |
| | |
| -- Only if the main argument values exist.
| |
| if main_arg_values then
| |
| local i = 1
| |
| --[[
| |
| Loop over all damage and attempt to inherit in chain.
| |
| Break the loop if a match was found. Note: For this to work, the value must be an empty string.
| |
| Alternatively, it can contain an "i" to template the value to inherit.
| |
| ]]
| |
| local cancel_dmg_len = args.cancel_dmg and #(args.cancel_dmg) or 0
| |
| while i <= (#(args.dmg) + cancel_dmg_len) do
| |
| local main_arg_value = main_arg_values[i]
| |
| | |
| for ix, inherit_key in ipairs(arg_table) do
| |
| local inherit_arg = args[prefix .. inherit_key] or args[inherit_key]
| |
| -- No inheritance from itself.
| |
| if inherit_arg and inherit_arg[i] and inherit_arg[i] ~= '' and ix ~= 1 then
| |
| -- Only inherit if empty
| |
| if main_arg_value == '' then
| |
| args[main_key_prefixed][i] = inherit_arg[i]
| |
| break
| |
| elseif main_arg_value and string.find(main_arg_value, 'i') and inherit_arg[i] then
| |
| args[main_key_prefixed][i] = eval(main_arg_value:gsub('i', inherit_arg[i]))
| |
| break
| |
| end
| |
| end
| |
| end
| |
| | |
| i = i + 1
| |
| end
| |
| end
| |
| end
| |
| end
| |
| end | | end |
| end | | end |
| | |
| | return tostring(infobox); |
|
| |
|
| 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
| |
| forEachDamageType(function(damage_type)
| |
| local cancel_candidate = BASIC_DAMAGE[mode]['cancel_' .. damage_key][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
| |
| 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
| |
| WITH_TRAITS[mode][damage_key .. ((trait.key == 'useful' or trait.key == '') and "" or ('_' .. trait.key))] =
| |
| damage_value * trait.value
| |
| 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
| |
| 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
| |
| WITH_PASSIVES[mode][damage_key .. name_suffix] = damage_value * passive_multiplier
| |
| 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 }
| |
| for _, range in ipairs({ 'min', 'max' }) do
| |
| local final_damage_value = damage_value * (1 + ((RANGE[mode][range] - 1) * RANGE[range .. '_count'])) *
| |
| OPTIONS.perm_buff[mode]
| |
| WITH_RANGE[mode][damage_key][range] = not OPTIONS.format and final_damage_value or
| |
| formatDamage(final_damage_value)
| |
| end
| |
| end
| |
| end
| |
| end
| |
|
| |
| -- doDamageBuffRange()
| |
|
| |
| -- local FINAL_DAMAGE = WITH_RANGE
| |
| local FINAL_DAMAGE = BASIC_DAMAGE
| |
|
| |
| -- 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
| |
|
| |
| -- doTable()
| |
|
| |
| -- 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 | | end |
|
| |
|
| return p | | return p |
| -- pyend
| |