Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Records: Difference between revisions

From eSportsAmaze
Created page with "local p = {} local cargo = mw.ext.cargo local html = mw.html local function sqlEscape(s) if not s then return "" end return s:gsub("\\", "\\\\"):gsub("'", "\\'") end local function getNum(s) if not s then return 0 end return tonumber(s:match("%d+")) or 0 end function p.main(frame) local args = frame:getParent().args local tournamentName = args.tournament or mw.title.getCurrentTitle().text local whereClause = string.format("tournament = '%s'..."
 
No edit summary
 
(5 intermediate revisions by the same user not shown)
Line 11: Line 11:
     if not s then return 0 end
     if not s then return 0 end
     return tonumber(s:match("%d+")) or 0
     return tonumber(s:match("%d+")) or 0
end
local function parseTime(s)
    if not s or s == "" then return 0 end
    local parts = {}
    for p in s:gmatch("%d+") do table.insert(parts, tonumber(p)) end
    if #parts == 1 then return parts[1] end
    if #parts == 2 then return parts[1] * 60 + parts[2] end
    if #parts == 3 then return parts[1] * 3600 + parts[2] * 60 + parts[3] end
    return 0
end
-- Helper to keep lists sorted to Top 5
local function insertTop5(list, name, team, val)
    if val <= 0 then return end
    table.insert(list, {n = name, t = team, v = val})
    table.sort(list, function(a, b) return a.v > b.v end)
    if #list > 5 then table.remove(list, 6) end
end
end


