Модуль:Статистика игроков

Версия от 20:15, 29 апреля 2026; ЧТМ Бот (обсуждение | вклад) (Cнял защиту с «Модуль:Статистика игроков»)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

Изменить документацию

Модуль предназначен для изменения статистики игроков по всей энциклопедии. Данные хранятся на подстранице Модуль:Статистика игроков/data.

Шаблоны для вызова данных



local p = {}
local data = mw.loadData('Module:Статистика игроков/data')

-----------------------------------------------------------------------
-- КОНФИГУРАЦИЯ ЭПОХ (ГОДОВ) (для шаблона СИ/Строка)
-- Здесь мы прописываем, какие турниры учитываются для разных показателей.
-----------------------------------------------------------------------
local ALL_YEARS = {"2006", "2010", "2014", "2018", "2022", "2026", "2030", "2034", "2038", "2042", "2046"}

local METRIC_YEARS = {
    -- Показатели с начала времён (11 турниров)
    ["Голы"] = ALL_YEARS,
    ["Игрок_матча"] = ALL_YEARS,
    ["Забитые_пенальти"] = ALL_YEARS,
    ["Незабитые_пенальти"] = ALL_YEARS,
    ["Отбитые_пенальти"] = ALL_YEARS,
    ["Матчи_на_ноль"] = ALL_YEARS,
    ["Автоголы"] = ALL_YEARS,
    ["Мега-трики"] = ALL_YEARS,
    
    -- Показатели, которые начали считать позже (с 2022) - 7 турниров
    ["Матчи"] = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"},
    ["Матчи_в_поле"] = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"},
    ["Призы_игровых_дней"] = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"},
    ["Призовые_места_игр_дней"] = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"},
    ["Показатель_полезности"] = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"},
    ["Выносы_из_пустых"] = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"},
    
    -- Показатели, которые начали считать с 2026 - 6 турниров
    ["Передачи"] = {"2026", "2030", "2034", "2038", "2042", "2046"},
    ["Голы_головой"] = {"2026", "2030", "2034", "2038", "2042", "2046"},
    ["Голы_пяточкой"] = {"2026", "2030", "2034", "2038", "2042", "2046"},
    ["Голы_со_штрафных"] = {"2026", "2030", "2034", "2038", "2042", "2046"},
    ["Мега-трики_голевых_передач"] = {"2026", "2030", "2034", "2038", "2042", "2046"},
    ["Привезённые_пенальти"] = {"2026", "2030", "2034", "2038", "2042", "2046"}
}

-- Старая функция (оставляем, чтобы ничего не сломать там, где она ещё используется)
function p.Statistics (frame)
    return data[frame.args[1]]
end

