From 5cb38289d365f2d0ded4780829a9040f60269775 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:49:22 +0900 Subject: [PATCH 1/8] add ceremony info processing --- .../valorant/MatchGroup/Input/Custom.lua | 1 + .../MatchGroup/Input/Custom/MatchPage.lua | 43 ++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom.lua b/lua/wikis/valorant/MatchGroup/Input/Custom.lua index 0a220fb2a08..c7edc264fac 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom.lua @@ -43,6 +43,7 @@ local VALORANT_REGIONS = {'eu', 'na', 'ap', 'kr', 'latam', 'br', 'pbe1', 'esport ---@field t2side ValorantSides ---@field winningSide ValorantSides ---@field ceremony string +---@field ceremonyFor string? ---@class ValorantMapParserInterface ---@field getMap fun(mapInput: table): table diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua index d445c871d73..8e89351ce04 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua @@ -222,6 +222,32 @@ function CustomMatchGroupInputMatchPage.getRounds(map) return ceremonyCode:sub(9) end + ---@param ceremonyCode string? + ---@param roundKills valorantMatchApiRoundKill[] + ---@param winningTeam integer + ---@return string? + local function processCeremony(ceremonyCode, roundKills, winningTeam) + local ceremony = mapCeremonyCodes(ceremonyCode) + if ceremony == 'Clutch' then + local killsFromWinningTeam = Array.filter( + roundKills, + function (roundKill) + return Table.includes(map.teams[winningTeam].puuids, roundKill.killer) + end + ) + return killsFromWinningTeam[#killsFromWinningTeam].killer + elseif ceremony == 'Ace' then + local _, killsByPlayer = Array.groupBy(roundKills, function (roundKill) + return roundKill.killer + end) + for killer, kills in pairs(killsByPlayer) do + if #kills == 5 then + return killer + end + end + end + end + local t1start = CustomMatchGroupInputMatchPage.getFirstSide(map, 1, 'normal') local t1startot = CustomMatchGroupInputMatchPage.getFirstSide(map, 1, 'ot') local nextOvertimeSide = t1startot @@ -249,20 +275,25 @@ function CustomMatchGroupInputMatchPage.getRounds(map) return nil end - ---@type valorantMatchApiRoundKill - local firstKill = Array.min( + ---@type valorantMatchApiRoundKill[] + local roundKills = Array.sortBy( Array.flatMap(round.player_stats, function (player) return player.kills or {} end), - function (kill, fastestKill) - return (kill.time_since_round_start_millis or math.huge) < ( - fastestKill.time_since_round_start_millis or math.huge) - end + Operator.property('time_since_round_start_millis') ) + ---@type valorantMatchApiRoundKill + local firstKill = roundKills[1] + ---@type ValorantRoundData return { ceremony = mapCeremonyCodes(round.round_ceremony), + ceremonyFor = processCeremony( + round.round_ceremony, + roundKills, + (t1side == makeShortSideName(round.winning_team_role)) and 1 or 2 + ), firstKill = Logic.isNotEmpty(firstKill) and { killer = firstKill.killer, victim = firstKill.victim, From 22f2fbc1b345e703a02583398ec80f11156d33d8 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:50:59 +0900 Subject: [PATCH 2/8] add flawless --- .../MatchGroup/Input/Custom/MatchPage.lua | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua index 8e89351ce04..76a5a2fe25b 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua @@ -213,21 +213,30 @@ function CustomMatchGroupInputMatchPage.getRounds(map) end ---@param ceremonyCode string? + ---@param roundKills valorantMatchApiRoundKill[] + ---@param winningTeam integer ---@return string - local function mapCeremonyCodes(ceremonyCode) + local function mapCeremonyCodes(ceremonyCode, roundKills, winningTeam) if Logic.isEmpty(ceremonyCode) then + if Array.all( + roundKills, + function (roundKill) + return Table.includes(map.teams[winningTeam].puuids, roundKill.killer) + end + ) then + return 'Flawless' + end return '' end ---@cast ceremonyCode -nil return ceremonyCode:sub(9) end - ---@param ceremonyCode string? + ---@param ceremony string? ---@param roundKills valorantMatchApiRoundKill[] ---@param winningTeam integer ---@return string? - local function processCeremony(ceremonyCode, roundKills, winningTeam) - local ceremony = mapCeremonyCodes(ceremonyCode) + local function processCeremony(ceremony, roundKills, winningTeam) if ceremony == 'Clutch' then local killsFromWinningTeam = Array.filter( roundKills, @@ -283,17 +292,16 @@ function CustomMatchGroupInputMatchPage.getRounds(map) Operator.property('time_since_round_start_millis') ) + local winningTeam = (t1side == makeShortSideName(round.winning_team_role)) and 1 or 2 + local ceremony = mapCeremonyCodes(round.round_ceremony, roundKills, winningTeam) + ---@type valorantMatchApiRoundKill local firstKill = roundKills[1] ---@type ValorantRoundData return { - ceremony = mapCeremonyCodes(round.round_ceremony), - ceremonyFor = processCeremony( - round.round_ceremony, - roundKills, - (t1side == makeShortSideName(round.winning_team_role)) and 1 or 2 - ), + ceremony = ceremony, + ceremonyFor = processCeremony(ceremony, roundKills, winningTeam), firstKill = Logic.isNotEmpty(firstKill) and { killer = firstKill.killer, victim = firstKill.victim, From 71c177d60ea19477cfa23611950e5db8e5af1a00 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:02:45 +0900 Subject: [PATCH 3/8] add flawless display --- lua/wikis/commons/Icon/Data.lua | 2 +- lua/wikis/valorant/MatchPage.lua | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/Icon/Data.lua b/lua/wikis/commons/Icon/Data.lua index d52e1b27d34..20bfdd37dc9 100644 --- a/lua/wikis/commons/Icon/Data.lua +++ b/lua/wikis/commons/Icon/Data.lua @@ -36,7 +36,7 @@ return { defuse = 'fas fa-wrench', outoftime = 'fas fa-hourglass', ace_valorant = 'fas fa-dagger', - round_winner = 'far fa-trophy-alt', + flawless_valorant = 'fas fa-skull-cow', -- Usage: Rumors, Predictions, etc. correct = 'fas fa-check', diff --git a/lua/wikis/valorant/MatchPage.lua b/lua/wikis/valorant/MatchPage.lua index a53d0e9e41f..4d606b4b259 100644 --- a/lua/wikis/valorant/MatchPage.lua +++ b/lua/wikis/valorant/MatchPage.lua @@ -409,6 +409,8 @@ MatchPage._displayCeremony = FnUtil.memoize(function (ceremony) imageDark = 'VALORANT Creds darkmode.png', size = '16px', } + elseif ceremony == 'Flawless' then + return IconFa{iconName = 'flawless_valorant'} end end From 07f66a2fa79384906ff914f39cf288c957542ca7 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:12:23 +0900 Subject: [PATCH 4/8] add flawless to overall stats --- lua/wikis/valorant/MatchGroup/Input/Custom.lua | 1 + .../MatchGroup/Input/Custom/MatchPage.lua | 17 +++++++++++------ lua/wikis/valorant/MatchPage.lua | 6 ++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom.lua b/lua/wikis/valorant/MatchGroup/Input/Custom.lua index c7edc264fac..90f517975a1 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom.lua @@ -208,6 +208,7 @@ function MatchFunctions.calculateOverallStatsForOpponent(maps, opponentIndex) return { firstKills = getSumOf('firstKills'), + flawless = getSumOf('flawless'), thrifties = getSumOf('thrifties'), clutches = getSumOf('clutches'), postPlant = postPlant, diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua index 76a5a2fe25b..ce3d12199e5 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua @@ -341,16 +341,21 @@ function CustomMatchGroupInputMatchPage.extendMapOpponent(map, opponentIndex) return round[teamSideKey] == 'atk' and round.planted end) + ---@param ceremony string + ---@return integer + local function countCeremonies(ceremony) + return #Array.filter(rounds, function (round) + return round[teamSideKey] == round.winningSide and round.ceremony == ceremony + end) + end + return { - thrifties = #Array.filter(rounds, function (round) - return round[teamSideKey] == round.winningSide and round.ceremony == 'Thrifty' - end), + thrifties = countCeremonies('Thrifty'), + flawless = countCeremonies('Flawless'), firstKills = #Array.filter(rounds, function (round) return round.firstKill.byTeam == opponentIndex end), - clutches = #Array.filter(rounds, function (round) - return round[teamSideKey] == round.winningSide and round.ceremony == 'Clutch' - end), + clutches = countCeremonies('Clutch'), postPlant = { #Array.filter(plantedRounds, function (round) return round.winningSide == 'atk' diff --git a/lua/wikis/valorant/MatchPage.lua b/lua/wikis/valorant/MatchPage.lua index 4d606b4b259..21c4728249d 100644 --- a/lua/wikis/valorant/MatchPage.lua +++ b/lua/wikis/valorant/MatchPage.lua @@ -346,6 +346,12 @@ function MatchPage:_renderTeamStats(game) team1Value = game.teams[1].thrifties, team2Value = game.teams[2].thrifties }, + { + icon = IconFa{iconName = 'flawless_valorant'}, + name = 'Flawless', + team1Value = game.teams[1].flawless, + team2Value = game.teams[2].flawless, + }, { icon = IconImage{ imageLight = 'VALORANT Spike lightmode.png', From 37824eaffe3a71db15b5e6c1b55bbebf2c61e21e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:26:08 +0900 Subject: [PATCH 5/8] adjust flawless processing --- .../valorant/MatchGroup/Input/Custom.lua | 1 + .../MatchGroup/Input/Custom/MatchPage.lua | 25 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom.lua b/lua/wikis/valorant/MatchGroup/Input/Custom.lua index 90f517975a1..3ee7fb41410 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom.lua @@ -42,6 +42,7 @@ local VALORANT_REGIONS = {'eu', 'na', 'ap', 'kr', 'latam', 'br', 'pbe1', 'esport ---@field t1side ValorantSides ---@field t2side ValorantSides ---@field winningSide ValorantSides +---@field flawless boolean ---@field ceremony string ---@field ceremonyFor string? diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua index ce3d12199e5..45d82562f91 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua @@ -213,19 +213,9 @@ function CustomMatchGroupInputMatchPage.getRounds(map) end ---@param ceremonyCode string? - ---@param roundKills valorantMatchApiRoundKill[] - ---@param winningTeam integer ---@return string - local function mapCeremonyCodes(ceremonyCode, roundKills, winningTeam) + local function mapCeremonyCodes(ceremonyCode) if Logic.isEmpty(ceremonyCode) then - if Array.all( - roundKills, - function (roundKill) - return Table.includes(map.teams[winningTeam].puuids, roundKill.killer) - end - ) then - return 'Flawless' - end return '' end ---@cast ceremonyCode -nil @@ -293,7 +283,13 @@ function CustomMatchGroupInputMatchPage.getRounds(map) ) local winningTeam = (t1side == makeShortSideName(round.winning_team_role)) and 1 or 2 - local ceremony = mapCeremonyCodes(round.round_ceremony, roundKills, winningTeam) + local ceremony = mapCeremonyCodes(round.round_ceremony) + local flawless = Array.all( + roundKills, + function (roundKill) + return Table.includes(map.teams[winningTeam].puuids, roundKill.killer) + end + ) ---@type valorantMatchApiRoundKill local firstKill = roundKills[1] @@ -309,6 +305,7 @@ function CustomMatchGroupInputMatchPage.getRounds(map) } or {}, planted = round.plant_round_time > 0, defused = round.defuse_round_time > 0, + flawless = flawless, round = roundNumber, t1side = t1side, t2side = t2side, @@ -351,7 +348,9 @@ function CustomMatchGroupInputMatchPage.extendMapOpponent(map, opponentIndex) return { thrifties = countCeremonies('Thrifty'), - flawless = countCeremonies('Flawless'), + flawless = #Array.filter(rounds, function (round) + return round[teamSideKey] == round.winningSide and round.flawless + end), firstKills = #Array.filter(rounds, function (round) return round.firstKill.byTeam == opponentIndex end), From df1dcefd04668319abcfd37a0a70a37d19337e34 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:30:08 +0900 Subject: [PATCH 6/8] add ceremony player display --- lua/wikis/valorant/MatchPage.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lua/wikis/valorant/MatchPage.lua b/lua/wikis/valorant/MatchPage.lua index 21c4728249d..fc49e03aca5 100644 --- a/lua/wikis/valorant/MatchPage.lua +++ b/lua/wikis/valorant/MatchPage.lua @@ -383,6 +383,9 @@ end ---@param puuid string ---@return {player: string, displayName: string}? function MatchPage._findPlayerByPuuid(game, puuid) + if Logic.isEmpty(puuid) then + return + end for _, opponent in ipairs(game.opponents) do for _, player in ipairs(opponent.players) do if player.puuid == puuid then @@ -460,6 +463,7 @@ end ---@return Widget function MatchPage:_renderRoundDetail(findPlayer, round, roundIndex) local firstKillPlayer = findPlayer(round.firstKill.killer) or {} + local ceremonyPlayer = findPlayer(round.ceremonyFor) local roundWinType = WIN_TYPES[round.winBy] or {} return Div{ @@ -504,7 +508,13 @@ function MatchPage:_renderRoundDetail(findPlayer, round, roundIndex) ' ', Link{link = firstKillPlayer.player, children = firstKillPlayer.displayName} }}, - MatchPage._displayCeremony(round.ceremony) + Span{children = WidgetUtil.collect( + MatchPage._displayCeremony(Logic.emptyOr(round.ceremony, round.flawless and 'Flawless' or nil)), + ceremonyPlayer and { + ' ', + Link{link = ceremonyPlayer.player, children = ceremonyPlayer.displayName} + } or nil + )} } } From db02dede28965e6000c5a9a161eb812987da1f48 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 23 Jan 2026 11:45:14 +0900 Subject: [PATCH 7/8] account for sage ult (resurrection) --- lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua index 45d82562f91..5f7a7fa3608 100644 --- a/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua +++ b/lua/wikis/valorant/MatchGroup/Input/Custom/MatchPage.lua @@ -240,7 +240,7 @@ function CustomMatchGroupInputMatchPage.getRounds(map) return roundKill.killer end) for killer, kills in pairs(killsByPlayer) do - if #kills == 5 then + if #kills >= 5 then return killer end end From 87d206cc34a0e1dae8f2d93a04818cb00c122861 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 27 Jan 2026 20:55:25 +0900 Subject: [PATCH 8/8] update icon for flawless --- lua/wikis/commons/Icon/Data.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/Icon/Data.lua b/lua/wikis/commons/Icon/Data.lua index 20bfdd37dc9..b3fad44a647 100644 --- a/lua/wikis/commons/Icon/Data.lua +++ b/lua/wikis/commons/Icon/Data.lua @@ -36,7 +36,7 @@ return { defuse = 'fas fa-wrench', outoftime = 'fas fa-hourglass', ace_valorant = 'fas fa-dagger', - flawless_valorant = 'fas fa-skull-cow', + flawless_valorant = 'fas fa-gem', -- Usage: Rumors, Predictions, etc. correct = 'fas fa-check',