refactor: replace 97 any types in fight system with Fighter/GameObj
- 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<string, unknown> for morph sprite opts - this: any → this: GameObj in basic choreography callback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
81048ff22f
commit
a86d39b982
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
import { createFactories } from './factories'
|
import { createFactories } from './factories'
|
||||||
import type { ChoreoFn } from './factories'
|
import type { ChoreoFn } from './factories'
|
||||||
@@ -534,7 +535,7 @@ async function bulletTimeAttack(atk: Fighter, def: Fighter, dir: number, origAX:
|
|||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxGunshot()
|
sfxGunshot()
|
||||||
const bulletCount = isCritical ? 5 : 3
|
const bulletCount = isCritical ? 5 : 3
|
||||||
const bullets: any[] = []
|
const bullets: GameObj[] = []
|
||||||
for (let i = 0; i < bulletCount; i++) {
|
for (let i = 0; i < bulletCount; i++) {
|
||||||
const bx = atk.pos.x + dir * 25
|
const bx = atk.pos.x + dir * 25
|
||||||
const by = atk.pos.y - 30 + (i - 1) * 12
|
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.color(safeColor(Math.random() > 0.5 ? '#ff6600' : '#ffcc00')),
|
||||||
k.opacity(0.7),
|
k.opacity(0.7),
|
||||||
k.z(11),
|
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)
|
}, 40)
|
||||||
const push = dir * (isCritical ? 110 : 50)
|
const push = dir * (isCritical ? 110 : 50)
|
||||||
k.tween(def.pos.x, origDX + push, 0.3, (v) => { def.pos.x = v }, k.easings.easeOutQuad)
|
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()
|
sfxZap()
|
||||||
// Spawn 3-5 "ghost" clones that zip around
|
// Spawn 3-5 "ghost" clones that zip around
|
||||||
const cloneCount = isCritical ? 5 : 3
|
const cloneCount = isCritical ? 5 : 3
|
||||||
const clones: any[] = []
|
const clones: GameObj[] = []
|
||||||
for (let i = 0; i < cloneCount; i++) {
|
for (let i = 0; i < cloneCount; i++) {
|
||||||
const clone = k.add([
|
const clone = k.add([
|
||||||
k.rect(20, 35),
|
k.rect(20, 35),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
import { createFactories } from './factories'
|
import { createFactories } from './factories'
|
||||||
import type { ChoreoFn } 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)
|
atk.play('special'); sfxSpecial(); await k.wait(0.12)
|
||||||
const segments = isCritical ? 8 : 5
|
const segments = isCritical ? 8 : 5
|
||||||
let px = atk.pos.x + dir * 30, py = atk.pos.y - 40
|
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++) {
|
for (let i = 0; i < segments; i++) {
|
||||||
const nx = px + dir * 30 + (Math.random() - 0.5) * 20
|
const nx = px + dir * 30 + (Math.random() - 0.5) * 20
|
||||||
const ny = py + (Math.random() - 0.5) * 30
|
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) {
|
async function merkleRoot(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special'); sfxSpecial(); await k.wait(0.1)
|
atk.play('special'); sfxSpecial(); await k.wait(0.1)
|
||||||
// Roots grow up from ground under defender
|
// Roots grow up from ground under defender
|
||||||
const roots: any[] = []
|
const roots: GameObj[] = []
|
||||||
for (let i = 0; i < (isCritical ? 6 : 3); i++) {
|
for (let i = 0; i < (isCritical ? 6 : 3); i++) {
|
||||||
const rx = def.pos.x + (i - (isCritical ? 2.5 : 1)) * 12
|
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)])
|
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) {
|
async function blockReward(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special'); sfxBoing()
|
atk.play('special'); sfxBoing()
|
||||||
// Gold coins gather around attacker then blast at defender
|
// Gold coins gather around attacker then blast at defender
|
||||||
const coins: any[] = []
|
const coins: GameObj[] = []
|
||||||
for (let i = 0; i < (isCritical ? 10 : 5); i++) {
|
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)])
|
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)
|
coins.push(c)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
import { createFactories } from './factories'
|
import { createFactories } from './factories'
|
||||||
import type { ChoreoFn } 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)
|
await k.tween(atk.pos.x, contactX, 0.12, (v: number) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
atk.play('attack')
|
atk.play('attack')
|
||||||
// Chain of blocks swinging as weapon
|
// Chain of blocks swinging as weapon
|
||||||
const blocks: any[] = []
|
const blocks: GameObj[] = []
|
||||||
const blockCount = isCritical ? 6 : 4
|
const blockCount = isCritical ? 6 : 4
|
||||||
for (let i = 0; i < blockCount; i++) {
|
for (let i = 0; i < blockCount; i++) {
|
||||||
const bk = k.add([
|
const bk = k.add([
|
||||||
@@ -329,7 +330,7 @@ async function bitcoinSwarm(atk: Fighter, def: Fighter, dir: number, origAX: num
|
|||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
const total = isCritical ? 30 : 18
|
const total = isCritical ? 30 : 18
|
||||||
const swarmThings: any[] = []
|
const swarmThings: GameObj[] = []
|
||||||
for (let i = 0; i < total; i++) {
|
for (let i = 0; i < total; i++) {
|
||||||
const sx = atk.pos.x + dir * 10 + (Math.random() - 0.5) * 30
|
const sx = atk.pos.x + dir * 10 + (Math.random() - 0.5) * 30
|
||||||
const sy = atk.pos.y - 50 + (Math.random() - 0.5) * 60
|
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()
|
sfxSpecial()
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
const total = isCritical ? 24 : 14
|
const total = isCritical ? 24 : 14
|
||||||
const vortex: any[] = []
|
const vortex: GameObj[] = []
|
||||||
for (let i = 0; i < total; i++) {
|
for (let i = 0; i < total; i++) {
|
||||||
const sz = 7 + Math.random() * 7
|
const sz = 7 + Math.random() * 7
|
||||||
const colors = ['#ffd700', '#ffee88', '#ff8c00', '#ffffff']
|
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)
|
await k.wait(0.1)
|
||||||
const rows = isCritical ? 6 : 4
|
const rows = isCritical ? 6 : 4
|
||||||
const cols = isCritical ? 10 : 7
|
const cols = isCritical ? 10 : 7
|
||||||
const wave: any[] = []
|
const wave: GameObj[] = []
|
||||||
const startX = atk.pos.x + dir * 25
|
const startX = atk.pos.x + dir * 25
|
||||||
const screenTop = -20
|
const screenTop = -20
|
||||||
const screenBot = GROUND_Y + 10
|
const screenBot = GROUND_Y + 10
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
|
|
||||||
export type PropDef = { parts: { shape: 'rect' | 'circle'; x: number; y: number; w: number; h: number; color: string }[] }
|
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]
|
const def = PROPS[propName]
|
||||||
if (!def) {
|
if (!def) {
|
||||||
// Fallback: single colored circle
|
// 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')])]
|
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) {
|
for (const p of def.parts) {
|
||||||
const px = flipX ? x - p.x : x + p.x
|
const px = flipX ? x - p.x : x + p.x
|
||||||
const obj = k.add([
|
const obj = k.add([
|
||||||
@@ -319,7 +320,7 @@ function spawnWeaponProp(propName: string, x: number, y: number, flipX: boolean
|
|||||||
return objs
|
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++) {
|
for (let i = 0; i < objs.length && i < def.parts.length; i++) {
|
||||||
const p = def.parts[i]
|
const p = def.parts[i]
|
||||||
objs[i].pos.x = flipX ? x - p.x : x + p.x
|
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() })
|
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)
|
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 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)])
|
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)]) }
|
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()
|
atk.play('attack'); sfxSpecial()
|
||||||
await k.tween(dir === 1 ? -40 : 40, dir === 1 ? 85 : -85, 0.13, (v) => { shaft.angle = v }, k.easings.easeInQuad)
|
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 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 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 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) {
|
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)]))
|
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) => {
|
return async (atk, def, dir, origAX, origDX, isCritical) => {
|
||||||
atk.play('special'); sfxSpecial(); await k.wait(0.15)
|
atk.play('special'); sfxSpecial(); await k.wait(0.15)
|
||||||
const total = count + (isCritical ? 3 : 0)
|
const total = count + (isCritical ? 3 : 0)
|
||||||
const swarmThings: any[] = []
|
const swarmThings: GameObj[] = []
|
||||||
for (let i = 0; i < total; i++) {
|
for (let i = 0; i < total; i++) {
|
||||||
const sx = atk.pos.x - dir * 20 + (Math.random() - 0.5) * 30
|
const sx = atk.pos.x - dir * 20 + (Math.random() - 0.5) * 30
|
||||||
const sy = atk.pos.y - 40 + (Math.random() - 0.5) * 50
|
const sy = atk.pos.y - 40 + (Math.random() - 0.5) * 50
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
import { createFactories } from './factories'
|
import { createFactories } from './factories'
|
||||||
import type { ChoreoFn } 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) {
|
async function touchGrassSmack(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special'); sfxSpecial(); await k.wait(0.1)
|
atk.play('special'); sfxSpecial(); await k.wait(0.1)
|
||||||
// Grass shoots up from ground
|
// Grass shoots up from ground
|
||||||
const blades: any[] = []
|
const blades: GameObj[] = []
|
||||||
for (let i = 0; i < (isCritical ? 8 : 4); i++) {
|
for (let i = 0; i < (isCritical ? 8 : 4); i++) {
|
||||||
const gx = def.pos.x + (i - (isCritical ? 3.5 : 1.5)) * 10
|
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)])
|
const blade = k.add([k.rect(2, 0), k.pos(gx, GROUND_Y), k.color(safeColor('#44cc22')), k.opacity(0.8), k.z(15)])
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
import { createFactories } from './factories'
|
import { createFactories } from './factories'
|
||||||
import type { ChoreoFn } 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) {
|
async function ultimateCloneSurround(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Spawn 4 clones around the defender, all attack simultaneously
|
// Spawn 4 clones around the defender, all attack simultaneously
|
||||||
const clones: any[] = []
|
const clones: GameObj[] = []
|
||||||
const positions = [
|
const positions = [
|
||||||
{ x: def.pos.x - 60, y: GROUND_Y }, { x: def.pos.x + 60, y: GROUND_Y },
|
{ 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 },
|
{ 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) {
|
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
|
// Summon an army of 8 mini clones that charge across the screen
|
||||||
atk.play('special'); sfxSpecial()
|
atk.play('special'); sfxSpecial()
|
||||||
const army: any[] = []
|
const army: GameObj[] = []
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
const ax = atk.pos.x + (Math.random() - 0.5) * 30
|
const ax = atk.pos.x + (Math.random() - 0.5) * 30
|
||||||
const ay = GROUND_Y - Math.random() * 10
|
const ay = GROUND_Y - Math.random() * 10
|
||||||
@@ -443,7 +444,7 @@ async function ultimateMeteorCrash(atk: Fighter, def: Fighter, dir: number, orig
|
|||||||
// Impact
|
// Impact
|
||||||
atk.pos.x = def.pos.x; atk.pos.y = -80; atk.opacity = 1
|
atk.pos.x = def.pos.x; atk.pos.y = -80; atk.opacity = 1
|
||||||
// Fire trail
|
// Fire trail
|
||||||
const fireTrail: any[] = []
|
const fireTrail: GameObj[] = []
|
||||||
await k.tween(-80, GROUND_Y, 0.15, (v) => {
|
await k.tween(-80, GROUND_Y, 0.15, (v) => {
|
||||||
atk.pos.y = 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)])
|
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()
|
atk.play('special'); sfxCritical()
|
||||||
def.play('knockback'); k.shake(25)
|
def.play('knockback'); k.shake(25)
|
||||||
// Create "shatter" fragments
|
// Create "shatter" fragments
|
||||||
const frags: any[] = []
|
const frags: GameObj[] = []
|
||||||
for (let i = 0; i < 12; i++) {
|
for (let i = 0; i < 12; i++) {
|
||||||
const fx = W * Math.random()
|
const fx = W * Math.random()
|
||||||
const fy = H * Math.random() * 0.8
|
const fy = H * Math.random() * 0.8
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from '../types'
|
import type { Fighter, ChoreoContext } from '../types'
|
||||||
import { createFactories } from './factories'
|
import { createFactories } from './factories'
|
||||||
import type { ChoreoFn } 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')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
// Draw sword arc (series of rects forming a crescent)
|
// Draw sword arc (series of rects forming a crescent)
|
||||||
const arcParts: any[] = []
|
const arcParts: GameObj[] = []
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
const angle = (i / 8) * Math.PI - Math.PI / 4
|
const angle = (i / 8) * Math.PI - Math.PI / 4
|
||||||
const ax = contactX + dir * 20 + Math.cos(angle) * 60
|
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()
|
sfxSpecial()
|
||||||
await k.wait(0.1)
|
await k.wait(0.1)
|
||||||
// Whip extends from attacker to defender (series of small segments)
|
// Whip extends from attacker to defender (series of small segments)
|
||||||
const segments: any[] = []
|
const segments: GameObj[] = []
|
||||||
const fromX = atk.pos.x + dir * 20
|
const fromX = atk.pos.x + dir * 20
|
||||||
const fromY = atk.pos.y - 40
|
const fromY = atk.pos.y - 40
|
||||||
const toX = def.pos.x
|
const toX = def.pos.x
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from './types'
|
import type { Fighter, ChoreoContext } from './types'
|
||||||
import { sfxRandomComedy, sfxBoneCrack, sfxVineBoom, sfxSlideWhistleDown, sfxPowerUp, sfxDrumRoll, announceDramatic, announceDeepIntro, announceSilly, announceRandom, announceCool, announceCrowdReaction, announceCreatorEntrance } from '../audio'
|
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 = [
|
const entrances = [
|
||||||
// 0: Drive in with a car
|
// 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 dir = fromLeft ? 1 : -1
|
||||||
const startX = fromLeft ? -120 : W + 120
|
const startX = fromLeft ? -120 : W + 120
|
||||||
const carY = GROUND_Y - 20
|
const carY = GROUND_Y - 20
|
||||||
@@ -55,13 +56,13 @@ async function playEntrance() {
|
|||||||
}, k.easings.easeInQuad).then(() => { carBody.destroy(); wheel1.destroy(); wheel2.destroy() })
|
}, k.easings.easeInQuad).then(() => { carBody.destroy(); wheel1.destroy(); wheel2.destroy() })
|
||||||
},
|
},
|
||||||
// 1: Fall from space
|
// 1: Fall from space
|
||||||
async (fighter: any, homeX: number, _fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, _fromLeft: boolean) => {
|
||||||
fighter.pos.x = homeX
|
fighter.pos.x = homeX
|
||||||
fighter.pos.y = -200
|
fighter.pos.y = -200
|
||||||
fighter.opacity = 1
|
fighter.opacity = 1
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
// Trail of fire
|
// Trail of fire
|
||||||
const trail: any[] = []
|
const trail: GameObj[] = []
|
||||||
for (let i = 0; i < 6; i++) {
|
for (let i = 0; i < 6; i++) {
|
||||||
trackedTimeout(() => {
|
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)])
|
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() })
|
trail.forEach(t => { if (t.exists()) t.destroy() })
|
||||||
},
|
},
|
||||||
// 2: Robe entrance (boxing style)
|
// 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
|
const startX = fromLeft ? -60 : W + 60
|
||||||
fighter.pos.x = startX
|
fighter.pos.x = startX
|
||||||
fighter.pos.y = GROUND_Y - 6
|
fighter.pos.y = GROUND_Y - 6
|
||||||
@@ -98,7 +99,7 @@ async function playEntrance() {
|
|||||||
spawnSparks(homeX, GROUND_Y - 40, 8, robeColor)
|
spawnSparks(homeX, GROUND_Y - 40, 8, robeColor)
|
||||||
},
|
},
|
||||||
// 3: Girlfriend argument
|
// 3: Girlfriend argument
|
||||||
async (fighter: any, homeX: number, fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, fromLeft: boolean) => {
|
||||||
const dir = fromLeft ? 1 : -1
|
const dir = fromLeft ? 1 : -1
|
||||||
const startX = fromLeft ? -60 : W + 60
|
const startX = fromLeft ? -60 : W + 60
|
||||||
fighter.pos.x = startX
|
fighter.pos.x = startX
|
||||||
@@ -126,7 +127,7 @@ async function playEntrance() {
|
|||||||
sfxBoing()
|
sfxBoing()
|
||||||
},
|
},
|
||||||
// 4: Helicopter drop
|
// 4: Helicopter drop
|
||||||
async (fighter: any, homeX: number, _fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, _fromLeft: boolean) => {
|
||||||
fighter.pos.x = homeX
|
fighter.pos.x = homeX
|
||||||
fighter.pos.y = -100
|
fighter.pos.y = -100
|
||||||
fighter.opacity = 1
|
fighter.opacity = 1
|
||||||
@@ -148,7 +149,7 @@ async function playEntrance() {
|
|||||||
.then(() => { heli.destroy(); blade.destroy() })
|
.then(() => { heli.destroy(); blade.destroy() })
|
||||||
},
|
},
|
||||||
// 5: Teleport glitch
|
// 5: Teleport glitch
|
||||||
async (fighter: any, homeX: number, _fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, _fromLeft: boolean) => {
|
||||||
fighter.opacity = 0
|
fighter.opacity = 0
|
||||||
sfxZap()
|
sfxZap()
|
||||||
// Glitch flickers at random positions
|
// Glitch flickers at random positions
|
||||||
@@ -170,7 +171,7 @@ async function playEntrance() {
|
|||||||
k.shake(5)
|
k.shake(5)
|
||||||
},
|
},
|
||||||
// 6: Skateboard ride in
|
// 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 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 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)])
|
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)
|
sfxBonk(); k.shake(3)
|
||||||
},
|
},
|
||||||
// 7: Rocket jetpack
|
// 7: Rocket jetpack
|
||||||
async (fighter: any, homeX: number, fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, fromLeft: boolean) => {
|
||||||
const startX = fromLeft ? -80 : W + 80
|
const startX = fromLeft ? -80 : W + 80
|
||||||
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 80; fighter.opacity = 1
|
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 80; fighter.opacity = 1
|
||||||
sfxJetpack()
|
sfxJetpack()
|
||||||
// Fly in with flame trail
|
// Fly in with flame trail
|
||||||
const flames: any[] = []
|
const flames: GameObj[] = []
|
||||||
const interval = trackedInterval(() => {
|
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)])
|
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)
|
flames.push(f)
|
||||||
@@ -210,7 +211,7 @@ async function playEntrance() {
|
|||||||
flames.forEach(f => { if (f.exists()) f.destroy() })
|
flames.forEach(f => { if (f.exists()) f.destroy() })
|
||||||
},
|
},
|
||||||
// 8: Emerge from portal
|
// 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)]
|
const portalColor = ['#b83dff', '#00f0ff', '#39ff14', '#ff2d7b'][Math.floor(Math.random() * 4)]
|
||||||
// Draw portal
|
// 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)])
|
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()
|
portal.destroy(); portalRing.destroy()
|
||||||
},
|
},
|
||||||
// 9: Backflip entrance
|
// 9: Backflip entrance
|
||||||
async (fighter: any, homeX: number, fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, fromLeft: boolean) => {
|
||||||
const startX = fromLeft ? -40 : W + 40
|
const startX = fromLeft ? -40 : W + 40
|
||||||
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
@@ -250,7 +251,7 @@ async function playEntrance() {
|
|||||||
spawnSparks(homeX, GROUND_Y - 10, 6, '#ffe14d')
|
spawnSparks(homeX, GROUND_Y - 10, 6, '#ffe14d')
|
||||||
},
|
},
|
||||||
// 10: Rise from underground
|
// 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
|
fighter.pos.x = homeX; fighter.pos.y = GROUND_Y + 60; fighter.opacity = 0.5
|
||||||
// Crack the ground
|
// 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)])
|
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()
|
crack1.destroy(); crack2.destroy()
|
||||||
},
|
},
|
||||||
// 11: Slide in on ice
|
// 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
|
const startX = fromLeft ? -80 : W + 80
|
||||||
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
||||||
sfxSlideDown()
|
sfxSlideDown()
|
||||||
// Ice trail
|
// Ice trail
|
||||||
const iceTrail: any[] = []
|
const iceTrail: GameObj[] = []
|
||||||
await k.tween(startX, homeX + (fromLeft ? 40 : -40), 0.4, (v) => {
|
await k.tween(startX, homeX + (fromLeft ? 40 : -40), 0.4, (v) => {
|
||||||
fighter.pos.x = v
|
fighter.pos.x = v
|
||||||
if (Math.random() < 0.3) {
|
if (Math.random() < 0.3) {
|
||||||
@@ -283,7 +284,7 @@ async function playEntrance() {
|
|||||||
trackedTimeout(() => { iceTrail.forEach(i => { if (i.exists()) i.destroy() }) }, 800)
|
trackedTimeout(() => { iceTrail.forEach(i => { if (i.exists()) i.destroy() }) }, 800)
|
||||||
},
|
},
|
||||||
// 12: Parachute drop
|
// 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
|
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 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)])
|
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)
|
sfxBonk(); k.shake(4)
|
||||||
},
|
},
|
||||||
// 13: Moonwalk in
|
// 13: Moonwalk in
|
||||||
async (fighter: any, homeX: number, fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, fromLeft: boolean) => {
|
||||||
const startX = fromLeft ? -60 : W + 60
|
const startX = fromLeft ? -60 : W + 60
|
||||||
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
||||||
announceCool('Smoother than a politician changing positions!')
|
announceCool('Smoother than a politician changing positions!')
|
||||||
@@ -316,7 +317,7 @@ async function playEntrance() {
|
|||||||
sfxBoing(); k.shake(2)
|
sfxBoing(); k.shake(2)
|
||||||
},
|
},
|
||||||
// 14: Thrown in by bouncer
|
// 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
|
const doorX = fromLeft ? -30 : W + 30
|
||||||
fighter.pos.x = doorX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
fighter.pos.x = doorX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
||||||
// Bouncer arm
|
// Bouncer arm
|
||||||
@@ -336,7 +337,7 @@ async function playEntrance() {
|
|||||||
spawnSparks(homeX, GROUND_Y - 10, 6, '#ff6600')
|
spawnSparks(homeX, GROUND_Y - 10, 6, '#ff6600')
|
||||||
},
|
},
|
||||||
// 15: Lightning strike entrance
|
// 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
|
fighter.pos.x = homeX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 0
|
||||||
// Lightning bolt from sky
|
// 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)])
|
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')
|
spawnSparks(homeX, GROUND_Y - 20, 10, '#ffff44')
|
||||||
},
|
},
|
||||||
// 16: Crowd surf in
|
// 16: Crowd surf in
|
||||||
async (fighter: any, homeX: number, fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, fromLeft: boolean) => {
|
||||||
const startX = fromLeft ? -60 : W + 60
|
const startX = fromLeft ? -60 : W + 60
|
||||||
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 50; fighter.opacity = 1
|
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 50; fighter.opacity = 1
|
||||||
// Crowd hands
|
// Crowd hands
|
||||||
const hands: any[] = []
|
const hands: GameObj[] = []
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
const hx = startX + (fromLeft ? 1 : -1) * (Math.abs(homeX - startX) / 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)])
|
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() })
|
hands.forEach(h => { if (h.exists()) h.destroy() })
|
||||||
},
|
},
|
||||||
// 17: Riding a shopping cart
|
// 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 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 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)])
|
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')
|
spawnSparks(homeX, GROUND_Y - 10, 5, '#888888')
|
||||||
},
|
},
|
||||||
// 18: Dramatic slow walk with spotlight
|
// 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
|
const startX = fromLeft ? -60 : W + 60
|
||||||
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
fighter.pos.x = startX; fighter.pos.y = GROUND_Y - 6; fighter.opacity = 1
|
||||||
// Spotlight cone
|
// Spotlight cone
|
||||||
@@ -409,7 +410,7 @@ async function playEntrance() {
|
|||||||
spawnSparks(homeX, GROUND_Y - 30, 8, '#ffe14d')
|
spawnSparks(homeX, GROUND_Y - 30, 8, '#ffe14d')
|
||||||
},
|
},
|
||||||
// 19: Cannon launch
|
// 19: Cannon launch
|
||||||
async (fighter: any, homeX: number, fromLeft: boolean) => {
|
async (fighter: Fighter, homeX: number, fromLeft: boolean) => {
|
||||||
const cannonX = fromLeft ? -40 : W + 40
|
const cannonX = fromLeft ? -40 : W + 40
|
||||||
// Draw cannon
|
// 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)])
|
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
|
// 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.x = homeX
|
||||||
fighter.pos.y = -100
|
fighter.pos.y = -100
|
||||||
fighter.opacity = 0
|
fighter.opacity = 0
|
||||||
// Golden code rain columns
|
// Golden code rain columns
|
||||||
const codeChars = '₿01∞⚡⛏§∂∆≈≠'.split('')
|
const codeChars = '₿01∞⚡⛏§∂∆≈≠'.split('')
|
||||||
const rainDrops: any[] = []
|
const rainDrops: GameObj[] = []
|
||||||
for (let col = 0; col < 12; col++) {
|
for (let col = 0; col < 12; col++) {
|
||||||
const cx = homeX - 60 + col * 10
|
const cx = homeX - 60 + col * 10
|
||||||
for (let row = 0; row < 4; row++) {
|
for (let row = 0; row < 4; row++) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext } from './types'
|
import type { Fighter, ChoreoContext } from './types'
|
||||||
import {
|
import {
|
||||||
announceHype, announceDramatic, announceSilly, announceFast, announceRobot,
|
announceHype, announceDramatic, announceSilly, announceFast, announceRobot,
|
||||||
@@ -19,13 +20,13 @@ interface FinisherDeps {
|
|||||||
spawnEmoteText: (x: number, y: number, text: string, color: string, duration?: number) => void
|
spawnEmoteText: (x: number, y: number, text: string, color: string, duration?: number) => void
|
||||||
spawnCrowdSigns: (count: number, color: string, text?: string) => void
|
spawnCrowdSigns: (count: number, color: string, text?: string) => void
|
||||||
spawnHeart: (x: number, y: number) => void
|
spawnHeart: (x: number, y: number) => void
|
||||||
spawnWeaponProp: (propName: string, x: number, y: number, flipX?: boolean, zIndex?: number) => any[]
|
spawnWeaponProp: (propName: string, x: number, y: number, flipX?: boolean, zIndex?: number) => GameObj[]
|
||||||
destroyProp: (objs: any[]) => void
|
destroyProp: (objs: GameObj[]) => void
|
||||||
spawnProp: (fromX: number, fromY: number, toX: number, toY: number, w: number, h: number, color: string, speed?: number, isCircle?: boolean) => Promise<void>
|
spawnProp: (fromX: number, fromY: number, toX: number, toY: number, w: number, h: number, color: string, speed?: number, isCircle?: boolean) => Promise<void>
|
||||||
playBow: (fighter: any) => Promise<void>
|
playBow: (fighter: Fighter) => Promise<void>
|
||||||
playFistBump: (fighterA: any, fighterB: any) => Promise<void>
|
playFistBump: (fighterA: Fighter, fighterB: Fighter) => Promise<void>
|
||||||
playHelpUp: (winner: any, loser: any, winningSide: 'a' | 'b') => Promise<void>
|
playHelpUp: (winner: Fighter, loser: Fighter, winningSide: 'a' | 'b') => Promise<void>
|
||||||
cameraZoom: (fighters: any[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise<void>
|
cameraZoom: (fighters: Fighter[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise<void>
|
||||||
hyperSpeedLines: (targetX: number, targetY: number, duration?: number) => void
|
hyperSpeedLines: (targetX: number, targetY: number, duration?: number) => void
|
||||||
showSpeechBubble: (side: 'a' | 'b', text: string, duration?: number) => void
|
showSpeechBubble: (side: 'a' | 'b', text: string, duration?: number) => void
|
||||||
spawnHumanCoach: (side: 'a' | 'b', mood: 'cheer' | 'panic' | 'coach') => Promise<void>
|
spawnHumanCoach: (side: 'a' | 'b', mood: 'cheer' | 'panic' | 'coach') => Promise<void>
|
||||||
|
|||||||
@@ -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 { generateSpriteSheet, MAX_FRAMES, TOTAL_ROWS, type SpriteCustomization } from '../sprites'
|
||||||
import { spriteAnims } from './constants'
|
import { spriteAnims } from './constants'
|
||||||
import { announceHype } from '../audio'
|
import { announceHype } from '../audio'
|
||||||
@@ -14,14 +15,14 @@ interface MorphDeps {
|
|||||||
colorsB: { primary: string; secondary: string }
|
colorsB: { primary: string; secondary: string }
|
||||||
isHumanA: boolean
|
isHumanA: boolean
|
||||||
isHumanB: boolean
|
isHumanB: boolean
|
||||||
loadSpriteWithTimeout: (name: string, src: string, opts: any) => Promise<void>
|
loadSpriteWithTimeout: (name: string, src: string, opts: Record<string, unknown>) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createMorphSystem(ctx: ChoreoContext, deps: MorphDeps) {
|
export function createMorphSystem(ctx: ChoreoContext, deps: MorphDeps) {
|
||||||
const { k, safeColor, screenFlash, spawnShockwave, glitchRGB, sfxZoomWhoosh, sfxSpecial, theme } = ctx
|
const { k, safeColor, screenFlash, spawnShockwave, glitchRGB, sfxZoomWhoosh, sfxSpecial, theme } = ctx
|
||||||
const { botA, botB, colorsA, colorsB, isHumanA, isHumanB, loadSpriteWithTimeout } = deps
|
const { botA, botB, colorsA, colorsB, isHumanA, isHumanB, loadSpriteWithTimeout } = deps
|
||||||
|
|
||||||
interface MorphOverlay { obj: any; type: string }
|
interface MorphOverlay { obj: GameObj; type: string }
|
||||||
let activeMorphs: MorphOverlay[] = []
|
let activeMorphs: MorphOverlay[] = []
|
||||||
|
|
||||||
function destroyMorphOverlays() {
|
function destroyMorphOverlays() {
|
||||||
@@ -30,7 +31,7 @@ function destroyMorphOverlays() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Morph 0: ELEMENTAL — fire/ice/electric aura wraps around the fighter
|
// 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
|
const seed = side === 'a' ? botA.seed : botB.seed
|
||||||
let h = 0
|
let h = 0
|
||||||
for (let i = 0; i < seed.length; i++) h = ((h << 5) - h + seed.charCodeAt(i)) | 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
|
// 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
|
const dir = fighter.scale.x > 0 ? 1 : -1
|
||||||
|
|
||||||
// Shoulder armor plates
|
// Shoulder armor plates
|
||||||
@@ -152,7 +153,7 @@ function applyMechMorph(fighter: any, _side: 'a' | 'b') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Morph 2: BEAST — grows horns/tail/claws, goes wild
|
// 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 dir = fighter.scale.x > 0 ? 1 : -1
|
||||||
const seed = side === 'a' ? botA.seed : botB.seed
|
const seed = side === 'a' ? botA.seed : botB.seed
|
||||||
let h = 0
|
let h = 0
|
||||||
@@ -255,7 +256,7 @@ const OMNI_MORPH_ARCHETYPES = [
|
|||||||
|
|
||||||
let omniMorphCounter = 0
|
let omniMorphCounter = 0
|
||||||
|
|
||||||
async function applyCreatorOmniMorph(fighter: any, side: 'a' | 'b'): Promise<string> {
|
async function applyCreatorOmniMorph(fighter: Fighter, side: 'a' | 'b'): Promise<string> {
|
||||||
const pick = OMNI_MORPH_ARCHETYPES[Math.floor(Math.random() * OMNI_MORPH_ARCHETYPES.length)]
|
const pick = OMNI_MORPH_ARCHETYPES[Math.floor(Math.random() * OMNI_MORPH_ARCHETYPES.length)]
|
||||||
|
|
||||||
// Generate and load a sprite sheet for the picked archetype
|
// 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]
|
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 savedScaleX = fighter.scale.x
|
||||||
const savedScaleY = fighter.scale.y
|
const savedScaleY = fighter.scale.y
|
||||||
const isHumanFighter = side === 'a' ? isHumanA : isHumanB
|
const isHumanFighter = side === 'a' ? isHumanA : isHumanB
|
||||||
|
|||||||
@@ -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 speed = (intense ? 150 : 100) + Math.random() * (intense ? 400 : 300)
|
||||||
const size = (intense ? 3 : 2) + Math.random() * (intense ? 6 : 4)
|
const size = (intense ? 3 : 2) + Math.random() * (intense ? 6 : 4)
|
||||||
const rotSpeed = (Math.random() - 0.5) * 600
|
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([
|
const spark = k.add([
|
||||||
k.rect(size, size * (0.5 + Math.random() * 0.5)),
|
k.rect(size, size * (0.5 + Math.random() * 0.5)),
|
||||||
k.pos(x, y),
|
k.pos(x, y),
|
||||||
@@ -18,13 +21,12 @@ export function spawnSparks(ctx: FightContext, x: number, y: number, count: numb
|
|||||||
k.z(20),
|
k.z(20),
|
||||||
k.rotate(Math.random() * 360),
|
k.rotate(Math.random() * 360),
|
||||||
k.anchor('center'),
|
k.anchor('center'),
|
||||||
{ vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed - 100, rotV: rotSpeed },
|
|
||||||
])
|
])
|
||||||
spark.onUpdate(() => {
|
spark.onUpdate(() => {
|
||||||
spark.pos.x += (spark as any).vx * k.dt()
|
spark.pos.x += vx * k.dt()
|
||||||
spark.pos.y += (spark as any).vy * k.dt()
|
spark.pos.y += vy * k.dt()
|
||||||
;(spark as any).vy += 500 * k.dt()
|
vy += 500 * k.dt()
|
||||||
spark.angle += (spark as any).rotV * k.dt()
|
spark.angle += rotV * k.dt()
|
||||||
spark.opacity -= 1.5 * k.dt()
|
spark.opacity -= 1.5 * k.dt()
|
||||||
if (spark.opacity <= 0) spark.destroy()
|
if (spark.opacity <= 0) spark.destroy()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { GameObj } from 'kaplay'
|
||||||
import type { Fighter, ChoreoContext, RoundEvent } from './types'
|
import type { Fighter, ChoreoContext, RoundEvent } from './types'
|
||||||
import type { ChoreoFn } from './choreography/factories'
|
import type { ChoreoFn } from './choreography/factories'
|
||||||
import { pickChoreography } from '../FightScene'
|
import { pickChoreography } from '../FightScene'
|
||||||
@@ -28,14 +29,14 @@ interface RoundDeps {
|
|||||||
choreographyMap: Record<string, ChoreoFn>
|
choreographyMap: Record<string, ChoreoFn>
|
||||||
playBlock: (side: 'a' | 'b') => Promise<void>
|
playBlock: (side: 'a' | 'b') => Promise<void>
|
||||||
destroyMorphOverlays: () => void
|
destroyMorphOverlays: () => void
|
||||||
playMorph: (fighter: any, side: 'a' | 'b', morphIndex: number) => Promise<{ revert: () => Promise<void> }>
|
playMorph: (fighter: Fighter, side: 'a' | 'b', morphIndex: number) => Promise<{ revert: () => Promise<void> }>
|
||||||
getMorphOrder: (seed: string) => number[]
|
getMorphOrder: (seed: string) => number[]
|
||||||
applyCreatorOmniMorph: (fighter: any, side: 'a' | 'b') => Promise<string>
|
applyCreatorOmniMorph: (fighter: Fighter, side: 'a' | 'b') => Promise<string>
|
||||||
spawnEmoteText: (x: number, y: number, text: string, color: string, duration?: number) => void
|
spawnEmoteText: (x: number, y: number, text: string, color: string, duration?: number) => void
|
||||||
spawnCrowdSigns: (count: number, color: string, text?: string) => void
|
spawnCrowdSigns: (count: number, color: string, text?: string) => void
|
||||||
judgeDoSomethingFunny: () => Promise<void>
|
judgeDoSomethingFunny: () => Promise<void>
|
||||||
maybeShowHuman: () => Promise<void>
|
maybeShowHuman: () => Promise<void>
|
||||||
cameraZoom: (fighters: any[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise<void>
|
cameraZoom: (fighters: Fighter[], savedScales: { x: number; y: number }[], zoomFactor: number, duration: number, easing?: (t: number) => number) => Promise<void>
|
||||||
hyperSpeedLines: (targetX: number, targetY: number, duration?: number) => void
|
hyperSpeedLines: (targetX: number, targetY: number, duration?: number) => void
|
||||||
schizoCut: () => Promise<void>
|
schizoCut: () => Promise<void>
|
||||||
showSpeechBubble: (side: 'a' | 'b', text: string, duration?: number) => void
|
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 ===
|
||||||
// === PHYSICAL CONTACT SYSTEMS ===
|
// === PHYSICAL CONTACT SYSTEMS ===
|
||||||
// Helper: spawn dizzy stars orbiting a fighter's head
|
// Helper: spawn dizzy stars orbiting a fighter's head
|
||||||
function _spawnDizzyStars(fighter: any, duration: number) {
|
function _spawnDizzyStars(fighter: Fighter, duration: number) {
|
||||||
const stars: any[] = []
|
const stars: GameObj[] = []
|
||||||
const starChars = ['*', '\u2605', '\u00d7', '!', '?']
|
const starChars = ['*', '\u2605', '\u00d7', '!', '?']
|
||||||
const starColors = ['#ffff00', '#ffffff', '#ff4444', '#44ff44', '#ff88ff']
|
const starColors = ['#ffff00', '#ffffff', '#ff4444', '#44ff44', '#ff88ff']
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
@@ -767,7 +768,7 @@ async function playRound(event: RoundEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Super-speed: persistent speed lines during round
|
// Super-speed: persistent speed lines during round
|
||||||
let speedLines: any[] = []
|
let speedLines: GameObj[] = []
|
||||||
if (superSpeed) {
|
if (superSpeed) {
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const lineY = GROUND_Y - 5 - Math.random() * 130
|
const lineY = GROUND_Y - 5 - Math.random() * 130
|
||||||
@@ -782,7 +783,7 @@ async function playRound(event: RoundEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// === RETRO MODE GAMEPAD OVERLAY ===
|
// === RETRO MODE GAMEPAD OVERLAY ===
|
||||||
const retroObjs: any[] = []
|
const retroObjs: GameObj[] = []
|
||||||
if (event.challengeType === 'retro_mode') {
|
if (event.challengeType === 'retro_mode') {
|
||||||
const padW = 90
|
const padW = 90
|
||||||
const padH = 70
|
const padH = 70
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export interface ChoreoContext {
|
|||||||
sfxBlock: () => void
|
sfxBlock: () => void
|
||||||
// Other effects
|
// Other effects
|
||||||
spawnBullet: (fromX: number, fromY: number, toX: number, toY: number) => Promise<void>
|
spawnBullet: (fromX: number, fromY: number, toX: number, toY: number) => Promise<void>
|
||||||
spawnGrotesqueDetails: (fighter: any, scaleFactor: number) => void
|
spawnGrotesqueDetails: (fighter: Fighter, scaleFactor: number) => void
|
||||||
destroyGrotesqueDetails: () => void
|
destroyGrotesqueDetails: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user