Module:Passive skill: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
(Trying to add some visuals and support for icons)
(Undo revision 1125280 by Angelic knight (talk))
Line 1: Line 1:
--
--
-- Module for Mastery Groups
-- Module for passive skills
--
--


Line 15: Line 15:


local i18n = {
local i18n = {
     icon_name = 'File:%s mastery icon.png',
     icon_name = 'File:%s passive skill icon.png',
   
     cats = {
     cats = {
        effect = 'Mastery Group',
         data = 'Passive skill data',
         data = 'Passive skill data',
          
          
Line 40: Line 40:
     },
     },
      
      
     mastery_group_box_table = {
     passive_box_table = {
         id = 'Id',
         id = 'Id',
         mastery_effects = 'Mastery Effects',
         int_id = 'Integer Id',
        flavour_text = 'Flavour Text',
        reminder_text = 'Reminder Text',
        skill_points = 'Skill Points Granted',
        ascendancy_class = 'Ascendancy Class',
        connections = 'Connections',
     },
     },
      
      
Line 52: Line 57:
      
      
     errors = {
     errors = {
         no_passives_found = 'No mastery groups with the given name were found',
         no_passives_found = 'No passive skills with the given name found',
     },
     },
}
}
Line 62: Line 67:
local tables = {}
local tables = {}


