Модуль:PlayerAchievements: различия между версиями

Материал из ЧТМ
Перейти к навигации Перейти к поиску
Нет описания правки
Нет описания правки
 
(не показаны 23 промежуточные версии 1 участника)
Строка 1: Строка 1:
-- ==========================================
-- ==========================================
-- Модуль:PlayerAchievements
-- Модуль:PlayerAchievements
-- Автоматический генератор достижений игроков ЧТМ
-- (Pure UI Render)
-- ==========================================
-- ==========================================
local PlayerAchievements = {}
local PlayerAchievements = {}


local Config = require('Module:Config')
local Config = require('Module:Config')
local CoreEngine = require('Module:StatEngine')
local ModuleDataTeams = require('Module:Data/Teams')
local ModuleDataTeams = require('Module:Data/Teams')
local TournamentAwards = require('Module:StatEngine/TournamentAwards')
local ModuleMegarating = require('Module:Megarating') -- Подключаем чистый движок мегарейтинга
-- ==========================================
-- ГЛОБАЛЬНЫЙ КЭШ (Выполняется ровно 1 раз при загрузке страницы!)
-- ==========================================
local is_initialized = false
local GrandStats
local CustomStats = {}
local Leaders = {}
local TeamLeaders = {}
local StadiumLeaders = {}
local ShoeTeams = {}
local AssistTeams = {}