-- НОВАЯ УНИВЕРСАЛЬНАЯ ФУНКЦИЯ
function p.UniversalRow(frame)
    local place = frame.args[1] or ""
    local player = frame.args[2] or ""
    local stat_type = frame.args[3] or "Голы"
    local pen_stat = frame.args[4] -- Опциональный показатель в скобках (например, Голы_с_пенальти)

    -- 1. Определяем нужные годы
    local years = METRIC_YEARS[stat_type] or ALL_YEARS

    -- 2. МАГИЯ ШАБЛОНОВ: вычисляем нужный шаблон (С14, С9, С8 и т.д.)
    -- Количество годов + 3 (Колонка места, Колонка Игрока, Колонка Суммы)
    local tpl_name = "С" .. tostring(#years + 3)

    -- 3. Формируем аргументы для шаблона С..
    local player_link = "[[" .. string.gsub(player, "_", " ") .. "]]"
    local c_args = { place, player_link }
    local sum = 0

    -- 4. Перебираем нужные годы, собираем статистику и суммируем
    for _, year in ipairs(years) do
        local key = player .. "/" .. stat_type .. "/" .. year
        local val = data[key]
        
        if val and val ~= "" then
            table.insert(c_args, val)
            -- Пытаемся превратить в число для суммы (работает и с отрицательными "-3")
            local num = tonumber(val)
            if num then sum = sum + num end
        else
            table.insert(c_args, "") -- Если в этот год не играл/не забивал
        end
    end

    -- 5. Обработка суммы (возвращаем плюсы для "Показателя полезности", если сумма больше нуля)
    local sum_str = tostring(sum)
    if stat_type == "Показатель_полезности" and sum > 0 then
        sum_str = "+" .. sum_str
    end
    table.insert(c_args, "'''" .. sum_str .. "'''")

    -- 6. Запускаем нужный шаблон (например, С14 или С9)
    local result = frame:expandTemplate{ title = tpl_name, args = c_args }

    -- 7. Добавляем данные в скобках (если переданы)
    if pen_stat and pen_stat ~= "" then
        local pen_key = player .. "/" .. pen_stat
        local pen_val = data[pen_key]
        if pen_val and pen_val ~= "0" and pen_val ~= "" then
            result = result .. " (" .. pen_val .. ")"
        end
    end

    return result
end

-----------------------------------------------------------------------
-- КОНФИГУРАЦИЯ ДЛЯ ПРОФИЛЯ ИГРОКА (для шаблона Автоматическая статистика)
-----------------------------------------------------------------------
local ALL_YEARS = {"2006", "2010", "2014", "2018", "2022", "2026", "2030", "2034", "2038", "2042", "2046"}

local START_YEARS = {
    ["Матчи"] = "2022",
    ["Матчи_в_поле"] = "2022",
    ["Ср_результативность"] = "2022", 
    ["Передачи"] = "2026",
    ["Ср_передачи"] = "2026",         
    ["Мега-трики_голевых_передач"] = "2026",
    ["Голы_пяточкой"] = "2026",
    ["Голы_со_штрафных"] = "2026",
    ["Привезённые_пенальти"] = "2026",
    ["Призы_игровых_дней"] = "2022",
    ["Призовые_места_игр_дней"] = "2022",
    ["Показатель_полезности"] = "2022",
    ["Голы_головой"] = "2022",
    ["Выносы_из_пустых"] = "2022"
}

local NEGATIVE_STATS = {
    ["Незабитые_пенальти"] = true,
    ["Привезённые_пенальти"] = true,
    ["Автоголы"] = true
}

-----------------------------------------------------------------------
-- КАРТА ВСЕХ СТРОК ТАБЛИЦЫ ПРОФИЛЯ (Порядок, ID метрики, Заголовок)
-----------------------------------------------------------------------
local PROFILE_METRICS = {
    { id = "Матчи", title = "[[Матчи|<abbr title=\"Подсчитываются начиная с ЧТМ-2022\">Матчи</abbr>]]" },
    { id = "Матчи_в_поле", title = "{{Abbr|Матчи в поле|Подсчитываются начиная с ЧТМ-2022}}" },
    { id = "Голы", title = "[[Список всех авторов голов на ЧТМ|Голы]]" },
    { id = "Передачи", title = "[[Передачи|<abbr title=\"Подсчитываются начиная с ЧТМ-2026\">Передачи</abbr>]]" },
    { id = "Ср_результативность", title = "[[Средняя результативность|<abbr title=\"Подсчитывается начиная с ЧТМ-2022\">Ср. результативность</abbr>]]", avg_base = "Голы" },
    { id = "Ср_передачи", title = "{{Abbr|Передачи (ср. за матч)|Подсчитывается начиная с ЧТМ-2026}}", avg_base = "Передачи" },
    { id = "Игрок_матча", title = "[[Игрок матча]]" },
    { id = "Призы_игровых_дней", title = "[[Призы игровых дней|<abbr title=\"Подсчитываются начиная с ЧТМ-2022\">Призы игровых дней</abbr>]]" },
    { id = "Призовые_места_игр_дней", title = "[[Призы игровых дней#Призовые места игровых дней|<abbr title=\"Подсчитываются начиная с ЧТМ-2022\">Призовые места игр. дней</abbr>]]" },
    { id = "Показатель_полезности", title = "[[Показатель полезности|<abbr title=\"Подсчитывается начиная с ЧТМ-2022\">Показатель полезности</abbr>]]" },
    { id = "Мега-трики", title = "[[Мега-трики]]" },
    { id = "Мега-трики_голевых_передач", title = "[[Мега-трики голевых передач|<abbr title=\"Подсчитываются начиная с ЧТМ-2026\">Мега-трики голевых передач</abbr>]]" },
    { id = "Голы_головой", title = "[[Голы головой|<abbr title=\"Подсчитываются начиная с ЧТМ-2022\">Голы головой</abbr>]]" },
    { id = "Голы_пяточкой", title = "[[Голы пяточкой|<abbr title=\"Подсчитываются начиная с ЧТМ-2026\">Голы пяточкой</abbr>]]" },
    { id = "Голы_со_штрафных", title = "[[Голы со штрафных|<abbr title=\"Подсчитываются начиная с ЧТМ-2026\">Голы со штрафных</abbr>]]" },
    { id = "Выносы_из_пустых", title = "[[Выносы из пустых|<abbr title=\"Подсчитываются начиная с ЧТМ-2022\">Выносы из пустых</abbr>]]" },
    { id = "Забитые_пенальти", title = "[[Пенальти|Забитые пенальти]]" },
    { id = "Незабитые_пенальти", title = "Незабитые пенальти" },
    { id = "Отбитые_пенальти", title = "[[Список пенальти ЧТМ#Отбитые пенальти|Отбитые пенальти]]" },
    { id = "Привезённые_пенальти", title = "[[Привезённые пенальти|<abbr title=\"Подсчитываются начиная с ЧТМ-2026\">Привезённые пенальти</abbr>]]" },
    { id = "Игрок_матча_как_вратарь", title = "[[Игрок матча как вратарь]]" },
    { id = "Матчи_на_ноль", title = "[[Матчи на ноль]]" },
    { id = "Автоголы", title = "Автоголы" }
}

-- УМНАЯ ОЛИМПИЙСКАЯ ФУНКЦИЯ ВЫДАЧИ МЕДАЛЕЙ (Оставляем как было)
local function evaluateRank(current_val, all_scores, metric)
    if not current_val or current_val == 0 then return nil end
    if #all_scores == 0 then return nil end

    if metric == "Показатель_полезности" then
        if current_val > 0 then
            table.sort(all_scores, function(a, b) return a > b end)
            local rank = 1
            for i, v in ipairs(all_scores) do
                if v == current_val then rank = i break end
            end
            if rank == 1 then return "GO" end
            if rank == 2 then return "SI" end
            if rank == 3 then return "BR" end
            if rank == 4 then return "WO" end
        elseif current_val < 0 then
            table.sort(all_scores, function(a, b) return a < b end)
            if current_val == all_scores[1] then return "lightsalmon" end
        end
        return nil
    end

    if NEGATIVE_STATS[metric] then
        table.sort(all_scores, function(a, b) return a > b end)
        if current_val == all_scores[1] then return "lightsalmon" end
        return nil
    end

    table.sort(all_scores, function(a, b) return a > b end)
    local rank = 1
    for i, v in ipairs(all_scores) do
        if v == current_val then rank = i break end
    end
    
    if rank == 1 then return "GO" end
    if rank == 2 then return "SI" end
    if rank == 3 then return "BR" end
    if rank == 4 then return "WO" end

    return nil
end

-- Ранжирование внутри одного года
local function getRankColor(metric, year, current_val, avg_base, current_player_matches)
    local scores = {}
    if avg_base and avg_base ~= "" then
        if current_player_matches < 20 then return nil end
        local players = {}
        for key, _ in pairs(data) do
            local p = string.match(key, "^(.-)/")
            if p then players[p] = true end
        end
        for p, _ in pairs(players) do
            local act = tonumber(data[p .. "/" .. avg_base .. "/" .. year]) or 0
            local mat = tonumber(data[p .. "/Матчи_в_поле/" .. year]) or 0
            if mat >= 20 then
                local p_val = tonumber(string.format("%.2f", act / mat))
                if p_val and p_val ~= 0 then table.insert(scores, p_val) end
            end
        end
    else
        for key, v in pairs(data) do
            local p, m, y = string.match(key, "^(.-)/([^/]+)/([^/]+)$")
            if m == metric and y == year then
                local num = tonumber(v)
                if num and num ~= 0 then table.insert(scores, num) end
            end
        end
    end
    return evaluateRank(current_val, scores, metric)
end

-- Подсчет суммы одного игрока
local function calculatePlayerTotal(player, metric, avg_base, start_year)
    local is_active = false
    local t_acts = 0
    local t_mats = 0
    local p_val = 0

    for _, y in ipairs(ALL_YEARS) do
        if y == start_year then is_active = true end
        if is_active then
            if avg_base and avg_base ~= "" then
                local act = tonumber(data[player .. "/" .. avg_base .. "/" .. y]) or 0
                local mat = tonumber(data[player .. "/Матчи_в_поле/" .. y]) or 0
                if mat > 0 then
                    t_acts = t_acts + act
                    t_mats = t_mats + mat
                end
            else
                p_val = p_val + (tonumber(data[player .. "/" .. metric .. "/" .. y]) or 0)
            end
        end
    end

    if avg_base and avg_base ~= "" then
        if t_mats > 0 then return tonumber(string.format("%.2f", t_acts / t_mats)), t_mats end
        return 0, 0
    else
        return p_val, 0
    end
end

-- Ранжирование ОБЩЕЙ СУММЫ
local function getSumRankColor(metric, avg_base, current_val, start_year, total_matches)
    if avg_base and avg_base ~= "" then
        if total_matches < 20 then return nil end
    end

    local players = {}
    for key, _ in pairs(data) do
        local p = string.match(key, "^(.-)/")
        if p then players[p] = true end
    end
    
    local scores = {}
    for p, _ in pairs(players) do
        local p_val, p_mats = calculatePlayerTotal(p, metric, avg_base, start_year)
        if avg_base and avg_base ~= "" then
            if p_val and p_val ~= 0 and p_mats >= 20 then table.insert(scores, p_val) end
        else
            if p_val and p_val ~= 0 then table.insert(scores, p_val) end
        end
    end
    return evaluateRank(current_val, scores, metric)
end

-----------------------------------------------------------------------
-- ВНУТРЕННЯЯ ФУНКЦИЯ: Генерирует одну строку (бывшая ProfileRow)
-----------------------------------------------------------------------
local function generateRow(frame, player, metric, title, avg_base)
    local tpl_name = "С" .. tostring(#ALL_YEARS + 2) 
    local c_args = { frame:preprocess(title) }
    local start_year = START_YEARS[metric] or "2006"
    local is_active = false
    local total_val = 0
    local total_matches = 0

    for i, year in ipairs(ALL_YEARS) do
        if year == start_year then is_active = true end
        local col_index = i + 1 
        
        if not is_active then
            table.insert(c_args, "")
            c_args["ц" .. col_index] = "rgb(235,243,254)" 
        else
            local val = ""
            local raw_val = 0
            
            if avg_base and avg_base ~= "" then
                local acts = tonumber(data[player .. "/" .. avg_base .. "/" .. year]) or 0
                local matches_field = tonumber(data[player .. "/Матчи_в_поле/" .. year]) or 0
                
                if matches_field > 0 then
                    raw_val = tonumber(string.format("%.2f", acts / matches_field))
                    val = string.format("%.2f", raw_val)
                    local color = getRankColor(metric, year, raw_val, avg_base, matches_field)
                    if color then c_args["ц" .. col_index] = color end
                    
                    total_val = total_val + acts
                    total_matches = total_matches + matches_field
                end
            else
                local raw_str = data[player .. "/" .. metric .. "/" .. year]
                if raw_str and raw_str ~= "" then
                    raw_val = tonumber(raw_str) or 0
                    if metric == "Показатель_полезности" and raw_val > 0 then
                        val = "+" .. tostring(raw_val)
                    else
                        val = tostring(raw_val)
                    end
                    total_val = total_val + raw_val
                    local color = getRankColor(metric, year, raw_val, nil, 0)
                    if color then c_args["ц" .. col_index] = color end
                end
            end
            table.insert(c_args, val)
        end
    end

    local final_sum_val = 0
    local final_sum_str = ""
    
    if avg_base and avg_base ~= "" then
        if total_matches > 0 then
            final_sum_val = tonumber(string.format("%.2f", total_val / total_matches))
            final_sum_str = string.format("%.2f", final_sum_val)
        else
            final_sum_str = "0.00"
        end
    else
        final_sum_val = total_val
        final_sum_str = tostring(total_val)
        if metric == "Показатель_полезности" and total_val > 0 then
            final_sum_str = "+" .. final_sum_str
        end
    end
    table.insert(c_args, "'''" .. final_sum_str .. "'''")
    
    if final_sum_val ~= 0 then
        local sum_color = getSumRankColor(metric, avg_base, final_sum_val, start_year, total_matches)
        if sum_color then c_args["ц" .. (#ALL_YEARS + 2)] = sum_color end
    end

    return frame:expandTemplate{ title = tpl_name, args = c_args }
end

-----------------------------------------------------------------------
-- НОВАЯ СУПЕР-ФУНКЦИЯ: Отрисовывает таблицу профиля ЦЕЛИКОМ!
-----------------------------------------------------------------------
function p.RenderFullProfile(frame)
    local player = frame.args[1] or frame:getParent().args[1]
    if not player or player == "" then return "" end

    local lines = {}

    -- 1. Шапка таблицы (открываем таблицу и задаем заголовок)
    table.insert(lines, frame:expandTemplate{ title = 'Статистика/Заголовок', args = { player } })

    -- 2. В цикле генерируем абсолютно все строки из нашего массива PROFILE_METRICS
    for _, m in ipairs(PROFILE_METRICS) do
        -- Вызываем внутреннюю генерацию строки (аналог бывшего ProfileRow)
        local row = generateRow(frame, player, m.id, m.title, m.avg_base)
        table.insert(lines, row)
    end

    -- 3. Закрываем таблицу
    table.insert(lines, "|}")

    -- Соединяем всё с переносами каретки
    return table.concat(lines, "\n")
end

-- Старая функция оставлена на всякий случай (мало ли где-то ещё осталась)
function p.ProfileRow(frame)
    return generateRow(frame, frame.args[1], frame.args[2], frame.args[3], frame.args[4])
end

-----------------------------------------------------------------------
-- АБСОЛЮТНО НАГЛАЯ ФУНКЦИЯ: Автоматическая полная таблица по показателю
-----------------------------------------------------------------------
function p.RenderStatTable(frame)
    local main_metric = frame.args[1] or "Голы"
    local pen_stat = frame.args[2]
    local current_tournament_matches = tonumber(frame.args[3]) or 64 -- Третий параметр для текущего ЧТМ
    
    -- Определяем годы. Для "Матчей в воротах" берём годы от обычных "Матчей"
    local years_lookup = main_metric
    if main_metric == "Матчи_в_воротах" then years_lookup = "Матчи" end
    local years = METRIC_YEARS[years_lookup] or ALL_YEARS
    
    -- Переменные для финальной строки "ВСЕГО"
    local col_totals = {}
    for i = 1, #years do col_totals[i] = 0 end
    local grand_total = 0
    local total_pens = 0

    -- ВНУТРЕННЯЯ ПОДФУНКЦИЯ: собирает, сортирует и генерирует строки
    local function generate_rows(metric_name, p_stat, hide_rank)
        local players_set = {}
        for key, _ in pairs(data) do
            local p_match = string.match(key, "^(.+)/[^/]+/%d%d%d%d$")
            if p_match then
                players_set[p_match] = true
            else
                p_match = string.match(key, "^(.+)/[^/]+$")
                if p_match then players_set[p_match] = true end
            end
        end
        
        local list = {}
        for player, _ in pairs(players_set) do
            local pdata = { name = player, total = 0, years = {}, has_data = false, pen_total = 0 }
            
            for i, year in ipairs(years) do
                local val_str = ""
                local num = 0
                
                -- ИСКЛЮЧЕНИЕ: Считаем матчи в воротах (Матчи минус Матчи в поле)
                if metric_name == "Матчи_в_воротах" then
                    local val_all = data[player .. "/Матчи/" .. year]
                    if val_all and val_all ~= "" then
                        local m_all = tonumber(val_all) or 0
                        local m_field = tonumber(data[player .. "/Матчи_в_поле/" .. year]) or 0
                        num = m_all - m_field
                        val_str = tostring(num)
                    end
                else
                    val_str = data[player .. "/" .. metric_name .. "/" .. year]
                    num = tonumber(val_str) or 0
                end
                
                pdata.years[i] = { raw = val_str, num = num }
                pdata.total = pdata.total + num
                
                if val_str and val_str ~= "" then
                    pdata.has_data = true
                end
            end
            
            if p_stat and p_stat ~= "" then
                local p_val = tonumber(data[player .. "/" .. p_stat]) or 0
                if p_val ~= 0 then pdata.pen_total = p_val end
            end
            
            if pdata.has_data then
                if pdata.total ~= 0 or metric_name == "Показатель_полезности" then
                    table.insert(list, pdata)
                end
            end
        end
        
        table.sort(list, function(a, b)
            if a.total ~= b.total then return a.total > b.total end
            if metric_name == "Голы" and p_stat and p_stat ~= "" then
                if a.pen_total ~= b.pen_total then return a.pen_total < b.pen_total end
            end
            local sumA, sumB = a.total, b.total
            for i = #years, 1, -1 do
                sumA = sumA - a.years[i].num
                sumB = sumB - b.years[i].num
                if sumA ~= sumB then return sumA > sumB end
            end
            return a.name < b.name
        end)
        
        local html_rows = {}
        for rank, p in ipairs(list) do
            local row = {'|-'}
            
            if hide_rank then
                table.insert(row, '| style="text-align:center;" | ')
            else
                table.insert(row, '| style="text-align:center;" | ' .. rank)
            end
            
            local p_link = "[[" .. string.gsub(p.name, "_", " ") .. "]]"
            table.insert(row, '| style="text-align:center; white-space:nowrap;" | ' .. p_link)
            
            for i, year in ipairs(years) do
                local v_str = ""
                if p.years[i].raw and p.years[i].raw ~= "" then
                    v_str = p.years[i].raw
                    -- ИСПРАВЛЕНИЕ: Добавляем плюс, только если его нет в оригинальной строке
                    if metric_name == "Показатель_полезности" and p.years[i].num > 0 and not string.match(v_str, "^%+") then
                        v_str = "+" .. v_str
                    end
                end
                table.insert(row, '| style="text-align:center;" | ' .. v_str)
                
                -- Копим общую сумму колонки
                col_totals[i] = col_totals[i] + p.years[i].num
            end
            
            local tot_str = tostring(p.total)
            if metric_name == "Показатель_полезности" and p.total > 0 then
                tot_str = "+" .. tot_str
            end
            
            if p.pen_total > 0 then
                tot_str = "'''" .. tot_str .. "''' (" .. p.pen_total .. ")"
            else
                tot_str = "'''" .. tot_str .. "'''"
            end
            table.insert(row, '| style="text-align:center;" | ' .. tot_str)
            
            -- Копим общие итоги
            grand_total = grand_total + p.total
            total_pens = total_pens + p.pen_total
            
            table.insert(html_rows, table.concat(row, "\n"))
        end
        
        return table.concat(html_rows, "\n")
    end

    -- НАЧАЛО ОТРИСОВКИ ТАБЛИЦЫ
    local html = {}
    table.insert(html, '{{ПШТ}}{{стиль столбцов}}')
    table.insert(html, '{| border="1" cellspacing="1" cellpadding="1" class="article-table sortable ts-stickytableheader"')
    
    -- Шапка
    local header_row = {'|-', '! style="background-color:#e0f0ff;" | Место !! style="background-color:#e0f0ff;" | Игрок'}
    for _, year in ipairs(years) do
        local short_year = "'" .. string.sub(year, 3, 4)
        table.insert(header_row, '! style="background-color:#e0f0ff;" | [[' .. year .. '|' .. short_year .. ']]')
    end
    
    local total_text = '! style="background-color:#e0f0ff;" | ВСЕГО'
    if pen_stat and pen_stat ~= "" then 
        total_text = '! style="background-color:#e0f0ff;" | ВСЕГО<br>\'\'(в скобках —<br>с<br>пенальти)\'\'' 
    end
    table.insert(header_row, total_text)
    table.insert(html, table.concat(header_row, "\n"))
    
    -- Запускаем генерацию для основного показателя
    table.insert(html, generate_rows(main_metric, pen_stat, false))
    
    -- ИСКЛЮЧЕНИЕ: Если это Голы, прикрепляем Автоголы
    if main_metric == "Голы" then
        local ag_header = {'|-'}
        table.insert(ag_header, '| style="background-color:silver;" | ')
        table.insert(ag_header, '| style="background-color:silver; text-align:center;" | \'\'\'Автоголы\'\'\'')
        for i = 1, #years do
            table.insert(ag_header, '| style="background-color:silver;" | ')
        end
        table.insert(ag_header, '| style="background-color:silver;" | ')
        table.insert(html, table.concat(ag_header, "\n"))
        
        table.insert(html, generate_rows("Автоголы", nil, true))
    end
    
    -- ИСКЛЮЧЕНИЕ: Для Голов добавляем 45 к 2006 году
    if main_metric == "Голы" then
        for i, year in ipairs(years) do
            if year == "2006" then
                col_totals[i] = col_totals[i] + 45
                grand_total = grand_total + 45
            end
        end
    end
    
    -- ИСКЛЮЧЕНИЕ: Хардкодим количество реальных матчей на турнирах с 2022 года
    if main_metric == "Матчи" then
        grand_total = 229 -- Добавляем матчи первых четырех турниров (47+54+64+64)
        for i, year in ipairs(years) do
            if i == #years then
                -- Для последнего турнира берем цифру из 3-го параметра шаблона
                col_totals[i] = current_tournament_matches
            else
                -- Все завершенные с 2022 года турниры имели по 64 матча
                col_totals[i] = 64
            end
            grand_total = grand_total + col_totals[i]
        end
    end
    
    -- ИСКЛЮЧЕНИЕ: Убираем строку ВСЕГО для некоторых показателей
    if main_metric ~= "Матчи_в_поле" and main_metric ~= "Матчи_в_воротах" and main_metric ~= "Показатель_полезности" then
        local t_row = {'|-'}
        table.insert(t_row, '|') -- пустая ячейка под местом
        table.insert(t_row, '| style="text-align:center;" | \'\'\'ВСЕГО\'\'\'')
        
        for i, _ in ipairs(years) do
            local s_str = tostring(col_totals[i])
            table.insert(t_row, '| style="text-align:center;" | \'\'\'' .. s_str .. '\'\'\'')
        end
        
        local g_str = tostring(grand_total)
        if total_pens > 0 then
            g_str = "\'\'\'" .. g_str .. "\'\'\' (" .. total_pens .. ")"
        else
            g_str = "\'\'\'" .. g_str .. "\'\'\'"
        end
        
        -- ИСКЛЮЧЕНИЕ: Добавляем сноску к общей сумме матчей
        if main_metric == "Матчи" then
            g_str = g_str .. " <ref>С учётом всех матчей до ЧТМ-2022.</ref>"
        end
        
        table.insert(t_row, '| style="text-align:center;" | ' .. g_str)
        
        table.insert(html, table.concat(t_row, "\n"))
    end
    
    table.insert(html, '|}')
    return frame:preprocess(table.concat(html, "\n"))
end

-----------------------------------------------------------------------
-- СПЕЦИАЛЬНАЯ ФУНКЦИЯ: Средняя результативность / Средние передачи
-----------------------------------------------------------------------
function p.RenderAvgTable(frame)
    local metric = frame.args[1] or "Голы"
    local years, col_name, avg_col_name, group_titles
    
    -- Настраиваем переменные в зависимости от того, что считаем
    if metric == "Передачи" then
        years = {"2026", "2030", "2034", "2038", "2042", "2046"}
        col_name = "Передачи"
        avg_col_name = "Ср. пер."
        group_titles = {
            [1] = "Не менее 20 матчей в поле",
            [2] = "От 5 до 19 матчей в поле",
            [3] = "Менее 5 матчей в поле",
            [4] = "Вообще не отдавали"
        }
    else
        metric = "Голы"
        years = {"2022", "2026", "2030", "2034", "2038", "2042", "2046"}
        col_name = "Голы"
        avg_col_name = "Ср. рез."
        group_titles = {
            [1] = "Не менее 20 матчей в поле",
            [2] = "От 5 до 19 матчей в поле",
            [3] = "Менее 5 матчей в поле",
            [4] = "Вообще не забивали"
        }
    end

    -- Вспомогательная функция для подсчета среднего
    local function calc_avg(v, m)
        if m == 0 then return 0 end
        return v / m
    end

    -- 1. Собираем имена всех игроков
    local player_names = {}
    for key, _ in pairs(data) do
        local p_match = string.match(key, "^(.+)/[^/]+/%d%d%d%d$") or string.match(key, "^(.+)/[^/]+$")
        if p_match then player_names[p_match] = true end
    end

    -- 2. Вытаскиваем стату и считаем показатели
    local list = {}
    for player, _ in pairs(player_names) do
        local pdata = {
            name = player,
            total_m = 0,
            total_val = 0,
            years = {}
        }
        local has_data = false

        for i, y in ipairs(years) do
            -- Обязательно берем матчи в поле за ТОТ ЖЕ год
            local m = tonumber(data[player .. "/Матчи_в_поле/" .. y]) or 0
            local v = tonumber(data[player .. "/" .. metric .. "/" .. y]) or 0
            
            pdata.years[i] = { m = m, v = v }
            pdata.total_m = pdata.total_m + m
            pdata.total_val = pdata.total_val + v
            
            if m > 0 then has_data = true end
        end

        -- Если игрок выходил в поле хотя бы раз в рамках заданных годов
        if has_data then
            pdata.avg = calc_avg(pdata.total_val, pdata.total_m)
            
            -- Определяем группу (касту)
            if pdata.total_val == 0 then
                pdata.group = 4 -- Вообще не забивали / не отдавали
            elseif pdata.total_m >= 20 then
                pdata.group = 1 -- Элита (>= 20)
            elseif pdata.total_m >= 5 then
                pdata.group = 2 -- Средняки (5 - 19)
            else
                pdata.group = 3 -- Мало играли (< 5)
            end
            
            table.insert(list, pdata)
        end
    end

    -- 3. СЛОЖНАЯ СОРТИРОВКА
    table.sort(list, function(a, b)
        -- Сначала сортируем по группам (1, 2, 3, 4)
        if a.group ~= b.group then
            return a.group < b.group
        end

        -- Внутри группы "Вообще не забивали / не отдавали"
        if a.group == 4 then
            -- Меньше матчей -> выше место
            if a.total_m ~= b.total_m then return a.total_m < b.total_m end
            return a.name < b.name
        end

        -- Внутри групп с успешными действиями (1, 2, 3)
        local strA = string.format("%.2f", a.avg)
        local strB = string.format("%.2f", b.avg)
        
        if strA ~= strB then
            return tonumber(strA) > tonumber(strB)
        end
        
        -- Если средняя в точности равна, отсекаем турниры с конца
        local sumA_v, sumA_m = a.total_val, a.total_m
        local sumB_v, sumB_m = b.total_val, b.total_m
        
        for i = #years, 1, -1 do
            sumA_v = sumA_v - a.years[i].v
            sumA_m = sumA_m - a.years[i].m
            sumB_v = sumB_v - b.years[i].v
            sumB_m = sumB_m - b.years[i].m
            
            local tA = string.format("%.2f", calc_avg(sumA_v, sumA_m))
            local tB = string.format("%.2f", calc_avg(sumB_v, sumB_m))
            
            if tA ~= tB then
                return tonumber(tA) > tonumber(tB)
            end
        end
        
        return a.name < b.name
    end)

    -- 4. ГЕНЕРАЦИЯ ТАБЛИЦЫ
    local html = {}
    table.insert(html, '{{ПШТ}}{{стиль столбцов}}')
    table.insert(html, '{| border="1" cellspacing="1" cellpadding="1" class="article-table sortable ts-stickytableheader"')
    
    -- Шапка
    local h = {'|-', '! style="background-color:#e0f0ff;" | Место !! style="background-color:#e0f0ff;" | Игрок !! style="background-color:#e0f0ff;" | Матчи<br>в поле !! style="background-color:#e0f0ff;" | ' .. col_name .. ' !! style="background-color:#e0f0ff;" | ' .. avg_col_name}
    for _, y in ipairs(years) do
        local short_year = "'" .. string.sub(y, 3, 4)
        table.insert(h, '! style="background-color:#e0f0ff;" | [[' .. y .. '|' .. short_year .. ']]')
    end
    table.insert(html, table.concat(h, "\n"))
    
    local current_group = 0
    local rank = 1
    
    for _, p in ipairs(list) do
        if p.group ~= current_group then
            current_group = p.group
            local colspan = 5 + #years
            table.insert(html, '|-')
            table.insert(html, '| colspan="' .. colspan .. '" style="background-color:silver; text-align:center;" | \'\'\'' .. group_titles[current_group] .. '\'\'\'')
        end
        
        local row = {'|-'}
        
        -- Место
        table.insert(row, '| style="text-align:center;" | ' .. rank)
        
        -- Имя
        local p_link = "[[" .. string.gsub(p.name, "_", " ") .. "]]"
        table.insert(row, '| style="text-align:center; white-space:nowrap;" | ' .. p_link)
        
        -- Матчи в поле и Голы/Передачи
        table.insert(row, '| style="text-align:center;" | ' .. p.total_m)
        table.insert(row, '| style="text-align:center;" | ' .. p.total_val)
        
        -- Итоговая средняя
        local avg_style = 'style="text-align:center; font-weight:bold;'
        if p.total_m < 20 then
            avg_style = avg_style .. ' background-color:lightsalmon;'
        end
        avg_style = avg_style .. '"'
        table.insert(row, '| ' .. avg_style .. ' | ' .. string.format("%.2f", p.avg))
        
        -- Года
        for i, _ in ipairs(years) do
            local m = p.years[i].m
            local v = p.years[i].v
            
            if m == 0 then
                table.insert(row, '| style="text-align:center;" | ')
            else
                local y_avg = string.format("%.2f", v / m)
                local cell_style = 'style="text-align:center;'
                if m < 20 then
                    cell_style = cell_style .. ' background-color:lightsalmon;'
                end
                cell_style = cell_style .. '"'
                
                table.insert(row, '| ' .. cell_style .. ' | ' .. y_avg)
            end
        end
        
        table.insert(html, table.concat(row, "\n"))
        rank = rank + 1
    end

    table.insert(html, '|}')
    return frame:preprocess(table.concat(html, "\n"))
end

-----------------------------------------------------------------------
-- УЛЬТРА-ФУНКЦИЯ: Автоматические таблицы пенальти (Хронология, Результаты, Турниры)
-----------------------------------------------------------------------
function p.RenderPenalties(frame)
    local mode = frame.args[1] or "Хронология" -- Хронология / Результаты / ПоТурнирам
    local time_type = frame.args[2] or "Все"   -- Все / Игровое
    local spec_year = frame.args[3]            -- Год (только для режима "Результаты")
    
    local ALL_YEARS = {"2006", "2010", "2014", "2018", "2022", "2026", "2030", "2034", "2038", "2042", "2046"}
    
    -- Вспомогательная функция: парсинг строки "г/вр/в/м/шт/п"
    local function parse_pen_str(str)
        if not str or str == "" then return 0, 0, 0, 0, 0, 0 end
        local g, k, o, w, p_post, c = string.match(str, "(%d+)/(%d+)/(%d+)/(%d+)/(%d+)/(%d+)")
        return tonumber(g) or 0, tonumber(k) or 0, tonumber(o) or 0, tonumber(w) or 0, tonumber(p_post) or 0, tonumber(c) or 0
    end

    -- Вспомогательная функция: сбор статы игрока за конкретный год
    local function get_player_year_stats(player, year, t_type)
        local g, k, o, w, p_post, c = 0, 0, 0, 0, 0, 0
        
        -- Игровое время
        local s1 = data[player .. "/Пенальти_Игровое/" .. year]
        local g1, k1, o1, w1, p1, c1 = parse_pen_str(s1)
        g = g + g1; k = k + k1; o = o + o1; w = w + w1; p_post = p_post + p1; c = c + c1
        
        -- Серии (только если тип "Все")
        if t_type == "Все" then
            local s2 = data[player .. "/Пенальти_Серии/" .. year]
            local g2, k2, o2, w2, p2, c2 = parse_pen_str(s2)
            g = g + g2; k = k + k2; o = o + o2; w = w + w2; p_post = p_post + p2; c = c + c2
        end
        
        local u = g + k + o + w + p_post + c
        return u, g, k, o, w, p_post, c
    end

    -- Математика: Показатель и Процент
    local function calc_pok(g, u) return g - (u - g) * 2 end
    local function format_pok(pok) return pok > 0 and "+" .. pok or tostring(pok) end
    local function format_pct(g, u)
        if u == 0 then return "0" end
        local val = math.floor((g / u * 100) * 100 + 0.5) / 100
        local str = string.format("%.2f", val)
        str = string.gsub(str, "%.00$", "")
        str = string.gsub(str, "(%..-)0+$", "%1")
        return str
    end

    -- РЕЖИМ 3: ПО ТУРНИРАМ (Вообще без игроков)
    if mode == "ПоТурнирам" then
        local players_set = {}
        for key, _ in pairs(data) do
            local p_match = string.match(key, "^(.+)/Пенальти_")
            if p_match then players_set[p_match] = true end
        end

        local html = {}
        table.insert(html, '{{ПШТ}}{{стиль столбцов}}')
        table.insert(html, '{| border="1" cellspacing="1" cellpadding="1" class="article-table sortable ts-stickytableheader"')
        table.insert(html, '|- \n! style="background-color:#e0f0ff;" | № \n! style="background-color:#e0f0ff;" | ЧТМ \n! style="background-color:#e0f0ff;" | % \n! style="background-color:#e0f0ff;" | [[Лучший пенальтист#Система определения победителя|Показатель]] \n! style="background-color:#e0f0ff;" | Удары \n! style="background-color:#e0f0ff;" | Голы \n! style="background-color:#e0f0ff;" | вр. \n! style="background-color:#e0f0ff;" | в. \n! style="background-color:#e0f0ff;" | м. \n! style="background-color:#e0f0ff;" | шт. \n! style="background-color:#e0f0ff;" | п.')
        
        local tot_u, tot_g, tot_k, tot_o, tot_w, tot_p, tot_c = 0,0,0,0,0,0,0
        local rank = 1
        
        for _, year in ipairs(ALL_YEARS) do
            local y_u, y_g, y_k, y_o, y_w, y_p, y_c = 0,0,0,0,0,0,0
            for player, _ in pairs(players_set) do
                local u, g, k, o, w, p_post, c = get_player_year_stats(player, year, time_type)
                y_u = y_u + u; y_g = y_g + g; y_k = y_k + k; y_o = y_o + o; y_w = y_w + w; y_p = y_p + p_post; y_c = y_c + c
            end
            
            if y_u > 0 then
                table.insert(html, '|- \n| style="text-align:center;" | ' .. rank .. ' \n| style="text-align:center;" | [[' .. year .. ']] \n| style="text-align:center;" | ' .. format_pct(y_g, y_u) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. format_pok(calc_pok(y_g, y_u)) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. y_u .. ' \n| style="text-align:center; font-weight:bold;" | ' .. y_g .. ' \n| style="text-align:center;" | ' .. y_k .. ' \n| style="text-align:center;" | ' .. y_o .. ' \n| style="text-align:center;" | ' .. y_w .. ' \n| style="text-align:center;" | ' .. y_p .. ' \n| style="text-align:center;" | ' .. y_c)
                rank = rank + 1
                tot_u = tot_u + y_u; tot_g = tot_g + y_g; tot_k = tot_k + y_k; tot_o = tot_o + y_o; tot_w = tot_w + y_w; tot_p = tot_p + y_p; tot_c = tot_c + y_c
            end
        end
        
        table.insert(html, '|- \n| colspan="2" style="text-align:center;" | \'\'\'ВСЕГО\'\'\' \n| style="text-align:center; font-weight:bold;" | ' .. format_pct(tot_g, tot_u) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. format_pok(calc_pok(tot_g, tot_u)) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_u .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_g .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_k .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_o .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_w .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_p .. ' \n| style="text-align:center; font-weight:bold;" | ' .. tot_c)
        table.insert(html, '|}')
        return frame:preprocess(table.concat(html, "\n"))
    end

    -- СБОР ИГРОКОВ (Для Хронологии и Результатов)
    local players_set = {}
    for key, _ in pairs(data) do
        local p_match = string.match(key, "^(.+)/Пенальти_")
        if p_match then players_set[p_match] = true end
    end

    local list = {}
    for player, _ in pairs(players_set) do
        local pdata = {name = player, u=0, g=0, k=0, o=0, w=0, p=0, c=0, years = {}}
        
        for i, year in ipairs(ALL_YEARS) do
            local y_u, y_g, y_k, y_o, y_w, y_p, y_c = get_player_year_stats(player, year, time_type)
            pdata.years[i] = {u = y_u, g = y_g}
            
            -- Если не выбран конкретный год ИЛИ год совпадает с выбранным
            if not spec_year or spec_year == "" or spec_year == year then
                pdata.u = pdata.u + y_u; pdata.g = pdata.g + y_g; pdata.k = pdata.k + y_k
                pdata.o = pdata.o + y_o; pdata.w = pdata.w + y_w; pdata.p = pdata.p + y_p; pdata.c = pdata.c + y_c
            end
        end
        
        if pdata.u > 0 then
            pdata.pok = calc_pok(pdata.g, pdata.u)
            pdata.stvor = pdata.g + pdata.k
            pdata.frame = pdata.stvor + pdata.p + pdata.c
            table.insert(list, pdata)
        end
    end

    -- УМНАЯ СОРТИРОВКА (С учетом всех твоих условий)
    table.sort(list, function(a, b)
        if a.pok ~= b.pok then return a.pok > b.pok end
        if a.g ~= b.g then return a.g > b.g end
        
        -- Отсечение последних турниров (только если мы смотрим общую таблицу, а не один год)
        if not spec_year or spec_year == "" then
            local sumA, sumB = a.g, b.g
            for i = #ALL_YEARS, 1, -1 do
                sumA = sumA - a.years[i].g
                sumB = sumB - b.years[i].g
                if sumA ~= sumB then return sumA > sumB end
            end
        end
        
        -- Доп. параметры только для режима Результатов
        if mode == "Результаты" then
            if a.stvor ~= b.stvor then return a.stvor > b.stvor end
            if a.frame ~= b.frame then return a.frame > b.frame end
        end
        
        return a.name < b.name
    end)

    -- ГЕНЕРАЦИЯ HTML
    local html = {}
    table.insert(html, '{{ПШТ}}{{стиль столбцов}}')
    table.insert(html, '{| border="1" cellspacing="1" cellpadding="1" class="article-table sortable ts-stickytableheader"')

    -- РЕЖИМ 1: ХРОНОЛОГИЯ
    if mode == "Хронология" then
        local h = {'|- \n! style="background-color:#e0f0ff;" | Место \n! style="background-color:#e0f0ff;" | Игрок'}
        for _, y in ipairs(ALL_YEARS) do
            table.insert(h, '! style="background-color:#e0f0ff;" | [[' .. y .. '|\'' .. string.sub(y, 3, 4) .. ']]')
        end
        table.insert(h, '! style="background-color:#e0f0ff;" | ВСЕГО \n! style="background-color:#e0f0ff;" | % \n! style="background-color:#e0f0ff;" | [[Лучший пенальтист#Система определения победителя|Показатель]]')
        table.insert(html, table.concat(h, "\n"))
        
        local col_u, col_g = {}, {}
        for i = 1, #ALL_YEARS do col_u[i]=0; col_g[i]=0 end
        local tot_u, tot_g = 0, 0
        
        for rank, p in ipairs(list) do
            local row = {'|- \n| style="text-align:center;" | ' .. rank .. ' \n| style="text-align:center; white-space:nowrap;" | [[' .. string.gsub(p.name, "_", " ") .. ']]'}
            for i, year in ipairs(ALL_YEARS) do
                local u, g = p.years[i].u, p.years[i].g
                col_u[i] = col_u[i] + u; col_g[i] = col_g[i] + g
                if u > 0 then
                    table.insert(row, '| style="text-align:center;" | ' .. u .. '/' .. g)
                else
                    table.insert(row, '| style="text-align:center;" | 0')
                end
            end
            tot_u = tot_u + p.u; tot_g = tot_g + p.g
            table.insert(row, '| style="text-align:center;" | \'\'\'' .. p.u .. '/' .. p.g .. '\'\'\' \n| style="text-align:center;" | ' .. format_pct(p.g, p.u) .. '% \n| style="text-align:center; font-weight:bold;" | ' .. format_pok(p.pok))
            table.insert(html, table.concat(row, "\n"))
        end
        
        local t_row = {'|- \n| \n| style="text-align:center;" | \'\'\'ВСЕГО\'\'\''}
        for i = 1, #ALL_YEARS do
            if col_u[i] > 0 then
                table.insert(t_row, '| style="text-align:center; font-weight:bold;" | ' .. col_u[i] .. '/' .. col_g[i])
            else
                table.insert(t_row, '| style="text-align:center; font-weight:bold;" | 0')
            end
        end
        table.insert(t_row, '| style="text-align:center; font-weight:bold;" | ' .. tot_u .. '/' .. tot_g .. ' \n| style="text-align:center; font-weight:bold;" | ' .. format_pct(tot_g, tot_u) .. '% \n| style="text-align:center; font-weight:bold;" | ' .. format_pok(calc_pok(tot_g, tot_u)))
        table.insert(html, table.concat(t_row, "\n"))
        
    -- РЕЖИМ 2: РЕЗУЛЬТАТЫ УДАРОВ
    elseif mode == "Результаты" then
        table.insert(html, '|- \n! style="background-color:#e0f0ff;" | Место \n! style="background-color:#e0f0ff;" | Игрок \n! style="background-color:#e0f0ff;" | % \n! style="background-color:#e0f0ff;" | [[Лучший пенальтист#Система определения победителя|Показатель]] \n! style="background-color:#e0f0ff;" | Удары \n! style="background-color:#e0f0ff;" | Голы \n! style="background-color:#e0f0ff;" | вр. \n! style="background-color:#e0f0ff;" | в. \n! style="background-color:#e0f0ff;" | м. \n! style="background-color:#e0f0ff;" | шт. \n! style="background-color:#e0f0ff;" | п.')
        
        local t_u, t_g, t_k, t_o, t_w, t_p, t_c = 0,0,0,0,0,0,0
        for rank, p in ipairs(list) do
            t_u = t_u + p.u; t_g = t_g + p.g; t_k = t_k + p.k; t_o = t_o + p.o; t_w = t_w + p.w; t_p = t_p + p.p; t_c = t_c + p.c
            table.insert(html, '|- \n| style="text-align:center;" | ' .. rank .. ' \n| style="text-align:center; white-space:nowrap;" | [[' .. string.gsub(p.name, "_", " ") .. ']] \n| style="text-align:center;" | ' .. format_pct(p.g, p.u) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. format_pok(p.pok) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. p.u .. ' \n| style="text-align:center; font-weight:bold;" | ' .. p.g .. ' \n| style="text-align:center;" | ' .. p.k .. ' \n| style="text-align:center;" | ' .. p.o .. ' \n| style="text-align:center;" | ' .. p.w .. ' \n| style="text-align:center;" | ' .. p.p .. ' \n| style="text-align:center;" | ' .. p.c)
        end
        
        table.insert(html, '|- \n| colspan="2" style="text-align:center;" | \'\'\'ВСЕГО\'\'\' \n| style="text-align:center; font-weight:bold;" | ' .. format_pct(t_g, t_u) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. format_pok(calc_pok(t_g, t_u)) .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_u .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_g .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_k .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_o .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_w .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_p .. ' \n| style="text-align:center; font-weight:bold;" | ' .. t_c)
    end

    table.insert(html, '|}')
    return frame:preprocess(table.concat(html, "\n"))
end

-----------------------------------------------------------------------
-- МАГИЧЕСКАЯ ФУНКЦИЯ: Мега-трики Голов и Передач
-----------------------------------------------------------------------
function p.RenderMegaTricks(frame)
    local mode = frame.args[1] or "Все" -- "Все", "2034", "Текст", "Сводка"
    local stat_type = frame.args[2] or "Голы" -- "Голы" или "Передачи"
    
    local ALL_YEARS
    local db_key
    local col_word_3, col_word_more
    local has_6
    
    if stat_type == "Передачи" then
        ALL_YEARS = {"2026", "2030", "2034", "2038", "2042", "2046"}
        db_key = "Мега%-трики_передач_Детали"
        col_word_3 = "передачи"
        col_word_more = "передач"
        has_6 = false
    else
        ALL_YEARS = {"2006", "2010", "2014", "2018", "2022", "2026", "2030", "2034", "2038", "2042", "2046"}
        db_key = "Мега%-трики_Детали"
        col_word_3 = "гола"
        col_word_more = "голов"
        has_6 = true
    end

    local text_data = {
        po_all = { [3]={}, [4]={}, [5]={}, [6]={} },
        qf_plus = { [3]={}, [4]={}, [5]={}, [6]={} },
        sf_plus = { [3]={}, [4]={}, [5]={}, [6]={} },
        finals = { [3]={}, [4]={}, [5]={}, [6]={} }
    }

    -- 1. СБОР ИГРОКОВ И ИХ ДАННЫХ
    local players_set = {}
    for key, _ in pairs(data) do
        local p_match = string.match(key, "^(.+)/" .. db_key)
        if p_match then players_set[p_match] = true end
    end

    local list = {}
    local table_totals = {g3=0, g4=0, g5=0, g6=0, tot=0, gr=0, po=0}

    for player, _ in pairs(players_set) do
        -- Точное имя ключа без экранирования для извлечения из data
        local exact_key = (stat_type == "Передачи") and "Мега-трики_передач_Детали" or "Мега-трики_Детали"
        local pdata = {name = player, total=0, g3=0, g4=0, g5=0, g6=0, po_tot=0, po3=0, po4=0, po5=0, po6=0, gr_tot=0, years={}}
        
        for i, year in ipairs(ALL_YEARS) do
            pdata.years[i] = {total = 0}
            local str = data[player .. "/" .. exact_key .. "/" .. year]
            
            if str and str ~= "" then
                for stage, goals_str in mw.ustring.gmatch(str, "([ГВЧПТФ])(%d)") do
                    local g = tonumber(goals_str)
                    local is_po = (stage == 'В' or stage == 'Ч' or stage == 'П' or stage == 'Ф')
                    
                    if mode == "Все" or mode == "Текст" or mode == "Сводка" or mode == year then
                        pdata.total = pdata.total + 1
                        pdata.years[i].total = pdata.years[i].total + 1
                        
                        if g == 3 then pdata.g3 = pdata.g3 + 1
                        elseif g == 4 then pdata.g4 = pdata.g4 + 1
                        elseif g == 5 then pdata.g5 = pdata.g5 + 1
                        elseif g == 6 then pdata.g6 = pdata.g6 + 1 end
                        
                        if is_po then
                            pdata.po_tot = pdata.po_tot + 1
                            if g == 3 then pdata.po3 = pdata.po3 + 1
                            elseif g == 4 then pdata.po4 = pdata.po4 + 1
                            elseif g == 5 then pdata.po5 = pdata.po5 + 1
                            elseif g == 6 then pdata.po6 = pdata.po6 + 1 end
                        else
                            pdata.gr_tot = pdata.gr_tot + 1
                        end
                    end
                    
                    if mode == "Текст" then
                        if stage == 'Ф' then
                            table.insert(text_data.finals[g], {name = player, year = year})
                        end
                        if stage == 'Ф' or stage == 'П' then
                            text_data.sf_plus[g][player] = (text_data.sf_plus[g][player] or 0) + 1
                        end
                        if stage == 'Ф' or stage == 'П' or stage == 'Ч' then
                            text_data.qf_plus[g][player] = (text_data.qf_plus[g][player] or 0) + 1
                        end
                        if is_po then
                            text_data.po_all[g][player] = (text_data.po_all[g][player] or 0) + 1
                        end
                    end
                end
            end
        end
        
        if pdata.total > 0 then
            table.insert(list, pdata)
            table_totals.g3 = table_totals.g3 + pdata.g3
            table_totals.g4 = table_totals.g4 + pdata.g4
            table_totals.g5 = table_totals.g5 + pdata.g5
            table_totals.g6 = table_totals.g6 + pdata.g6
            table_totals.tot = table_totals.tot + pdata.total
            table_totals.gr = table_totals.gr + pdata.gr_tot
            table_totals.po = table_totals.po + pdata.po_tot
        end
    end

    -- 2. ГЕНЕРАЦИЯ ОБЩЕЙ СВОДКИ (Режим "Сводка")
    if mode == "Сводка" then
        local function get_plural(n, form1, form2, form5)
            local n10 = n % 10
            local n100 = n % 100
            if n10 == 1 and n100 ~= 11 then return form1 end
            if n10 >= 2 and n10 <= 4 and (n100 < 10 or n100 >= 20) then return form2 end
            return form5
        end

        local html = {}
        if table_totals.g3 > 0 then table.insert(html, "* '''Хет-трик''' (3 " .. col_word_3 .. ") — " .. table_totals.g3 .. " " .. get_plural(table_totals.g3, "раз", "раза", "раз") .. ".") end
        if table_totals.g4 > 0 then table.insert(html, "* '''Покер''' (4 " .. col_word_3 .. ") — " .. table_totals.g4 .. " " .. get_plural(table_totals.g4, "раз", "раза", "раз") .. ".") end
        if table_totals.g5 > 0 then table.insert(html, "* '''Пента-трик''' (5 " .. col_word_more .. ") — " .. table_totals.g5 .. " " .. get_plural(table_totals.g5, "раз", "раза", "раз") .. ".") end
        if has_6 and table_totals.g6 > 0 then table.insert(html, "* '''Гекса-трик''' (6 " .. col_word_more .. ") — " .. table_totals.g6 .. " " .. get_plural(table_totals.g6, "раз", "раза", "раз") .. ".") end
        
        local players_count = #list
        local action_word = (stat_type == "Передачи") and "отдать" or "оформить"
        table.insert(html, "Как минимум один мега-трик в истории ЧТМ удалось " .. action_word .. " " .. players_count .. " " .. get_plural(players_count, "игроку", "игрокам", "игрокам") .. ".")
        
        return frame:preprocess(table.concat(html, "\n"))
    end

    -- 3. ГЕНЕРАЦИЯ ТЕКСТА ПЛЕЙ-ОФФ (Режим "Текст")
    if mode == "Текст" then
        local function build_group(counts)
            local rev, keys = {}, {}
            local sum = 0
            for p, c in pairs(counts) do
                if not rev[c] then rev[c] = {}; table.insert(keys, c) end
                table.insert(rev[c], p)
                sum = sum + c
            end
            if sum == 0 then return 0, "" end
            table.sort(keys, function(a,b) return a>b end)
            
            local parts = {}
            for _, c in ipairs(keys) do
                table.sort(rev[c])
                local links = {}
                for _, p in ipairs(rev[c]) do table.insert(links, "[[" .. string.gsub(p, "_", " ") .. "]]") end
                local str = table.concat(links, ", ")
                if c > 1 then str = str .. " (по " .. c .. ")"
                elseif c == 1 and #keys > 1 then str = str .. " (по 1)" end
                table.insert(parts, str)
            end
            return sum, table.concat(parts, ", ") .. "."
        end

        local function build_finals(arr)
            if #arr == 0 then return 0, "" end
            table.sort(arr, function(a, b)
                if a.year ~= b.year then return tonumber(a.year) < tonumber(b.year) end
                return a.name < b.name
            end)
            local parts = {}
            for _, item in ipairs(arr) do
                table.insert(parts, "* '''[[" .. string.gsub(item.name, "_", " ") .. "]]''' ([[Финал ЧТМ-" .. item.year .. "|" .. item.year .. "]])")
            end
            return #arr, table.concat(parts, "\n")
        end

        local function section(title, data_grp, is_fin)
            local res = {"=== " .. title .. " ==="}
            local labels = { [6]="Гекса-трики", [5]="Пента-трики", [4]="Покеры", [3]="Хет-трики" }
            local start_g = has_6 and 6 or 5
            
            for g = start_g, 3, -1 do
                if is_fin then
                    local count, txt = build_finals(data_grp[g])
                    if count > 0 then table.insert(res, "'''" .. labels[g] .. " (" .. count .. "):'''\n" .. txt) end
                else
                    local count, txt = build_group(data_grp[g])
                    if count > 0 then table.insert(res, "'''" .. labels[g] .. " (" .. count .. "):''' " .. txt) end
                end
            end
            
            -- Если секция пуста (только заголовок), возвращаем пустую строку
            if #res == 1 then return "" end
            return table.concat(res, "\n\n")
        end

        local text_html = {}
        local s1 = section("Всего", text_data.po_all, false)
        local s2 = section("В четвертьфиналах и позже", text_data.qf_plus, false)
        local s3 = section("В полуфиналах и позже", text_data.sf_plus, false)
        local s4 = section("В финалах", text_data.finals, true)
        
        if s1 ~= "" then table.insert(text_html, s1) end
        if s2 ~= "" then table.insert(text_html, s2) end
        if s3 ~= "" then table.insert(text_html, s3) end
        if s4 ~= "" then table.insert(text_html, s4) end
        
        if #text_html == 0 then return "''В плей-офф мега-триков пока не зафиксировано.''" end
        return frame:preprocess(table.concat(text_html, "\n\n"))
    end

    -- 4. СУПЕР-СОРТИРОВКА ДЛЯ ТАБЛИЦ
    table.sort(list, function(a, b)
        if a.total ~= b.total then return a.total > b.total end
        if has_6 and a.g6 ~= b.g6 then return a.g6 > b.g6 end
        if a.g5 ~= b.g5 then return a.g5 > b.g5 end
        if a.g4 ~= b.g4 then return a.g4 > b.g4 end
        if a.g3 ~= b.g3 then return a.g3 > b.g3 end
        if a.po_tot ~= b.po_tot then return a.po_tot > b.po_tot end
        if has_6 and a.po6 ~= b.po6 then return a.po6 > b.po6 end
        if a.po5 ~= b.po5 then return a.po5 > b.po5 end
        if a.po4 ~= b.po4 then return a.po4 > b.po4 end
        if a.po3 ~= b.po3 then return a.po3 > b.po3 end
        if mode == "Все" then
            local sA, sB = a.total, b.total
            for i = #ALL_YEARS, 1, -1 do
                sA = sA - a.years[i].total
                sB = sB - b.years[i].total
                if sA ~= sB then return sA > sB end
            end
        end
        return a.name < b.name
    end)

    -- 5. ГЕНЕРАЦИЯ ТАБЛИЦЫ
    local html = {}
    table.insert(html, '{{ПШТ}}{{стиль столбцов}}')
    table.insert(html, '{| border="1" cellspacing="1" cellpadding="1" class="article-table sortable ts-stickytableheader"')
    
    local h = {'|- \n! style="background-color:#e0f0ff;" | Место \n! style="background-color:#e0f0ff;" | Игрок \n! style="background-color:#e0f0ff;" | 3 ' .. col_word_3 .. ' \n! style="background-color:#e0f0ff;" | 4 ' .. col_word_3 .. ' \n! style="background-color:#e0f0ff;" | 5 ' .. col_word_more}
    if has_6 then table.insert(h, '! style="background-color:#e0f0ff;" | 6 ' .. col_word_more) end
    table.insert(h, '! style="background-color:#e0f0ff;" | ВСЕГО \n! style="background-color:#e0f0ff;" | Группа/плей-офф')
    table.insert(html, table.concat(h, " \n"))
    
    for rank, p in ipairs(list) do
        local row = {'|- \n| style="text-align:center;" | ' .. rank}
        table.insert(row, '| style="text-align:center; white-space:nowrap;" | [[' .. string.gsub(p.name, "_", " ") .. ']]')
        table.insert(row, '| style="text-align:center;" | ' .. p.g3)
        table.insert(row, '| style="text-align:center;" | ' .. p.g4)
        table.insert(row, '| style="text-align:center;" | ' .. p.g5)
        if has_6 then table.insert(row, '| style="text-align:center;" | ' .. p.g6) end
        table.insert(row, '| style="text-align:center; font-weight:bold;" | ' .. p.total)
        table.insert(row, '| style="text-align:center;" | (' .. p.gr_tot .. '/' .. p.po_tot .. ')')
        table.insert(html, table.concat(row, " \n"))
    end
    
    local t_row = {'|- \n| \n| style="text-align:center;" | \'\'\'ВСЕГО\'\'\' \n| style="text-align:center; font-weight:bold;" | ' .. table_totals.g3 .. ' \n| style="text-align:center; font-weight:bold;" | ' .. table_totals.g4 .. ' \n| style="text-align:center; font-weight:bold;" | ' .. table_totals.g5}
    if has_6 then table.insert(t_row, '| style="text-align:center; font-weight:bold;" | ' .. table_totals.g6) end
    table.insert(t_row, '| style="text-align:center; font-weight:bold;" | ' .. table_totals.tot .. ' \n| style="text-align:center; font-weight:bold;" | (' .. table_totals.gr .. '/' .. table_totals.po .. ')')
    
    table.insert(html, table.concat(t_row, " \n"))
    table.insert(html, '|}')

    return frame:preprocess(table.concat(html, "\n"))
end

return p