From de8f1a3cb9f3920fe6106e23edcff3b3bce03f3b Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Mon, 26 Aug 2024 16:56:09 +0200 Subject: [PATCH 01/16] ms adjusts --- components/match2/wikis/stormgate/match_summary.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/match2/wikis/stormgate/match_summary.lua b/components/match2/wikis/stormgate/match_summary.lua index fb59edff5d7..4d8fddc3350 100644 --- a/components/match2/wikis/stormgate/match_summary.lua +++ b/components/match2/wikis/stormgate/match_summary.lua @@ -366,17 +366,21 @@ function CustomMatchSummary._submatchHeader(submatch) } end + ---@param opponentIndex any + ---@return Html local createScore = function(opponentIndex) local isWinner = opponentIndex == submatch.winner if submatch.resultType == 'default' then return OpponentDisplay.BlockScore{ isWinner = isWinner, - scoreText = isWinner and 'W' or submatch.walkover, + scoreText = isWinner and 'W' or string.upper(submatch.walkover), } end + + local score = submatch.resultType ~= 'np' and (submatch.scores or {})[opponentIndex] or nil return OpponentDisplay.BlockScore{ isWinner = isWinner, - scoreText = (submatch.scores or {})[opponentIndex] or '', + scoreText = score, } end From a266e2f0bcb042c2dee62fdff4a271a341ee2ca8 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Mon, 26 Aug 2024 16:56:49 +0200 Subject: [PATCH 02/16] player ext adjusts --- components/opponent/wikis/stormgate/player_ext_custom.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/opponent/wikis/stormgate/player_ext_custom.lua b/components/opponent/wikis/stormgate/player_ext_custom.lua index ee0286cb486..4ee44c3f27f 100644 --- a/components/opponent/wikis/stormgate/player_ext_custom.lua +++ b/components/opponent/wikis/stormgate/player_ext_custom.lua @@ -52,7 +52,11 @@ end) function CustomPlayerExt.fetchPlayerFaction(resolvedPageName, date) local lpdbPlayer = CustomPlayerExt.fetchPlayer(resolvedPageName) if lpdbPlayer and lpdbPlayer.factionHistory then - date = date or DateExt.getContextualDateOrNow() + local timestamp = DateExt.readTimestamp(date or DateExt.getContextualDateOrNow()) + ---@cast timestamp -nil + -- convert date to iso format to match the dates retrieved from the data points + -- need the time too so the below check remains the same as before + date = DateExt.formatTimestamp('Y-m-d H:i:s', timestamp) local entry = Array.find(lpdbPlayer.factionHistory, function(entry) return date <= entry.endDate end) return entry and Faction.read(entry.faction) else From 3aec1a2be1466d3b447f2bd140306f1cef8a5a99 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Mon, 26 Aug 2024 17:16:31 +0200 Subject: [PATCH 03/16] input processing --- .../stormgate/match_group_input_custom.lua | 958 +++++++----------- 1 file changed, 378 insertions(+), 580 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index da7408e805a..2227d087acb 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -10,58 +10,116 @@ local Array = require('Module:Array') local Faction = require('Module:Faction') local Flags = require('Module:Flags') local HeroData = mw.loadData('Module:HeroData') -local Json = require('Module:Json') local Logic = require('Module:Logic') local Lua = require('Module:Lua') +local Operator = require('Module:Operator') +local Page = require('Module:Page') local String = require('Module:StringUtils') local Table = require('Module:Table') local Variables = require('Module:Variables') -local MatchGroupInput = Lua.import('Module:MatchGroup/Input/Util') +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 OpponentLibrary = require('Module:OpponentLibraries') -local Opponent = OpponentLibrary.Opponent - -local DEFAULT_LOSS_STATUSES = {'FF', 'L', 'DQ'} -local DEFAULT_WIN_STATUS = 'W' -local SCORE_STATUS = 'S' -local ALLOWED_STATUSES = Array.append(DEFAULT_LOSS_STATUSES, DEFAULT_WIN_STATUS) -local RESULT_TYPE_DEFAULT = 'default' -local RESULT_TYPE_NOT_PLAYED = 'np' -local MAX_NUM_OPPONENTS = 2 -local DEFAULT_BEST_OF = 99 -local MODE_MIXED = 'mixed' -local TBD = 'tbd' +local OPPONENT_CONFIG = { + resolveRedirect = true, + pagifyTeamNames = true, + pagifyPlayerNames = true, +} +local TBD = 'TBD' local DEFAULT_HERO_FACTION = HeroData.default.faction -local NOW = os.time(os.date('!*t') --[[@as osdateparam]]) +local MODE_MIXED = 'mixed' + +---@class StormgateParticipant +---@field player string +---@field faction string? +---@field heroes string[]? +---@field position integer? +---@field flag string? +---@field random boolean? local CustomMatchGroupInput = {} +local MatchFunctions = {} +local MapFunctions = {} ---- called from Module:MatchGroup ---@param match table ---@param options table? ---@return table function CustomMatchGroupInput.processMatch(match, options) assert(not Logic.readBool(match.ffa), 'FFA is not yet supported in stormgate match2') - Table.mergeInto( - match, - CustomMatchGroupInput._readDate(match) - ) - CustomMatchGroupInput._getTournamentVars(match) - CustomMatchGroupInput._adjustData(match) - CustomMatchGroupInput._updateFinished(match) + Table.mergeInto(match, MatchFunctions.readDate(match)) + + local opponents = Array.mapIndexes(function(opponentIndex) + return MatchGroupInputUtil.readOpponent(match, opponentIndex, OPPONENT_CONFIG) + end) + + Array.forEach(opponents, function(opponent) + opponent.extradata = opponent.extradata or {} + Table.mergeInto(opponent.extradata, MatchFunctions.getOpponentExtradata(opponent)) + -- make sure match2players is not nil to avoid indexing nil + opponent.match2players = opponent.match2players or {} + Array.forEach(opponent.match2players, function(player) + player.extradata = player.extradata or {} + player.extradata.faction = MatchFunctions.getPlayerFaction(player) + end) + end) + + local games = MatchFunctions.extractMaps(match, opponents) + + local autoScoreFunction = MatchGroupInputUtil.canUseAutoScore(match, games) + and MatchFunctions.calculateMatchScore(games, opponents) + 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.mode = MatchFunctions.getMode(opponents) + + match.bestof = MatchFunctions.getBestOf(match.bestof) + local cancelled = Logic.readBool(Logic.emptyOr(match.cancelled, Variables.varDefault('tournament_cancelled'))) + if cancelled then + match.finished = match.finished or 'skip' + end + + local winnerInput = match.winner --[[@as string?]] + local finishedInput = match.finished --[[@as string?]] + match.finished = MatchGroupInputUtil.matchIsFinished(match, opponents) + + if match.finished then + 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 + + MatchGroupInputUtil.getCommonTournamentVars(match) + match.stream = Streams.processStreams(match) - CustomMatchGroupInput._getExtraData(match) + match.vod = Logic.nilIfEmpty(match.vod) + match.extradata = MatchFunctions.getExtraData(match, #games) + + match.games = games + match.opponents = opponents return match end ---@param matchArgs table ----@return table -function CustomMatchGroupInput._readDate(matchArgs) - local dateProps = MatchGroupInput.readDate(matchArgs.date, { +---@return {date: string, dateexact: boolean, timestamp: integer, timezoneId: string?, timezoneOffset: string?} +function MatchFunctions.readDate(matchArgs) + local dateProps = MatchGroupInputUtil.readDate(matchArgs.date, { 'matchDate', 'tournament_startdate', 'tournament_enddate' @@ -75,625 +133,393 @@ function CustomMatchGroupInput._readDate(matchArgs) 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 +---@param opponents table[] +---@return table[] +function MatchFunctions.extractMaps(match, opponents) + local maps = {} + local subGroup = 0 + for mapKey, mapInput, mapIndex in Table.iter.pairsByPrefix(match, 'map', {requireIndex = true}) do + local map + map, subGroup = MapFunctions.readMap(mapInput, subGroup, #opponents) - -- 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 + map.participants = MapFunctions.getParticipants(mapInput, opponents) ----@param match table -function CustomMatchGroupInput._getTournamentVars(match) - match.cancelled = Logic.emptyOr(match.cancelled, Variables.varDefault('tournament_cancelled', 'false')) - match.publishertier = Logic.emptyOr(match.publishertier, Variables.varDefault('tournament_publishertier')) - match.bestof = tonumber(Logic.emptyOr(match.bestof, Variables.varDefault('match_bestof'))) - Variables.varDefine('match_bestof', match.bestof) + map.mode = MapFunctions.getMode(mapInput, map.participants, opponents) - MatchGroupInput.getCommonTournamentVars(match) -end + Table.mergeInto(map.extradata, MapFunctions.getAdditionalExtraData(map, map.participants)) ----@param match table -function CustomMatchGroupInput._getExtraData(match) - match.extradata = { - casters = MatchGroupInput.readCasters(match), - ffa = 'false', - } + map.vod = Logic.emptyOr(mapInput.vod, match['vodgame' .. mapIndex]) - for prefix, mapVeto in Table.iter.pairsByPrefix(match, 'veto') do - match.extradata[prefix] = mapVeto and mw.ext.TeamLiquidIntegration.resolve_redirect(mapVeto) or nil - match.extradata[prefix .. 'by'] = match[prefix .. 'by'] - match.extradata[prefix .. 'displayname'] = match[prefix .. 'displayName'] + table.insert(maps, map) + match[mapKey] = nil end - Table.mergeInto(match.extradata, Table.filterByKey(match, function(key, value) - return key:match('subgroup%d+header') end)) + return maps end ----@param match table -function CustomMatchGroupInput._adjustData(match) - --parse opponents + set base sumscores + set mode - CustomMatchGroupInput._opponentInput(match) - - --main processing done here - local subGroupIndex = 0 - for _, _, mapIndex in Table.iter.pairsByPrefix(match, 'map') do - subGroupIndex = CustomMatchGroupInput._mapInput(match, mapIndex, subGroupIndex) +---@param maps table[] +---@param opponents table[] +---@return fun(opponentIndex: integer): integer? +function MatchFunctions.calculateMatchScore(maps, opponents) + return function(opponentIndex) + local calculatedScore = MatchGroupInputUtil.computeMatchScoreFromMapWinners(maps, opponentIndex) + if not calculatedScore then return end + local opponent = opponents[opponentIndex] + return calculatedScore + (opponent.extradata.advantage or 0) - (opponent.extradata.penalty or 0) end - - CustomMatchGroupInput._matchWinnerProcessing(match) end ----@param match table -function CustomMatchGroupInput._matchWinnerProcessing(match) - local bestof = match.bestof or DEFAULT_BEST_OF - local numberofOpponents = 0 - - Array.map(Array.range(1, MAX_NUM_OPPONENTS),function(opponentIndex) - local opponent = match['opponent' .. opponentIndex] - - if Logic.isEmpty(opponent) then return end - - numberofOpponents = numberofOpponents + 1 - - if Table.includes(ALLOWED_STATUSES, string.upper(opponent.score or '')) then - opponent.status = string.upper(opponent.score) - match.resulttype = RESULT_TYPE_DEFAULT - match.finished = true - opponent.score = -1 - - if opponent.status == DEFAULT_WIN_STATUS then - match.winner = opponentIndex - else - match.walkover = opponent.status - end - else - opponent.status = SCORE_STATUS - opponent.score = tonumber(opponent.score) or tonumber(opponent.sumscore) or -1 - if opponent.score > bestof / 2 then - match.finished = Logic.emptyOr(match.finished, true) - match.winner = tonumber(match.winner) or opponentIndex - end - end - - if Logic.readBool(match.cancelled) then - match.finished = true - if String.isEmpty(match.resulttype) and Logic.isEmpty(opponent.score) then - match.resulttype = RESULT_TYPE_NOT_PLAYED - opponent.score = opponent.score or -1 - end - end - - -- to not break the loop - return true - end) - - CustomMatchGroupInput._determineWinnerIfMissing(match) +---@param opponent table +---@return table +function MatchFunctions.getOpponentExtradata(opponent) + return { + advantage = tonumber(opponent.advantage), + penalty = tonumber(opponent.penalty), + score2 = opponent.score2, + } +end - 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 +---@param player table +---@return string +function MatchFunctions.getPlayerFaction(player) + return player.extradata.faction or Faction.defaultFaction +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 +---@param opponents {type: OpponentType} +---@return string +function MatchFunctions.getMode(opponents) + local opponentTypes = Array.map(opponents, Operator.property('type')) + return #Array.unique(opponentTypes) == 1 and opponentTypes[1] or MODE_MIXED end ----@param match table ----@return table -function CustomMatchGroupInput._determineWinnerIfMissing(match) - if Logic.readBool(match.finished) and Logic.isEmpty(match.winner) then - 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) + local bestof = tonumber(bestofInput) or tonumber(Variables.varDefault('match_bestof')) + + if bestof then + Variables.varDefine('match_bestof', bestof) end - return match + return bestof end ---OpponentInput functions - ---@param match table +---@param numberOfGames integer ---@return table -function CustomMatchGroupInput._opponentInput(match) - local opponentTypes = {} - - for opponentKey, opponent, opponentIndex in Table.iter.pairsByPrefix(match, 'opponent') do - opponent = Json.parseIfString(opponent) - - --Convert byes to literals - if Opponent.isBye(opponent) then - opponent = {type = Opponent.literal, name = 'BYE'} - end - - -- Opponent processing (first part) - -- Sort out extradata - opponent.extradata = { - advantage = opponent.advantage, - penalty = opponent.penalty, - score2 = opponent.score2, - } - - local partySize = Opponent.partySize(opponent.type) - if partySize then - opponent = CustomMatchGroupInput.processPartyOpponentInput(opponent, partySize) - elseif opponent.type == Opponent.team then - opponent = CustomMatchGroupInput.ProcessTeamOpponentInput(opponent, match.date) - opponent = CustomMatchGroupInput._readPlayersOfTeam(match, opponentIndex, opponent) - elseif opponent.type == Opponent.literal then - opponent = CustomMatchGroupInput.ProcessLiteralOpponentInput(opponent) - else - error('Unsupported Opponent Type "' .. (opponent.type or '') .. '"') - end - - --set initial opponent sumscore - opponent.sumscore = tonumber(opponent.extradata.advantage) or (-1 * (tonumber(opponent.extradata.penalty) or 0)) - - table.insert(opponentTypes, opponent.type) +function MatchFunctions.getExtraData(match, numberOfGames) + local extradata = { + casters = MatchGroupInputUtil.readCasters(match, {noSort = true}), + ffa = 'false', + } - match[opponentKey] = opponent + for prefix, mapVeto in Table.iter.pairsByPrefix(match, 'veto') do + extradata[prefix] = mapVeto and mw.ext.TeamLiquidIntegration.resolve_redirect(mapVeto) or nil + extradata[prefix .. 'by'] = match[prefix .. 'by'] + extradata[prefix .. 'displayname'] = match[prefix .. 'displayName'] end - assert(#opponentTypes <= MAX_NUM_OPPONENTS, 'Too many opponents') - - match.mode = Array.all(opponentTypes, function(opponentType) return opponentType == opponentTypes[1] end) - and opponentTypes[1] or MODE_MIXED + Table.mergeInto(extradata, Table.filterByKey(match, function(key) return key:match('subgroup%d+header') end)) - match.isTeamMatch = Array.any(opponentTypes, function(opponentType) return opponentType == Opponent.team end) - - return match + return extradata end ----reads the players of a team from input and wiki variables +---@param extradata table +---@param map string ---@param match table ----@param opponentIndex integer ----@param opponent table ----@return table -function CustomMatchGroupInput._readPlayersOfTeam(match, opponentIndex, opponent) - local players = {} - - local teamName = opponent.name - - local insertIntoPlayers = function(player) - if type(player) ~= 'table' or Logic.isEmpty(player) or Logic.isEmpty(player.name) then - return - end - - player.name = mw.ext.TeamLiquidIntegration.resolve_redirect(player.name):gsub(' ', '_') - player.flag = Flags.CountryName(player.flag) - player.displayname = Logic.emptyOr(player.displayname, player.displayName) - player.extradata = {faction = Faction.read(player.faction)} +---@param prefix string +---@param vetoIndex integer +function MatchFunctions.getVeto(extradata, map, match, prefix, vetoIndex) + extradata[prefix] = map and mw.ext.TeamLiquidIntegration.resolve_redirect(map) or nil + extradata[prefix .. 'by'] = match['vetoplayer' .. vetoIndex] or match['vetoopponent' .. vetoIndex] + extradata[prefix .. 'displayname'] = match[prefix .. 'displayName'] +end - players[player.name] = players[player.name] or {} - Table.deepMergeInto(players[player.name], player) +---@param mapInput table +---@param subGroup integer +---@param opponentCount integer +---@return table +---@return integer +function MapFunctions.readMap(mapInput, subGroup, opponentCount) + subGroup = tonumber(mapInput.subgroup) or (subGroup + 1) + + local mapName = mapInput.map + if mapName and mapName:upper() ~= TBD then + mapName = mw.ext.TeamLiquidIntegration.resolve_redirect(mapName) + elseif mapName then + mapName = TBD end - local playerIndex = 1 - local varPrefix = teamName .. '_p' .. playerIndex - local name = Variables.varDefault(varPrefix) - while name do - insertIntoPlayers{ - name = name, - displayName = Variables.varDefault(varPrefix .. 'dn'), - faction = Variables.varDefault(varPrefix .. 'faction'), - flag = Variables.varDefault(varPrefix .. 'flag'), + local map = { + map = mapName, + patch = Variables.varDefault('tournament_patch', ''), + subgroup = subGroup, + extradata = { + comment = mapInput.comment, + header = mapInput.header, } - playerIndex = playerIndex + 1 - varPrefix = teamName .. '_p' .. playerIndex - name = Variables.varDefault(varPrefix) - end - - --players from manual input as `opponnetX_pY` - for _, player in Table.iter.pairsByPrefix(match, 'opponent' .. opponentIndex .. '_p') do - insertIntoPlayers(Json.parseIfString(player)) - end + } - opponent.match2players = Array.extractValues(players) - --set default faction for unset factions - Array.forEach(opponent.match2players, function(player) - player.extradata.faction = player.extradata.faction or Faction.defaultFaction + map.finished = MapFunctions.isFinished(mapInput, opponentCount) + local opponentInfo = Array.map(Array.range(1, opponentCount), function(opponentIndex) + local score, status = MatchGroupInputUtil.computeOpponentScore({ + walkover = mapInput.walkover, + winner = mapInput.winner, + opponentIndex = opponentIndex, + score = mapInput['score' .. opponentIndex], + }, MapFunctions.calculateMapScore(mapInput.winner, map.finished)) + return {score = score, status = status} end) - return opponent -end + map.scores = Array.map(opponentInfo, Operator.property('score')) ----@param opponent table ----@return table -function CustomMatchGroupInput.ProcessLiteralOpponentInput(opponent) - local faction = opponent.faction - local flag = opponent.flag - local name = opponent.name or opponent[1] - local extradata = opponent.extradata - - local players = {} - if String.isNotEmpty(faction) or String.isNotEmpty(flag) then - players[1] = { - displayname = name, - name = TBD:upper(), - flag = Flags.CountryName(flag), - extradata = {faction = Faction.read(faction) or Faction.defaultFaction} - } - extradata.hasFactionOrFlag = true + if map.finished or MatchGroupInputUtil.isNotPlayed(map.winner, mapInput.finished) then + map.resulttype = MatchGroupInputUtil.getResultType(mapInput.winner, mapInput.finished, opponentInfo) + map.walkover = MatchGroupInputUtil.getWalkover(map.resulttype, opponentInfo) + map.winner = MatchGroupInputUtil.getWinner(map.resulttype, mapInput.winner, opponentInfo) end - return { - type = opponent.type, - name = name, - score = opponent.score, - extradata = extradata, - match2players = players - } + return map, subGroup end ----@param opponent table ----@param partySize integer ----@return table -function CustomMatchGroupInput.processPartyOpponentInput(opponent, partySize) - local players = {} - local links = {} - - for playerIndex = 1, partySize do - local name = Logic.emptyOr(opponent['p' .. playerIndex], opponent[playerIndex]) or '' - local link = mw.ext.TeamLiquidIntegration.resolve_redirect(Logic.emptyOr( - opponent['p' .. playerIndex .. 'link'], - Variables.varDefault(name .. '_page') - ) or name):gsub(' ', '_') - table.insert(links, link) - - table.insert(players, { - displayname = name, - name = link, - flag = Flags.CountryName(Logic.emptyOr( - opponent['p' .. playerIndex .. 'flag'], - Variables.varDefault(name .. '_flag') - )), - extradata = {faction = Faction.read(Logic.emptyOr( - opponent['p' .. playerIndex .. 'faction'], - Variables.varDefault(name .. '_faction') - )) or Faction.defaultFaction} - }) +---@param mapInput table +---@param opponentCount integer +---@return boolean +function MapFunctions.isFinished(mapInput, opponentCount) + local finished = Logic.readBoolOrNil(mapInput.finished) + if finished ~= nil then + return finished end - table.sort(links) - - return { - type = opponent.type, - name = table.concat(links, ' / '), - score = opponent.score, - extradata = opponent.extradata, - match2players = players - } -end - ----@param opponent table ----@param date string ----@return table -function CustomMatchGroupInput.ProcessTeamOpponentInput(opponent, date) - local template = string.lower(Logic.emptyOr(opponent.template, opponent[1], '')--[[@as string]]):gsub('_', ' ') - - if String.isEmpty(template) or template == 'noteam' then - opponent = Table.merge(opponent, Opponent.blank(Opponent.team)) - opponent.name = Opponent.toName(opponent) - return opponent + if Logic.isNotEmpty(mapInput.winner) then + return true end - assert(mw.ext.TeamTemplate.teamexists(template), 'Missing team template "' .. template .. '"') - - local templateData = mw.ext.TeamTemplate.raw(template, date) - - opponent.icon = templateData.image - opponent.icondark = Logic.emptyOr(templateData.imagedark, templateData.image) - opponent.name = templateData.page:gsub(' ', '_') - opponent.template = templateData.templatename or template - - return opponent -end - ---MapInput functions - ----@param match table ----@param mapIndex integer ----@param subGroupIndex integer ----@return integer -function CustomMatchGroupInput._mapInput(match, mapIndex, subGroupIndex) - local map = Json.parseIfString(match['map' .. mapIndex]) - map.map = mw.ext.TeamLiquidIntegration.resolve_redirect(map.map or '') - - -- set initial extradata for maps - map.extradata = { - comment = map.comment, - header = map.header, - } - - -- determine score, resulttype, walkover and winner - map = CustomMatchGroupInput._mapWinnerProcessing(map) - - -- get participants data for the map + get map mode + winnerfaction and loserfaction - --(w/l faction stuff only for 1v1 maps) - CustomMatchGroupInput.ProcessPlayerMapData(map, match, 2) - - --adjust sumscore for winner opponent - if (tonumber(map.winner) or 0) > 0 then - match['opponent' .. map.winner].sumscore = - match['opponent' .. map.winner].sumscore + 1 + if Logic.isNotEmpty(mapInput.walkover) then + return true end - -- handle subgroup stuff if team match - if match.isTeamMatch then - map.subgroup = tonumber(map.subgroup) or (subGroupIndex + 1) - subGroupIndex = map.subgroup + if Logic.isNotEmpty(mapInput.finished) then + return true end - match['map' .. mapIndex] = map + -- check for manual score inputs + for opponentIndex = 1, opponentCount do + if String.isNotEmpty(mapInput['score' .. opponentIndex]) then + return true + end + end - return subGroupIndex + return false 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 = 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 winner = tonumber(map.winner) - if Logic.isNotEmpty(map.walkover) then - local walkoverInput = tonumber(map.walkover) - if walkoverInput == 1 or walkoverInput == 2 or walkoverInput == 0 then - 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.team then + Table.mergeInto(participants, MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex)) + return + elseif opponent.type == Opponent.literal then + return end - map.walkover = Table.includes(ALLOWED_STATUSES, map.walkover) and map.walkover or 'L' - map.scores = {-1, -1} - map.resulttype = 'default' - map.winner = winner + Table.mergeInto(participants, MapFunctions.getPartyParticipants(mapInput, opponent, opponentIndex)) + end) - return map - end + return participants +end - if hasManualScores then - map.winner = winner or CustomMatchGroupInput._getWinner(indexedScores) +---@param mapInput table +---@param opponent table +---@param opponentIndex integer +---@return table +function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) + local players = opponent.match2players + + ---@type {input:string,faction:string?,link:string?,heroes:string?,heroesCheckDisabled:boolean,playedRandom:boolean}[] + local participantsList = Array.mapIndexes(function(playerIndex) + local prefix = 't' .. opponentIndex .. 'p' .. playerIndex + + if Logic.isEmpty(mapInput[prefix]) then return end + + return { + input = mapInput[prefix], + link = Logic.nilIfEmpty(mapInput[prefix .. 'link']), + faction = Faction.read(mapInput[prefix .. 'faction']), + heroes = mapInput[prefix .. 'heroes'], + heroesCheckDisabled = Logic.readBool(mapInput[prefix .. 'noheroescheck']), + playedRandom = Logic.readBool(mapInput[prefix .. 'random']), + } + end) - return map - end + local participants = {} - if map.winner == 'skip' then - map.scores = {-1, -1} - map.resulttype = RESULT_TYPE_NOT_PLAYED - elseif winner == 1 then - map.scores = {1, 0} - elseif winner == 2 then - map.scores = {0, 1} - elseif winner == 0 or map.winner == 'draw' then - map.scores = {0.5, 0.5} - map.resulttype = 'draw' - end + Array.forEach(participantsList, function(participantInput, position) + local nameInput = participantInput.input - map.winner = winner + local isTBD = nameInput:upper() == TBD - return map -end + local link = participantInput.link or Variables.varDefault(nameInput .. '_page') or nameInput + link = Page.pageifyLink(link) --[[@as string -- can't be nil as input isn't nil]] ----@param map table ----@param match table ----@param numberOfOpponents integer -function CustomMatchGroupInput.ProcessPlayerMapData(map, match, numberOfOpponents) - local participants = {} - local modeParts = {} - for opponentIndex = 1, numberOfOpponents do - local opponent = match['opponent' .. opponentIndex] - local partySize = Opponent.partySize(opponent.type) - local players = opponent.match2players - if partySize then - table.insert(modeParts, partySize) - CustomMatchGroupInput._processPartyPlayerMapData(players, map, opponentIndex, participants) - elseif opponent.type == Opponent.team then - table.insert(modeParts, CustomMatchGroupInput._processTeamPlayerMapData(players, map, opponentIndex, participants)) - elseif opponent.type == Opponent.literal then - table.insert(modeParts, 'literal') + local playerIndex = MapFunctions.getPlayerIndex(players, link, nameInput) + + -- in case we have a TBD or a player not known in match2players inster a new player in match2players + if isTBD or playerIndex == 0 then + table.insert(players, { + name = isTBD and TBD or link, + displayname = isTBD and TBD or nameInput, + extradata = {faction = participantInput.faction or Faction.defaultFaction}, + }) + playerIndex = #players end - end - map.mode = table.concat(modeParts, 'v') - map.participants = participants + local player = players[playerIndex] + local faction = participantInput.faction or player.extradata.faction - if numberOfOpponents ~= MAX_NUM_OPPONENTS or map.mode ~= '1v1' then - return - end + participants[opponentIndex .. '_' .. playerIndex] = { + faction = faction, + player = link, + position = position, + flag = Flags.CountryName(player.flag), + random = participantInput.playedRandom, + heroes = MapFunctions.readHeroes( + participantInput.heroes, + faction, + link, + participantInput.heroesCheckDisabled + ), + } + end) - local opponentFactions, playerNameArray, heroesData - = CustomMatchGroupInput._fetchOpponentMapParticipantData(participants) - map.extradata = Table.merge(map.extradata, heroesData) - if tonumber(map.winner) == 1 then - map.extradata.winnerfaction = opponentFactions[1] - map.extradata.loserfaction = opponentFactions[2] - elseif tonumber(map.winner) == 2 then - map.extradata.winnerfaction = opponentFactions[2] - map.extradata.loserfaction = opponentFactions[1] - end - map.extradata.opponent1 = playerNameArray[1] - map.extradata.opponent2 = playerNameArray[2] + return participants end ----@param participants table ----@return table ----@return table ----@return table -function CustomMatchGroupInput._fetchOpponentMapParticipantData(participants) - local opponentFactions, playerNameArray, heroesData = {}, {}, {} - for participantKey, participantData in pairs(participants) do - local opponentIndex = tonumber(string.sub(participantKey, 1, 1)) - -- opponentIndex can not be nil due to the format of the participants keys - ---@cast opponentIndex -nil - opponentFactions[opponentIndex] = participantData.faction - playerNameArray[opponentIndex] = participantData.player - Array.forEach(participantData.heroes or {}, function(hero, heroIndex) - heroesData['opponent' .. opponentIndex .. 'hero' .. heroIndex] = hero - end) +---@param players {name: string, displayname: string} +---@param name string +---@param displayName string +---@return integer +function MapFunctions.getPlayerIndex(players, name, displayName) + local playerIndex = Array.indexOf(players, function(player) return player.name == name end) + + if playerIndex ~= 0 then + return playerIndex end - return opponentFactions, playerNameArray, heroesData + return Array.indexOf(players, function(player) return player.displayname == displayName end) end ----@param players table[] ----@param map table +---@param mapInput table +---@param opponent table ---@param opponentIndex integer ----@param participants table ----@return table -function CustomMatchGroupInput._processPartyPlayerMapData(players, map, opponentIndex, participants) +---@return table +function MapFunctions.getPartyParticipants(mapInput, opponent, opponentIndex) + local players = opponent.match2players + + -- resolve the aliases in case they are used local prefix = 't' .. opponentIndex .. 'p' - for playerIndex, player in pairs(players) do - local faction = Logic.emptyOr( - map[prefix .. playerIndex .. 'faction'], - player.extradata.faction, - Faction.defaultFaction - ) - faction = Faction.read(faction) + local participants = {} + + Array.forEach(players, function(player, playerIndex) + local faction = Faction.read(mapInput['t' .. opponentIndex .. 'p' .. playerIndex .. 'faction']) + or player.extradata.faction participants[opponentIndex .. '_' .. playerIndex] = { - faction = faction, + faction = Faction.read(faction or player.extradata.faction), player = player.name, - heroes = CustomMatchGroupInput._readHeroes( - map[prefix .. playerIndex .. 'heroes'], + heroes = MapFunctions.readHeroes( + mapInput[prefix .. playerIndex .. 'heroes'], faction, player.name, - Logic.readBool(map[prefix .. playerIndex .. 'noheroescheck']) + Logic.readBool(mapInput[prefix .. playerIndex .. 'noheroescheck']) ), } - end + end) return participants end ----@param players table[] ----@param map table ----@param opponentIndex integer ----@param participants table ----@return integer -function CustomMatchGroupInput._processTeamPlayerMapData(players, map, opponentIndex, participants) - local amountOfTbds = 0 - local playerData = {} - - local numberOfPlayers = 0 - for prefix, playerInput, playerIndex in Table.iter.pairsByPrefix(map, 't' .. opponentIndex .. 'p') do - numberOfPlayers = numberOfPlayers + 1 - if playerInput:lower() == TBD then - amountOfTbds = amountOfTbds + 1 - else - local link = Logic.emptyOr(map[prefix .. 'link'], Variables.varDefault(playerInput .. '_page')) or playerInput - link = mw.ext.TeamLiquidIntegration.resolve_redirect(link):gsub(' ', '_') - - playerData[link] = { - faction = Faction.read(map[prefix .. 'faction']), - position = playerIndex, - heroes = map[prefix .. 'heroes'], - heroesCheckDisabled = Logic.readBool(map[prefix .. 'noheroescheck']), - playedRandom = Logic.readBool(map[prefix .. 'random']), - displayName = playerInput, - } - end +---@param mapInput table # the input data +---@param participants table +---@param opponents table[] +---@return string +function MapFunctions.getMode(mapInput, participants, opponents) + -- assume we have a min of 2 opponents in a game + local playerCounts = {0, 0} + for key in pairs(participants) do + local parsedOpponentIndex = key:match('(%d+)_%d+') + local opponetIndex = tonumber(parsedOpponentIndex) --[[@as integer]] + playerCounts[opponetIndex] = (playerCounts[opponetIndex] or 0) + 1 end - local addToParticipants = function(currentPlayer, player, playerIndex) - local faction = currentPlayer.faction or (player.extradata or {}).faction or Faction.defaultFaction + local modeParts = Array.map(playerCounts, function(count, opponentIndex) + if count == 0 then + return Opponent.literal + end - participants[opponentIndex .. '_' .. playerIndex] = { - faction = faction, - player = player.name, - position = currentPlayer.position, - flag = Flags.CountryName(player.flag), - heroes = CustomMatchGroupInput._readHeroes( - currentPlayer.heroes, - faction, - player.name, - currentPlayer.heroesCheckDisabled - ), - random = currentPlayer.playedRandom, - } + return count + end) + + return table.concat(modeParts, 'v') +end + +---@param map table +---@param participants table +---@return table +function MapFunctions.getAdditionalExtraData(map, participants) + if map.mode ~= '1v1' then return {} end + + local extradata = MapFunctions.getHeroesExtradata(participants) + + local players = {} + for _, player in Table.iter.spairs(participants) do + table.insert(players, player) end - Array.forEach(players, function(player, playerIndex) - local currentPlayer = playerData[player.name] - if not currentPlayer then return end + extradata.opponent1 = players[1].player + extradata.opponent2 = players[2].player - addToParticipants(currentPlayer, player, playerIndex) - playerData[player.name] = nil - end) + if map.winner ~= 1 and map.winner ~= 2 then + return extradata + end + local loser = 3 - map.winner - -- if we have players not already in the match2players insert them - -- this is to break conditional data loops between match2 and teamCard/HDB - Table.iter.forEachPair(playerData, function(playerLink, player) - local faction = player.faction or Faction.defaultFaction - table.insert(players, { - name = playerLink, - displayname = player.displayName, - extradata = {faction = faction}, - }) - addToParticipants(player, players[#players], #players) - numberOfPlayers = numberOfPlayers + 1 - end) + extradata.winnerfaction = players[map.winner].faction + extradata.loserfaction = players[loser].faction - Array.forEach(Array.range(1, amountOfTbds), function(tbdIndex) - participants[opponentIndex .. '_' .. (#players + tbdIndex)] = { - faction = Faction.defaultFaction, - player = TBD:upper(), - } - end) + return extradata +end - map.participants = participants +--- additionally store heroes in extradata so we can condition on them +---@param participants table +---@return table +function MapFunctions.getHeroesExtradata(participants) + local extradata = {} + for participantKey, participant in Table.iter.spairs(participants) do + local opponentIndex = string.match(participantKey, '^(%d+)_') + Array.forEach(participant.heroes or {}, function(hero, heroIndex) + extradata['opponent' .. opponentIndex .. 'hero' .. heroIndex] = hero + end) + end - return numberOfPlayers + return extradata end ---@param heroesInput string? @@ -701,7 +527,7 @@ end ---@param playerName string ---@param ignoreFactionHeroCheck boolean ---@return string[]? -function CustomMatchGroupInput._readHeroes(heroesInput, faction, playerName, ignoreFactionHeroCheck) +function MapFunctions.readHeroes(heroesInput, faction, playerName, ignoreFactionHeroCheck) if String.isEmpty(heroesInput) then return end @@ -715,38 +541,10 @@ function CustomMatchGroupInput._readHeroes(heroesInput, faction, playerName, ign local isCoreFaction = Table.includes(Faction.coreFactions, faction) assert(ignoreFactionHeroCheck or not isCoreFaction or faction == heroData.faction or heroData.faction == DEFAULT_HERO_FACTION, - 'Invalid hero input "' .. hero .. '" for faction "' - .. Faction.toName(faction) .. '" of player "' .. playerName .. '"') + 'Invalid hero input "' .. hero .. '" for faction "' .. Faction.toName(faction) .. '" of player "' .. playerName .. '"') return heroData.name end) end ----@param indexedScores table ----@return integer? -function CustomMatchGroupInput._getWinner(indexedScores) - table.sort(indexedScores, CustomMatchGroupInput._mapWinnerSortFunction) - - return indexedScores[1].index -end - ----@param opponent1 table ----@param opponent2 table ----@return boolean -function CustomMatchGroupInput._mapWinnerSortFunction(opponent1, opponent2) - local opponent1Norm = opponent1.status == SCORE_STATUS - local opponent2Norm = opponent2.status == SCORE_STATUS - - if opponent1Norm and opponent2Norm then - return tonumber(opponent1.score) > tonumber(opponent2.score) - elseif opponent1Norm then return true - elseif opponent2Norm then return false - elseif opponent1.status == DEFAULT_WIN_STATUS then return true - elseif Table.includes(ALLOWED_STATUSES, opponent1.status) then return false - elseif opponent2.status == DEFAULT_WIN_STATUS then return false - elseif Table.includes(ALLOWED_STATUSES, opponent2.status) then return true - else return true - end -end - return CustomMatchGroupInput From 1905c56239ec1ccf7b5b96259f6af7efb7f164ba Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Mon, 26 Aug 2024 17:22:06 +0200 Subject: [PATCH 04/16] linter: line is too long --- components/match2/wikis/stormgate/match_group_input_custom.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 2227d087acb..aa8ff3d57da 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -541,7 +541,8 @@ function MapFunctions.readHeroes(heroesInput, faction, playerName, ignoreFaction local isCoreFaction = Table.includes(Faction.coreFactions, faction) assert(ignoreFactionHeroCheck or not isCoreFaction or faction == heroData.faction or heroData.faction == DEFAULT_HERO_FACTION, - 'Invalid hero input "' .. hero .. '" for faction "' .. Faction.toName(faction) .. '" of player "' .. playerName .. '"') + 'Invalid hero input "' .. hero .. '" for faction "' .. Faction.toName(faction) + .. '" of player "' .. playerName .. '"') return heroData.name end) From 89bcd028594d5c3f5fa7835eadc75ab015b6f583 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Wed, 28 Aug 2024 12:29:59 +0200 Subject: [PATCH 05/16] as per review in warcraft --- .../stormgate/match_group_input_custom.lua | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index aa8ff3d57da..f1ff3d17407 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -210,7 +210,6 @@ end function MatchFunctions.getExtraData(match, numberOfGames) local extradata = { casters = MatchGroupInputUtil.readCasters(match, {noSort = true}), - ffa = 'false', } for prefix, mapVeto in Table.iter.pairsByPrefix(match, 'veto') do @@ -224,17 +223,6 @@ function MatchFunctions.getExtraData(match, numberOfGames) return extradata end ----@param extradata table ----@param map string ----@param match table ----@param prefix string ----@param vetoIndex integer -function MatchFunctions.getVeto(extradata, map, match, prefix, vetoIndex) - extradata[prefix] = map and mw.ext.TeamLiquidIntegration.resolve_redirect(map) or nil - extradata[prefix .. 'by'] = match['vetoplayer' .. vetoIndex] or match['vetoopponent' .. vetoIndex] - extradata[prefix .. 'displayname'] = match[prefix .. 'displayName'] -end - ---@param mapInput table ---@param subGroup integer ---@param opponentCount integer @@ -252,7 +240,6 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount) local map = { map = mapName, - patch = Variables.varDefault('tournament_patch', ''), subgroup = subGroup, extradata = { comment = mapInput.comment, @@ -333,10 +320,10 @@ end function MapFunctions.getParticipants(mapInput, opponents) local participants = {} Array.forEach(opponents, function(opponent, opponentIndex) - if opponent.type == Opponent.team then - Table.mergeInto(participants, MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex)) + if opponent.type == Opponent.literal then return - elseif opponent.type == Opponent.literal then + elseif opponent.type == Opponent.team then + Table.mergeInto(participants, MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex)) return end Table.mergeInto(participants, MapFunctions.getPartyParticipants(mapInput, opponent, opponentIndex)) From 5ed921ac8629411b4375a482df4dd2881fff3e3c Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Wed, 28 Aug 2024 12:31:49 +0200 Subject: [PATCH 06/16] from wc review that applies here but not there --- components/match2/wikis/stormgate/match_group_input_custom.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index f1ff3d17407..9a6ec072b8c 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -175,7 +175,6 @@ function MatchFunctions.getOpponentExtradata(opponent) return { advantage = tonumber(opponent.advantage), penalty = tonumber(opponent.penalty), - score2 = opponent.score2, } end @@ -247,7 +246,7 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount) } } - map.finished = MapFunctions.isFinished(mapInput, opponentCount) + map.finished = MatchGroupInputUtil.mapIsFinished(map) local opponentInfo = Array.map(Array.range(1, opponentCount), function(opponentIndex) local score, status = MatchGroupInputUtil.computeOpponentScore({ walkover = mapInput.walkover, From 10dc3d62b7423d46cb74088fc4bef0bab6f962d4 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Fri, 30 Aug 2024 12:34:56 +0200 Subject: [PATCH 07/16] double space fix --- components/match2/wikis/stormgate/match_group_input_custom.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 9a6ec072b8c..0c617ff2f44 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -402,7 +402,7 @@ end ---@param displayName string ---@return integer function MapFunctions.getPlayerIndex(players, name, displayName) - local playerIndex = Array.indexOf(players, function(player) return player.name == name end) + local playerIndex = Array.indexOf(players, function(player) return player.name == name end) if playerIndex ~= 0 then return playerIndex From 8a0951136b3e825d5031e4bf99f00d8656fbac60 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Wed, 4 Sep 2024 12:38:02 +0200 Subject: [PATCH 08/16] kick unused function --- .../stormgate/match_group_input_custom.lua | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 0c617ff2f44..075332324d5 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -268,37 +268,6 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount) return map, subGroup end ----@param mapInput table ----@param opponentCount integer ----@return boolean -function MapFunctions.isFinished(mapInput, opponentCount) - local finished = Logic.readBoolOrNil(mapInput.finished) - if finished ~= nil then - return finished - end - - if Logic.isNotEmpty(mapInput.winner) then - return true - end - - if Logic.isNotEmpty(mapInput.walkover) then - return true - end - - if Logic.isNotEmpty(mapInput.finished) then - return true - end - - -- check for manual score inputs - for opponentIndex = 1, opponentCount do - if String.isNotEmpty(mapInput['score' .. opponentIndex]) then - return true - end - end - - return false -end - ---@param winnerInput string|integer|nil ---@param finished boolean ---@return fun(opponentIndex: integer): integer? From 2152576be435fd08091edfcdcc23ff49adca9b00 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Sat, 7 Sep 2024 16:16:40 +0200 Subject: [PATCH 09/16] fix map handling --- .../match2/wikis/stormgate/match_group_input_custom.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 075332324d5..da934a26157 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -246,7 +246,7 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount) } } - map.finished = MatchGroupInputUtil.mapIsFinished(map) + map.finished = MatchGroupInputUtil.mapIsFinished(mapInput) local opponentInfo = Array.map(Array.range(1, opponentCount), function(opponentIndex) local score, status = MatchGroupInputUtil.computeOpponentScore({ walkover = mapInput.walkover, @@ -259,7 +259,7 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount) map.scores = Array.map(opponentInfo, Operator.property('score')) - if map.finished or MatchGroupInputUtil.isNotPlayed(map.winner, mapInput.finished) then + if map.finished or MatchGroupInputUtil.isNotPlayed(mapInput.winner, mapInput.finished) then map.resulttype = MatchGroupInputUtil.getResultType(mapInput.winner, mapInput.finished, opponentInfo) map.walkover = MatchGroupInputUtil.getWalkover(map.resulttype, opponentInfo) map.winner = MatchGroupInputUtil.getWinner(map.resulttype, mapInput.winner, opponentInfo) From 1aaa4b21dd82f958eb1c4443f1e4de64046ad97f Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Sat, 7 Sep 2024 16:29:49 +0200 Subject: [PATCH 10/16] bold submatch scores on submatch draw --- components/match2/wikis/stormgate/match_summary.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/match2/wikis/stormgate/match_summary.lua b/components/match2/wikis/stormgate/match_summary.lua index 4d8fddc3350..3ae5ba25147 100644 --- a/components/match2/wikis/stormgate/match_summary.lua +++ b/components/match2/wikis/stormgate/match_summary.lua @@ -369,7 +369,7 @@ function CustomMatchSummary._submatchHeader(submatch) ---@param opponentIndex any ---@return Html local createScore = function(opponentIndex) - local isWinner = opponentIndex == submatch.winner + local isWinner = opponentIndex == submatch.winner or submatch.resultType == 'draw' if submatch.resultType == 'default' then return OpponentDisplay.BlockScore{ isWinner = isWinner, From 45709274e5e004483161b24a593a92ba443c38d4 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 10 Sep 2024 07:38:00 +0200 Subject: [PATCH 11/16] #4650 --- .../match2/wikis/stormgate/match_group_input_custom.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index da934a26157..072debed4f9 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -98,10 +98,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 MatchGroupInputUtil.getCommonTournamentVars(match) @@ -259,7 +256,7 @@ function MapFunctions.readMap(mapInput, subGroup, opponentCount) map.scores = Array.map(opponentInfo, Operator.property('score')) - if map.finished or MatchGroupInputUtil.isNotPlayed(mapInput.winner, mapInput.finished) then + if map.finished then map.resulttype = MatchGroupInputUtil.getResultType(mapInput.winner, mapInput.finished, opponentInfo) map.walkover = MatchGroupInputUtil.getWalkover(map.resulttype, opponentInfo) map.winner = MatchGroupInputUtil.getWinner(map.resulttype, mapInput.winner, opponentInfo) From a46879f1b346cf9efcce501081d0708eb8b391a7 Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Fri, 13 Sep 2024 13:59:07 +0200 Subject: [PATCH 12/16] #4645 --- .../stormgate/match_group_input_custom.lua | 119 ++++++++---------- 1 file changed, 49 insertions(+), 70 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 072debed4f9..62cdb577203 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -13,7 +13,6 @@ local HeroData = mw.loadData('Module:HeroData') local Logic = require('Module:Logic') local Lua = require('Module:Lua') local Operator = require('Module:Operator') -local Page = require('Module:Page') local String = require('Module:StringUtils') local Table = require('Module:Table') local Variables = require('Module:Variables') @@ -302,79 +301,59 @@ end ---@param opponentIndex integer ---@return table function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) - local players = opponent.match2players - - ---@type {input:string,faction:string?,link:string?,heroes:string?,heroesCheckDisabled:boolean,playedRandom:boolean}[] - local participantsList = Array.mapIndexes(function(playerIndex) - local prefix = 't' .. opponentIndex .. 'p' .. playerIndex - - if Logic.isEmpty(mapInput[prefix]) then return end - - return { - input = mapInput[prefix], - link = Logic.nilIfEmpty(mapInput[prefix .. 'link']), - faction = Faction.read(mapInput[prefix .. 'faction']), - heroes = mapInput[prefix .. 'heroes'], - heroesCheckDisabled = Logic.readBool(mapInput[prefix .. 'noheroescheck']), - playedRandom = Logic.readBool(mapInput[prefix .. 'random']), - } + local players = Array.mapIndexes(function(playerIndex) + return Logic.nilIfEmpty(mapInput['t' .. opponentIndex .. 'p' .. playerIndex]) end) - local participants = {} - - Array.forEach(participantsList, function(participantInput, position) - local nameInput = participantInput.input - - local isTBD = nameInput:upper() == TBD - - local link = participantInput.link or Variables.varDefault(nameInput .. '_page') or nameInput - link = Page.pageifyLink(link) --[[@as string -- can't be nil as input isn't nil]] - - local playerIndex = MapFunctions.getPlayerIndex(players, link, nameInput) - - -- in case we have a TBD or a player not known in match2players inster a new player in match2players - if isTBD or playerIndex == 0 then - table.insert(players, { - name = isTBD and TBD or link, - displayname = isTBD and TBD or nameInput, - extradata = {faction = participantInput.faction or Faction.defaultFaction}, - }) - playerIndex = #players - end - - local player = players[playerIndex] - local faction = participantInput.faction or player.extradata.faction - - participants[opponentIndex .. '_' .. playerIndex] = { - faction = faction, - player = link, - position = position, - flag = Flags.CountryName(player.flag), - random = participantInput.playedRandom, - heroes = MapFunctions.readHeroes( - participantInput.heroes, - faction, - link, - participantInput.heroesCheckDisabled - ), - } + local participants, unattachedParticipants = MatchGroupInputUtil.parseParticipants( + opponent.match2players, + players, + function(playerIndex) + local prefix = 't' .. opponentIndex .. 'p' .. playerIndex + return { + name = mapInput[prefix], + link = Logic.nilIfEmpty(mapInput[prefix .. 'link']) or Variables.varDefault(mapInput[prefix] .. '_page'), + faction = Faction.read(mapInput[prefix .. 'race']), + heroes = mapInput[prefix .. 'heroes'], + heroesCheckDisabled = Logic.readBool(mapInput[prefix .. 'noheroescheck']), + playedRandom = Logic.readBool(mapInput[prefix .. 'random']), + } + end, + function(playerIndex, playerIdData, playerInputData) + local faction = playerInputData.faction or (playerIdData.extradata or {}).faction or Faction.defaultFaction + local link = playerIdData.name or playerInputData.link + return { + faction = faction, + player = link, + flag = Flags.CountryName(playerIdData.flag), + position = playerIndex, + random = playerInputData.playedRandom, + heroes = MapFunctions.readHeroes( + playerInputData.heroes, + faction, + link, + playerInputData.heroesCheckDisabled + ), + } + end, + OPPONENT_CONFIG + ) + + Array.forEach(unattachedParticipants, function(participant) + local name = mapInput['t' .. opponentIndex .. 'p' .. participant.position] + local nameUpper = name:upper() + local isTBD = nameUpper == TBD + + table.insert(opponent.match2players, { + name = isTBD and TBD or participant.player, + displayname = isTBD and TBD or name, + flag = participant.flag, + extradata = {faction = participant.faction}, + }) + participants[#opponent.match2players] = participant end) - return participants -end - ----@param players {name: string, displayname: string} ----@param name string ----@param displayName string ----@return integer -function MapFunctions.getPlayerIndex(players, name, displayName) - local playerIndex = Array.indexOf(players, function(player) return player.name == name end) - - if playerIndex ~= 0 then - return playerIndex - end - - return Array.indexOf(players, function(player) return player.displayname == displayName end) + return Table.map(participants, MatchGroupInputUtil.prefixPartcipants(opponentIndex)) end ---@param mapInput table From a6cd5726beca784b6ec2be6fb42505f5e6a8f1fa Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 17 Sep 2024 07:34:03 +0200 Subject: [PATCH 13/16] missing faction normalization --- components/match2/wikis/stormgate/match_group_input_custom.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 62cdb577203..922e6587517 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -177,7 +177,7 @@ end ---@param player table ---@return string function MatchFunctions.getPlayerFaction(player) - return player.extradata.faction or Faction.defaultFaction + return Faction.read(player.extradata.faction) or Faction.defaultFaction end ---@param opponents {type: OpponentType} From 7afc768f3556a2e5303bb052515ef48e9d731b5b Mon Sep 17 00:00:00 2001 From: hjpalpha Date: Tue, 17 Sep 2024 11:08:01 +0200 Subject: [PATCH 14/16] #4752 --- .../wikis/stormgate/match_group_input_custom.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 922e6587517..f085561cbab 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -313,26 +313,24 @@ function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) return { name = mapInput[prefix], link = Logic.nilIfEmpty(mapInput[prefix .. 'link']) or Variables.varDefault(mapInput[prefix] .. '_page'), - faction = Faction.read(mapInput[prefix .. 'race']), - heroes = mapInput[prefix .. 'heroes'], - heroesCheckDisabled = Logic.readBool(mapInput[prefix .. 'noheroescheck']), - playedRandom = Logic.readBool(mapInput[prefix .. 'random']), } end, function(playerIndex, playerIdData, playerInputData) - local faction = playerInputData.faction or (playerIdData.extradata or {}).faction or Faction.defaultFaction - local link = playerIdData.name or playerInputData.link + local prefix = 't' .. opponentIndex .. 'p' .. playerIndex + local faction = Faction.read(mapInput[prefix .. 'faction']) + or (playerIdData.extradata or {}).faction or Faction.defaultFaction + local link = playerIdData.name or playerInputData.link or playerInputData.name:gsub(' ', '_') return { faction = faction, player = link, flag = Flags.CountryName(playerIdData.flag), position = playerIndex, - random = playerInputData.playedRandom, + random = Logic.readBool(mapInput[prefix .. 'random']), heroes = MapFunctions.readHeroes( - playerInputData.heroes, + mapInput[prefix .. 'heroes'], faction, link, - playerInputData.heroesCheckDisabled + Logic.readBool(mapInput[prefix .. 'noheroescheck']) ), } end, From 1c594578bb0872448585df90f97035712e96f2c3 Mon Sep 17 00:00:00 2001 From: hjpalpha <75081997+hjpalpha@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:52:25 +0200 Subject: [PATCH 15/16] Update components/match2/wikis/stormgate/match_group_input_custom.lua Co-authored-by: Rikard Blixt --- components/match2/wikis/stormgate/match_group_input_custom.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index f085561cbab..4185fc56529 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -25,7 +25,6 @@ local Streams = Lua.import('Module:Links/Stream') local OPPONENT_CONFIG = { resolveRedirect = true, pagifyTeamNames = true, - pagifyPlayerNames = true, } local TBD = 'TBD' local DEFAULT_HERO_FACTION = HeroData.default.faction From f2775ec1d15af09c0a6bafb0aa1a5a30fa21cd82 Mon Sep 17 00:00:00 2001 From: hjpalpha <75081997+hjpalpha@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:53:40 +0200 Subject: [PATCH 16/16] Update components/match2/wikis/stormgate/match_group_input_custom.lua Co-authored-by: Rikard Blixt --- components/match2/wikis/stormgate/match_group_input_custom.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/match2/wikis/stormgate/match_group_input_custom.lua b/components/match2/wikis/stormgate/match_group_input_custom.lua index 4185fc56529..54a3a53ea22 100644 --- a/components/match2/wikis/stormgate/match_group_input_custom.lua +++ b/components/match2/wikis/stormgate/match_group_input_custom.lua @@ -332,8 +332,7 @@ function MapFunctions.getTeamParticipants(mapInput, opponent, opponentIndex) Logic.readBool(mapInput[prefix .. 'noheroescheck']) ), } - end, - OPPONENT_CONFIG + end ) Array.forEach(unattachedParticipants, function(participant)