Module:Test: Difference between revisions

From Elwiki
(Bot: Automated import of articles *** existing text overwritten ***)
(Bot: Automated import of articles *** existing text overwritten ***)
Line 227: Line 227:
                                     break
                                     break
                                 elseif main_arg_value and string.find(main_arg_value, 'i') and args[inherit_key][i] then
                                 elseif main_arg_value and string.find(main_arg_value, 'i') and args[inherit_key][i] then
                                     args[first_key][i] = eval(main_arg_value:gsub('i', '2'))
                                     args[first_key][i] = eval(main_arg_value:gsub('i', args[inherit_key][i]))
                                     break
                                     break
                                 end
                                 end
Line 282: Line 282:


     -- Detected "count", for skills like Clementine, Enough Mineral, etc.
     -- Detected "count", for skills like Clementine, Enough Mineral, etc.
     function doEachDamage()
     -- function doEachDamage()
        for mode, mode_content in pairs(DAMAGE_PARSED) do
    --    for mode, mode_content in pairs(DAMAGE_PARSED) do
            for damage_key, damage_value in pairs(mode_content) do
    --        for damage_key, damage_value in pairs(mode_content) do
                if string.find(damage_key, 'total_') then
    --            if string.find(damage_key, 'total_') then
                    local new_value = table.deep_copy(damage_value)
    --                local new_value = table.deep_copy(damage_value)


                    for k, hit_count in ipairs(new_value.hit_counts) do
    --                for k, hit_count in ipairs(new_value.hit_counts) do
                        hit_count = hit_count == '' and 1 or hit_count
    --                    hit_count = hit_count == '' and 1 or hit_count
                        new_value.hit_counts[k] = hit_count *
    --                    new_value.hit_counts[k] = hit_count *
                            ((string.find(damage_key, 'awk_') and args.awk_count) and args.awk_count[1] or args.count[1])
    --                        ((string.find(damage_key, 'awk_') and args.awk_count) and args.awk_count[1] or args.count[1])
                    end
    --                end


                    DAMAGE_PARSED[mode][damage_key:gsub("total_", "each_")] = damage_value
    --                DAMAGE_PARSED[mode][damage_key:gsub("total_", "each_")] = damage_value
                    DAMAGE_PARSED[mode][damage_key] = new_value
    --                DAMAGE_PARSED[mode][damage_key] = new_value
                end
    --            end
            end
    --        end
        end
    --    end
     end
     -- end


     if args.count then
     -- if args.count then
        doEachDamage()
    --    doEachDamage()
     end
     -- end


     function doBasicDamage()
     -- function doBasicDamage()
        for mode, mode_content in pairs(DAMAGE_PARSED) do
    --    for mode, mode_content in pairs(DAMAGE_PARSED) do
            for damage_key, damage_value in pairs(mode_content) do
    --        for damage_key, damage_value in pairs(mode_content) do
                local i = 1
    --            local i = 1
                local output = 0
    --            local output = 0
                -- Check if to even generate the damage.
    --            -- Check if to even generate the damage.
                if damage_value.provided then
    --            if damage_value.provided then
                    -- Loop through damage numbers and multiply them with hits.
    --                -- Loop through damage numbers and multiply them with hits.
                    for k, damage_number in ipairs(damage_value.damage_numbers) do
    --                for k, damage_number in ipairs(damage_value.damage_numbers) do
                        local hit_count = damage_value.hit_counts[i]
    --                    local hit_count = damage_value.hit_counts[i]
                        hit_count = hit_count == '' and 1 or hit_count
    --                    hit_count = hit_count == '' and 1 or hit_count
                        output = output + (damage_number * hit_count)
    --                    output = output + (damage_number * hit_count)
                        i = i + 1
    --                    i = i + 1
                    end
    --                end
                    -- Write the result to a separate object.
    --                -- Write the result to a separate object.
                    BASIC_DAMAGE[mode][damage_key] = output
    --                BASIC_DAMAGE[mode][damage_key] = output
                end
    --            end
            end
    --        end
        end
    --    end
     end
     -- end


     doBasicDamage()
     -- doBasicDamage()


     local WITH_TRAITS = createDamageDataTable()
     -- local WITH_TRAITS = createDamageDataTable()
     function doTraits()
     -- function doTraits()
        -- Handle traits here
    --    -- Handle traits here
        for mode, mode_content in pairs(BASIC_DAMAGE) do
    --    for mode, mode_content in pairs(BASIC_DAMAGE) do
            for damage_key, damage_value in pairs(mode_content) do
    --        for damage_key, damage_value in pairs(mode_content) do
                for _, trait in pairs(TRAITS) do
    --            for _, trait in pairs(TRAITS) do
                    --[[
    --                --[[
                    Suffix all damage values with existing traits.
    --                Suffix all damage values with existing traits.
                    Useful already has the prefix, so only multiply with its value.
    --                Useful already has the prefix, so only multiply with its value.
                    Also, we don't want other traits to multiply with Useful,
    --                Also, we don't want other traits to multiply with Useful,
                    so we skip those situations, as impossible in-game.
    --                so we skip those situations, as impossible in-game.
                    --]]
    --                --]]
                    if trait.value then
    --                if trait.value then
                        WITH_TRAITS[mode][damage_key .. ((trait.key == 'useful' or trait.key == '') and "" or ('_' .. trait.key))] =
    --                    WITH_TRAITS[mode][damage_key .. ((trait.key == 'useful' or trait.key == '') and "" or ('_' .. trait.key))] =
                            damage_value * trait.value
    --                        damage_value * trait.value
                    end
    --                 end
                 end
    --             end
             end
    --         end
         end
     --    end
     end
    -- end


     doTraits()
     -- doTraits()


     local WITH_PASSIVES = createDamageDataTable()
     -- local WITH_PASSIVES = createDamageDataTable()


     --[[
     -- --[[
     Generates passives with every possible combinations of all subsets.
     -- Generates passives with every possible combinations of all subsets.
     For example: 3 passives are given, so it will generate the following:
     -- For example: 3 passives are given, so it will generate the following:
     (1), (2), (3), (1, 2), (1, 3), (1, 2, 3), (2, 3)
     -- (1), (2), (3), (1, 2), (1, 3), (1, 2, 3), (2, 3)
     ]]
     -- ]]
     function doPassives()
     -- function doPassives()
        for mode, mode_content in pairs(WITH_TRAITS) do
    --    for mode, mode_content in pairs(WITH_TRAITS) do
            for damage_key, damage_value in pairs(mode_content) do
    --        for damage_key, damage_value in pairs(mode_content) do
                local combinations = { {} }
    --            local combinations = { {} }
                for passive_key, passive in pairs(PASSIVES) do
    --            for passive_key, passive in pairs(PASSIVES) do
                    local count = #combinations
    --                local count = #combinations
                    for i = 1, count do
    --                for i = 1, count do
                        local new_combination = { unpack(combinations[i]) }
    --                    local new_combination = { unpack(combinations[i]) }
                        table.insert(new_combination, passive_key)
    --                    table.insert(new_combination, passive_key)
                        table.insert(combinations, new_combination)
    --                    table.insert(combinations, new_combination)
                    end
    --                end
                end
    --            end
                for _, combination in pairs(combinations) do
    --            for _, combination in pairs(combinations) do
                    local passive_multiplier = 1
    --                local passive_multiplier = 1
                    local name_suffix = ''
    --                local name_suffix = ''
                    if #combination > 0 then
    --                if #combination > 0 then
                        table.sort(combination)
    --                    table.sort(combination)
                        for _, passive_key in pairs(combination) do
    --                    for _, passive_key in pairs(combination) do
                            passive_multiplier = passive_multiplier *
    --                        passive_multiplier = passive_multiplier *
                                tonumber(PASSIVES[passive_key][mode == 'PvE' and 'value' or 'value_pvp'])
    --                            tonumber(PASSIVES[passive_key][mode == 'PvE' and 'value' or 'value_pvp'])
                            name_suffix = name_suffix .. '_passive' .. passive_key
    --                        name_suffix = name_suffix .. '_passive' .. passive_key
                        end
    --                    end
                    end
    --                end
                    WITH_PASSIVES[mode][damage_key .. name_suffix] = damage_value * passive_multiplier
    --                WITH_PASSIVES[mode][damage_key .. name_suffix] = damage_value * passive_multiplier
                end
    --            end
            end
    --        end
        end
    --    end
     end
     -- end


     doPassives()
     -- doPassives()


     local RANGE = {
     -- local RANGE = {
        min_count = args.range_min_count and args.range_min_count[1] or 1,
    --    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,
    --    max_count = args.range_max_count and args.range_max_count[1] or 1,
        PvE = {
    --    PvE = {
            min = args.range_min and args.range_min[1] or 1,
    --        min = args.range_min and args.range_min[1] or 1,
            max = args.range_max and args.range_max[1] or 1
    --        max = args.range_max and args.range_max[1] or 1
        },
    --    },
        PvP = {
    --    PvP = {
            min = args.range_min and (args.range_min[2] or args.range_min[1]) or 1,
    --        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
    --        max = args.range_max and (args.range_max[2] or args.range_max[1]) or 1
        }
    --    }
     }
     -- }


     local WITH_RANGE = createDamageDataTable()
     -- local WITH_RANGE = createDamageDataTable()
     function doDamageBuffRange()
     -- function doDamageBuffRange()
        -- Handle damage range here
    --    -- Handle damage range here
        for mode, mode_content in pairs(WITH_PASSIVES) do
    --    for mode, mode_content in pairs(WITH_PASSIVES) do
            for damage_key, damage_value in pairs(mode_content) do
    --        for damage_key, damage_value in pairs(mode_content) do
                WITH_RANGE[mode][damage_key] = { min = 0, max = 0 }
    --            WITH_RANGE[mode][damage_key] = { min = 0, max = 0 }
                for _, range in ipairs({ 'min', 'max' }) do
    --            for _, range in ipairs({ 'min', 'max' }) do
                    local final_damage_value = damage_value * (1 + ((RANGE[mode][range] - 1) * RANGE[range .. '_count'])) *
    --                local final_damage_value = damage_value * (1 + ((RANGE[mode][range] - 1) * RANGE[range .. '_count'])) *
                        OPTIONS.perm_buff[mode]
    --                    OPTIONS.perm_buff[mode]
                    WITH_RANGE[mode][damage_key][range] = not OPTIONS.format and final_damage_value or
    --                WITH_RANGE[mode][damage_key][range] = not OPTIONS.format and final_damage_value or
                        formatDamage(final_damage_value)
    --                    formatDamage(final_damage_value)
                end
    --            end
            end
    --        end
        end
    --    end
     end
     -- end


     doDamageBuffRange()
     -- doDamageBuffRange()


     local FINAL_DAMAGE = WITH_RANGE
     -- local FINAL_DAMAGE = WITH_RANGE


     -- Helper function to iterate over traits.
     -- -- Helper function to iterate over traits.
     function checkTraits(settings)
     -- function checkTraits(settings)
        local output
    --    local output
        if not settings then
    --    if not settings then
            output = false
    --        output = false
        else
    --    else
            output = settings.output or {}
    --        output = settings.output or {}
        end
    --    end


        for trait_index, trait in ipairs(TRAITS) do
    --    for trait_index, trait in ipairs(TRAITS) do
            if trait.value ~= false and trait_index ~= 1 then
    --        if trait.value ~= false and trait_index ~= 1 then
                if settings and type(settings.action) == 'function' then
    --            if settings and type(settings.action) == 'function' then
                    settings.action(trait, output, settings)
    --                settings.action(trait, output, settings)
                else
    --            else
                    return true
    --                return true
                end
    --            end
            end
    --        end
        end
    --    end
        return output
    --    return output
     end
     -- end


     -- Helper function to detect combined passives.
     -- -- Helper function to detect combined passives.
     function isCombined(index)
     -- function isCombined(index)
        for k, v in ipairs(OPTIONS.combine) do
    --    for k, v in ipairs(OPTIONS.combine) do
            if index == v then
    --        if index == v then
                return true
    --            return true
            end
    --        end
        end
    --    end
        return false
    --    return false
     end
     -- end


     -- Helper function to iterate over passives.
     -- -- Helper function to iterate over passives.
     function checkPassives(settings)
     -- function checkPassives(settings)
        local output = settings.output or {}
    --    local output = settings.output or {}
        local PASSIVES_WITH_COMBINED = table.deep_copy(PASSIVES)
    --    local PASSIVES_WITH_COMBINED = table.deep_copy(PASSIVES)


        -- Handle combined passives properly.
    --    -- Handle combined passives properly.
        if OPTIONS.combine then
    --    if OPTIONS.combine then
            table.insert(PASSIVES_WITH_COMBINED, {
    --        table.insert(PASSIVES_WITH_COMBINED, {
                is_combined = true
    --            is_combined = true
            })
    --        })
        end
    --    end


        for passive_index, passive in ipairs(PASSIVES_WITH_COMBINED) do
    --    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 {}) then
    --        if (not OPTIONS.is_append or (OPTIONS.is_append and OPTIONS.append_index ~= passive_index)) and not inArrayHasValue(passive_index, OPTIONS.combine or {}) then
                if type(settings.action) == 'function' then
    --            if type(settings.action) == 'function' then
                    settings.action(passive, output, passive_index)
    --                settings.action(passive, output, passive_index)
                else
    --            else
                    return true
    --                return true
                end
    --            end
            end
    --        end
        end
    --    end
        return output
    --    return output
     end
     -- end


     -- Generate the table
     -- -- Generate the table
     local TABLE = mw.html.create('table'):attr({
     -- local TABLE = mw.html.create('table'):attr({
        cellpadding = 5,
    --    cellpadding = 5,
        border = 1,
    --    border = 1,
        style = 'border-collapse: collapse; text-align: center',
    --    style = 'border-collapse: collapse; text-align: center',
        class = 'colortable-' .. OPTIONS.character
    --    class = 'colortable-' .. OPTIONS.character
     })
     -- })


     -- Our table structure
     -- -- Our table structure
     local TABLE_CONTENT = {
     -- local TABLE_CONTENT = {
        {
    --    {
            type = 'extra',
    --        type = 'extra',
            text = { 'Average' },
    --        text = { 'Average' },
            is_visible = OPTIONS.no_max,
    --        is_visible = OPTIONS.no_max,
            no_damage = true
    --        no_damage = true
        },
    --    },
        {
    --    {
            type = 'passives',
    --        type = 'passives',
            text = checkPassives({
    --        text = checkPassives({
                output = { 'Base' },
    --            output = { 'Base' },
                action = function(passive, output)
    --            action = function(passive, output)
                    if passive.is_combined then
    --                if passive.is_combined then
                        -- Handling combined passive header name.
    --                    -- Handling combined passive header name.
                        local combo = {}
    --                    local combo = {}
                        for _, passive_key in ipairs(OPTIONS.combine) do
    --                    for _, passive_key in ipairs(OPTIONS.combine) do
                            passive = PASSIVES[passive_key]
    --                        passive = PASSIVES[passive_key]
                            table.insert(combo, link(passive.name, passive.alias) .. passive.suffix)
    --                        table.insert(combo, link(passive.name, passive.alias) .. passive.suffix)
                        end
    --                    end
                        table.insert(output, table.concat(combo, '/') .. OPTIONS.combine_suffix)
    --                    table.insert(output, table.concat(combo, '/') .. OPTIONS.combine_suffix)
                    else
    --                else
                        table.insert(output, link(passive.name, passive.alias) .. passive.suffix)
    --                    table.insert(output, link(passive.name, passive.alias) .. passive.suffix)
                    end
    --                end
                end
    --            end
            }),
    --        }),
            keywords = checkPassives({
    --        keywords = checkPassives({
                action = function(passive, output, passive_index)
    --            action = function(passive, output, passive_index)
                    if passive.is_combined then
    --                if passive.is_combined then
                        -- Handling combined passive damage cells.
    --                    -- Handling combined passive damage cells.
                        table.insert(output, sortPassives('passive' .. table.concat(OPTIONS.combine, '_passive')))
    --                    table.insert(output, sortPassives('passive' .. table.concat(OPTIONS.combine, '_passive')))
                    else
    --                else
                        table.insert(output, 'passive' .. passive_index)
    --                    table.insert(output, 'passive' .. passive_index)
                    end
    --                end
                end
    --            end
            }),
    --        }),
            is_visible = not OPTIONS.no_max or #PASSIVES > 0
    --        is_visible = not OPTIONS.no_max or #PASSIVES > 0
        },
    --    },
        {
    --    {
            type = 'passive_appended',
    --        type = 'passive_appended',
            text = { 'Normal',
    --        text = { 'Normal',
                OPTIONS.is_append and
    --            OPTIONS.is_append and
                link(PASSIVES[OPTIONS.append_index].name, PASSIVES[OPTIONS.append_index].alias or OPTIONS.append_name) },
    --            link(PASSIVES[OPTIONS.append_index].name, PASSIVES[OPTIONS.append_index].alias or OPTIONS.append_name) },
            keywords = { OPTIONS.is_append and ('passive' .. OPTIONS.append_index) or nil },
    --        keywords = { OPTIONS.is_append and ('passive' .. OPTIONS.append_index) or nil },
            is_visible = OPTIONS.is_append or false
    --        is_visible = OPTIONS.is_append or false
        },
    --    },
        {
    --    {
            type = 'awakening',
    --        type = 'awakening',
            text = { 'Regular', (function()
    --        text = { 'Regular', (function()
                if OPTIONS.dmp then
    --            if OPTIONS.dmp then
                    return link('Dynamo Point System', 'Dynamo Configuration',
    --                return link('Dynamo Point System', 'Dynamo Configuration',
                        OPTIONS.dmp ~= 'false' and ('(' .. OPTIONS.dmp .. ' DMP)'))
    --                    OPTIONS.dmp ~= 'false' and ('(' .. OPTIONS.dmp .. ' DMP)'))
                elseif args.awk_alias then
    --            elseif args.awk_alias then
                    return link(unpack(args.awk_alias))
    --                return link(unpack(args.awk_alias))
                end
    --            end
                return link('Awakening Mode')
    --            return link('Awakening Mode')
            end)()
    --        end)()
            },
    --        },
            keywords = { 'awk' },
    --        keywords = { 'awk' },
            keyword_next_to_main_key = true,
    --        keyword_next_to_main_key = true,
            is_visible = inArgs('awk_dmg') or inArgs('awk_hits') or inArgs('avg_awk_hits') or false
    --        is_visible = inArgs('awk_dmg') or inArgs('awk_hits') or inArgs('avg_awk_hits') or false
        },
    --    },
        {
    --    {
            type = 'traits',
    --        type = 'traits',
            text = checkTraits({
    --        text = checkTraits({
                output = { 'Normal' },
    --            output = { 'Normal' },
                action = function(trait, output)
    --            action = function(trait, output)
                    table.insert(output, trait.name)
    --                table.insert(output, trait.name)
                end
    --            end
            }),
    --        }),
            keywords = checkTraits({
    --        keywords = checkTraits({
                action = function(trait, output)
    --            action = function(trait, output)
                    table.insert(output, trait.key)
    --                table.insert(output, trait.key)
                end
    --            end
            }),
    --        }),
            is_visible = checkTraits()
    --        is_visible = checkTraits()
        },
    --    },
        {
    --    {
            type = 'hit_count',
    --        type = 'hit_count',
            text = {
    --        text = {
                (inArgs('count') and not OPTIONS.use_avg) and
    --            (inArgs('count') and not OPTIONS.use_avg) and
                (table.concat({ 'Per', args.count_name or 'Instance' }, ' ')) or 'Average',
    --            (table.concat({ 'Per', args.count_name or 'Instance' }, ' ')) or 'Average',
                'Max'
    --            'Max'
            },
    --        },
            keywords = (function()
    --        keywords = (function()
                if inArgs('avg_hits') or inArgs('count') then
    --            if inArgs('avg_hits') or inArgs('count') then
                    return { (inArgs('count') and not OPTIONS.use_avg) and 'each' or 'avg', 'total' }
    --                return { (inArgs('count') and not OPTIONS.use_avg) and 'each' or 'avg', 'total' }
                end
    --            end
                return { 'total' }
    --            return { 'total' }
            end)(),
    --        end)(),
            is_visible = ((inArgs('avg_hits') or inArgs('count')) and not OPTIONS.no_max) or false
    --        is_visible = ((inArgs('avg_hits') or inArgs('count')) and not OPTIONS.no_max) or false
        }
    --    }
     }
     -- }


     function TABLE:new()
     -- function TABLE:new()
        return self:tag('tr')
    --    return self:tag('tr')
     end
     -- end


     function returnDamageInOrder()
     -- function returnDamageInOrder()
        local main_key = 'damage'
    --    local main_key = 'damage'
        local all_list = {}
    --    local all_list = {}


        -- Initialize current list with main key
    --    -- Initialize current list with main key
        local current_list = { main_key }
    --    local current_list = { main_key }


        for i = #TABLE_CONTENT, 1, -1 do
    --    for i = #TABLE_CONTENT, 1, -1 do
            local current_row = TABLE_CONTENT[i]
    --        local current_row = TABLE_CONTENT[i]
            local new_list = {}
    --        local new_list = {}


            -- Check if it's the first iteration. If so, append phrases.
    --        -- Check if it's the first iteration. If so, append phrases.
            if not current_row.no_damage then
    --        if not current_row.no_damage then
                if i == #TABLE_CONTENT then
    --            if i == #TABLE_CONTENT then
                    for _, keyword in ipairs(current_row.keywords) do
    --                for _, keyword in ipairs(current_row.keywords) do
                        if not OPTIONS.no_max or (OPTIONS.no_max and keyword ~= 'total') then
    --                    if not OPTIONS.no_max or (OPTIONS.no_max and keyword ~= 'total') then
                            local new_key = keyword .. '_' .. main_key
    --                        local new_key = keyword .. '_' .. main_key
                            table.insert(new_list, new_key)
    --                        table.insert(new_list, new_key)
                        end
    --                    end
                    end
    --                end
                elseif current_row.is_visible then
    --            elseif current_row.is_visible then
                    -- Append suffix for each keyword in current row
    --                -- Append suffix for each keyword in current row
                    for _, keyword in ipairs(current_row.keywords) do
    --                for _, keyword in ipairs(current_row.keywords) do
                        -- Iterate through previous keys
    --                    -- Iterate through previous keys
                        for _, prev_key in ipairs(all_list) do
    --                    for _, prev_key in ipairs(all_list) do
                            local new_key = prev_key .. '_' .. keyword
    --                        local new_key = prev_key .. '_' .. keyword
                            -- If needed, move the suffix to the rightmost of main_key.
    --                        -- If needed, move the suffix to the rightmost of main_key.
                            if current_row.keyword_next_to_main_key then
    --                        if current_row.keyword_next_to_main_key then
                                new_key = prev_key:gsub(main_key, main_key .. '_' .. keyword)
    --                            new_key = prev_key:gsub(main_key, main_key .. '_' .. keyword)
                            end
    --                        end
                            table.insert(new_list, new_key)
    --                        table.insert(new_list, new_key)
                        end
    --                    end
                    end
    --                end
                end
    --            end


                -- Append new_list to all_list
    --            -- Append new_list to all_list
                for _, new_key in ipairs(new_list) do
    --            for _, new_key in ipairs(new_list) do
                    table.insert(all_list, sortPassives(new_key))
    --                table.insert(all_list, sortPassives(new_key))
                end
    --            end
            end
    --        end
        end
    --    end


        return all_list
    --    return all_list
     end
     -- end


     function doInitialCell(new_row)
     -- function doInitialCell(new_row)
        return new_row:tag('th'):wikitext('Mode')
    --    return new_row:tag('th'):wikitext('Mode')
     end
     -- end


     function doHeaders()
     -- function doHeaders()
        local current_multiplier = 0 -- Keeps track of the number of cells to spawn
    --    local current_multiplier = 0 -- Keeps track of the number of cells to spawn
        local initial_header_cell    -- The leftmost cell that says "Mode"
    --    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.
    --    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
    --    for row_index, row in ipairs(TABLE_CONTENT) do
            if row.is_visible then
    --        if row.is_visible then
                local new_row = TABLE:new()
    --            local new_row = TABLE:new()
                local next_multiplier = 0
    --            local next_multiplier = 0


                -- Only spawn the initial cell in the first generated row.
    --            -- Only spawn the initial cell in the first generated row.
                if iterations == 0 and not initial_header_cell then
    --            if iterations == 0 and not initial_header_cell then
                    initial_header_cell = doInitialCell(new_row)
    --                initial_header_cell = doInitialCell(new_row)
                end
    --            end


                --[[
    --            --[[
                We need to know how the colspan will look like.
    --            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.
    --            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.
    --            And also multiply everything, because it is exponential.
                ]]
    --            ]]
                local colspan_value = 1
    --            local colspan_value = 1
                for k, v in ipairs(TABLE_CONTENT) do
    --            for k, v in ipairs(TABLE_CONTENT) do
                    if k > row_index and v.is_visible then
    --                if k > row_index and v.is_visible then
                        colspan_value = colspan_value * #v.text
    --                    colspan_value = colspan_value * #v.text
                    end
    --                end
                end
    --            end


                -- Now we can spawn our header cells depending on what is known.
    --            -- 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 i = 1, (current_multiplier == 0 and 1 or current_multiplier), 1 do
                    for _, text in ipairs(row.text) do
    --                for _, text in ipairs(row.text) do
                        local new_cell = new_row:tag('th')
    --                    local new_cell = new_row:tag('th')
                        new_cell:attr('colspan', colspan_value):wikitext(text)
    --                    new_cell:attr('colspan', colspan_value):wikitext(text)
                        next_multiplier = next_multiplier + 1
    --                    next_multiplier = next_multiplier + 1
                    end
    --                end
                end
    --            end
                current_multiplier = next_multiplier
    --            current_multiplier = next_multiplier
                iterations = iterations + 1
    --            iterations = iterations + 1
            end
    --        end
        end
    --    end
        -- Apply rowspan of the same value as iteration count.
    --    -- Apply rowspan of the same value as iteration count.
        initial_header_cell:attr('rowspan', iterations)
    --    initial_header_cell:attr('rowspan', iterations)
     end
     -- end


     -- Helper function to display ranges.
     -- -- Helper function to display ranges.
     function doRangeText(damage_number)
     -- function doRangeText(damage_number)
        if damage_number and damage_number.min == damage_number.max then
    --    if damage_number and damage_number.min == damage_number.max then
            damage_number = damage_number.min
    --        damage_number = damage_number.min
        elseif damage_number then
    --    elseif damage_number then
            damage_number = damage_number.min ..
    --        damage_number = damage_number.min ..
                '<span style="white-space: nowrap;"> ~</span> ' .. damage_number.max
    --            '<span style="white-space: nowrap;"> ~</span> ' .. damage_number.max
        end
    --    end
        return damage_number
    --    return damage_number
     end
     -- end


     function doContentByMode(mode)
     -- function doContentByMode(mode)
        local mode_row = TABLE:new()
    --    local mode_row = TABLE:new()
        mode_row:tag('td'):wikitext(frame:expandTemplate { title = mode })
    --    mode_row:tag('td'):wikitext(frame:expandTemplate { title = mode })
        local damage_entries = returnDamageInOrder()
    --    local damage_entries = returnDamageInOrder()


        for _, damage_key in ipairs(damage_entries) do
    --    for _, damage_key in ipairs(damage_entries) do
            if args.dump_names ~= 'true' then
    --        if args.dump_names ~= 'true' then
                local damage_number = FINAL_DAMAGE[mode][damage_key]
    --            local damage_number = FINAL_DAMAGE[mode][damage_key]


                -- Display ranges.
    --            -- Display ranges.
                damage_number = doRangeText(damage_number)
    --            damage_number = doRangeText(damage_number)


                mode_row:tag('td'):wikitext(damage_number
    --            mode_row:tag('td'):wikitext(damage_number
                    -- Error out if it doesn't exist
    --                -- Error out if it doesn't exist
                    or frame:expandTemplate {
    --                or frame:expandTemplate {
                        title = 'color',
    --                    title = 'color',
                        args = { 'red', '&#35;ERROR' }
    --                    args = { 'red', '&#35;ERROR' }
                    })
    --                })
            else
    --        else
                mode_row:tag('td'):wikitext(damage_key)
    --            mode_row:tag('td'):wikitext(damage_key)
            end
    --        end
        end
    --    end
     end
     -- end


     function doTable()
     -- function doTable()
        doHeaders()
    --    doHeaders()
        doContentByMode('PvE')
    --    doContentByMode('PvE')
        doContentByMode('PvP')
    --    doContentByMode('PvP')
     end
     -- end


     doTable()
     -- doTable()


     -- Dump all values if wanted.
     -- Dump all values if wanted.

