feat: v3 — fight loop, expanded challenges, FightPage ownership guard

- Add fight-loop CLI for automated mock fights with elo-based matchmaking
- Expand challenge types, scoring narrations, arenas, and mock bot pool
- FightPage "Fight again" buttons now only show for your own bot
- FightViewer async scene init, live fight polling with round counter
- Extract mock answers into separate answers module

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-06 23:44:40 +00:00
co-authored by Claude Opus 4.6
parent 47d20fbe66
commit 2c0323d5fb
14 changed files with 2669 additions and 996 deletions
+26 -1
View File
@@ -13,7 +13,9 @@ const isLoading = ref(true)
const isRequeueing = ref(false)
const isLive = ref(false)
const liveRounds = ref(0)
const fightError = ref('')
let pollHandle: ReturnType<typeof setInterval> | null = null
let pollCount = 0
async function loadFight(): Promise<string | null> {
try {
@@ -34,13 +36,25 @@ onMounted(async () => {
const status = await loadFight()
isLoading.value = false
if (status !== 'finished') {
if (status === null) {
// Fight doesn't exist yet — might still be creating. Poll briefly.
isLive.value = true
} else if (status !== 'finished') {
isLive.value = true
}
if (isLive.value) {
pollHandle = setInterval(async () => {
pollCount++
const s = await loadFight()
if (s === 'finished') {
isLive.value = false
if (pollHandle) { clearInterval(pollHandle); pollHandle = null }
} else if (pollCount > 60) {
// 90 seconds max wait
isLive.value = false
fightError.value = 'Fight took too long. It may still be running — try refreshing.'
if (pollHandle) { clearInterval(pollHandle); pollHandle = null }
}
}, 1500)
}
@@ -78,6 +92,17 @@ async function fightAgain(botId: string) {
</p>
</div>
<div v-else-if="fightError" class="flex-1 flex flex-col items-center justify-center gap-3">
<p class="font-display text-ko text-sm tracking-wider">{{ fightError }}</p>
<button
class="px-6 py-2 border border-neon-cyan/40 text-neon-cyan font-display font-bold text-xs tracking-wider
hover:bg-neon-cyan/10 transition-all"
@click="$router.push('/join')"
>
BACK TO LOBBY
</button>
</div>
<div v-else-if="!fight" class="flex-1 flex items-center justify-center">
<p class="font-display text-text-muted">Fight not found.</p>
</div>