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

{m.name || 'Match'}

Best of {config.bestOf} · First to {winsNeeded} {#if m.mappool} · {m.mappool.name}{mappoolAvgSR() ? ` ยท avg โ˜…${mappoolAvgSR()}` : ''} {/if} {#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(1)} · {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}

Mappool

{#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}
{#each slots as slot} {@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}
{ picking = true; pickError = ''; return async ({ result, update }) => { picking = false; if (result.type === 'failure' || (result.type === 'success' && (result.data as any)?.error)) { pickError = (result.data as any)?.error ?? 'Pick failed'; } else { await update(); } }; }}>
{/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} {/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 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}