From 21a41b8a8f97da4a6c9ede24cef101ced9fc247e Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:38:45 +0200 Subject: [PATCH 01/27] Create Match Summary for hearthstone --- .../wikis/hearthstone/match_summary.lua | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 components/match2/wikis/hearthstone/match_summary.lua diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua new file mode 100644 index 00000000000..aef22c1541b --- /dev/null +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -0,0 +1,160 @@ +--- +-- @Liquipedia +-- wiki=hearthstone +-- page=Module:MatchSummary +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Array = require('Module:Array') +local DateExt = require('Module:Date/Ext') +local CharacterIcon = require('Module:CharacterIcon') +local Icon = require('Module:Icon') +local Logic = require('Module:Logic') +local Lua = require('Module:Lua') + +local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') +local MatchSummary = Lua.import('Module:MatchSummary/Base') + +local OpponentLibraries = require('Module:OpponentLibraries') +local Opponent = OpponentLibraries.Opponent + +local ICONS = { + winner = Icon.makeIcon{iconName = 'winner', color = 'forest-green-text', size = 'initial'}, + draw = Icon.makeIcon{iconName = 'draw', color = 'bright-sun-text', size = 'initial'}, + loss = Icon.makeIcon{iconName = 'loss', color = 'cinnabar-text', size = 'initial'}, + empty = '[[File:NoCheck.png|link=|16px]]', +} + +local CustomMatchSummary = {} + +---@param args table +---@return Html +function CustomMatchSummary.getByMatchId(args) + return MatchSummary.defaultGetByMatchId(CustomMatchSummary, args, { + width = CustomMatchSummary._determineWidth, + teamStyle = 'bracket', + }) +end + +---@param match MatchGroupUtilMatch +---@return string +function CustomMatchSummary._determineWidth(match) + return '350px' +end + +---@param match MatchGroupUtilMatch +---@return MatchSummaryBody +function CustomMatchSummary.createBody(match) + local body = MatchSummary.Body() + + if match.dateIsExact or (match.timestamp ~= DateExt.defaultTimestamp) then + body:addRow(MatchSummary.Row():addElement( + DisplayHelper.MatchCountdownBlock(match) + )) + end + + if not CustomMatchSummary._isSolo(match) then + return body + end + + Array.forEach(match.games, function(game) + if not game.map and not game.winner then return end + local row = MatchSummary.Row() + :addClass('brkts-popup-body-game') + :css('font-size', '0.75rem') + :css('padding', '4px') + :css('min-height', '24px') + + CustomMatchSummary._createGame(row, game, { + opponents = match.opponents, + game = match.game, + }) + body:addRow(row) + end) + + return body +end + +---@param match MatchGroupUtilMatch +---@param footer MatchSummaryFooter +---@return MatchSummaryFooter +function CustomMatchSummary.addToFooter(match, footer) + footer = MatchSummary.addVodsToFooter(match, footer) + + return footer +end + +---@param match MatchGroupUtilMatch +---@return boolean +function CustomMatchSummary._isSolo(match) + if type(match.opponents[1]) ~= 'table' or type(match.opponents[2]) ~= 'table' then + return false + end + return match.opponents[1].type == Opponent.solo and match.opponents[2].type == Opponent.solo +end + +---@param game MatchGroupUtilGame +---@param paricipantId string +---@return {displayName: string?, pageName: string?, flag: string?, character: string?} +function CustomMatchSummary._getPlayerData(game, paricipantId) + if not game or not game.participants then + return {} + end + return game.participants[paricipantId] or {} +end + +---@param row MatchSummaryRow +---@param game MatchGroupUtilGame +---@param props {game: string?, opponents: standardOpponent[]} +function CustomMatchSummary._createGame(row, game, props) + game.extradata = game.extradata or {} + + local char1 = CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '1_1').character, false) + local char2 = CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '2_1').character, true) + + row:addElement(char1:css('flex', '1 1 35%'):css('text-align', 'right')) + row:addElement(CustomMatchSummary._createCheckMark(game.winner, 1)) + row:addElement(CustomMatchSummary._createCheckMark(game.winner, 2)) + row:addElement(char2:css('flex', '1 1 35%')) +end + +---@param char string? +---@param reverse boolean? +---@return Html +function CustomMatchSummary._createCharacterDisplay(character, reverse) + if not character then + return + end + + local characterDisplay = mw.html.create('span'):addClass('draft faction') + local charIcon = CharacterIcon.Icon{ + character = character, + size = '18px', + } + if reverse then + characterDisplay:wikitext(charIcon):wikitext(' '):wikitext(character) + else + characterDisplay:wikitext(character):wikitext(' '):wikitext(charIcon) + end + return characterDisplay +end + +---@param winner integer|string +---@param opponentIndex integer +---@return Html +function CustomMatchSummary._createCheckMark(winner, opponentIndex) + return mw.html.create('div') + :addClass('brkts-popup-spaced') + :css('line-height', '17px') + :css('margin-left', '2%') + :css('margin-right', '2%') + :wikitext( + winner == opponentIndex and ICONS.winner + or winner == 0 and ICONS.draw + or Logic.isNotEmpty(winner) and ICONS.loss + or ICONS.empty + ) +end + +return CustomMatchSummary From 220dd9d9c3a850a343113e00c11c76875376ce6c Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:40:35 +0200 Subject: [PATCH 02/27] Create Match Group Input for Hearthstone --- .../hearthstone/match_group_input_custom.lua | 447 ++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 components/match2/wikis/hearthstone/match_group_input_custom.lua diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua new file mode 100644 index 00000000000..5cf30878496 --- /dev/null +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -0,0 +1,447 @@ +--- +-- @Liquipedia +-- wiki=hearthstone +-- page=Module:MatchGroup/Input/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Array = require('Module:Array') +local CharacterStandardization = mw.loadData('Module:CharacterStandardization') +local DateExt = require('Module:Date/Ext') +local FnUtil = require('Module:FnUtil') +local Game = require('Module:Game') +local Json = require('Module:Json') +local Logic = require('Module:Logic') +local Lua = require('Module:Lua') +local String = require('Module:StringUtils') +local Table = require('Module:Table') +local Variables = require('Module:Variables') + +local MatchGroupInput = Lua.import('Module:MatchGroup/Input') +local Opponent = Lua.import('Module:Opponent') +local Streams = Lua.import('Module:Links/Stream') + +local ALLOWED_STATUSES = {'W', 'FF', 'DQ', 'L'} +local CONVERT_STATUS_INPUT = {W = 'W', FF = 'FF', L = 'L', DQ = 'DQ', ['-'] = 'L'} +local DEFAULT_LOSS_STATUSES = {'FF', 'L', 'DQ'} +local MAX_NUM_OPPONENTS = 2 +local MAX_NUM_PLAYERS = 10 +local DEFAULT_BESTOF = 99 + +local DUMMY_MAP_NAME = 'null' -- Is set in Template:Map when |map= is empty. + +local CustomMatchGroupInput = {} + +--- called from Module:MatchGroup +---@param match table +---@param options table? +---@return table +function CustomMatchGroupInput.processMatch(match, options) + Table.mergeInto(match, MatchGroupInput.readDate(match.date)) + CustomMatchGroupInput._getOpponents(match) + CustomMatchGroupInput._getTournamentVars(match) + CustomMatchGroupInput._verifyMaps(match) + CustomMatchGroupInput._processMaps(match) + CustomMatchGroupInput._calculateWinner(match) + CustomMatchGroupInput._updateFinished(match) + match.stream = Streams.processStreams(match) + CustomMatchGroupInput._getVod(match) + return match +end + +CustomMatchGroupInput.processMap = FnUtil.identity + +---@param match table +function CustomMatchGroupInput._getTournamentVars(match) + match = MatchGroupInput.getCommonTournamentVars(match) + + match.mode = Variables.varDefault('tournament_mode', 'singles') +end + +---@param match table +function CustomMatchGroupInput._updateFinished(match) + match.finished = Logic.nilOr(Logic.readBoolOrNil(match.finished), Logic.isNotEmpty(match.winner)) + if match.finished then + return + end + + -- Match is automatically marked finished upon page edit after a + -- certain amount of time (depending on whether the date is exact) + local currentUnixTime = os.time(os.date('!*t') --[[@as osdateparam]]) + local threshold = match.dateexact and 30800 or 86400 + match.finished = match.timestamp + threshold < currentUnixTime +end + +---@param match table +function CustomMatchGroupInput._getVod(match) + match.stream = Streams.processStreams(match) + match.vod = Logic.emptyOr(match.vod) +end + +-- This function is used to discard maps that are none-relevant ones +-- Template:Map sets a default map name because sometimes +-- we have results without knowledge of which map it was planned on. +---@param match table +function CustomMatchGroupInput._verifyMaps(match) + for key, map in Table.iter.pairsByPrefix(match, 'map') do + if CustomMatchGroupInput._discardMap(map) then + match[key] = nil + elseif map.map == DUMMY_MAP_NAME then + match[key].map = '' + end + end +end + +-- Check if a map should be discarded due to not containing anything relevant +-- DUMMY_MAP_NAME variable must the match the default value in Template:Map +---@param map table +---@return boolean +function CustomMatchGroupInput._discardMap(map) + return map.map == DUMMY_MAP_NAME + and not map.score1 and not map.score2 and not map.winner and not map.o1p1 and not map.o2p1 +end + +---@param match table +function CustomMatchGroupInput._processMaps(match) + for _, _, mapIndex in Table.iter.pairsByPrefix(match, 'map') do + CustomMatchGroupInput._mapInput(match, mapIndex) + end +end + +---@param match table +function CustomMatchGroupInput._calculateWinner(match) + local bestof = match.bestof or DEFAULT_BESTOF + local numberOfOpponents = 0 + + for opponentIndex = 1, MAX_NUM_OPPONENTS do + local opponent = match['opponent' .. opponentIndex] + if Logic.isEmpty(opponent) then + break + end + + numberOfOpponents = numberOfOpponents + 1 + + if Logic.isNotEmpty(match.walkover) then + if Logic.isNumeric(match.walkover) then + local walkover = tonumber(match.walkover) + if walkover == opponentIndex then + match.winner = opponentIndex + match.walkover = 'FF' + opponent.status = 'W' + elseif walkover == 0 then + match.winner = 0 + match.walkover = 'FF' + opponent.status = 'FF' + else + local score = string.upper(opponent.score or '') + opponent.status = CONVERT_STATUS_INPUT[score] or 'FF' + end + elseif Table.includes(ALLOWED_STATUSES, string.upper(match.walkover)) then + if tonumber(match.winner or 0) == opponentIndex then + opponent.status = 'W' + else + opponent.status = CONVERT_STATUS_INPUT[string.upper(match.walkover)] or 'L' + end + else + local score = string.upper(opponent.score or '') + opponent.status = CONVERT_STATUS_INPUT[score] or 'L' + match.walkover = 'L' + end + opponent.score = -1 + match.finished = true + match.resulttype = 'default' + elseif CONVERT_STATUS_INPUT[string.upper(opponent.score or '')] then + if string.upper(opponent.score) == 'W' then + match.winner = opponentIndex + match.finished = true + opponent.score = -1 + opponent.status = 'W' + else + local score = string.upper(opponent.score) + match.finished = true + match.walkover = CONVERT_STATUS_INPUT[score] + opponent.status = CONVERT_STATUS_INPUT[score] + opponent.score = -1 + end + match.resulttype = 'default' + else + opponent.status = 'S' + opponent.score = tonumber(opponent.score) or tonumber(opponent.autoscore) or -1 + if opponent.score > bestof / 2 then + match.finished = Logic.emptyOr(match.finished, true) + match.winner = tonumber(match.winner) or opponentIndex + end + end + end + + CustomMatchGroupInput._determineWinnerIfMissing(match) + + for opponentIndex = 1, numberOfOpponents do + local opponent = match['opponent' .. opponentIndex] + if match.winner == 'draw' or tonumber(match.winner) == 0 or + (match.opponent1.score == bestof / 2 and match.opponent1.score == match.opponent2.score) then + match.finished = true + match.winner = 0 + match.resulttype = 'draw' + end + + if tonumber(match.winner) == opponentIndex or + match.resulttype == 'draw' then + opponent.placement = 1 + elseif Logic.isNumeric(match.winner) then + opponent.placement = 2 + end + end +end + +---@param match table +function CustomMatchGroupInput._determineWinnerIfMissing(match) + if not Logic.readBool(match.finished) or Logic.isNotEmpty(match.winner) then + return + end + + local scores = Array.mapIndexes(function(opponentIndex) + local opponent = match['opponent' .. opponentIndex] + if not opponent then + return nil + end + return match['opponent' .. opponentIndex].score or -1 + end + ) + local maxScore = math.max(unpack(scores) or 0) + -- if we have a positive score and the match is finished we also have a winner + if maxScore > 0 then + local maxIndexFound = false + for opponentIndex, score in pairs(scores) do + if maxIndexFound and score == maxScore then + match.winner = 0 + break + elseif score == maxScore then + maxIndexFound = true + match.winner = opponentIndex + end + end + end +end + +---@param match table +---@return table +function CustomMatchGroupInput._getOpponents(match) + -- read opponents and ignore empty ones + for opponentIndex = 1, MAX_NUM_OPPONENTS do + -- read opponent + local opponent = match['opponent' .. opponentIndex] or {} + if Logic.isNotEmpty(opponent) then + CustomMatchGroupInput.processOpponent(opponent, match.timestamp) + end + match['opponent' .. opponentIndex] = opponent + + if opponent.type == Opponent.team and Logic.isNotEmpty(opponent.name) then + MatchGroupInput.readPlayersOfTeam(match, opponentIndex, opponent.name, { + resolveRedirect = true, + applyUnderScores = true, + maxNumPlayers = MAX_NUM_PLAYERS, + }) + end + end + + return match +end + +---@param record table +---@param timestamp number +function CustomMatchGroupInput.processOpponent(record, timestamp) + local opponent = Opponent.readOpponentArgs(record) or Opponent.blank() + + -- Convert byes to literals + if Opponent.isBye(opponent) then + opponent = {type = Opponent.literal, name = 'BYE'} + end + + + local teamTemplateDate = timestamp + if teamTemplateDate == DateExt.defaultTimestamp then + teamTemplateDate = Variables.varDefaultMulti('tournament_enddate', 'tournament_startdate', NOW) + end + + Opponent.resolve(opponent, teamTemplateDate, {syncPlayer = true}) + MatchGroupInput.mergeRecordWithOpponent(record, opponent) +end + +---@param match table +---@param mapIndex integer +function CustomMatchGroupInput._mapInput(match, mapIndex) + local map = Json.parseIfString(match['map' .. mapIndex]) + if String.isNotEmpty(map.map) and map.map ~= 'TBD' then + map.map = mw.ext.TeamLiquidIntegration.resolve_redirect(map.map) + end + + -- set initial extradata for maps + map.extradata = { + comment = map.comment, + header = map.header, + } + map.game = match.game + map.mode = match.mode + + -- determine score, resulttype, walkover and winner + map = CustomMatchGroupInput._mapWinnerProcessing(map) + + -- Init score if match started and map info is present + if not match.opponent1.autoscore and not match.opponent2.autoscore + and map.map and map.map ~= 'TBD' + and match.timestamp < os.time(os.date('!*t') --[[@as osdateparam]]) then + match.opponent1.autoscore = 0 + match.opponent2.autoscore = 0 + end + + if Logic.isEmpty(map.resulttype) and map.scores[1] and map.scores[2] then + match.opponent1.autoscore = (match.opponent1.autoscore or 0) + (map.winner == 1 and 1 or 0) + match.opponent2.autoscore = (match.opponent2.autoscore or 0) + (map.winner == 2 and 1 or 0) + end + + CustomMatchGroupInput.processPlayersMapData(map, match, 2) + + match['map' .. mapIndex] = map +end + +---@param map table +---@return table +function CustomMatchGroupInput._mapWinnerProcessing(map) + map.scores = {} + local hasManualScores = false + local indexedScores = {} + for scoreIndex = 1, MAX_NUM_OPPONENTS do + -- read scores + local score = map['score' .. scoreIndex] + local obj = {} + if Logic.isNotEmpty(score) then + hasManualScores = true + score = CONVERT_STATUS_INPUT[score] or score + if Logic.isNumeric(score) then + obj.status = 'S' + obj.score = score + elseif Table.includes(ALLOWED_STATUSES, score) then + obj.status = score + obj.score = -1 + end + table.insert(map.scores, score) + indexedScores[scoreIndex] = obj + else + break + end + end + + local winnerInput = tonumber(map.winner) + if Logic.isNotEmpty(map.walkover) then + local walkoverInput = tonumber(map.walkover) + if walkoverInput == 1 or walkoverInput == 2 or walkoverInput == 0 then + map.winner = walkoverInput + end + map.walkover = Table.includes(ALLOWED_STATUSES, map.walkover) and map.walkover or 'L' + map.scores = {-1, -1} + map.resulttype = 'default' + + return map + end + + if hasManualScores then + for scoreIndex, _ in Table.iter.spairs(indexedScores, CustomMatchGroupInput._placementSortFunction) do + if not tonumber(map.winner) then + map.winner = scoreIndex + else + break + end + end + + return map + end + + if map.winner == 'skip' then + map.scores = {-1, -1} + map.resulttype = 'np' + elseif winnerInput == 1 then + map.scores = {1, 0} + elseif winnerInput == 2 then + map.scores = {0, 1} + elseif winnerInput == 0 or map.winner == 'draw' then + map.scores = {0, 0} + map.resulttype = 'draw' + end + + return map +end + +---@param map table +---@param match table +---@param numberOfOpponents integer +function CustomMatchGroupInput.processPlayersMapData(map, match, numberOfOpponents) + local participants = {} + for opponentIndex = 1, numberOfOpponents do + local opponent = match['opponent' .. opponentIndex] + if opponent.type ~= Opponent.literal then + local playerCount = Opponent.partySize(opponent.type) or MAX_NUM_PLAYERS + for playerIndex = 1, playerCount do + local player = opponent.match2players[playerIndex] + if player then + local playerData = CustomMatchGroupInput._processPlayerMapData(player, map, opponentIndex, playerIndex) + participants[opponentIndex .. '_' .. playerIndex] = playerData + end + end + end + end + + map.participants = participants +end + +---@param player table +---@param map table +---@param opponentIndex integer +---@param playerIndex integer +---@return {character: string?, player: string} +function CustomMatchGroupInput._processPlayerMapData(player, map, opponentIndex, playerIndex) + local getCharacterName = FnUtil.curry(MatchGroupInput.getCharacterName, CharacterStandardization) + + local character = getCharacterName(map['o' .. opponentIndex .. 'p' .. playerIndex]) + + return { + character = character, + player = player.name, + } +end + +-- function to sort out winner/placements +---@param tbl table +---@param key1 string +---@param key2 string +---@return boolean +function CustomMatchGroupInput._placementSortFunction(tbl, key1, key2) + local opponent1 = tbl[key1] + local opponent2 = tbl[key2] + local opponent1Norm = opponent1.status == 'S' + local opponent2Norm = opponent2.status == 'S' + if opponent1Norm then + if opponent2Norm then + return tonumber(opponent1.score) > tonumber(opponent2.score) + else + return true + end + else + if opponent2Norm then + return false + elseif opponent1.status == 'W' then + return true + elseif Table.includes(DEFAULT_LOSS_STATUSES, opponent1.status) then + return false + elseif opponent2.status == 'W' then + return false + elseif Table.includes(DEFAULT_LOSS_STATUSES, opponent2.status) then + return true + else + return true + end + end +end + +return CustomMatchGroupInput From ed81878ba22555ddd6d67a58fd745b31137b22f3 Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:41:04 +0200 Subject: [PATCH 03/27] Create get_match_group_copy_paste_wiki.lua --- .../get_match_group_copy_paste_wiki.lua | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua diff --git a/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua b/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua new file mode 100644 index 00000000000..cd807fcf338 --- /dev/null +++ b/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua @@ -0,0 +1,55 @@ +--- +-- @Liquipedia +-- wiki=hearthstone +-- page=Module:GetMatchGroupCopyPaste/wiki +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Array = require('Module:Array') +local Class = require('Module:Class') +local Logic = require('Module:Logic') +local Lua = require('Module:Lua') + +local BaseCopyPaste = Lua.import('Module:GetMatchGroupCopyPaste/wiki/Base') + +---WikiSpecific Code for MatchList and Bracket Code Generators +---@class HearthstoneMatchCopyPaste: Match2CopyPasteBase +local WikiCopyPaste = Class.new(BaseCopyPaste) + +local INDENT = WikiCopyPaste.Indent + +---returns the Code for a Match, depending on the input +---@param bestof integer +---@param mode string +---@param index integer +---@param opponents integer +---@param args table +---@return string +function WikiCopyPaste.getMatchCode(bestof, mode, index, opponents, args) + local showScore = Logic.nilOr(Logic.readBool(args.score), true) + + local lines = Array.extend( + '{{Match|bestof=' .. bestof, + INDENT .. '|date=', + INDENT .. '|twitch=|vod=', + Array.map(Array.range(1, opponents), function(opponentIndex) + return INDENT .. '|opponent' .. opponentIndex .. '=' .. WikiCopyPaste.getOpponent(mode, showScore) + end), + Array.map(Array.range(1, bestof), function(mapIndex) + return INDENT .. '|map' .. mapIndex .. WikiCopyPaste._getMap(mode) + end), + '}}' + ) + + return table.concat(lines, '\n') +end + +--subfunction used to generate code for the Map template, depending on the type of opponent +---@param mode string +---@return string +function WikiCopyPaste._getMap(mode) + return '={{Map|o1p1=|o2p1=|winner=}}' +end + +return WikiCopyPaste From dbd805d1a613f7a26e64fa2be0df0907dd7ea30b Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:51:56 +0200 Subject: [PATCH 04/27] Update match_group_input_custom.lua --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 5cf30878496..4f64c471342 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -10,7 +10,6 @@ local Array = require('Module:Array') local CharacterStandardization = mw.loadData('Module:CharacterStandardization') local DateExt = require('Module:Date/Ext') local FnUtil = require('Module:FnUtil') -local Game = require('Module:Game') local Json = require('Module:Json') local Logic = require('Module:Logic') local Lua = require('Module:Lua') @@ -258,8 +257,7 @@ function CustomMatchGroupInput.processOpponent(record, timestamp) if Opponent.isBye(opponent) then opponent = {type = Opponent.literal, name = 'BYE'} end - - + local teamTemplateDate = timestamp if teamTemplateDate == DateExt.defaultTimestamp then teamTemplateDate = Variables.varDefaultMulti('tournament_enddate', 'tournament_startdate', NOW) From 256a7ba49afd3f1ba0a5efee079af37f120c6a85 Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:21:22 +0200 Subject: [PATCH 05/27] Update match_group_input_custom.lua --- .../hearthstone/match_group_input_custom.lua | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 4f64c471342..8738bcef01b 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -10,6 +10,7 @@ local Array = require('Module:Array') local CharacterStandardization = mw.loadData('Module:CharacterStandardization') local DateExt = require('Module:Date/Ext') local FnUtil = require('Module:FnUtil') +local Game = require('Module:Game') local Json = require('Module:Json') local Logic = require('Module:Logic') local Lua = require('Module:Lua') @@ -30,6 +31,8 @@ local DEFAULT_BESTOF = 99 local DUMMY_MAP_NAME = 'null' -- Is set in Template:Map when |map= is empty. +local NOW = os.time(os.date('!*t') --[[@as osdateparam]]) + local CustomMatchGroupInput = {} --- called from Module:MatchGroup @@ -67,9 +70,8 @@ function CustomMatchGroupInput._updateFinished(match) -- Match is automatically marked finished upon page edit after a -- certain amount of time (depending on whether the date is exact) - local currentUnixTime = os.time(os.date('!*t') --[[@as osdateparam]]) local threshold = match.dateexact and 30800 or 86400 - match.finished = match.timestamp + threshold < currentUnixTime + match.finished = match.timestamp + threshold < NOW end ---@param match table @@ -84,23 +86,12 @@ end ---@param match table function CustomMatchGroupInput._verifyMaps(match) for key, map in Table.iter.pairsByPrefix(match, 'map') do - if CustomMatchGroupInput._discardMap(map) then - match[key] = nil - elseif map.map == DUMMY_MAP_NAME then - match[key].map = '' + if map.map == DUMMY_MAP_NAME then + match[key].map = nil end end end --- Check if a map should be discarded due to not containing anything relevant --- DUMMY_MAP_NAME variable must the match the default value in Template:Map ----@param map table ----@return boolean -function CustomMatchGroupInput._discardMap(map) - return map.map == DUMMY_MAP_NAME - and not map.score1 and not map.score2 and not map.winner and not map.o1p1 and not map.o2p1 -end - ---@param match table function CustomMatchGroupInput._processMaps(match) for _, _, mapIndex in Table.iter.pairsByPrefix(match, 'map') do @@ -257,7 +248,8 @@ function CustomMatchGroupInput.processOpponent(record, timestamp) if Opponent.isBye(opponent) then opponent = {type = Opponent.literal, name = 'BYE'} end - + + local teamTemplateDate = timestamp if teamTemplateDate == DateExt.defaultTimestamp then teamTemplateDate = Variables.varDefaultMulti('tournament_enddate', 'tournament_startdate', NOW) @@ -287,9 +279,7 @@ function CustomMatchGroupInput._mapInput(match, mapIndex) map = CustomMatchGroupInput._mapWinnerProcessing(map) -- Init score if match started and map info is present - if not match.opponent1.autoscore and not match.opponent2.autoscore - and map.map and map.map ~= 'TBD' - and match.timestamp < os.time(os.date('!*t') --[[@as osdateparam]]) then + if not match.opponent1.autoscore and not match.opponent2.autoscore and map.winner then match.opponent1.autoscore = 0 match.opponent2.autoscore = 0 end @@ -361,16 +351,18 @@ function CustomMatchGroupInput._mapWinnerProcessing(map) map.resulttype = 'np' elseif winnerInput == 1 then map.scores = {1, 0} + map.winner = 1 elseif winnerInput == 2 then map.scores = {0, 1} + map.winner = 2 elseif winnerInput == 0 or map.winner == 'draw' then + map.winner = 0 map.scores = {0, 0} map.resulttype = 'draw' end return map end - ---@param map table ---@param match table ---@param numberOfOpponents integer From 1dc5cd54ff90c2dbbb26ff1957e5197e7b460cff Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:22:46 +0200 Subject: [PATCH 06/27] Update match_group_input_custom.lua --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 8738bcef01b..fdfffa0f085 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -10,7 +10,6 @@ local Array = require('Module:Array') local CharacterStandardization = mw.loadData('Module:CharacterStandardization') local DateExt = require('Module:Date/Ext') local FnUtil = require('Module:FnUtil') -local Game = require('Module:Game') local Json = require('Module:Json') local Logic = require('Module:Logic') local Lua = require('Module:Lua') @@ -248,8 +247,7 @@ function CustomMatchGroupInput.processOpponent(record, timestamp) if Opponent.isBye(opponent) then opponent = {type = Opponent.literal, name = 'BYE'} end - - + local teamTemplateDate = timestamp if teamTemplateDate == DateExt.defaultTimestamp then teamTemplateDate = Variables.varDefaultMulti('tournament_enddate', 'tournament_startdate', NOW) From 33abedf42b29667a292b4a54b5f86419a5f97e15 Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:23:21 +0200 Subject: [PATCH 07/27] Update match_summary.lua --- components/match2/wikis/hearthstone/match_summary.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index aef22c1541b..b553c83d3c4 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -110,8 +110,10 @@ end function CustomMatchSummary._createGame(row, game, props) game.extradata = game.extradata or {} - local char1 = CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '1_1').character, false) - local char2 = CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '2_1').character, true) + local char1 = + CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '1_1').character, false) + local char2 = + CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '2_1').character, true) row:addElement(char1:css('flex', '1 1 35%'):css('text-align', 'right')) row:addElement(CustomMatchSummary._createCheckMark(game.winner, 1)) From 5c670bb0bb46bec61303f226497e88251141fcbe Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:24:13 +0200 Subject: [PATCH 08/27] Update match_summary.lua --- components/match2/wikis/hearthstone/match_summary.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index b553c83d3c4..048f5643791 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -110,9 +110,9 @@ end function CustomMatchSummary._createGame(row, game, props) game.extradata = game.extradata or {} - local char1 = + local char1 = CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '1_1').character, false) - local char2 = + local char2 = CustomMatchSummary._createCharacterDisplay(CustomMatchSummary._getPlayerData(game, '2_1').character, true) row:addElement(char1:css('flex', '1 1 35%'):css('text-align', 'right')) From d1cd7d7c12f0bb4782e3b110683cac0225f7e363 Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:33:14 +0200 Subject: [PATCH 09/27] Update match_summary.lua --- 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 048f5643791..92ec97de46e 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -132,7 +132,7 @@ function CustomMatchSummary._createCharacterDisplay(character, reverse) local characterDisplay = mw.html.create('span'):addClass('draft faction') local charIcon = CharacterIcon.Icon{ character = character, - size = '18px', + size = '64px', } if reverse then characterDisplay:wikitext(charIcon):wikitext(' '):wikitext(character) From 48afbc542a049890a1f89c05879537909fe75bca Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 09:42:29 +0200 Subject: [PATCH 10/27] as per review --- .../hearthstone/get_match_group_copy_paste_wiki.lua | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua b/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua index cd807fcf338..444ab7b30c5 100644 --- a/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua +++ b/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua @@ -37,7 +37,7 @@ function WikiCopyPaste.getMatchCode(bestof, mode, index, opponents, args) return INDENT .. '|opponent' .. opponentIndex .. '=' .. WikiCopyPaste.getOpponent(mode, showScore) end), Array.map(Array.range(1, bestof), function(mapIndex) - return INDENT .. '|map' .. mapIndex .. WikiCopyPaste._getMap(mode) + return INDENT .. '|map' .. mapIndex .. '={{Map|o1p1=|o2p1=|winner=}}' end), '}}' ) @@ -45,11 +45,4 @@ function WikiCopyPaste.getMatchCode(bestof, mode, index, opponents, args) return table.concat(lines, '\n') end ---subfunction used to generate code for the Map template, depending on the type of opponent ----@param mode string ----@return string -function WikiCopyPaste._getMap(mode) - return '={{Map|o1p1=|o2p1=|winner=}}' -end - return WikiCopyPaste From 5d73384820bf888aa7f3e3fc6c2d15ad84947d74 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 09:43:12 +0200 Subject: [PATCH 11/27] ms: fix anno --- 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 92ec97de46e..feee3b744c8 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -121,7 +121,7 @@ function CustomMatchSummary._createGame(row, game, props) row:addElement(char2:css('flex', '1 1 35%')) end ----@param char string? +---@param character string? ---@param reverse boolean? ---@return Html function CustomMatchSummary._createCharacterDisplay(character, reverse) From 7277443bd62f79bd4dc33096c77d24ce2ef78b9a Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 09:44:17 +0200 Subject: [PATCH 12/27] fix(ms): do not apply css to potential nil --- components/match2/wikis/hearthstone/match_summary.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/match2/wikis/hearthstone/match_summary.lua b/components/match2/wikis/hearthstone/match_summary.lua index feee3b744c8..efd0e6d247c 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -125,8 +125,10 @@ end ---@param reverse boolean? ---@return Html function CustomMatchSummary._createCharacterDisplay(character, reverse) + local characterDisplay = mw.html.create('span'):addClass('draft faction') + if not character then - return + return characterDisplay end local characterDisplay = mw.html.create('span'):addClass('draft faction') From 06d02b5c376e11e0bca8592601c904e7b9853a78 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 10:37:12 +0200 Subject: [PATCH 13/27] linter --- 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 efd0e6d247c..f493bb168c7 100644 --- a/components/match2/wikis/hearthstone/match_summary.lua +++ b/components/match2/wikis/hearthstone/match_summary.lua @@ -131,7 +131,6 @@ function CustomMatchSummary._createCharacterDisplay(character, reverse) return characterDisplay end - local characterDisplay = mw.html.create('span'):addClass('draft faction') local charIcon = CharacterIcon.Icon{ character = character, size = '64px', From 69c2dcda6efe929c10b968e813d4ead67571478d Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 11:00:48 +0200 Subject: [PATCH 14/27] input refactor --- .../hearthstone/match_group_input_custom.lua | 544 ++++++------------ 1 file changed, 187 insertions(+), 357 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index fdfffa0f085..4fab527f79c 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -8,428 +8,258 @@ local Array = require('Module:Array') local CharacterStandardization = mw.loadData('Module:CharacterStandardization') -local DateExt = require('Module:Date/Ext') +local Faction = require('Module:Faction') +local Flags = require('Module:Flags') local FnUtil = require('Module:FnUtil') -local Json = require('Module:Json') +local HeroData = mw.loadData('Module:HeroData') local Logic = require('Module:Logic') local Lua = require('Module:Lua') +local Operator = require('Module:Operator') local String = require('Module:StringUtils') local Table = require('Module:Table') local Variables = require('Module:Variables') -local MatchGroupInput = Lua.import('Module:MatchGroup/Input') -local Opponent = Lua.import('Module:Opponent') +local MatchGroupInputUtil = Lua.import('Module:MatchGroup/Input/Util') +local OpponentLibraries = require('Module:OpponentLibraries') +local Opponent = OpponentLibraries.Opponent local Streams = Lua.import('Module:Links/Stream') -local ALLOWED_STATUSES = {'W', 'FF', 'DQ', 'L'} -local CONVERT_STATUS_INPUT = {W = 'W', FF = 'FF', L = 'L', DQ = 'DQ', ['-'] = 'L'} -local DEFAULT_LOSS_STATUSES = {'FF', 'L', 'DQ'} -local MAX_NUM_OPPONENTS = 2 -local MAX_NUM_PLAYERS = 10 -local DEFAULT_BESTOF = 99 - -local DUMMY_MAP_NAME = 'null' -- Is set in Template:Map when |map= is empty. - -local NOW = os.time(os.date('!*t') --[[@as osdateparam]]) +local OPPONENT_CONFIG = { + resolveRedirect = true, + pagifyTeamNames = true, + pagifyPlayerNames = true, +} +local TBD = 'TBD' +local DEFAULT_HERO_FACTION = HeroData.default.faction +local MODE_MIXED = 'mixed' local CustomMatchGroupInput = {} +local MatchFunctions = {} +local MapFunctions = {} ---- called from Module:MatchGroup ---@param match table ---@param options table? ---@return table function CustomMatchGroupInput.processMatch(match, options) - Table.mergeInto(match, MatchGroupInput.readDate(match.date)) - CustomMatchGroupInput._getOpponents(match) - CustomMatchGroupInput._getTournamentVars(match) - CustomMatchGroupInput._verifyMaps(match) - CustomMatchGroupInput._processMaps(match) - CustomMatchGroupInput._calculateWinner(match) - CustomMatchGroupInput._updateFinished(match) - match.stream = Streams.processStreams(match) - CustomMatchGroupInput._getVod(match) - return match -end + local finishedInput = match.finished --[[@as string?]] + local winnerInput = match.winner --[[@as string?]] -CustomMatchGroupInput.processMap = FnUtil.identity + Table.mergeInto(match, MatchGroupInputUtil.readDate(match.date)) ----@param match table -function CustomMatchGroupInput._getTournamentVars(match) - match = MatchGroupInput.getCommonTournamentVars(match) + local opponents = Array.mapIndexes(function(opponentIndex) + return MatchGroupInputUtil.readOpponent(match, opponentIndex, OPPONENT_CONFIG) + end) - match.mode = Variables.varDefault('tournament_mode', 'singles') -end + local games = MatchFunctions.extractMaps(match, opponents) + + local autoScoreFunction = MatchGroupInputUtil.canUseAutoScore(match, games) + and MatchFunctions.calculateMatchScore(games) + or nil + + Array.forEach(opponents, function(opponent, opponentIndex) + opponent.score, opponent.status = MatchGroupInputUtil.computeOpponentScore({ + walkover = match.walkover, + winner = match.winner, + opponentIndex = opponentIndex, + score = opponent.score, + }, autoScoreFunction) + end) + + match.finished = MatchGroupInputUtil.matchIsFinished(match, opponents) ----@param match table -function CustomMatchGroupInput._updateFinished(match) - match.finished = Logic.nilOr(Logic.readBoolOrNil(match.finished), Logic.isNotEmpty(match.winner)) if match.finished then - return + match.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents) + match.walkover = MatchGroupInputUtil.getWalkover(match.resulttype, opponents) + match.winner = MatchGroupInputUtil.getWinner(match.resulttype, winnerInput, opponents) + MatchGroupInputUtil.setPlacement(opponents, match.winner, 1, 2) + elseif MatchGroupInputUtil.isNotPlayed(winnerInput, finishedInput) then + match.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents) + match.winner = nil end - -- Match is automatically marked finished upon page edit after a - -- certain amount of time (depending on whether the date is exact) - local threshold = match.dateexact and 30800 or 86400 - match.finished = match.timestamp + threshold < NOW -end + MatchFunctions.getTournamentVars(match) ----@param match table -function CustomMatchGroupInput._getVod(match) match.stream = Streams.processStreams(match) - match.vod = Logic.emptyOr(match.vod) -end --- This function is used to discard maps that are none-relevant ones --- Template:Map sets a default map name because sometimes --- we have results without knowledge of which map it was planned on. ----@param match table -function CustomMatchGroupInput._verifyMaps(match) - for key, map in Table.iter.pairsByPrefix(match, 'map') do - if map.map == DUMMY_MAP_NAME then - match[key].map = nil - end - end -end + match.games = games + match.opponents = opponents ----@param match table -function CustomMatchGroupInput._processMaps(match) - for _, _, mapIndex in Table.iter.pairsByPrefix(match, 'map') do - CustomMatchGroupInput._mapInput(match, mapIndex) - end + return match end ---@param match table -function CustomMatchGroupInput._calculateWinner(match) - local bestof = match.bestof or DEFAULT_BESTOF - local numberOfOpponents = 0 - - for opponentIndex = 1, MAX_NUM_OPPONENTS do - local opponent = match['opponent' .. opponentIndex] - if Logic.isEmpty(opponent) then - break - end - - numberOfOpponents = numberOfOpponents + 1 - - if Logic.isNotEmpty(match.walkover) then - if Logic.isNumeric(match.walkover) then - local walkover = tonumber(match.walkover) - if walkover == opponentIndex then - match.winner = opponentIndex - match.walkover = 'FF' - opponent.status = 'W' - elseif walkover == 0 then - match.winner = 0 - match.walkover = 'FF' - opponent.status = 'FF' - else - local score = string.upper(opponent.score or '') - opponent.status = CONVERT_STATUS_INPUT[score] or 'FF' - end - elseif Table.includes(ALLOWED_STATUSES, string.upper(match.walkover)) then - if tonumber(match.winner or 0) == opponentIndex then - opponent.status = 'W' - else - opponent.status = CONVERT_STATUS_INPUT[string.upper(match.walkover)] or 'L' - end - else - local score = string.upper(opponent.score or '') - opponent.status = CONVERT_STATUS_INPUT[score] or 'L' - match.walkover = 'L' - end - opponent.score = -1 - match.finished = true - match.resulttype = 'default' - elseif CONVERT_STATUS_INPUT[string.upper(opponent.score or '')] then - if string.upper(opponent.score) == 'W' then - match.winner = opponentIndex - match.finished = true - opponent.score = -1 - opponent.status = 'W' - else - local score = string.upper(opponent.score) - match.finished = true - match.walkover = CONVERT_STATUS_INPUT[score] - opponent.status = CONVERT_STATUS_INPUT[score] - opponent.score = -1 - end - match.resulttype = 'default' - else - opponent.status = 'S' - opponent.score = tonumber(opponent.score) or tonumber(opponent.autoscore) or -1 - if opponent.score > bestof / 2 then - match.finished = Logic.emptyOr(match.finished, true) - match.winner = tonumber(match.winner) or opponentIndex - end - end - end - - CustomMatchGroupInput._determineWinnerIfMissing(match) - - for opponentIndex = 1, numberOfOpponents do - local opponent = match['opponent' .. opponentIndex] - if match.winner == 'draw' or tonumber(match.winner) == 0 or - (match.opponent1.score == bestof / 2 and match.opponent1.score == match.opponent2.score) then - match.finished = true - match.winner = 0 - match.resulttype = 'draw' - end - - if tonumber(match.winner) == opponentIndex or - match.resulttype == 'draw' then - opponent.placement = 1 - elseif Logic.isNumeric(match.winner) then - opponent.placement = 2 - end - end +---@return table +function MatchFunctions.getTournamentVars(match) + match.mode = Variables.varDefault('tournament_mode', 'singles') + return MatchGroupInputUtil.getCommonTournamentVars(match) end ----@param match table -function CustomMatchGroupInput._determineWinnerIfMissing(match) - if not Logic.readBool(match.finished) or Logic.isNotEmpty(match.winner) then - return +---@param maps table[] +---@return fun(opponentIndex: integer): integer? +function MatchFunctions.calculateMatchScore(maps) + return function(opponentIndex) + return MatchGroupInputUtil.computeMatchScoreFromMapWinners(maps, opponentIndex) end +end - local scores = Array.mapIndexes(function(opponentIndex) - local opponent = match['opponent' .. opponentIndex] - if not opponent then - return nil - end - return match['opponent' .. opponentIndex].score or -1 - end - ) - local maxScore = math.max(unpack(scores) or 0) - -- if we have a positive score and the match is finished we also have a winner - if maxScore > 0 then - local maxIndexFound = false - for opponentIndex, score in pairs(scores) do - if maxIndexFound and score == maxScore then - match.winner = 0 - break - elseif score == maxScore then - maxIndexFound = true - match.winner = opponentIndex - end - end - end +---@param bestofInput string|integer? +---@return integer? +function MatchFunctions.getBestOf(bestofInput) + return tonumber(bestofInput) end ---@param match table ----@return table -function CustomMatchGroupInput._getOpponents(match) - -- read opponents and ignore empty ones - for opponentIndex = 1, MAX_NUM_OPPONENTS do - -- read opponent - local opponent = match['opponent' .. opponentIndex] or {} - if Logic.isNotEmpty(opponent) then - CustomMatchGroupInput.processOpponent(opponent, match.timestamp) - end - match['opponent' .. opponentIndex] = opponent - - if opponent.type == Opponent.team and Logic.isNotEmpty(opponent.name) then - MatchGroupInput.readPlayersOfTeam(match, opponentIndex, opponent.name, { - resolveRedirect = true, - applyUnderScores = true, - maxNumPlayers = MAX_NUM_PLAYERS, - }) +---@param opponents table[] +---@return table[] +function MatchFunctions.extractMaps(match, opponents) + local maps = {} + local subGroup = 0 + for mapKey, map in Table.iter.pairsByPrefix(match, 'map', {requireIndex = true}) do + local finishedInput = map.finished --[[@as string?]] + local winnerInput = map.winner --[[@as string?]] + + if String.isNotEmpty(map.map) and string.upper(map.map) ~= TBD then + map.map = mw.ext.TeamLiquidIntegration.resolve_redirect(map.map) end - end - return match -end + map.finished = MatchGroupInputUtil.mapIsFinished(map) + + local opponentInfo = Array.map(Array.range(1, #opponents), function(opponentIndex) + local score, status = MatchGroupInputUtil.computeOpponentScore({ + walkover = map.walkover, + winner = map.winner, + opponentIndex = opponentIndex, + score = map['score' .. opponentIndex], + }, MapFunctions.calculateMapScore(map.winner, map.finished)) + return {score = score, status = status} + end) + + map.scores = Array.map(opponentInfo, Operator.property('score')) + if map.finished or MatchGroupInputUtil.isNotPlayed(map.winner, finishedInput) then + map.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponentInfo) + map.walkover = MatchGroupInputUtil.getWalkover(map.resulttype, opponentInfo) + map.winner = MatchGroupInputUtil.getWinner(map.resulttype, winnerInput, opponentInfo) + end ----@param record table ----@param timestamp number -function CustomMatchGroupInput.processOpponent(record, timestamp) - local opponent = Opponent.readOpponentArgs(record) or Opponent.blank() + map.extradata = MapFunctions.getExtradata(map) - -- Convert byes to literals - if Opponent.isBye(opponent) then - opponent = {type = Opponent.literal, name = 'BYE'} - end + map.participants = MapFunctions.getParticipants(map, opponents) - local teamTemplateDate = timestamp - if teamTemplateDate == DateExt.defaultTimestamp then - teamTemplateDate = Variables.varDefaultMulti('tournament_enddate', 'tournament_startdate', NOW) + table.insert(maps, map) + match[mapKey] = nil end - Opponent.resolve(opponent, teamTemplateDate, {syncPlayer = true}) - MatchGroupInput.mergeRecordWithOpponent(record, opponent) + return maps end ----@param match table ----@param mapIndex integer -function CustomMatchGroupInput._mapInput(match, mapIndex) - local map = Json.parseIfString(match['map' .. mapIndex]) - if String.isNotEmpty(map.map) and map.map ~= 'TBD' then - map.map = mw.ext.TeamLiquidIntegration.resolve_redirect(map.map) - end - - -- set initial extradata for maps - map.extradata = { - comment = map.comment, - header = map.header, - } - map.game = match.game - map.mode = match.mode - - -- determine score, resulttype, walkover and winner - map = CustomMatchGroupInput._mapWinnerProcessing(map) - - -- Init score if match started and map info is present - if not match.opponent1.autoscore and not match.opponent2.autoscore and map.winner then - match.opponent1.autoscore = 0 - match.opponent2.autoscore = 0 - end - - if Logic.isEmpty(map.resulttype) and map.scores[1] and map.scores[2] then - match.opponent1.autoscore = (match.opponent1.autoscore or 0) + (map.winner == 1 and 1 or 0) - match.opponent2.autoscore = (match.opponent2.autoscore or 0) + (map.winner == 2 and 1 or 0) - end - - CustomMatchGroupInput.processPlayersMapData(map, match, 2) - - match['map' .. mapIndex] = map +---@param mapInput table +---@return table +function MapFunctions.getExtradata(mapInput) + return {comment = mapInput.comment} end ----@param map table ----@return table -function CustomMatchGroupInput._mapWinnerProcessing(map) - map.scores = {} - local hasManualScores = false - local indexedScores = {} - for scoreIndex = 1, MAX_NUM_OPPONENTS do - -- read scores - local score = map['score' .. scoreIndex] - local obj = {} - if Logic.isNotEmpty(score) then - hasManualScores = true - score = CONVERT_STATUS_INPUT[score] or score - if Logic.isNumeric(score) then - obj.status = 'S' - obj.score = score - elseif Table.includes(ALLOWED_STATUSES, score) then - obj.status = score - obj.score = -1 - end - table.insert(map.scores, score) - indexedScores[scoreIndex] = obj - else - break +---@param winnerInput string|integer|nil +---@param finished boolean +---@return fun(opponentIndex: integer): integer? +function MapFunctions.calculateMapScore(winnerInput, finished) + local winner = tonumber(winnerInput) + return function(opponentIndex) + -- TODO Better to check if map has started, rather than finished, for a more correct handling + if not winner and not finished then + return end + return winner == opponentIndex and 1 or 0 end +end - local winnerInput = tonumber(map.winner) - if Logic.isNotEmpty(map.walkover) then - local walkoverInput = tonumber(map.walkover) - if walkoverInput == 1 or walkoverInput == 2 or walkoverInput == 0 then - map.winner = walkoverInput +---@param mapInput table +---@param opponents table[] +---@return table +function MapFunctions.getParticipants(mapInput, opponents) + local participants = {} + Array.forEach(opponents, function(opponent, opponentIndex) + if opponent.type == Opponent.literal then + return + elseif opponent.type == Opponent.team then + Table.mergeInto(participants, MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex)) + return end - map.walkover = Table.includes(ALLOWED_STATUSES, map.walkover) and map.walkover or 'L' - map.scores = {-1, -1} - map.resulttype = 'default' + Table.mergeInto(participants, MapFunctions.getPartyParticipants(mapInput, opponent, opponentIndex)) + end) - return map - end + return participants +end - if hasManualScores then - for scoreIndex, _ in Table.iter.spairs(indexedScores, CustomMatchGroupInput._placementSortFunction) do - if not tonumber(map.winner) then - map.winner = scoreIndex - else - break - end +---@param mapInput table +---@param opponent table +---@param opponentIndex integer +---@return table +function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) + local players = Array.mapIndexes(function(playerIndex) + return Logic.nilIfEmpty(mapInput['o' .. opponentIndex .. 'p' .. playerIndex]) + end) + + local participants, unattachedParticipants = MatchGroupInputUtil.parseParticipants( + opponent.match2players, + players, + function(playerIndex) + local prefix = 'o' .. opponentIndex .. 'p' .. playerIndex + return { + name = mapInput[prefix], + link = Logic.nilIfEmpty(mapInput[prefix .. 'link']), + character = Logic.nilIfEmpty(mapInput[prefix .. 'char']), + } + end, + function(playerIndex, playerIdData, playerInputData) + return { + player = playerIdData.name or playerInputData.link, + character = MapFunctions.readCharacter(playerInputData.character), + } end + ) - return map - end + Array.forEach(unattachedParticipants, function(participant) + local isTBD = participant.name:upper() == TBD - if map.winner == 'skip' then - map.scores = {-1, -1} - map.resulttype = 'np' - elseif winnerInput == 1 then - map.scores = {1, 0} - map.winner = 1 - elseif winnerInput == 2 then - map.scores = {0, 1} - map.winner = 2 - elseif winnerInput == 0 or map.winner == 'draw' then - map.winner = 0 - map.scores = {0, 0} - map.resulttype = 'draw' - end + table.insert(opponent.match2players, { + name = isTBD and TBD or participant.link, + displayname = isTBD and TBD or participant.name, + flag = participant.flag, + extradata = {faction = participant.faction}, + }) + participants[#opponent.match2players] = participant + end) - return map + return Table.map(participants, MatchGroupInputUtil.prefixPartcipants(opponentIndex)) end ----@param map table ----@param match table ----@param numberOfOpponents integer -function CustomMatchGroupInput.processPlayersMapData(map, match, numberOfOpponents) - local participants = {} - for opponentIndex = 1, numberOfOpponents do - local opponent = match['opponent' .. opponentIndex] - if opponent.type ~= Opponent.literal then - local playerCount = Opponent.partySize(opponent.type) or MAX_NUM_PLAYERS - for playerIndex = 1, playerCount do - local player = opponent.match2players[playerIndex] - if player then - local playerData = CustomMatchGroupInput._processPlayerMapData(player, map, opponentIndex, playerIndex) - participants[opponentIndex .. '_' .. playerIndex] = playerData - end - end - end - end - map.participants = participants -end - ----@param player table ----@param map table +---@param mapInput table +---@param opponent table ---@param opponentIndex integer ----@param playerIndex integer ----@return {character: string?, player: string} -function CustomMatchGroupInput._processPlayerMapData(player, map, opponentIndex, playerIndex) - local getCharacterName = FnUtil.curry(MatchGroupInput.getCharacterName, CharacterStandardization) +---@return table +function MapFunctions.getPartyParticipants(mapInput, opponent, opponentIndex) + local players = opponent.match2players + + local prefix = 'o' .. opponentIndex .. 'p' + + local participants = {} - local character = getCharacterName(map['o' .. opponentIndex .. 'p' .. playerIndex]) + Array.forEach(players, function(player, playerIndex) + participants[opponentIndex .. '_' .. playerIndex] = { + character = MapFunctions.readCharacter(mapInput[prefix .. playerIndex .. 'char']), + player = player.name, + } + end) - return { - character = character, - player = player.name, - } + return participants end --- function to sort out winner/placements ----@param tbl table ----@param key1 string ----@param key2 string ----@return boolean -function CustomMatchGroupInput._placementSortFunction(tbl, key1, key2) - local opponent1 = tbl[key1] - local opponent2 = tbl[key2] - local opponent1Norm = opponent1.status == 'S' - local opponent2Norm = opponent2.status == 'S' - if opponent1Norm then - if opponent2Norm then - return tonumber(opponent1.score) > tonumber(opponent2.score) - else - return true - end - else - if opponent2Norm then - return false - elseif opponent1.status == 'W' then - return true - elseif Table.includes(DEFAULT_LOSS_STATUSES, opponent1.status) then - return false - elseif opponent2.status == 'W' then - return false - elseif Table.includes(DEFAULT_LOSS_STATUSES, opponent2.status) then - return true - else - return true - end - end +---@param input string? +---@return string? +function MapFunctions.readCharacter(input) + local getCharacterName = FnUtil.curry(MatchGroupInputUtil.getCharacterName, CharacterStandardization) + + return getCharacterName(input) end return CustomMatchGroupInput From 6475574a77681d6d3c5b4012aac5922fbfccfd23 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 11:16:08 +0200 Subject: [PATCH 15/27] adjust copy paste (add team support) --- .../get_match_group_copy_paste_wiki.lua | 27 ++++++++++++++++++- .../hearthstone/match_group_input_custom.lua | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua b/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua index 444ab7b30c5..580ce5afbe6 100644 --- a/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua +++ b/components/match2/wikis/hearthstone/get_match_group_copy_paste_wiki.lua @@ -12,6 +12,8 @@ local Logic = require('Module:Logic') local Lua = require('Module:Lua') local BaseCopyPaste = Lua.import('Module:GetMatchGroupCopyPaste/wiki/Base') +local OpponentLibrary = Lua.import('Module:OpponentLibraries') +local Opponent = OpponentLibrary.Opponent ---WikiSpecific Code for MatchList and Bracket Code Generators ---@class HearthstoneMatchCopyPaste: Match2CopyPasteBase @@ -37,7 +39,7 @@ function WikiCopyPaste.getMatchCode(bestof, mode, index, opponents, args) return INDENT .. '|opponent' .. opponentIndex .. '=' .. WikiCopyPaste.getOpponent(mode, showScore) end), Array.map(Array.range(1, bestof), function(mapIndex) - return INDENT .. '|map' .. mapIndex .. '={{Map|o1p1=|o2p1=|winner=}}' + return INDENT .. '|map' .. mapIndex .. WikiCopyPaste._getMap(mode, opponents) end), '}}' ) @@ -45,4 +47,27 @@ function WikiCopyPaste.getMatchCode(bestof, mode, index, opponents, args) return table.concat(lines, '\n') end +--subfunction used to generate code for the Map template, depending on the type of opponent +---@param mode string +---@param opponents integer +---@return string +function WikiCopyPaste._getMap(mode, opponents) + if mode == Opponent.team then + return '={{Map|o1p1=|o2p1=|o1p1char=|o2p1char=|winner=}}' + elseif mode == Opponent.literal then + return '={{Map|winner=}}' + end + + local parts = Array.extend({}, + Array.map(Array.range(1, opponents), function(opponentIndex) + return table.concat(Array.map(Array.range(1, Opponent.partySize(mode) --[[@as integer]]), function(playerIndex) + return '|o' .. opponentIndex .. 'p' .. playerIndex .. '=' + end)) + end), + '}}' + ) + + return table.concat(parts) +end + return WikiCopyPaste diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 4fab527f79c..9fe7f5a6e3c 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -246,7 +246,7 @@ function MapFunctions.getPartyParticipants(mapInput, opponent, opponentIndex) Array.forEach(players, function(player, playerIndex) participants[opponentIndex .. '_' .. playerIndex] = { - character = MapFunctions.readCharacter(mapInput[prefix .. playerIndex .. 'char']), + character = MapFunctions.readCharacter(mapInput[prefix .. playerIndex]), player = player.name, } end) From 4d46a2865912c23fb8dade88d8ce989b8889d487 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 11:24:52 +0200 Subject: [PATCH 16/27] store chars in extradata too (to be able to condition on them) --- .../hearthstone/match_group_input_custom.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 9fe7f5a6e3c..de7fcffc50b 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -141,7 +141,7 @@ function MatchFunctions.extractMaps(match, opponents) map.winner = MatchGroupInputUtil.getWinner(map.resulttype, winnerInput, opponentInfo) end - map.extradata = MapFunctions.getExtradata(map) + map.extradata = MapFunctions.getExtradata(map, opponents) map.participants = MapFunctions.getParticipants(map, opponents) @@ -153,9 +153,22 @@ function MatchFunctions.extractMaps(match, opponents) end ---@param mapInput table +---@param opponents table[] ---@return table -function MapFunctions.getExtradata(mapInput) - return {comment = mapInput.comment} +function MapFunctions.getExtradata(mapInput, opponents) + local extradata = {comment = mapInput.comment} + + Array.forEach(opponents, function(opponent, opponentIndex) + local prefix = 'o' .. opponentIndex .. 'p' + local chars = Array.mapIndexes(function(charIndex) + return Logic.nilIfEmpty(mapInput[prefix .. charIndex .. 'char']) or Logic.nilIfEmpty(mapInput[prefix .. charIndex]) + end) + Array.forEach(chars, function(char, charIndex) + extradata[prefix .. charIndex] = MapFunctions.readCharacter(char) + end) + end) + + return extradata end ---@param winnerInput string|integer|nil From 4289d8471c4bb4afbe921d605db7665be3a3d9d6 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 11:26:02 +0200 Subject: [PATCH 17/27] kick unsued cars and requires --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index de7fcffc50b..8554b29cbb7 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -8,10 +8,7 @@ local Array = require('Module:Array') local CharacterStandardization = mw.loadData('Module:CharacterStandardization') -local Faction = require('Module:Faction') -local Flags = require('Module:Flags') local FnUtil = require('Module:FnUtil') -local HeroData = mw.loadData('Module:HeroData') local Logic = require('Module:Logic') local Lua = require('Module:Lua') local Operator = require('Module:Operator') @@ -30,8 +27,6 @@ local OPPONENT_CONFIG = { pagifyPlayerNames = true, } local TBD = 'TBD' -local DEFAULT_HERO_FACTION = HeroData.default.faction -local MODE_MIXED = 'mixed' local CustomMatchGroupInput = {} local MatchFunctions = {} From 0e8bcd57953d9e75afe8e897b81e4cb47e21d539 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 11:27:07 +0200 Subject: [PATCH 18/27] and another one --- components/match2/wikis/hearthstone/match_group_input_custom.lua | 1 - 1 file changed, 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 8554b29cbb7..6fcda117447 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -108,7 +108,6 @@ end ---@return table[] function MatchFunctions.extractMaps(match, opponents) local maps = {} - local subGroup = 0 for mapKey, map in Table.iter.pairsByPrefix(match, 'map', {requireIndex = true}) do local finishedInput = map.finished --[[@as string?]] local winnerInput = map.winner --[[@as string?]] From 02315fa9d076cbdc3d0e3837f701b34f566342da Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 11:34:22 +0200 Subject: [PATCH 19/27] map function -> identity --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 6fcda117447..8aedce837ef 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -32,6 +32,8 @@ local CustomMatchGroupInput = {} local MatchFunctions = {} local MapFunctions = {} +CustomMatchGroupInput.processMap = FnUtil.identity + ---@param match table ---@param options table? ---@return table From f9937b14c2d80d25b9d7e3158d618ca9dd104d9d Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 3 Sep 2024 18:21:12 +0200 Subject: [PATCH 20/27] fix --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 8aedce837ef..cbdd8320722 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -228,13 +228,9 @@ function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) ) Array.forEach(unattachedParticipants, function(participant) - local isTBD = participant.name:upper() == TBD - table.insert(opponent.match2players, { - name = isTBD and TBD or participant.link, - displayname = isTBD and TBD or participant.name, - flag = participant.flag, - extradata = {faction = participant.faction}, + name = participant.player, + displayname = participant.player, }) participants[#opponent.match2players] = participant end) From 762eb51d23d4896c251623d0a11c146a04b6c158 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 10 Sep 2024 08:00:09 +0200 Subject: [PATCH 21/27] #4650 --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index cbdd8320722..4dcd0126b33 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -68,10 +68,7 @@ function CustomMatchGroupInput.processMatch(match, options) match.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents) match.walkover = MatchGroupInputUtil.getWalkover(match.resulttype, opponents) match.winner = MatchGroupInputUtil.getWinner(match.resulttype, winnerInput, opponents) - MatchGroupInputUtil.setPlacement(opponents, match.winner, 1, 2) - elseif MatchGroupInputUtil.isNotPlayed(winnerInput, finishedInput) then - match.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents) - match.winner = nil + MatchGroupInputUtil.setPlacement(opponents, match.winner, 1, 2, match.resulttype) end MatchFunctions.getTournamentVars(match) @@ -131,7 +128,7 @@ function MatchFunctions.extractMaps(match, opponents) end) map.scores = Array.map(opponentInfo, Operator.property('score')) - if map.finished or MatchGroupInputUtil.isNotPlayed(map.winner, finishedInput) then + if map.finished then map.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponentInfo) map.walkover = MatchGroupInputUtil.getWalkover(map.resulttype, opponentInfo) map.winner = MatchGroupInputUtil.getWinner(map.resulttype, winnerInput, opponentInfo) From 6bcb14f92ae4ddbf962d0da7553ad52418138c86 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Thu, 10 Oct 2024 12:03:17 +0200 Subject: [PATCH 22/27] update with changes from #4839 --- .../wikis/hearthstone/match_group_input_custom.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 4dcd0126b33..910a05e20a9 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -71,7 +71,8 @@ function CustomMatchGroupInput.processMatch(match, options) MatchGroupInputUtil.setPlacement(opponents, match.winner, 1, 2, match.resulttype) end - MatchFunctions.getTournamentVars(match) + match.mode = Variables.varDefault('tournament_mode', 'singles') + Table.mergeInto(match, MatchGroupInputUtil.getTournamentContext(match)) match.stream = Streams.processStreams(match) @@ -81,13 +82,6 @@ function CustomMatchGroupInput.processMatch(match, options) return match end ----@param match table ----@return table -function MatchFunctions.getTournamentVars(match) - match.mode = Variables.varDefault('tournament_mode', 'singles') - return MatchGroupInputUtil.getCommonTournamentVars(match) -end - ---@param maps table[] ---@return fun(opponentIndex: integer): integer? function MatchFunctions.calculateMatchScore(maps) @@ -213,13 +207,13 @@ function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) return { name = mapInput[prefix], link = Logic.nilIfEmpty(mapInput[prefix .. 'link']), - character = Logic.nilIfEmpty(mapInput[prefix .. 'char']), } end, function(playerIndex, playerIdData, playerInputData) + local prefix = 'o' .. opponentIndex .. 'p' .. playerIndex return { player = playerIdData.name or playerInputData.link, - character = MapFunctions.readCharacter(playerInputData.character), + character = MapFunctions.readCharacter(Logic.nilIfEmpty(mapInput[prefix .. 'char']).character), } end ) From 79fe987ff74bf255bda380e0330314e2d9495017 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Thu, 10 Oct 2024 13:16:03 +0200 Subject: [PATCH 23/27] change from #4842 --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/match2/wikis/hearthstone/match_group_input_custom.lua b/components/match2/wikis/hearthstone/match_group_input_custom.lua index 910a05e20a9..4b3b034203c 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -32,8 +32,6 @@ local CustomMatchGroupInput = {} local MatchFunctions = {} local MapFunctions = {} -CustomMatchGroupInput.processMap = FnUtil.identity - ---@param match table ---@param options table? ---@return table From 8cb274400b56178fbd5fd44067ebb635684dcab0 Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:19:17 +0200 Subject: [PATCH 24/27] Update Hearthstone Info M2 status (#4844) * Update Hearthstone Info Based on: https://github.com/Liquipedia/Lua-Modules/pull/4518#issuecomment-2396334507 * Update standard/info/wikis/hearthstone/info.lua Co-authored-by: Rikard Blixt --------- Co-authored-by: Rikard Blixt --- standard/info/wikis/hearthstone/info.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/info/wikis/hearthstone/info.lua b/standard/info/wikis/hearthstone/info.lua index a63f8538dec..7d5a1236639 100644 --- a/standard/info/wikis/hearthstone/info.lua +++ b/standard/info/wikis/hearthstone/info.lua @@ -33,7 +33,7 @@ return { allowManual = true, }, match2 = { - status = 0, + status = 1, }, }, } From 4af5add5f20bd1669846a37f5e458246d0643cc7 Mon Sep 17 00:00:00 2001 From: Jasper Jacobs <97167868+Kanopedia@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:24:43 +0200 Subject: [PATCH 25/27] Create match_legacy for Hearthstone (#4846) * Create match_legacy for Hearthstone * Apply suggestions from code review Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> * Update components/match2/wikis/hearthstone/match_legacy.lua * Update components/match2/wikis/hearthstone/match_legacy.lua * Update components/match2/wikis/hearthstone/match_legacy.lua --------- Co-authored-by: Rikard Blixt Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- .../match2/wikis/hearthstone/match_legacy.lua | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 components/match2/wikis/hearthstone/match_legacy.lua diff --git a/components/match2/wikis/hearthstone/match_legacy.lua b/components/match2/wikis/hearthstone/match_legacy.lua new file mode 100644 index 00000000000..36266788958 --- /dev/null +++ b/components/match2/wikis/hearthstone/match_legacy.lua @@ -0,0 +1,91 @@ +--- +-- @Liquipedia +-- wiki=hearthstone +-- page=Module:Match/Legacy +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local MatchLegacy = {} + +local Array = require('Module:Array') +local Json = require('Module:Json') +local Logic = require('Module:Logic') +local String = require('Module:StringUtils') +local Table = require('Module:Table') +local Opponent = require('Module:Opponent') + +function MatchLegacy.storeMatch(match2) + local match = MatchLegacy._convertParameters(match2) + + match.games = MatchLegacy.storeGames(match, match2) + + return mw.ext.LiquipediaDB.lpdb_match( + 'legacymatch_' .. match2.match2id, + match + ) +end + +function MatchLegacy._convertParameters(match2) + local match = Table.filterByKey(Table.deepCopy(match2), function(key) return not String.startsWith(key, 'match2') end) + match.links = nil + + if Logic.isNotEmpty(match.walkover) then + match.resulttype = match.walkover + match.walkover = match.winner + end + + match.staticid = match2.match2id + + + -- Handle Opponents + local handleOpponent = function (index) + local prefix = 'opponent'..index + local opponent = match2.match2opponents[index] or {} + match[prefix .. 'score'] = tonumber(opponent.score) or 0 + if opponent.type == Opponent.team then + match[prefix] = opponent.name + local players = {} + Array.forEach(opponent.match2players or {}, function(player, playerIndex) + players['p' .. playerIndex] = player.name or '' + players['p' .. playerIndex .. 'flag'] = player.flag or '' + players['p' .. playerIndex .. 'dn'] = player.displayname or '' + end) + match[prefix .. 'players'] = players + elseif opponent.type == Opponent.solo then + local player = (opponent.match2players or {})[1] or {} + match[prefix] = player.name + match[prefix..'flag'] = player.flag + elseif opponent.type == Opponent.literal then + match[prefix] = 'TBD' + end + end + + handleOpponent(1) + handleOpponent(2) + + return Json.stringifySubTables(match) +end + +function MatchLegacy.storeGames(match, match2) + local games = Array.map(match2.match2games or {}, function(game2, gameIndex) + local game = Table.deepCopy(game2) + + -- Other stuff + game.opponent1 = match.opponent1 + game.opponent2 = match.opponent2 + game.opponent1flag = match.opponent1flag + game.opponent2flag = match.opponent2flag + game.date = match.date + local winner = tonumber(game.winner) + game.opponent1score = winner == 1 and 1 or 0 + game.opponent2score = winner == 2 and 1 or 0 + return mw.ext.LiquipediaDB.lpdb_game( + 'legacygame_' .. match2.match2id .. '_' .. gameIndex, + Json.stringifySubTables(game) + ) + end) + return table.concat(games) +end + +return MatchLegacy From 73498d8116ca1681d4e5a8205f46b6e574199895 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Thu, 10 Oct 2024 15:30:14 +0200 Subject: [PATCH 26/27] change from #4841 --- .../match2/wikis/hearthstone/match_group_input_custom.lua | 4 +++- 1 file changed, 3 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 4b3b034203c..4d13365b951 100644 --- a/components/match2/wikis/hearthstone/match_group_input_custom.lua +++ b/components/match2/wikis/hearthstone/match_group_input_custom.lua @@ -66,7 +66,9 @@ function CustomMatchGroupInput.processMatch(match, options) match.resulttype = MatchGroupInputUtil.getResultType(winnerInput, finishedInput, opponents) match.walkover = MatchGroupInputUtil.getWalkover(match.resulttype, opponents) match.winner = MatchGroupInputUtil.getWinner(match.resulttype, winnerInput, opponents) - MatchGroupInputUtil.setPlacement(opponents, match.winner, 1, 2, match.resulttype) + Array.forEach(opponents, function(opponent, opponentIndex) + opponent.placement = MatchGroupInputUtil.placementFromWinner(match.resulttype, match.winner, opponentIndex) + end) end match.mode = Variables.varDefault('tournament_mode', 'singles') From da10edcd5b39db5c8cac51599e1876fa26a80e35 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Thu, 10 Oct 2024 15:31:13 +0200 Subject: [PATCH 27/27] aano wanring --- components/match2/wikis/hearthstone/match_legacy.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/components/match2/wikis/hearthstone/match_legacy.lua b/components/match2/wikis/hearthstone/match_legacy.lua index 36266788958..63021e1f3fc 100644 --- a/components/match2/wikis/hearthstone/match_legacy.lua +++ b/components/match2/wikis/hearthstone/match_legacy.lua @@ -27,6 +27,7 @@ function MatchLegacy.storeMatch(match2) end function MatchLegacy._convertParameters(match2) + ---@type table local match = Table.filterByKey(Table.deepCopy(match2), function(key) return not String.startsWith(key, 'match2') end) match.links = nil