{ogTitle} {#snippet reinviteButton()}
{ reinviting = true; return async ({ update }) => { reinviting = false; await update(); }; }}>
{/snippet}
←

{m.name || 'Match'}

Best of {config.bestOf} · First to {winsNeeded} {#if duration} · {duration} {/if} {#if m.osuLobbyId} · osu! mp/{m.osuLobbyId} {/if}

{stateLabel(m.state)} {#if !['FINISHED', 'CANCELLED'].includes(m.state) && data.isStaff}
{/if}
{#if p1 && p2}
{#if p1.team.avatarUrl} {/if}

{p1.team.name}

{#if config.teamSize === 1} {@const r1 = data.playerRatings?.[p1.players[0]?.userId]} {#if r1}

{r1.elo} ELO ยท {r1.wins}W {r1.losses}L

{/if} {/if} {#if p1.rollValue != null}

Roll: {p1.rollValue}{p1.pickOrder === 1 ? ' โ˜…' : ''}

{/if}
{p1.score}
vs
{p2.score}

{p2.team.name}

{#if config.teamSize === 1} {@const r2 = data.playerRatings?.[p2.players[0]?.userId]} {#if r2}

{r2.wins}W {r2.losses}L ยท {r2.elo} ELO

{/if} {/if} {#if p2.rollValue != null}

{p2.pickOrder === 1 ? 'โ˜… ' : ''}Roll: {p2.rollValue}

{/if}
{#if p2.team.avatarUrl} {/if}
{/if} {#if ['LOBBY', 'ROLLING', 'PICKING', 'PLAYING'].includes(m.state)}
{@render reinviteButton()} {#if m.osuLobbyId} Open lobby โ†’ {/if}
{/if} {#if m.state === 'ROLLING'}

๐ŸŽฒ Roll Phase

Highest roll picks first

{#each m.participants as p}

{p.team.name}

{#if p.rollValue != null}

{p.rollValue}

{:else}

โ€”

{/if}
{/each}
{#if canRoll}
{ rolling = true; return async ({ update }) => { rolling = false; await update(); }; }}>

or type !roll in osu! chat

{:else}

Waiting for all rolls...

{/if}
{@render reinviteButton()} {#if m.osuLobbyId} Open lobby in browser โ†’ {/if}
{/if} {#if m.state === 'PLAYING'} {@const currentGame = m.games[m.games.length - 1]} {@const bm = currentGame ? data.beatmapCache[currentGame.slot?.beatmapId] : null}
{lobbyStatus?.gameInProgress ? 'Playing' : 'Now Playing โ€” Waiting for ready'}
{#if bm}

{bm.artist} - {bm.title} [{bm.version}]

{bm.starRating.toFixed(2)}โ˜… · {bm.bpm}bpm · {formatLength(bm.totalLength)}

{/if} {#if lobbyStatus}
{#each m.participants as participant} {#each participant.players as player} {@const username = playerNames[player.userId]} {@const inLobby = username && lobbyStatus.inLobby.includes(username)} {@const isReady = username && lobbyStatus.readyPlayers.includes(username)}
{username ?? player.userId} {#if lobbyStatus.gameInProgress} Playing {:else if isReady} Ready {:else if inLobby} In Lobby {:else} Not in lobby {/if}
{/each} {/each}
{:else}

Waiting for players to ready up & play in osu!...

{/if} {#if data.isStaff}
{ forceStarting = true; return async ({ update }) => { forceStarting = false; await update(); }; }}>
{/if}
{/if} {#if showMappool && Object.keys(groupedSlots()).length > 0}

{#if m.mappool} {m.mappool.name} {:else} Mappool {/if}

{#if mappoolAvgSR()} avg {mappoolAvgSR()}โ˜… {/if} {#if pickingPhase} {isMyTurnToPick ? 'Your pick!' : `${expectedPicker?.team.name}'s pick`} {:else if m.state === 'PLAYING'} In progress {:else if m.state === 'ROLLING'} Rolling... {/if}
{#if pickingPhase && !isMyTurnToPick}

Waiting for {expectedPicker?.team.name} to pick... You can also type !pick NM1 in osu! chat when it's your turn.

{:else if pickingPhase && isMyTurnToPick}

Pick a map below, or type !pick NM1 in osu! chat.

{/if} {#if pickError}

{pickError}

{/if} {#if !mappoolCollapsed}
{#each Object.entries(groupedSlots()) as [category, slots] (category)}
{category}
{#each slots as slot (slot.id)} {@const bm = data.beatmapCache[slot.beatmapId]} {@const isPlayed = playedSlotIds.has(slot.id)} {@const isTB = slot.category === 'TB'} {@const tbLocked = isTB && !isTiebreakerAllowed} {@const cantPick = !isMyTurnToPick || !pickingPhase}
{#if bm?.listCoverUrl} {:else}
?
{/if}
{#if bm}

{bm.artist} - {bm.title}

[{bm.version}] · {bm.starRating.toFixed(2)}โ˜… · {bm.bpm}bpm

{:else}

#{slot.beatmapId}

{/if}
{category}{slot.orderInCategory} {#if isPlayed} โœ“ {:else if tbLocked} ๐Ÿ”’ {:else if pickingPhase}
{ picking = true; pickError = ''; return async ({ result, update }) => { picking = false; if (result.type === 'failure' || (result.type === 'success' && (result.data as Record)?.error)) { pickError = (result.data as Record)?.error as string ?? 'Pick failed'; } else { await update(); } }; }}> {/if}
{/each}
{/each}
{/if}
{/if} {#if m.state === 'FINISHED'} {@const winner = m.participants.find((p: any) => p.teamId === m.winnerId)}
๐Ÿ†

{winner?.team.name} wins!

{p1?.score} โ€“ {p2?.score}

{/if} {#if m.games.length > 0}

Games Played

{#each m.games as game} {@const bm = data.beatmapCache[game.slot?.beatmapId]} {@const pickerTeam = m.participants.find((p: any) => p.id === game.pickedByParticipantId)?.team} {@const winnerTeam = m.participants.find((p: any) => p.id === game.winnerParticipantId)?.team}
{game.gameNumber} {#if bm?.listCoverUrl} {:else}
{/if}
{#if bm}

{bm.artist} - {bm.title} [{bm.version}]

{/if}
{game.slot?.category}{game.slot?.orderInCategory} {#if pickerTeam}Picked by {pickerTeam.name}{/if}
{#if game.state === 'PLAYING'} Live {:else if winnerTeam} โœ“ {winnerTeam.name} {/if}
{#if game.state === 'FINISHED' && game.scores?.length > 0}
{#each [...game.scores].sort((a: any, b: any) => { if (config.scoringType === 'accuracy') return b.accuracy - a.accuracy; if (config.scoringType === 'combo') return b.maxCombo - a.maxCombo; if (config.scoringType === 'pp') return (b.pp ?? 0) - (a.pp ?? 0); return b.score - a.score; }) as s} {@const username = playerNames[s.player?.userId]}
{username ?? s.player?.userId ?? '?'} {s.score.toLocaleString()} {#if s.accuracy > 0} {(s.accuracy * 100).toFixed(2)}% {/if} {#if s.maxCombo > 0} {s.maxCombo}x {/if} {#if s.count300 > 0 || s.count100 > 0 || s.countMiss >= 0} {s.count300} /{s.count100} /{s.count50} /{s.countMiss} {/if} {#if s.pp != null} {Math.round(s.pp)}pp {/if} {#if !s.passed} FAILED {/if}
{/each}
{/if} {/each}
{/if} {#if m.state === 'CREATED'}

Creating osu! lobby...

{/if} {#if m.state === 'LOBBY'}

Lobby created. Waiting for players to join...

{#if m.osuLobbyId}

osu! mp/{m.osuLobbyId}

{/if} {#if lobbyStatus}
{#each m.participants as participant} {#each participant.players as player} {@const username = playerNames[player.userId]} {@const inLobby = username && lobbyStatus.inLobby.includes(username)}
{username ?? player.userId} {#if inLobby} In Lobby {:else} Not in lobby {/if}
{/each} {/each}
{/if}
{@render reinviteButton()}
{/if} {#if m.state === 'CANCELLED'}

This match was cancelled.

{/if}