function p.main(frame)
function p.main(frame)
     local args = frame:getParent().args
     local args = frame:getParent().args
    if not args.tournament and frame.args.tournament then args = frame.args end
     local tournamentName = args.tournament or mw.title.getCurrentTitle().text
     local tournamentName = args.tournament or mw.title.getCurrentTitle().text
    local whereClause = string.format("tournament = '%s'", sqlEscape(tournamentName))
   
    -- ==========================================
    -- 1. DAILY RECORDS (Grouped by Day & Team)
    -- ==========================================
    local dayStats = cargo.query("MatchStats_Team",
        "tournament_day, team, SUM(total_pts)=day_total, SUM(elim_pts)=day_elims",
        { where = whereClause, groupBy = "tournament_day, team", limit = 5000 }
    )
   
    local days = {}
    local dayList = {}
      
      
     if dayStats and #dayStats > 0 then
     local whereParts = {}
        for _, r in ipairs(dayStats) do
    local filters = {"tournament", "tournament_type", "stage", "group_name", "map", "match_type", "tournament_day", "date", "time", "team", "player"}
            local day = r.tournament_day
    for _, f in ipairs(filters) do
            if day and day ~= "" then
        local val = args[f] or args[f:gsub("_name", "")]
                if not days[day] then
        if f == "tournament" and (not val or val == "") then val = tournamentName end
                    days[day] = { pts = {team="-", val=-1}, elims = {team="-", val=-1} }  
        if val and val ~= "" then
                    table.insert(dayList, day)
            if val:find(",") then
                end
                local inVals = {}
                  
                for v in val:gmatch("[^,]+") do table.insert(inVals, "'" .. sqlEscape(v:match("^%s*(.-)%s*$")) .. "'") end
                local pts = tonumber(r.day_total) or 0
                 table.insert(whereParts, string.format("%s IN (%s)", f, table.concat(inVals, ",")))
                local elims = tonumber(r.day_elims) or 0
            else
               
                 table.insert(whereParts, string.format("%s = '%s'", f, sqlEscape(val)))
                 if pts > days[day].pts.val then days[day].pts = {team=r.team, val=pts} end
                if elims > days[day].elims.val then days[day].elims = {team=r.team, val=elims} end
             end
             end
         end
         end
     end
     end
    local whereClause = #whereParts > 0 and table.concat(whereParts, " AND ") or "1=1"
      
      
     -- Sort days intelligently (Day 1, Day 2 ... Day 10)
     local mvpStages = {}
    table.sort(dayList, function(a, b) return getNum(a) < getNum(b) end)
    if args.mvp_stage and args.mvp_stage ~= "" then
 
        for v in args.mvp_stage:gmatch("[^,]+") do mvpStages[v:match("^%s*(.-)%s*$")] = true end
     -- ==========================================
    end
     -- 2. OVERALL MATCH RECORDS
      
     -- ==========================================
     -- Toggles
     local teamMatch = cargo.query("MatchStats_Team", "team, total_pts, elim_pts", { where = whereClause, limit = 5000 })
     local show_damage    = args.show_damage ~= "false"
     local playerMatch = cargo.query("MatchStats_Player", "player, team, player_elims, damage", { where = whereClause, limit = 5000 })
    local show_long_elim  = args.show_long_elim ~= "false"
    local show_rescues    = args.show_rescues ~= "false"
    local show_maps      = args.show_maps ~= "false"
    local show_daily      = args.show_daily ~= "false"
    local show_single    = args.show_single ~= "false"
    local show_mvp        = args.show_mvp ~= "false" and args.show_match_mvps ~= "false"
    local show_fragger    = args.show_fragger ~= "false"
    local show_event_mvp  = args.show_event_mvp == "true"
   
     local teamMatch = cargo.query("MatchStats_Team", "tournament_day, team, map, total_pts, elim_pts", { where = whereClause, limit = 5000 })
     local playerMatch = cargo.query("MatchStats_Player", "tournament_day, stage, player, team, player_elims, damage, knockouts, survival, mvp, long_elim, rescues", { where = whereClause, limit = 5000 })
      
      
     local rec = {
     local d_team = {}; local d_player = {}; local o_player = {}; local map_rec = {}; local daySet = {}
        t_pts = {name="-", val=-1},
        t_elims = {name="-", val=-1},
        p_elims = {name="-", team="-", val=-1},
        p_dmg = {name="-", team="-", val=-1}
    }
      
      
    -- Now arrays to hold Top 5
    local rec = { t_pts = {}, t_elims = {}, p_elims = {}, p_dmg = {}, long = {}, rescues = {} }
    local g = { surv = 0, dmg = 0, elims = 0, knocks = 0 }
     if teamMatch then
     if teamMatch then
         for _, r in ipairs(teamMatch) do
         for _, r in ipairs(teamMatch) do
             local pts = tonumber(r.total_pts) or 0
             local pts = tonumber(r.total_pts) or 0; local elims = tonumber(r.elim_pts) or 0
            local elims = tonumber(r.elim_pts) or 0
             local day = r.tournament_day; local map = r.map
             if pts > rec.t_pts.val then rec.t_pts = {name=r.team, val=pts} end
           
            if elims > rec.t_elims.val then rec.t_elims = {name=r.team, val=elims} end
            insertTop5(rec.t_pts, r.team, nil, pts)
            insertTop5(rec.t_elims, r.team, nil, elims)
           
            if map and map ~= "" then
                if not map_rec[map] then map_rec[map] = {} end
                insertTop5(map_rec[map], r.team, nil, pts)
            end
           
            if day and day ~= "" then
                daySet[day] = true; if not d_team[day] then d_team[day] = {} end
                if not d_team[day][r.team] then d_team[day][r.team] = {pts=0, elims=0} end
                d_team[day][r.team].pts = d_team[day][r.team].pts + pts; d_team[day][r.team].elims = d_team[day][r.team].elims + elims
            end
         end
         end
     end
     end
