From 5d6136a4456b93ece3036cb9dcb3217aa69a0ba7 Mon Sep 17 00:00:00 2001 From: yohlo Date: Sun, 1 Mar 2026 17:04:06 -0600 Subject: [PATCH] new badges --- src/lib/pocketbase/services/badges.ts | 216 +++++++++++++++++++++++--- 1 file changed, 196 insertions(+), 20 deletions(-) diff --git a/src/lib/pocketbase/services/badges.ts b/src/lib/pocketbase/services/badges.ts index 1a0b1a5..7f2968b 100644 --- a/src/lib/pocketbase/services/badges.ts +++ b/src/lib/pocketbase/services/badges.ts @@ -160,6 +160,97 @@ export function createBadgesService(pb: PocketBase) { return bigWins.length > 0 ? 1 : 0; } + if (criteria.unique_partners !== undefined) { + const matches = await pb.collection("matches").getFullList({ + filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended"`, + expand: 'home,away,home.players,away.players', + }); + + const partners = new Set(); + + for (const match of matches) { + const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) || + match.expand?.home?.players?.includes(playerId); + const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) || + match.expand?.away?.players?.includes(playerId); + + let teamPlayers: any[] = []; + if (isHome) { + teamPlayers = match.expand?.home?.expand?.players || match.expand?.home?.players || []; + } else if (isAway) { + teamPlayers = match.expand?.away?.expand?.players || match.expand?.away?.players || []; + } + + for (const player of teamPlayers) { + const partnerId = typeof player === 'string' ? player : player.id; + if (partnerId !== playerId) { + partners.add(partnerId); + } + } + } + + return partners.size; + } + + if (criteria.beat_mainline_partner !== undefined) { + const mainlineMatches = await pb.collection("matches").getFullList({ + filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && (tournament.regional = false || tournament.regional = null)`, + expand: 'home,away,home.players,away.players', + }); + + const mainlinePartners = new Set(); + for (const match of mainlineMatches) { + const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) || + match.expand?.home?.players?.includes(playerId); + const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) || + match.expand?.away?.players?.includes(playerId); + + let teamPlayers: any[] = []; + if (isHome) { + teamPlayers = match.expand?.home?.expand?.players || match.expand?.home?.players || []; + } else if (isAway) { + teamPlayers = match.expand?.away?.expand?.players || match.expand?.away?.players || []; + } + + for (const player of teamPlayers) { + const partnerId = typeof player === 'string' ? player : player.id; + if (partnerId !== playerId) { + mainlinePartners.add(partnerId); + } + } + } + + const regionalMatches = await pb.collection("matches").getFullList({ + filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && tournament.regional = true`, + expand: 'home,away,home.players,away.players', + }); + + for (const match of regionalMatches) { + const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) || + match.expand?.home?.players?.includes(playerId); + const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) || + match.expand?.away?.players?.includes(playerId); + + const didWin = (isHome && match.home_cups > match.away_cups) || + (isAway && match.away_cups > match.home_cups); + + if (!didWin) continue; + + const opponentPlayers: any[] = isHome + ? (match.expand?.away?.expand?.players || match.expand?.away?.players || []) + : (match.expand?.home?.expand?.players || match.expand?.home?.players || []); + + for (const opponent of opponentPlayers) { + const opponentId = typeof opponent === 'string' ? opponent : opponent.id; + if (mainlinePartners.has(opponentId)) { + return 1; + } + } + } + + return 0; + } + return 0; }, @@ -314,18 +405,34 @@ export function createBadgesService(pb: PocketBase) { } if (criteria.tournament_record !== undefined) { + const includeRegional = criteria.tournament_record === "0-3"; + const tournamentFilter = includeRegional + ? '' + : 'regional = false || regional = null'; + const tournaments = await pb.collection("tournaments").getFullList({ - filter: 'regional = false || regional = null', + filter: tournamentFilter, sort: 'start_time', }); - let timesWent02 = 0; + const allMatches = includeRegional + ? await pb.collection("matches").getFullList({ + filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended"`, + expand: 'tournament,home,away,home.players,away.players', + }) + : matches; - for (const tournamentId of tournamentIds) { + const relevantTournamentIds = includeRegional + ? new Set(allMatches.map(m => m.tournament)) + : tournamentIds; + + let timesMetRecord = 0; + + for (const tournamentId of relevantTournamentIds) { const tournament = tournaments.find(t => t.id === tournamentId); if (!tournament) continue; - const tournamentMatches = matches.filter(m => m.tournament === tournamentId); + const tournamentMatches = allMatches.filter(m => m.tournament === tournamentId); let wins = 0; let losses = 0; @@ -353,16 +460,16 @@ export function createBadgesService(pb: PocketBase) { if (currentIndex > 0) { const previousTournament = tournaments[currentIndex - 1]; if (previousTournament.winner_id === playerId) { - timesWent02++; + timesMetRecord++; } } } else { - timesWent02++; + timesMetRecord++; } } } - return timesWent02 > 0 ? 1 : 0; + return timesMetRecord > 0 ? 1 : 0; } if (criteria.consecutive_wins !== undefined) { @@ -380,26 +487,34 @@ export function createBadgesService(pb: PocketBase) { expand: 'home,away,home.players,away.players', }); + if (tournamentMatches.length === 0) { + continue; + } + const winnersMatches = tournamentMatches.filter(m => !m.is_losers_bracket); + if (winnersMatches.length === 0) { + continue; + } + const finalsMatch = winnersMatches.reduce((highest: any, current: any) => (!highest || current.lid > highest.lid) ? current : highest, null); - if (finalsMatch && finalsMatch.status === 'ended') { - const finalsWinnerId = (finalsMatch.home_cups > finalsMatch.away_cups) ? finalsMatch.home : finalsMatch.away; + if (!finalsMatch || finalsMatch.status !== 'ended') { + continue; + } - const winningTeam = finalsMatch.expand?.[finalsWinnerId === finalsMatch.home ? 'home' : 'away']; - const winningPlayers = winningTeam?.expand?.players || winningTeam?.players || []; + const finalsWinnerId = (finalsMatch.home_cups > finalsMatch.away_cups) ? finalsMatch.home : finalsMatch.away; - const playerWon = winningPlayers.some((p: any) => - (typeof p === 'string' ? p : p.id) === playerId - ); + const winningTeam = finalsMatch.expand?.[finalsWinnerId === finalsMatch.home ? 'home' : 'away']; + const winningPlayers = winningTeam?.expand?.players || winningTeam?.players || []; - if (playerWon) { - consecutiveWins++; - maxConsecutiveWins = Math.max(maxConsecutiveWins, consecutiveWins); - } else { - consecutiveWins = 0; - } + const playerWon = winningPlayers.some((p: any) => + (typeof p === 'string' ? p : p.id) === playerId + ); + + if (playerWon) { + consecutiveWins++; + maxConsecutiveWins = Math.max(maxConsecutiveWins, consecutiveWins); } else { consecutiveWins = 0; } @@ -408,6 +523,64 @@ export function createBadgesService(pb: PocketBase) { return maxConsecutiveWins >= criteria.consecutive_wins ? 1 : 0; } + if (criteria.undefeated_group_stage !== undefined) { + const regionalTournaments = await pb.collection("tournaments").getFullList({ + filter: 'regional = true', + sort: 'start_time', + }); + + for (const tournament of regionalTournaments) { + const groups = await pb.collection("groups").getFullList({ + filter: `tournament = "${tournament.id}"`, + expand: 'teams,teams.players', + }); + + for (const group of groups) { + const teams = group.expand?.teams || []; + let playerTeamId: string | null = null; + + for (const team of teams) { + const teamPlayers = team.expand?.players || team.players || []; + const isPlayerInTeam = teamPlayers.some((p: any) => + (typeof p === 'string' ? p : p.id) === playerId + ); + if (isPlayerInTeam) { + playerTeamId = team.id; + break; + } + } + + if (!playerTeamId) continue; + + const groupMatches = await pb.collection("matches").getFullList({ + filter: `tournament = "${tournament.id}" && group = "${group.id}" && status = "ended" && (home = "${playerTeamId}" || away = "${playerTeamId}")`, + expand: 'home,away', + }); + + if (groupMatches.length === 0) continue; + + let isUndefeated = true; + for (const match of groupMatches) { + const isHome = match.home === playerTeamId; + const didWin = isHome + ? match.home_cups > match.away_cups + : match.away_cups > match.home_cups; + + if (!didWin) { + isUndefeated = false; + break; + } + } + + if (isUndefeated) { + return 1; + } + } + } + + return 0; + } + return 0; }, @@ -425,10 +598,13 @@ export function createBadgesService(pb: PocketBase) { if (criteria.overtime_matches !== undefined) return criteria.overtime_matches; if (criteria.overtime_wins !== undefined) return criteria.overtime_wins; if (criteria.consecutive_wins !== undefined) return criteria.consecutive_wins; + if (criteria.unique_partners !== undefined) return criteria.unique_partners; if (criteria.won_tournament !== undefined) return 1; if (criteria.placement !== undefined) return 1; if (criteria.margin_of_victory !== undefined) return 1; if (criteria.tournament_record !== undefined) return 1; + if (criteria.beat_mainline_partner !== undefined) return 1; + if (criteria.undefeated_group_stage !== undefined) return 1; return 1; },