Module:Traits: Difference between revisions

From Elwiki
No edit summary
Tag: Manual revert
No edit summary
Line 1: Line 1:
-- pystart
require('Module:CommonFunctions')
require('Module:CommonFunctions')
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
Line 20: Line 21:


     -- Dictionary for headers
     -- Dictionary for headers
     local headers = {'MP Usage', 'Cooldown', 'Duration', 'MP Recovery', 'Max Hits'}
     local prop = {'MP Usage', 'Cooldown', 'Duration', 'MP Recovery', 'Max Hits'}
     local properties = {'mp_cost', 'cooldown', 'duration', 'mp_cost'}
 
     local header_dict = {
     local prop_short = {'mp', 'cd', 'duration', 'mp_recovery', 'dmg', 'chance'}
         [1] = {'Light', 'Critical', 'Reversed'},
 
         [2] = {'Heavy', 'Haste', 'Regenerating (2)', 'Ruthless', 'Powerful', 'Reversed'},
     local STR = {
         [3] = {'Killing Blow (1)'},
         LIGHT = 'Light',
         [4] = {'Regenerating (1)'},
        CRITICAL = 'Critical',
         [5] = {'Useful'}
        REVERSED = 'Reversed',
         HEAVY = 'Heavy',
        HASTE = 'Haste',
        REGEN1 = 'Regenerating (1)',
        REGEN2 = 'Regenerating (2)',
        KB1 = 'Killing Blow (1)',
         KB2 = 'Killing Blow (2)',
         RUTHLESS = 'Ruthless',
        POWERFUL = 'Powerful',
         USEFUL = 'Useful',
        SEC = ' Seconds',
        MP = ' MP'
     }
     }
    local details = {{STR.LIGHT, STR.CRITICAL, STR.REVERSED},
                    {STR.HEAVY, STR.HASTE, STR.REGEN2, STR.RUTHLESS, STR.POWERFUL, STR.REVERSED}, {STR.KB1},
                    {STR.REGEN1}, {STR.USEFUL}}


     -- Default values
     -- Default values
     local default_trait_values = {
     local DEFAULT = {
         Heavy = {
         [STR.HEAVY] = {
             multi_cooldown = 120
             cd = 120
        },
        [STR.LIGHT] = {
            mp = 80
        },
        [STR.CRITICAL] = {
            mp = 120,
            def = 50
        },
        [STR.HASTE] = {
            cd = 80
        },
        [STR.RUTHLESS] = {
            cd = 200
         },
         },
         Light = {
         [STR.POWERFUL] = {
             multi_mp_cost = 80
             cd = 150
         },
         },
         Critical = {
         [STR.REGEN1] = {
             multi_mp_cost = 120
             chance = 50,
            mp_recovery = 50
         },
         },
         Haste = {
         [STR.REGEN2] = {
             multi_cooldown = 80
             chance = 50,
            cd = 50
         },
         },
         Ruthless = {
         [STR.REVERSED] = {
             multi_cooldown = 200
             mp = 50,
            cd = 150
         },
         },
         Powerful = {
         [STR.USEFUL] = {
             multi_cooldown = 150
             dmg = 80
         }
         }
     }
     }


    -- Define the class blueprint for traits.
    Trait = {}
    function Trait:new(t)
        t = t or {}
        -- Define all values taken from arguments.
        t.details_mp_cost = {
            [1] = args.mp,
            [2] = args.mp_pvp,
            [3] = args.mp_enhanced
        }
        t.details_cooldown = {
            [1] = args.cd,
            [2] = args.cd_pvp,
            [3] = args.cd_enhanced
        }
        t.details_duration = {
            [1] = args.duration,
            [2] = args.duration_pvp,
            [3] = args.duration_enhanced
        }
        -- Automatic math and pvp/enhanced check
        local has_multiple_rows;
        t.multiple_rows = {}
        for k, v in pairs(t) do
            if string.find(k, 'details_') then
                for k2, v2 in pairs(v) do
                    local kind = k:gsub('details_', '')
                    if (t.multiple_rows[kind] == nil) then
                        t.multiple_rows[kind] = {}
                    end
                    if (k2 == 2) then
                        t.multiple_rows[kind]['pvp'] = true
                    elseif (k2 == 3) then
                        t.multiple_rows[kind]['enhanced'] = true
                    end
                    t[k][k2] = frame:preprocess('{{#expr: ' .. v2 .. '}}')
                end
            end
        end
        -- Solution for custom headers
        for k, v in pairs(args) do
            if string.find(k, 'detail') then
                if t.multiple_rows[k] == nil then
                    t.multiple_rows[k] = {}
                end
                if string.find(k, '_pvp') then
                    local kind = k:gsub('_pvp', '')
                    t.multiple_rows[kind] = {}
                    t.multiple_rows[kind]['pvp'] = true
                elseif string.find(k, '_enhanced') then
                    local kind = k:gsub('_enhanced', '')
                    t.multiple_rows[kind] = {}
                    t.multiple_rows[kind]['enhanced'] = true
                end
            end
        end
        -- Check if multiple_rows property has ANY values in the 2nd depth
        for k, v in pairs(t.multiple_rows) do
            if next(v) ~= nil then
                has_multiple_rows = true
            end
        end
        -- Figure out how many columns there is to span
        if has_multiple_rows and t.index == 1 then
            t.mode_row = true
        end
        t.colspan = 1
        t.colspan = t.colspan + table.matches(header_dict, t.name)
        -- Get multipliers from args
        t.multi_mp_cost = args['mp' .. t.index] or default_multiplier
        t.multi_cooldown = args['cd' .. t.index] or default_multiplier
        t.multi_duration = args['duration' .. t.index] or default_multiplier
        t.chance = args['chance' .. t.index]
        t.unnamed_2 = args['desc1_trait' .. t.index]
        t.unnamed_3 = args['desc2_trait' .. t.index]
        -- Set defaults for generic trait values.
        local default = default_trait_values[t.name]
        if (default ~= nil) then
            for k, v in pairs(default) do
                if (t[k] == default_multiplier) then
                    t[k] = v
                end
            end
        end
        -- Figure out if there are any pvp or enhanced values
        for k, v in pairs(t.multiple_rows) do
            for k2, v2 in pairs(v) do
                if k2 == 'pvp' and v2 == true then
                    t.has_pvp = true
                end
                if k2 == 'enhanced' and v2 == true then
                    t.has_enhanced = true
                end
            end
        end
        self.__index = self
        setmetatable(t, self)
        return t
    end
    function Trait:onError(message)
        assert(false, message)
    end
    -- Calculation method
    function Trait:calc()
        for k, v in pairs(self) do
            if (string.find(k, 'details_')) then
                local current_data = k:gsub('details_', '')
                local current_multiplier = self['multi_' .. current_data]
                -- Handle errors
                if not next(v) and current_multiplier ~= default_multiplier then
                    self:onError("Cannot calculate the '" .. current_data .. "' property that is not defined")
                end
                local requires_current_data
                for k1, v1 in pairs(header_dict) do
                    for k2, v2 in pairs(v1) do
                        if v2 == self.name and properties[k1] == current_data then
                            requires_current_data = true
                        end
                    end
                end
                if current_multiplier == default_multiplier and next(v) ~= nil and requires_current_data then
                    self:onError("Cannot calculate the '" .. current_data .. "' property: " .. self.name ..
                                    " trait description is not enough")
                end
                for k2, v2 in pairs(v) do
                    self[k][k2] = current_multiplier / 100 * v2
                    if current_data == 'mp_cost' then
                        self[k][k2] = self[k][k2] .. ' MP'
                    else
                        self[k][k2] = self[k][k2] .. ' Seconds'
                    end
                end
            end
        end
        return self
    end
    local trait_left = Trait:new({
        name = traits[1],
        index = 1
    })
    local trait_right = Trait:new({
        name = traits[2],
        index = 2
    })
    -- Main block
    -- Get table head color
     local function color(char)
     local function color(char)
         char = char or args[1] or args.char
         char = char or args[1] or args.char or 'Elsword'
         return char:gsub('/', '')
         return char:gsub('/', '')
     end
     end