Line 76: Line 103:
             local elims = tonumber(r.player_elims) or 0
             local elims = tonumber(r.player_elims) or 0
             local dmg = tonumber(r.damage) or 0
             local dmg = tonumber(r.damage) or 0
             if elims > rec.p_elims.val then rec.p_elims = {name=r.player, team=r.team, val=elims} end
            local knocks = tonumber(r.knockouts) or 0
             if dmg > rec.p_dmg.val then rec.p_dmg = {name=r.player, team=r.team, val=dmg} end
            local surv = parseTime(r.survival)
            local mvp = tonumber(r.mvp) or 0
            local long = tonumber(r.long_elim) or 0
            local res = tonumber(r.rescues) or 0
            local day = r.tournament_day
            local stage = r.stage
            local p = r.player
           
            local isValidMvp = true
             if next(mvpStages) and not mvpStages[stage] then isValidMvp = false end
            if isValidMvp then g.elims = g.elims + elims; g.dmg = g.dmg + dmg; g.knocks = g.knocks + knocks; g.surv = g.surv + surv end
           
            insertTop5(rec.p_elims, p, r.team, elims)
            insertTop5(rec.p_dmg, p, r.team, dmg)
            insertTop5(rec.long, p, r.team, long)
            insertTop5(rec.rescues, p, r.team, res)
           
            if not o_player[p] then o_player[p] = {t=r.team, elims=0, mvp=0, m_elims=0, m_dmg=0, m_knocks=0, m_surv=0} end
            o_player[p].elims = o_player[p].elims + elims; o_player[p].mvp = o_player[p].mvp + mvp
           
            if isValidMvp then
                o_player[p].m_elims = o_player[p].m_elims + elims; o_player[p].m_dmg = o_player[p].m_dmg + dmg
                o_player[p].m_knocks = o_player[p].m_knocks + knocks; o_player[p].m_surv = o_player[p].m_surv + surv
            end
           
            if day and day ~= "" then
                daySet[day] = true; if not d_player[day] then d_player[day] = {} end
                if not d_player[day][p] then d_player[day][p] = {t=r.team, elims=0, dmg=0} end
                d_player[day][p].elims = d_player[day][p].elims + elims
            end
        end
    end
 
    local top_fragger = {n="-", t="-", v=-1}
    local top_mvp = {n="-", t="-", v=-1}
    local true_mvp = {n="-", t="-", v=-1}
   
    for p, d in pairs(o_player) do
        if d.elims > top_fragger.v then top_fragger = {n=p, t=d.t, v=d.elims} end
        if d.mvp > top_mvp.v then top_mvp = {n=p, t=d.t, v=d.mvp} end
        if show_event_mvp then
            local score = 0
             if g.surv > 0 then score = score + (d.m_surv / g.surv) * 0.2 end
            if g.dmg > 0 then score = score + (d.m_dmg / g.dmg) * 0.3 end
            if g.elims > 0 then score = score + (d.m_elims / g.elims) * 0.4 end
            if g.knocks > 0 then score = score + (d.m_knocks / g.knocks) * 0.1 end
            local rating = score * 100
            if rating > true_mvp.v then true_mvp = {n=p, t=d.t, v=rating} end
         end
         end
     end
     end


     -- ==========================================
     local dayList = {}
     -- 3. BUILD UI
    for day in pairs(daySet) do table.insert(dayList, day) end
     -- ==========================================
     table.sort(dayList, function(a, b) return getNum(a) < getNum(b) end)
    if #dayList == 0 and rec.t_pts.val == -1 then
     local dailyWinners = {}
        return "" -- No data yet
    for _, day in ipairs(dayList) do
        dailyWinners[day] = { t_pts = {n="-", v=-1}, p_elims = {n="-", t="-", v=-1} }
        if d_team[day] then for t, d in pairs(d_team[day]) do if d.pts > dailyWinners[day].t_pts.v then dailyWinners[day].t_pts = {n=t, v=d.pts} end end end
        if d_player[day] then for p, d in pairs(d_player[day]) do if d.elims > dailyWinners[day].p_elims.v then dailyWinners[day].p_elims = {n=p, t=d.t, v=d.elims} end end end
     end
     end
    local mapOrder = {}
    for m in pairs(map_rec) do table.insert(mapOrder, m) end
    table.sort(mapOrder)
    if #rec.t_pts == 0 then return "" end


     local root = html.create('div'):addClass('rec-wrapper')
     local root = html.create('div'):addClass('rec-wrapper')
     root:tag('div'):addClass('rec-header'):wikitext('🏆 Tournament Records')
     root:tag('div'):addClass('rec-header'):wikitext('🏆 Tournament Records')
      
      
     local grid = root:tag('div'):addClass('rec-grid')
     local bannerItems = 0
    local banner = html.create('div'):addClass('rec-banner')
      
      
     -- LEFT COLUMN: Overall Single Match Records
     if show_event_mvp and true_mvp.v > 0 then
     local colLeft = grid:tag('div'):addClass('rec-col')
        bannerItems = bannerItems + 1
     colLeft:tag('div'):addClass('rec-col-title'):wikitext('Single Match Highs')
        local b0 = banner:tag('div'):addClass('rec-ban-item')
        b0:tag('div'):addClass('rec-ban-lbl'):wikitext('Overall Event MVP')
        b0:tag('div'):addClass('rec-ban-val'):wikitext('💎 [[' .. true_mvp.n .. ']] <span class="rec-dim">(' .. true_mvp.t .. ')</span>')
        b0:tag('div'):addClass('rec-ban-stat'):wikitext(string.format("%.2f", true_mvp.v) .. ' Rating')
    end
     if show_fragger and top_fragger.v > 0 then
        bannerItems = bannerItems + 1
        local b1 = banner:tag('div'):addClass('rec-ban-item')
        b1:tag('div'):addClass('rec-ban-lbl'):wikitext('Tournament Top Fragger')
        b1:tag('div'):addClass('rec-ban-val'):wikitext('🥇 [[' .. top_fragger.n .. ']] <span class="rec-dim">(' .. top_fragger.t .. ')</span>')
        b1:tag('div'):addClass('rec-ban-stat'):wikitext(top_fragger.v .. ' Elims')
     end
    if show_mvp and top_mvp.v > 0 then
        bannerItems = bannerItems + 1
        local b2 = banner:tag('div'):addClass('rec-ban-item')
        b2:tag('div'):addClass('rec-ban-lbl'):wikitext('Most Match MVPs')
        b2:tag('div'):addClass('rec-ban-val'):wikitext('⭐ [[' .. top_mvp.n .. ']] <span class="rec-dim">(' .. top_mvp.t .. ')</span>')
        b2:tag('div'):addClass('rec-ban-stat'):wikitext(top_mvp.v .. ' Matches')
    end
    if bannerItems > 0 then root:node(banner) end
 
    local grid = html.create('div'):addClass('rec-grid')
    local colCount = 0
      
      
     local function addRecord(parent, icon, title, name, subname, val, suffix)
     local function addTop5Box(parent, icon, title, list, suffix)
         if val == -1 then return end
         if #list == 0 then return end
         local box = parent:tag('div'):addClass('rec-box')
         local box = parent:tag('div'):addClass('rec-top5-box')
         box:tag('div'):addClass('rec-icon'):wikitext(icon)
         local header = box:tag('div'):addClass('rec-top5-header')
         local info = box:tag('div'):addClass('rec-info')
        header:tag('div'):addClass('rec-icon'):wikitext(icon)
         info:tag('div'):addClass('rec-title'):wikitext(title)
        header:tag('div'):addClass('rec-title'):wikitext(title)
       
 
        local nameStr = "'''[[" .. name .. "]]'''"
         local listWrap = box:tag('div'):addClass('rec-top5-list')
        if subname and subname ~= "-" then nameStr = nameStr .. " <span style='font-size:0.75em; color:var(--text-muted);'>(" .. subname .. ")</span>" end
         for i, item in ipairs(list) do
        info:tag('div'):addClass('rec-name'):wikitext(nameStr)
            local row = listWrap:tag('div'):addClass('rec-top5-item')
       
            row:tag('div'):addClass('rec-top5-rank'):wikitext(i)
        box:tag('div'):addClass('rec-val'):wikitext(val .. '<span>' .. suffix .. '</span>')
           
            local nameStr = "'''[[" .. item.n .. "]]'''"
            if item.t and item.t ~= "-" then nameStr = nameStr .. " <span class='rec-dim'>(" .. item.t .. ")</span>" end
            row:tag('div'):addClass('rec-top5-name'):wikitext(nameStr)
            row:tag('div'):addClass('rec-top5-val'):wikitext(math.floor(item.v) .. ' <span style="font-size:0.5em; color:var(--text-muted);">' .. suffix .. '</span>')
        end
     end
     end
    if show_single then
        colCount = colCount + 1
        local col1 = grid:tag('div'):addClass('rec-col')
        col1:tag('div'):addClass('rec-col-title'):wikitext('Single Match Records')
        addTop5Box(col1, '🔥', 'Highest Team Points', rec.t_pts, 'pts')
        addTop5Box(col1, '⚔️', 'Highest Team Elims', rec.t_elims, 'elims')
        addTop5Box(col1, '💀', 'Highest Player Elims', rec.p_elims, 'elims')
        if show_damage then addTop5Box(col1, '💥', 'Highest Player Dmg', rec.p_dmg, 'dmg') end
    end
    local hasCol2 = false
    if show_maps and #mapOrder > 0 then hasCol2 = true end
    if show_long_elim and #rec.long > 0 then hasCol2 = true end
    if show_rescues and #rec.rescues > 0 then hasCol2 = true end
      
      
     addRecord(colLeft, '🔥', 'Highest Team Points', rec.t_pts.name, nil, rec.t_pts.val, 'pts')
     if hasCol2 then
    addRecord(colLeft, '⚔️', 'Highest Team Elims', rec.t_elims.name, nil, rec.t_elims.val, 'elims')
        colCount = colCount + 1
    addRecord(colLeft, '💀', 'Highest Player Elims', rec.p_elims.name, rec.p_elims.team, rec.p_elims.val, 'elims')
        local col2 = grid:tag('div'):addClass('rec-col')
    if rec.p_dmg.val > 0 then
        col2:tag('div'):addClass('rec-col-title'):wikitext('Map Highs & Highlights')
        addRecord(colLeft, '💥', 'Highest Player Damage', rec.p_dmg.name, rec.p_dmg.team, math.floor(rec.p_dmg.val), 'dmg')
        if show_maps then for _, m in ipairs(mapOrder) do addTop5Box(col2, '🗺️', m .. ' Record', map_rec[m], 'pts') end end
        if show_long_elim then addTop5Box(col2, '🔭', 'Longest Elimination', rec.long, 'm') end
        if show_rescues then addTop5Box(col2, '⛑️', 'Most Rescues', rec.rescues, 'revives') end
     end
     end


     -- RIGHT COLUMN: Daily Leaders
     if show_daily and #dayList > 0 then
    local colRight = grid:tag('div'):addClass('rec-col')
        colCount = colCount + 1
    colRight:tag('div'):addClass('rec-col-title'):wikitext('Daily Leaders')
        local col3 = grid:tag('div'):addClass('rec-col')
   
        col3:tag('div'):addClass('rec-col-title'):wikitext('Daily Top Performers')
    local dayListContainer = colRight:tag('div'):addClass('rec-day-list')
        local dayListContainer = col3:tag('div'):addClass('rec-day-list')
   
        for _, day in ipairs(dayList) do
    for _, day in ipairs(dayList) do
            local d = dailyWinners[day]
        local d = days[day]
            local dayRow = dayListContainer:tag('div'):addClass('rec-day-row')
        local dayRow = dayListContainer:tag('div'):addClass('rec-day-row')
            dayRow:tag('div'):addClass('rec-day-name'):wikitext(day)
        dayRow:tag('div'):addClass('rec-day-name'):wikitext(day)
            local dStats = dayRow:tag('div'):addClass('rec-day-stats')
       
            if d.t_pts.v > 0 then dStats:tag('div'):wikitext("🛡️ [[" .. d.t_pts.n .. "]] <span class='rec-dim'>(" .. d.t_pts.v .. " pts)</span>") end
        local dStats = dayRow:tag('div'):addClass('rec-day-stats')
            if d.p_elims.v > 0 then dStats:tag('div'):wikitext("🎯 [[" .. d.p_elims.n .. "]] <span class='rec-dim'>(" .. d.p_elims.v .. " elims)</span>") end
        dStats:tag('div'):wikitext("🏅 '''Pts:''' [[" .. d.pts.team .. "]] <span style='color:var(--text-muted)'>(" .. d.pts.val .. ")</span>")
        end
        dStats:tag('div'):wikitext("🔫 '''Elims:''' [[" .. d.elims.team .. "]] <span style='color:var(--text-muted)'>(" .. d.elims.val .. ")</span>")
     end
     end


    if colCount > 0 then
        grid:addClass('cols-' .. colCount)
        root:node(grid)
    end
     return tostring(root)
     return tostring(root)