local word_map = {
local word_map = {
Строка 29: Строка 13:
     [9] = "Девятый", [10] = "Десятый", [11] = "Одиннадцатый", [12] = "Двенадцатый"
     [9] = "Девятый", [10] = "Десятый", [11] = "Одиннадцатый", [12] = "Двенадцатый"
}
}
local function format_achievement(prefix_singular, prefix_plural, items, sum_counts)
    if #items == 0 then return "" end
    table.sort(items, function(a, b) return a.year < b.year end)
    local texts = {}; local total = 0
    for _, item in ipairs(items) do
        local t = tostring(item.year)
        if item.addon and item.addon ~= "" then t = t .. " " .. item.addon end
        table.insert(texts, t); total = total + (item.count or 1)
    end
    local display_prefix = (#items > 1 or (sum_counts and total > 1)) and prefix_plural or prefix_singular
    local count_str = ""
    if sum_counts and total > 1 then count_str = " (" .. total .. ")"
    elseif not sum_counts and #items > 1 then count_str = " (" .. #items .. ")" end
    return "* " .. display_prefix .. count_str .. ": " .. table.concat(texts, ", ") .. "."
end
local function format_list_with_and(list)
    if #list == 1 then return list[1] end
    if #list == 2 then return list[1] .. " и " .. list[2] end
    local last = table.remove(list)
    return table.concat(list, ", ") .. " и " .. last
end


local function has_player(aw_data, player)
local function has_player(aw_data, player)
Строка 39: Строка 46:
end
end


local function initialize_data()
local function get_hardcoded_historical(player)
     if is_initialized then return end
     if player == "Алишер" then return "Единственный игрок, забивший гол своим первым касанием мяча в рамках ЧТМ." end
   
     if player == "Андрей О." then return "Самая продолжительная серия матчей, в которых игроку удавалось как забить гол, так и отдать голевую передачу — 5." end
    -- 1. ТЯЖЁЛАЯ АРТИЛЛЕРИЯ
    if player == "Бирюк" then return "Автор единственного в истории ЧТМ гола, забитого прямым ударом с углового." end
    GrandStats = CoreEngine.Harvester.run_all_time({
    if player == "Дима Кес." then return "Наибольшее число вратарских голевых передач за один матч — 2." end
        need_players = true,
    if player == "Диман" then return "Наибольшее число вратарских голевых передач за один матч — 2.\n* Самая продолжительная сухая серия за одну команду — 57 минут ([[Индия]])." end
        need_teams = true,
    if player == "Макс" then return "Наибольшее число [[Вратарские голы|вратарских голов]] — 6." end
        need_combos = true,  
    return nil
        keep_years = true
end
    })
   
    -- 2. ТОЧЕЧНЫЙ ПРОХОД (Для стадионов, Клуба 100 и точных наград)
     CustomStats = { club100 = {}, stadiums = {}, finals = {} }
    local global_goals = {}
 
    for _, year in ipairs(Config.years) do
        local success, year_db = pcall(require, 'Module:Data/' .. year)
        if success then
            local matches = {}
            for id, m in pairs(year_db) do table.insert(matches, {id = id, m = m}) end
            table.sort(matches, function(a, b) return (a.m.num_hist or 0) < (b.m.num_hist or 0) end)
 
            CustomStats.finals[year] = {}
 
            for _, item in ipairs(matches) do
                local match = item.m
               
                if match.goals then
                    for _, g in ipairs(match.goals) do
                        if g.scorer then
                            local p = g.scorer
                            global_goals[p] = (global_goals[p] or 0) + 1
                            if global_goals[p] == 100 then
                                table.insert(CustomStats.club100, {name = p, year = year})
                            end
 
                            if match.stadium then
                                CustomStats.stadiums[match.stadium] = CustomStats.stadiums[match.stadium] or {}
                                CustomStats.stadiums[match.stadium][p] = (CustomStats.stadiums[match.stadium][p] or 0) + 1
                            end
                        end
                    end
                end
 
                if match.stage == "Финал" then
                    local f = CustomStats.finals[year]
                    local function parse_aw(aw_data)
                        if type(aw_data) == "table" then return aw_data end
                        if type(aw_data) == "string" then return {aw_data} end
                        return {}
                    end
                    f.superchampions = parse_aw(match.superchampions)
                    f.golden_sphere = parse_aw(match.golden_sphere)
                    f.silver_sphere = parse_aw(match.silver_sphere)
                    f.bronze_sphere = parse_aw(match.bronze_sphere)
                    f.wooden_sphere = parse_aw(match.wooden_sphere)


                    f.golden_shoe = parse_aw(match.golden_shoe)
function PlayerAchievements.render(player, data, args)
                    f.silver_shoe = parse_aw(match.silver_shoe)
    if not player or player == "" then return "" end
                    f.bronze_shoe = parse_aw(match.bronze_shoe)
    if not data.Players[player] then return "" end
                    f.wooden_shoe = parse_aw(match.wooden_shoe)


                    f.golden_assistant = parse_aw(match.golden_assistant)
    args = args or {}
                    f.silver_assistant = parse_aw(match.silver_assistant)
    local show_ach_list = (args.ach_list ~= "no" and args.ach_list ~= "0")
                    f.bronze_assistant = parse_aw(match.bronze_assistant)
    local show_rec_list = (args.records_list ~= "no" and args.records_list ~= "0")
                    f.wooden_assistant = parse_aw(match.wooden_assistant)


                    f.elnur_award = parse_aw(match.elnur_award)
    local Players = data.Players
                    f.best_goal = parse_aw(match.best_goal)
    local CustomStats = data.CustomStats
                    f.mvp = match.mvp and match.mvp.player or nil
    local ShoeTeams = data.ShoeTeams
                   
    local AssistTeams = data.AssistTeams
                    f.has_shootout = (match.shootout_score1 ~= nil)
    local GlobalRecords = data.GlobalRecords
                    f.shootout_winner = nil
    local Leaders = data.Leaders
                    if match.shootout and f.has_shootout then
    local TeamLeaders = data.TeamLeaders
                        local w_team = match.shootout_score1 > match.shootout_score2 and 1 or 2
    local StadiumLeaders = data.StadiumLeaders
                        local w_score = 0
    local mr_hist = data.MegaratingHistory
                        local max_score = math.max(match.shootout_score1, match.shootout_score2)
                        for _, shot in ipairs(match.shootout) do
                            if shot.team == w_team and shot.result == "гол" then
                                w_score = w_score + 1
                                if w_score == max_score then
                                    f.shootout_winner = shot.taker
                                    break
                                end
                            end
                        end
                    end
                end
            end


            local shoes = TournamentAwards.getTournamentAwards(year, year_db, "goals")
    local function build_achievements_text()
            ShoeTeams[year] = {}
        local main = {}; local add = {}
            for _, p in ipairs(shoes) do
        local latest_year = Config.years[#Config.years]
                if p.rank <= 4 and not ShoeTeams[year][p.player] then ShoeTeams[year][p.player] = p.team_code end
        local is_latest_finished = Config.is_latest_finished
            end
        local p = Players[player]


            local assists = TournamentAwards.getTournamentAwards(year, year_db, "assists")
        local function check_and_add(list, singular, plural, condition_func, sum_counts)
             AssistTeams[year] = {}
             local items = {}
             for _, p in ipairs(assists) do
             for _, year in ipairs(Config.years) do
                 if p.rank <= 4 and not AssistTeams[year][p.player] then AssistTeams[year][p.player] = p.team_code end
                 local res = condition_func(year)
                if res then table.insert(items, res) end
             end
             end
            local str = format_achievement(singular, plural, items, sum_counts)
            if str ~= "" then table.insert(list, str) end
         end
         end
    end


    -- 3. ВЫЧИСЛЕНИЕ ЛИДЕРОВ ГОДА (Фейковый мегарейтинг отсюда выпилен)
        local function check_mr(y, target_rank)
    local function evaluate_leader(year, metric_func)
            local is_finished = (y ~= latest_year) or is_latest_finished
        local max_val = -9999
            if is_finished and mr_hist.players[player] and mr_hist.players[player].years[y] and mr_hist.players[player].years[y].rank == target_rank then return {year = y} end
        local leaders = {}
        for p, p_stats in pairs(GrandStats.Years[year].Players) do
            local val = metric_func(p_stats)
            if val and val > 0 then
                if val > max_val then
                    max_val = val; leaders = {p}
                elseif val == max_val then
                    table.insert(leaders, p)
                end
            end
         end
         end
        return leaders
    end


    local function evaluate_min_leader(year, metric_func, min_req)
        check_and_add(main, "[[Список игроков-чемпионов третьего мира|Чемпион третьего мира]]", "[[Список игроков-чемпионов третьего мира|Чемпион третьего мира]]", function(y)
        local min_val = 9999
            if Config.champions_players[y] and Config.utils.has_value(Config.champions_players[y], player) then return {year = y} end
        local leaders = {}
         end)
        for p, p_stats in pairs(GrandStats.Years[year].Players) do
         check_and_add(main, "[[Суперчемпион]]", "[[Суперчемпион]]", function(y)
            if min_req(p_stats) then
            if CustomStats.finals[y] and has_player(CustomStats.finals[y].superchampions, player) then return {year = y} end
                local val = metric_func(p_stats)
        end)
                if val then
        check_and_add(main, "Обладатель [[Золотой Шар|Золотого Шара]]", "Обладатель [[Золотой Шар|Золотого Шара]]", function(y)
                    if val < min_val then
             if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_sphere, player) then return {year = y} end
                        min_val = val; leaders = {p}
        end)
                    elseif val == min_val then
        check_and_add(main, "Обладатель [[Золотой Башмак|Золотого Башмака]]", "Обладатель [[Золотой Башмак|Золотого Башмака]]", function(y)
                        table.insert(leaders, p)
             if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end
                    end
        end)
                end
        check_and_add(main, "[[Лучший ассистент]]", "[[Лучший ассистент]]", function(y)
            end
             if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end
         end
        end)
         return leaders
        check_and_add(main, "[[Приз имени Эльнура|Лучший вратарь турнира]]", "[[Приз имени Эльнура|Лучший вратарь турнира]]", function(y)
    end
             if CustomStats.finals[y] and has_player(CustomStats.finals[y].elnur_award, player) then return {year = y} end
 
        end)
    for _, year in ipairs(Config.years) do
        if GrandStats.Years[year] then
            Leaders.goals = Leaders.goals or {}; Leaders.goals[year] = evaluate_leader(year, function(s) return s.goals.total end)
            Leaders.assists = Leaders.assists or {}; Leaders.assists[year] = evaluate_leader(year, function(s) return s.assists.total end)
             Leaders.matches = Leaders.matches or {}; Leaders.matches[year] = evaluate_leader(year, function(s) return s.matches_total end)
            Leaders.mvp = Leaders.mvp or {}; Leaders.mvp[year] = evaluate_leader(year, function(s) return s.mvp.is_mvp end)
             Leaders.mvp_goalie = Leaders.mvp_goalie or {}; Leaders.mvp_goalie[year] = evaluate_leader(year, function(s) return s.mvp.is_goalie_mvp end)
            Leaders.mega_tricks = Leaders.mega_tricks or {}; Leaders.mega_tricks[year] = evaluate_leader(year, function(s) return (s.goals.hat_trick + s.goals.poker + s.goals.penta + s.goals.hexa) end)
            Leaders.head_goals = Leaders.head_goals or {}; Leaders.head_goals[year] = evaluate_leader(year, function(s) return s.goals.head end)
             Leaders.pens_saved = Leaders.pens_saved or {}; Leaders.pens_saved[year] = evaluate_leader(year, function(s) return s.penalties.saved_as_goalie end)
            Leaders.clearances = Leaders.clearances or {}; Leaders.clearances[year] = evaluate_leader(year, function(s) return s.clearances end)
            Leaders.playoff_wins = Leaders.playoff_wins or {}; Leaders.playoff_wins[year] = evaluate_leader(year, function(s) return s.megarating.playoff_wins.r16 + s.megarating.playoff_wins.qf + s.megarating.playoff_wins.sf + (s.megarating.is_champion and 1 or 0) end)
            Leaders.plus_minus = Leaders.plus_minus or {}; Leaders.plus_minus[year] = evaluate_leader(year, function(s) return s.plus_minus end)
            Leaders.pens_scored = Leaders.pens_scored or {}; Leaders.pens_scored[year] = evaluate_leader(year, function(s) return s.penalties.in_game.g + s.penalties.shootout.g end)
              
            Leaders.avg_goals = Leaders.avg_goals or {}
            Leaders.avg_goals[year] = evaluate_leader(year, function(s)  
                if s.avg.goals_den > 0 then return s.avg.goals_num / s.avg.goals_den else return 0 end  
            end)


            Leaders.gaa = Leaders.gaa or {}
        local club_index, club_year = 0, 0
             Leaders.gaa[year] = evaluate_min_leader(year,
        for i, p_info in ipairs(CustomStats.club100) do if p_info.name == player then club_index = i; club_year = p_info.year; break end end
                function(s) return s.weighted_ga / s.matches_goalie end,
        if club_index > 0 then
                function(s) return s.matches_goalie and s.matches_goalie >= 3 end
             local w = word_map[club_index] or tostring(club_index) .. "-й"
             )
            local suffix = (club_index > 4) and " по счёту участник" or " участник"
             table.insert(main, "* " .. w .. suffix .. " [[Клуб 100|Клуба 100]]: " .. club_year .. ".")
         end
         end
    end


    -- 4. ЛУЧШИЕ БОМБАРДИРЫ КОМАНД И СТАДИОНОВ
        check_and_add(add, "Лидер [[мегарейтинг]]а", "Лидер [[мегарейтинг]]а", function(y) return check_mr(y, 1) end)
    for combo, c_data in pairs(GrandStats.PlayerTeam) do
         check_and_add(add, "Второе место в [[мегарейтинг]]е", "Второе место в [[мегарейтинг]]е", function(y) return check_mr(y, 2) end)
         local p = c_data.name
        check_and_add(add, "Третье место в [[мегарейтинг]]е", "Третье место в [[мегарейтинг]]е", function(y) return check_mr(y, 3) end)
        local t = c_data.team
        check_and_add(add, "Четвёртое место в [[мегарейтинг]]е", "Четвёртое место в [[мегарейтинг]]е", function(y) return check_mr(y, 4) end)
        local goals = c_data.goals.total
        if goals > 0 then
            TeamLeaders[t] = TeamLeaders[t] or {players = {}, goals = 0}
            if goals > TeamLeaders[t].goals then
                TeamLeaders[t].goals = goals; TeamLeaders[t].players = {p}
            elseif goals == TeamLeaders[t].goals then
                table.insert(TeamLeaders[t].players, p)
            end
        end
    end


    for st, p_list in pairs(CustomStats.stadiums) do
        check_and_add(add, "Обладатель [[Золотой Шар|Серебряного Шара]]", "Обладатель [[Золотой Шар|Серебряного Шара]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_sphere, player) then return {year = y} end end)
        local max = 0
         check_and_add(add, "Обладатель [[Золотой Шар|Бронзового Шара]]", "Обладатель [[Золотой Шар|Бронзового Шара]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_sphere, player) then return {year = y} end end)
        local best = {}
         check_and_add(add, "Обладатель [[Золотой Шар|Деревянного Шара]]", "Обладатель [[Золотой Шар|Деревянного Шара]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_sphere, player) then return {year = y} end end)
         for p, goals in pairs(p_list) do
            if goals > max then
                max = goals; best = {p}
            elseif goals == max then
                table.insert(best, p)
            end
        end
         if max > 0 then StadiumLeaders[st] = {players = best, goals = max} end
    end


    is_initialized = true
        check_and_add(add, "Обладатель [[Золотой Башмак|Серебряного Башмака]]", "Обладатель [[Золотой Башмак|Серебряного Башмака]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end end)
end
        check_and_add(add, "Обладатель [[Золотой Башмак|Бронзового Башмака]]", "Обладатель [[Золотой Башмак|Бронзового Башмака]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end end)
        check_and_add(add, "Обладатель [[Золотой Башмак|Деревянного Башмака]]", "Обладатель [[Золотой Башмак|Деревянного Башмака]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end end)


-- ==========================================
        check_and_add(add, "Второе место в списке [[Лучший ассистент|лучших ассистентов]]", "Второе место в списке [[Лучший ассистент|лучших ассистентов]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end end)
-- УТИЛИТЫ ФОРМАТИРОВАНИЯ СТРОК
         check_and_add(add, "Третье место в списке [[Лучший ассистент|лучших ассистентов]]", "Третье место в списке [[Лучший ассистент|лучших ассистентов]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end end)
-- ==========================================
        check_and_add(add, "Четвёртое место в списке [[Лучший ассистент|лучших ассистентов]]", "Четвёртое место в списке [[Лучший ассистент|лучших ассистентов]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end end)
local function format_achievement(prefix_singular, prefix_plural, items, sum_counts)
    if #items == 0 then return "" end
    table.sort(items, function(a, b) return a.year < b.year end)
   
    local texts = {}
    local total = 0
    for _, item in ipairs(items) do
        local t = tostring(item.year)
        if item.addon and item.addon ~= "" then t = t .. " " .. item.addon end
         table.insert(texts, t)
        total = total + (item.count or 1)
    end
   
    local display_prefix = (#items > 1 or (sum_counts and total > 1)) and prefix_plural or prefix_singular
    local count_str = ""
   
    if sum_counts and total > 1 then
        count_str = " (" .. total .. ")"
    elseif not sum_counts and #items > 1 then
        count_str = " (" .. #items .. ")"
    end
   
    return "* " .. display_prefix .. count_str .. ": " .. table.concat(texts, ", ") .. "."
end


local function format_list_with_and(list)
        check_and_add(add, "Автор золотого гола ЧТМ", "Автор золотых голов ЧТМ", function(y)
    if #list == 1 then return list[1] end
            local is_gold, is_shootout = false, false
    if #list == 2 then return list[1] .. " и " .. list[2] end
            if p.final_gold_goals_years and Config.utils.has_value(p.final_gold_goals_years, y) then is_gold = true end
    local last = table.remove(list)
            if CustomStats.finals[y] and CustomStats.finals[y].shootout_winner == player then is_gold = true; is_shootout = true end
    return table.concat(list, ", ") .. " и " .. last
            if is_gold then return {year = y, addon = is_shootout and "(в серии пенальти)" or "", count = 1} end
end
        end, false)
       
        check_and_add(add, "Автор гола в финале ЧТМ", "Автор голов в финалах ЧТМ", function(y)
            local f = CustomStats.finals[y]
            if f and f.goals_count and f.goals_count[player] then return {year = y, addon = (f.goals_count[player] > 1) and ("(" .. f.goals_count[player] .. ")") or "", count = f.goals_count[player]} end
        end, true)


-- ==========================================
        check_and_add(add, "Автор голевой передачи в финале ЧТМ", "Автор голевых передач в финалах ЧТМ", function(y)
-- ОСНОВНАЯ ФУНКЦИЯ СБОРКИ ИГРОКА
            local f = CustomStats.finals[y]
-- ==========================================
            if f and f.assists_count and f.assists_count[player] then return {year = y, addon = (f.assists_count[player] > 1) and ("(" .. f.assists_count[player] .. ")") or "", count = f.assists_count[player]} end
function PlayerAchievements.build_achievements(player)
        end, true)
    initialize_data()
   
    if not GrandStats.Players[player] then return "" end


    local main = {}
        check_and_add(add, "Лучший игрок финала ЧТМ", "Лучший игрок финалов ЧТМ", function(y) if CustomStats.finals[y] and CustomStats.finals[y].mvp == player then return {year = y} end end)
    local add = {}
        check_and_add(add, "Автор [[Лучший гол турнира|лучшего гола турнира]]", "Автор [[Лучший гол турнира|лучшего гола турнира]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].best_goal, player) then return {year = y} end end)
   
    -- Получаем реальную историю мегарейтинга (обращение за кэшем, O(1))
    local mr_history = ModuleMegarating.get_public_history()
    local p_hist = mr_history.players[player]


    local function check_and_add(list, singular, plural, condition_func, sum_counts)
        local function check_leader(list_arr, title_sing, title_plur, cat)
        local items = {}
            check_and_add(list_arr, title_sing, title_plur, function(y) if Leaders[cat] and Leaders[cat][y] and Config.utils.has_value(Leaders[cat][y], player) then return {year = y} end end)
        for _, year in ipairs(Config.years) do
            local res = condition_func(year)
            if res then table.insert(items, res) end
         end
         end
        local str = format_achievement(singular, plural, items, sum_counts)
        if str ~= "" then table.insert(list, str) end
    end


    -- ================= ОСНОВНЫЕ ДОСТИЖЕНИЯ =================
        check_leader(add, "Лидер по общему числу [[Голы|голов]]", "Лидер по общему числу [[Голы|голов]]", "goals")
    check_and_add(main, "[[Список игроков-чемпионов третьего мира|Чемпион третьего мира]]", "[[Список игроков-чемпионов третьего мира|Чемпион третьего мира]]", function(y)
        check_leader(add, "Лидер по общему числу [[Голевые передачи|голевых передач]]", "Лидер по общему числу [[Голевые передачи|голевых передач]]", "assists")
         if Config.champions_players[y] and Config.utils.has_value(Config.champions_players[y], player) then return {year = y} end
        check_leader(add, "Лидер по количеству признаний [[Игрок матча|игроком матча]]", "Лидер по количеству признаний [[Игрок матча|игроком матча]]", "mvp")
    end)
        check_leader(add, "Лидер по количеству признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", "Лидер по количеству признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", "mvp_goalie")
        check_leader(add, "[[Самый полезный игрок]]", "[[Самый полезный игрок]]", "plus_minus")
        check_leader(add, "Лидер по числу [[Победы в плей-офф|побед в плей-офф]]", "Лидер по числу [[Победы в плей-офф|побед в плей-офф]]", "playoff_wins")
        check_leader(add, "Лидер по [[Средняя результативность|средней результативности]]", "Лидер по [[Средняя результативность|средней результативности]]", "avg_goals")
        check_leader(add, "Лидер по общему числу [[Матчи|сыгранных матчей]]", "Лидер по общему числу [[Матчи|сыгранных матчей]]", "matches")
        check_leader(add, "[[Лучший пенальтист]]", "[[Лучший пенальтист]]", "pens_scored")
        check_leader(add, "Лидер по общему числу [[Мега-трики|мега-триков]]", "Лидер по общему числу [[Мега-трики|мега-триков]]", "mega_tricks")
        check_leader(add, "Лидер по [[Голы головой|голам головой]]", "Лидер по [[Голы головой|голам головой]]", "head_goals")
         check_leader(add, "Лидер по [[Выносы из пустых|выносам из пустых]]", "Лидер по [[Выносы из пустых|выносам из пустых]]", "clearances")
        check_leader(add, "Лидер по числу [[Отбитые пенальти|отбитых пенальти]]", "Лидер по числу [[Отбитые пенальти|отбитых пенальти]]", "pens_saved")
        check_leader(add, "Лидер по [[Коэффициент пропущенных голов|коэффициенту пропущенных голов]]", "Лидер по [[Коэффициент пропущенных голов|коэффициенту пропущенных голов]]", "gaa")


    check_and_add(main, "[[Суперчемпион]]", "[[Суперчемпион]]", function(y)
        local player_teams_best = {}
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].superchampions, player) then return {year = y} end
        for t, info in pairs(TeamLeaders) do
    end)
            if t ~= "Нейтрал" and Config.utils.has_value(info.players, player) then table.insert(player_teams_best, {team = t, goals = info.goals}) end
        end
        if #player_teams_best > 0 then
            table.sort(player_teams_best, function(a, b) return a.goals > b.goals end)
            if #player_teams_best == 1 then
                local t_name = ModuleDataTeams.getName(player_teams_best[1].team, 'gen')
                local g_word = (player_teams_best[1].goals % 10 == 1 and player_teams_best[1].goals % 100 ~= 11) and "гол" or (player_teams_best[1].goals % 10 >= 2 and player_teams_best[1].goals % 10 <= 4 and (player_teams_best[1].goals % 100 < 10 or player_teams_best[1].goals % 100 >= 20)) and "гола" or "голов"
                table.insert(add, "* [[Список голов ЧТМ по сборным|Лучший бомбардир]] в истории сборной [[" .. ModuleDataTeams.getName(player_teams_best[1].team) .. "|" .. t_name .. "]] (" .. player_teams_best[1].goals .. " " .. g_word .. ").")
            else
                local t_strs = {}
                for _, info in ipairs(player_teams_best) do table.insert(t_strs, "[[" .. ModuleDataTeams.getName(info.team) .. "]] (" .. info.goals .. ")") end
                local w_map = { [2]="двух", [3]="трёх", [4]="четырёх", [5]="пяти", [6]="шести", [7]="семи", [8]="восьми", [9]="девяти", [10]="десяти", [11]="одиннадцати" }
                local word = w_map[#player_teams_best] or tostring(#player_teams_best)
                table.insert(add, "* [[Список голов ЧТМ по сборным|Лучший бомбардир]] в истории " .. word .. " сборных: " .. table.concat(t_strs, ", ") .. ".")
            end
        end


    check_and_add(main, "Обладатель [[Золотой Шар|Золотого Шара]]", "Обладатель [[Золотой Шар|Золотого Шара]]", function(y)
        local player_stadiums_best = {}
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_sphere, player) then return {year = y} end
        for st, info in pairs(StadiumLeaders) do
    end)
            if Config.utils.has_value(info.players, player) then table.insert(player_stadiums_best, {stadium = st, goals = info.goals}) end
 
        end
    check_and_add(main, "Обладатель [[Золотой Башмак|Золотого Башмака]]", "Обладатель [[Золотой Башмак|Золотого Башмака]]", function(y)
        if #player_stadiums_best > 0 then
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_shoe, player) then
            table.sort(player_stadiums_best, function(a, b) return a.goals > b.goals end)
            local t = ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно"
            local s_strs = {}
             return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
            for _, info in ipairs(player_stadiums_best) do
                local g_word = (info.goals % 10 == 1 and info.goals % 100 ~= 11) and "гол" or (info.goals % 10 >= 2 and info.goals % 10 <= 4 and (info.goals % 100 < 10 or info.goals % 100 >= 20)) and "гола" or "голов"
                table.insert(s_strs, "«[[" .. info.stadium .. "]]» (" .. info.goals .. " " .. g_word .. ")")
             end
            if #player_stadiums_best == 1 then table.insert(add, "* Лучший бомбардир в истории стадиона " .. s_strs[1] .. ".")
            else table.insert(add, "* Лучший бомбардир в истории стадионов " .. format_list_with_and(s_strs) .. ".") end
         end
         end
    end)


    check_and_add(main, "[[Лучший ассистент]]", "[[Лучший ассистент]]", function(y)
        local output = {}
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_assistant, player) then
        if #main > 0 then table.insert(output, "; Основные:"); for _, item in ipairs(main) do table.insert(output, item) end end
             local t = AssistTeams[y] and AssistTeams[y][player] or "Неизвестно"
        if #add > 0 then
             return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
             if #main > 0 then table.insert(output, "") end
             table.insert(output, "; Дополнительные:"); for _, item in ipairs(add) do table.insert(output, item) end
         end
         end
    end)


    check_and_add(main, "[[Приз имени Эльнура|Лучший вратарь турнира]]", "[[Приз имени Эльнура|Лучший вратарь турнира]]", function(y)
        return table.concat(output, "\n")
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].elnur_award, player) then return {year = y} end
     end
     end)


     local club_index, club_year = 0, 0
     local function build_records_text()
    for i, p_info in ipairs(CustomStats.club100) do
        local p = Players[player]
        if p_info.name == player then
        local function get_val(pp, key)
             club_index = i; club_year = p_info.year; break
            if string.sub(key, 1, 12) == "max_streaks_" then return pp.max_streaks[string.sub(key, 13)] or 0 end
             if string.sub(key, 1, 5) == "wins_" then return pp.wins[string.sub(key, 6)] or 0 end
            return pp[key]
         end
         end
    end
    if club_index > 0 then
        local w = word_map[club_index] or tostring(club_index) .. "-й"
        local suffix = (club_index > 4) and " по счёту участник" or " участник"
        table.insert(main, "* " .. w .. suffix .. " [[Клуб 100|Клуба 100]]: " .. club_year .. ".")
    end


    -- ================= ДОПОЛНИТЕЛЬНЫЕ ДОСТИЖЕНИЯ =================
        local metrics_all = {
   
            {"champs", "Наибольшее число [[Список игроков-чемпионов третьего мира|чемпионских титулов]]", false}, {"champ_captain", "Наибольшее число чемпионских титулов в качестве капитана", false}, {"champ_streak", "Наибольшее число чемпионских титулов, выигранных подряд", false}, {"champ_team_streak", "Наибольшее число чемпионских титулов, выигранных подряд в составе одной команды", false}, {"superchamps", "Наибольшее число [[Суперчемпион|суперчемпионств]]", false}, {"golden_goals", "Наибольшее число золотых голов", false}, {"final_goals_real", "Наибольшее число голов в финалах", false}, {"final_assists_real", "Наибольшее число голевых передач в финалах", false}, {"final_ga_real", "Наибольшее число очков по системе «гол+пас» в финалах", false}, {"max_final_goals", "Наибольшее число голов в одном финале", false}, {"finals_played", "Наибольшее число сыгранных финалов", false}, {"goals", "Наибольшее число забитых [[Голы|голов]]", false}, {"max_goals_team", "Наибольшее число голов, забитых за одну сборную", true}, {"max_goals_match", "Наибольшее число голов, забитых за один матч", false}, {"mega_tricks", "Наибольшее число [[Мега-трики ЧТМ|мега-триков]]", false}, {"max_mega_tricks_team", "Наибольшее число мега-триков за одну сборную", true}, {"classic_hat_tricks", "Наибольшее число классических мега-триков", false}, {"pokers", "Наибольшее число покеров", false}, {"pentas", "Наибольшее число пента-триков", false}, {"hexas", "Наибольшее число гекса-триков", false}, {"pens_scored", "Наибольшее число забитых пенальти", false}, {"pens_in_game_scored", "Наибольшее число забитых пенальти в игровое время", false}, {"max_assists_match", "Наибольшее число голевых передач за один матч", false}, {"pm", "Наивысший [[показатель полезности]]", false, true}, {"pts_pct", "Наивысший [[процент набранных очков]]", false, false, true}, {"md_prizes", "Наибольшее число [[Призы игровых дней|призов игровых дней]]", false}, {"md_places", "Наибольшее число [[Призы игровых дней|призовых мест игровых дней]]", false}, {"assists", "Наибольшее число [[Голевые передачи|голевых передач]]", false}, {"avg_assists", "Наивысший показатель голевых передач в среднем за один матч", false, false, true}, {"heel_goals", "Наибольшее число [[Голы пяточкой|голов пяточкой]]", false}, {"golden_spheres", "Наибольшее количество [[Золотой Шар|Золотых Шаров]]", false}, {"total_spheres", "Наибольшее количество Шаров", false}, {"golden_shoes", "Наибольшее количество [[Золотой Башмак|Золотых Башмаков]]", false}, {"total_shoes", "Наибольшее количество Башмаков", false}, {"wins_goals", "Наибольшее количество побед в зачёте забитых голов", false}, {"wins_mega_tricks", "Наибольшее количество побед в зачёте мега-триков", false}, {"wins_pm", "Наибольшее количество побед в зачёте показателя полезности", false}, {"wins_mvp", "Наибольшее количество побед в зачёте признаний игроком матча", false}, {"wins_mvp_goalie", "Наибольшее количество побед в зачёте признаний игроком матча в качестве вратаря", false}, {"wins_head_goals", "Наибольшее количество побед в зачёте голов головой", false}, {"wins_clearances", "Наибольшее количество побед в зачёте выносов из пустых", false}, {"wins_gaa", "Наибольшее количество побед в зачёте коэффициента пропущенных голов", false}, {"max_streaks_team_scored", "Самая продолжительная серия матчей, в каждом из которых команда игрока забивала минимум один гол", false}, {"max_streaks_matches_played", "Самая продолжительная серия матчей, сыгранных подряд", false}, {"max_streaks_goals", "Самая продолжительная серия матчей с забитыми голами", false}, {"max_streaks_double", "Самая продолжительная серия матчей, в каждом из которых был оформлен минимум дубль", false}, {"max_streaks_hat_trick", "Самая продолжительная серия матчей, в каждом из которых был оформлен минимум хет-трик", false}, {"max_streaks_poker", "Самая продолжительная серия матчей, в каждом из которых был оформлен минимум покер", false}, {"max_streaks_ga", "Самая продолжительная серия матчей, в которых игроку удавалось набрать хотя бы одно очко по системе «гол+пас»", false}, {"max_streaks_missed_matches", "Наибольшее число [[Возвращения|матчей, пропущенных подряд]]", false}, {"max_gap_days", "Самая длительная [[Возвращения|пауза между матчами]]", false}, {"avg_goals", "Наивысшая [[средняя результативность]]", false, false, true}, {"gaa", "Лучший [[коэффициент пропущенных голов]]", false, false, true, true}, {"mvp", "Наибольшее число признаний [[Игрок матча|игроком матча]]", false}, {"max_team_mvp", "Наибольшее число признаний игроком матча за одну сборную", true}, {"mvp_goalie", "Наибольшее число признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", false}, {"mr_points_pct", "Наивысший процент очков [[мегарейтинг]]а", false, false, true}, {"pens_saved", "Наибольшее число отбитых пенальти", false}, {"matches", "Наибольшее число [[Матчи|сыгранных матчей]]", false}, {"field_matches", "Наибольшее число матчей в поле", false}, {"goalie_matches", "Наибольшее число матчей в воротах", false}, {"head_goals", "Наибольшее число [[Голы головой|голов головой]]", false}, {"clearances", "Наибольшее число [[Выносы из пустых|выносов из пустых]]", false}, {"max_streaks_goalie_clean", "Наибольшее число матчей подряд за одну команду в качестве вратаря без пропущенных голов", true}, {"max_streaks_team_matches", "Самая продолжительная серия матчей за одну команду", true}, {"max_streaks_unbeaten", "Самая продолжительная беспроигрышная серия", false}, {"max_streaks_win", "Самая продолжительная победная серия", false}
    -- Блок реального Мегарейтинга (Топ-4)
        }
    check_and_add(add, "Лидер [[мегарейтинг]]а", "Лидер [[мегарейтинг]]а", function(y)
 
        if p_hist and p_hist.years[y] and p_hist.years[y].rank == 1 and p_hist.years[y].is_finished then return {year = y} end
        local hist = {}
    end)
        for _, row in ipairs(metrics_all) do
    check_and_add(add, "Второе место в [[мегарейтинг]]е", "Второе место в [[мегарейтинг]]е", function(y)
            local key = row[1]; local desc = row[2]; local need_team = row[3]; local is_pm = row[4]; local is_float = row[5]; local is_min = row[6]
        if p_hist and p_hist.years[y] and p_hist.years[y].rank == 2 and p_hist.years[y].is_finished then return {year = y} end
            local p_val = get_val(p, key); local global_val = GlobalRecords.AllTime[key]
    end)
    check_and_add(add, "Третье место в [[мегарейтинг]]е", "Третье место в [[мегарейтинг]]е", function(y)
        if p_hist and p_hist.years[y] and p_hist.years[y].rank == 3 and p_hist.years[y].is_finished then return {year = y} end
    end)
    check_and_add(add, "Четвёртое место в [[мегарейтинг]]е", "Четвёртое место в [[мегарейтинг]]е", function(y)
        if p_hist and p_hist.years[y] and p_hist.years[y].rank == 4 and p_hist.years[y].is_finished then return {year = y} end
    end)


    check_and_add(add, "Обладатель [[Золотой Шар|Серебряного Шара]]", "Обладатель [[Золотой Шар|Серебряного Шара]]", function(y)
            if p_val and global_val and p_val == global_val and ((not is_min and p_val > 0) or (is_min and p_val < 9999)) then
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_sphere, player) then return {year = y} end
                local val_str = tostring(p_val)
    end)
                if key == "max_gap_days" then val_str = p.max_gap_str
    check_and_add(add, "Обладатель [[Золотой Шар|Бронзового Шара]]", "Обладатель [[Золотой Шар|Бронзового Шара]]", function(y)
                elseif is_float then val_str = string.format("%.2f", p_val):gsub("%.00$", ""):gsub("%.(%d)0$", ".%1"):gsub("%.", ",")
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_sphere, player) then return {year = y} end
                elseif is_pm then val_str = (p_val > 0 and "«+" .. p_val .. "»") or "«" .. p_val .. "»" end
    end)
               
    check_and_add(add, "Обладатель [[Золотой Шар|Деревянного Шара]]", "Обладатель [[Золотой Шар|Деревянного Шара]]", function(y)
                if key == "mr_points_pct" then val_str = val_str .. "%" end
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_sphere, player) then return {year = y} end
                if need_team then val_str = val_str .. " ([[" .. ModuleDataTeams.getName(p[key .. "_code"] or "") .. "]])" end
    end)
                if key == "final_ga_real" then val_str = val_str .. " (" .. p.final_goals_real .. "+" .. p.final_assists_real .. ")" end


    check_and_add(add, "Обладатель [[Золотой Башмак|Серебряного Башмака]]", "Обладатель [[Золотой Башмак|Серебряного Башмака]]", function(y)
                table.insert(hist, "* " .. desc .. " " .. val_str .. ".")
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_shoe, player) then
            end
            local t = ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно"
            return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
         end
         end
    end)
    check_and_add(add, "Обладатель [[Золотой Башмак|Бронзового Башмака]]", "Обладатель [[Золотой Башмак|Бронзового Башмака]]", function(y)
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_shoe, player) then
            local t = ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно"
            return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
        end
    end)
    check_and_add(add, "Обладатель [[Золотой Башмак|Деревянного Башмака]]", "Обладатель [[Золотой Башмак|Деревянного Башмака]]", function(y)
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_shoe, player) then
            local t = ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно"
            return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
        end
    end)


    check_and_add(add, "Второе место в списке [[Лучший ассистент|лучших ассистентов]]", "Второе место в списке [[Лучший ассистент|лучших ассистентов]]", function(y)
        local hc = get_hardcoded_historical(player)
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_assistant, player) then
        if hc then for line in hc:gmatch("[^\r\n]+") do table.insert(hist, "* " .. line:gsub("^%*?%s*", "")) end end
            local t = AssistTeams[y] and AssistTeams[y][player] or "Неизвестно"
 
            return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
        local metrics_tourney = {
        end
            {"goals", "Наибольшее число [[Голы|голов]]", false}, {"max_goals_team", "Наибольшее число голов за одну команду", true}, {"mega_tricks", "Наибольшее число [[Мега-трики|мега-триков]]", false}, {"pm", "Наивысший [[показатель полезности]]", false, true}, {"matches", "Наибольшее число [[Матчи|сыгранных матчей]]", false}, {"pts_pct", "Наивысший [[процент набранных очков]]", false, false, true}, {"md_prizes", "Наибольшее число [[Призы игровых дней|призов игровых дней]]", false}, {"md_places", "Наибольшее число [[Призы игровых дней|призовых мест игровых дней]]", false}, {"heel_goals", "Наибольшее число [[Голы пяточкой|голов пяточкой]]", false}, {"mvp", "Наибольшее число признаний [[Игрок матча|игроком матча]]", false}, {"max_team_mvp", "Наибольшее число признаний игроком матча за одну сборную", true}, {"playoff_wins", "Наибольшее число [[Победы в плей-офф|побед в плей-офф]]", false}, {"head_goals", "Наибольшее число [[Голы головой|голов головой]]", false}, {"clearances", "Наибольшее число [[Выносы из пустых|выносов из пустых]]", false}, {"gaa", "Лучший [[коэффициент пропущенных голов]]", false, false, true, true}, {"playoff_goals", "Наибольшее число [[Голы в плей-офф|голов в плей-офф]]", false}, {"winning_goals", "Наибольшее число [[Победные голы|победных голов]]", false}, {"avg_goals", "Наивысшая [[средняя результативность]]", false, false, true}, {"max_team_assists", "Наибольшее число [[Голевые передачи|голевых передач]] за одну команду", true}, {"mvp_goalie", "Наибольшее число признаний игроком матча в качестве вратаря", false}, {"mr_points", "Наибольшее количество очков [[мегарейтинг]]а", false, false, true}, {"assists", "Наибольшее число [[Голевые передачи|голевых передач]]", false}, {"avg_assists", "Наивысший показатель голевых передач в среднем за один матч", false, false, true}, {"ga", "Наибольшее число очков по системе «гол+пас»", false}, {"max_team_ga", "Наибольшее число очков по системе «гол+пас» за одну команду", true}
    end)
        }
    check_and_add(add, "Третье место в списке [[Лучший ассистент|лучших ассистентов]]", "Третье место в списке [[Лучший ассистент|лучших ассистентов]]", function(y)
 
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_assistant, player) then
        local tourney = {}
            local t = AssistTeams[y] and AssistTeams[y][player] or "Неизвестно"
        for _, row in ipairs(metrics_tourney) do
            return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
            local key = row[1]; local desc = row[2]; local need_team = row[3]; local is_pm = row[4]; local is_float = row[5]; local is_min = row[6]
        end
             local global_val = GlobalRecords.PerTournament[key]; local hits = {}
    end)
    check_and_add(add, "Четвёртое место в списке [[Лучший ассистент|лучших ассистентов]]", "Четвёртое место в списке [[Лучший ассистент|лучших ассистентов]]", function(y)
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_assistant, player) then
            local t = AssistTeams[y] and AssistTeams[y][player] or "Неизвестно"
             return {year = y, addon = "([[" .. ModuleDataTeams.getName(t) .. "]])"}
        end
    end)


    -- Герои финалов
            for _, t in pairs(p.tournaments) do
    check_and_add(add, "Автор золотого гола ЧТМ", "Автор золотых голов ЧТМ", function(y)
                local v = t[key]
        local ys = GrandStats.Years[y] and GrandStats.Years[y].Players[player]
                if key == "gaa" and t.goalie_matches < 5 then v = nil end
        local is_gold, is_shootout = false, false
                if (key == "avg_goals" or key == "avg_assists") and t.field_matches < 20 then v = nil end
       
                if key == "pts_pct" and t.matches < 20 then v = nil end
        if ys and ys.megarating.final_gold_goals > 0 then is_gold = true end
        if CustomStats.finals[y] and CustomStats.finals[y].shootout_winner == player then
            is_gold = true; is_shootout = true
        end
       
        if is_gold then return {year = y, addon = is_shootout and "(в серии пенальти)" or "", count = 1} end
    end, false)
   
    check_and_add(add, "Автор гола в финале ЧТМ", "Автор голов в финалах ЧТМ", function(y)
        local ys = GrandStats.Years[y] and GrandStats.Years[y].Players[player]
        if ys and ys.megarating.final_goals > 0 then
            local c = ys.megarating.final_goals
            return {year = y, addon = (c > 1) and ("(" .. c .. ")") or "", count = c}
        end
    end, true)


    check_and_add(add, "Автор голевой передачи в финале ЧТМ", "Автор голевых передач в финалах ЧТМ", function(y)
                if v and v == global_val and ((not is_min and v >= 0) or (is_min and v >= 0 and v < 9999)) then
        local ys = GrandStats.Years[y] and GrandStats.Years[y].Players[player]
                    local str = tostring(t.year)
        if ys and ys.megarating.final_assists > 0 then
                    if need_team then str = str .. ", [[" .. ModuleDataTeams.getName(t[key .. "_code"] or "") .. "]]" end
            local c = ys.megarating.final_assists
                    table.insert(hits, str)
            return {year = y, addon = (c > 1) and ("(" .. c .. ")") or "", count = c}
                end
        end
            end
    end, true)


    check_and_add(add, "Лучший игрок финала ЧТМ", "Лучший игрок финалов ЧТМ", function(y)
            if #hits > 0 then
        if CustomStats.finals[y] and CustomStats.finals[y].mvp == player then return {year = y} end
                table.sort(hits)
    end)
                local val_str = tostring(global_val)
   
                if is_float then val_str = string.format("%.2f", global_val):gsub("%.00$", ""):gsub("%.(%d)0$", ".%1"):gsub("%.", ",")
    check_and_add(add, "Автор [[Лучший гол турнира|лучшего гола турнира]]", "Автор [[Лучший гол турнира|лучшего гола турнира]]", function(y)
                elseif is_pm then val_str = (global_val > 0 and "«+" .. global_val .. "»") or "«" .. global_val .. "»" end
        if CustomStats.finals[y] and has_player(CustomStats.finals[y].best_goal, player) then return {year = y} end
    end)


    -- Остальные Лидеры
                if key == "pts_pct" then val_str = val_str .. "%" end
    local function check_leader(list_arr, title_sing, title_plur, cat)
        check_and_add(list_arr, title_sing, title_plur, function(y)
            if Leaders[cat] and Leaders[cat][y] and Config.utils.has_value(Leaders[cat][y], player) then return {year = y} end
        end)
    end


    check_leader(add, "Лидер по общему числу [[Голы|голов]]", "Лидер по общему числу [[Голы|голов]]", "goals")
                if key == "ga" then
    check_leader(add, "Лидер по общему числу [[Голевые передачи|голевых передач]]", "Лидер по общему числу [[Голевые передачи|голевых передач]]", "assists")
                    local first_t = p.tournaments[tonumber(string.match(hits[1], "%d+"))]
    check_leader(add, "Лидер по количеству признаний [[Игрок матча|игроком матча]]", "Лидер по количеству признаний [[Игрок матча|игроком матча]]", "mvp")
                    val_str = val_str .. " (" .. first_t.goals .. "+" .. first_t.assists .. ")"
    check_leader(add, "Лидер по количеству признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", "Лидер по количеству признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", "mvp_goalie")
                elseif key == "max_team_ga" then
    check_leader(add, "[[Самый полезный игрок]]", "[[Самый полезный игрок]]", "plus_minus")
                    local first_t = p.tournaments[tonumber(string.match(hits[1], "%d+"))]
    check_leader(add, "Лидер по числу [[Победы в плей-офф|побед в плей-офф]]", "Лидер по числу [[Победы в плей-офф|побед в плей-офф]]", "playoff_wins")
                    local tc = first_t.max_team_ga_code
    check_leader(add, "Лидер по [[Средняя результативность|средней результативности]]", "Лидер по [[Средняя результативность|средней результативности]]", "avg_goals")
                    val_str = val_str .. " (" .. (first_t.team_goals and first_t.team_goals[tc] or 0) .. "+" .. (first_t.team_assists and first_t.team_assists[tc] or 0) .. ")"
    check_leader(add, "Лидер по общему числу [[Матчи|сыгранных матчей]]", "Лидер по общему числу [[Матчи|сыгранных матчей]]", "matches")
                end
    check_leader(add, "[[Лучший пенальтист]]", "[[Лучший пенальтист]]", "pens_scored")
    check_leader(add, "Лидер по общему числу [[Мега-трики|мега-триков]]", "Лидер по общему числу [[Мега-трики|мега-триков]]", "mega_tricks")
    check_leader(add, "Лидер по [[Голы головой|голам головой]]", "Лидер по [[Голы головой|голам головой]]", "head_goals")
    check_leader(add, "Лидер по [[Выносы из пустых|выносам из пустых]]", "Лидер по [[Выносы из пустых|выносам из пустых]]", "clearances")
    check_leader(add, "Лидер по числу [[Отбитые пенальти|отбитых пенальти]]", "Лидер по числу [[Отбитые пенальти|отбитых пенальти]]", "pens_saved")
    check_leader(add, "Лидер по [[Коэффициент пропущенных голов|коэффициенту пропущенных голов]]", "Лидер по [[Коэффициент пропущенных голов|коэффициенту пропущенных голов]]", "gaa")


    -- Лучшие бомбардиры сборных
                table.insert(tourney, "* " .. desc .. " " .. val_str .. " (" .. table.concat(hits, "; ") .. ").")
    local player_teams_best = {}
    for t, info in pairs(TeamLeaders) do
        if Config.utils.has_value(info.players, player) then
            table.insert(player_teams_best, {team = t, goals = info.goals})
        end
    end
    if #player_teams_best > 0 then
        table.sort(player_teams_best, function(a, b) return a.goals > b.goals end)
        if #player_teams_best == 1 then
            local t_name = ModuleDataTeams.getName(player_teams_best[1].team, 'gen')
            local g_word = (player_teams_best[1].goals % 10 == 1 and player_teams_best[1].goals % 100 ~= 11) and "гол" or (player_teams_best[1].goals % 10 >= 2 and player_teams_best[1].goals % 10 <= 4 and (player_teams_best[1].goals % 100 < 10 or player_teams_best[1].goals % 100 >= 20)) and "гола" or "голов"
            table.insert(add, "* [[Список голов ЧТМ по сборным|Лучший бомбардир]] в истории сборной [[" .. ModuleDataTeams.getName(player_teams_best[1].team) .. "|" .. t_name .. "]] (" .. player_teams_best[1].goals .. " " .. g_word .. ").")
        else
            local t_strs = {}
            for _, info in ipairs(player_teams_best) do
                table.insert(t_strs, "[[" .. ModuleDataTeams.getName(info.team) .. "]] (" .. info.goals .. ")")
             end
             end
            local w_map = { [2]="двух", [3]="трёх", [4]="четырёх", [5]="пяти", [6]="шести", [7]="семи", [8]="восьми", [9]="девяти", [10]="десяти", [11]="одиннадцати" }
            local word = w_map[#player_teams_best] or tostring(#player_teams_best)
            table.insert(add, "* [[Список голов ЧТМ по сборным|Лучший бомбардир]] в истории " .. word .. " сборных: " .. table.concat(t_strs, ", ") .. ".")
         end
         end
    end


    -- Лучшие бомбардиры стадионов
        local output = {}
    local player_stadiums_best = {}
         if #hist > 0 then table.insert(output, "; Исторические:"); for _, h in ipairs(hist) do table.insert(output, h) end end
    for st, info in pairs(StadiumLeaders) do
         if #tourney > 0 then
         if Config.utils.has_value(info.players, player) then
             if #hist > 0 then table.insert(output, "") end
            table.insert(player_stadiums_best, {stadium = st, goals = info.goals})
             table.insert(output, "; За один турнир:"); for _, t in ipairs(tourney) do table.insert(output, t) end
        end
    end
    if #player_stadiums_best > 0 then
        table.sort(player_stadiums_best, function(a, b) return a.goals > b.goals end)
        local s_strs = {}
        for _, info in ipairs(player_stadiums_best) do
            local g_word = (info.goals % 10 == 1 and info.goals % 100 ~= 11) and "гол" or (info.goals % 10 >= 2 and info.goals % 10 <= 4 and (info.goals % 100 < 10 or info.goals % 100 >= 20)) and "гола" or "голов"
            table.insert(s_strs, "«[[" .. info.stadium .. "]]» (" .. info.goals .. " " .. g_word .. ")")
        end
         if #player_stadiums_best == 1 then
             table.insert(add, "* Лучший бомбардир в истории стадиона " .. s_strs[1] .. ".")
        else
             table.insert(add, "* Лучший бомбардир в истории стадионов " .. format_list_with_and(s_strs) .. ".")
         end
         end
        return table.concat(output, "\n")
     end
     end


    -- ================= СБОРКА ИТОГОВОГО ТЕКСТА =================
    -- Выпилены nowiki, теперь это чистый аккуратный вики-формат
     local output = {}
     local output = {}
   
     if show_ach_list then
     if #main > 0 then
         local ach_text = build_achievements_text()
         table.insert(output, "; Основные:")
         if ach_text and ach_text ~= "" then table.insert(output, "== Достижения ==\n" .. ach_text) end
         for _, item in ipairs(main) do table.insert(output, item) end
     end
     end
   
 
     if #add > 0 then
     if show_rec_list then
         if #main > 0 then table.insert(output, "") end
        local rec_text = build_records_text()
        table.insert(output, "; Дополнительные:")
         if rec_text and rec_text ~= "" then
         for _, item in ipairs(add) do table.insert(output, item) end
            if #output > 0 then table.insert(output, "") end
            table.insert(output, "== Рекорды ==\n" .. rec_text)
         end
     end
     end


     return table.concat(output, "\n")
     return table.concat(output, "\n")
end
-- ==========================================
-- ТОЧКА ВХОДА ИЗ WIKI
-- ==========================================
function PlayerAchievements.main(frame)
    local player = frame.args[1] or frame.args["игрок"] or ""
    player = mw.text.trim(player)
    if player == "" then return "''Не указан игрок''" end
   
    local text = PlayerAchievements.build_achievements(player)
    if text == "" then return "''Нет зафиксированных достижений.''" end
    return text
end
end


return PlayerAchievements
return PlayerAchievements

Текущая версия от 20:06, 4 июня 2026

Документация Документация

debug


Пожалуйста, добавляйте категории на страницу документации.

-- ==========================================
-- Модуль:PlayerAchievements
-- (Pure UI Render)
-- ==========================================
local PlayerAchievements = {}

local Config = require('Module:Config')
local ModuleDataTeams = require('Module:Data/Teams')

local word_map = {
    [1] = "Первый", [2] = "Второй", [3] = "Третий", [4] = "Четвёртый",
    [5] = "Пятый", [6] = "Шестой", [7] = "Седьмой", [8] = "Восьмой",
    [9] = "Девятый", [10] = "Десятый", [11] = "Одиннадцатый", [12] = "Двенадцатый"
}

local function format_achievement(prefix_singular, prefix_plural, items, sum_counts)
    if #items == 0 then return "" end
    table.sort(items, function(a, b) return a.year < b.year end)
    local texts = {}; local total = 0
    for _, item in ipairs(items) do
        local t = tostring(item.year)
        if item.addon and item.addon ~= "" then t = t .. " " .. item.addon end
        table.insert(texts, t); total = total + (item.count or 1)
    end
    local display_prefix = (#items > 1 or (sum_counts and total > 1)) and prefix_plural or prefix_singular
    local count_str = ""
    if sum_counts and total > 1 then count_str = " (" .. total .. ")"
    elseif not sum_counts and #items > 1 then count_str = " (" .. #items .. ")" end
    return "* " .. display_prefix .. count_str .. ": " .. table.concat(texts, ", ") .. "."
end

local function format_list_with_and(list)
    if #list == 1 then return list[1] end
    if #list == 2 then return list[1] .. " и " .. list[2] end
    local last = table.remove(list)
    return table.concat(list, ", ") .. " и " .. last
end

local function has_player(aw_data, player)
    if type(aw_data) == "table" then
        for _, p in ipairs(aw_data) do if p == player then return true end end
    elseif type(aw_data) == "string" then
        return aw_data == player
    end
    return false
end

local function get_hardcoded_historical(player)
    if player == "Алишер" then return "Единственный игрок, забивший гол своим первым касанием мяча в рамках ЧТМ." end
    if player == "Андрей О." then return "Самая продолжительная серия матчей, в которых игроку удавалось как забить гол, так и отдать голевую передачу — 5." end
    if player == "Бирюк" then return "Автор единственного в истории ЧТМ гола, забитого прямым ударом с углового." end
    if player == "Дима Кес." then return "Наибольшее число вратарских голевых передач за один матч — 2." end
    if player == "Диман" then return "Наибольшее число вратарских голевых передач за один матч — 2.\n* Самая продолжительная сухая серия за одну команду — 57 минут ([[Индия]])." end
    if player == "Макс" then return "Наибольшее число [[Вратарские голы|вратарских голов]] — 6." end
    return nil
end

function PlayerAchievements.render(player, data, args)
    if not player or player == "" then return "" end
    if not data.Players[player] then return "" end

    args = args or {}
    local show_ach_list = (args.ach_list ~= "no" and args.ach_list ~= "0")
    local show_rec_list = (args.records_list ~= "no" and args.records_list ~= "0")

    local Players = data.Players
    local CustomStats = data.CustomStats
    local ShoeTeams = data.ShoeTeams
    local AssistTeams = data.AssistTeams
    local GlobalRecords = data.GlobalRecords
    local Leaders = data.Leaders
    local TeamLeaders = data.TeamLeaders
    local StadiumLeaders = data.StadiumLeaders
    local mr_hist = data.MegaratingHistory

    local function build_achievements_text()
        local main = {}; local add = {}
        local latest_year = Config.years[#Config.years]
        local is_latest_finished = Config.is_latest_finished
        local p = Players[player]

        local function check_and_add(list, singular, plural, condition_func, sum_counts)
            local items = {}
            for _, year in ipairs(Config.years) do
                local res = condition_func(year)
                if res then table.insert(items, res) end
            end
            local str = format_achievement(singular, plural, items, sum_counts)
            if str ~= "" then table.insert(list, str) end
        end

        local function check_mr(y, target_rank)
            local is_finished = (y ~= latest_year) or is_latest_finished
            if is_finished and mr_hist.players[player] and mr_hist.players[player].years[y] and mr_hist.players[player].years[y].rank == target_rank then return {year = y} end
        end

        check_and_add(main, "[[Список игроков-чемпионов третьего мира|Чемпион третьего мира]]", "[[Список игроков-чемпионов третьего мира|Чемпион третьего мира]]", function(y)
            if Config.champions_players[y] and Config.utils.has_value(Config.champions_players[y], player) then return {year = y} end
        end)
        check_and_add(main, "[[Суперчемпион]]", "[[Суперчемпион]]", function(y)
            if CustomStats.finals[y] and has_player(CustomStats.finals[y].superchampions, player) then return {year = y} end
        end)
        check_and_add(main, "Обладатель [[Золотой Шар|Золотого Шара]]", "Обладатель [[Золотой Шар|Золотого Шара]]", function(y)
            if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_sphere, player) then return {year = y} end
        end)
        check_and_add(main, "Обладатель [[Золотой Башмак|Золотого Башмака]]", "Обладатель [[Золотой Башмак|Золотого Башмака]]", function(y)
            if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end
        end)
        check_and_add(main, "[[Лучший ассистент]]", "[[Лучший ассистент]]", function(y)
            if CustomStats.finals[y] and has_player(CustomStats.finals[y].golden_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end
        end)
        check_and_add(main, "[[Приз имени Эльнура|Лучший вратарь турнира]]", "[[Приз имени Эльнура|Лучший вратарь турнира]]", function(y)
            if CustomStats.finals[y] and has_player(CustomStats.finals[y].elnur_award, player) then return {year = y} end
        end)

        local club_index, club_year = 0, 0
        for i, p_info in ipairs(CustomStats.club100) do if p_info.name == player then club_index = i; club_year = p_info.year; break end end
        if club_index > 0 then
            local w = word_map[club_index] or tostring(club_index) .. "-й"
            local suffix = (club_index > 4) and " по счёту участник" or " участник"
            table.insert(main, "* " .. w .. suffix .. " [[Клуб 100|Клуба 100]]: " .. club_year .. ".")
        end

        check_and_add(add, "Лидер [[мегарейтинг]]а", "Лидер [[мегарейтинг]]а", function(y) return check_mr(y, 1) end)
        check_and_add(add, "Второе место в [[мегарейтинг]]е", "Второе место в [[мегарейтинг]]е", function(y) return check_mr(y, 2) end)
        check_and_add(add, "Третье место в [[мегарейтинг]]е", "Третье место в [[мегарейтинг]]е", function(y) return check_mr(y, 3) end)
        check_and_add(add, "Четвёртое место в [[мегарейтинг]]е", "Четвёртое место в [[мегарейтинг]]е", function(y) return check_mr(y, 4) end)

        check_and_add(add, "Обладатель [[Золотой Шар|Серебряного Шара]]", "Обладатель [[Золотой Шар|Серебряного Шара]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_sphere, player) then return {year = y} end end)
        check_and_add(add, "Обладатель [[Золотой Шар|Бронзового Шара]]", "Обладатель [[Золотой Шар|Бронзового Шара]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_sphere, player) then return {year = y} end end)
        check_and_add(add, "Обладатель [[Золотой Шар|Деревянного Шара]]", "Обладатель [[Золотой Шар|Деревянного Шара]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_sphere, player) then return {year = y} end end)

        check_and_add(add, "Обладатель [[Золотой Башмак|Серебряного Башмака]]", "Обладатель [[Золотой Башмак|Серебряного Башмака]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end end)
        check_and_add(add, "Обладатель [[Золотой Башмак|Бронзового Башмака]]", "Обладатель [[Золотой Башмак|Бронзового Башмака]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end end)
        check_and_add(add, "Обладатель [[Золотой Башмак|Деревянного Башмака]]", "Обладатель [[Золотой Башмак|Деревянного Башмака]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_shoe, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(ShoeTeams[y] and ShoeTeams[y][player] or "Неизвестно") .. "]])"} end end)

        check_and_add(add, "Второе место в списке [[Лучший ассистент|лучших ассистентов]]", "Второе место в списке [[Лучший ассистент|лучших ассистентов]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].silver_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end end)
        check_and_add(add, "Третье место в списке [[Лучший ассистент|лучших ассистентов]]", "Третье место в списке [[Лучший ассистент|лучших ассистентов]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].bronze_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end end)
        check_and_add(add, "Четвёртое место в списке [[Лучший ассистент|лучших ассистентов]]", "Четвёртое место в списке [[Лучший ассистент|лучших ассистентов]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].wooden_assistant, player) then return {year = y, addon = "([[" .. ModuleDataTeams.getName(AssistTeams[y] and AssistTeams[y][player] or "Неизвестно") .. "]])"} end end)

        check_and_add(add, "Автор золотого гола ЧТМ", "Автор золотых голов ЧТМ", function(y)
            local is_gold, is_shootout = false, false
            if p.final_gold_goals_years and Config.utils.has_value(p.final_gold_goals_years, y) then is_gold = true end
            if CustomStats.finals[y] and CustomStats.finals[y].shootout_winner == player then is_gold = true; is_shootout = true end
            if is_gold then return {year = y, addon = is_shootout and "(в серии пенальти)" or "", count = 1} end
        end, false)
        
        check_and_add(add, "Автор гола в финале ЧТМ", "Автор голов в финалах ЧТМ", function(y)
            local f = CustomStats.finals[y]
            if f and f.goals_count and f.goals_count[player] then return {year = y, addon = (f.goals_count[player] > 1) and ("(" .. f.goals_count[player] .. ")") or "", count = f.goals_count[player]} end
        end, true)

        check_and_add(add, "Автор голевой передачи в финале ЧТМ", "Автор голевых передач в финалах ЧТМ", function(y)
            local f = CustomStats.finals[y]
            if f and f.assists_count and f.assists_count[player] then return {year = y, addon = (f.assists_count[player] > 1) and ("(" .. f.assists_count[player] .. ")") or "", count = f.assists_count[player]} end
        end, true)

        check_and_add(add, "Лучший игрок финала ЧТМ", "Лучший игрок финалов ЧТМ", function(y) if CustomStats.finals[y] and CustomStats.finals[y].mvp == player then return {year = y} end end)
        check_and_add(add, "Автор [[Лучший гол турнира|лучшего гола турнира]]", "Автор [[Лучший гол турнира|лучшего гола турнира]]", function(y) if CustomStats.finals[y] and has_player(CustomStats.finals[y].best_goal, player) then return {year = y} end end)

        local function check_leader(list_arr, title_sing, title_plur, cat)
            check_and_add(list_arr, title_sing, title_plur, function(y) if Leaders[cat] and Leaders[cat][y] and Config.utils.has_value(Leaders[cat][y], player) then return {year = y} end end)
        end

        check_leader(add, "Лидер по общему числу [[Голы|голов]]", "Лидер по общему числу [[Голы|голов]]", "goals")
        check_leader(add, "Лидер по общему числу [[Голевые передачи|голевых передач]]", "Лидер по общему числу [[Голевые передачи|голевых передач]]", "assists")
        check_leader(add, "Лидер по количеству признаний [[Игрок матча|игроком матча]]", "Лидер по количеству признаний [[Игрок матча|игроком матча]]", "mvp")
        check_leader(add, "Лидер по количеству признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", "Лидер по количеству признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", "mvp_goalie")
        check_leader(add, "[[Самый полезный игрок]]", "[[Самый полезный игрок]]", "plus_minus")
        check_leader(add, "Лидер по числу [[Победы в плей-офф|побед в плей-офф]]", "Лидер по числу [[Победы в плей-офф|побед в плей-офф]]", "playoff_wins")
        check_leader(add, "Лидер по [[Средняя результативность|средней результативности]]", "Лидер по [[Средняя результативность|средней результативности]]", "avg_goals")
        check_leader(add, "Лидер по общему числу [[Матчи|сыгранных матчей]]", "Лидер по общему числу [[Матчи|сыгранных матчей]]", "matches")
        check_leader(add, "[[Лучший пенальтист]]", "[[Лучший пенальтист]]", "pens_scored")
        check_leader(add, "Лидер по общему числу [[Мега-трики|мега-триков]]", "Лидер по общему числу [[Мега-трики|мега-триков]]", "mega_tricks")
        check_leader(add, "Лидер по [[Голы головой|голам головой]]", "Лидер по [[Голы головой|голам головой]]", "head_goals")
        check_leader(add, "Лидер по [[Выносы из пустых|выносам из пустых]]", "Лидер по [[Выносы из пустых|выносам из пустых]]", "clearances")
        check_leader(add, "Лидер по числу [[Отбитые пенальти|отбитых пенальти]]", "Лидер по числу [[Отбитые пенальти|отбитых пенальти]]", "pens_saved")
        check_leader(add, "Лидер по [[Коэффициент пропущенных голов|коэффициенту пропущенных голов]]", "Лидер по [[Коэффициент пропущенных голов|коэффициенту пропущенных голов]]", "gaa")

        local player_teams_best = {}
        for t, info in pairs(TeamLeaders) do
            if t ~= "Нейтрал" and Config.utils.has_value(info.players, player) then table.insert(player_teams_best, {team = t, goals = info.goals}) end
        end
        if #player_teams_best > 0 then
            table.sort(player_teams_best, function(a, b) return a.goals > b.goals end)
            if #player_teams_best == 1 then
                local t_name = ModuleDataTeams.getName(player_teams_best[1].team, 'gen')
                local g_word = (player_teams_best[1].goals % 10 == 1 and player_teams_best[1].goals % 100 ~= 11) and "гол" or (player_teams_best[1].goals % 10 >= 2 and player_teams_best[1].goals % 10 <= 4 and (player_teams_best[1].goals % 100 < 10 or player_teams_best[1].goals % 100 >= 20)) and "гола" or "голов"
                table.insert(add, "* [[Список голов ЧТМ по сборным|Лучший бомбардир]] в истории сборной [[" .. ModuleDataTeams.getName(player_teams_best[1].team) .. "|" .. t_name .. "]] (" .. player_teams_best[1].goals .. " " .. g_word .. ").")
            else
                local t_strs = {}
                for _, info in ipairs(player_teams_best) do table.insert(t_strs, "[[" .. ModuleDataTeams.getName(info.team) .. "]] (" .. info.goals .. ")") end
                local w_map = { [2]="двух", [3]="трёх", [4]="четырёх", [5]="пяти", [6]="шести", [7]="семи", [8]="восьми", [9]="девяти", [10]="десяти", [11]="одиннадцати" }
                local word = w_map[#player_teams_best] or tostring(#player_teams_best)
                table.insert(add, "* [[Список голов ЧТМ по сборным|Лучший бомбардир]] в истории " .. word .. " сборных: " .. table.concat(t_strs, ", ") .. ".")
            end
        end

        local player_stadiums_best = {}
        for st, info in pairs(StadiumLeaders) do
            if Config.utils.has_value(info.players, player) then table.insert(player_stadiums_best, {stadium = st, goals = info.goals}) end
        end
        if #player_stadiums_best > 0 then
            table.sort(player_stadiums_best, function(a, b) return a.goals > b.goals end)
            local s_strs = {}
            for _, info in ipairs(player_stadiums_best) do
                local g_word = (info.goals % 10 == 1 and info.goals % 100 ~= 11) and "гол" or (info.goals % 10 >= 2 and info.goals % 10 <= 4 and (info.goals % 100 < 10 or info.goals % 100 >= 20)) and "гола" or "голов"
                table.insert(s_strs, "«[[" .. info.stadium .. "]]» (" .. info.goals .. " " .. g_word .. ")")
            end
            if #player_stadiums_best == 1 then table.insert(add, "* Лучший бомбардир в истории стадиона " .. s_strs[1] .. ".")
            else table.insert(add, "* Лучший бомбардир в истории стадионов " .. format_list_with_and(s_strs) .. ".") end
        end

        local output = {}
        if #main > 0 then table.insert(output, "; Основные:"); for _, item in ipairs(main) do table.insert(output, item) end end
        if #add > 0 then
            if #main > 0 then table.insert(output, "") end
            table.insert(output, "; Дополнительные:"); for _, item in ipairs(add) do table.insert(output, item) end
        end

        return table.concat(output, "\n")
    end

    local function build_records_text()
        local p = Players[player]
        local function get_val(pp, key)
            if string.sub(key, 1, 12) == "max_streaks_" then return pp.max_streaks[string.sub(key, 13)] or 0 end
            if string.sub(key, 1, 5) == "wins_" then return pp.wins[string.sub(key, 6)] or 0 end
            return pp[key]
        end

        local metrics_all = {
            {"champs", "Наибольшее число [[Список игроков-чемпионов третьего мира|чемпионских титулов]]", false}, {"champ_captain", "Наибольшее число чемпионских титулов в качестве капитана", false}, {"champ_streak", "Наибольшее число чемпионских титулов, выигранных подряд", false}, {"champ_team_streak", "Наибольшее число чемпионских титулов, выигранных подряд в составе одной команды", false}, {"superchamps", "Наибольшее число [[Суперчемпион|суперчемпионств]]", false}, {"golden_goals", "Наибольшее число золотых голов", false}, {"final_goals_real", "Наибольшее число голов в финалах", false}, {"final_assists_real", "Наибольшее число голевых передач в финалах", false}, {"final_ga_real", "Наибольшее число очков по системе «гол+пас» в финалах", false}, {"max_final_goals", "Наибольшее число голов в одном финале", false}, {"finals_played", "Наибольшее число сыгранных финалов", false}, {"goals", "Наибольшее число забитых [[Голы|голов]]", false}, {"max_goals_team", "Наибольшее число голов, забитых за одну сборную", true}, {"max_goals_match", "Наибольшее число голов, забитых за один матч", false}, {"mega_tricks", "Наибольшее число [[Мега-трики ЧТМ|мега-триков]]", false}, {"max_mega_tricks_team", "Наибольшее число мега-триков за одну сборную", true}, {"classic_hat_tricks", "Наибольшее число классических мега-триков", false}, {"pokers", "Наибольшее число покеров", false}, {"pentas", "Наибольшее число пента-триков", false}, {"hexas", "Наибольшее число гекса-триков", false}, {"pens_scored", "Наибольшее число забитых пенальти", false}, {"pens_in_game_scored", "Наибольшее число забитых пенальти в игровое время", false}, {"max_assists_match", "Наибольшее число голевых передач за один матч", false}, {"pm", "Наивысший [[показатель полезности]]", false, true}, {"pts_pct", "Наивысший [[процент набранных очков]]", false, false, true}, {"md_prizes", "Наибольшее число [[Призы игровых дней|призов игровых дней]]", false}, {"md_places", "Наибольшее число [[Призы игровых дней|призовых мест игровых дней]]", false}, {"assists", "Наибольшее число [[Голевые передачи|голевых передач]]", false}, {"avg_assists", "Наивысший показатель голевых передач в среднем за один матч", false, false, true}, {"heel_goals", "Наибольшее число [[Голы пяточкой|голов пяточкой]]", false}, {"golden_spheres", "Наибольшее количество [[Золотой Шар|Золотых Шаров]]", false}, {"total_spheres", "Наибольшее количество Шаров", false}, {"golden_shoes", "Наибольшее количество [[Золотой Башмак|Золотых Башмаков]]", false}, {"total_shoes", "Наибольшее количество Башмаков", false}, {"wins_goals", "Наибольшее количество побед в зачёте забитых голов", false}, {"wins_mega_tricks", "Наибольшее количество побед в зачёте мега-триков", false}, {"wins_pm", "Наибольшее количество побед в зачёте показателя полезности", false}, {"wins_mvp", "Наибольшее количество побед в зачёте признаний игроком матча", false}, {"wins_mvp_goalie", "Наибольшее количество побед в зачёте признаний игроком матча в качестве вратаря", false}, {"wins_head_goals", "Наибольшее количество побед в зачёте голов головой", false}, {"wins_clearances", "Наибольшее количество побед в зачёте выносов из пустых", false}, {"wins_gaa", "Наибольшее количество побед в зачёте коэффициента пропущенных голов", false}, {"max_streaks_team_scored", "Самая продолжительная серия матчей, в каждом из которых команда игрока забивала минимум один гол", false}, {"max_streaks_matches_played", "Самая продолжительная серия матчей, сыгранных подряд", false}, {"max_streaks_goals", "Самая продолжительная серия матчей с забитыми голами", false}, {"max_streaks_double", "Самая продолжительная серия матчей, в каждом из которых был оформлен минимум дубль", false}, {"max_streaks_hat_trick", "Самая продолжительная серия матчей, в каждом из которых был оформлен минимум хет-трик", false}, {"max_streaks_poker", "Самая продолжительная серия матчей, в каждом из которых был оформлен минимум покер", false}, {"max_streaks_ga", "Самая продолжительная серия матчей, в которых игроку удавалось набрать хотя бы одно очко по системе «гол+пас»", false}, {"max_streaks_missed_matches", "Наибольшее число [[Возвращения|матчей, пропущенных подряд]]", false}, {"max_gap_days", "Самая длительная [[Возвращения|пауза между матчами]]", false}, {"avg_goals", "Наивысшая [[средняя результативность]]", false, false, true}, {"gaa", "Лучший [[коэффициент пропущенных голов]]", false, false, true, true}, {"mvp", "Наибольшее число признаний [[Игрок матча|игроком матча]]", false}, {"max_team_mvp", "Наибольшее число признаний игроком матча за одну сборную", true}, {"mvp_goalie", "Наибольшее число признаний [[Игрок матча как вратарь|игроком матча в качестве вратаря]]", false}, {"mr_points_pct", "Наивысший процент очков [[мегарейтинг]]а", false, false, true}, {"pens_saved", "Наибольшее число отбитых пенальти", false}, {"matches", "Наибольшее число [[Матчи|сыгранных матчей]]", false}, {"field_matches", "Наибольшее число матчей в поле", false}, {"goalie_matches", "Наибольшее число матчей в воротах", false}, {"head_goals", "Наибольшее число [[Голы головой|голов головой]]", false}, {"clearances", "Наибольшее число [[Выносы из пустых|выносов из пустых]]", false}, {"max_streaks_goalie_clean", "Наибольшее число матчей подряд за одну команду в качестве вратаря без пропущенных голов", true}, {"max_streaks_team_matches", "Самая продолжительная серия матчей за одну команду", true}, {"max_streaks_unbeaten", "Самая продолжительная беспроигрышная серия", false}, {"max_streaks_win", "Самая продолжительная победная серия", false}
        }

        local hist = {}
        for _, row in ipairs(metrics_all) do
            local key = row[1]; local desc = row[2]; local need_team = row[3]; local is_pm = row[4]; local is_float = row[5]; local is_min = row[6]
            local p_val = get_val(p, key); local global_val = GlobalRecords.AllTime[key]

            if p_val and global_val and p_val == global_val and ((not is_min and p_val > 0) or (is_min and p_val < 9999)) then
                local val_str = tostring(p_val)
                if key == "max_gap_days" then val_str = p.max_gap_str
                elseif is_float then val_str = string.format("%.2f", p_val):gsub("%.00$", ""):gsub("%.(%d)0$", ".%1"):gsub("%.", ",")
                elseif is_pm then val_str = (p_val > 0 and "«+" .. p_val .. "»") or "«" .. p_val .. "»" end
                
                if key == "mr_points_pct" then val_str = val_str .. "%" end
                if need_team then val_str = val_str .. " ([[" .. ModuleDataTeams.getName(p[key .. "_code"] or "") .. "]])" end
                if key == "final_ga_real" then val_str = val_str .. " (" .. p.final_goals_real .. "+" .. p.final_assists_real .. ")" end

                table.insert(hist, "* " .. desc .. " — " .. val_str .. ".")
            end
        end

        local hc = get_hardcoded_historical(player)
        if hc then for line in hc:gmatch("[^\r\n]+") do table.insert(hist, "* " .. line:gsub("^%*?%s*", "")) end end

        local metrics_tourney = {
            {"goals", "Наибольшее число [[Голы|голов]]", false}, {"max_goals_team", "Наибольшее число голов за одну команду", true}, {"mega_tricks", "Наибольшее число [[Мега-трики|мега-триков]]", false}, {"pm", "Наивысший [[показатель полезности]]", false, true}, {"matches", "Наибольшее число [[Матчи|сыгранных матчей]]", false}, {"pts_pct", "Наивысший [[процент набранных очков]]", false, false, true}, {"md_prizes", "Наибольшее число [[Призы игровых дней|призов игровых дней]]", false}, {"md_places", "Наибольшее число [[Призы игровых дней|призовых мест игровых дней]]", false}, {"heel_goals", "Наибольшее число [[Голы пяточкой|голов пяточкой]]", false}, {"mvp", "Наибольшее число признаний [[Игрок матча|игроком матча]]", false}, {"max_team_mvp", "Наибольшее число признаний игроком матча за одну сборную", true}, {"playoff_wins", "Наибольшее число [[Победы в плей-офф|побед в плей-офф]]", false}, {"head_goals", "Наибольшее число [[Голы головой|голов головой]]", false}, {"clearances", "Наибольшее число [[Выносы из пустых|выносов из пустых]]", false}, {"gaa", "Лучший [[коэффициент пропущенных голов]]", false, false, true, true}, {"playoff_goals", "Наибольшее число [[Голы в плей-офф|голов в плей-офф]]", false}, {"winning_goals", "Наибольшее число [[Победные голы|победных голов]]", false}, {"avg_goals", "Наивысшая [[средняя результативность]]", false, false, true}, {"max_team_assists", "Наибольшее число [[Голевые передачи|голевых передач]] за одну команду", true}, {"mvp_goalie", "Наибольшее число признаний игроком матча в качестве вратаря", false}, {"mr_points", "Наибольшее количество очков [[мегарейтинг]]а", false, false, true}, {"assists", "Наибольшее число [[Голевые передачи|голевых передач]]", false}, {"avg_assists", "Наивысший показатель голевых передач в среднем за один матч", false, false, true}, {"ga", "Наибольшее число очков по системе «гол+пас»", false}, {"max_team_ga", "Наибольшее число очков по системе «гол+пас» за одну команду", true}
        }

        local tourney = {}
        for _, row in ipairs(metrics_tourney) do
            local key = row[1]; local desc = row[2]; local need_team = row[3]; local is_pm = row[4]; local is_float = row[5]; local is_min = row[6]
            local global_val = GlobalRecords.PerTournament[key]; local hits = {}

            for _, t in pairs(p.tournaments) do
                local v = t[key]
                if key == "gaa" and t.goalie_matches < 5 then v = nil end
                if (key == "avg_goals" or key == "avg_assists") and t.field_matches < 20 then v = nil end
                if key == "pts_pct" and t.matches < 20 then v = nil end

                if v and v == global_val and ((not is_min and v >= 0) or (is_min and v >= 0 and v < 9999)) then
                    local str = tostring(t.year)
                    if need_team then str = str .. ", [[" .. ModuleDataTeams.getName(t[key .. "_code"] or "") .. "]]" end
                    table.insert(hits, str)
                end
            end

            if #hits > 0 then
                table.sort(hits)
                local val_str = tostring(global_val)
                if is_float then val_str = string.format("%.2f", global_val):gsub("%.00$", ""):gsub("%.(%d)0$", ".%1"):gsub("%.", ",")
                elseif is_pm then val_str = (global_val > 0 and "«+" .. global_val .. "»") or "«" .. global_val .. "»" end

                if key == "pts_pct" then val_str = val_str .. "%" end

                if key == "ga" then
                    local first_t = p.tournaments[tonumber(string.match(hits[1], "%d+"))]
                    val_str = val_str .. " (" .. first_t.goals .. "+" .. first_t.assists .. ")"
                elseif key == "max_team_ga" then
                    local first_t = p.tournaments[tonumber(string.match(hits[1], "%d+"))]
                    local tc = first_t.max_team_ga_code
                    val_str = val_str .. " (" .. (first_t.team_goals and first_t.team_goals[tc] or 0) .. "+" .. (first_t.team_assists and first_t.team_assists[tc] or 0) .. ")"
                end

                table.insert(tourney, "* " .. desc .. " — " .. val_str .. " (" .. table.concat(hits, "; ") .. ").")
            end
        end

        local output = {}
        if #hist > 0 then table.insert(output, "; Исторические:"); for _, h in ipairs(hist) do table.insert(output, h) end end
        if #tourney > 0 then
            if #hist > 0 then table.insert(output, "") end
            table.insert(output, "; За один турнир:"); for _, t in ipairs(tourney) do table.insert(output, t) end
        end

        return table.concat(output, "\n")
    end

    local output = {}
    if show_ach_list then
        local ach_text = build_achievements_text()
        if ach_text and ach_text ~= "" then table.insert(output, "== Достижения ==\n" .. ach_text) end
    end

    if show_rec_list then
        local rec_text = build_records_text()
        if rec_text and rec_text ~= "" then
            if #output > 0 then table.insert(output, "") end
            table.insert(output, "== Рекорды ==\n" .. rec_text)
        end
    end

    return table.concat(output, "\n")
end

return PlayerAchievements