tables.mastery_groups = {
tables.passive_skills = {
     table = 'mastery_groups',
     table = 'passive_skills',
     order = {'id', 'main_page', 'mastery_effects',},
     order = {'id', 'int_id', 'name', 'main_page', 'flavour_text', 'reminder_text', 'buff_id', 'skill_points', 'icon', 'ascendancy_class', 'is_keystone', 'is_notable', 'is_multiple_choice_option', 'is_multiple_choice', 'is_icon_only', 'is_jewel_socket', 'is_ascendancy_starting_node', 'stat_text', 'stat_text_raw', 'connections',},
     fields = {
     fields = {
         id = {
         id = {
Line 70: Line 75:
             type = 'String',
             type = 'String',
             required = true,
             required = true,
        },
        int_id = {
            field = 'int_id',
            type = 'Integer',
            required = true,
        },
        name = {
            field = 'name',
            type = 'String',
         },
         },
         main_page = {
         main_page = {
             field = 'main_page',
             field = 'main_page',
             type = 'Page',
             type = 'Page',
        },
        flavour_text = {
            field = 'flavour_text',
            type = 'Text',
        },
        reminder_text = {
            field = 'reminder_text',
            type = 'Text',
        },
        buff_id = {
            field = 'buff_id',
            type = 'String',
        },
        -- TODO: Other buff stuff
        skill_points = {
            field = 'skill_points',
            type = 'Integer',
            default = 0,
         },
         },
         icon = {
         icon = {
Line 84: Line 116:
             end
             end
         },
         },
         mastery_effects = {
         ascendancy_class = {
             field = 'mastery_effects',
             field = 'ascendancy_class',
            type = 'String',
        },
        is_keystone = {
            field = 'is_keystone',
            type = 'Boolean',
            default = false,
        },
        is_notable = {
            field = 'is_notable',
            type = 'Boolean',
            default = false,
        },
        is_multiple_choice_option = {
            field = 'is_multiple_choice_option',
            type = 'Boolean',
            default = false,
        },
        is_multiple_choice = {
            field = 'is_multiple_choice',
            type = 'Boolean',
            default = false,
        },
        is_icon_only = {
            field = 'is_icon_only',
            type = 'Boolean',
            default = false,
        },
        is_jewel_socket = {
            field = 'is_jewel_socket',
            type = 'Boolean',
            default = false,
        },
        is_ascendancy_starting_node = {
            field = 'is_ascendancy_starting_node',
            type = 'Boolean',
            default = false,
        },
        stat_text = {
            field = 'stat_text',
            type = 'Text',
        },
        stat_text_raw = {
            field = 'stat_text',
            type = 'Text',
            func = function (tpl_args, frame)
                if tpl_args.stat_text then
                    tpl_args.stat_text_raw = string.gsub(
                        -- [[x]] -> x
                        string.gsub(
                            tpl_args.stat_text, '%[%[([^%]|]+)%]%]', '%1'
                        ),
                        -- [[x|y]] -> y
                        '%[%[[^|]+|([^%]|]+)%]%]', '%1'
                    )
                end
                return tpl_args.stat_text_raw
            end
        },
        -- from the graph file:
        connections = {
            field = 'connections',
             type = 'List (,) of String',
             type = 'List (,) of String',
         },
         },
Line 91: Line 184:
}
}


tables.passive_skill_stats = {
    table = 'passive_skill_stats',
    fields = {
        id = {
            field = 'id',
            type = 'String',
        },
        value = {
            field = 'value',
            type = 'Integer',
        },
    }
}


local display = {}
local display = {}
Line 97: Line 203:
     {
     {
         key = 'id',
         key = 'id',
         header = i18n.mastery_group_box_table.id,
         header = i18n.passive_box_table.id,
        display = nil,
    },
    {
        key = 'int_id',
        header = i18n.passive_box_table.int_id,
        display = nil,
    },
    {
        css = 'tc -flavour',
        key = 'flavour_text',
        header = i18n.passive_box_table.flavour_text,
         display = nil,
         display = nil,
     },
     },
     {
     {
         key = 'mastery_effects',
         key = 'reminder_text',
         header = i18n.mastery_group_box_table.mastery_effects,
         header = i18n.passive_box_table.reminder_text,
        display = nil,
    },
    {
        key = 'skill_points',
        header = i18n.passive_box_table.skill_points,
        display = nil,
    },
    {
        key = 'ascendancy_class',
        header = i18n.passive_box_table.ascendancy_class,
        display = function (tpl_args, frame, value)
            return string.format('[[%s]]', value)
        end,
    },
    {
        key = 'connections',
        header = i18n.passive_box_table.connections,
         display = function (tpl_args, frame, value)
         display = function (tpl_args, frame, value)
             local results = m_cargo.map_results_to_id{
             local results = m_cargo.map_results_to_id{
                 field='mastery_effects.id',
                 field='passive_skills.id',
                 results=m_cargo.array_query{
                 results=m_cargo.array_query{
                     tables={'mastery_effects'},
                     tables={'passive_skills'},
                     fields={'mastery_effects.id', 'mastery_effects._pageName', 'mastery_effects.stat_text'},
                     fields={'passive_skills.name', 'passive_skills._pageName'},
                     id_array=value,
                     id_array=value,
                     id_field='mastery_effects.id',
                     id_field='passive_skills.id',
                     ignore_missing=true,
                     ignore_missing=true,
                 }
                 }
Line 123: Line 257:
                 local text
                 local text
                 if row then
                 if row then
                     text = string.format('[[%s|%s]]', row['mastery_effects._pageName'], row['mastery_effects.id'] or row['mastery_effects._pageName'])
                     text = string.format('[[%s|%s]]', row['passive_skills._pageName'], row['passive_skills.name'] or row['passive_skills._pageName'])
                 else
                 else
                     text = key
                     text = key
Line 144: Line 278:
local h = {}
local h = {}


function h.format_mastery_icon(mastery_group)
function h.format_passive_icon(passive, passive_type)
     if mastery_group['mastery_groups.icon'] == nil then
     if passive['passive_skills.icon'] == nil then
         return ''
         return ''
     end
     end
      
      
     local cls = 'passive-icon-type__mastery'
     local cls = string.format('passive-icon-type__%s', passive_type)
     local main_page = mastery_group['mastery_groups.main_page'] or mastery_group['main_pages._pageName'] or mastery_group['mastery_groups.name'] or mastery_group['mastery_groups.icon']
     local main_page = passive['passive_skills.main_page'] or passive['main_pages._pageName'] or passive['passive_skills.name'] or passive['passive_skills.icon']
     div = mw.html.create('div')
     div = mw.html.create('div')
     div:addClass('passive-icon-container')
     div:addClass('passive-icon-container')
Line 160: Line 294:
         string.format(
         string.format(
             '[[%s|link=%s]]',  
             '[[%s|link=%s]]',  
             passive['mastery_groups.icon'],  
             passive['passive_skills.icon'],  
             main_page
             main_page
         )
         )
Line 168: Line 302:
end
end


-- function h.make_stat_order(results)
function h.make_stat_order(results)
--     local stats = {}
     local stats = {}
--     local stat_order = {}
     local stat_order = {}
--    for _, row in ipairs(results) do
--        local stat = row['passive_skills.stat_text']
--        -- Can't show results here that don't have a stat line
--        if stat then
--            if stats[stat] == nil then
--                stats[stat] = {row}
--                table.insert(stat_order, stat)
--            else
--                table.insert(stats[stat], row)
--            end
--        end
--    end
   
--    return stats, stat_order
-- end
 
-------------------------------------
-- Parameters:
--  results: the set of mastery_groups matching the query
-------------------------------------
function h.make_effects_order(results)
    local mastery_effects_list = {}
    local me_order = {}
     for _, row in ipairs(results) do  
     for _, row in ipairs(results) do  
         local mastery_effects = row['mastery_groups.mastery_effects']
         local stat = row['passive_skills.stat_text']
         -- Can't show results here that don't have a stat line
         -- Can't show results here that don't have a stat line
         if mastery_effects then
         if stat then
             if mastery_effects_list[mastery_effects] == nil then
             if stats[stat] == nil then
                 mastery_effects_list[mastery_effects] = {row}
                 stats[stat] = {row}
                 table.insert(me_order, mastery_effects)
                 table.insert(stat_order, stat)
             else
             else
                 table.insert(mastery_effects_list[mastery_effects], row)
                 table.insert(stats[stat], row)
             end
             end
         end
         end
     end
     end
      
      
     return mastery_effects_list, me_order
     return stats, stat_order
end
end


-- function h.stat_page_links(stat_order, stats)
function h.stat_page_links(stat_order, stats)
--    local out = {}
--    for i, key in ipairs(stat_order) do
--        local links = {}
--        for j, row in ipairs(stats[key]) do
--            links[#links+1] = string.format('[[%s|[%s]]]', row['passive_skills._pageName'], j)
--        end
--        out[i] = string.format('<span class="passive-line">%s <span class="passive-hover">%s</span></span>', key, table.concat(links, ' '))
--    end
   
--    return table.concat(out, '<hr>')
-- end
 
function h.effect_page_links(effects_order, effects_list)
     local out = {}
     local out = {}
     for i, key in ipairs(effects_order) do
     for i, key in ipairs(stat_order) do
         local links = {}
         local links = {}
         for j, row in ipairs(effects_list[key]) do
         for j, row in ipairs(stats[key]) do
             links[#links+1] = string.format('[[%s|&#91;%s&#93;]]', row['mastery_groups._pageName'], j)
             links[#links+1] = string.format('[[%s|&#91;%s&#93;]]', row['passive_skills._pageName'], j)
         end
         end
         out[i] = string.format('<span class="passive-line">%s <span class="passive-hover">%s</span></span>', key, table.concat(links, ' '))
         out[i] = string.format('<span class="passive-line">%s <span class="passive-hover">%s</span></span>', key, table.concat(links, ' '))
Line 236: Line 334:
end
end


-- function h.intro_text(tpl_args, frame)
h.type_order = {'basic', 'notable', 'keystone', 'ascendancy_basic', 'ascendancy_notable'}
--     --[[
function h.get_type(passive)
--     Display an introductory text about the passive skill.
    local key
--     ]]
    if tonumber(passive['passive_skills.is_keystone']) == 1 then
--     local out = {}
        key = 'keystone'
--     if mw.ustring.find(tpl_args['id'], '_') then
    elseif tonumber(passive['passive_skills.is_notable']) == 1 then
--         out[#out+1] = frame:expandTemplate{
        key = 'notable'
--             title='Incorrect title',  
    else
--             args = {title=tpl_args['id']}  
        key = 'basic'
--         }
    end
--     end
   
    if passive['passive_skills.ascendancy_class'] ~= nil then
        key = 'ascendancy_' .. key
    end
   
    return key
end
 
function h.sort_by_type(results)
    local new = {}
    for _, key in ipairs(h.type_order) do
        new[key] = {}
    end
   
    for _, passive in ipairs(results) do
        table.insert(new[h.get_type(passive)], passive)
    end
   
    return new
end
 
function h.intro_text(tpl_args, frame)
     --[[
     Display an introductory text about the passive skill.
     ]]
     local out = {}
     if mw.ustring.find(tpl_args['id'], '_') then
         out[#out+1] = frame:expandTemplate{
             title='Incorrect title',  
             args = {title=tpl_args['id']}  
         }
     end
      
      
--     if tpl_args['name'] then
     if tpl_args['name'] then
--         out[#out+1] = string.format(
         out[#out+1] = string.format(
--             i18n.intro.text_with_name,  
             i18n.intro.text_with_name,  
--             tpl_args['id'],  
             tpl_args['id'],  
--             tpl_args['main_page'] or tostring(mw.title.getCurrentTitle()),  
             tpl_args['main_page'] or tostring(mw.title.getCurrentTitle()),  
--             tpl_args['name']
             tpl_args['name']
--         )
         )
--     else  
     else  
--         out[#out+1] = string.format(
         out[#out+1] = string.format(
--             i18n.intro.text_without_name,
             i18n.intro.text_without_name,
--             tpl_args['id']
             tpl_args['id']
--         )
         )
--     end  
     end  
      
      
--     return table.concat(out)
     return table.concat(out)
-- end
end


-- function h.stat_box(tpl_args, frame)
function h.stat_box(tpl_args, frame)
--     --[[
     --[[
--     Display the stat box.
     Display the stat box.
--     ]]
     ]]
--     local container = mw.html.create('div')
     local container = mw.html.create('div')
--     container
     container
--         :attr('class', 'modbox floatright')
         :attr('class', 'modbox floatright')
          
          
--     -- stat table  
     -- stat table  
--     tbl = container:tag('table')
     tbl = container:tag('table')
--     tbl
     tbl
--         :attr('class', 'wikitable sortable')
         :attr('class', 'wikitable sortable')
--         -- :attr('style', 'style="width: 100%;"')
         -- :attr('style', 'style="width: 100%;"')
--         :tag('tr')
         :tag('tr')
--             :tag('th')
             :tag('th')
--                 :attr('colspan', 3)
                 :attr('colspan', 3)
--                 :wikitext('Stats')
                 :wikitext('Stats')
--                 :done()
                 :done()
--             :done()
             :done()
--         :tag('tr')
         :tag('tr')
--             :tag('th')
             :tag('th')
--                 :wikitext('#')
                 :wikitext('#')
--                 :done()
                 :done()
--             :tag('th')
             :tag('th')
--                 :wikitext('Stat Id')
                 :wikitext('Stat Id')
--                 :done()
                 :done()
--             :tag('th')
             :tag('th')
--                 :wikitext('Value')
                 :wikitext('Value')
--                 :done()
                 :done()
--             :done()
             :done()
--             :done()
             :done()
--         :done()
         :done()
          
          
--     local i = 0
     local i = 0
--     local value = nil
     local value = nil
--     repeat
     repeat
--         i = i + 1
         i = i + 1
--         value = {
         value = {
--             id = tpl_args[string.format('stat%s_id', i)],
             id = tpl_args[string.format('stat%s_id', i)],
--             value = tpl_args[string.format('stat%s_value', i)],
             value = tpl_args[string.format('stat%s_value', i)],
--         }
         }
          
          
--         if value.id then
         if value.id then
--             tbl
             tbl
--                 :tag('tr')
                 :tag('tr')
--                     :tag('td')
                     :tag('td')
--                         :wikitext(i)
                         :wikitext(i)
--                         :done()
                         :done()
--                     :tag('td')
                     :tag('td')
--                         :wikitext(value.id)
                         :wikitext(value.id)
--                         :done()
                         :done()
--                     :tag('td')
                     :tag('td')
--                         :wikitext(value.value)
                         :wikitext(value.value)
--                         :done()
                         :done()
--                     :done()
                     :done()
--                 :done()
                 :done()
--         end
         end
--     until value.id == nil
     until value.id == nil
      
      
--     return tostring(container)
     return tostring(container)
-- end
end




Line 338: Line 467:


-- Declare cargo tables:
-- Declare cargo tables:
p.table_mastery_groups = m_cargo.declare_factory{data=tables.mastery_groups}
p.table_passive_skills = m_cargo.declare_factory{data=tables.passive_skills}
p.table_passive_skill_stats = m_cargo.declare_factory{data=tables.passive_skill_stats}


function p.mastery_group(frame)
function p.passive_skill(frame)
     --[[
     --[[
     Stores data and displays a infobox about the passive skill.
     Stores data and displays a infobox about the passive skill.
Line 346: Line 476:
     Examples
     Examples
     --------
     --------
     = p.mastery_group{
     = p.passive_skill{
         id = 'Armour',
         id = 'life_life_leech1629',
         mastery_effects = 'Armour1,Armour2,Armour3,Armour4,Armour5,Armour6',
         int_id = '27788',
        name = 'Blood Drinker',
        is_notable = 'True',
        icon = 'lifeleech',
        stat1_id = 'maximum_life_+%',
        stat1_value = '8',
        stat2_id = 'base_life_leech_from_attack_damage_permyriad',
        stat2_value = '40',
        stat_text = '8% increased maximum life<br>0.4% of Attack Damage Leeched as Life',
        connections = 'life1415,life1413',
     }
     }
      
      
Line 358: Line 497:
     })
     })
     frame = m_util.misc.get_frame(frame)
     frame = m_util.misc.get_frame(frame)
 
     -- parse  
     -- parse and store mastery group into cargo table
     m_util.args.from_cargo_map{
     m_util.args.from_cargo_map{
         tpl_args=tpl_args,
         tpl_args=tpl_args,
         frame=frame,
         frame=frame,
         table_map=tables.mastery_groups,
         table_map=tables.passive_skills,
     }
     }
   
    -- parse stats
    m_util.args.stats(tpl_args, {})
    for _, stat in ipairs(tpl_args.stats) do
        stat._table = tables.passive_skill_stats.table
        m_cargo.store(frame, stat)
    end
      
      
     --
     --
     -- Infobox
     -- Infobox
     --
     --
     -- local passive = {}
     local passive = {}
     -- for _, key in ipairs(display.map_to_property) do
     for _, key in ipairs(display.map_to_property) do
    --    local v = tpl_args[key]
        local v = tpl_args[key]
    --    if type(v) == 'boolean' then
        if type(v) == 'boolean' then
    --        if v then
            if v then
    --            v = 1
                v = 1
    --        else
            else
    --            v = 0
                v = 0
    --        end
            end
    --    end
        end
    --    passive[string.format('%s.%s', tables.passive_skills.table, tables.passive_skills.fields[key].field)] = v
        passive[string.format('%s.%s', tables.passive_skills.table, tables.passive_skills.fields[key].field)] = v
     -- end
     end
      
      
     -- local type_key = h.get_type(passive)
     local type_key = h.get_type(passive)
      
      
     -- local infocard_args = {}
     local infocard_args = {}
     -- infocard_args.header = tpl_args.name
     infocard_args.header = tpl_args.name
     -- infocard_args.subheader = i18n.passive_box[type_key]
     infocard_args.subheader = i18n.passive_box[type_key]
      
      
     -- local tbl = mw.html.create('table')
     local tbl = mw.html.create('table')
     -- for _, data in ipairs(display.tbl) do
     for _, data in ipairs(display.tbl) do
    --    local value = tpl_args[data.key]
        local value = tpl_args[data.key]
    --    -- if default is nil, this will be compared against nil which is what we want, so value ~= nil isn't needed
        -- if default is nil, this will be compared against nil which is what we want, so value ~= nil isn't needed
    --    if value ~= tables.passive_skills.fields[data.key].default then
        if value ~= tables.passive_skills.fields[data.key].default then
    --        local dsp
            local dsp
    --        if data.display then
            if data.display then
    --            dsp = data.display(tpl_args, frame, value)
                dsp = data.display(tpl_args, frame, value)
    --        else
            else
    --            dsp = value
                dsp = value
    --        end
            end
    --        tbl
            tbl
    --            :tag('tr')
                :tag('tr')
    --                :tag('th')
                    :tag('th')
    --                    :wikitext(data.header)
                        :wikitext(data.header)
    --                    :done()
                        :done()
    --                :tag('td')
                    :tag('td')
    --                    :attr('class', data.css)
                        :attr('class', data.css)
    --                    :wikitext(dsp)
                        :wikitext(dsp)
    --                    :done()
                        :done()
    --                :done()
                    :done()
    --    end
        end
     -- end
     end
      
      
     -- infocard_args[1] = tostring(tbl)
     infocard_args[1] = tostring(tbl)
     -- infocard_args[2] = tpl_args.stat_text
     infocard_args[2] = tpl_args.stat_text
     -- infocard_args[3] = h.format_mastery_icon(mastery_group)
     infocard_args[3] = h.format_passive_icon(passive, type_key)


     -- local out = {
     local out = {
    --    f_infocard(infocard_args),
        f_infocard(infocard_args),
    --    h.intro_text(tpl_args, frame),
        h.intro_text(tpl_args, frame),
    --    h.stat_box(tpl_args, frame),
        h.stat_box(tpl_args, frame),
     -- }
     }
      
      
     -- local cats = {
     local cats = {
    --    i18n.cats.data,
        i18n.cats.data,
     -- }
     }
     -- return table.concat(out) .. m_util.misc.add_category(cats)
     return table.concat(out) .. m_util.misc.add_category(cats)
end
end


function p.mastery_group_box(frame)
function p.passive_skill_box(frame)
     --[[
     --[[
     Queries a mastery group and displays it.
     Queries a passive skill and displays it.
      
      
     Examples
     Examples
     --------
     --------
     = p.mastery_group_box{name='Accuracy'}
     = p.passive_skill_box{name='Ghost Reaver'}
      
      
     ]]
     ]]
Line 447: Line 592:
      
      
     if not tpl_args.q_where and tpl_args.name then
     if not tpl_args.q_where and tpl_args.name then
         tpl_args.q_where = string.format('mastery_groups.id="%s" AND mastery_groups.mastery_effects > ""', tpl_args.name)
         tpl_args.q_where = string.format('passive_skills.name="%s" AND passive_skills.stat_text IS NOT NULL', tpl_args.name)
     elseif not (tpl_args.q_where and not tpl_args.name) then
     elseif not (tpl_args.q_where and not tpl_args.name) then
         error('q_where or name must be specified')
         error('q_where or name must be specified')
Line 453: Line 598:
      
      
     local results = m_cargo.query(
     local results = m_cargo.query(
         {'mastery_groups'},
         {'passive_skills'},
         {
         {
             'mastery_groups.icon',
             'passive_skills._pageName',
             'mastery_groups.mastery_effects',
            'passive_skills.main_page',
             'mastery_groups.id'
            'passive_skills.name',
            'passive_skills.stat_text',
            -- TODO: only really need these once, maybe put in extra query
            'passive_skills.flavour_text',
            'passive_skills.icon',
             'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
             'passive_skills.id'
         },  
         },  
         {
         {
             where=tpl_args.q_where,
             where=tpl_args.q_where,
             orderBy='mastery_groups.id',
             orderBy='passive_skills.stat_text',
             limit=5000,
             limit=5000,
         }
         }
Line 469: Line 622:
     end
     end
      
      
    results = h.sort_by_type(results)
     local out = {}
     local out = {}
     local cats = {}
     local cats = {}
     if #results > 0 then
     for _, type_key in ipairs(h.type_order) do
        cats[#cats+1] = i18n.cats[type_key]
        local type_results = results[type_key]
        local effects_list, effects_order = h.make_effects_order(results)
        if #type_results > 0 then
       
            cats[#cats+1] = i18n.cats[type_key]
        local mastery_group = results[1]
            local stats, stat_order = h.make_stat_order(type_results)
       
           
        local infocard_args = {}
            local passive = type_results[1]
        infocard_args.header = mastery_group['mastery_groups.id']
           
       
            local infocard_args = {}
        infocard_args[1] = h.format_mastery_icon(passive)
            infocard_args.header = passive['passive_skills.name']
        infocard_args[2] = h.effect_page_links(effects_order, effects_list)
            infocard_args.subheader = i18n.passive_box[type_key]
        infocard_args[3] = m_util.html.poe_color('flavour', passive['passive_skills.flavour_text'])
           
       
            infocard_args[1] = h.format_passive_icon(passive, type_key)
        out[#out+1] = f_infocard(infocard_args)
            infocard_args[2] = h.stat_page_links(stat_order, stats)
       
            infocard_args[3] = m_util.html.poe_color('flavour', passive['passive_skills.flavour_text'])
        -- Store as main page:
           
        for _, v in ipairs(results) do
            out[#out+1] = f_infocard(infocard_args)
            m_cargo.store(
           
                frame,  
            -- Store as main page:
                {
            for _, v in ipairs(type_results) do
                    _table='main_pages',  
                m_cargo.store(
                    id=v['mastery_groups.id'],
                    frame,  
                 }
                    {
             )
                        _table='main_pages',  
                        id=v['passive_skills.id'],
                    }
                 )
             end
         end
         end
     end
     end
Line 505: Line 663:
end
end


-- function p.passive_skill_link(frame)
function p.passive_skill_link(frame)
--     --[[
     --[[
--     Links a passive skill
     Links a passive skill
      
      
--     Examples
     Examples
--     --------
     --------
--     = p.passive_skill_link{id='AscendancyAscendant45', format='tablerow'}
     = p.passive_skill_link{id='AscendancyAscendant45', format='tablerow'}
--     ]]
     ]]
      
      
--     -- Get args
     -- Get args
--     tpl_args = getArgs(frame, {
     tpl_args = getArgs(frame, {
--         parentFirst = true
         parentFirst = true
--     })
     })
--     frame = m_util.misc.get_frame(frame)
     frame = m_util.misc.get_frame(frame)
      
      
--     tpl_args.name = tpl_args.name or tpl_args[1]
     tpl_args.name = tpl_args.name or tpl_args[1]
--     if tpl_args.name then
     if tpl_args.name then
--         tpl_args.q_where = string.format('passive_skills.name="%s"', tpl_args.name)
         tpl_args.q_where = string.format('passive_skills.name="%s"', tpl_args.name)
--     elseif tpl_args.id then
     elseif tpl_args.id then
--         tpl_args.q_where = string.format('passive_skills.id="%s"', tpl_args.id)
         tpl_args.q_where = string.format('passive_skills.id="%s"', tpl_args.id)
--     elseif tpl_args.q_where then
     elseif tpl_args.q_where then
--     else
     else
--         error('Either name, id or q_where must be specified')
         error('Either name, id or q_where must be specified')
--     end
     end
      
      
--     local results = m_cargo.query(
     local results = m_cargo.query(
--         {'passive_skills', 'main_pages'},
         {'passive_skills', 'main_pages'},
--         {
         {
--             'passive_skills._pageName',  
             'passive_skills._pageName',  
--             'passive_skills.stat_text',
             'passive_skills.stat_text',
--             'passive_skills.main_page',
             'passive_skills.main_page',
--             'passive_skills.name',
             'passive_skills.name',
--             'passive_skills.icon',
             'passive_skills.icon',
--             'passive_skills.is_keystone',
             'passive_skills.is_keystone',
--             'passive_skills.is_notable',
             'passive_skills.is_notable',
--             'passive_skills.ascendancy_class',
             'passive_skills.ascendancy_class',
--             'main_pages._pageName',
             'main_pages._pageName',
--         },  
         },  
--         {   
         {   
--             join='passive_skills.id=main_pages.id',
             join='passive_skills.id=main_pages.id',
--             where=string.format('(%s) AND passive_skills.stat_text IS NOT NULL', tpl_args.q_where),
             where=string.format('(%s) AND passive_skills.stat_text IS NOT NULL', tpl_args.q_where),
--             orderBy='passive_skills.stat_text',
             orderBy='passive_skills.stat_text',
--             limit=2,
             limit=2,
--         }
         }
--     )
     )
      
      
--     if #results > 1 then
     if #results > 1 then
--         error('Too many passives found!')
         error('Too many passives found!')
--     elseif #results < 1 then
     elseif #results < 1 then
--         error('No passives found')
         error('No passives found')
--     end
     end
--     local passive = results[1]
     local passive = results[1]
      
      
--     if tpl_args.format == 'tablerow' then
     if tpl_args.format == 'tablerow' then
--         local main_page = passive['passive_skills.main_page'] or passive['main_pages._pageName'] or passive['passive_skills.name']
         local main_page = passive['passive_skills.main_page'] or passive['main_pages._pageName'] or passive['passive_skills.name']
--         return string.format(
         return string.format(
--             '| [[%s|%s]]%s\n| %s',  
             '| [[%s|%s]]%s\n| %s',  
--             main_page,  
             main_page,  
--             passive['passive_skills.name'],  
             passive['passive_skills.name'],  
--             h.format_mastery_icon(passive),  
             h.format_passive_icon(passive, h.get_type(passive)),  
--             passive['passive_skills.stat_text']
             passive['passive_skills.stat_text']
--         )
         )
--     elseif tpl_args.format == nil then
     elseif tpl_args.format == nil then
--         return
         return
--     else
     else
--         error(string.format('Invalid return format specified: %s', tpl_args.format))
         error(string.format('Invalid return format specified: %s', tpl_args.format))
--     end
     end
-- end
end


-- -- Not sure whether we need a more sophisticated variant like item or mod tables here yet
-- Not sure whether we need a more sophisticated variant like item or mod tables here yet


-- function p.passive_skill_table2(frame)
function p.passive_skill_table2(frame)
--     -- Get args
     -- Get args
--     tpl_args = getArgs(frame, {
     tpl_args = getArgs(frame, {
--         parentFirst = true
         parentFirst = true
--     })
     })
--     frame = m_util.misc.get_frame(frame)
     frame = m_util.misc.get_frame(frame)
      
      
--     if tpl_args.q_tables then  
     if tpl_args.q_tables then  
--         tpl_args.q_tables = tpl_args.q_tables .. ',' .. 'passive_skill_stats'
         tpl_args.q_tables = tpl_args.q_tables .. ',' .. 'passive_skill_stats'
--     else
     else
--         tpl_args.q_tables = 'passive_skill_stats'
         tpl_args.q_tables = 'passive_skill_stats'
--     end
     end
      
      
--     if tpl_args.q_join then  
     if tpl_args.q_join then  
--         tpl_args.q_join = tpl_args.q_join .. ',' .. 'passive_skills._pageID=passive_skill_stats._pageID'
         tpl_args.q_join = tpl_args.q_join .. ',' .. 'passive_skills._pageID=passive_skill_stats._pageID'
--     else
     else
--         tpl_args.q_join = 'passive_skills._pageID=passive_skill_stats._pageID'
         tpl_args.q_join = 'passive_skills._pageID=passive_skill_stats._pageID'
--     end  
     end  
      
      
--     tpl_args.q_orderBy = 'passive_skills.ascendancy_class IS NULL DESC, passive_skills.is_keystone, passive_skills.is_notable, passive_skills.name'
     tpl_args.q_orderBy = 'passive_skills.ascendancy_class IS NULL DESC, passive_skills.is_keystone, passive_skills.is_notable, passive_skills.name'
      
      
--     return m_cargo.table_query{
     return m_cargo.table_query{
--         tpl_args=tpl_args,
         tpl_args=tpl_args,
--         frame=frame,
         frame=frame,
--         main_table='passive_skills',
         main_table='passive_skills',
--         row_unique_fields = {'passive_skills.name'},
         row_unique_fields = {'passive_skills.name'},
--         data={
         data={
--             tables = {
             tables = {
--                 passive_skill_stats = {
                 passive_skill_stats = {
--                     join='passive_skills._pageID=passive_skill_stats._pageID',
                     join='passive_skills._pageID=passive_skill_stats._pageID',
--                 },
                 },
--             },
             },
--             -- display data
             -- display data
--             {
             {
--                 args = {'ascendancy'},
                 args = {'ascendancy'},
--                 header = i18n.passive_table.ascendancy_class,
                 header = i18n.passive_table.ascendancy_class,
--                 fields = {
                 fields = {
--                     'passive_skills.ascendancy_class',
                     'passive_skills.ascendancy_class',
--                 },
                 },
--                 display = function (tpl_args, frame, tr, data)
                 display = function (tpl_args, frame, tr, data)
--                     local passive = data[1]
                     local passive = data[1]
--                     if passive['passive_skills.ascendancy_class'] then
                     if passive['passive_skills.ascendancy_class'] then
--                         tr:tag('td')
                         tr:tag('td')
--                             :wikitext(string.format('[[%s]]<br>[[File:%s avatar.png|link=%s]]', passive['passive_skills.ascendancy_class'], passive['passive_skills.ascendancy_class'], passive['passive_skills.ascendancy_class']))
                             :wikitext(string.format('[[%s]]<br>[[File:%s avatar.png|link=%s]]', passive['passive_skills.ascendancy_class'], passive['passive_skills.ascendancy_class'], passive['passive_skills.ascendancy_class']))
--                     else
                     else
--                         tr:wikitext(m_util.html.td.na{})
                         tr:wikitext(m_util.html.td.na{})
--                     end
                     end
--                 end,
                 end,
--                 order = 0,
                 order = 0,
--                 sort_type = 'text',
                 sort_type = 'text',
--             },
             },
--             {
             {
--                 args = nil,
                 args = nil,
--                 header = i18n.passive_table.name,
                 header = i18n.passive_table.name,
--                 fields = {
                 fields = {
--                     'passive_skills._pageName',
                     'passive_skills._pageName',
--                     'passive_skills.main_page',
                     'passive_skills.main_page',
--                     'passive_skills.name',
                     'passive_skills.name',
--                     'passive_skills.icon',
                     'passive_skills.icon',
--                     'passive_skills.is_keystone',
                     'passive_skills.is_keystone',
--                     'passive_skills.is_notable',
                     'passive_skills.is_notable',
--                     'passive_skills.ascendancy_class',
                     'passive_skills.ascendancy_class',
--                 },
                 },
--                 display = function (tpl_args, frame, tr, data)
                 display = function (tpl_args, frame, tr, data)
--                     local passive = data[1]
                     local passive = data[1]
--                     local type_key = h.get_type(passive)
                     local type_key = h.get_type(passive)
--                     tr
                     tr
--                         :tag('td')
                         :tag('td')
--                             :attr('data-sort-value', passive['passive_skills.name'] .. type_key)
                             :attr('data-sort-value', passive['passive_skills.name'] .. type_key)
--                             :wikitext(string.format('[[%s|%s]]<br>%s', passive['passive_skills.main_page'] or passive['passive_skills.name'], passive['passive_skills.name'], h.format_mastery_icon(passive))
                             :wikitext(string.format('[[%s|%s]]<br>%s', passive['passive_skills.main_page'] or passive['passive_skills.name'], passive['passive_skills.name'], h.format_passive_icon(passive, type_key)))
--                             :done()
                             :done()
--                 end,
                 end,
--                 order = 1000,
                 order = 1000,
--                 sort_type = 'text',
                 sort_type = 'text',
--                 options = {
                 options = {
--                     [7] = {
                     [7] = {
--                         optional=true,
                         optional=true,
--                     },
                     },
--                 },
                 },
--             },
             },
--             {
             {
--                 arg = {'default', 'stat', 'stats', 'stat_text'},
                 arg = {'default', 'stat', 'stats', 'stat_text'},
--                 header = i18n.passive_table.stats,
                 header = i18n.passive_table.stats,
--                 fields = {
                 fields = {
--                     'passive_skills.stat_text',
                     'passive_skills.stat_text',
--                 },
                 },
--                 display = function (tpl_args, frame, tr, data)
                 display = function (tpl_args, frame, tr, data)
--                     local passive = data[1]
                     local passive = data[1]
--                     local stats, stat_order = h.make_stat_order(data)
                     local stats, stat_order = h.make_stat_order(data)
--                     tr
                     tr
--                         :tag('td')
                         :tag('td')
--                             :wikitext(h.stat_page_links(stat_order, stats))
                             :wikitext(h.stat_page_links(stat_order, stats))
--                             :done()
                             :done()
--                 end,
                 end,
--                 order = 1001,
                 order = 1001,
--                 sort_type = 'text',
                 sort_type = 'text',
--             }
             }
--         },
         },
--     }
     }
-- end
end


-- function p.passive_skill_table(frame)
function p.passive_skill_table(frame)
--     -- Get args
     -- Get args
--     tpl_args = getArgs(frame, {
     tpl_args = getArgs(frame, {
--         parentFirst = true
         parentFirst = true
--     })
     })
--     frame = m_util.misc.get_frame(frame)
     frame = m_util.misc.get_frame(frame)
      
      
--     tpl_args.ascendancy = m_util.cast.boolean(tpl_args.ascendancy)
     tpl_args.ascendancy = m_util.cast.boolean(tpl_args.ascendancy)
      
      
--     local prepend = {
     local prepend = {
--         q_join=true,
         q_join=true,
--     }
     }
      
      
--     local query = {
     local query = {
--         join='passive_skills._pageID=passive_skill_stats._pageID',
         join='passive_skills._pageID=passive_skill_stats._pageID',
--         limit=5000,
         limit=5000,
--         groupBy='passive_skills._pageID',
         groupBy='passive_skills._pageID',
--     }
     }
--     for key, value in pairs(tpl_args) do  
     for key, value in pairs(tpl_args) do  
--         if string.sub(key, 0, 2) == 'q_' then
         if string.sub(key, 0, 2) == 'q_' then
--             if prepend[key] then
             if prepend[key] then
--                 value = ',' .. value
                 value = ',' .. value
--             end
             end
              
              
--             query[string.sub(key, 3)] = value
             query[string.sub(key, 3)] = value
--         end
         end
--     end
     end
      
      
--     local results = m_cargo.query(
     local results = m_cargo.query(
--         {'passive_skills', 'passive_skill_stats'},
         {'passive_skills', 'passive_skill_stats'},
--         {
         {
--             'passive_skills._pageName',  
             'passive_skills._pageName',  
--             'passive_skills.main_page',
             'passive_skills.main_page',
--             'passive_skills.name',
             'passive_skills.name',
--             'passive_skills.stat_text',
             'passive_skills.stat_text',
--             'passive_skills.icon',
             'passive_skills.icon',
--             'passive_skills.is_keystone',
             'passive_skills.is_keystone',
--             'passive_skills.is_notable',
             'passive_skills.is_notable',
--             'passive_skills.ascendancy_class',
             'passive_skills.ascendancy_class',
--         },  
         },  
--         query
         query
--     )
     )
--     result_map = m_cargo.map_results_to_id{
     result_map = m_cargo.map_results_to_id{
--         results=results,
         results=results,
--         field='passive_skills.name',
         field='passive_skills.name',
--         keep_id_field=true,
         keep_id_field=true,
--     }
     }
--     for key, rows in pairs(result_map) do
     for key, rows in pairs(result_map) do
--         result_map[key] = h.sort_by_type(rows)
         result_map[key] = h.sort_by_type(rows)
--     end
     end
      
      
--     -- header
     -- header
--     local tbl = mw.html.create('table')
     local tbl = mw.html.create('table')
--     tbl:addClass('wikitable')
     tbl:addClass('wikitable')
--     tbl:addClass('sortable')
     tbl:addClass('sortable')
      
      
--     local tr = tbl:tag('tr')
     local tr = tbl:tag('tr')
--     if tpl_args.ascendancy then
     if tpl_args.ascendancy then
--         tr:tag('th')
         tr:tag('th')
--             :wikitext(i18n.passive_table.ascendancy_class)
             :wikitext(i18n.passive_table.ascendancy_class)
--     end
     end
      
      
--     tr
     tr
--         :tag('th')
         :tag('th')
--             :wikitext(i18n.passive_table.name)
             :wikitext(i18n.passive_table.name)
--             :done()
             :done()
--         :tag('th')
         :tag('th')
--             :wikitext(i18n.passive_table.stats)
             :wikitext(i18n.passive_table.stats)
--             :done()
             :done()
      
      
--     -- rows
     -- rows
--     local used_names = {}
     local used_names = {}
--     for _, passive_row in ipairs(results) do
     for _, passive_row in ipairs(results) do
--         local pn = passive_row['passive_skills.name']
         local pn = passive_row['passive_skills.name']
--         if used_names[pn] == nil then
         if used_names[pn] == nil then
--             local type_results_map = result_map[passive_row['passive_skills.name']]
             local type_results_map = result_map[passive_row['passive_skills.name']]
--             used_names[pn] = true
             used_names[pn] = true
--             for _, type_key in ipairs(h.type_order) do
             for _, type_key in ipairs(h.type_order) do
--                 local type_results = type_results_map[type_key]
                 local type_results = type_results_map[type_key]
--                 if #type_results > 0 then
                 if #type_results > 0 then
--                     local row = type_results[1]
                     local row = type_results[1]
--                     tr = tbl:tag('tr')
                     tr = tbl:tag('tr')
                          
                          
--                     if tpl_args.ascendancy then
                     if tpl_args.ascendancy then
--                         if row['passive_skills.ascendancy_class'] then
                         if row['passive_skills.ascendancy_class'] then
--                             tr:tag('td')
                             tr:tag('td')
--                                 :wikitext(string.format('[[%s]]<br>[[File:%s avatar.png|link=%s]]', row['passive_skills.ascendancy_class'], row['passive_skills.ascendancy_class'], row['passive_skills.ascendancy_class']))
                                 :wikitext(string.format('[[%s]]<br>[[File:%s avatar.png|link=%s]]', row['passive_skills.ascendancy_class'], row['passive_skills.ascendancy_class'], row['passive_skills.ascendancy_class']))
--                         else
                         else
--                             tr:wikitext(m_util.html.td.na{})
                             tr:wikitext(m_util.html.td.na{})
--                         end
                         end
--                     end
                     end
                      
                      
--                     local stats, stat_order = h.make_stat_order(type_results)
                     local stats, stat_order = h.make_stat_order(type_results)
--                     tr
                     tr
--                         :tag('td')
                         :tag('td')
--                             :attr('data-sort-value', row['passive_skills.name'] .. type_key)
                             :attr('data-sort-value', row['passive_skills.name'] .. type_key)
--                             :wikitext(string.format('[[%s|%s]]<br>%s', row['passive_skills.main_page'] or row['passive_skills.name'], row['passive_skills.name'], h.format_mastery_icon(row)))
                             :wikitext(string.format('[[%s|%s]]<br>%s', row['passive_skills.main_page'] or row['passive_skills.name'], row['passive_skills.name'], h.format_passive_icon(row, type_key)))
--                             :done()
                             :done()
--                         :tag('td')
                         :tag('td')
--                             :wikitext(h.stat_page_links(stat_order, stats))
                             :wikitext(h.stat_page_links(stat_order, stats))
--                             :done()
                             :done()
--                 end
                 end
--             end
             end
--         end
         end
--     end
     end
      
      
--     return tostring(tbl)
     return tostring(tbl)
-- end
end


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------

Revision as of 07:48, 12 November 2021

Module documentation[create] [purge]
--
-- Module for passive skills
--

local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local getArgs = require('Module:Arguments').getArgs
local f_infocard = require('Module:Infocard')._main

local p = {}

-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------

local i18n = {
    icon_name = 'File:%s passive skill icon.png',
    
    cats = {
        data = 'Passive skill data',
        
        keystone = 'Keystone passive skills',
        notable = 'Notable passive skills',
        basic = 'Basic passive skills',
        ascendancy_notable = 'Ascendancy notable passive skills',
        ascendancy_basic = 'Ascendancy basic passive skills',
    },
    
    passive_box = {
        keystone = 'Keystone',
        notable = 'Notable Passive Skill',
        basic = 'Passive Skill',
        ascendancy_notable = 'Ascendancy Notable Passive Skill',
        ascendancy_basic = 'Ascendancy Passive Skill',
    },
    
    intro = {
        text_with_name = "'''%s''' is the internal id for the [[%s|%s]] [[passive skill]]. ",
        text_without_name = "'''%s''' is the internal id of an unnamed [[passive skill]]. ",
    },
    
    passive_box_table = {
        id = 'Id',
        int_id = 'Integer Id',
        flavour_text = 'Flavour Text',
        reminder_text = 'Reminder Text',
        skill_points = 'Skill Points Granted',
        ascendancy_class = 'Ascendancy Class',
        connections = 'Connections',
    },
    
    passive_table = {
        ascendancy_class = 'Ascendancy<br>Class',
        name = 'Name',
        stats = 'Stats',
    },
    
    errors = {
        no_passives_found = 'No passive skills with the given name found',
    },
}

-- ----------------------------------------------------------------------------
-- Cargo
-- ----------------------------------------------------------------------------

local tables = {}

tables.passive_skills = {
    table = 'passive_skills',
    order = {'id', 'int_id', 'name', 'main_page', 'flavour_text', 'reminder_text', 'buff_id', 'skill_points', 'icon', 'ascendancy_class', 'is_keystone', 'is_notable', 'is_multiple_choice_option', 'is_multiple_choice', 'is_icon_only', 'is_jewel_socket', 'is_ascendancy_starting_node', 'stat_text', 'stat_text_raw', 'connections',},
    fields = {
        id = {
            field = 'id',
            type = 'String',
            required = true,
        },
        int_id = {
            field = 'int_id',
            type = 'Integer',
            required = true,
        },
        name = {
            field = 'name',
            type = 'String',
        },
        main_page = {
            field = 'main_page',
            type = 'Page',
        },
        flavour_text = {
            field = 'flavour_text',
            type = 'Text',
        },
        reminder_text = {
            field = 'reminder_text',
            type = 'Text',
        },
        buff_id = {
            field = 'buff_id',
            type = 'String',
        },
        -- TODO: Other buff stuff 
        skill_points = {
            field = 'skill_points',
            type = 'Integer',
            default = 0,
        },
        icon = {
            field = 'icon',
            type = 'Page',
            func = function(tpl_args, frame, value)
                if value then
                    return string.format(i18n.icon_name, value)
                end
            end
        },
        ascendancy_class = {
            field = 'ascendancy_class',
            type = 'String',
        },
        is_keystone = {
            field = 'is_keystone',
            type = 'Boolean',
            default = false,
        },
        is_notable = {
            field = 'is_notable',
            type = 'Boolean',
            default = false,
        },
        is_multiple_choice_option = {
            field = 'is_multiple_choice_option',
            type = 'Boolean',
            default = false,
        },
        is_multiple_choice = {
            field = 'is_multiple_choice',
            type = 'Boolean',
            default = false,
        },
        is_icon_only = {
            field = 'is_icon_only',
            type = 'Boolean',
            default = false,
        },
        is_jewel_socket = {
            field = 'is_jewel_socket',
            type = 'Boolean',
            default = false,
        },
        is_ascendancy_starting_node = {
            field = 'is_ascendancy_starting_node',
            type = 'Boolean',
            default = false,
        },
        stat_text = {
            field = 'stat_text',
            type = 'Text',
        },
        stat_text_raw = {
            field = 'stat_text',
            type = 'Text',
            func = function (tpl_args, frame)
                if tpl_args.stat_text then
                    tpl_args.stat_text_raw = string.gsub(
                        -- [[x]] -> x
                        string.gsub(
                            tpl_args.stat_text, '%[%[([^%]|]+)%]%]', '%1'
                        ), 
                        -- [[x|y]] -> y
                        '%[%[[^|]+|([^%]|]+)%]%]', '%1'
                    )
                end
                return tpl_args.stat_text_raw
            end
        },
        -- from the graph file:
        connections = {
            field = 'connections',
            type = 'List (,) of String',
        },
    }
}

tables.passive_skill_stats = {
    table = 'passive_skill_stats',
    fields = {
        id = {
            field = 'id',
            type = 'String',
        },
        value = {
            field = 'value',
            type = 'Integer',
        },
    }
}

local display = {}
display.map_to_property = {'icon', 'is_keystone', 'is_notable', 'ascendancy_class'}
display.tbl = {
    {
        key = 'id',
        header = i18n.passive_box_table.id,
        display = nil,
    },
    {
        key = 'int_id',
        header = i18n.passive_box_table.int_id,
        display = nil,
    },
    {
        css = 'tc -flavour',
        key = 'flavour_text',
        header = i18n.passive_box_table.flavour_text,
        display = nil,
    },
    {
        key = 'reminder_text',
        header = i18n.passive_box_table.reminder_text,
        display = nil,
    },
    {
        key = 'skill_points',
        header = i18n.passive_box_table.skill_points,
        display = nil,
    },
    {
        key = 'ascendancy_class',
        header = i18n.passive_box_table.ascendancy_class,
        display = function (tpl_args, frame, value)
            return string.format('[[%s]]', value)
        end,
    },
    {
        key = 'connections',
        header = i18n.passive_box_table.connections,
        display = function (tpl_args, frame, value)
            local results = m_cargo.map_results_to_id{
                field='passive_skills.id',
                results=m_cargo.array_query{
                    tables={'passive_skills'},
                    fields={'passive_skills.name', 'passive_skills._pageName'},
                    id_array=value,
                    id_field='passive_skills.id',
                    ignore_missing=true,
                }
            }
            
            local ul = mw.html.create('ul')
            for _, key in ipairs(value) do
                local row = results[key]
                if row then
                    row = row[1]
                end
                local text
                if row then
                    text = string.format('[[%s|%s]]', row['passive_skills._pageName'], row['passive_skills.name'] or row['passive_skills._pageName'])
                else
                    text = key
                end
                ul
                    :tag('li')
                        :wikitext(text)
                        :done()
            end
            
            return tostring(ul)
        end,
    },
}

-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------

local h = {}

function h.format_passive_icon(passive, passive_type)
    if passive['passive_skills.icon'] == nil then
        return ''
    end
    
    local cls = string.format('passive-icon-type__%s', passive_type)
    local main_page = passive['passive_skills.main_page'] or passive['main_pages._pageName'] or passive['passive_skills.name'] or passive['passive_skills.icon']
    div = mw.html.create('div')
    div:addClass('passive-icon-container')
    div:addClass(cls)
    div:tag('div')
        :addClass('passive-icon-frame')
        :done()
    div:wikitext(
        string.format(
            '[[%s|link=%s]]', 
            passive['passive_skills.icon'], 
            main_page
        )
    )
    
    return tostring(div)
end

function h.make_stat_order(results)
    local stats = {}
    local stat_order = {}
    for _, row in ipairs(results) do 
        local stat = row['passive_skills.stat_text']
        -- Can't show results here that don't have a stat line
        if stat then
            if stats[stat] == nil then
                stats[stat] = {row}
                table.insert(stat_order, stat)
            else
                table.insert(stats[stat], row)
            end
        end
    end
    
    return stats, stat_order
end

function h.stat_page_links(stat_order, stats)
    local out = {}
    for i, key in ipairs(stat_order) do
        local links = {}
        for j, row in ipairs(stats[key]) do
            links[#links+1] = string.format('[[%s|&#91;%s&#93;]]', row['passive_skills._pageName'], j)
        end
        out[i] = string.format('<span class="passive-line">%s <span class="passive-hover">%s</span></span>', key, table.concat(links, ' '))
    end
    
    return table.concat(out, '<hr>')
end

h.type_order = {'basic', 'notable', 'keystone', 'ascendancy_basic', 'ascendancy_notable'}
function h.get_type(passive)
    local key
    if tonumber(passive['passive_skills.is_keystone']) == 1 then
        key = 'keystone'
    elseif tonumber(passive['passive_skills.is_notable']) == 1 then
        key = 'notable'
    else
        key = 'basic'
    end
    
    if passive['passive_skills.ascendancy_class'] ~= nil then
        key = 'ascendancy_' .. key
    end
    
    return key
end

function h.sort_by_type(results)
    local new = {}
    for _, key in ipairs(h.type_order) do
        new[key] = {}
    end
    
    for _, passive in ipairs(results) do
        table.insert(new[h.get_type(passive)], passive)
    end
    
    return new
end

function h.intro_text(tpl_args, frame)
    --[[
    Display an introductory text about the passive skill.
    ]]
    local out = {}
    if mw.ustring.find(tpl_args['id'], '_') then
        out[#out+1] = frame:expandTemplate{
            title='Incorrect title', 
            args = {title=tpl_args['id']} 
        }
    end
    
    if tpl_args['name'] then
        out[#out+1] = string.format(
            i18n.intro.text_with_name, 
            tpl_args['id'], 
            tpl_args['main_page'] or tostring(mw.title.getCurrentTitle()), 
            tpl_args['name']
        )
    else 
        out[#out+1] = string.format(
            i18n.intro.text_without_name,
            tpl_args['id']
        )
    end 
    
    return table.concat(out)
end

function h.stat_box(tpl_args, frame)
    --[[
    Display the stat box.
    ]]
    local container = mw.html.create('div')
    container
        :attr('class', 'modbox floatright')
        
    -- stat table 
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        -- :attr('style', 'style="width: 100%;"')
        :tag('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext('Stats')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('#')
                :done()
            :tag('th')
                :wikitext('Stat Id')
                :done()
            :tag('th')
                :wikitext('Value')
                :done()
            :done()
            :done()
        :done()
        
    local i = 0
    local value = nil
    repeat
        i = i + 1
        value = {
            id = tpl_args[string.format('stat%s_id', i)],
            value = tpl_args[string.format('stat%s_value', i)],
        }
        
        if value.id then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.id)
                        :done()
                    :tag('td')
                        :wikitext(value.value)
                        :done()
                    :done()
                :done()
        end
    until value.id == nil
    
    return tostring(container)
end


-- ----------------------------------------------------------------------------
-- Page functions
-- ----------------------------------------------------------------------------

local p = {}

-- This way the helper functions can be used in other modules
p.h = h

-- Declare cargo tables:
p.table_passive_skills = m_cargo.declare_factory{data=tables.passive_skills}
p.table_passive_skill_stats = m_cargo.declare_factory{data=tables.passive_skill_stats}

function p.passive_skill(frame)
    --[[
    Stores data and displays a infobox about the passive skill.
    
    Examples
    --------
    = p.passive_skill{
        id = 'life_life_leech1629',
        int_id = '27788',
        name = 'Blood Drinker',
        is_notable = 'True',
        icon = 'lifeleech',
        stat1_id = 'maximum_life_+%',
        stat1_value = '8',
        stat2_id = 'base_life_leech_from_attack_damage_permyriad',
        stat2_value = '40',
        stat_text = '8% increased maximum life<br>0.4% of Attack Damage Leeched as Life',
        connections = 'life1415,life1413',
    }
    
    ]]

    -- Get args
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    -- parse 
    m_util.args.from_cargo_map{
        tpl_args=tpl_args,
        frame=frame,
        table_map=tables.passive_skills,
    }
    
    -- parse stats
    m_util.args.stats(tpl_args, {})
    for _, stat in ipairs(tpl_args.stats) do
        stat._table = tables.passive_skill_stats.table
        m_cargo.store(frame, stat)
    end
    
    --
    -- Infobox
    --
    local passive = {}
    for _, key in ipairs(display.map_to_property) do
        local v = tpl_args[key]
        if type(v) == 'boolean' then
            if v then
                v = 1
            else
                v = 0
            end
        end
        passive[string.format('%s.%s', tables.passive_skills.table, tables.passive_skills.fields[key].field)] = v
    end
    
    local type_key = h.get_type(passive)
    
    local infocard_args = {}
    infocard_args.header = tpl_args.name
    infocard_args.subheader = i18n.passive_box[type_key]
    
    local tbl = mw.html.create('table')
    for _, data in ipairs(display.tbl) do
        local value = tpl_args[data.key]
        -- if default is nil, this will be compared against nil which is what we want, so value ~= nil isn't needed
        if value ~= tables.passive_skills.fields[data.key].default then
            local dsp
            if data.display then
                dsp = data.display(tpl_args, frame, value)
            else
                dsp = value
            end
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.header)
                        :done()
                    :tag('td')
                        :attr('class', data.css)
                        :wikitext(dsp)
                        :done()
                    :done()
        end
    end
    
    infocard_args[1] = tostring(tbl)
    infocard_args[2] = tpl_args.stat_text
    infocard_args[3] = h.format_passive_icon(passive, type_key)

    local out = {
        f_infocard(infocard_args),
        h.intro_text(tpl_args, frame),
        h.stat_box(tpl_args, frame),
    }
    
    local cats = {
        i18n.cats.data,
    }
    return table.concat(out) .. m_util.misc.add_category(cats)
end

function p.passive_skill_box(frame)
    --[[
    Queries a passive skill and displays it.
    
    Examples
    --------
    = p.passive_skill_box{name='Ghost Reaver'}
    
    ]]
    
    -- Get args
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    tpl_args.name = tpl_args.name or tpl_args[1]
    
    if not tpl_args.q_where and tpl_args.name then
        tpl_args.q_where = string.format('passive_skills.name="%s" AND passive_skills.stat_text IS NOT NULL', tpl_args.name)
    elseif not (tpl_args.q_where and not tpl_args.name) then
        error('q_where or name must be specified')
    end
    
    local results = m_cargo.query(
        {'passive_skills'},
        {
            'passive_skills._pageName', 
            'passive_skills.main_page',
            'passive_skills.name', 
            'passive_skills.stat_text',
            -- TODO: only really need these once, maybe put in extra query
            'passive_skills.flavour_text',
            'passive_skills.icon',
            'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
            'passive_skills.id'
        }, 
        {
            where=tpl_args.q_where,
            orderBy='passive_skills.stat_text',
            limit=5000,
        }
    )
    if #results == 0 then
        error(i18n.errors.no_passives_found)
    end
    
    results = h.sort_by_type(results)
    local out = {}
    local cats = {}
    for _, type_key in ipairs(h.type_order) do
        local type_results = results[type_key]
        if #type_results > 0 then
            cats[#cats+1] = i18n.cats[type_key]
            local stats, stat_order = h.make_stat_order(type_results)
            
            local passive = type_results[1]
            
            local infocard_args = {}
            infocard_args.header = passive['passive_skills.name']
            infocard_args.subheader = i18n.passive_box[type_key]
            
            infocard_args[1] = h.format_passive_icon(passive, type_key)
            infocard_args[2] = h.stat_page_links(stat_order, stats)
            infocard_args[3] = m_util.html.poe_color('flavour', passive['passive_skills.flavour_text'])
            
            out[#out+1] = f_infocard(infocard_args)
            
            -- Store as main page:
            for _, v in ipairs(type_results) do
                m_cargo.store(
                    frame, 
                    {
                        _table='main_pages', 
                        id=v['passive_skills.id'],
                    }
                )
            end
        end
    end
    
    if tpl_args.cats == nil or m_util.cast.boolean(tpl_args.cats) then
        out[#out+1] = m_util.misc.add_category(cats)
    end
    
    return table.concat(out)
end

function p.passive_skill_link(frame)
    --[[
    Links a passive skill
    
    Examples
    --------
    = p.passive_skill_link{id='AscendancyAscendant45', format='tablerow'}
    ]]
    
    -- Get args
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    tpl_args.name = tpl_args.name or tpl_args[1]
    if tpl_args.name then
        tpl_args.q_where = string.format('passive_skills.name="%s"', tpl_args.name)
    elseif tpl_args.id then
        tpl_args.q_where = string.format('passive_skills.id="%s"', tpl_args.id)
    elseif tpl_args.q_where then
    else
        error('Either name, id or q_where must be specified')
    end
    
    local results = m_cargo.query(
        {'passive_skills', 'main_pages'},
        {
            'passive_skills._pageName', 
            'passive_skills.stat_text',
            'passive_skills.main_page',
            'passive_skills.name',
            'passive_skills.icon',
            'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
            'main_pages._pageName',
        }, 
        {   
            join='passive_skills.id=main_pages.id',
            where=string.format('(%s) AND passive_skills.stat_text IS NOT NULL', tpl_args.q_where),
            orderBy='passive_skills.stat_text',
            limit=2,
        }
    )
    
    if #results > 1 then
        error('Too many passives found!')
    elseif #results < 1 then
        error('No passives found')
    end
    local passive = results[1]
    
    if tpl_args.format == 'tablerow' then
        local main_page = passive['passive_skills.main_page'] or passive['main_pages._pageName'] or passive['passive_skills.name']
        return string.format(
            '| [[%s|%s]]%s\n| %s', 
            main_page, 
            passive['passive_skills.name'], 
            h.format_passive_icon(passive, h.get_type(passive)), 
            passive['passive_skills.stat_text']
        )
    elseif tpl_args.format == nil then
        return
    else
        error(string.format('Invalid return format specified: %s', tpl_args.format))
    end
end

-- Not sure whether we need a more sophisticated variant like item or mod tables here yet

function p.passive_skill_table2(frame)
    -- Get args
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    if tpl_args.q_tables then 
        tpl_args.q_tables = tpl_args.q_tables .. ',' .. 'passive_skill_stats'
    else
        tpl_args.q_tables = 'passive_skill_stats'
    end
    
    if tpl_args.q_join then 
        tpl_args.q_join = tpl_args.q_join .. ',' .. 'passive_skills._pageID=passive_skill_stats._pageID'
    else
        tpl_args.q_join = 'passive_skills._pageID=passive_skill_stats._pageID'
    end 
    
    tpl_args.q_orderBy = 'passive_skills.ascendancy_class IS NULL DESC, passive_skills.is_keystone, passive_skills.is_notable, passive_skills.name'
    
    return m_cargo.table_query{
        tpl_args=tpl_args,
        frame=frame,
        main_table='passive_skills',
        row_unique_fields = {'passive_skills.name'},
        data={
            tables = {
                passive_skill_stats = {
                    join='passive_skills._pageID=passive_skill_stats._pageID',
                },
            },
            -- display data
            {
                args = {'ascendancy'},
                header = i18n.passive_table.ascendancy_class,
                fields = {
                    'passive_skills.ascendancy_class',
                },
                display = function (tpl_args, frame, tr, data)
                    local passive = data[1]
                    if passive['passive_skills.ascendancy_class'] then
                        tr:tag('td')
                            :wikitext(string.format('[[%s]]<br>[[File:%s avatar.png|link=%s]]', passive['passive_skills.ascendancy_class'], passive['passive_skills.ascendancy_class'], passive['passive_skills.ascendancy_class']))
                    else
                        tr:wikitext(m_util.html.td.na{})
                    end
                end,
                order = 0,
                sort_type = 'text',
            },
            {
                args = nil,
                header = i18n.passive_table.name,
                fields = {
                    'passive_skills._pageName',
                    'passive_skills.main_page',
                    'passive_skills.name',
                    'passive_skills.icon',
                    'passive_skills.is_keystone',
                    'passive_skills.is_notable',
                    'passive_skills.ascendancy_class',
                },
                display = function (tpl_args, frame, tr, data)
                    local passive = data[1]
                    local type_key = h.get_type(passive)
                    tr
                        :tag('td')
                            :attr('data-sort-value', passive['passive_skills.name'] .. type_key)
                            :wikitext(string.format('[[%s|%s]]<br>%s', passive['passive_skills.main_page'] or passive['passive_skills.name'], passive['passive_skills.name'], h.format_passive_icon(passive, type_key)))
                            :done()
                end,
                order = 1000,
                sort_type = 'text',
                options = {
                    [7] = {
                        optional=true,
                    },
                },
            },
            {
                arg = {'default', 'stat', 'stats', 'stat_text'},
                header = i18n.passive_table.stats,
                fields = {
                    'passive_skills.stat_text',
                },
                display = function (tpl_args, frame, tr, data)
                    local passive = data[1]
                    local stats, stat_order = h.make_stat_order(data)
                    tr
                        :tag('td')
                            :wikitext(h.stat_page_links(stat_order, stats))
                            :done()
                end,
                order = 1001,
                sort_type = 'text',
            }
        },
    }
end

function p.passive_skill_table(frame)
    -- Get args
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    tpl_args.ascendancy = m_util.cast.boolean(tpl_args.ascendancy)
    
    local prepend = {
        q_join=true,
    }
    
    local query = {
        join='passive_skills._pageID=passive_skill_stats._pageID',
        limit=5000,
        groupBy='passive_skills._pageID',
    }
    for key, value in pairs(tpl_args) do 
        if string.sub(key, 0, 2) == 'q_' then
            if prepend[key] then
                value = ',' .. value
            end
            
            query[string.sub(key, 3)] = value
        end
    end
    
    local results = m_cargo.query(
        {'passive_skills', 'passive_skill_stats'},
        {
            'passive_skills._pageName', 
            'passive_skills.main_page',
            'passive_skills.name',
            'passive_skills.stat_text',
            'passive_skills.icon',
            'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
        }, 
        query
    )
    result_map = m_cargo.map_results_to_id{
        results=results,
        field='passive_skills.name',
        keep_id_field=true,
    }
    for key, rows in pairs(result_map) do
        result_map[key] = h.sort_by_type(rows)
    end
    
    -- header
    local tbl = mw.html.create('table')
    tbl:addClass('wikitable')
    tbl:addClass('sortable')
    
    local tr = tbl:tag('tr')
    if tpl_args.ascendancy then
        tr:tag('th')
            :wikitext(i18n.passive_table.ascendancy_class)
    end
    
    tr
        :tag('th')
            :wikitext(i18n.passive_table.name)
            :done()
        :tag('th')
            :wikitext(i18n.passive_table.stats)
            :done()
    
    -- rows
    local used_names = {}
    for _, passive_row in ipairs(results) do
        local pn = passive_row['passive_skills.name']
        if used_names[pn] == nil then
            local type_results_map = result_map[passive_row['passive_skills.name']]
            used_names[pn] = true
            for _, type_key in ipairs(h.type_order) do
                local type_results = type_results_map[type_key]
                if #type_results > 0 then
                    local row = type_results[1]
                    tr = tbl:tag('tr')
                        
                    if tpl_args.ascendancy then
                        if row['passive_skills.ascendancy_class'] then
                            tr:tag('td')
                                :wikitext(string.format('[[%s]]<br>[[File:%s avatar.png|link=%s]]', row['passive_skills.ascendancy_class'], row['passive_skills.ascendancy_class'], row['passive_skills.ascendancy_class']))
                        else
                            tr:wikitext(m_util.html.td.na{})
                        end
                    end
                    
                    local stats, stat_order = h.make_stat_order(type_results)
                    tr
                        :tag('td')
                            :attr('data-sort-value', row['passive_skills.name'] .. type_key)
                            :wikitext(string.format('[[%s|%s]]<br>%s', row['passive_skills.main_page'] or row['passive_skills.name'], row['passive_skills.name'], h.format_passive_icon(row, type_key)))
                            :done()
                        :tag('td')
                            :wikitext(h.stat_page_links(stat_order, stats))
                            :done()
                end
            end
        end
    end
    
    return tostring(tbl)
end

-- ----------------------------------------------------------------------------
-- End
-- ----------------------------------------------------------------------------

return p