end
end


return p
return p

Latest revision as of 07:17, 21 May 2026

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

local p = {}
local cargo = mw.ext.cargo
local html = mw.html

local function sqlEscape(s)
    if not s then return "" end
    return s:gsub("\\", "\\\\"):gsub("'", "\\'")
end

local function getNum(s)
    if not s then return 0 end
    return tonumber(s:match("%d+")) or 0
end

local function parseTime(s)
    if not s or s == "" then return 0 end
    local parts = {}
    for p in s:gmatch("%d+") do table.insert(parts, tonumber(p)) end
    if #parts == 1 then return parts[1] end
    if #parts == 2 then return parts[1] * 60 + parts[2] end
    if #parts == 3 then return parts[1] * 3600 + parts[2] * 60 + parts[3] end
    return 0
end

-- Helper to keep lists sorted to Top 5
local function insertTop5(list, name, team, val)
    if val <= 0 then return end
    table.insert(list, {n = name, t = team, v = val})
    table.sort(list, function(a, b) return a.v > b.v end)
    if #list > 5 then table.remove(list, 6) end
end

function p.main(frame)
    local args = frame:getParent().args
    if not args.tournament and frame.args.tournament then args = frame.args end
    local tournamentName = args.tournament or mw.title.getCurrentTitle().text
    
    local whereParts = {}
    local filters = {"tournament", "tournament_type", "stage", "group_name", "map", "match_type", "tournament_day", "date", "time", "team", "player"}
    for _, f in ipairs(filters) do
        local val = args[f] or args[f:gsub("_name", "")]
        if f == "tournament" and (not val or val == "") then val = tournamentName end
        if val and val ~= "" then
            if val:find(",") then
                local inVals = {}
                for v in val:gmatch("[^,]+") do table.insert(inVals, "'" .. sqlEscape(v:match("^%s*(.-)%s*$")) .. "'") end
                table.insert(whereParts, string.format("%s IN (%s)", f, table.concat(inVals, ",")))
            else
                table.insert(whereParts, string.format("%s = '%s'", f, sqlEscape(val)))
            end
        end
    end
    local whereClause = #whereParts > 0 and table.concat(whereParts, " AND ") or "1=1"
    
    local mvpStages = {}
    if args.mvp_stage and args.mvp_stage ~= "" then
        for v in args.mvp_stage:gmatch("[^,]+") do mvpStages[v:match("^%s*(.-)%s*$")] = true end
    end
    
    -- Toggles
    local show_damage     = args.show_damage ~= "false"
    local show_long_elim  = args.show_long_elim ~= "false"
    local show_rescues    = args.show_rescues ~= "false"
    local show_maps       = args.show_maps ~= "false"
    local show_daily      = args.show_daily ~= "false"
    local show_single     = args.show_single ~= "false"
    local show_mvp        = args.show_mvp ~= "false" and args.show_match_mvps ~= "false"
    local show_fragger    = args.show_fragger ~= "false"
    local show_event_mvp  = args.show_event_mvp == "true"
    
    local teamMatch = cargo.query("MatchStats_Team", "tournament_day, team, map, total_pts, elim_pts", { where = whereClause, limit = 5000 })
    local playerMatch = cargo.query("MatchStats_Player", "tournament_day, stage, player, team, player_elims, damage, knockouts, survival, mvp, long_elim, rescues", { where = whereClause, limit = 5000 })
    
    local d_team = {}; local d_player = {}; local o_player = {}; local map_rec = {}; local daySet = {}
    
    -- Now arrays to hold Top 5
    local rec = { t_pts = {}, t_elims = {}, p_elims = {}, p_dmg = {}, long = {}, rescues = {} }
    local g = { surv = 0, dmg = 0, elims = 0, knocks = 0 }

    if teamMatch then
        for _, r in ipairs(teamMatch) do
            local pts = tonumber(r.total_pts) or 0; local elims = tonumber(r.elim_pts) or 0
            local day = r.tournament_day; local map = r.map
            
            insertTop5(rec.t_pts, r.team, nil, pts)
            insertTop5(rec.t_elims, r.team, nil, elims)
            
            if map and map ~= "" then 
                if not map_rec[map] then map_rec[map] = {} end
                insertTop5(map_rec[map], r.team, nil, pts)
            end
            
            if day and day ~= "" then
                daySet[day] = true; if not d_team[day] then d_team[day] = {} end
                if not d_team[day][r.team] then d_team[day][r.team] = {pts=0, elims=0} end
                d_team[day][r.team].pts = d_team[day][r.team].pts + pts; d_team[day][r.team].elims = d_team[day][r.team].elims + elims
            end
        end
    end
    
    if playerMatch then
        for _, r in ipairs(playerMatch) do
            local elims = tonumber(r.player_elims) or 0
            local dmg = tonumber(r.damage) or 0
            local knocks = tonumber(r.knockouts) or 0
            local surv = parseTime(r.survival)
            local mvp = tonumber(r.mvp) or 0
            local long = tonumber(r.long_elim) or 0
            local res = tonumber(r.rescues) or 0
            local day = r.tournament_day
            local stage = r.stage
            local p = r.player
            
            local isValidMvp = true
            if next(mvpStages) and not mvpStages[stage] then isValidMvp = false end
            if isValidMvp then g.elims = g.elims + elims; g.dmg = g.dmg + dmg; g.knocks = g.knocks + knocks; g.surv = g.surv + surv end
            
            insertTop5(rec.p_elims, p, r.team, elims)
            insertTop5(rec.p_dmg, p, r.team, dmg)
            insertTop5(rec.long, p, r.team, long)
            insertTop5(rec.rescues, p, r.team, res)
            
            if not o_player[p] then o_player[p] = {t=r.team, elims=0, mvp=0, m_elims=0, m_dmg=0, m_knocks=0, m_surv=0} end
            o_player[p].elims = o_player[p].elims + elims; o_player[p].mvp = o_player[p].mvp + mvp
            
            if isValidMvp then
                o_player[p].m_elims = o_player[p].m_elims + elims; o_player[p].m_dmg = o_player[p].m_dmg + dmg
                o_player[p].m_knocks = o_player[p].m_knocks + knocks; o_player[p].m_surv = o_player[p].m_surv + surv
            end
            
            if day and day ~= "" then
                daySet[day] = true; if not d_player[day] then d_player[day] = {} end
                if not d_player[day][p] then d_player[day][p] = {t=r.team, elims=0, dmg=0} end
                d_player[day][p].elims = d_player[day][p].elims + elims
            end
        end
    end

    local top_fragger = {n="-", t="-", v=-1}
    local top_mvp = {n="-", t="-", v=-1}
    local true_mvp = {n="-", t="-", v=-1}
    
    for p, d in pairs(o_player) do
        if d.elims > top_fragger.v then top_fragger = {n=p, t=d.t, v=d.elims} end
        if d.mvp > top_mvp.v then top_mvp = {n=p, t=d.t, v=d.mvp} end
        if show_event_mvp then
            local score = 0
            if g.surv > 0 then score = score + (d.m_surv / g.surv) * 0.2 end
            if g.dmg > 0 then score = score + (d.m_dmg / g.dmg) * 0.3 end
            if g.elims > 0 then score = score + (d.m_elims / g.elims) * 0.4 end
            if g.knocks > 0 then score = score + (d.m_knocks / g.knocks) * 0.1 end
            local rating = score * 100 
            if rating > true_mvp.v then true_mvp = {n=p, t=d.t, v=rating} end
        end
    end

    local dayList = {}
    for day in pairs(daySet) do table.insert(dayList, day) end
    table.sort(dayList, function(a, b) return getNum(a) < getNum(b) end)
    local dailyWinners = {}
    for _, day in ipairs(dayList) do
        dailyWinners[day] = { t_pts = {n="-", v=-1}, p_elims = {n="-", t="-", v=-1} }
        if d_team[day] then for t, d in pairs(d_team[day]) do if d.pts > dailyWinners[day].t_pts.v then dailyWinners[day].t_pts = {n=t, v=d.pts} end end end
        if d_player[day] then for p, d in pairs(d_player[day]) do if d.elims > dailyWinners[day].p_elims.v then dailyWinners[day].p_elims = {n=p, t=d.t, v=d.elims} end end end
    end
    local mapOrder = {}
    for m in pairs(map_rec) do table.insert(mapOrder, m) end
    table.sort(mapOrder)

    if #rec.t_pts == 0 then return "" end 

    local root = html.create('div'):addClass('rec-wrapper')
    root:tag('div'):addClass('rec-header'):wikitext('🏆 Tournament Records')
    
    local bannerItems = 0
    local banner = html.create('div'):addClass('rec-banner')
    
    if show_event_mvp and true_mvp.v > 0 then
        bannerItems = bannerItems + 1
        local b0 = banner:tag('div'):addClass('rec-ban-item')
        b0:tag('div'):addClass('rec-ban-lbl'):wikitext('Overall Event MVP')
        b0:tag('div'):addClass('rec-ban-val'):wikitext('💎 [[' .. true_mvp.n .. ']] <span class="rec-dim">(' .. true_mvp.t .. ')</span>')
        b0:tag('div'):addClass('rec-ban-stat'):wikitext(string.format("%.2f", true_mvp.v) .. ' Rating')
    end
    if show_fragger and top_fragger.v > 0 then
        bannerItems = bannerItems + 1
        local b1 = banner:tag('div'):addClass('rec-ban-item')
        b1:tag('div'):addClass('rec-ban-lbl'):wikitext('Tournament Top Fragger')
        b1:tag('div'):addClass('rec-ban-val'):wikitext('🥇 [[' .. top_fragger.n .. ']] <span class="rec-dim">(' .. top_fragger.t .. ')</span>')
        b1:tag('div'):addClass('rec-ban-stat'):wikitext(top_fragger.v .. ' Elims')
    end
    if show_mvp and top_mvp.v > 0 then
        bannerItems = bannerItems + 1
        local b2 = banner:tag('div'):addClass('rec-ban-item')
        b2:tag('div'):addClass('rec-ban-lbl'):wikitext('Most Match MVPs')
        b2:tag('div'):addClass('rec-ban-val'):wikitext('⭐ [[' .. top_mvp.n .. ']] <span class="rec-dim">(' .. top_mvp.t .. ')</span>')
        b2:tag('div'):addClass('rec-ban-stat'):wikitext(top_mvp.v .. ' Matches')
    end
    if bannerItems > 0 then root:node(banner) end

    local grid = html.create('div'):addClass('rec-grid')
    local colCount = 0
    
    local function addTop5Box(parent, icon, title, list, suffix)
        if #list == 0 then return end
        local box = parent:tag('div'):addClass('rec-top5-box')
        local header = box:tag('div'):addClass('rec-top5-header')
        header:tag('div'):addClass('rec-icon'):wikitext(icon)
        header:tag('div'):addClass('rec-title'):wikitext(title)

        local listWrap = box:tag('div'):addClass('rec-top5-list')
        for i, item in ipairs(list) do
            local row = listWrap:tag('div'):addClass('rec-top5-item')
            row:tag('div'):addClass('rec-top5-rank'):wikitext(i)
            
            local nameStr = "'''[[" .. item.n .. "]]'''"
            if item.t and item.t ~= "-" then nameStr = nameStr .. " <span class='rec-dim'>(" .. item.t .. ")</span>" end
            row:tag('div'):addClass('rec-top5-name'):wikitext(nameStr)
            row:tag('div'):addClass('rec-top5-val'):wikitext(math.floor(item.v) .. ' <span style="font-size:0.5em; color:var(--text-muted);">' .. suffix .. '</span>')
        end
    end

    if show_single then
        colCount = colCount + 1
        local col1 = grid:tag('div'):addClass('rec-col')
        col1:tag('div'):addClass('rec-col-title'):wikitext('Single Match Records')
        addTop5Box(col1, '🔥', 'Highest Team Points', rec.t_pts, 'pts')
        addTop5Box(col1, '⚔️', 'Highest Team Elims', rec.t_elims, 'elims')
        addTop5Box(col1, '💀', 'Highest Player Elims', rec.p_elims, 'elims')
        if show_damage then addTop5Box(col1, '💥', 'Highest Player Dmg', rec.p_dmg, 'dmg') end
    end

    local hasCol2 = false
    if show_maps and #mapOrder > 0 then hasCol2 = true end
    if show_long_elim and #rec.long > 0 then hasCol2 = true end
    if show_rescues and #rec.rescues > 0 then hasCol2 = true end
    
    if hasCol2 then
        colCount = colCount + 1
        local col2 = grid:tag('div'):addClass('rec-col')
        col2:tag('div'):addClass('rec-col-title'):wikitext('Map Highs & Highlights')
        if show_maps then for _, m in ipairs(mapOrder) do addTop5Box(col2, '🗺️', m .. ' Record', map_rec[m], 'pts') end end
        if show_long_elim then addTop5Box(col2, '🔭', 'Longest Elimination', rec.long, 'm') end
        if show_rescues then addTop5Box(col2, '⛑️', 'Most Rescues', rec.rescues, 'revives') end
    end

    if show_daily and #dayList > 0 then
        colCount = colCount + 1
        local col3 = grid:tag('div'):addClass('rec-col')
        col3:tag('div'):addClass('rec-col-title'):wikitext('Daily Top Performers')
        local dayListContainer = col3:tag('div'):addClass('rec-day-list')
        for _, day in ipairs(dayList) do
            local d = dailyWinners[day]
            local dayRow = dayListContainer:tag('div'):addClass('rec-day-row')
            dayRow:tag('div'):addClass('rec-day-name'):wikitext(day)
            local dStats = dayRow:tag('div'):addClass('rec-day-stats')
            if d.t_pts.v > 0 then dStats:tag('div'):wikitext("🛡️ [[" .. d.t_pts.n .. "]] <span class='rec-dim'>(" .. d.t_pts.v .. " pts)</span>") end
            if d.p_elims.v > 0 then dStats:tag('div'):wikitext("🎯 [[" .. d.p_elims.n .. "]] <span class='rec-dim'>(" .. d.p_elims.v .. " elims)</span>") end
        end
    end

    if colCount > 0 then
        grid:addClass('cols-' .. colCount)
        root:node(grid)
    end
    return tostring(root)
end

return p