Line 237: Line 98:
     })
     })


     function newtr()
     function new_row()
         return trait_table:tag('tr')
         return trait_table:tag('tr')
     end
     end


     local thead = newtr()
     -- headers
     local tr = newtr()
     local tr1 = new_row()
     local tr_2 = newtr()
     local tr2 = new_row()
 
     local tr3 = new_row()
     -- Header spawning method
    function Trait:do_headers(tr)
        local function getCustomHeader(spawn)
            local i = 1
            while true do
                local custom_header = args['header' .. i .. '_trait' .. self.index]
                if custom_header ~= nil then
                    if spawn then
                        tr_2:tag('th'):wikitext(custom_header)
                    end
                    i = i + 1
                else
                    break
                end
            end
            return i - 1
        end
 
        if self.mode_row then
            if self.has_enhanced == true then
                thead:tag('th'):wikitext('Stage'):attr('rowspan', '2')
            else
                thead:tag('th'):wikitext('Mode'):attr('rowspan', '2')
            end
        end
 
        thead:tag('th'):attr('colspan', self.colspan + getCustomHeader()):wikitext(self.name .. ' ' .. skill)
 
        tr_2:tag('th'):wikitext('Attribute Effect')


         local has_details = table.matches(header_dict, self.name);
    -- Loop through 2 input traits.
    for trait_count, trait_name in ipairs(traits) do
         local th = tr1:tag('th'):wikitext(trait_name .. ' ' .. skill);
        local th_effect;
        local th_skilltext;
        local detail_list = {}
        local default_value = DEFAULT[trait_name] or {};


         if has_details then
         -- Check if detail fields are required and which.
            if self.multi_mp_cost ~= default_multiplier then
        for detail_key, detail in ipairs(details) do
                if self.name == 'Regenerating (1)' then
             if not th_effect then
                    tr_2:tag('th'):wikitext('MP Recovery')
                 th_effect = tr2:tag('th'):wikitext('Attribute Effect');
                else
                    tr_2:tag('th'):wikitext('MP Usage')
                end
            end
            if self.multi_cooldown ~= default_multiplier then
                tr_2:tag('th'):wikitext('Cooldown')
            end
             if self.multi_duration ~= default_multiplier then
                 local duration_name = args.duration_name or ''
                if duration_name then
                    duration_name = duration_name .. ' '
                end
                tr_2:tag('th'):wikitext(duration_name .. 'Duration')
             end
             end
             if self.name == 'Useful' then
             if indexOf(trait_name, detail) then
                 tr_2:tag('th'):wikitext('Max Hits')
                 local th_detail = tr2:tag('th'):wikitext(prop[detail_key]);
                th:attr('colspan', tonumber(th:getAttr('colspan') or 1) + 1)
             end
             end
         end
         end
        getCustomHeader(true)
    end


    trait_left:do_headers()
        -- Unnamed argument.
    trait_right:do_headers()
        local unnamed = split(args[3 + trait_count]);


    trait_left:calc()
         -- Append contents.
    trait_right:calc()
         for detail_key, detail in ipairs(details) do
 
             if not th_skilltext then
    function Trait:do_content(tr, mode)
                 th_skilltext = tr3:tag('td'):wikitext(frame:expandTemplate{
 
                    title = 'SkillText',
         -- Generate appropriate headers on the left
                     args = {
         if self.mode_row then
                        trait_name,
             if mode then
                        unnamed[1],
                 if self.has_enhanced == true then
                        unnamed[2],
                    if mode == 'enhanced' then
                        MP = args['mp' .. trait_count] or args['mp_recovery' .. trait_count] or default_value['mp'] or
                        tr:tag('td'):wikitext("'''[Enhanced]'''")
                            default_value['mp_recovery'],
                    end
                        CD = args['cd' .. trait_count] or default_value['cd'],
                elseif mode == 'pvp' then
                        DURATION = args['duration' .. trait_count],
                    tr:tag('td'):wikitext(frame:expandTemplate{
                        CHANCE = args['chance' .. trait_count] or default_value['chance'],
                        title = 'PvP'
                         DAMAGE = args['dmg' .. trait_count] or default_value['dmg']
                     })
                     }
                end
                 });
            else
                if self.has_enhanced == true then
                    tr:tag('td'):wikitext("'''Normal'''")
                else
                    tr:tag('td'):wikitext(frame:expandTemplate{
                         title = 'PvE'
                     })
                 end
             end
             end
        end
             if indexOf(trait_name, detail) then
 
                 local short_detail = prop_short[detail_key]
        -- Generate trait descriptions
                 local detail_content = args[short_detail .. trait_count]
        if not mode then
                 local multiplier = detail_content or default_value[short_detail]
            local rowspan = 1
                 if multiplier ~= nil then
             if next(self.multiple_rows) ~= nil then
                     local suffix = ''
                 rowspan = 2
                     if short_detail == 'mp' or short_detail == 'mp_recovery' then
            end
                         suffix = STR.MP
            tr:tag('td'):wikitext(frame:expandTemplate{
                     elseif short_detail == 'cd' or short_detail == 'duration' then
                 title = 'SkillText',
                         suffix = STR.SEC
                args = {
                    self.name,
                    self.unnamed_2,
                    self.unnamed_3,
                    MP = self.multi_mp_cost,
                    CD = self.multi_cooldown,
                    DURATION = self.multi_duration,
                    CHANCE = self.chance
                 }
            }):attr('rowspan', rowspan)
        end
 
        -- Fill cells with values
        local function addIfMultiExists(param_tbl)
            for k, v in ipairs(param_tbl) do
                 if (self['multi_' .. v] ~= default_multiplier) then
                     local info;
 
                     if mode == 'enhanced' then
                         info = self['details_' .. v][3]
                     elseif mode == 'pvp' then
                        info = self['details_' .. v][2]
                    else
                         info = self['details_' .. v][1]
                     end
                     end
 
                     local end_value = (tonumber(multiplier) / 100) * tonumber(args[short_detail] or 0);
                     if (info ~= nil) then
                    if end_value <= 0 then
                        local td = tr:tag('td'):wikitext(info)
                         end_value = '-'
 
                        if next(self.multiple_rows[v]) ~= nil then
                            td:attr('rowspan', 1)
                         else
                            td:attr('rowspan', 2)
                        end
                     end
                     end
 
                    local detail_cell = tr3:tag('td'):wikitext(end_value .. suffix);
                 end
                 end
             end
             end
         end
         end
        addIfMultiExists({'mp_cost', 'cooldown', 'duration'})
        -- Custom data support
        local function getCustomContent()
            local i = 1
            local y = 1
            while true do
                local mode_str = mode or ''
                if mode_str ~= '' then
                    mode_str = '_' .. mode_str
                end
                local custom_content_arg = 'detail' .. i .. '_trait' .. self.index .. mode_str;
                local custom_content = args[custom_content_arg]
                if custom_content ~= nil then
                    local rowspan = 1
                    for k, v in pairs(self.multiple_rows) do
                        if k == custom_content_arg and next(v) == nil then
                            rowspan = 2
                        end
                    end
                    tr:tag('td'):wikitext(custom_content):attr('rowspan', rowspan)
                    i = i + 1
                else
                    y = y + 1
                    if (y == 5) then
                        break
                    else
                        i = i + 1
                    end
                end
            end
        end
        getCustomContent()


     end
     end
    local tr = trait_table:tag('tr')
    trait_left:do_content(tr)
    trait_right:do_content(tr)
    local tr_2 = trait_table:tag('tr')
    -- Will use the same table row. Enhanced + pvp not supported for now.
    trait_left:do_content(tr_2, 'enhanced')
    trait_right:do_content(tr_2, 'enhanced')
    trait_left:do_content(tr_2, 'pvp')
    trait_right:do_content(tr_2, 'pvp')


     return tostring(trait_table);
     return tostring(trait_table);
    -- return dump(trait_left) .. '<br><br>' .. dump(trait_right);