Revision as of 00:13, 25 April 2023

Documentation for this module may be created at Module:Test/doc

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)

    function inArgs(key)
        if args[key] ~= nil then
            return true
        end
    end

    -- Define the schema for the table
    local tableSchema = {
        PvE = {},
        PvP = {}
    }

    -- 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',
            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 and (args.useful_penalty or args.useful or 0.7) 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_index = string.match(k, "%d")
            local passive_values = split(frame:preprocess('{{:' .. v .. '}}{{#arrayprint:' .. v .. '}}'));
            PASSIVES[tonumber(passive_index)] = {
                name = v,
                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 '',
            }
        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

    -- 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 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' }
        },
    }
    -- 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 config_key, config_value in pairs(DAMAGE_CONFIG) do
            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 first_key = arg_table[1]

                    -- If doesn't exist, create it.
                    if not args[first_key] then
                        args[first_key] = {}
                    end

                    local main_arg_values = args[first_key]

                    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.
                    while i <= #(args.dmg) do
                        local main_arg_value = args[first_key][i]

                        for ix, inherit_key in ipairs(arg_table) do
                            if args[inherit_key] and args[inherit_key][i] and args[inherit_key][i] ~= '' then
                                -- Only inherit if empty or nil
                                if main_arg_value == '' then
                                    args[first_key][i] = args[inherit_key][i]
                                    break
                                elseif main_arg_value and string.find(main_arg_value, 'i') and args[inherit_key][i] then
                                    args[first_key][i] = eval(main_arg_value:gsub('i', args[inherit_key][i]))
                                    break
                                end
                            end
                        end

                        i = i + 1
                    end

                    if #(args[first_key]) == 0 then
                        args[first_key] = nil
                    end
                end
            end
        end
    end

    inherit('PvE')

    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 = v
                for _, v2 in ipairs(v) do
                    local arg_from_template = args[prefix .. v2] or args[v2]
                    if arg_from_template ~= nil then
                        output_value = arg_from_template
                        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
                        end
                        break
                    else
                        if k == 'provided' then
                            output_value = false
                        end
                    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

    parseConfig('PvE')
    parseConfig('PvP')

    -- Detected "count", for skills like Clementine, Enough Mineral, etc.
    -- function doEachDamage()
    --     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)

    --                 for k, hit_count in ipairs(new_value.hit_counts) do
    --                     hit_count = hit_count == '' and 1 or hit_count
    --                     new_value.hit_counts[k] = hit_count *
    --                         ((string.find(damage_key, 'awk_') and args.awk_count) and args.awk_count[1] or args.count[1])
    --                 end

    --                 DAMAGE_PARSED[mode][damage_key:gsub("total_", "each_")] = damage_value
    --                 DAMAGE_PARSED[mode][damage_key] = new_value
    --             end
    --         end
    --     end
    -- end

    -- if args.count then
    --     doEachDamage()
    -- end

    -- function doBasicDamage()
    --     for mode, mode_content in pairs(DAMAGE_PARSED) do
    --         for damage_key, damage_value in pairs(mode_content) do
    --             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) do
    --                     local hit_count = damage_value.hit_counts[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.
    --                 BASIC_DAMAGE[mode][damage_key] = output
    --             end
    --         end
    --     end
    -- end

    -- doBasicDamage()

    -- 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 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

    -- -- 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 detect combined passives.
    -- function isCombined(index)
    --     for k, v in ipairs(OPTIONS.combine) do
    --         if index == v then
    --             return true
    --         end
    --     end
    --     return false
    -- 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 {}) 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.suffix)
    --                     end
    --                     table.insert(output, table.concat(combo, '/') .. OPTIONS.combine_suffix)
    --                 else
    --                     table.insert(output, link(passive.name, passive.alias) .. passive.suffix)
    --                 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) },
    --         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',
    --                     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 = 'hit_count',
    --         text = {
    --             (inArgs('count') and not OPTIONS.use_avg) and
    --             (table.concat({ 'Per', args.count_name or 'Instance' }, ' ')) 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)
    --                         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

    --     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()

    --     for _, damage_key in ipairs(damage_entries) do
    --         if args.dump_names ~= 'true' then
    --             local damage_number = FINAL_DAMAGE[mode][damage_key]

    --             -- Display ranges.
    --             damage_number = doRangeText(damage_number)

    --             mode_row:tag('td'):wikitext(damage_number
    --                 -- Error out if it doesn't exist
    --                 or frame:expandTemplate {
    --                     title = 'color',
    --                     args = { 'red', '&#35;ERROR' }
    --                 })
    --         else
    --             mode_row:tag('td'):wikitext(damage_key)
    --         end
    --     end
    -- end

    -- function doTable()
    --     doHeaders()
    --     doContentByMode('PvE')
    --     doContentByMode('PvP')
    -- 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)

    return variables .. bug .. (OPTIONS.do_table and tostring(TABLE) or '')
end

return p