From 1ce2ea9eb769a6286789aeb03143468a40af59d9 Mon Sep 17 00:00:00 2001 From: kron Date: Mon, 18 Aug 2025 16:09:11 +0800 Subject: [PATCH] =?UTF-8?q?BUG=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis.js | 20 +++- src/components/BattleFooter.vue | 36 +++++- src/components/BattleHeader.vue | 7 +- src/components/HeaderProgress.vue | 10 +- src/components/RoundEndTip.vue | 4 +- src/constants.js | 52 ++++++--- src/pages/battle-result.vue | 20 ++-- src/pages/match-detail.vue | 178 +++++++++++++++++------------- src/pages/rank-list.vue | 41 ++++--- src/pages/team-battle.vue | 16 ++- src/pages/team-bow-data.vue | 84 +++++++++----- src/static/title-mvp.png | Bin 3916 -> 3358 bytes src/static/winner-badge.png | Bin 1220 -> 1321 bytes 13 files changed, 302 insertions(+), 166 deletions(-) diff --git a/src/apis.js b/src/apis.js index 7b3d6cd..ad213ee 100644 --- a/src/apis.js +++ b/src/apis.js @@ -268,10 +268,8 @@ export const getGameAPI = async (battleId) => { data.redPlayers = {}; data.bluePlayers = {}; data.mvps = []; - data.goldenRound = - goldenRoundRecords && goldenRoundRecords.length - ? goldenRoundRecords[0] - : null; + data.goldenRounds = + goldenRoundRecords && goldenRoundRecords.length ? goldenRoundRecords : []; playerStats.forEach((item) => { const { playerBattleStats = {}, roundRecords = [] } = item; if (playerBattleStats.team === 0) { @@ -290,6 +288,20 @@ export const getGameAPI = async (battleId) => { }; }); }); + (goldenRoundRecords || []).forEach((item, index) => { + item.arrowHistory.forEach((arrow) => { + if (!data.roundsData[playerStats.length + index + 1]) { + data.roundsData[playerStats.length + index + 1] = {}; + } + if (!data.roundsData[playerStats.length + index + 1][arrow.playerId]) { + data.roundsData[playerStats.length + index + 1][arrow.playerId] = []; + } + data.roundsData[playerStats.length + index + 1][arrow.playerId].push( + arrow + ); + }); + }); + data.mvps.sort((a, b) => b.totalRings - a.totalRings); } if (battleStats && battleStats.mode === 2) { diff --git a/src/components/BattleFooter.vue b/src/components/BattleFooter.vue index 2185c3f..2f1eaf5 100644 --- a/src/components/BattleFooter.vue +++ b/src/components/BattleFooter.vue @@ -19,6 +19,10 @@ defineProps({ type: Number, default: 0, }, + goldenRound: { + type: Number, + default: 0, + }, }); @@ -43,7 +47,21 @@ defineProps({ - + + + + + + {{ result.blueArrows.length @@ -70,7 +88,21 @@ defineProps({ - + + + + + + {{ result.redArrows.length diff --git a/src/components/BattleHeader.vue b/src/components/BattleHeader.vue index 71e17f7..fc5b2ba 100644 --- a/src/components/BattleHeader.vue +++ b/src/components/BattleHeader.vue @@ -137,9 +137,10 @@ defineProps({ } .players > view > image:last-child { position: absolute; - width: 40px; - top: 0; - left: 0; + width: 50px; + top: -10%; + left: -5%; + transform: rotate(-12deg); } .players > view > view { display: flex; diff --git a/src/components/HeaderProgress.vue b/src/components/HeaderProgress.vue index 609cae5..e4c8caa 100644 --- a/src/components/HeaderProgress.vue +++ b/src/components/HeaderProgress.vue @@ -9,7 +9,6 @@ const { user } = storeToRefs(store); const tips = ref(""); const melee = ref(false); -const battleId = ref(""); const timer = ref(null); const sound = ref(true); const currentSound = ref(""); @@ -51,16 +50,15 @@ const updateSound = () => { async function onReceiveMessage(messages = []) { if (!sound.value || ended.value) return; messages.forEach((msg) => { - if (battleId.value && msg.constructor === MESSAGETYPES.ShootResult) { + if (msg.constructor === MESSAGETYPES.ShootResult) { if (melee.value && msg.userId !== user.value.id) return; if (!halfTime.value && msg.target) { currentSound.value = msg.target.ring ? `${msg.target.ring}环` : "未上靶"; + console.log(currentSound.value); audioManager.play(currentSound.value); } - } else if (msg.constructor === MESSAGETYPES.WaitForAllReady) { - battleId.value = msg.id; } else if (msg.constructor === MESSAGETYPES.AllReady) { currentRoundEnded.value = true; audioManager.play("比赛开始"); @@ -84,6 +82,10 @@ async function onReceiveMessage(messages = []) { ended.value = true; } else if (msg.constructor === MESSAGETYPES.MatchOver) { ended.value = true; + } else if (msg.constructor === MESSAGETYPES.BackToGame) { + if (msg.battleInfo) { + melee.value = msg.battleInfo.config.battleMode === 2; + } } }); } diff --git a/src/components/RoundEndTip.vue b/src/components/RoundEndTip.vue index 3c7d91b..b4ebb57 100644 --- a/src/components/RoundEndTip.vue +++ b/src/components/RoundEndTip.vue @@ -101,9 +101,9 @@ onUnmounted(() => { 蓝队 - 5 + {{ bluePoint }} 分,红队 - 5 + {{ redPoint }} 同分僵局!最后一箭定江山 diff --git a/src/constants.js b/src/constants.js index fd10836..16fc062 100644 --- a/src/constants.js +++ b/src/constants.js @@ -177,23 +177,39 @@ export const getBattleResultTips = ( }; export const RoundImages = { - "round1":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slggifbnw9snvs.png", - "round2":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgf0swue5xzpd.png", - "round3":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slglkylhmq8beb.png", - "round4":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slggc88nasmxf5.png", - "round5":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgeloitb8mixf.png", - "round6":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgsjbyyuu1des.png", - "round7":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgdysd1wqulj5.png", - "round8":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgm82ny3qjd8m.png", -} + round1: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slggifbnw9snvs.png", + round2: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgf0swue5xzpd.png", + round3: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slglkylhmq8beb.png", + round4: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slggc88nasmxf5.png", + round5: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgeloitb8mixf.png", + gold1: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgsjbyyuu1des.png", + gold2: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgdysd1wqulj5.png", + gold3: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgm82ny3qjd8m.png", +}; export const RoundGoldImages = { - "round1":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slg7kfzzwwiwcb.png", - "round2":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgs5htghfh3a9.png", - "round3":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgc9ge3paqkba.png", - "round4":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgehduk96yurp.png", - "round5":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgefz3hdmwbnz.png", - "round6":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgeyb4cqwezgc.png", - "round7":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slggu3tlh97v5p.png", - "round8":"https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgszmdtmaotch.png", -} \ No newline at end of file + round1: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slg7kfzzwwiwcb.png", + round2: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgs5htghfh3a9.png", + round3: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgc9ge3paqkba.png", + round4: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgehduk96yurp.png", + round5: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgefz3hdmwbnz.png", + gold1: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgeyb4cqwezgc.png", + gold2: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slggu3tlh97v5p.png", + gold3: + "https://static.shelingxingqiu.com/attachment/2025-08-13/dc12slgszmdtmaotch.png", +}; diff --git a/src/pages/battle-result.vue b/src/pages/battle-result.vue index de05b16..c066fa9 100644 --- a/src/pages/battle-result.vue +++ b/src/pages/battle-result.vue @@ -120,7 +120,11 @@ const checkBowData = () => { :src="`../static/${data.winner === 1 ? 'blue' : 'red'}-team-win.png`" mode="widthFix" /> - + { - + { .header-mvp > view { display: flex; justify-content: center; - transform: translateY(55px); } .header-mvp > view > view:first-child { display: flex; flex-direction: column; align-items: center; - margin-right: 5vw; } .header-mvp > view > view:first-child > image { width: 24vw; @@ -509,24 +511,24 @@ const checkBowData = () => { .header-mvp > view > view:first-child > text { color: #fff; font-size: 14px; - transform: translateY(-4px) rotate(-5deg); + transform: skewX(-10deg); } .header-mvp > view > view:last-child { display: flex; align-items: center; color: #fff; - font-size: 8px; + font-size: 9px; text-align: center; - transform: translateY(-16px) rotate(-5deg); - min-width: 40%; + transform: translateY(-4px); } .header-mvp > view > view:last-child > view { - margin-right: 4vw; + margin-left: 4vw; display: flex; flex-direction: column; } .header-mvp > view > view:last-child > view > text { margin-top: 4px; width: 40px; + transform: skewX(-10deg) translateX(-3px); } diff --git a/src/pages/match-detail.vue b/src/pages/match-detail.vue index c39566a..436717b 100644 --- a/src/pages/match-detail.vue +++ b/src/pages/match-detail.vue @@ -12,6 +12,7 @@ import { getGameAPI } from "@/apis"; const blueTeam = ref([]); const redTeam = ref([]); const roundsData = ref([]); +const goldenRoundsData = ref([]); const battleId = ref(""); const data = ref({ players: [], @@ -20,7 +21,7 @@ const data = ref({ onLoad(async (options) => { if (options.id) { - battleId.value = options.id || "BATTLE-1755239389665389000-812"; + battleId.value = options.id || "BATTLE-1755484626207409508-955"; const result = await getGameAPI(battleId.value); data.value = result; if (result.mode === 1) { @@ -34,10 +35,12 @@ onLoad(async (options) => { let blueArrows = []; let redArrows = []; blueTeam.value.forEach((p) => { + if (!item[p.playerId]) return; blueTotalRings += item[p.playerId].reduce((a, b) => a + b.ring, 0); blueArrows = [...blueArrows, ...item[p.playerId]]; }); redTeam.value.forEach((p) => { + if (!item[p.playerId]) return; redTotalRings += item[p.playerId].reduce((a, b) => a + b.ring, 0); redArrows = [...redArrows, ...item[p.playerId]]; }); @@ -50,14 +53,12 @@ onLoad(async (options) => { } roundsData.value.push({ blue: { - names: blueTeam.value.map((p) => p.name), avatars: blueTeam.value.map((p) => p.avatar), arrows: blueArrows, totalRing: blueTotalRings, totalScore: bluePoint, }, red: { - names: redTeam.value.map((p) => p.name), avatars: redTeam.value.map((p) => p.avatar), arrows: redArrows, totalRing: redTotalRings, @@ -65,6 +66,19 @@ onLoad(async (options) => { }, }); }); + result.goldenRounds.forEach((round) => { + goldenRoundsData.value.push({ + blue: { + avatars: blueTeam.value.map((p) => p.avatar), + arrows: round.arrowHistory.filter((a) => a.team === 1), + }, + red: { + avatars: redTeam.value.map((p) => p.avatar), + arrows: round.arrowHistory.filter((a) => a.team === 0), + }, + winner: round.winner, + }); + }); } } }); @@ -113,7 +127,7 @@ const checkBowData = () => { :totalRing="player.totalRings" :rank="index + 1" /> - + 决金箭轮(环数) @@ -121,61 +135,6 @@ const checkBowData = () => { - - - - - {{ arrow.ring }}环 - - - - - - - - - {{ arrow.ring }}环 - - - - - - - - 第{{ index + 1 }}轮 - - 查看靶纸 - - - @@ -190,18 +149,17 @@ const checkBowData = () => { mode="widthFix" /> - + {{ arrow.ring }}环 - - - {{ round.blue.totalRing }}环 - - 得分 {{ round.blue.totalScore }} - + - + { mode="widthFix" /> - + {{ arrow.ring }}环 - - - {{ round.red.totalRing }}环 - - 得分 {{ round.red.totalScore }} - + + + + + + 第{{ index + 1 }}轮 + + 查看靶纸 + + + + + + + + + + {{ arrow.ring }}环 + + + + + {{ round.blue.totalRing }}环 + + 得分 {{ round.blue.totalScore }} + + + + + + + + + {{ arrow.ring }}环 + + + + + {{ round.red.totalRing }}环 + + 得分 {{ round.red.totalScore }} + + + @@ -299,10 +325,10 @@ const checkBowData = () => { .score-row > image:last-child { width: 40px; } -.score-row > view:last-child { +.score-row > view:nth-child(2) { padding-right: 5px; } -.score-row > view:last-child > text:last-child { +.score-row > view:nth-child(2) > text:last-child { margin-left: 20px; } diff --git a/src/pages/rank-list.vue b/src/pages/rank-list.vue index b3fca80..ca4c8c9 100644 --- a/src/pages/rank-list.vue +++ b/src/pages/rank-list.vue @@ -16,25 +16,28 @@ const addBg = ref(""); onMounted(async () => { const menuBtnInfo = uni.getMenuButtonBoundingClientRect(); capsuleHeight.value = menuBtnInfo.top - 9; - currentList.value = rankData.value.rank; - if (rankData.value.myRankPos) myData.value = rankData.value.myRankPos; + handleSelect(0); }); const handleSelect = (index) => { selectedIndex.value = index; + myData.value = {}; + currentList.value = []; if (index === 0) { currentList.value = rankData.value.rank; - if (rankData.value.myRankPos) myData.value = rankData.value.myRankPos; } else if (index === 1) { currentList.value = rankData.value.mvpRank; - if (rankData.value.myMvpRankPos) myData.value = rankData.value.myMvpRankPos; } else if (index === 2) { currentList.value = rankData.value.ringRank; - if (rankData.value.myRingRankPos) - myData.value = rankData.value.myRingRankPos; - } else { - myData.value = {}; - currentList.value = []; + } + if (user.value.id) { + currentList.value.some((item) => { + if (item.userId === user.value.id) { + myData.value = item; + return true; + } + return false; + }); } }; @@ -172,18 +175,24 @@ const subTitles = ["排位赛积分", "本周MVP次数", "本周十环次数"]; {{ user.nickName }} {{ user.lvlName }},{{ myData.TotalGames }}场 + + {{ myData.totalScore }} + + {{ myData.TotalGames }} {{ rankData.myRankPos.TenRings }}{{ myData.TenRings }} - - {{ rankData.myRingRankPos.totalScore }} diff --git a/src/pages/team-battle.vue b/src/pages/team-battle.vue index 75bd624..f3e298b 100644 --- a/src/pages/team-battle.vue +++ b/src/pages/team-battle.vue @@ -25,6 +25,7 @@ const start = ref(false); const tips = ref(""); const battleId = ref(""); const currentRound = ref(1); +const goldenRound = ref(0); const currentRedPoint = ref(0); const currentBluePoint = ref(0); const totalRounds = ref(0); @@ -105,6 +106,7 @@ function recoverData(battleInfo) { const { ShotCount, RedRecords, BlueRecords } = battleInfo.goldenRound; const roundCount = Math.max(RedRecords.length, BlueRecords.length); currentRound.value += roundCount; + goldenRound.value += roundCount; isFinalShoot.value = true; for (let i = 0; i < roundCount; i++) { const roundData = { @@ -112,6 +114,7 @@ function recoverData(battleInfo) { RedRecords && RedRecords[i] ? RedRecords[i].Arrows || [] : [], blueArrows: BlueRecords && BlueRecords[i] ? BlueRecords[i].Arrows || [] : [], + gold: true, }; if (roundResults.value[5 + i]) { roundResults.value[5 + i] = roundData; @@ -185,6 +188,7 @@ async function onReceiveMessage(messages = []) { roundResults.value.push({ redArrows: [], blueArrows: [], + gold: goldenRound.value > 0, }); } roundResults.value[currentRound.value - 1][ @@ -211,11 +215,13 @@ async function onReceiveMessage(messages = []) { } if (msg.constructor === MESSAGETYPES.FinalShoot) { currentShooterId.value = 0; - currentRound.value += 1; + goldenRound.value += 1; roundResults.value.push({ redArrows: [], blueArrows: [], }); + currentBluePoint.value = bluePoints.value; + currentRedPoint.value = redPoints.value; if (!isFinalShoot.value) { isFinalShoot.value = true; showRoundTip.value = true; @@ -307,7 +313,12 @@ onHide(() => { :isRed="false" :currentShooterId="currentShooterId" /> - + { :roundResults="roundResults" :redPoints="redPoints" :bluePoints="bluePoints" + :goldenRound="goldenRound" :power="power" /> diff --git a/src/pages/team-bow-data.vue b/src/pages/team-bow-data.vue index c164f5c..12ea76c 100644 --- a/src/pages/team-bow-data.vue +++ b/src/pages/team-bow-data.vue @@ -12,11 +12,13 @@ const blueScores = ref([]); const tabs = ref(["所有轮次"]); const players = ref([]); const allRoundsScore = ref({}); -const data = ref({}); +const data = ref({ + goldenRounds: [], +}); onLoad(async (options) => { if (options.battleId) { const result = await getGameAPI( - options.battleId || "BATTLE-1754988051086075885-926" + options.battleId || "BATTLE-1755484626207409508-955" ); data.value = result; Object.values(result.bluePlayers).forEach((p, index) => { @@ -28,9 +30,18 @@ onLoad(async (options) => { players.value.push(Object.values(result.redPlayers)[index]); } }); - if (result.goldenRound) tabs.value.push("决金箭"); - Object.keys(result.roundsData).forEach((key) => { - tabs.value.push(`第${roundsName[key]}轮`); + if (result.goldenRounds) { + result.goldenRounds.forEach(() => { + tabs.value.push("决金箭"); + }); + } + Object.keys(result.roundsData).forEach((key, index) => { + if ( + index < + Object.keys(result.roundsData).length - result.goldenRounds.length + ) { + tabs.value.push(`第${roundsName[key]}轮`); + } }); onClickTab(0); } @@ -39,11 +50,13 @@ const onClickTab = (index) => { selected.value = index; redScores.value = []; blueScores.value = []; - const { bluePlayers, redPlayers, roundsData, goldenRound } = data.value; + const { bluePlayers, redPlayers, roundsData, goldenRounds } = data.value; + let maxArrowLength = 0; if (index === 0) { Object.keys(bluePlayers).forEach((p) => { allRoundsScore.value[p] = []; Object.values(roundsData).forEach((round) => { + if (!round[p]) return; allRoundsScore.value[p].push( round[p].reduce((last, next) => last + next.ring, 0) ); @@ -55,6 +68,7 @@ const onClickTab = (index) => { Object.keys(redPlayers).forEach((p) => { allRoundsScore.value[p] = []; Object.values(roundsData).forEach((round) => { + if (!round[p]) return; allRoundsScore.value[p].push( round[p].reduce((last, next) => last + next.ring, 0) ); @@ -63,20 +77,29 @@ const onClickTab = (index) => { }); }); }); - } else if (index === 1 && goldenRound) { - if (goldenRound.winner === 1) { - blueScores.value = goldenRound.arrowHistory; - } else { - redScores.value = goldenRound.arrowHistory; - } - } else { + } else if (index <= goldenRounds.length) { + const dataIndex = + Object.keys(roundsData).length - goldenRounds.length + index; Object.keys(bluePlayers).forEach((p) => { - roundsData[goldenRound ? index - 1 : index][p].forEach((arrow) => { + if (!roundsData[dataIndex][p]) return; + roundsData[dataIndex][p].forEach((arrow) => { blueScores.value.push(arrow); }); }); Object.keys(redPlayers).forEach((p) => { - roundsData[goldenRound ? index - 1 : index][p].forEach((arrow) => { + if (!roundsData[dataIndex][p]) return; + roundsData[dataIndex][p].forEach((arrow) => { + redScores.value.push(arrow); + }); + }); + } else { + Object.keys(bluePlayers).forEach((p) => { + roundsData[index - goldenRounds.length][p].forEach((arrow) => { + blueScores.value.push(arrow); + }); + }); + Object.keys(redPlayers).forEach((p) => { + roundsData[index - goldenRounds.length][p].forEach((arrow) => { redScores.value.push(arrow); }); }); @@ -127,25 +150,23 @@ const onClickTab = (index) => { {{ score.ring }} { .score-row { display: flex; align-items: flex-start; - margin-left: 5px; + margin-bottom: 5px; + width: calc(50% - 5px); + padding-left: 5px; } .score-row > view:last-child { margin-left: 10px; @@ -204,6 +227,7 @@ const onClickTab = (index) => { grid-template-columns: repeat(3, auto); gap: 5px; margin-right: 5px; + min-width: 26%; } .score-item { background-image: url("../static/score-bg.png"); @@ -219,8 +243,8 @@ const onClickTab = (index) => { height: 10vw; } .score-container { - display: grid; - grid-template-columns: repeat(2, 1fr); - row-gap: 15px; + display: flex; + flex-wrap: wrap; + width: 100%; } diff --git a/src/static/title-mvp.png b/src/static/title-mvp.png index 73e9871f0a8ffcb7cbb6597f5b9b39093d92334f..fdeccce41cb9903a267d947ac7b2e79082f8cbc4 100644 GIT binary patch literal 3358 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 G$o~MfO)TC3 literal 3916 zcmYjUc{r49`$kkqA=#3UWwKSWQ%Pg1?6PmKM8=jSlBFn&wNeyPmJ->)b0N% zf%-$)e=&by&>8;?kQc%J5}@t;5ArW&ZyE;g{ec_Oh;VlIH`t&5%aaOEq!IzCAMUWz z3%2V4Z42zpgZ*Vd>;;?Ua0!hJcG|&KB_NFf(m2?g_{(kXF9&D@?QaBX^#|-U!7{gT za1a_q*?^_(O##v{*y#dWIIz_vyc^#Od1Igf5SHfDuLKS<&bb_G~~(dAb`vv z{Dk|H=V1a{Ut#S#ov??H(w#OyS%u#Pz8QWglr2~h%&ifkmw?3hKv#d*-^9R_j?73q za`+$%iNGh!du-9lZ0drnC_6XP<}=MJoV2y>!uPLEb8+r(w|%R+A|@}S{@(nXlbl`8|~%kYHO;mqotrAe@<3_m%2|T?GiQ?M|xkU#G%6-jBZ>;h#sX- zh{VmAse%69?(%X>er8CJ?;R&A<4dZPef-+m>io=~sj=bipH1IuzgFhuym|iYfuE1J zk)A4oyt}b5Kikn*UtjmNrZW9C`l+ACZ3kVXDrst@35&`5`23EOiJ=y0tEZ={4CabvFNF6X3HR=k0J8cI+mH>Om-w}N@ZqR_1r zD-BeEIZxlxBj5lQU}R1 zU0L0$CpSsyRN3XG>47@oy&RatrV(cXuG1w8rn1WriKAgpbXPp}H#ZhrE3Chljg`>rqu+Vu zGbZ*%46a~C9M9;@V7iQrbNXwh50-iS26rT{*yHpQebRTl%FE46D~HE|se*3zM8CE3 z*55DJ=tyBO7(fpOC>5{2Fu&yeRDR!}f`Rs`$WFguiroq|E*stDqJLD3frpxtvZI-AIV)AMQPH}^okTmHyN98dYs#J>PJJAVy*p*-gJGv*ZoA^c z!Ie~|NvGTx(aye3&*>O0GcC-TC)cvg??_aox%l{?P?9X43F9?t?3i%LnbGs5Ee^Uz zBQ$dg9b>k{aX%|-?KRSnZC<>*>-CJZ4|0uEE<4>uG51f~dkar8uOo-Vo$Sm?nJGN` zR&w0Io}%5kX2Gv|5W75Ona^4LTIHn<{o0W|FF^3jVMUiR#}_-_2~I3r+m95bj-1cy z(bo$ozvY^Dwtwbcf4u0B(Ni^@DK)l_WT}ynqp9xCFq?1uj2N8@n_Vvr4~wY$Gsdq7B1(4!^) zM4tkzhlK`NG{Yc&=HAJ;ZtwoaBe{rEuD4&|qLC(x?su0(OU>s}lXdXuXSaeqvy2`v z#^0)uh;f^iDRn8~>r-hLv|SVM7F_P~=VjwUPm3^XEXIZ0~Z<@G53v}C82As@x*6UMUHOrxq} zdTCQ8g3Vqv$t>z^!mo8JkLJuYc2}mia-4IJRmS>f9c=rZ-t%QuL%kRE{FYuB@#U|> z%k58MF|K{<%qJPcPpL@zd^)wpSm}LA+jXg10<&aLeGh>tW|lcGYbf%=$LBtOPK`AE z?dF(c=epOMIucx8dsRPlI5ekhGv^?3_~312R`mW4k5R@T?jc_{jUK^@0;Q!u;{f~7 z7n=X9(I*H-XIqYaI@FSN3Q^g_G!p7=|INOPsqV%EiLlh$Szu#cSZH4qZl%23zZ#EU z%b{kuq<7shM~VK-uGbEdUffxdzUcc@IkKXxcZ4D^eCvQ6)2y(*M}`$&?9wlaky=7% z9bh{|9Qn=b$MrO zr$SX_(!9mw52lK_1l#?geZ!v+g+;rM*0s`So;dOz|;pCv3osu5?|S z!-L*W&rV;le-q4|WAiNVL~$sUJl|2Ik{Rn?>)KR!)8%3Aw!cWbxJ%O_gNEhHVe5*O zzJ$Sl?p>-=l`LR=?po2m*8F)mv3%TlOD-lVqAZv}y&_}d@}r3dYrHN>rjyij&o5b% z*s8tPQfzcz`tY_)4&<6mA|&}w38OgsSeA3GjL=WxhSZ%E6pl10{Ji}%;5S!WMW3|T zG3O|^pr7YWkwpmA_l9PSnyJ!qG6h9qs^=a~7%t>Sir;zAy@BG{pZXItu@DLMr?RP4zuiDs3M9o%UvDj~!4XI5^PMw@T-N(xEIRyAg)A2K3Q0UY^-uoTf zx9)_6sHxq!$|O*D$Xk6+VRl47Gk)@{gO&H$p(@=o!xuD8W5igUbBxCO$|m#RY|VOZ`DLz zd$;tRO4T7$@LjXe*9aRpQqlL`Hjx;6#!FAj=41$~Zsz4#EOV4*L*&7K25u0&w&LCX z`5h3%u{KUj@J7ySx=Ornr&0FWRf|5k*a~S3xl9f(uxxHMMtS_{5JTOcV_x`AlwXIbi@_|v$;AbK31+9tv zDh|Fdk~-Tc^1ZHxb*%QQqwM+YH#l-WTn@39=ViQV&{%)pg15MRby^U-Ek-_!kgXsR8rF3&KnT6*JaY&ym7qpX6~VRQ8_F|BWK&RgCs zUYkE%>*r+T^O!!eQt88L%s1Vu;uj2OqO_g!zh1k>qJ9Z;;|+QoX|lF1|EL@n6GKS+ z;=&qB67i_MYiuC6@#hc=L-h>=5|>7kt84J`RvC=MoAF#La2y}}5Z_lnKVJ~`?2iDy zvF$U@w>^D+q6CZSxma5PmXg^Gp+O;w{XSdNflF@7#SVG;?28*BINsS7T&_J{xlH9e z`vjJ5fcr+r=G11_E-|lsT6@e{_JXT11v&J zj%-`VQ(7feL^gfv^zb3kPJ)le(iUgJ`MbX{Dq5TW0=M=XjS)TDVv$P~A1yiD4YIF( z;(n->@m@zG)d(Rb;dRwg<+^EI_}L(mZRU$>wv7u_*-;@S$f7UVCz^IoT8mK`>{_F) z58Se}2`(ruZqPk*y|ABuMVV5@a`NcVDSpnZ?dmidUkq8DPX&2dcKgMp~Z2$lO diff --git a/src/static/winner-badge.png b/src/static/winner-badge.png index 6af6b58af33756b05fd3f77ed34685eb08faef3d..dbbf5eec98c73ad6885565866ebdd35a965998b0 100644 GIT binary patch delta 1252 zcmV=SwrSk;^Ss$aF{FPGT^xZT=zDOi}ChEEiX_P6>+U&H{z1O;c?v-b4|FIo~XB; zpRW>(zyWUg;wtnqp#FFkcB7c8gxl36U*c*)A5Klc8AUfLCt&DzoB0++X zM_*8`CI_76EGTn^J=jFq>MKsUnIX1?7QDttB(R<8a(bUy+)_nTA2F}!XC=ybi46>5 zRuO_v*aQ{KeZCQ`vOxayQAx}M@2E+X+`0=wne0%e1`FLl zr!AGD6Xd=4ek3Wyb&=^1w(rWMjGuw)6s}SwlpmtBI+D`fH&L|Eq54p!3R6L)pFgks zBBgQUh>Tn8Ud5>WQpM_0O06B1>2ZHwQC6eolxh_!-`}mi58L&?=EEnm zv-s<{Z;;HcW78K!)$D^sGKwtscdJK-Cv#%~|7rCyGN%Q4$DVK2h5Z8v1v_i@q75kk O0000kPdyPKLM1L4G95NLHSbdi000CN zNkl#v%|37U4x{JDPSNqV_JRC(}7-o{4Q~iH|B|k^3-==4+ z8}%8fYK2Y1=G^Bn=a()&4B6kp%&xB#QTi}xbrR5j$4h>AG7p%BVMhQyO=^0f8Xg-XaY$*Y5sEVs;brR@!d!(&cn zwJghjZdmD49sXNTYJn*-*&JpgyWC_S;KVEq+VcH+G*>}Ml4{(;w9x^xmoRGc!$E0e zv#^~EYEGS-6vwX4r}-VeEf&;bk4dE6Pi2NFR!#k)C`pd>C5+k`djf%i5*;I(MJmai z0l!LNFxC^Mq2%k(vN$?A=#kU4OUf_|%l!s_BWiXx7%3s4#C|md%YH3#(^Kw`qUbKJ zc*)d{GIL4E$t}T-S*BFNh!hsEMfM9!$qsJGCFHYTsYJn)jJm)~gF?xuy!60qI^N_3 zS!2k4aYRRBd55g1xiB)tn^aVU>w#S{r78QBLS13%un2oz@(5*h6|f7XR*Iof8g=%6 zi{m1N){&EEG!fXUXAqPkGTEAq|Lha2 zQ%Ttf*EXQ%3ByC7)O5oej8w{TGXkmdUW)U#VODEM9_$G3JyHm)TV-J4T(bch@#GWt1WTP%^qAw z&zSpXe|d)KGETOh%(peKjiL05J~#`2`PVQzVf;Qbp)((Qk{0h#@8nma&3xlYO^C-b zEh+RKxz&mi@S`eGW)Ip%mTs1RBL>FN?*e^V?c6EJUDt;>#yE$G2OU?N5gv>m=@ln8 z24FpCb9L1X?@vK)YO