From 5c85c0dc6106298af07163e68759058a9c757c20 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Sun, 15 Dec 2024 21:59:45 +0000 Subject: [PATCH 01/10] fix summary error on char .. --- components/match2/wikis/hearthstone/match_summary.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index d9363aa57c..e263c53c67 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -166,7 +166,7 @@ function CustomMatchSummary._displayOpponents(isTeamMatch, players, flip) gap = '2px', width = '100%' }, - children = { + children = Array.extend{ char, isTeamMatch and PlayerDisplay.BlockPlayer{player = player, flip = flip} or nil, }, From 4dd5d9f8b1c7bb76697a6aed39e82628f0bfe2bf Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Mon, 16 Dec 2024 08:50:26 +0000 Subject: [PATCH 02/10] handle subgroup inputs --- .../wikis/hearthstone/match_group_input_custom.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 3cf9de3946..1b84fc5415 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -11,6 +11,7 @@ local CharacterStandardization = mw.loadData('Module:CharacterStandardization') local FnUtil = require('Module:FnUtil') local Logic = require('Module:Logic') local Lua = require('Module:Lua') +local Table = require('Module:Table') local MatchGroupInputUtil = Lua.import('Module:MatchGroup/Input/Util') local OpponentLibraries = require('Module:OpponentLibraries') @@ -18,12 +19,14 @@ local Opponent = OpponentLibraries.Opponent local CustomMatchGroupInput = {} local MatchFunctions = {} -local MapFunctions = {} MatchFunctions.OPPONENT_CONFIG = { resolveRedirect = true, pagifyTeamNames = true, pagifyPlayerNames = true, } +local MapFunctions = { + ADD_SUB_GROUP = true, +} ---@param match table ---@param options table? @@ -53,6 +56,14 @@ function MatchFunctions.getBestOf(bestofInput) return tonumber(bestofInput) end +---@param match table +---@param games table[] +---@param opponents table[] +---@return table +function MatchFunctions.getExtraData(match, games, opponents) + return Table.filterByKey(match, function(key) return key:match('subgroup%d+header') end) +end + ---@param match table ---@param map table ---@param opponents table[] From 87c051a1a5a3b5f53606e8275241e9cd2c89c710 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Mon, 16 Dec 2024 16:17:12 +0000 Subject: [PATCH 03/10] fix summary --- .../wikis/hearthstone/brkts_wiki_specific.lua | 24 +++ .../hearthstone/match_group_util_custom.lua | 167 ++++++++++++++++++ .../wikis/hearthstone/match_summary.lua | 156 ++++++++-------- 3 files changed, 277 insertions(+), 70 deletions(-) create mode 100644 components/match2/wikis/hearthstone/brkts_wiki_specific.lua create mode 100644 components/match2/wikis/hearthstone/match_group_util_custom.lua diff --git a/components/match2/wikis/hearthstone/brkts_wiki_specific.lua b/components/match2/wikis/hearthstone/brkts_wiki_specific.lua new file mode 100644 index 0000000000..10ebf43c20 --- /dev/null +++ b/components/match2/wikis/hearthstone/brkts_wiki_specific.lua @@ -0,0 +1,24 @@ +--- +-- @Liquipedia +-- wiki=hearthstone +-- page=Module:Brkts/WikiSpecific +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local FnUtil = require('Module:FnUtil') +local Lua = require('Module:Lua') +local Table = require('Module:Table') + +local BaseWikiSpecific = Lua.import('Module:Brkts/WikiSpecific/Base') + +---@class HearthstoneBrktsWikiSpecific: BrktsWikiSpecific +local WikiSpecific = Table.copy(BaseWikiSpecific) + +WikiSpecific.matchFromRecord = FnUtil.lazilyDefineFunction(function() + local CustomMatchGroupUtil = Lua.import('Module:MatchGroup/Util/Custom') + return CustomMatchGroupUtil.matchFromRecord +end) + + +return WikiSpecific diff --git a/components/match2/wikis/hearthstone/match_group_util_custom.lua b/components/match2/wikis/hearthstone/match_group_util_custom.lua new file mode 100644 index 0000000000..48e3bf9303 --- /dev/null +++ b/components/match2/wikis/hearthstone/match_group_util_custom.lua @@ -0,0 +1,167 @@ +--- +-- @Liquipedia +-- wiki=hearthstone +-- page=Module:MatchGroup/Util/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Array = require('Module:Array') +local Logic = require('Module:Logic') +local Lua = require('Module:Lua') +local String = require('Module:StringUtils') +local Table = require('Module:Table') + +local MatchGroupUtil = Lua.import('Module:MatchGroup/Util') +-- can not use `Module:OpponentLibraries`/`Module:Opponent/Custom` to avoid loop +local Opponent = Lua.import('Module:Opponent') + +local CustomMatchGroupUtil = Table.deepCopy(MatchGroupUtil) + +---@class HearthstoneMatchGroupUtilSubmatch +---@field games MatchGroupUtilGame[] +---@field opponents GameOpponent[] +---@field resultType ResultType +---@field scores table +---@field subgroup number +---@field walkover WalkoverType +---@field winner number? +---@field header string? + +---@class HearthstoneMatchGroupUtilMatch: MatchGroupUtilMatch +---@field submatches StormgateMatchGroupUtilSubmatch[]? +---@field isTeamMatch boolean + +---@param record table +---@return HearthstoneMatchGroupUtilMatch +function CustomMatchGroupUtil.matchFromRecord(record) + local match = MatchGroupUtil.matchFromRecord(record) --[[@as HearthstoneMatchGroupUtilMatch]] + + -- Adjust game.opponents by looking up game.opponents.players in match.opponents + Array.forEach(match.games, function(game) + game.opponents = CustomMatchGroupUtil.computeGameOpponents(game, match.opponents) + end) + + match.isTeamMatch = Array.any(match.opponents, function(opponent) + return opponent.type == Opponent.team end + ) + + local extradata = match.extradata + ---@cast extradata table + if match.isTeamMatch then + -- Compute submatches + match.submatches = Array.map( + CustomMatchGroupUtil.groupBySubmatch(match.games), + function(games) return CustomMatchGroupUtil.constructSubmatch(games) end + ) + + -- Extract submatch headers from extradata + for _, submatch in pairs(match.submatches) do + submatch.header = Table.extract(extradata, 'subgroup' .. submatch.subgroup .. 'header') + end + end + + return match +end + +---@param game MatchGroupUtilGame +---@param matchOpponents standardOpponent[] +---@return table[] +function CustomMatchGroupUtil.computeGameOpponents(game, matchOpponents) + return Array.map(game.opponents, function (opponent, opponentIndex) + return Table.merge(opponent, { + players = Array.map(game.opponents[opponentIndex].players or {},function (player, playerIndex) + if Logic.isEmpty(player) then return nil end + return Table.merge(matchOpponents[opponentIndex].players[playerIndex] or {}, player) + end) + }) + end) +end + +---Group games on the subgroup field to form submatches +---@param matchGames MatchGroupUtilGame[] +---@return MatchGroupUtilGame[][] +function CustomMatchGroupUtil.groupBySubmatch(matchGames) + -- Group games on adjacent subgroups + local previousSubgroup = nil + local currentGames = nil + local submatchGames = {} + for _, game in ipairs(matchGames) do + if previousSubgroup == nil or previousSubgroup ~= game.subgroup then + currentGames = {} + table.insert(submatchGames, currentGames) + previousSubgroup = game.subgroup + end + ---@cast currentGames -nil + table.insert(currentGames, game) + end + return submatchGames +end + +---Constructs a submatch object whose properties are aggregated from that of its games. +---@param games MatchGroupUtilGame[] +---@return HearthstoneMatchGroupUtilSubmatch +function CustomMatchGroupUtil.constructSubmatch(games) + local opponents = Table.deepCopy(games[1].opponents) + + -- Sum up scores + local scores = {} + for opponentIndex, _ in pairs(opponents) do + scores[opponentIndex] = 0 + end + for _, game in pairs(games) do + if game.map and String.startsWith(game.map, 'Submatch') and not game.resultType then + for opponentIndex, score in pairs(scores) do + scores[opponentIndex] = score + (tonumber(game.scores[opponentIndex]) or 0) + end + elseif game.winner then + scores[game.winner] = (scores[game.winner] or 0) + 1 + end + end + + -- Compute winner if all games have been played, skipped, or defaulted + local allPlayed = Array.all(games, function(game) + return game.winner ~= nil or game.resultType ~= nil + end) + + local resultType = nil + local winner = nil + if allPlayed then + local diff = (scores[1] or 0) - (scores[2] or 0) + if diff < 0 then + winner = 2 + elseif diff == 0 then + resultType = 'draw' + else + winner = 1 + end + end + + -- Set resultType and walkover if every game is a walkover + local walkovers = {} + local resultTypes = {} + for _, game in pairs(games) do + resultTypes[game.resultType or ''] = true + walkovers[game.walkover or ''] = true + end + local walkover + local uniqueResult = Table.uniqueKey(resultTypes) + if uniqueResult == 'default' then + resultType = 'default' + walkover = String.nilIfEmpty(Table.uniqueKey(walkovers)) or 'L' + elseif uniqueResult == 'np' then + resultType = 'np' + end + + return { + games = games, + opponents = opponents, + resultType = resultType, + scores = scores, + subgroup = games[1].subgroup, + walkover = walkover, + winner = winner, + } +end + +return CustomMatchGroupUtil diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index e263c53c67..8334d10c53 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -8,9 +8,9 @@ local Array = require('Module:Array') local DateExt = require('Module:Date/Ext') +local FnUtil = require('Module:FnUtil') local Logic = require('Module:Logic') local Lua = require('Module:Lua') -local String = require('Module:StringUtils') local Table = require('Module:Table') local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') @@ -22,7 +22,6 @@ local WidgetUtil = Lua.import('Module:Widget/Util') local OpponentLibraries = require('Module:OpponentLibraries') local Opponent = OpponentLibraries.Opponent local OpponentDisplay = OpponentLibraries.OpponentDisplay -local PlayerDisplay = require('Module:Player/Display') local CustomMatchSummary = {} @@ -32,48 +31,62 @@ function CustomMatchSummary.getByMatchId(args) return MatchSummary.defaultGetByMatchId(CustomMatchSummary, args, {width = '350px', teamStyle = 'bracket'}) end ----@param match MatchGroupUtilMatch +---@param match HearthstoneMatchGroupUtilMatch ---@return MatchSummaryBody function CustomMatchSummary.createBody(match) local showCountdown = match.timestamp ~= DateExt.defaultTimestamp - CustomMatchSummary._fixGameOpponents(match.games, match.opponents) - - local isTeamMatch = Array.any(match.opponents, function(opponent) - return opponent.type == Opponent.team - end) + local submatches + if match.isTeamMatch then + submatches = match.submatches or {} + end return MatchSummaryWidgets.Body{children = WidgetUtil.collect( showCountdown and MatchSummaryWidgets.Row{children = DisplayHelper.MatchCountdownBlock(match)} or nil, - Array.map(match.games, function (game, gameIndex) - if isTeamMatch and String.startsWith(game.map or '', 'Submatch') then - return CustomMatchSummary._createSubmatch(game) - else - return CustomMatchSummary._createGame(isTeamMatch, game, gameIndex) - end - end) + submatches and Array.map(submatches, CustomMatchSummary.TeamSubmatch) + or Array.map(match.games, FnUtil.curry(CustomMatchSummary.Game, {isPartOfSubMatch = false})) )} end ----@param games MatchGroupUtilGame ----@param opponents standardOpponent[] -function CustomMatchSummary._fixGameOpponents(games, opponents) - Array.forEach(games, function (game) - game.opponents = Array.map(game.opponents, function (opponent, opponentIndex) - return Table.merge(opponent, { - players = Array.map(game.opponents[opponentIndex].players or {},function (player, playerIndex) - if Logic.isEmpty(player) then return nil end - return Table.merge(opponents[opponentIndex].players[playerIndex] or {}, player) - end) - }) - end) +---@param submatch HearthstoneMatchGroupUtilSubmatch +---@return MatchSummaryRow +function CustomMatchSummary.TeamSubmatch(submatch) + local hasDetails = CustomMatchSummary._submatchHasDetails(submatch) + return MatchSummaryWidgets.Row{ + classes = {'brkts-popup-body-game'}, + children = WidgetUtil.collect( + submatch.header and { + HtmlWidgets.Div{css = {margin = 'auto', ['font-weight'] = 'bold'}, children = {submatch.header}}, + MatchSummaryWidgets.Break{}, + } or nil, + CustomMatchSummary.TeamSubMatchOpponnetRow(submatch), + hasDetails and Array.map(submatch.games, function(game, gameIndex) + return CustomMatchSummary.Game( + {isPartOfSubMatch = true}, + game, + gameIndex + ) + end) or nil + ) + } +end + +---@param submatch HearthstoneMatchGroupUtilSubmatch +---@return boolean +function CustomMatchSummary._submatchHasDetails(submatch) + return #submatch.games > 0 and Array.any(submatch.games, function(game) + return not string.find(game.map or '', '^[sS]ubmatch %d+$') + or Array.any(game.opponents, function(opponent) + return Array.any(opponent.players, function(player) + return Table.isNotEmpty(player) end) end) end) end ----@param game MatchGroupUtilGame +---@param submatch HearthstoneMatchGroupUtilSubmatch ---@return Widget -function CustomMatchSummary._createSubmatch(game) - local opponents = game.opponents or {{}, {}} +function CustomMatchSummary.TeamSubMatchOpponnetRow(submatch) + local opponents = submatch.opponents or {{}, {}} + local createOpponent = function(opponentIndex) local players = (opponents[opponentIndex] or {}).players or {} if Logic.isEmpty(players) then @@ -90,15 +103,15 @@ function CustomMatchSummary._createSubmatch(game) ---@param opponentIndex any ---@return Html local createScore = function(opponentIndex) - local isWinner = opponentIndex == game.winner or game.resultType == 'draw' - if game.resultType == 'default' then + local isWinner = opponentIndex == submatch.winner or submatch.resultType == 'draw' + if submatch.resultType == 'default' then return OpponentDisplay.BlockScore{ isWinner = isWinner, - scoreText = isWinner and 'W' or string.upper(game.walkover), + scoreText = isWinner and 'W' or string.upper(submatch.walkover), } end - local score = game.resultType ~= 'np' and (game.scores or {})[opponentIndex] or nil + local score = submatch.resultType ~= 'np' and (submatch.scores or {})[opponentIndex] or nil return OpponentDisplay.BlockScore{ isWinner = isWinner, scoreText = score, @@ -127,55 +140,58 @@ function CustomMatchSummary._createSubmatch(game) } end ----@param isTeamMatch boolean +---@param options {isPartOfSubMatch: boolean?} ---@param game MatchGroupUtilGame ---@param gameIndex number ---@return Widget -function CustomMatchSummary._createGame(isTeamMatch, game, gameIndex) - return MatchSummaryWidgets.Row{ +function CustomMatchSummary.Game(options, game, gameIndex) + local rowWidget = options.isPartOfSubMatch and HtmlWidgets.Div or MatchSummaryWidgets.Row + + return rowWidget{ classes = {'brkts-popup-body-game'}, - css = {padding = '4px', ['min-height'] = '24px'}, + css = {width = options.isPartOfSubMatch and '100%' or nil, ['font-size'] = '0.75rem'}, children = WidgetUtil.collect( - CustomMatchSummary._displayOpponents(isTeamMatch, game.opponents[1].players, true), - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 1}, - MatchSummaryWidgets.GameCenter{css = {['font-size'] = '80%'}, children = 'Game ' .. gameIndex}, - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 2}, - CustomMatchSummary._displayOpponents(isTeamMatch, game.opponents[2].players) + MatchSummaryWidgets.GameTeamWrapper{children = { + CustomMatchSummary.DisplayClass(game.opponents[1], true), + MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 1}, + }, + }, + MatchSummaryWidgets.GameCenter{css = {flex = '0 0 16%'}, children = 'Game ' .. gameIndex}, + MatchSummaryWidgets.GameTeamWrapper{children = { + CustomMatchSummary.DisplayClass(game.opponents[2]), + MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 2}, + }, + flipped = true + } ) } end ----@param isTeamMatch boolean ----@param players table[] +---@param opponent table ---@param flip boolean? ---@return Html? -function CustomMatchSummary._displayOpponents(isTeamMatch, players, flip) - local playerDisplays = Array.map(players, function (player) - local char = Logic.isNotEmpty(player.class) and HtmlWidgets.Div{ - classes = {'brkts-champion-icon'}, - children = MatchSummaryWidgets.Character{ - character = player.class, - showName = not isTeamMatch, - flipped = flip, - } - } or nil - return HtmlWidgets.Div{ - css = { - display = 'flex', - ['flex-direction'] = flip and 'row-reverse' or 'row', - gap = '2px', - width = '100%' - }, - children = Array.extend{ - char, - isTeamMatch and PlayerDisplay.BlockPlayer{player = player, flip = flip} or nil, - }, - } +function CustomMatchSummary.DisplayClass(opponent, flip) + local player = Array.find(opponent.players or {}, function (player) + return Logic.isNotEmpty(player.class) end) - return MatchSummaryWidgets.GameTeamWrapper{ - flipped = flip, - children = playerDisplays + if Logic.isEmpty(player) then + return nil + end + ---@cast player -nil + + return HtmlWidgets.Div{ + classes = {'brkts-champion-icon'}, + css = { + display = 'flex', + flex = '1', + ['justify-content'] = flip and 'flex-end' or 'flex-start' + }, + children = MatchSummaryWidgets.Character{ + character = player.class, + showName = true, + flipped = flip, + } } end From 032ef8c59f4ee7fd8d2687c622394b47404a8326 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Mon, 16 Dec 2024 16:37:17 +0000 Subject: [PATCH 04/10] if submatch the scores are set so no game by game display --- .../wikis/hearthstone/match_summary.lua | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index 8334d10c53..0e2bd0603d 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -76,9 +76,6 @@ end function CustomMatchSummary._submatchHasDetails(submatch) return #submatch.games > 0 and Array.any(submatch.games, function(game) return not string.find(game.map or '', '^[sS]ubmatch %d+$') - or Array.any(game.opponents, function(opponent) - return Array.any(opponent.players, function(player) - return Table.isNotEmpty(player) end) end) end) end @@ -147,22 +144,22 @@ end function CustomMatchSummary.Game(options, game, gameIndex) local rowWidget = options.isPartOfSubMatch and HtmlWidgets.Div or MatchSummaryWidgets.Row + ---@param opponentIndex any + ---@return table[] + local function createOpponentDisplay(opponentIndex) + return Array.extend({ + CustomMatchSummary.DisplayClass(game.opponents[opponentIndex], opponentIndex == 1), + MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = opponentIndex}, + }) + end + return rowWidget{ classes = {'brkts-popup-body-game'}, css = {width = options.isPartOfSubMatch and '100%' or nil, ['font-size'] = '0.75rem'}, children = WidgetUtil.collect( - MatchSummaryWidgets.GameTeamWrapper{children = { - CustomMatchSummary.DisplayClass(game.opponents[1], true), - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 1}, - }, - }, + MatchSummaryWidgets.GameTeamWrapper{children = createOpponentDisplay(1)}, MatchSummaryWidgets.GameCenter{css = {flex = '0 0 16%'}, children = 'Game ' .. gameIndex}, - MatchSummaryWidgets.GameTeamWrapper{children = { - CustomMatchSummary.DisplayClass(game.opponents[2]), - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 2}, - }, - flipped = true - } + MatchSummaryWidgets.GameTeamWrapper{children = createOpponentDisplay(2), flipped = true} ) } end From bdefea8ae72ae175e57f5fc4d0dbde57ea515650 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Mon, 16 Dec 2024 17:05:12 +0000 Subject: [PATCH 05/10] Array.forEach <3 --- .../hearthstone/match_group_util_custom.lua | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_util_custom.lua b/components/match2/wikis/hearthstone/match_group_util_custom.lua index 48e3bf9303..19abc58d8d 100644 --- a/components/match2/wikis/hearthstone/match_group_util_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_util_custom.lua @@ -46,8 +46,6 @@ function CustomMatchGroupUtil.matchFromRecord(record) return opponent.type == Opponent.team end ) - local extradata = match.extradata - ---@cast extradata table if match.isTeamMatch then -- Compute submatches match.submatches = Array.map( @@ -55,10 +53,11 @@ function CustomMatchGroupUtil.matchFromRecord(record) function(games) return CustomMatchGroupUtil.constructSubmatch(games) end ) - -- Extract submatch headers from extradata - for _, submatch in pairs(match.submatches) do + local extradata = match.extradata + ---@cast extradata table + Array.forEach(match.submatches, function (submatch) submatch.header = Table.extract(extradata, 'subgroup' .. submatch.subgroup .. 'header') - end + end) end return match @@ -86,7 +85,7 @@ function CustomMatchGroupUtil.groupBySubmatch(matchGames) local previousSubgroup = nil local currentGames = nil local submatchGames = {} - for _, game in ipairs(matchGames) do + Array.forEach(matchGames, function (game) if previousSubgroup == nil or previousSubgroup ~= game.subgroup then currentGames = {} table.insert(submatchGames, currentGames) @@ -94,7 +93,7 @@ function CustomMatchGroupUtil.groupBySubmatch(matchGames) end ---@cast currentGames -nil table.insert(currentGames, game) - end + end) return submatchGames end @@ -106,18 +105,19 @@ function CustomMatchGroupUtil.constructSubmatch(games) -- Sum up scores local scores = {} - for opponentIndex, _ in pairs(opponents) do + Array.forEach(opponents, function (_, opponentIndex) scores[opponentIndex] = 0 - end - for _, game in pairs(games) do + end) + + Array.forEach(games, function (game) if game.map and String.startsWith(game.map, 'Submatch') and not game.resultType then - for opponentIndex, score in pairs(scores) do - scores[opponentIndex] = score + (tonumber(game.scores[opponentIndex]) or 0) - end + Array.forEach(scores, function (score, index) + scores[index] = score + (tonumber(game.scores[index]) or 0) + end) elseif game.winner then scores[game.winner] = (scores[game.winner] or 0) + 1 end - end + end) -- Compute winner if all games have been played, skipped, or defaulted local allPlayed = Array.all(games, function(game) @@ -140,10 +140,10 @@ function CustomMatchGroupUtil.constructSubmatch(games) -- Set resultType and walkover if every game is a walkover local walkovers = {} local resultTypes = {} - for _, game in pairs(games) do + Array.forEach(games, function (game) resultTypes[game.resultType or ''] = true walkovers[game.walkover or ''] = true - end + end) local walkover local uniqueResult = Table.uniqueKey(resultTypes) if uniqueResult == 'default' then From 2f00cd10d3b283fd60b2bd4d3364ca2b1c44a9e9 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Mon, 16 Dec 2024 17:08:58 +0000 Subject: [PATCH 06/10] Remove unused import --- components/match2/wikis/hearthstone/match_summary.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index 0e2bd0603d..d75291a355 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -11,7 +11,6 @@ local DateExt = require('Module:Date/Ext') local FnUtil = require('Module:FnUtil') local Logic = require('Module:Logic') local Lua = require('Module:Lua') -local Table = require('Module:Table') local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') local HtmlWidgets = Lua.import('Module:Widget/Html/All') From 97e7421ae10e8d7fcebf4ba89e075c3688e567f6 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Tue, 17 Dec 2024 14:22:32 +0000 Subject: [PATCH 07/10] space --- components/match2/wikis/hearthstone/match_group_util_custom.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/match2/wikis/hearthstone/match_group_util_custom.lua b/components/match2/wikis/hearthstone/match_group_util_custom.lua index 19abc58d8d..61c5e73460 100644 --- a/components/match2/wikis/hearthstone/match_group_util_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_util_custom.lua @@ -69,7 +69,7 @@ end function CustomMatchGroupUtil.computeGameOpponents(game, matchOpponents) return Array.map(game.opponents, function (opponent, opponentIndex) return Table.merge(opponent, { - players = Array.map(game.opponents[opponentIndex].players or {},function (player, playerIndex) + players = Array.map(game.opponents[opponentIndex].players or {}, function (player, playerIndex) if Logic.isEmpty(player) then return nil end return Table.merge(matchOpponents[opponentIndex].players[playerIndex] or {}, player) end) From 7a68d5941a77f1b65b736cfdfc1d000c6acc1862 Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Tue, 17 Dec 2024 16:56:50 +0000 Subject: [PATCH 08/10] use new fields --- .../match2/commons/match_group_util.lua | 2 +- .../hearthstone/match_group_util_custom.lua | 86 ++++++++----------- .../wikis/hearthstone/match_summary.lua | 14 +-- 3 files changed, 38 insertions(+), 64 deletions(-) diff --git a/components/match2/commons/match_group_util.lua b/components/match2/commons/match_group_util.lua index c487cf6890..59b0abd724 100644 --- a/components/match2/commons/match_group_util.lua +++ b/components/match2/commons/match_group_util.lua @@ -207,7 +207,7 @@ MatchGroupUtil.types.Walkover = TypeUtil.literalUnion('l', 'ff', 'dq') ---@field map string? ---@field mapDisplayName string? ---@field mode string? ----@field opponents {players: table[]}[] +---@field opponents {status: string?, score: number, players: table[] }[] ---@field participants table ---@field resultType ResultType? ---@field scores number[] diff --git a/components/match2/wikis/hearthstone/match_group_util_custom.lua b/components/match2/wikis/hearthstone/match_group_util_custom.lua index 61c5e73460..2a17eaae28 100644 --- a/components/match2/wikis/hearthstone/match_group_util_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_util_custom.lua @@ -9,22 +9,21 @@ local Array = require('Module:Array') local Logic = require('Module:Logic') local Lua = require('Module:Lua') -local String = require('Module:StringUtils') local Table = require('Module:Table') local MatchGroupUtil = Lua.import('Module:MatchGroup/Util') -- can not use `Module:OpponentLibraries`/`Module:Opponent/Custom` to avoid loop local Opponent = Lua.import('Module:Opponent') +local SCORE_STATUS = 'S' + local CustomMatchGroupUtil = Table.deepCopy(MatchGroupUtil) ---@class HearthstoneMatchGroupUtilSubmatch ---@field games MatchGroupUtilGame[] ---@field opponents GameOpponent[] ----@field resultType ResultType ----@field scores table +---@field scores table ---@field subgroup number ----@field walkover WalkoverType ---@field winner number? ---@field header string? @@ -101,65 +100,48 @@ end ---@param games MatchGroupUtilGame[] ---@return HearthstoneMatchGroupUtilSubmatch function CustomMatchGroupUtil.constructSubmatch(games) - local opponents = Table.deepCopy(games[1].opponents) - - -- Sum up scores + local firstGame = games[1] + local opponents = Table.deepCopy(firstGame.opponents) local scores = {} - Array.forEach(opponents, function (_, opponentIndex) - scores[opponentIndex] = 0 - end) - - Array.forEach(games, function (game) - if game.map and String.startsWith(game.map, 'Submatch') and not game.resultType then - Array.forEach(scores, function (score, index) - scores[index] = score + (tonumber(game.scores[index]) or 0) - end) - elseif game.winner then - scores[game.winner] = (scores[game.winner] or 0) + 1 - end - end) + local winner = nil - -- Compute winner if all games have been played, skipped, or defaulted - local allPlayed = Array.all(games, function(game) - return game.winner ~= nil or game.resultType ~= nil - end) + if string.find(firstGame.map or '', '^[sS]ubmatch %d+$') then + Array.forEach(firstGame.opponents, function (opponent, opponentIndex) + if opponent.status and opponent.status ~= SCORE_STATUS then + scores[opponentIndex] = opponent.status + else + scores[opponentIndex] = opponent.score + end + end) + winner = firstGame.winner + else + local allPlayed = true + scores = {0, 0} + -- Sum up scores + Array.forEach(games, function (game) + if game.winner then + scores[game.winner] = (scores[game.winner] or 0) + 1 + end + allPlayed = game.winner ~= nil + end) - local resultType = nil - local winner = nil - if allPlayed then - local diff = (scores[1] or 0) - (scores[2] or 0) - if diff < 0 then - winner = 2 - elseif diff == 0 then - resultType = 'draw' - else - winner = 1 + if allPlayed then + local diff = (scores[1] or 0) - (scores[2] or 0) + if diff < 0 then + winner = 2 + elseif diff == 0 then + winner = 0 + else + winner = 1 + end end end - -- Set resultType and walkover if every game is a walkover - local walkovers = {} - local resultTypes = {} - Array.forEach(games, function (game) - resultTypes[game.resultType or ''] = true - walkovers[game.walkover or ''] = true - end) - local walkover - local uniqueResult = Table.uniqueKey(resultTypes) - if uniqueResult == 'default' then - resultType = 'default' - walkover = String.nilIfEmpty(Table.uniqueKey(walkovers)) or 'L' - elseif uniqueResult == 'np' then - resultType = 'np' - end - return { games = games, opponents = opponents, - resultType = resultType, scores = scores, subgroup = games[1].subgroup, - walkover = walkover, winner = winner, } end diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index d75291a355..256ee6a44c 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -99,19 +99,11 @@ function CustomMatchSummary.TeamSubMatchOpponnetRow(submatch) ---@param opponentIndex any ---@return Html local createScore = function(opponentIndex) - local isWinner = opponentIndex == submatch.winner or submatch.resultType == 'draw' - if submatch.resultType == 'default' then - return OpponentDisplay.BlockScore{ + local isWinner = opponentIndex == submatch.winner + return OpponentDisplay.BlockScore{ isWinner = isWinner, - scoreText = isWinner and 'W' or string.upper(submatch.walkover), + scoreText = (submatch.scores or {})[opponentIndex], } - end - - local score = submatch.resultType ~= 'np' and (submatch.scores or {})[opponentIndex] or nil - return OpponentDisplay.BlockScore{ - isWinner = isWinner, - scoreText = score, - } end return HtmlWidgets.Div{ From eabac47fe0eeea3ee6ea683011b10153a6a531a5 Mon Sep 17 00:00:00 2001 From: LuckeLucky <43279191+LuckeLucky@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:00:47 +0000 Subject: [PATCH 09/10] Update components/match2/commons/match_group_util.lua Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- components/match2/commons/match_group_util.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/match2/commons/match_group_util.lua b/components/match2/commons/match_group_util.lua index 59b0abd724..73e321fce2 100644 --- a/components/match2/commons/match_group_util.lua +++ b/components/match2/commons/match_group_util.lua @@ -207,7 +207,7 @@ MatchGroupUtil.types.Walkover = TypeUtil.literalUnion('l', 'ff', 'dq') ---@field map string? ---@field mapDisplayName string? ---@field mode string? ----@field opponents {status: string?, score: number, players: table[] }[] +---@field opponents {status: string?, score: number?, players: table[]}[] ---@field participants table ---@field resultType ResultType? ---@field scores number[] From 1a146c54b6a9d03d2649f9c9077dff806cd70b3f Mon Sep 17 00:00:00 2001 From: LuckeLucky Date: Wed, 18 Dec 2024 17:56:30 +0000 Subject: [PATCH 10/10] improve handling opponent handling --- .../hearthstone/match_group_util_custom.lua | 92 +++++++++---------- .../wikis/hearthstone/match_summary.lua | 51 ++-------- 2 files changed, 53 insertions(+), 90 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_util_custom.lua b/components/match2/wikis/hearthstone/match_group_util_custom.lua index 2a17eaae28..6f3b2bbdab 100644 --- a/components/match2/wikis/hearthstone/match_group_util_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_util_custom.lua @@ -9,8 +9,10 @@ local Array = require('Module:Array') local Logic = require('Module:Logic') local Lua = require('Module:Lua') +local Operator = require('Module:Operator') local Table = require('Module:Table') +local MatchGroupInputUtil = Lua.import('Module:MatchGroup/Input/Util') local MatchGroupUtil = Lua.import('Module:MatchGroup/Util') -- can not use `Module:OpponentLibraries`/`Module:Opponent/Custom` to avoid loop local Opponent = Lua.import('Module:Opponent') @@ -19,10 +21,12 @@ local SCORE_STATUS = 'S' local CustomMatchGroupUtil = Table.deepCopy(MatchGroupUtil) +---@class HearthstoneMatchGroupUtilGameOpponent: GameOpponent +---@field placement number? + ---@class HearthstoneMatchGroupUtilSubmatch ---@field games MatchGroupUtilGame[] ----@field opponents GameOpponent[] ----@field scores table +---@field opponents HearthstoneMatchGroupUtilGameOpponent[] ---@field subgroup number ---@field winner number? ---@field header string? @@ -45,20 +49,22 @@ function CustomMatchGroupUtil.matchFromRecord(record) return opponent.type == Opponent.team end ) - if match.isTeamMatch then - -- Compute submatches - match.submatches = Array.map( - CustomMatchGroupUtil.groupBySubmatch(match.games), - function(games) return CustomMatchGroupUtil.constructSubmatch(games) end - ) - - local extradata = match.extradata - ---@cast extradata table - Array.forEach(match.submatches, function (submatch) - submatch.header = Table.extract(extradata, 'subgroup' .. submatch.subgroup .. 'header') - end) + if not match.isTeamMatch then + return match end + -- Compute submatches + match.submatches = Array.map( + CustomMatchGroupUtil.groupBySubmatch(match.games), + function(games) return CustomMatchGroupUtil.constructSubmatch(games) end + ) + + local extradata = match.extradata + ---@cast extradata table + Array.forEach(match.submatches, function (submatch) + submatch.header = Table.extract(extradata, 'subgroup' .. submatch.subgroup .. 'header') + end) + return match end @@ -102,46 +108,34 @@ end function CustomMatchGroupUtil.constructSubmatch(games) local firstGame = games[1] local opponents = Table.deepCopy(firstGame.opponents) - local scores = {} - local winner = nil - - if string.find(firstGame.map or '', '^[sS]ubmatch %d+$') then - Array.forEach(firstGame.opponents, function (opponent, opponentIndex) - if opponent.status and opponent.status ~= SCORE_STATUS then - scores[opponentIndex] = opponent.status - else - scores[opponentIndex] = opponent.score - end - end) - winner = firstGame.winner - else - local allPlayed = true - scores = {0, 0} - -- Sum up scores - Array.forEach(games, function (game) - if game.winner then - scores[game.winner] = (scores[game.winner] or 0) + 1 - end - allPlayed = game.winner ~= nil - end) - - if allPlayed then - local diff = (scores[1] or 0) - (scores[2] or 0) - if diff < 0 then - winner = 2 - elseif diff == 0 then - winner = 0 - else - winner = 1 - end - end + local isSubmatch = string.find(firstGame.map or '', '^[sS]ubmatch %d+$') + if isSubmatch then + games = {firstGame} end + ---@param opponent table + local getOpponentScoreAndStatus = function(opponent, opponentIndex) + local statuses = Array.unique(Array.map(games, function(game) + return game.opponents[opponentIndex].status + end)) + opponent.status = #statuses == 1 and statuses[1] ~= SCORE_STATUS and statuses[1] or SCORE_STATUS + opponent.score = isSubmatch and firstGame.scores[opponentIndex] or Array.reduce(Array.map(games, function(game) + return (game.winner == opponentIndex and 1 or 0) + end), Operator.add) + end + + Array.forEach(opponents, getOpponentScoreAndStatus) + + local allPlayed = Array.all(games, function (game) return game.winner ~= nil end) + local winner = allPlayed and MatchGroupInputUtil.getWinner('', nil, opponents) or nil + Array.forEach(opponents, function(opponent, opponentIndex) + opponent.placement = MatchGroupInputUtil.placementFromWinner('', winner, opponentIndex) + end) + return { games = games, opponents = opponents, - scores = scores, - subgroup = games[1].subgroup, + subgroup = firstGame.subgroup, winner = winner, } end diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index 256ee6a44c..bde923399c 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -20,7 +20,6 @@ local WidgetUtil = Lua.import('Module:Widget/Util') local OpponentLibraries = require('Module:OpponentLibraries') local Opponent = OpponentLibraries.Opponent -local OpponentDisplay = OpponentLibraries.OpponentDisplay local CustomMatchSummary = {} @@ -79,52 +78,22 @@ function CustomMatchSummary._submatchHasDetails(submatch) end ---@param submatch HearthstoneMatchGroupUtilSubmatch ----@return Widget +---@return Html function CustomMatchSummary.TeamSubMatchOpponnetRow(submatch) local opponents = submatch.opponents or {{}, {}} - - local createOpponent = function(opponentIndex) - local players = (opponents[opponentIndex] or {}).players or {} + Array.forEach(opponents, function (opponent, opponentIndex) + local players = opponent.players or {} if Logic.isEmpty(players) then players = Opponent.tbd(Opponent.solo).players end - return OpponentDisplay.BlockOpponent{ - flip = opponentIndex == 1, - opponent = {players = players, type = Opponent.partyTypes[math.max(#players, 1)]}, - showLink = true, - overflow = 'ellipsis', - } - end - - ---@param opponentIndex any - ---@return Html - local createScore = function(opponentIndex) - local isWinner = opponentIndex == submatch.winner - return OpponentDisplay.BlockScore{ - isWinner = isWinner, - scoreText = (submatch.scores or {})[opponentIndex], - } - end + ---@cast players -nil + opponent.type = Opponent.partyTypes[math.max(#players, 1)] + opponent.players = players + end) - return HtmlWidgets.Div{ - classes = {'brkts-popup-header-dev'}, - css = {['justify-content'] = 'center', margin = 'auto'}, - children = WidgetUtil.collect( - HtmlWidgets.Div{ - classes = {'brkts-popup-header-opponent', 'brkts-popup-header-opponent-left'}, - children = { - createOpponent(1), - createScore(1):addClass('brkts-popup-header-opponent-score-left'), - }, - }, - HtmlWidgets.Div{ - classes = {'brkts-popup-header-opponent', 'brkts-popup-header-opponent-right'}, - children = { - createScore(2):addClass('brkts-popup-header-opponent-score-right'), - createOpponent(2), - }, - } - ) + return HtmlWidgets.Div { + css = {margin = 'auto'}, + children = MatchSummary.createDefaultHeader({opponents = opponents}):create() } end