end
end


return p
return p
-- pyend

Revision as of 22:47, 28 October 2022

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

-- pystart
require('Module:CommonFunctions')
local getArgs = require('Module:Arguments').getArgs
local p = {}

-- Main process
function p.main(frame)
    local args = getArgs(frame);

    -- Argument init
    local traits = args[3] or args.traits
    if (traits == nil) then
        traits = '-, -'
    end
    traits = split(traits);
    for k, v in ipairs(traits) do
        traits[k] = trim(v)
    end
    local skill = args[2] or args.skill
    local default_multiplier = 100

    -- Dictionary for headers
    local prop = {'MP Usage', 'Cooldown', 'Duration', 'MP Recovery', 'Max Hits'}

    local prop_short = {'mp', 'cd', 'duration', 'mp_recovery', 'dmg', 'chance'}

    local STR = {
        LIGHT = 'Light',
        CRITICAL = 'Critical',
        REVERSED = 'Reversed',
        HEAVY = 'Heavy',
        HASTE = 'Haste',
        REGEN1 = 'Regenerating (1)',
        REGEN2 = 'Regenerating (2)',
        KB1 = 'Killing Blow (1)',
        KB2 = 'Killing Blow (2)',
        RUTHLESS = 'Ruthless',
        POWERFUL = 'Powerful',
        USEFUL = 'Useful',
        SEC = ' Seconds',
        MP = ' MP'
    }

    local details = {{STR.LIGHT, STR.CRITICAL, STR.REVERSED},
                     {STR.HEAVY, STR.HASTE, STR.REGEN2, STR.RUTHLESS, STR.POWERFUL, STR.REVERSED}, {STR.KB1},
                     {STR.REGEN1}, {STR.USEFUL}}

    -- Default values
    local DEFAULT = {
        [STR.HEAVY] = {
            cd = 120
        },
        [STR.LIGHT] = {
            mp = 80
        },
        [STR.CRITICAL] = {
            mp = 120,
            def = 50
        },
        [STR.HASTE] = {
            cd = 80
        },
        [STR.RUTHLESS] = {
            cd = 200
        },
        [STR.POWERFUL] = {
            cd = 150
        },
        [STR.REGEN1] = {
            chance = 50,
            mp_recovery = 50
        },
        [STR.REGEN2] = {
            chance = 50,
            cd = 50
        },
        [STR.REVERSED] = {
            mp = 50,
            cd = 150
        },
        [STR.USEFUL] = {
            dmg = 80
        }
    }

    local function color(char)
        char = char or args[1] or args.char or 'Elsword'
        return char:gsub('/', '')
    end

    local trait_table = mw.html.create('div'):attr('class', 'content-table'):tag('table'):attr({
        cellpadding = '5',
        border = '1',
        class = 'colortable-' .. color()
    }):css({
        ['border-collapse'] = 'collapse',
        ['text-align'] = 'center'
    })

    function new_row()
        return trait_table:tag('tr')
    end

    -- headers
    local tr1 = new_row()
    local tr2 = new_row()
    local tr3 = new_row()

    -- Loop through 2 input traits.
    for trait_count, trait_name in ipairs(traits) do
        local th = tr1:tag('th'):wikitext(trait_name .. ' ' .. skill);
        local th_effect;
        local th_skilltext;
        local detail_list = {}
        local default_value = DEFAULT[trait_name] or {};

        -- Check if detail fields are required and which.
        for detail_key, detail in ipairs(details) do
            if not th_effect then
                th_effect = tr2:tag('th'):wikitext('Attribute Effect');
            end
            if indexOf(trait_name, detail) then
                local th_detail = tr2:tag('th'):wikitext(prop[detail_key]);
                th:attr('colspan', tonumber(th:getAttr('colspan') or 1) + 1)
            end
        end

        -- Unnamed argument.
        local unnamed = split(args[3 + trait_count]);

        -- Append contents.
        for detail_key, detail in ipairs(details) do
            if not th_skilltext then
                th_skilltext = tr3:tag('td'):wikitext(frame:expandTemplate{
                    title = 'SkillText',
                    args = {
                        trait_name,
                        unnamed[1],
                        unnamed[2],
                        MP = args['mp' .. trait_count] or args['mp_recovery' .. trait_count] or default_value['mp'] or
                            default_value['mp_recovery'],
                        CD = args['cd' .. trait_count] or default_value['cd'],
                        DURATION = args['duration' .. trait_count],
                        CHANCE = args['chance' .. trait_count] or default_value['chance'],
                        DAMAGE = args['dmg' .. trait_count] or default_value['dmg']
                    }
                });
            end
            if indexOf(trait_name, detail) then
                local short_detail = prop_short[detail_key]
                local detail_content = args[short_detail .. trait_count]
                local multiplier = detail_content or default_value[short_detail]
                if multiplier ~= nil then
                    local suffix = ''
                    if short_detail == 'mp' or short_detail == 'mp_recovery' then
                        suffix = STR.MP
                    elseif short_detail == 'cd' or short_detail == 'duration' then
                        suffix = STR.SEC
                    end
                    local end_value = (tonumber(multiplier) / 100) * tonumber(args[short_detail] or 0);
                    if end_value <= 0 then
                        end_value = '-'
                    end
                    local detail_cell = tr3:tag('td'):wikitext(end_value .. suffix);
                end
            end
        end

    end

    return tostring(trait_table);

end

return p
-- pyend