Модуль:QuestReward

Материал из Path of Exile Wiki
Перейти к: навигация, поиск
Документация модуля[просмотр] [править] [история] [очистить]

Общий шаблон[править код]

Награды за задания[править код]

Блок кода


{{QuestReward
|display_type=skill
|filter_by_reward={{PAGENAME}}
}}

Даст нам следующее:

Пример умения Рассечение
Можно получить в награду за выполнение следующих заданий:
Класс →ДикарьЖрецВедьмаБандитОхотницаДуэлянтДворянка
Задание ↓
Враг у ворот
Акт 1

Покупка у торговцев[править код]

Блок кода


{{VendorQuestReward
|display_type=skill
|filter_by_reward={{PAGENAME}}
}}

Даст нам следующее:

Пример умения Выстрел шрапнелью
Можно купить у торговцев после выполнения следующих заданий:
Класс →ДикарьЖрецВедьмаБандитОхотницаДуэлянтДворянка
Задание ↓
Враг у ворот
Акт 1
Несса
Разгадка судьбы
Акт 3
Сиоса
Впавшие в немилость
Акт 6
Лилия Рот

Камни поддержки или умений[править код]

Внимание, если в названии страницы используется окончание в виде (камень поддержки), тогда нужно изменить значение параметра filter_by_reward с {{PAGENAME}} на название умения без окончания.

К примеру:[править код]

Умение Магия крови (камень поддержки) на filter_by_reward=Магия крови

--[[
Run Time: 

Template expansion takes most of the run time. It seems that the expansion is cached already, so there is no point in writing a cache for it. 
I suppose the only thing that could be done is improving the modules.
]]--

local xtable = require('Module:Table')
local util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_item = require('Module:Item')

local p = {}
local g_frame, g_args

local poe_classes = xtable:new({'Дикарь', 'Жрец', 'Ведьма', 'Бандит', 'Охотница', 'Дуэлянт', 'Дворянка'})
local poe_difficulties = xtable:new({'Обычный мир', 'Жестокий мир', 'Безжалостный мир'})
local data_keys = xtable:new({'reward', 'type', 'class', 'quest', 'quest_id', 'act', 'itemlevel', 'Редкое', 'sockets', 'page_link', 'npc'})
local supported_compares = xtable:new({'eq', 'neq', 'lt', 'gt', 'le', 'ge'})
-- for debug_time to console atm
local debug_enabled = false

local data = {}

-- unused atm
data.quest_order = {
     'Враг у ворот',
     'Разорение гнезд',
     'Зверь в клетке',
     'Зов сирены',
     'Мучительно острый',
     'Корень бед',
     'Разлученные влюбленные',
     'Отсечение правой руки',
     'Разгадка судьбы',
     'Снятие печати',
     'Вечный кошмар',
}

