From 429f64c64077174e8c69c0ecf7512395491bdf72 Mon Sep 17 00:00:00 2001 From: Rikard Blixt Date: Wed, 28 Aug 2024 14:54:56 +0200 Subject: [PATCH] feat(match2): deadlock setup (#4652) * feat(match2): deadlock setup * from review * shortern down the copy paste * update m2 status * hero icon tweaks * remove side colors --- .../get_match_group_copy_paste_wiki.lua | 50 +++++ .../deadlock/match_group_input_custom.lua | 181 ++++++++++++++++++ .../match2/wikis/deadlock/match_summary.lua | 164 ++++++++++++++++ standard/info/wikis/deadlock/info.lua | 2 +- 4 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 components/match2/wikis/deadlock/get_match_group_copy_paste_wiki.lua create mode 100644 components/match2/wikis/deadlock/match_group_input_custom.lua create mode 100644 components/match2/wikis/deadlock/match_summary.lua diff --git a/components/match2/wikis/deadlock/get_match_group_copy_paste_wiki.lua b/components/match2/wikis/deadlock/get_match_group_copy_paste_wiki.lua new file mode 100644 index 00000000000..484ce35e0d7 --- /dev/null +++ b/components/match2/wikis/deadlock/get_match_group_copy_paste_wiki.lua @@ -0,0 +1,50 @@ +--- +-- @Liquipedia +-- wiki=deadlock +-- 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') + +---@class DeadlockMatch2CopyPaste: Match2CopyPasteBase +local WikiCopyPaste = Class.new(BaseCopyPaste) + +local INDENT = WikiCopyPaste.Indent + +function WikiCopyPaste.getMatchCode(bestof, mode, index, opponents, args) + local showScore = Logic.nilOr(Logic.readBoolOrNil, bestof == 0) + + local lines = Array.extend( + '{{Match|bestof=' .. (bestof ~= 0 and bestof or ''), + Array.map(Array.range(1, opponents), function(opponentIndex) + return INDENT .. '|opponent' .. opponentIndex .. '=' .. WikiCopyPaste.getOpponent(mode, showScore) + end), + INDENT .. '|date=|finished=', + INDENT .. '|twitch=|youtube=|vod=', + Array.map(Array.range(1, bestof), WikiCopyPaste._getMapCode), + '}}' + ) + + return table.concat(lines, '\n') +end + +---@param mapIndex integer +---@return string +function WikiCopyPaste._getMapCode(mapIndex) + return table.concat(Array.extend( + INDENT .. '|map' .. mapIndex .. '={{Map|length=|winner=|vod=', + INDENT .. INDENT .. '|team1side=|team2side=', + INDENT .. INDENT .. '|t1h1=|t1h2=|t1h3=|t1h4=|t1h5=|t1h6=', + INDENT .. INDENT .. '|t2h1=|t2h2=|t2h3=|t2h4=|t2h5=|t2h6=', + INDENT .. '}}' + ), '\n') +end + +return WikiCopyPaste diff --git a/components/match2/wikis/deadlock/match_group_input_custom.lua b/components/match2/wikis/deadlock/match_group_input_custom.lua new file mode 100644 index 00000000000..366e3f56845 --- /dev/null +++ b/components/match2/wikis/deadlock/match_group_input_custom.lua @@ -0,0 +1,181 @@ +--- +-- @Liquipedia +-- wiki=deadlock +-- page=Module:MatchGroup/Input/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Array = require('Module:Array') +local FnUtil = require('Module:FnUtil') +local HeroNames = mw.loadData('Module:HeroNames') +local Lua = require('Module:Lua') +local Operator = require('Module:Operator') +local Streams = require('Module:Links/Stream') +local Table = require('Module:Table') + +local MatchGroupInputUtil = Lua.import('Module:MatchGroup/Input/Util') + +local OPPONENT_CONFIG = { + resolveRedirect = true, + pagifyTeamNames = true, + pagifyPlayerNames = true, + maxNumPlayers = 10, +} + +local MatchFunctions = {} +local MapFunctions = {} + +local CustomMatchGroupInput = {} + +---@param match table +---@param options? {isMatchPage: boolean?} +---@return table +function CustomMatchGroupInput.processMatch(match, options) + local finishedInput = match.finished --[[@as string?]] + local winnerInput = match.winner --[[@as string?]] + Table.mergeInto(match, MatchGroupInputUtil.readDate(match.date)) + + local opponents = Array.mapIndexes(function(opponentIndex) + return MatchGroupInputUtil.readOpponent(match, opponentIndex, OPPONENT_CONFIG) + end) + local games = MatchFunctions.extractMaps(match, opponents) + match.bestof = MatchGroupInputUtil.getBestOf(match.bestof, games) + + 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) + + 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) + match.extradata = MatchFunctions.getExtraData(match) + + match.games = games + match.opponents = opponents + + return match +end + +---@param match table +---@param opponents table[] +---@return table[] +function MatchFunctions.extractMaps(match, opponents) + local maps = {} + for key, map in Table.iter.pairsByPrefix(match, 'map', {requireIndex = true}) do + local finishedInput = map.finished --[[@as string?]] + local winnerInput = map.winner --[[@as string?]] + + map.map = nil + map.participants = MapFunctions.getParticipants(map, opponents) + map.extradata = MapFunctions.getExtraData(map) + + map.finished = MatchGroupInputUtil.mapIsFinished(map) + local opponentInfo = Array.map(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 + + table.insert(maps, map) + match[key] = nil + end + + return maps +end + +CustomMatchGroupInput.processMap = FnUtil.identity + +---@param maps table[] +---@return fun(opponentIndex: integer): integer +function MatchFunctions.calculateMatchScore(maps) + return function(opponentIndex) + return MatchGroupInputUtil.computeMatchScoreFromMapWinners(maps, opponentIndex) + end +end + +---@param match table +---@return table +function MatchFunctions.getExtraData(match) + return { + comment = match.comment, + } +end + +---@param map table +---@return table +function MapFunctions.getExtraData(map) + local extraData = { + comment = map.comment, + team1side = map.team1side, + team2side = map.team2side, + } + + return extraData +end + +---@param map table +---@param opponents table[] +---@return table +function MapFunctions.getParticipants(map, opponents) + local participants = {} + local getCharacterName = FnUtil.curry(MatchGroupInputUtil.getCharacterName, HeroNames) + + for opponentIndex in ipairs(opponents) do + for _, hero, playerIndex in Table.iter.pairsByPrefix(map, 't' .. opponentIndex .. 'h', {requireIndex = true}) do + participants[opponentIndex .. '_' .. playerIndex] = { + character = getCharacterName(hero), + } + end + end + + return participants +end + +---@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 + +return CustomMatchGroupInput diff --git a/components/match2/wikis/deadlock/match_summary.lua b/components/match2/wikis/deadlock/match_summary.lua new file mode 100644 index 00000000000..c5b744c88dd --- /dev/null +++ b/components/match2/wikis/deadlock/match_summary.lua @@ -0,0 +1,164 @@ +--- +-- @Liquipedia +-- wiki=deadlock +-- page=Module:MatchSummary +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Abbreviation = require('Module:Abbreviation') +local Array = require('Module:Array') +local CharacterIcon = require('Module:CharacterIcon') +local FnUtil = require('Module:FnUtil') +local DateExt = require('Module:Date/Ext') +local Icon = require('Module:Icon') +local Logic = require('Module:Logic') +local Lua = require('Module:Lua') +local Table = require('Module:Table') + +local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') +local MatchSummary = Lua.import('Module:MatchSummary/Base') + +local SIZE_HERO = '48x48px' +local ICONS = { + winner = Icon.makeIcon{iconName = 'winner', color = 'forest-green-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 = '400px', teamStyle = 'bracket'}) +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 + + Array.forEach(Array.map(match.games, CustomMatchSummary._createGame), FnUtil.curry(body.addRow, body)) + + return body +end + +---@param participants table +---@param opponentIndex integer +---@return table +function CustomMatchSummary._getHeroesForOpponent(participants, opponentIndex) + local characters = {} + for _, participant in Table.iter.pairsByPrefix(participants, opponentIndex .. '_') do + table.insert(characters, participant.character) + end + return characters +end + +---@param game MatchGroupUtilGame +---@param gameIndex integer +---@return MatchSummaryRow +function CustomMatchSummary._createGame(game, gameIndex) + local row = MatchSummary.Row() + local extradata = game.extradata or {} + + row:addClass('brkts-popup-body-game') + :css('font-size', '80%') + :css('padding', '4px') + + local function makeCharacterDisplay(opponentIndex) + return CustomMatchSummary._createCharacterDisplay( + CustomMatchSummary._getHeroesForOpponent(game.participants, opponentIndex), + extradata['team' .. opponentIndex .. 'side'], + opponentIndex == 2 + ) + end + + row:addElement(makeCharacterDisplay(1)) + row:addElement(CustomMatchSummary._createCheckMark(game.winner, 1)) + row:addElement(mw.html.create('div') + :addClass('brkts-popup-body-element-vertical-centered') + :wikitext(Abbreviation.make( + Logic.isEmpty(game.length) and ('Game ' .. gameIndex) or game.length, + Logic.isEmpty(game.length) and ('Game ' .. gameIndex .. ' picks') or 'Match Length' + )) + ) + row:addElement(CustomMatchSummary._createCheckMark(game.winner, 2)) + row:addElement(makeCharacterDisplay(2)) + + if Logic.isNotEmpty(game.comment) then + row:addElement(MatchSummary.Break():create()) + row:addElement(mw.html.create('div'):css('margin', 'auto'):wikitext(game.comment)) + end + + return row +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', '1%') + :css('margin-right', '1%') + :wikitext( + winner == opponentIndex and ICONS.winner + or winner == 0 and ICONS.draw + or Logic.isNotEmpty(winner) and ICONS.loss + or ICONS.empty + ) +end + +---@param characters {name: string, active: boolean}[]? +---@param side string? +---@param reverse boolean? +---@return Html +function CustomMatchSummary._createCharacterDisplay(characters, side, reverse) + local wrapper = mw.html.create('div') + :addClass('brkts-popup-body-element-thumbs') + :addClass('brkts-popup-body-element-thumbs-' .. (reverse and 'right' or 'left')) + :addClass('brkts-champion-icon') + + local function makeCharacterIcon(character) + return CharacterIcon.Icon{ + character = character, + size = SIZE_HERO, + } + end + + local function characterDisplay(character, showName) + local display = mw.html.create('div') + if not showName then + display:node(makeCharacterIcon(character)) + return display + end + if reverse then + display:wikitext(character):wikitext(' '):wikitext(makeCharacterIcon(character)) + else + display:node(makeCharacterIcon(character)):wikitext(' '):wikitext(character) + end + return display + end + + local characterDisplays = Array.map(characters or {}, function (character) + return characterDisplay(character, #characters == 1) + end) + + if reverse then + characterDisplays = Array.reverse(characterDisplays) + end + + Array.forEach(characterDisplays, FnUtil.curry(wrapper.node, wrapper)) + + return wrapper +end + +return CustomMatchSummary diff --git a/standard/info/wikis/deadlock/info.lua b/standard/info/wikis/deadlock/info.lua index 00cda98d3ef..7b7fc8c7c0f 100644 --- a/standard/info/wikis/deadlock/info.lua +++ b/standard/info/wikis/deadlock/info.lua @@ -33,7 +33,7 @@ return { allowManual = false, }, match2 = { - status = 0, + status = 2, }, }, defaultRoundPrecision = 0,