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

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