function p.show_reward(frame)
    --if not getArgs then
	--	getArgs = require('Module:Arguments').getArgs
	--end
    -- TODO: Remove
    g_args = getArgs(frame, {
		parentFirst = true
	})

    g_frame = util.misc.get_frame(frame)
    
    --g_args.filter_by_act = g_args.filter_by_act or 1
    --g_args.filter_by_quest_id = g_args.quest_id or 1
    
    debug_time{label='Init'}
    
    g_args.data_type = g_args.data_type or 'default'
    if g_args.show_empty_message == nil then
        g_args.show_empty_message = true
    else
        g_args.show_empty_message = util.cast.boolean(g_args.show_empty_message)
    end
    
    local reward_data = {}
    if g_args.data_type == 'default' then
        reward_data = mw.loadData('Module:QuestReward/data')
        data_keys:remove('npc')
        g_args.order_priority = g_args.order_priority or 'reward quest_id act'
    elseif g_args.data_type == 'vendor' then
        reward_data = mw.loadData('Module:QuestReward/vendor_reward_data')
        g_args.order_priority = g_args.order_priority or 'reward quest_id act'
        --reward_data = require('Module:QuestReward/vendor_reward_data')
    else
        error('Data type "' .. g_args.data_type .. '" is invalid.')
    end
    
    if g_args.disable_item_preview ~= nil then
        g_args.disable_item_preview = util.cast.boolean(g_args.disable_item_preview)
    else
        g_args.disable_item_preview = false
    end
    
    
    g_args.display_style = g_args.display_style or 'table'
    g_args.display_type = g_args.display_type or 'full'
    
    g_args.order_by_act = g_args.order_by_act or 'le'
    g_args.order_by_quest_id = g_args.order_by_quest_id or 'le'
    g_args.order_by_reward = g_args.order_by_reward or 'le'
    
    
    for display_type, filter_key in pairs({skill='reward', class='class'}) do
        if g_args.display_type == display_type and g_args['filter_by_' .. filter_key] == nil then
            error('Display type "' .. display_type .. '" requires "filter_by_' .. filter_key .. '" to be set')
        end
    end
    
    if g_args.order_priority ~= nil then
        local temp = mw.text.split(g_args.order_priority, ' ')
        local out = xtable:new()
        for _, key1 in ipairs(temp) do
            if data_keys:contains(key1) then
                table.insert(out, key1)
            else
                error("Invalid key in order priority" .. key1) 
            end
        end
        g_args.order_priority = out
    end
    
    local filter_keys = {}
    local search_keys = {}
    
    for _, key in ipairs(data_keys) do
        local filter = g_args['filter_by_' .. key]
        if filter ~= nil then
            if key == 'class' then
                if not poe_classes:contains(filter) then
                    error("Invalid class: " .. filter)
                end
            end
            -- Fix for numeric values so they get matched later
            if string.match(filter, '^%d+$') then
                filter = tonumber(filter)
            end
            filter_keys[key] = filter
        end
        local comp = g_args['order_by_' .. key] 
        if comp ~= nil then
            if supported_compares:contains(comp) then
                table.insert(search_keys, {key=key, cmp=comp}) 
            else
                error("Invalid compare: " .. comp)
            end
        end
    end
    
    debug_time{label='Arguments'}
    
    --
    -- Filter
    --
    
    local return_data = {}

    for _, row in ipairs(reward_data) do
        (function()
            for key, v in pairs(filter_keys) do
                if row[key] ~= v then return end
            end
            table.insert(return_data, row)
        end)()
    end
    
    debug_time{label='Filter'}
    
    --
    -- Sort
    --
    
    --- First, sort the keys accoringly
    table.sort(search_keys, function (a, b)
        return g_args.order_priority:index(a['key']) < g_args.order_priority:index(b['key']) 
    end)
    
    for _, d in ipairs(search_keys) do
        function sorter(row1, row2)
            return compare(row1, row2, d.cmp, d.key)
        end
        
        insertion_sort(return_data, sorter)
    end
    
    debug_time{label='Sort'}
    
    --
    -- Display
    --
    local outstr = ''
    if #return_data == 0 then
        if g_args.show_empty_message then
            if g_args.display_type == 'skill' then
                if g_args.data_type == 'default' then
                    outstr = 'Нельзя получить в [[Награды_за_задания|награду за задание]].'
                elseif g_args.data_type == 'vendor' then
                    outstr = 'Не выдается в награду, можно купить у торговцев..'
                end
            else
                outstr = 'Награда не найдена.'
            end
        end
    else
        if g_args.display_type == 'skill' then
            if g_args.data_type == 'default' then
                outstr = 'Можно получить в [[Награды_за_задания|награду за выполнение следующих заданий]]:<br>'
            elseif g_args.data_type == 'vendor' then
                outstr = 'Можно купить у торговцев после выполнения следующих заданий:<br>'
            end
        end
        if g_args.display_style == 'table' then
            outstr = outstr .. display_table(return_data)
        end
    end
    
    debug_time{label='Display Total'}
    
    return outstr
end

--
-- Display functions
--

