From a86d39b9826d49549db109aef5a4379b1e1a6c9b Mon Sep 17 00:00:00 2001 From: Dorian Date: Mon, 9 Mar 2026 06:26:33 +0000 Subject: [PATCH] refactor: replace 97 any types in fight system with Fighter/GameObj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fighter: any → Fighter in entrances, morphs, finishers, rounds, types - any[] → GameObj[] for spawned game objects across all choreography files - as any cast in particles.ts replaced with closure variables - Record for morph sprite opts - this: any → this: GameObj in basic choreography callback Co-Authored-By: Claude Opus 4.6 --- frontend/src/game/fight/choreography/basic.ts | 7 +-- .../src/game/fight/choreography/bitcoin.ts | 7 +-- .../src/game/fight/choreography/creator.ts | 9 ++-- .../src/game/fight/choreography/factories.ts | 15 +++--- frontend/src/game/fight/choreography/silly.ts | 3 +- .../src/game/fight/choreography/ultimates.ts | 9 ++-- .../src/game/fight/choreography/weapons.ts | 5 +- frontend/src/game/fight/entrances.ts | 53 ++++++++++--------- frontend/src/game/fight/finishers.ts | 13 ++--- frontend/src/game/fight/morphs.ts | 17 +++--- frontend/src/game/fight/particles.ts | 12 +++-- frontend/src/game/fight/rounds.ts | 15 +++--- frontend/src/game/fight/types.ts | 2 +- 13 files changed, 90 insertions(+), 77 deletions(-) diff --git a/frontend/src/game/fight/choreography/basic.ts b/frontend/src/game/fight/choreography/basic.ts index 73a48f9..249dcef 100644 --- a/frontend/src/game/fight/choreography/basic.ts +++ b/frontend/src/game/fight/choreography/basic.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' import { createFactories } from './factories' import type { ChoreoFn } from './factories' @@ -534,7 +535,7 @@ async function bulletTimeAttack(atk: Fighter, def: Fighter, dir: number, origAX: atk.play('special') sfxGunshot() const bulletCount = isCritical ? 5 : 3 - const bullets: any[] = [] + const bullets: GameObj[] = [] for (let i = 0; i < bulletCount; i++) { const bx = atk.pos.x + dir * 25 const by = atk.pos.y - 30 + (i - 1) * 12 @@ -734,7 +735,7 @@ async function fireBreath(atk: Fighter, def: Fighter, dir: number, origAX: numbe k.color(safeColor(Math.random() > 0.5 ? '#ff6600' : '#ffcc00')), k.opacity(0.7), k.z(11), - ]).onUpdate(function(this: any) { this.pos.y -= 80 * k.dt(); this.opacity -= 2 * k.dt(); if (this.opacity <= 0) this.destroy() }) + ]).onUpdate(function(this: GameObj) { this.pos.y -= 80 * k.dt(); this.opacity -= 2 * k.dt(); if (this.opacity <= 0) this.destroy() }) }, 40) const push = dir * (isCritical ? 110 : 50) k.tween(def.pos.x, origDX + push, 0.3, (v) => { def.pos.x = v }, k.easings.easeOutQuad) @@ -794,7 +795,7 @@ async function cloneStrike(atk: Fighter, def: Fighter, dir: number, origAX: numb sfxZap() // Spawn 3-5 "ghost" clones that zip around const cloneCount = isCritical ? 5 : 3 - const clones: any[] = [] + const clones: GameObj[] = [] for (let i = 0; i < cloneCount; i++) { const clone = k.add([ k.rect(20, 35), diff --git a/frontend/src/game/fight/choreography/bitcoin.ts b/frontend/src/game/fight/choreography/bitcoin.ts index 15a10c5..4c46e5c 100644 --- a/frontend/src/game/fight/choreography/bitcoin.ts +++ b/frontend/src/game/fight/choreography/bitcoin.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' import { createFactories } from './factories' import type { ChoreoFn } from './factories' @@ -24,7 +25,7 @@ async function lightningBolt(atk: Fighter, def: Fighter, dir: number, origAX: nu atk.play('special'); sfxSpecial(); await k.wait(0.12) const segments = isCritical ? 8 : 5 let px = atk.pos.x + dir * 30, py = atk.pos.y - 40 - const bolts: any[] = [] + const bolts: GameObj[] = [] for (let i = 0; i < segments; i++) { const nx = px + dir * 30 + (Math.random() - 0.5) * 20 const ny = py + (Math.random() - 0.5) * 30 @@ -262,7 +263,7 @@ const proofOfWork = makeSwing(12, 30, '#888888', '#f7931a', 14, 14) async function merkleRoot(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) { atk.play('special'); sfxSpecial(); await k.wait(0.1) // Roots grow up from ground under defender - const roots: any[] = [] + const roots: GameObj[] = [] for (let i = 0; i < (isCritical ? 6 : 3); i++) { const rx = def.pos.x + (i - (isCritical ? 2.5 : 1)) * 12 const root = k.add([k.rect(3, 0), k.pos(rx, GROUND_Y), k.color(safeColor('#44aa22')), k.opacity(0.8), k.z(15)]) @@ -285,7 +286,7 @@ async function merkleRoot(atk: Fighter, def: Fighter, dir: number, origAX: numbe async function blockReward(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) { atk.play('special'); sfxBoing() // Gold coins gather around attacker then blast at defender - const coins: any[] = [] + const coins: GameObj[] = [] for (let i = 0; i < (isCritical ? 10 : 5); i++) { const c = k.add([k.circle(4), k.pos(atk.pos.x + (Math.random() - 0.5) * 40, atk.pos.y - 50 + (Math.random() - 0.5) * 30), k.color(safeColor('#ffd700')), k.opacity(1), k.z(17)]) coins.push(c) diff --git a/frontend/src/game/fight/choreography/creator.ts b/frontend/src/game/fight/choreography/creator.ts index bbe6c60..1107fe8 100644 --- a/frontend/src/game/fight/choreography/creator.ts +++ b/frontend/src/game/fight/choreography/creator.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' import { createFactories } from './factories' import type { ChoreoFn } from './factories' @@ -186,7 +187,7 @@ async function blockchainSlam(atk: Fighter, def: Fighter, dir: number, origAX: n await k.tween(atk.pos.x, contactX, 0.12, (v: number) => { atk.pos.x = v }, k.easings.easeOutQuad) atk.play('attack') // Chain of blocks swinging as weapon - const blocks: any[] = [] + const blocks: GameObj[] = [] const blockCount = isCritical ? 6 : 4 for (let i = 0; i < blockCount; i++) { const bk = k.add([ @@ -329,7 +330,7 @@ async function bitcoinSwarm(atk: Fighter, def: Fighter, dir: number, origAX: num sfxSpecial() await k.wait(0.15) const total = isCritical ? 30 : 18 - const swarmThings: any[] = [] + const swarmThings: GameObj[] = [] for (let i = 0; i < total; i++) { const sx = atk.pos.x + dir * 10 + (Math.random() - 0.5) * 30 const sy = atk.pos.y - 50 + (Math.random() - 0.5) * 60 @@ -379,7 +380,7 @@ async function satoshiTornado(atk: Fighter, def: Fighter, dir: number, origAX: n sfxSpecial() sfxZoomWhoosh() const total = isCritical ? 24 : 14 - const vortex: any[] = [] + const vortex: GameObj[] = [] for (let i = 0; i < total; i++) { const sz = 7 + Math.random() * 7 const colors = ['#ffd700', '#ffee88', '#ff8c00', '#ffffff'] @@ -440,7 +441,7 @@ async function hodlWave(atk: Fighter, def: Fighter, dir: number, origAX: number, await k.wait(0.1) const rows = isCritical ? 6 : 4 const cols = isCritical ? 10 : 7 - const wave: any[] = [] + const wave: GameObj[] = [] const startX = atk.pos.x + dir * 25 const screenTop = -20 const screenBot = GROUND_Y + 10 diff --git a/frontend/src/game/fight/choreography/factories.ts b/frontend/src/game/fight/choreography/factories.ts index 600226b..8a24f3c 100644 --- a/frontend/src/game/fight/choreography/factories.ts +++ b/frontend/src/game/fight/choreography/factories.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' export type PropDef = { parts: { shape: 'rect' | 'circle'; x: number; y: number; w: number; h: number; color: string }[] } @@ -297,13 +298,13 @@ async function spawnBarrage( } } -function spawnWeaponProp(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): GameObj[] { const def = PROPS[propName] if (!def) { // Fallback: single colored circle return [k.add([k.circle(8), k.pos(x, y), k.color(k.Color.fromHex('#ff8800')), k.opacity(1), k.z(zIndex), k.anchor('center')])] } - const objs: any[] = [] + const objs: GameObj[] = [] for (const p of def.parts) { const px = flipX ? x - p.x : x + p.x const obj = k.add([ @@ -319,7 +320,7 @@ function spawnWeaponProp(propName: string, x: number, y: number, flipX: boolean return objs } -function moveProp(objs: any[], def: PropDef, x: number, y: number, flipX: boolean = false) { +function moveProp(objs: GameObj[], def: PropDef, x: number, y: number, flipX: boolean = false) { for (let i = 0; i < objs.length && i < def.parts.length; i++) { const p = def.parts[i] objs[i].pos.x = flipX ? x - p.x : x + p.x @@ -327,7 +328,7 @@ function moveProp(objs: any[], def: PropDef, x: number, y: number, flipX: boolea } } -function destroyProp(objs: any[]) { +function destroyProp(objs: GameObj[]) { objs.forEach(o => { if (o.exists()) o.destroy() }) } @@ -431,7 +432,7 @@ function makeSwing(weaponW: number, weaponH: number, weaponColor: string, headCo await k.tween(atk.pos.x, cx, 0.14, (v) => { atk.pos.x = v }, k.easings.easeOutQuad) const wx = atk.pos.x + dir * 18, wy = atk.pos.y - 55 const shaft = k.add([k.rect(weaponW, weaponH), k.pos(wx, wy), k.color(safeColor(weaponColor)), k.anchor('bot'), k.z(18), k.rotate(dir === 1 ? -40 : 40)]) - let head: any = null + let head: GameObj | null = null if (headColor) { head = k.add([k.rect(headW || 18, headH || 14), k.pos(wx + dir * 10, wy - weaponH + 5), k.color(safeColor(headColor)), k.z(18)]) } atk.play('attack'); sfxSpecial() await k.tween(dir === 1 ? -40 : 40, dir === 1 ? 85 : -85, 0.13, (v) => { shaft.angle = v }, k.easings.easeInQuad) @@ -481,7 +482,7 @@ function makeVehicle(bodyW: number, bodyH: number, bodyColor: string, wheelR: nu const body = k.add([k.rect(bodyW, bodyH), k.pos(startX, vY), k.color(safeColor(isCritical ? '#ff2d2d' : bodyColor)), k.z(16)]) const w1 = k.add([k.circle(wheelR), k.pos(startX + dir * (bodyW / 3), vY + bodyH / 2 + wheelR - 2), k.color(safeColor('#222222')), k.z(16)]) const w2 = k.add([k.circle(wheelR), k.pos(startX - dir * (bodyW / 3), vY + bodyH / 2 + wheelR - 2), k.color(safeColor('#222222')), k.z(16)]) - const extras: any[] = [] + const extras: GameObj[] = [] if (parts) for (const p of parts) { extras.push(k.add([k.rect(p.w, p.h), k.pos(startX + p.ox * dir, vY + p.oy), k.color(safeColor(p.color)), k.z(17)])) } @@ -598,7 +599,7 @@ function makeSwarm(color: string, count: number, size: number, speed: number = 4 return async (atk, def, dir, origAX, origDX, isCritical) => { atk.play('special'); sfxSpecial(); await k.wait(0.15) const total = count + (isCritical ? 3 : 0) - const swarmThings: any[] = [] + const swarmThings: GameObj[] = [] for (let i = 0; i < total; i++) { const sx = atk.pos.x - dir * 20 + (Math.random() - 0.5) * 30 const sy = atk.pos.y - 40 + (Math.random() - 0.5) * 50 diff --git a/frontend/src/game/fight/choreography/silly.ts b/frontend/src/game/fight/choreography/silly.ts index e38f8f2..fdbdd8b 100644 --- a/frontend/src/game/fight/choreography/silly.ts +++ b/frontend/src/game/fight/choreography/silly.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' import { createFactories } from './factories' import type { ChoreoFn } from './factories' @@ -266,7 +267,7 @@ const airHornStun = makeBeam('#ff4444', 8, 1) async function touchGrassSmack(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) { atk.play('special'); sfxSpecial(); await k.wait(0.1) // Grass shoots up from ground - const blades: any[] = [] + const blades: GameObj[] = [] for (let i = 0; i < (isCritical ? 8 : 4); i++) { const gx = def.pos.x + (i - (isCritical ? 3.5 : 1.5)) * 10 const blade = k.add([k.rect(2, 0), k.pos(gx, GROUND_Y), k.color(safeColor('#44cc22')), k.opacity(0.8), k.z(15)]) diff --git a/frontend/src/game/fight/choreography/ultimates.ts b/frontend/src/game/fight/choreography/ultimates.ts index 22a756e..b1e2468 100644 --- a/frontend/src/game/fight/choreography/ultimates.ts +++ b/frontend/src/game/fight/choreography/ultimates.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' import { createFactories } from './factories' import type { ChoreoFn } from './factories' @@ -67,7 +68,7 @@ async function ultimateOrbitalStrike(atk: Fighter, def: Fighter, dir: number, or async function ultimateCloneSurround(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) { // Spawn 4 clones around the defender, all attack simultaneously - const clones: any[] = [] + const clones: GameObj[] = [] const positions = [ { x: def.pos.x - 60, y: GROUND_Y }, { x: def.pos.x + 60, y: GROUND_Y }, { x: def.pos.x - 30, y: GROUND_Y - 50 }, { x: def.pos.x + 30, y: GROUND_Y - 50 }, @@ -266,7 +267,7 @@ async function ultimateTimeSlow(atk: Fighter, def: Fighter, dir: number, origAX: async function ultimateSummonArmy(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) { // Summon an army of 8 mini clones that charge across the screen atk.play('special'); sfxSpecial() - const army: any[] = [] + const army: GameObj[] = [] for (let i = 0; i < 8; i++) { const ax = atk.pos.x + (Math.random() - 0.5) * 30 const ay = GROUND_Y - Math.random() * 10 @@ -443,7 +444,7 @@ async function ultimateMeteorCrash(atk: Fighter, def: Fighter, dir: number, orig // Impact atk.pos.x = def.pos.x; atk.pos.y = -80; atk.opacity = 1 // Fire trail - const fireTrail: any[] = [] + const fireTrail: GameObj[] = [] await k.tween(-80, GROUND_Y, 0.15, (v) => { atk.pos.y = v const f = k.add([k.circle(6), k.pos(atk.pos.x + (Math.random() - 0.5) * 15, v - 15), k.color(safeColor('#ff6600')), k.opacity(0.7), k.z(9)]) @@ -468,7 +469,7 @@ async function ultimateScreenShatter(atk: Fighter, def: Fighter, dir: number, or atk.play('special'); sfxCritical() def.play('knockback'); k.shake(25) // Create "shatter" fragments - const frags: any[] = [] + const frags: GameObj[] = [] for (let i = 0; i < 12; i++) { const fx = W * Math.random() const fy = H * Math.random() * 0.8 diff --git a/frontend/src/game/fight/choreography/weapons.ts b/frontend/src/game/fight/choreography/weapons.ts index 3829dec..c19f932 100644 --- a/frontend/src/game/fight/choreography/weapons.ts +++ b/frontend/src/game/fight/choreography/weapons.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from '../types' import { createFactories } from './factories' import type { ChoreoFn } from './factories' @@ -26,7 +27,7 @@ async function swordSlash(atk: Fighter, def: Fighter, dir: number, origAX: numbe atk.play('special') sfxSpecial() // Draw sword arc (series of rects forming a crescent) - const arcParts: any[] = [] + const arcParts: GameObj[] = [] for (let i = 0; i < 8; i++) { const angle = (i / 8) * Math.PI - Math.PI / 4 const ax = contactX + dir * 20 + Math.cos(angle) * 60 @@ -90,7 +91,7 @@ async function whipCrack(atk: Fighter, def: Fighter, dir: number, origAX: number sfxSpecial() await k.wait(0.1) // Whip extends from attacker to defender (series of small segments) - const segments: any[] = [] + const segments: GameObj[] = [] const fromX = atk.pos.x + dir * 20 const fromY = atk.pos.y - 40 const toX = def.pos.x diff --git a/frontend/src/game/fight/entrances.ts b/frontend/src/game/fight/entrances.ts index 0737b48..cf916e2 100644 --- a/frontend/src/game/fight/entrances.ts +++ b/frontend/src/game/fight/entrances.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from './types' import { sfxRandomComedy, sfxBoneCrack, sfxVineBoom, sfxSlideWhistleDown, sfxPowerUp, sfxDrumRoll, announceDramatic, announceDeepIntro, announceSilly, announceRandom, announceCool, announceCrowdReaction, announceCreatorEntrance } from '../audio' @@ -29,7 +30,7 @@ async function playEntrance() { const entrances = [ // 0: Drive in with a car - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const dir = fromLeft ? 1 : -1 const startX = fromLeft ? -120 : W + 120 const carY = GROUND_Y - 20 @@ -55,13 +56,13 @@ async function playEntrance() { }, k.easings.easeInQuad).then(() => { carBody.destroy(); wheel1.destroy(); wheel2.destroy() }) }, // 1: Fall from space - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.pos.x = homeX fighter.pos.y = -200 fighter.opacity = 1 sfxZoomWhoosh() // Trail of fire - const trail: any[] = [] + const trail: GameObj[] = [] for (let i = 0; i < 6; i++) { trackedTimeout(() => { const t = k.add([k.circle(4 + Math.random() * 6), k.pos(homeX + (Math.random() - 0.5) * 20, fighter.pos.y + 20), k.color(safeColor(i < 3 ? '#ff6600' : '#ffcc00')), k.opacity(0.7), k.z(9)]) @@ -78,7 +79,7 @@ async function playEntrance() { trail.forEach(t => { if (t.exists()) t.destroy() }) }, // 2: Robe entrance (boxing style) - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -60 : W + 60 fighter.pos.x = startX fighter.pos.y = GROUND_Y - 6 @@ -98,7 +99,7 @@ async function playEntrance() { spawnSparks(homeX, GROUND_Y - 40, 8, robeColor) }, // 3: Girlfriend argument - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const dir = fromLeft ? 1 : -1 const startX = fromLeft ? -60 : W + 60 fighter.pos.x = startX @@ -126,7 +127,7 @@ async function playEntrance() { sfxBoing() }, // 4: Helicopter drop - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.pos.x = homeX fighter.pos.y = -100 fighter.opacity = 1 @@ -148,7 +149,7 @@ async function playEntrance() { .then(() => { heli.destroy(); blade.destroy() }) }, // 5: Teleport glitch - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.opacity = 0 sfxZap() // Glitch flickers at random positions @@ -170,7 +171,7 @@ async function playEntrance() { k.shake(5) }, // 6: Skateboard ride in - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -80 : W + 80 const board = k.add([k.rect(40, 6), k.pos(startX, GROUND_Y - 3), k.anchor('center'), k.color(safeColor('#884422')), k.z(9)]) const wheelL = k.add([k.circle(4), k.pos(startX - 14, GROUND_Y + 1), k.anchor('center'), k.color(safeColor('#333')), k.z(9)]) @@ -189,12 +190,12 @@ async function playEntrance() { sfxBonk(); k.shake(3) }, // 7: Rocket jetpack - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -80 : W + 80 fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 80; fighter.opacity = 1 sfxJetpack() // Fly in with flame trail - const flames: any[] = [] + const flames: GameObj[] = [] const interval = trackedInterval(() => { const f = k.add([k.circle(5 + Math.random() * 5), k.pos(fighter.pos.x, fighter.pos.y + 25), k.color(safeColor(Math.random() > 0.5 ? '#ff6600' : '#ffcc00')), k.opacity(0.8), k.z(9)]) flames.push(f) @@ -210,7 +211,7 @@ async function playEntrance() { flames.forEach(f => { if (f.exists()) f.destroy() }) }, // 8: Emerge from portal - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { const portalColor = ['#b83dff', '#00f0ff', '#39ff14', '#ff2d7b'][Math.floor(Math.random() * 4)] // Draw portal const portal = k.add([k.circle(35), k.pos(homeX, GROUND_Y - 30), k.anchor('center'), k.color(safeColor(portalColor)), k.opacity(0), k.z(9)]) @@ -228,7 +229,7 @@ async function playEntrance() { portal.destroy(); portalRing.destroy() }, // 9: Backflip entrance - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -40 : W + 40 fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1 sfxZoomWhoosh() @@ -250,7 +251,7 @@ async function playEntrance() { spawnSparks(homeX, GROUND_Y - 10, 6, '#ffe14d') }, // 10: Rise from underground - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.pos.x = homeX; fighter.pos.y = GROUND_Y + 60; fighter.opacity = 0.5 // Crack the ground const crack1 = k.add([k.rect(3, 15), k.pos(homeX - 10, GROUND_Y - 5), k.color(safeColor('#ffcc00')), k.opacity(0.7), k.z(9), k.rotate(15)]) @@ -264,12 +265,12 @@ async function playEntrance() { crack1.destroy(); crack2.destroy() }, // 11: Slide in on ice - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -80 : W + 80 fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1 sfxSlideDown() // Ice trail - const iceTrail: any[] = [] + const iceTrail: GameObj[] = [] await k.tween(startX, homeX + (fromLeft ? 40 : -40), 0.4, (v) => { fighter.pos.x = v if (Math.random() < 0.3) { @@ -283,7 +284,7 @@ async function playEntrance() { trackedTimeout(() => { iceTrail.forEach(i => { if (i.exists()) i.destroy() }) }, 800) }, // 12: Parachute drop - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.pos.x = homeX + (Math.random() - 0.5) * 60; fighter.pos.y = -120; fighter.opacity = 1 const chute = k.add([k.circle(30), k.pos(fighter.pos.x, fighter.pos.y - 35), k.anchor('center'), k.color(safeColor(['#ff2d2d', '#2d7bff', '#39ff14', '#ffcc00'][Math.floor(Math.random() * 4)])), k.opacity(0.8), k.z(12)]) const line1 = k.add([k.rect(1, 30), k.pos(fighter.pos.x - 10, fighter.pos.y - 20), k.color(safeColor('#888')), k.z(11)]) @@ -302,7 +303,7 @@ async function playEntrance() { sfxBonk(); k.shake(4) }, // 13: Moonwalk in - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -60 : W + 60 fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1 announceCool('Smoother than a politician changing positions!') @@ -316,7 +317,7 @@ async function playEntrance() { sfxBoing(); k.shake(2) }, // 14: Thrown in by bouncer - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const doorX = fromLeft ? -30 : W + 30 fighter.pos.x = doorX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1 // Bouncer arm @@ -336,7 +337,7 @@ async function playEntrance() { spawnSparks(homeX, GROUND_Y - 10, 6, '#ff6600') }, // 15: Lightning strike entrance - async (fighter: any, homeX: number, _fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.pos.x = homeX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 0 // Lightning bolt from sky const bolt = k.add([k.rect(4, H), k.pos(homeX, 0), k.color(safeColor('#ffff44')), k.opacity(0.9), k.z(20)]) @@ -354,11 +355,11 @@ async function playEntrance() { spawnSparks(homeX, GROUND_Y - 20, 10, '#ffff44') }, // 16: Crowd surf in - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -60 : W + 60 fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 50; fighter.opacity = 1 // Crowd hands - const hands: any[] = [] + const hands: GameObj[] = [] for (let i = 0; i < 8; i++) { const hx = startX + (fromLeft ? 1 : -1) * (Math.abs(homeX - startX) / 8) * i const h = k.add([k.rect(6, 20), k.pos(hx, GROUND_Y - 10), k.anchor('bot'), k.color(safeColor('#cc9966')), k.z(8)]) @@ -375,7 +376,7 @@ async function playEntrance() { hands.forEach(h => { if (h.exists()) h.destroy() }) }, // 17: Riding a shopping cart - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -100 : W + 100 const cartBody = k.add([k.rect(45, 30), k.pos(startX, GROUND_Y - 18), k.anchor('center'), k.color(safeColor('#888888')), k.opacity(0.9), k.z(9)]) const cartWheel = k.add([k.circle(5), k.pos(startX + (fromLeft ? 15 : -15), GROUND_Y - 3), k.anchor('center'), k.color(safeColor('#444')), k.z(9)]) @@ -394,7 +395,7 @@ async function playEntrance() { spawnSparks(homeX, GROUND_Y - 10, 5, '#888888') }, // 18: Dramatic slow walk with spotlight - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const startX = fromLeft ? -60 : W + 60 fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1 // Spotlight cone @@ -409,7 +410,7 @@ async function playEntrance() { spawnSparks(homeX, GROUND_Y - 30, 8, '#ffe14d') }, // 19: Cannon launch - async (fighter: any, homeX: number, fromLeft: boolean) => { + async (fighter: Fighter, homeX: number, fromLeft: boolean) => { const cannonX = fromLeft ? -40 : W + 40 // Draw cannon const cannon = k.add([k.rect(50, 25), k.pos(cannonX, GROUND_Y - 20), k.anchor('center'), k.color(safeColor('#333333')), k.z(9), k.rotate(fromLeft ? -30 : 210)]) @@ -436,13 +437,13 @@ async function playEntrance() { ] // THE CREATOR: Golden code rain portal entrance - const creatorEntrance = async (fighter: any, homeX: number, _fromLeft: boolean) => { + const creatorEntrance = async (fighter: Fighter, homeX: number, _fromLeft: boolean) => { fighter.pos.x = homeX fighter.pos.y = -100 fighter.opacity = 0 // Golden code rain columns const codeChars = '₿01∞⚡⛏§∂∆≈≠'.split('') - const rainDrops: any[] = [] + const rainDrops: GameObj[] = [] for (let col = 0; col < 12; col++) { const cx = homeX - 60 + col * 10 for (let row = 0; row < 4; row++) { diff --git a/frontend/src/game/fight/finishers.ts b/frontend/src/game/fight/finishers.ts index 247c95d..3a74c64 100644 --- a/frontend/src/game/fight/finishers.ts +++ b/frontend/src/game/fight/finishers.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext } from './types' import { announceHype, announceDramatic, announceSilly, announceFast, announceRobot, @@ -19,13 +20,13 @@ interface FinisherDeps { spawnEmoteText: (x: number, y: number, text: string, color: string, duration?: number) => void spawnCrowdSigns: (count: number, color: string, text?: string) => void spawnHeart: (x: number, y: number) => void - spawnWeaponProp: (propName: string, x: number, y: number, flipX?: boolean, zIndex?: number) => any[] - destroyProp: (objs: any[]) => void + spawnWeaponProp: (propName: string, x: number, y: number, flipX?: boolean, zIndex?: number) => GameObj[] + destroyProp: (objs: GameObj[]) => void spawnProp: (fromX: number, fromY: number, toX: number, toY: number, w: number, h: number, color: string, speed?: number, isCircle?: boolean) => Promise - playBow: (fighter: any) => Promise - playFistBump: (fighterA: any, fighterB: any) => Promise - playHelpUp: (winner: any, loser: any, winningSide: 'a' | 'b') => Promise - cameraZoom: (fighters: any[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise + playBow: (fighter: Fighter) => Promise + playFistBump: (fighterA: Fighter, fighterB: Fighter) => Promise + playHelpUp: (winner: Fighter, loser: Fighter, winningSide: 'a' | 'b') => Promise + cameraZoom: (fighters: Fighter[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise hyperSpeedLines: (targetX: number, targetY: number, duration?: number) => void showSpeechBubble: (side: 'a' | 'b', text: string, duration?: number) => void spawnHumanCoach: (side: 'a' | 'b', mood: 'cheer' | 'panic' | 'coach') => Promise diff --git a/frontend/src/game/fight/morphs.ts b/frontend/src/game/fight/morphs.ts index 9ce2e4e..9ce67bd 100644 --- a/frontend/src/game/fight/morphs.ts +++ b/frontend/src/game/fight/morphs.ts @@ -1,4 +1,5 @@ -import type { ChoreoContext } from './types' +import type { GameObj } from 'kaplay' +import type { Fighter, ChoreoContext } from './types' import { generateSpriteSheet, MAX_FRAMES, TOTAL_ROWS, type SpriteCustomization } from '../sprites' import { spriteAnims } from './constants' import { announceHype } from '../audio' @@ -14,14 +15,14 @@ interface MorphDeps { colorsB: { primary: string; secondary: string } isHumanA: boolean isHumanB: boolean - loadSpriteWithTimeout: (name: string, src: string, opts: any) => Promise + loadSpriteWithTimeout: (name: string, src: string, opts: Record) => Promise } export function createMorphSystem(ctx: ChoreoContext, deps: MorphDeps) { const { k, safeColor, screenFlash, spawnShockwave, glitchRGB, sfxZoomWhoosh, sfxSpecial, theme } = ctx const { botA, botB, colorsA, colorsB, isHumanA, isHumanB, loadSpriteWithTimeout } = deps -interface MorphOverlay { obj: any; type: string } +interface MorphOverlay { obj: GameObj; type: string } let activeMorphs: MorphOverlay[] = [] function destroyMorphOverlays() { @@ -30,7 +31,7 @@ function destroyMorphOverlays() { } // Morph 0: ELEMENTAL — fire/ice/electric aura wraps around the fighter -function applyElementalMorph(fighter: any, side: 'a' | 'b') { +function applyElementalMorph(fighter: Fighter, side: 'a' | 'b') { const seed = side === 'a' ? botA.seed : botB.seed let h = 0 for (let i = 0; i < seed.length; i++) h = ((h << 5) - h + seed.charCodeAt(i)) | 0 @@ -88,7 +89,7 @@ function applyElementalMorph(fighter: any, side: 'a' | 'b') { } // Morph 1: MECH — armor plates, wings/jets, metallic overlay -function applyMechMorph(fighter: any, _side: 'a' | 'b') { +function applyMechMorph(fighter: Fighter, _side: 'a' | 'b') { const dir = fighter.scale.x > 0 ? 1 : -1 // Shoulder armor plates @@ -152,7 +153,7 @@ function applyMechMorph(fighter: any, _side: 'a' | 'b') { } // Morph 2: BEAST — grows horns/tail/claws, goes wild -function applyBeastMorph(fighter: any, side: 'a' | 'b') { +function applyBeastMorph(fighter: Fighter, side: 'a' | 'b') { const dir = fighter.scale.x > 0 ? 1 : -1 const seed = side === 'a' ? botA.seed : botB.seed let h = 0 @@ -255,7 +256,7 @@ const OMNI_MORPH_ARCHETYPES = [ let omniMorphCounter = 0 -async function applyCreatorOmniMorph(fighter: any, side: 'a' | 'b'): Promise { +async function applyCreatorOmniMorph(fighter: Fighter, side: 'a' | 'b'): Promise { const pick = OMNI_MORPH_ARCHETYPES[Math.floor(Math.random() * OMNI_MORPH_ARCHETYPES.length)] // Generate and load a sprite sheet for the picked archetype @@ -346,7 +347,7 @@ function getMorphOrder(seed: string): number[] { return orders[Math.abs(h) % 6] } -async function playMorph(fighter: any, side: 'a' | 'b', morphIndex: number) { +async function playMorph(fighter: Fighter, side: 'a' | 'b', morphIndex: number) { const savedScaleX = fighter.scale.x const savedScaleY = fighter.scale.y const isHumanFighter = side === 'a' ? isHumanA : isHumanB diff --git a/frontend/src/game/fight/particles.ts b/frontend/src/game/fight/particles.ts index ffb313c..a772382 100644 --- a/frontend/src/game/fight/particles.ts +++ b/frontend/src/game/fight/particles.ts @@ -10,6 +10,9 @@ export function spawnSparks(ctx: FightContext, x: number, y: number, count: numb const speed = (intense ? 150 : 100) + Math.random() * (intense ? 400 : 300) const size = (intense ? 3 : 2) + Math.random() * (intense ? 6 : 4) const rotSpeed = (Math.random() - 0.5) * 600 + const vx = Math.cos(angle) * speed + let vy = Math.sin(angle) * speed - 100 + const rotV = rotSpeed const spark = k.add([ k.rect(size, size * (0.5 + Math.random() * 0.5)), k.pos(x, y), @@ -18,13 +21,12 @@ export function spawnSparks(ctx: FightContext, x: number, y: number, count: numb k.z(20), k.rotate(Math.random() * 360), k.anchor('center'), - { vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed - 100, rotV: rotSpeed }, ]) spark.onUpdate(() => { - spark.pos.x += (spark as any).vx * k.dt() - spark.pos.y += (spark as any).vy * k.dt() - ;(spark as any).vy += 500 * k.dt() - spark.angle += (spark as any).rotV * k.dt() + spark.pos.x += vx * k.dt() + spark.pos.y += vy * k.dt() + vy += 500 * k.dt() + spark.angle += rotV * k.dt() spark.opacity -= 1.5 * k.dt() if (spark.opacity <= 0) spark.destroy() }) diff --git a/frontend/src/game/fight/rounds.ts b/frontend/src/game/fight/rounds.ts index 7673469..49ddc86 100644 --- a/frontend/src/game/fight/rounds.ts +++ b/frontend/src/game/fight/rounds.ts @@ -1,3 +1,4 @@ +import type { GameObj } from 'kaplay' import type { Fighter, ChoreoContext, RoundEvent } from './types' import type { ChoreoFn } from './choreography/factories' import { pickChoreography } from '../FightScene' @@ -28,14 +29,14 @@ interface RoundDeps { choreographyMap: Record playBlock: (side: 'a' | 'b') => Promise destroyMorphOverlays: () => void - playMorph: (fighter: any, side: 'a' | 'b', morphIndex: number) => Promise<{ revert: () => Promise }> + playMorph: (fighter: Fighter, side: 'a' | 'b', morphIndex: number) => Promise<{ revert: () => Promise }> getMorphOrder: (seed: string) => number[] - applyCreatorOmniMorph: (fighter: any, side: 'a' | 'b') => Promise + applyCreatorOmniMorph: (fighter: Fighter, side: 'a' | 'b') => Promise spawnEmoteText: (x: number, y: number, text: string, color: string, duration?: number) => void spawnCrowdSigns: (count: number, color: string, text?: string) => void judgeDoSomethingFunny: () => Promise maybeShowHuman: () => Promise - cameraZoom: (fighters: any[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise + cameraZoom: (fighters: Fighter[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise hyperSpeedLines: (targetX: number, targetY: number, duration?: number) => void schizoCut: () => Promise showSpeechBubble: (side: 'a' | 'b', text: string, duration?: number) => void @@ -112,8 +113,8 @@ export function createRoundSystem(ctx: ChoreoContext, deps: RoundDeps) { // === PHYSICAL CONTACT SYSTEMS === // === PHYSICAL CONTACT SYSTEMS === // Helper: spawn dizzy stars orbiting a fighter's head -function _spawnDizzyStars(fighter: any, duration: number) { - const stars: any[] = [] +function _spawnDizzyStars(fighter: Fighter, duration: number) { + const stars: GameObj[] = [] const starChars = ['*', '\u2605', '\u00d7', '!', '?'] const starColors = ['#ffff00', '#ffffff', '#ff4444', '#44ff44', '#ff88ff'] for (let i = 0; i < 4; i++) { @@ -767,7 +768,7 @@ async function playRound(event: RoundEvent) { } // Super-speed: persistent speed lines during round - let speedLines: any[] = [] + let speedLines: GameObj[] = [] if (superSpeed) { for (let i = 0; i < 10; i++) { const lineY = GROUND_Y - 5 - Math.random() * 130 @@ -782,7 +783,7 @@ async function playRound(event: RoundEvent) { } // === RETRO MODE GAMEPAD OVERLAY === - const retroObjs: any[] = [] + const retroObjs: GameObj[] = [] if (event.challengeType === 'retro_mode') { const padW = 90 const padH = 70 diff --git a/frontend/src/game/fight/types.ts b/frontend/src/game/fight/types.ts index b0953a8..c297936 100644 --- a/frontend/src/game/fight/types.ts +++ b/frontend/src/game/fight/types.ts @@ -62,7 +62,7 @@ export interface ChoreoContext { sfxBlock: () => void // Other effects spawnBullet: (fromX: number, fromY: number, toX: number, toY: number) => Promise - spawnGrotesqueDetails: (fighter: any, scaleFactor: number) => void + spawnGrotesqueDetails: (fighter: Fighter, scaleFactor: number) => void destroyGrotesqueDetails: () => void }