fix: Drizzle transaction execution, canvas re-init, crowd removal
- Add .run() to all Drizzle queries inside sqlite.transaction() in orchestrator.ts and mock.ts — queries were building but never executing, leaving fights stuck as 'live' forever - Remove spectator crowd from drawArenaDecor() - Rename duplicate spawnProp to spawnWeaponProp (was crashing module load) - Replace canvas element on re-init to avoid "KAPLAY already initialized" - Add safeText() to strip brackets from k.text() calls (Kaplay treats [ ] as style markup, crashes on unclosed tags) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
4d8b18a58a
commit
e49735002c
@@ -90,13 +90,19 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
async function initScene() {
|
async function initScene() {
|
||||||
if (!canvasRef.value || !props.fight.botA || !props.fight.botB) return
|
if (!canvasRef.value || !props.fight.botA || !props.fight.botB) return
|
||||||
// Destroy previous scene to avoid "KAPLAY already initialized" warning
|
// Destroy previous scene fully — replace canvas to avoid "KAPLAY already initialized"
|
||||||
if (scene) { scene.destroy(); scene = null }
|
if (scene) { scene.destroy(); scene = null }
|
||||||
|
|
||||||
const container = canvasRef.value.parentElement
|
const container = canvasRef.value.parentElement
|
||||||
if (container) {
|
if (container) {
|
||||||
canvasRef.value.width = container.clientWidth
|
// Replace canvas element so Kaplay gets a fresh context
|
||||||
canvasRef.value.height = container.clientHeight
|
const oldCanvas = canvasRef.value
|
||||||
|
const newCanvas = document.createElement('canvas')
|
||||||
|
newCanvas.className = oldCanvas.className
|
||||||
|
newCanvas.width = container.clientWidth
|
||||||
|
newCanvas.height = container.clientHeight
|
||||||
|
oldCanvas.replaceWith(newCanvas)
|
||||||
|
canvasRef.value = newCanvas
|
||||||
}
|
}
|
||||||
|
|
||||||
scene = await createFightScene({
|
scene = await createFightScene({
|
||||||
|
|||||||
+11
-166
@@ -1414,164 +1414,6 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === SPECTATORS — multi-row pixel-art crowd ===
|
|
||||||
const skinTones = ['#e8c49a', '#d4a574', '#c68c53', '#a0704a', '#7a5230', '#f5deb3', '#deb887', '#cc9966']
|
|
||||||
const shirtColors = ['#cc3333', '#3366cc', '#33aa33', '#cc9933', '#9933cc', '#cc3399', '#33aaaa', '#ee6622',
|
|
||||||
'#5555dd', '#dd5555', '#55bb55', '#dd8822', '#aa44aa', '#44aaaa', '#ddaa33', '#cc5588']
|
|
||||||
const hatColors = ['#222222', '#443322', '#663333', '#224466', '#336633', '#882222', '#444488', '#884400']
|
|
||||||
const hairColors = ['#1a1a1a', '#3a2210', '#6b3a14', '#8b6340', '#c4a060', '#ddb870', '#883322', '#aa4422']
|
|
||||||
|
|
||||||
// 3 rows of crowd — back row (small/faded), middle row, front row
|
|
||||||
const rows = [
|
|
||||||
{ y: GROUND_Y - 22, count: 14 + Math.floor(Math.random() * 4), scale: 0.6, opacity: 0.35, z: 4 },
|
|
||||||
{ y: GROUND_Y - 13, count: 12 + Math.floor(Math.random() * 4), scale: 0.8, opacity: 0.5, z: 5 },
|
|
||||||
{ y: GROUND_Y - 3, count: 10 + Math.floor(Math.random() * 3), scale: 1.0, opacity: 0.65, z: 6 },
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
for (let i = 0; i < row.count; i++) {
|
|
||||||
const sx = 15 + (i / (row.count - 1)) * (W - 30) + (Math.random() - 0.5) * 12
|
|
||||||
const sy = row.y + (Math.random() - 0.5) * 4
|
|
||||||
const sc = row.scale
|
|
||||||
const op = row.opacity
|
|
||||||
const skin = skinTones[Math.floor(Math.random() * skinTones.length)]
|
|
||||||
const shirt = shirtColors[Math.floor(Math.random() * shirtColors.length)]
|
|
||||||
const bobSpeed = 1.2 + Math.random() * 2.5
|
|
||||||
const bobPhase = Math.random() * Math.PI * 2
|
|
||||||
const bobAmt = (0.8 + Math.random() * 1.5) * sc
|
|
||||||
|
|
||||||
// Torso
|
|
||||||
const torsoW = Math.round(7 * sc)
|
|
||||||
const torsoH = Math.round(9 * sc)
|
|
||||||
const torso = k.add([
|
|
||||||
k.rect(torsoW, torsoH), k.pos(sx, sy - torsoH),
|
|
||||||
k.color(safeColor(k,shirt)), k.opacity(op), k.z(row.z),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Head
|
|
||||||
const headR = Math.round(4 * sc)
|
|
||||||
const head = k.add([
|
|
||||||
k.circle(headR), k.pos(sx + torsoW / 2, sy - torsoH - headR + 1),
|
|
||||||
k.anchor('center'),
|
|
||||||
k.color(safeColor(k,skin)), k.opacity(op), k.z(row.z),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Hair or hat (70% chance)
|
|
||||||
let accessory: any = null
|
|
||||||
if (Math.random() < 0.7) {
|
|
||||||
if (Math.random() < 0.4) {
|
|
||||||
// Hat
|
|
||||||
const hatC = hatColors[Math.floor(Math.random() * hatColors.length)]
|
|
||||||
accessory = k.add([
|
|
||||||
k.rect(Math.round(10 * sc), Math.round(3 * sc)),
|
|
||||||
k.pos(sx + torsoW / 2 - Math.round(5 * sc), sy - torsoH - headR * 2),
|
|
||||||
k.color(safeColor(k,hatC)), k.opacity(op), k.z(row.z + 0.1),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
// Hair tuft
|
|
||||||
const hairC = hairColors[Math.floor(Math.random() * hairColors.length)]
|
|
||||||
accessory = k.add([
|
|
||||||
k.rect(Math.round(6 * sc), Math.round(4 * sc)),
|
|
||||||
k.pos(sx + torsoW / 2 - Math.round(3 * sc), sy - torsoH - headR * 2 + Math.round(sc)),
|
|
||||||
k.color(safeColor(k,hairC)), k.opacity(op * 0.9), k.z(row.z + 0.1),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arms — little rectangles on each side
|
|
||||||
const armW = Math.round(2 * sc)
|
|
||||||
const armH = Math.round(6 * sc)
|
|
||||||
const armL = k.add([
|
|
||||||
k.rect(armW, armH), k.pos(sx - armW, sy - torsoH + Math.round(2 * sc)),
|
|
||||||
k.color(safeColor(k,shirt)), k.opacity(op * 0.9), k.z(row.z - 0.1),
|
|
||||||
])
|
|
||||||
const armR = k.add([
|
|
||||||
k.rect(armW, armH), k.pos(sx + torsoW, sy - torsoH + Math.round(2 * sc)),
|
|
||||||
k.color(safeColor(k,shirt)), k.opacity(op * 0.9), k.z(row.z - 0.1),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Hands (skin-colored dots at arm tips)
|
|
||||||
const handL = k.add([
|
|
||||||
k.circle(Math.max(1, Math.round(1.5 * sc))),
|
|
||||||
k.pos(sx - armW + Math.round(sc), sy - torsoH + Math.round(2 * sc) + armH),
|
|
||||||
k.anchor('center'),
|
|
||||||
k.color(safeColor(k,skin)), k.opacity(op * 0.8), k.z(row.z - 0.1),
|
|
||||||
])
|
|
||||||
const handR = k.add([
|
|
||||||
k.circle(Math.max(1, Math.round(1.5 * sc))),
|
|
||||||
k.pos(sx + torsoW + Math.round(sc), sy - torsoH + Math.round(2 * sc) + armH),
|
|
||||||
k.anchor('center'),
|
|
||||||
k.color(safeColor(k,skin)), k.opacity(op * 0.8), k.z(row.z - 0.1),
|
|
||||||
])
|
|
||||||
|
|
||||||
// Signs (10% of front row spectators)
|
|
||||||
let sign: any = null
|
|
||||||
let signText: any = null
|
|
||||||
if (row.z === 6 && Math.random() < 0.15) {
|
|
||||||
const signW = Math.round(18 * sc)
|
|
||||||
const signH = Math.round(12 * sc)
|
|
||||||
const signColors = ['#ffffff', '#ffee44', '#ff4444', '#44ff44', '#ff88ff']
|
|
||||||
const signC = signColors[Math.floor(Math.random() * signColors.length)]
|
|
||||||
sign = k.add([
|
|
||||||
k.rect(signW, signH), k.pos(sx + torsoW / 2 - signW / 2, sy - torsoH - headR * 2 - signH - 2),
|
|
||||||
k.color(safeColor(k,signC)), k.opacity(op * 0.8), k.z(row.z + 0.2),
|
|
||||||
])
|
|
||||||
const texts = ['GO!', 'KO!', 'WIN', '!!!', '#1', 'LOL', 'GG', 'WOW', 'BOT']
|
|
||||||
signText = k.add([
|
|
||||||
k.text(texts[Math.floor(Math.random() * texts.length)], { size: Math.round(6 * sc) }),
|
|
||||||
k.pos(sx + torsoW / 2, sy - torsoH - headR * 2 - signH / 2 - 2),
|
|
||||||
k.anchor('center'),
|
|
||||||
k.color(safeColor(k,'#111111')), k.opacity(op * 0.7), k.z(row.z + 0.3),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store base positions for animation
|
|
||||||
const tBaseY = torso.pos.y
|
|
||||||
const hBaseY = head.pos.y
|
|
||||||
const alBaseY = armL.pos.y
|
|
||||||
const arBaseY = armR.pos.y
|
|
||||||
const hlBaseY = handL.pos.y
|
|
||||||
const hrBaseY = handR.pos.y
|
|
||||||
const accBaseY = accessory?.pos.y
|
|
||||||
const signBaseY = sign?.pos.y
|
|
||||||
const stBaseY = signText?.pos.y
|
|
||||||
|
|
||||||
// Randomize behavior: some bob gently, some pump fists, some wave
|
|
||||||
const behavior = Math.random()
|
|
||||||
const isWaver = behavior < 0.25
|
|
||||||
const isPumper = behavior < 0.45 && !isWaver
|
|
||||||
|
|
||||||
torso.onUpdate(() => {
|
|
||||||
const bob = Math.sin(k.time() * bobSpeed + bobPhase) * bobAmt
|
|
||||||
const pump = isPumper ? Math.max(0, Math.sin(k.time() * 3 + bobPhase)) * 2 * sc : 0
|
|
||||||
torso.pos.y = tBaseY + bob - pump
|
|
||||||
head.pos.y = hBaseY + bob - pump
|
|
||||||
if (accessory) accessory.pos.y = accBaseY! + bob - pump
|
|
||||||
|
|
||||||
// Arms: wavers raise one arm rhythmically, pumpers raise both
|
|
||||||
if (isWaver) {
|
|
||||||
const wave = Math.sin(k.time() * 2.5 + bobPhase) * armH * 0.7
|
|
||||||
armR.pos.y = arBaseY + bob - Math.max(0, -wave)
|
|
||||||
handR.pos.y = hrBaseY + bob - Math.max(0, -wave)
|
|
||||||
armL.pos.y = alBaseY + bob
|
|
||||||
handL.pos.y = hlBaseY + bob
|
|
||||||
} else if (isPumper) {
|
|
||||||
armL.pos.y = alBaseY + bob - pump * 1.5
|
|
||||||
armR.pos.y = arBaseY + bob - pump * 1.5
|
|
||||||
handL.pos.y = hlBaseY + bob - pump * 1.5
|
|
||||||
handR.pos.y = hrBaseY + bob - pump * 1.5
|
|
||||||
} else {
|
|
||||||
armL.pos.y = alBaseY + bob
|
|
||||||
armR.pos.y = arBaseY + bob
|
|
||||||
handL.pos.y = hlBaseY + bob
|
|
||||||
handR.pos.y = hrBaseY + bob
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sign) { sign.pos.y = signBaseY! + bob - pump; signText.pos.y = stBaseY! + bob - pump }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
k.scene('fight', () => {
|
k.scene('fight', () => {
|
||||||
@@ -1912,7 +1754,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
const gunX = atk.pos.x + dir * 15
|
const gunX = atk.pos.x + dir * 15
|
||||||
const gunY = atk.pos.y - 40
|
const gunY = atk.pos.y - 40
|
||||||
const gunDef = PROPS.gun
|
const gunDef = PROPS.gun
|
||||||
const gunObjs = spawnProp('gun', gunX, gunY, dir < 0, 18)
|
const gunObjs = spawnWeaponProp('gun', gunX, gunY, dir < 0, 18)
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
const bulletCount = isCritical ? 8 : 4
|
const bulletCount = isCritical ? 8 : 4
|
||||||
const muzzleX = gunX + dir * 30
|
const muzzleX = gunX + dir * 30
|
||||||
@@ -2943,7 +2785,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
const gpx = atk.pos.x + dir * 15
|
const gpx = atk.pos.x + dir * 15
|
||||||
const gpy = atk.pos.y - 38
|
const gpy = atk.pos.y - 38
|
||||||
const mgDef = PROPS.minigun
|
const mgDef = PROPS.minigun
|
||||||
const mgObjs = spawnProp('minigun', gpx, gpy, dir < 0, 18)
|
const mgObjs = spawnWeaponProp('minigun', gpx, gpy, dir < 0, 18)
|
||||||
await k.wait(0.1)
|
await k.wait(0.1)
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
const bulletCount = isCritical ? 16 : 10
|
const bulletCount = isCritical ? 16 : 10
|
||||||
@@ -3765,7 +3607,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Spawn a multi-part pixel prop at position, returns array of game objects
|
// Spawn a multi-part pixel prop at position, returns array of game objects
|
||||||
function spawnProp(propName: string, x: number, y: number, flipX: boolean = false, zIndex: number = 16): any[] {
|
function spawnWeaponProp(propName: string, x: number, y: number, flipX: boolean = false, zIndex: number = 16): any[] {
|
||||||
const def = PROPS[propName]
|
const def = PROPS[propName]
|
||||||
if (!def) {
|
if (!def) {
|
||||||
// Fallback: single colored circle
|
// Fallback: single colored circle
|
||||||
@@ -3849,7 +3691,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
if (!defender) return
|
if (!defender) return
|
||||||
const dir = side === 'a' ? 1 : -1
|
const dir = side === 'a' ? 1 : -1
|
||||||
// Spawn shield in front
|
// Spawn shield in front
|
||||||
const shieldObjs = spawnProp('shield', defender.pos.x + dir * 20, defender.pos.y - 25, side === 'b', 19)
|
const shieldObjs = spawnWeaponProp('shield', defender.pos.x + dir * 20, defender.pos.y - 25, side === 'b', 19)
|
||||||
sfxBlock()
|
sfxBlock()
|
||||||
// Flash shield
|
// Flash shield
|
||||||
shieldObjs.forEach(o => { o.opacity = 0.9 })
|
shieldObjs.forEach(o => { o.opacity = 0.9 })
|
||||||
@@ -3910,7 +3752,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
for (let i = 0; i < total; i++) {
|
for (let i = 0; i < total; i++) {
|
||||||
const fx = atk.pos.x + dir * 25, fy = atk.pos.y - 35
|
const fx = atk.pos.x + dir * 25, fy = atk.pos.y - 35
|
||||||
const tx = def.pos.x + (Math.random() - 0.5) * 30, ty = def.pos.y - 25 + (Math.random() - 0.5) * 20
|
const tx = def.pos.x + (Math.random() - 0.5) * 30, ty = def.pos.y - 25 + (Math.random() - 0.5) * 20
|
||||||
const objs = spawnProp(propName, fx, fy, dir < 0)
|
const objs = spawnWeaponProp(propName, fx, fy, dir < 0)
|
||||||
k.tween(0, 1, 0.2, (t) => {
|
k.tween(0, 1, 0.2, (t) => {
|
||||||
const cx = fx + (tx - fx) * t
|
const cx = fx + (tx - fx) * t
|
||||||
const cy = fy + (ty - fy) * t - Math.sin(t * Math.PI) * arc
|
const cy = fy + (ty - fy) * t - Math.sin(t * Math.PI) * arc
|
||||||
@@ -3936,7 +3778,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('special'); sfxSpecial()
|
atk.play('special'); sfxSpecial()
|
||||||
// Spawn gun prop on attacker
|
// Spawn gun prop on attacker
|
||||||
const gx = atk.pos.x + dir * 15, gy = atk.pos.y - 35
|
const gx = atk.pos.x + dir * 15, gy = atk.pos.y - 35
|
||||||
const gunObjs = spawnProp(propName, gx, gy, dir < 0, 18)
|
const gunObjs = spawnWeaponProp(propName, gx, gy, dir < 0, 18)
|
||||||
const gunDef = PROPS[propName]
|
const gunDef = PROPS[propName]
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
// Fire bullets
|
// Fire bullets
|
||||||
@@ -5697,10 +5539,13 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
'No hard feelings!', 'Honor to fight you!', 'GG.',
|
'No hard feelings!', 'Honor to fight you!', 'GG.',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Sanitize text for Kaplay (treats [ ] as styled text tags)
|
||||||
|
function safeText(t: string): string { return t.replace(/[\[\]]/g, '') }
|
||||||
|
|
||||||
// Spawn floating text above a position
|
// Spawn floating text above a position
|
||||||
function spawnEmoteText(x: number, y: number, text: string, color: string, duration: number = 1.2) {
|
function spawnEmoteText(x: number, y: number, text: string, color: string, duration: number = 1.2) {
|
||||||
const label = k.add([
|
const label = k.add([
|
||||||
k.text(text, { size: 10 }),
|
k.text(safeText(text), { size: 10 }),
|
||||||
k.pos(x, y - 10),
|
k.pos(x, y - 10),
|
||||||
k.color(safeColor(k,color)),
|
k.color(safeColor(k,color)),
|
||||||
k.opacity(1),
|
k.opacity(1),
|
||||||
@@ -5753,7 +5598,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
})
|
})
|
||||||
if (text) {
|
if (text) {
|
||||||
const label = k.add([
|
const label = k.add([
|
||||||
k.text(text, { size: 6 }), k.pos(sx, sy),
|
k.text(safeText(text), { size: 6 }), k.pos(sx, sy),
|
||||||
k.color(safeColor(k,'#000000')), k.opacity(0.8), k.z(3), k.anchor('center'),
|
k.color(safeColor(k,'#000000')), k.opacity(0.8), k.z(3), k.anchor('center'),
|
||||||
])
|
])
|
||||||
label.onUpdate(() => { label.pos.x = sign.pos.x; label.pos.y = sign.pos.y })
|
label.onUpdate(() => { label.pos.x = sign.pos.x; label.pos.y = sign.pos.y })
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
|||||||
botAHp: hpA,
|
botAHp: hpA,
|
||||||
botBHp: hpB,
|
botBHp: hpB,
|
||||||
totalRounds: round,
|
totalRounds: round,
|
||||||
}).where(eq(schema.fights.id, fightId))
|
}).where(eq(schema.fights.id, fightId)).run()
|
||||||
|
|
||||||
if (hpA <= 0 || hpB <= 0) {
|
if (hpA <= 0 || hpB <= 0) {
|
||||||
winnerId = hpA <= 0 ? botB.id : botA.id
|
winnerId = hpA <= 0 ? botB.id : botA.id
|
||||||
@@ -398,7 +398,7 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
|||||||
status: 'finished',
|
status: 'finished',
|
||||||
winnerId,
|
winnerId,
|
||||||
endedAt: new Date().toISOString(),
|
endedAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.fights.id, fightId))
|
}).where(eq(schema.fights.id, fightId)).run()
|
||||||
|
|
||||||
if (winnerId) {
|
if (winnerId) {
|
||||||
const loserId = winnerId === botA.id ? botB.id : botA.id
|
const loserId = winnerId === botA.id ? botB.id : botA.id
|
||||||
@@ -415,7 +415,7 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
|||||||
bestStreak: sql`MAX(${schema.bots.bestStreak}, ${newWinStreak})`,
|
bestStreak: sql`MAX(${schema.bots.bestStreak}, ${newWinStreak})`,
|
||||||
tier: calculateTier(newWinnerElo, winner.wins + 1),
|
tier: calculateTier(newWinnerElo, winner.wins + 1),
|
||||||
lastFightAt: new Date().toISOString(),
|
lastFightAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.bots.id, winnerId))
|
}).where(eq(schema.bots.id, winnerId)).run()
|
||||||
|
|
||||||
db.update(schema.bots).set({
|
db.update(schema.bots).set({
|
||||||
losses: sql`${schema.bots.losses} + 1`,
|
losses: sql`${schema.bots.losses} + 1`,
|
||||||
@@ -423,7 +423,7 @@ export async function runMockFight(botAId: string, botBId: string): Promise<stri
|
|||||||
winStreak: 0,
|
winStreak: 0,
|
||||||
tier: calculateTier(newLoserElo, loser.wins),
|
tier: calculateTier(newLoserElo, loser.wins),
|
||||||
lastFightAt: new Date().toISOString(),
|
lastFightAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.bots.id, loserId))
|
}).where(eq(schema.bots.id, loserId)).run()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
|||||||
botAHp: hpA,
|
botAHp: hpA,
|
||||||
botBHp: hpB,
|
botBHp: hpB,
|
||||||
totalRounds: round,
|
totalRounds: round,
|
||||||
}).where(eq(schema.fights.id, fightId))
|
}).where(eq(schema.fights.id, fightId)).run()
|
||||||
|
|
||||||
// Check for KO
|
// Check for KO
|
||||||
if (hpA <= KO_THRESHOLD || hpB <= KO_THRESHOLD) {
|
if (hpA <= KO_THRESHOLD || hpB <= KO_THRESHOLD) {
|
||||||
@@ -390,7 +390,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
|||||||
status: 'finished',
|
status: 'finished',
|
||||||
winnerId,
|
winnerId,
|
||||||
endedAt: new Date().toISOString(),
|
endedAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.fights.id, fightId))
|
}).where(eq(schema.fights.id, fightId)).run()
|
||||||
|
|
||||||
// Update bot stats
|
// Update bot stats
|
||||||
if (winnerId) {
|
if (winnerId) {
|
||||||
@@ -409,7 +409,7 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
|||||||
bestStreak: newBestStreak,
|
bestStreak: newBestStreak,
|
||||||
tier: calculateTier(newWinnerElo, winner.wins + 1),
|
tier: calculateTier(newWinnerElo, winner.wins + 1),
|
||||||
lastFightAt: new Date().toISOString(),
|
lastFightAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.bots.id, winnerId))
|
}).where(eq(schema.bots.id, winnerId)).run()
|
||||||
|
|
||||||
db.update(schema.bots).set({
|
db.update(schema.bots).set({
|
||||||
losses: sql`${schema.bots.losses} + 1`,
|
losses: sql`${schema.bots.losses} + 1`,
|
||||||
@@ -417,11 +417,11 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
|||||||
winStreak: 0,
|
winStreak: 0,
|
||||||
tier: calculateTier(newLoserElo, loser.wins),
|
tier: calculateTier(newLoserElo, loser.wins),
|
||||||
lastFightAt: new Date().toISOString(),
|
lastFightAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.bots.id, loserId))
|
}).where(eq(schema.bots.id, loserId)).run()
|
||||||
} else {
|
} else {
|
||||||
// Draw — update lastFightAt for both
|
// Draw — update lastFightAt for both
|
||||||
db.update(schema.bots).set({ lastFightAt: new Date().toISOString() }).where(eq(schema.bots.id, botA.id))
|
db.update(schema.bots).set({ lastFightAt: new Date().toISOString() }).where(eq(schema.bots.id, botA.id)).run()
|
||||||
db.update(schema.bots).set({ lastFightAt: new Date().toISOString() }).where(eq(schema.bots.id, botB.id))
|
db.update(schema.bots).set({ lastFightAt: new Date().toISOString() }).where(eq(schema.bots.id, botB.id)).run()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ export async function runFightAsync(botAId: string, botBId: string): Promise<str
|
|||||||
db.update(schema.fights).set({
|
db.update(schema.fights).set({
|
||||||
status: 'cancelled',
|
status: 'cancelled',
|
||||||
endedAt: new Date().toISOString(),
|
endedAt: new Date().toISOString(),
|
||||||
}).where(eq(schema.fights.id, fightId))
|
}).where(eq(schema.fights.id, fightId)).run()
|
||||||
fightEvents.cleanup(fightId)
|
fightEvents.cleanup(fightId)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user