function display_table(return_data)
    local tbl = mw.html.create('table')
    local tblhead = tbl:tag('tr')
    
    tbl
        :addClass('wikitable')
        
    tblhead
        :tag('th')
            :wikitext('Класс →')
    if g_args.display_type == 'class' then
        tblhead
            :tag('th')
                :attr('rowspan', 2)
                :wikitext('[[' .. g_args.filter_by_class .. ']]')
    else
        for _, value in ipairs(poe_classes) do
            tblhead
                :tag('th')
                    :attr('rowspan', 2)
                    :wikitext('[[' .. value .. ']]')
        end
    end
    tbl
        :tag('tr')
            :tag('th')
                :wikitext('Задание ↓')
    
    local format_data = {}
    -- Two Purposes
    -- 1. To keep the order
    -- 2. To avoid reindexing / lookups for each row when inserting
    local format_keys = {}
    
    -- reformat to make proper lists
    debug_time{label='Display - Table Pre Format', func=function ()
    for _, row in ipairs(return_data) do
        local key = row['quest_id']
        local sub = format_data[key]

        if sub == nil then
            format_data[key] = {
                quest=row['quest'], 
                act=row['act'],
                npc=row['npc'],
                classes={}
            }
            
            format_data[key]['classes'][row['class']] = {row}
            
            table.insert(format_keys, key)
            
        elseif sub['classes'][row['class']] == nil then
            sub['classes'][row['class']] = {row}
        else
            table.insert(sub['classes'][row['class']], row)
        end
    end
    end}
    
    local display_func
    local display_func_args = {}
    if g_args.display_type == 'full' then
        display_func = display_table_row_full
    elseif g_args.display_type == 'skill' then
        display_func = display_table_row_skill
    elseif g_args.display_type == 'class' then
        display_func = display_table_row_class
    end
    
    for _, key in pairs(format_keys) do
        local row_data = format_data[key] 
        local tblrow = tbl:tag('tr')
        local qtext = {'[[', row_data['quest'], ']]'}
        if row_data.quest == 'The Eternal Nightmare' then
            qtext[#qtext+1] = util.html.tooltip('*', 'The items are awarded for this quest when Dialla activates the device')
        end
        
        if row_data['act'] ~= nil then
            qtext[#qtext+1] = '<br>Акт '
            qtext[#qtext+1] = row_data['act']
        end
        
        if row_data['npc'] ~= nil then
            qtext[#qtext+1] = '<br>'
            qtext[#qtext+1] = '[[' .. row_data['npc'] .. ']]'
        end
        
        local tblrow_head = tblrow:tag('th')
        tblrow_head:wikitext(table.concat(qtext))
                
                
        local args = {row_data=row_data, tblrow=tblrow, tbl=tbl, tblrow_head=tblrow_head}
        -- Add any extra args
        for k, v in pairs(display_func_args) do
            args[k] = v
        end
        
        display_func(args)
    end
    
    return tostring(tbl)
end

function display_table_check_row_count(args)
    local cls = false
    if args.row_data['classes']['All'] == nil then
        cls = true
    -- Is a row that has both all and class entries, as such has spans two rows
    elseif xtable.size(args.row_data['classes']) > 1 then
        args.tblrow_head:attr('rowspan', 2)
        args.tblrow = args.tbl:tag('tr')
        cls = true
    end
    
    return cls
end

function display_table_row_full(args)
    if args.row_data['classes']['All'] ~= nil then
        local txt = {}
        for _, row in ipairs(args.row_data['classes']['All']) do
            txt[#txt+1] = format_reward(row)
            txt[#txt+1] = '<br>'
        end
        args.tblrow
            :tag('td')
                :attr('colspan', 7)
                :tag('center')
                    :wikitext(table.concat(txt))
    end
    
    if display_table_check_row_count(args) then
        for _, class in ipairs(poe_classes) do
            local txt = {}
            if args.row_data['classes'][class] ~= nil then
                for _, row in ipairs(args.row_data['classes'][class]) do
                    txt[#txt+1] = format_reward(row)
                    txt[#txt+1] = '<br>'
                end
            end
            args.tblrow
                :tag('td')
                    :wikitext(table.concat(txt))
        end
    end
end

function display_table_row_skill(args)
    if args.row_data['classes']['All'] ~= nil then
        local txt = g_frame:expandTemplate{title='yes'}
        args.tblrow
            :tag('td')
                :attr('colspan', 7)
                :tag('center')
                    :wikitext(txt)
    end
                  
    if display_table_check_row_count(args) then
        for _, class in ipairs(poe_classes) do
            local txt
            if args.row_data['classes'][class] ~= nil then
                txt = g_frame:expandTemplate{title='yes'}
            else
                txt = g_frame:expandTemplate{title='no'}
            end
            args.tblrow
                :tag('td')
                    :wikitext(txt)
        end
    end
end

function display_table_row_class(args)
    local txt = {}
    local classes = {'All', g_args.filter_by_class}
    for _, class in ipairs(classes) do
        if args.row_data['classes'][class] then
            for _, row in ipairs(args.row_data['classes'][class]) do
                txt[#txt+1] = format_reward(row)
                txt[#txt+1] = '<br>'
            end
        end
    end
    args.tblrow
        :tag('td')
            :wikitext(table.concat(txt))
end

function format_reward(row)
    -- Take a row and format it accordingly
    local output
    
    -- {{il|page|reward}} or {{il|reward}}
    local tplargs = {row['page_link'] or row['reward']}
    if row['page_link'] ~= nil then
        tplargs[2] = row['reward']
    end
    
    if g_args.disable_item_preview then
        output = '[[' .. tplargs[1] .. ']]'
    else
        if row['type'] == 'skill' then
            output = g_frame:expandTemplate{title='sl', args=tplargs}
        -- The difference between using the module item directly and expanding template il is up to 1 second (!)
        -- The time was taken for the full data set (1020 records at time of writing)
        elseif row['type'] == nil or row['type'] == 'item' then
            --[[output = g_frame:expandTemplate{title='il', args=tplargs}
            if string.find(output, 'scribunto') ~= nil then
                output = row['reward']
            end]]--
            local result = ''
            if pcall(function () 
                result = m_item.item_link{page=tplargs[1], name=tplargs[2], raise=true}
            end) then
                output = result 
            else
                output = '[[' .. tplargs[1] .. ']]'
            end
        end
    end
    
    local extra = {}
    if row['itemlevel'] ~= nil then
        extra[#extra+1] = 'Уровень предмета '
        extra[#extra+1] = row['itemlevel']
    end
    if row['rarity'] ~= nil then
        extra[#extra+1] = ', '
        extra[#extra+1] = g_frame:expandTemplate{title='c', args={string.lower(row['rarity']), row['rarity']}}
    end
    if row['sockets'] ~= nil then
        extra[#extra+1] = ', '
        extra[#extra+1] = row['sockets']
        extra[#extra+1] = '-[[Связи|Соед. гнезда]]'
    end
    
    if #extra > 0 then
        extra[#extra+1] = ')'
        table.insert(extra, 1, ' (')
    end
   
    return output .. table.concat(extra)
end

-- 
-- Sorting Functions
--

function insertion_sort(array, cmp)
    for i = 2, #array do
        local entry = array[i]
        for j = i-1, 0, -1 do 
            if j == 0 or cmp(array[j], entry) then 
                array[j+1] = entry 
                break 
            else 
                array[j+1] = array[j]
            end 
        end 
    end
end 

function compare(row1, row2, compare_type, key)
    if compare_type == 'eq' then
        return row1[key] == row2[key]
    elseif compare_type == 'neq' then
        return row1[key] ~= row2[key]
    elseif compare_type == 'lt' then
        return row1[key] < row2[key]
    elseif compare_type == 'gt' then
        return row1[key] > row2[key]
    elseif compare_type == 'le' then
        return row1[key] <= row2[key]
    elseif compare_type == 'ge' then
        return row1[key] >= row2[key]
    end
end

-- 
-- Misc
--

local debug_old_time = os.clock()
function debug_time(args)
    if not debug_enabled then 
        if args.func then
            return args.func()
        end
        return
    end
    
    if args.label then
        args.label = args.label .. ': '
    else
        args.label = 'Time: '
    end
    
    local delta
    local rtr
    local cur_time = os.clock() 
    
    if args.func then
        rtr = args.func()
        delta = os.clock() - cur_time
    else
        delta = cur_time - debug_old_time
        debug_old_time = cur_time
    end
    mw.log(args.label .. delta)
    return rtr
end

function replace_vars(str, vars)
  -- Allow replace_vars{str, vars} syntax as well as replace_vars(str, {vars})
  if not vars then
    vars = str
    str = vars[1]
  end
  return (string_gsub(str, "({([^}]+)})",
    function(whole,i)
      return vars[i] or whole
    end))
end

return p