refactor: type Fighter interface for all choreographies
Define Fighter type alias using Kaplay's GameObj with SpriteComp, PosComp, ScaleComp, AnchorComp, OpacityComp, ColorComp, RotateComp, and ZComp. Replace 81 choreography function signatures from (atk: any, def: any, ...) to (atk: Fighter, def: Fighter, ...). Type loadSpriteWithTimeout opts as LoadSpriteOpt. Type lines/bands arrays as GameObj[]. Type spawnAfterimages and cameraZoom fighter params. Add as Fighter casts at k.get() call sites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
156c727f82
commit
1ca8a6feb0
+137
-134
@@ -1,4 +1,7 @@
|
|||||||
import kaplay from 'kaplay'
|
import kaplay from 'kaplay'
|
||||||
|
import type { GameObj, SpriteComp, PosComp, ScaleComp, AnchorComp, OpacityComp, ColorComp, RotateComp, ZComp, LoadSpriteOpt } from 'kaplay'
|
||||||
|
|
||||||
|
type Fighter = GameObj<SpriteComp | PosComp | ScaleComp | AnchorComp | OpacityComp | ColorComp | RotateComp | ZComp>
|
||||||
import { generateSpriteSheet, generateJudgeSpriteSheet, generateHumanSpriteSheet, generateHumanFighterSpriteSheet, getBotColors, FRAME_SIZE, MAX_FRAMES, TOTAL_ROWS, ANIMATIONS, JUDGE_ANIMATIONS, JUDGE_MAX_FRAMES, JUDGE_ROWS, HUMAN_ANIMATIONS, HUMAN_MAX_FRAMES, HUMAN_ROWS, type SpriteCustomization } from './sprites'
|
import { generateSpriteSheet, generateJudgeSpriteSheet, generateHumanSpriteSheet, generateHumanFighterSpriteSheet, getBotColors, FRAME_SIZE, MAX_FRAMES, TOTAL_ROWS, ANIMATIONS, JUDGE_ANIMATIONS, JUDGE_MAX_FRAMES, JUDGE_ROWS, HUMAN_ANIMATIONS, HUMAN_MAX_FRAMES, HUMAN_ROWS, type SpriteCustomization } from './sprites'
|
||||||
import {
|
import {
|
||||||
sfxPunch, sfxKick, sfxSpecial, sfxCritical, sfxGunshot, sfxBulletHit,
|
sfxPunch, sfxKick, sfxSpecial, sfxCritical, sfxGunshot, sfxBulletHit,
|
||||||
@@ -293,7 +296,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
const isHumanB = botB.archetype === 'human'
|
const isHumanB = botB.archetype === 'human'
|
||||||
|
|
||||||
// Helper: load sprite with a 5s timeout so mobile never hangs forever
|
// Helper: load sprite with a 5s timeout so mobile never hangs forever
|
||||||
async function loadSpriteWithTimeout(name: string, src: string, opts: any): Promise<void> {
|
async function loadSpriteWithTimeout(name: string, src: string, opts: LoadSpriteOpt): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
k.loadSprite(name, src, opts),
|
k.loadSprite(name, src, opts),
|
||||||
@@ -519,7 +522,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Scanline glitch — horizontal bands flicker
|
// Scanline glitch — horizontal bands flicker
|
||||||
function scanlineGlitch(duration: number = 0.3) {
|
function scanlineGlitch(duration: number = 0.3) {
|
||||||
const lines: any[] = []
|
const lines: GameObj[] = []
|
||||||
const count = 6 + Math.floor(Math.random() * 8)
|
const count = 6 + Math.floor(Math.random() * 8)
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const ly = Math.random() * H
|
const ly = Math.random() * H
|
||||||
@@ -540,7 +543,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// VHS tracking distortion — horizontal offset bands
|
// VHS tracking distortion — horizontal offset bands
|
||||||
function vhsTracking(duration: number = 0.4) {
|
function vhsTracking(duration: number = 0.4) {
|
||||||
const bands: any[] = []
|
const bands: GameObj[] = []
|
||||||
const bandCount = 3
|
const bandCount = 3
|
||||||
for (let i = 0; i < bandCount; i++) {
|
for (let i = 0; i < bandCount; i++) {
|
||||||
const by = Math.random() * H
|
const by = Math.random() * H
|
||||||
@@ -576,8 +579,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Schizo cut — rapid zoom/position jitter simulating jump cuts
|
// Schizo cut — rapid zoom/position jitter simulating jump cuts
|
||||||
async function schizoCut() {
|
async function schizoCut() {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const origAX = fA.pos.x, origBX = fB.pos.x
|
const origAX = fA.pos.x, origBX = fB.pos.x
|
||||||
const origAY = fA.pos.y, origBY = fB.pos.y
|
const origAY = fA.pos.y, origBY = fB.pos.y
|
||||||
@@ -598,7 +601,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Hyperspeed lines — converging toward a point
|
// Hyperspeed lines — converging toward a point
|
||||||
function hyperSpeedLines(targetX: number, targetY: number, duration: number = 0.3) {
|
function hyperSpeedLines(targetX: number, targetY: number, duration: number = 0.3) {
|
||||||
const lines: any[] = []
|
const lines: GameObj[] = []
|
||||||
for (let i = 0; i < 16; i++) {
|
for (let i = 0; i < 16; i++) {
|
||||||
const angle = (i / 16) * Math.PI * 2
|
const angle = (i / 16) * Math.PI * 2
|
||||||
const dist = 300 + Math.random() * 200
|
const dist = 300 + Math.random() * 200
|
||||||
@@ -650,7 +653,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Spawn afterimages: trailing ghost sprites behind a moving fighter
|
// Spawn afterimages: trailing ghost sprites behind a moving fighter
|
||||||
function spawnAfterimages(fighter: any, count: number = 3) {
|
function spawnAfterimages(fighter: Fighter, count: number = 3) {
|
||||||
if (!fighter?.exists()) return
|
if (!fighter?.exists()) return
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const ghost = k.add([
|
const ghost = k.add([
|
||||||
@@ -667,7 +670,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Camera zoom: scale fighters toward/away from a focal point for impact emphasis
|
// Camera zoom: scale fighters toward/away from a focal point for impact emphasis
|
||||||
async function cameraZoom(
|
async function cameraZoom(
|
||||||
fighters: any[],
|
fighters: Fighter[],
|
||||||
savedScales: { x: number; y: number }[],
|
savedScales: { x: number; y: number }[],
|
||||||
zoomFactor: number,
|
zoomFactor: number,
|
||||||
duration: number,
|
duration: number,
|
||||||
@@ -685,7 +688,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Grotesque close-up overlays removed — noops retained for call-site compatibility
|
// Grotesque close-up overlays removed — noops retained for call-site compatibility
|
||||||
function spawnGrotesqueDetails(_fighter: any, _scaleFactor: number) {}
|
function spawnGrotesqueDetails(_fighter: Fighter, _scaleFactor: number) {}
|
||||||
function destroyGrotesqueDetails() {}
|
function destroyGrotesqueDetails() {}
|
||||||
// Arena-specific background decoration
|
// Arena-specific background decoration
|
||||||
function drawArenaDecor() {
|
function drawArenaDecor() {
|
||||||
@@ -2038,7 +2041,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
// === CHOREOGRAPHY FUNCTIONS ===
|
// === CHOREOGRAPHY FUNCTIONS ===
|
||||||
// Each returns a Promise and handles all movement + animation
|
// Each returns a Promise and handles all movement + animation
|
||||||
|
|
||||||
async function dashPunch(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function dashPunch(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 50
|
const contactX = origDX - dir * 50
|
||||||
// Fast sprint to contact
|
// Fast sprint to contact
|
||||||
await k.tween(atk.pos.x, contactX, 0.12, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.12, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
@@ -2061,7 +2064,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function aerialSlam(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function aerialSlam(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const origAY = atk.pos.y
|
const origAY = atk.pos.y
|
||||||
const contactX = origDX - dir * 20
|
const contactX = origDX - dir * 20
|
||||||
// Jump way up
|
// Jump way up
|
||||||
@@ -2090,7 +2093,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function flyingKick(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function flyingKick(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const origAY = atk.pos.y
|
const origAY = atk.pos.y
|
||||||
const contactX = origDX - dir * 30
|
const contactX = origDX - dir * 30
|
||||||
// Small jump + fly horizontally
|
// Small jump + fly horizontally
|
||||||
@@ -2117,7 +2120,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dashThrough(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function dashThrough(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const behindX = origDX + dir * 80
|
const behindX = origDX + dir * 80
|
||||||
atk.play('attack')
|
atk.play('attack')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -2137,7 +2140,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.1)
|
await k.wait(0.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uppercut(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function uppercut(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 40
|
const contactX = origDX - dir * 40
|
||||||
const origDY = def.pos.y
|
const origDY = def.pos.y
|
||||||
// Run to contact
|
// Run to contact
|
||||||
@@ -2163,7 +2166,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(atk.pos.x, origAX, 0.2, (v) => { atk.pos.x = v }, k.easings.easeInQuad)
|
await k.tween(atk.pos.x, origAX, 0.2, (v) => { atk.pos.x = v }, k.easings.easeInQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function multiHit(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function multiHit(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 45
|
const contactX = origDX - dir * 45
|
||||||
// Sprint to contact
|
// Sprint to contact
|
||||||
await k.tween(atk.pos.x, contactX, 0.1, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.1, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
@@ -2192,7 +2195,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function projectile(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function projectile(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.25)
|
await k.wait(0.25)
|
||||||
@@ -2213,7 +2216,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function jetpackDive(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function jetpackDive(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const origAY = atk.pos.y
|
const origAY = atk.pos.y
|
||||||
sfxJetpack()
|
sfxJetpack()
|
||||||
// Jetpack blast off - fly way up off screen
|
// Jetpack blast off - fly way up off screen
|
||||||
@@ -2250,7 +2253,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gunBurst(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function gunBurst(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
// Spawn visible pixel gun prop
|
// Spawn visible pixel gun prop
|
||||||
const gunX = atk.pos.x + dir * 15
|
const gunX = atk.pos.x + dir * 15
|
||||||
@@ -2292,7 +2295,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function groundPound(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function groundPound(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const origAY = atk.pos.y
|
const origAY = atk.pos.y
|
||||||
// Jump super high off screen
|
// Jump super high off screen
|
||||||
sfxJetpack()
|
sfxJetpack()
|
||||||
@@ -2332,7 +2335,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function teleportStrike(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function teleportStrike(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Fade out
|
// Fade out
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
spawnSparks(atk.pos.x, atk.pos.y - 30, 8, '#b83dff')
|
spawnSparks(atk.pos.x, atk.pos.y - 30, 8, '#b83dff')
|
||||||
@@ -2365,7 +2368,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fullScreenDash(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function fullScreenDash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Back up to wall
|
// Back up to wall
|
||||||
const wallX = dir === 1 ? 20 : W - 20
|
const wallX = dir === 1 ? 20 : W - 20
|
||||||
await k.tween(atk.pos.x, wallX, 0.15, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, wallX, 0.15, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
@@ -2448,7 +2451,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CODE GOLF: swing a giant golf club, golf balls fly at opponent
|
// CODE GOLF: swing a giant golf club, golf balls fly at opponent
|
||||||
async function golfClubSmash(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function golfClubSmash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 60
|
const contactX = origDX - dir * 60
|
||||||
await k.tween(atk.pos.x, contactX, 0.15, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.15, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
// Spawn golf club (long thin rect)
|
// Spawn golf club (long thin rect)
|
||||||
@@ -2509,7 +2512,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ROAST BATTLE: fire breath — stream of fire from attacker's face
|
// ROAST BATTLE: fire breath — stream of fire from attacker's face
|
||||||
async function fireBreath(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function fireBreath(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 80
|
const contactX = origDX - dir * 80
|
||||||
await k.tween(atk.pos.x, contactX, 0.12, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.12, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
@@ -2567,7 +2570,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RIDDLE: question mark barrage rains from the sky
|
// RIDDLE: question mark barrage rains from the sky
|
||||||
async function riddleBarrage(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function riddleBarrage(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
sfxZap()
|
sfxZap()
|
||||||
@@ -2612,7 +2615,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HALLUCINATION CHECK: clone confusion — multiple fake attackers appear
|
// HALLUCINATION CHECK: clone confusion — multiple fake attackers appear
|
||||||
async function cloneStrike(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function cloneStrike(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
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
|
||||||
@@ -2662,7 +2665,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TOKEN ECONOMY: coin shower — coins rain down on opponent
|
// TOKEN ECONOMY: coin shower — coins rain down on opponent
|
||||||
async function coinShower(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function coinShower(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
sfxBoing()
|
sfxBoing()
|
||||||
@@ -2698,7 +2701,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CREATIVE WRITING: giant pen stab + ink splatter
|
// CREATIVE WRITING: giant pen stab + ink splatter
|
||||||
async function penStab(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function penStab(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Spawn a giant pen/quill
|
// Spawn a giant pen/quill
|
||||||
const penX = atk.pos.x + dir * 15
|
const penX = atk.pos.x + dir * 15
|
||||||
const penY = atk.pos.y - 70
|
const penY = atk.pos.y - 70
|
||||||
@@ -2757,7 +2760,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MATH BLITZ: number projectiles fly at opponent
|
// MATH BLITZ: number projectiles fly at opponent
|
||||||
async function mathAttack(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function mathAttack(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
@@ -2798,7 +2801,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TRAP CARD: card shuriken + trap springs
|
// TRAP CARD: card shuriken + trap springs
|
||||||
async function trapCardAttack(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function trapCardAttack(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.1)
|
await k.wait(0.1)
|
||||||
@@ -2863,7 +2866,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SPEED BLITZ: afterimage dash — leaves ghost copies behind
|
// SPEED BLITZ: afterimage dash — leaves ghost copies behind
|
||||||
async function afterimageDash(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function afterimageDash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
const contactX = origDX - dir * 40
|
const contactX = origDX - dir * 40
|
||||||
// Leave afterimages as we dash
|
// Leave afterimages as we dash
|
||||||
@@ -2911,7 +2914,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BULLET TIME: slow-mo dodge, afterimages trail behind, golden traces, devastating counter
|
// BULLET TIME: slow-mo dodge, afterimages trail behind, golden traces, devastating counter
|
||||||
async function bulletTimeAttack(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function bulletTimeAttack(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Phase 1: Darken screen — entering bullet time
|
// Phase 1: Darken screen — entering bullet time
|
||||||
const dim = k.add([k.rect(W, H), k.pos(0, 0), k.color(safeColor(k, '#000011')), k.opacity(0), k.z(25)])
|
const dim = k.add([k.rect(W, H), k.pos(0, 0), k.color(safeColor(k, '#000011')), k.opacity(0), k.z(25)])
|
||||||
await k.tween(0, 0.45, 0.15, (v) => { dim.opacity = v })
|
await k.tween(0, 0.45, 0.15, (v) => { dim.opacity = v })
|
||||||
@@ -3017,7 +3020,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// INVISIBLE SPEED: so fast you can barely see — just blur lines and impact
|
// INVISIBLE SPEED: so fast you can barely see — just blur lines and impact
|
||||||
async function lightningRush(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function lightningRush(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
// Attacker vanishes
|
// Attacker vanishes
|
||||||
atk.opacity = 0
|
atk.opacity = 0
|
||||||
@@ -3069,7 +3072,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ZOOM RUSH: fly toward camera (scale huge) then slam back into opponent
|
// ZOOM RUSH: fly toward camera (scale huge) then slam back into opponent
|
||||||
async function zoomRush(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function zoomRush(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const origAY = atk.pos.y
|
const origAY = atk.pos.y
|
||||||
const origScaleX = atk.scale.x
|
const origScaleX = atk.scale.x
|
||||||
const origScaleY = atk.scale.y
|
const origScaleY = atk.scale.y
|
||||||
@@ -3150,7 +3153,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RAPID FLURRY: DBZ-style machine gun punches — so fast it's a blur
|
// RAPID FLURRY: DBZ-style machine gun punches — so fast it's a blur
|
||||||
async function rapidFlurry(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function rapidFlurry(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 40
|
const contactX = origDX - dir * 40
|
||||||
// Sprint to contact
|
// Sprint to contact
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -3210,7 +3213,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SWORD SLASH: giant energy sword swing with arc trail
|
// SWORD SLASH: giant energy sword swing with arc trail
|
||||||
async function swordSlash(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function swordSlash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 50
|
const contactX = origDX - dir * 50
|
||||||
await k.tween(atk.pos.x, contactX, 0.1, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.1, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
@@ -3245,7 +3248,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HAMMER SMASH: pull out giant hammer, overhead slam with ground crack
|
// HAMMER SMASH: pull out giant hammer, overhead slam with ground crack
|
||||||
async function hammerSmash(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function hammerSmash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 40
|
const contactX = origDX - dir * 40
|
||||||
await k.tween(atk.pos.x, contactX, 0.12, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.12, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
// Giant hammer
|
// Giant hammer
|
||||||
@@ -3277,7 +3280,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LASER BEAM: charge up then fire continuous beam across screen
|
// LASER BEAM: charge up then fire continuous beam across screen
|
||||||
async function laserBeam(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function laserBeam(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
// Charge up (growing ball of energy)
|
// Charge up (growing ball of energy)
|
||||||
@@ -3308,7 +3311,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ROCKET LAUNCHER: fire a big slow rocket that explodes on impact
|
// ROCKET LAUNCHER: fire a big slow rocket that explodes on impact
|
||||||
async function rocketLauncher(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function rocketLauncher(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
@@ -3352,7 +3355,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BOMB THROW: lob an arcing bomb that bounces once then detonates
|
// BOMB THROW: lob an arcing bomb that bounces once then detonates
|
||||||
async function bombThrow(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function bombThrow(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('attack')
|
atk.play('attack')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
const bombX = atk.pos.x + dir * 20
|
const bombX = atk.pos.x + dir * 20
|
||||||
@@ -3387,7 +3390,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MINIGUN SPRAY: sustained automatic fire with shell casings
|
// MINIGUN SPRAY: sustained automatic fire with shell casings
|
||||||
async function minigunSpray(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function minigunSpray(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
// Spawn visible pixel minigun
|
// Spawn visible pixel minigun
|
||||||
const gpx = atk.pos.x + dir * 15
|
const gpx = atk.pos.x + dir * 15
|
||||||
@@ -3433,7 +3436,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SNIPER SHOT: long pause, laser sight, then one devastating hit
|
// SNIPER SHOT: long pause, laser sight, then one devastating hit
|
||||||
async function sniperShot(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function sniperShot(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Back up to wall
|
// Back up to wall
|
||||||
const wallX = dir === 1 ? 30 : W - 30
|
const wallX = dir === 1 ? 30 : W - 30
|
||||||
await k.tween(atk.pos.x, wallX, 0.2, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, wallX, 0.2, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
@@ -3471,7 +3474,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WHIP CRACK: ranged whip attack with crack sound
|
// WHIP CRACK: ranged whip attack with crack sound
|
||||||
async function whipCrack(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function whipCrack(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('attack')
|
atk.play('attack')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.1)
|
await k.wait(0.1)
|
||||||
@@ -3514,7 +3517,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// KATANA COMBO: triple slash with cross patterns
|
// KATANA COMBO: triple slash with cross patterns
|
||||||
async function katanaCombo(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function katanaCombo(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 45
|
const contactX = origDX - dir * 45
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.tween(atk.pos.x, contactX, 0.08, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.08, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
@@ -3552,7 +3555,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CHAINSAW: charge with buzzing chainsaw, multi-hit grind
|
// CHAINSAW: charge with buzzing chainsaw, multi-hit grind
|
||||||
async function chainsawRev(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function chainsawRev(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 35
|
const contactX = origDX - dir * 35
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
// Chainsaw buzzing (visual: vibrating rect)
|
// Chainsaw buzzing (visual: vibrating rect)
|
||||||
@@ -3595,7 +3598,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MOTORBIKE CHARGE: ride a motorbike across the screen and slam into defender
|
// MOTORBIKE CHARGE: ride a motorbike across the screen and slam into defender
|
||||||
async function motorbikeCharge(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function motorbikeCharge(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Back up to edge
|
// Back up to edge
|
||||||
const startX = dir === 1 ? -30 : W + 30
|
const startX = dir === 1 ? -30 : W + 30
|
||||||
atk.opacity = 0
|
atk.opacity = 0
|
||||||
@@ -3643,7 +3646,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CAR SMASH: drive a car from off-screen, defender gets sent flying
|
// CAR SMASH: drive a car from off-screen, defender gets sent flying
|
||||||
async function carSmash(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function carSmash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.opacity = 0
|
atk.opacity = 0
|
||||||
const carY = GROUND_Y - 20
|
const carY = GROUND_Y - 20
|
||||||
const startX = dir === 1 ? -60 : W + 60
|
const startX = dir === 1 ? -60 : W + 60
|
||||||
@@ -3688,7 +3691,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BOAT CANNON: a boat slides across the ground and fires cannons
|
// BOAT CANNON: a boat slides across the ground and fires cannons
|
||||||
async function boatCannon(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function boatCannon(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
// Build boat
|
// Build boat
|
||||||
@@ -3731,7 +3734,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ANVIL DROP: classic cartoon — anvil falls from sky onto opponent
|
// ANVIL DROP: classic cartoon — anvil falls from sky onto opponent
|
||||||
async function anvilDrop(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function anvilDrop(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
@@ -3766,7 +3769,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POCKET CANNON: reach into pocket, pull out comically oversized gun, fire
|
// POCKET CANNON: reach into pocket, pull out comically oversized gun, fire
|
||||||
async function pocketCannon(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function pocketCannon(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// "Reach into pocket" — arm down motion
|
// "Reach into pocket" — arm down motion
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
await k.wait(0.12)
|
await k.wait(0.12)
|
||||||
@@ -3866,7 +3869,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GRAPPLE FLURRY: both bots close in and trade sustained blows without splitting apart
|
// GRAPPLE FLURRY: both bots close in and trade sustained blows without splitting apart
|
||||||
async function grappleFlurry(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function grappleFlurry(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Both rush to center — meet in the middle
|
// Both rush to center — meet in the middle
|
||||||
const meetX = (origAX + origDX) / 2
|
const meetX = (origAX + origDX) / 2
|
||||||
const atkMeetX = meetX - dir * 25
|
const atkMeetX = meetX - dir * 25
|
||||||
@@ -3928,7 +3931,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BODY SLAM: wrestling-style grab, lift, and slam
|
// BODY SLAM: wrestling-style grab, lift, and slam
|
||||||
async function bodySlam(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function bodySlam(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Rush in close
|
// Rush in close
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
const contactX = def.pos.x - dir * 30
|
const contactX = def.pos.x - dir * 30
|
||||||
@@ -3978,7 +3981,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PINBALL COMBO: bounce the opponent between walls/edges like a pinball
|
// PINBALL COMBO: bounce the opponent between walls/edges like a pinball
|
||||||
async function pinballCombo(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function pinballCombo(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Initial launch punch
|
// Initial launch punch
|
||||||
atk.play('attack')
|
atk.play('attack')
|
||||||
sfxPunch()
|
sfxPunch()
|
||||||
@@ -4026,7 +4029,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SUPLEX: grab from behind, lift, and drive headfirst into ground
|
// SUPLEX: grab from behind, lift, and drive headfirst into ground
|
||||||
async function suplex(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function suplex(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Dash behind defender
|
// Dash behind defender
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
const behindX = def.pos.x + dir * 30
|
const behindX = def.pos.x + dir * 30
|
||||||
@@ -4379,7 +4382,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
// === DEFENSE / SHIELD SYSTEM ===
|
// === DEFENSE / SHIELD SYSTEM ===
|
||||||
|
|
||||||
async function playBlock(side: 'a' | 'b') {
|
async function playBlock(side: 'a' | 'b') {
|
||||||
const defender = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0]
|
const defender = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
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
|
||||||
@@ -5246,7 +5249,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
// --- TIER 2+ ULTIMATES (Silver) — flashy multi-hits ---
|
// --- TIER 2+ ULTIMATES (Silver) — flashy multi-hits ---
|
||||||
async function ultimateRapidBarrage(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateRapidBarrage(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Rush in and land 8 alternating punches + kicks with screen shake
|
// Rush in and land 8 alternating punches + kicks with screen shake
|
||||||
const contactX = origDX - dir * 45
|
const contactX = origDX - dir * 45
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
@@ -5275,7 +5278,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateOrbitalStrike(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateOrbitalStrike(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Call down 5 orbital beams on the defender's position
|
// Call down 5 orbital beams on the defender's position
|
||||||
atk.play('special'); sfxSpecial()
|
atk.play('special'); sfxSpecial()
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
@@ -5291,7 +5294,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.3); atk.play('idle')
|
await k.wait(0.3); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateCloneSurround(atk: any, def: any, 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: any[] = []
|
||||||
const positions = [
|
const positions = [
|
||||||
@@ -5316,7 +5319,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.3)
|
await k.wait(0.3)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateGatlingFury(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateGatlingFury(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Pull out massive gatling gun, fire 12 shots with screen shake
|
// Pull out massive gatling gun, fire 12 shots with screen shake
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
const gunLen = 60
|
const gunLen = 60
|
||||||
@@ -5343,7 +5346,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.3); atk.play('idle')
|
await k.wait(0.3); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateEarthquake(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateEarthquake(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Jump high, slam ground, cracks + shockwaves across screen
|
// Jump high, slam ground, cracks + shockwaves across screen
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
await k.tween(atk.pos.y, GROUND_Y - 150, 0.2, (v) => { atk.pos.y = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.y, GROUND_Y - 150, 0.2, (v) => { atk.pos.y = v }, k.easings.easeOutQuad)
|
||||||
@@ -5373,7 +5376,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
const ultimateMeteorShower = makeSwarm('#ff6600', 8, 10, 500, true)
|
const ultimateMeteorShower = makeSwarm('#ff6600', 8, 10, 500, true)
|
||||||
|
|
||||||
// --- TIER 3+ ULTIMATES (Gold) — multi-phase, full screen ---
|
// --- TIER 3+ ULTIMATES (Gold) — multi-phase, full screen ---
|
||||||
async function ultimateScreenNuke(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateScreenNuke(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Charge up glowing orb, launch it, entire screen whites out
|
// Charge up glowing orb, launch it, entire screen whites out
|
||||||
atk.play('special'); sfxSpecial()
|
atk.play('special'); sfxSpecial()
|
||||||
const orb = k.add([k.circle(5), k.pos(atk.pos.x + dir * 20, atk.pos.y - 30), k.color(safeColor(k,'#ffffff')), k.opacity(0.8), k.z(18)])
|
const orb = k.add([k.circle(5), k.pos(atk.pos.x + dir * 20, atk.pos.y - 30), k.color(safeColor(k,'#ffffff')), k.opacity(0.8), k.z(18)])
|
||||||
@@ -5398,7 +5401,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.4); atk.play('idle')
|
await k.wait(0.4); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateBlackHoleVortex(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateBlackHoleVortex(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Summon black hole that pulls defender in, crushes, and ejects
|
// Summon black hole that pulls defender in, crushes, and ejects
|
||||||
atk.play('special'); sfxZap()
|
atk.play('special'); sfxZap()
|
||||||
const bh = k.add([k.circle(8), k.pos(W / 2, GROUND_Y - 50), k.anchor('center'), k.color(safeColor(k,'#220044')), k.opacity(0.9), k.z(20)])
|
const bh = k.add([k.circle(8), k.pos(W / 2, GROUND_Y - 50), k.anchor('center'), k.color(safeColor(k,'#220044')), k.opacity(0.9), k.z(20)])
|
||||||
@@ -5429,7 +5432,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.2); atk.play('idle')
|
await k.wait(0.2); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateDimensionalRift(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateDimensionalRift(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Open rift, attacker disappears, attacks from multiple portals
|
// Open rift, attacker disappears, attacks from multiple portals
|
||||||
sfxZap(); atk.opacity = 0
|
sfxZap(); atk.opacity = 0
|
||||||
const colors = ['#ff2d7b', '#00f0ff', '#39ff14', '#b83dff']
|
const colors = ['#ff2d7b', '#00f0ff', '#39ff14', '#b83dff']
|
||||||
@@ -5460,7 +5463,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateTimeSlow(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateTimeSlow(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Everything goes slow-mo, attacker lands 6 devastating hits in "frozen" time
|
// Everything goes slow-mo, attacker lands 6 devastating hits in "frozen" time
|
||||||
scanlineGlitch(0.1)
|
scanlineGlitch(0.1)
|
||||||
// Dim screen
|
// Dim screen
|
||||||
@@ -5486,7 +5489,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateSummonArmy(atk: any, def: any, 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: any[] = []
|
||||||
@@ -5516,7 +5519,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
const ultimateAvalancheDrop = makeExplosion(30, 30, '#aaeeff', 0.2, '#ffffff', true)
|
const ultimateAvalancheDrop = makeExplosion(30, 30, '#aaeeff', 0.2, '#ffffff', true)
|
||||||
|
|
||||||
// ULTIMATE BULLET TIME BARRAGE — full slow-mo cinematic: dodge rain of projectiles, afterimage rush, multi-hit counter
|
// ULTIMATE BULLET TIME BARRAGE — full slow-mo cinematic: dodge rain of projectiles, afterimage rush, multi-hit counter
|
||||||
async function ultimateBulletTimeBarrage(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateBulletTimeBarrage(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Phase 1: World goes dark, heavy slow-mo overlay
|
// Phase 1: World goes dark, heavy slow-mo overlay
|
||||||
const dim = k.add([k.rect(W, H), k.pos(0, 0), k.color(safeColor(k, '#000022')), k.opacity(0), k.z(25)])
|
const dim = k.add([k.rect(W, H), k.pos(0, 0), k.color(safeColor(k, '#000022')), k.opacity(0), k.z(25)])
|
||||||
await k.tween(0, 0.55, 0.2, (v) => { dim.opacity = v })
|
await k.tween(0, 0.55, 0.2, (v) => { dim.opacity = v })
|
||||||
@@ -5620,7 +5623,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- TIER 4+ ULTIMATES (Platinum) — full screen, multi-phase spectacles ---
|
// --- TIER 4+ ULTIMATES (Platinum) — full screen, multi-phase spectacles ---
|
||||||
async function ultimateFinaleRush(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateFinaleRush(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Full-screen dash through, reverse, dash again, then combo finish
|
// Full-screen dash through, reverse, dash again, then combo finish
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
// First pass
|
// First pass
|
||||||
@@ -5649,7 +5652,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateMeteorCrash(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateMeteorCrash(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Attacker launches into sky, becomes a meteor, crashes into arena
|
// Attacker launches into sky, becomes a meteor, crashes into arena
|
||||||
sfxZoomWhoosh(); atk.play('special')
|
sfxZoomWhoosh(); atk.play('special')
|
||||||
await k.tween(atk.pos.y, -100, 0.2, (v) => { atk.pos.y = v }, k.easings.easeInQuad)
|
await k.tween(atk.pos.y, -100, 0.2, (v) => { atk.pos.y = v }, k.easings.easeInQuad)
|
||||||
@@ -5682,7 +5685,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateScreenShatter(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateScreenShatter(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Hit so hard the "screen" shatters with fragment overlay
|
// Hit so hard the "screen" shatters with fragment overlay
|
||||||
const contactX = origDX - dir * 45
|
const contactX = origDX - dir * 45
|
||||||
await k.tween(atk.pos.x, contactX, 0.1, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
await k.tween(atk.pos.x, contactX, 0.1, (v) => { atk.pos.x = v }, k.easings.easeOutQuad)
|
||||||
@@ -5709,7 +5712,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateGravityReverse(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateGravityReverse(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Flip gravity — defender floats up, gets juggled, slammed back down
|
// Flip gravity — defender floats up, gets juggled, slammed back down
|
||||||
atk.play('special'); sfxZap()
|
atk.play('special'); sfxZap()
|
||||||
// Defender floats up
|
// Defender floats up
|
||||||
@@ -5733,7 +5736,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.3); atk.play('idle')
|
await k.wait(0.3); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateInfiniteCombo(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateInfiniteCombo(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// 15-hit combo with escalating screen effects
|
// 15-hit combo with escalating screen effects
|
||||||
const contactX = origDX - dir * 40
|
const contactX = origDX - dir * 40
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
@@ -5769,7 +5772,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
const ultimateRocketBarrage = makeSwarm('#ff6600', 10, 14, 550, false)
|
const ultimateRocketBarrage = makeSwarm('#ff6600', 10, 14, 550, false)
|
||||||
|
|
||||||
// --- TIER 5+ ULTIMATES (Diamond/Legend) — absolutely insane full-screen ---
|
// --- TIER 5+ ULTIMATES (Diamond/Legend) — absolutely insane full-screen ---
|
||||||
async function ultimateArmageddon(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateArmageddon(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Full screen goes dark, rain of fire, massive explosion, screen flash
|
// Full screen goes dark, rain of fire, massive explosion, screen flash
|
||||||
const darkness = k.add([k.rect(W, H), k.pos(0, 0), k.color(safeColor(k,'#000000')), k.opacity(0), k.z(25)])
|
const darkness = k.add([k.rect(W, H), k.pos(0, 0), k.color(safeColor(k,'#000000')), k.opacity(0), k.z(25)])
|
||||||
await k.tween(0, 0.7, 0.3, (v) => { darkness.opacity = v })
|
await k.tween(0, 0.7, 0.3, (v) => { darkness.opacity = v })
|
||||||
@@ -5797,7 +5800,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.5); atk.play('idle')
|
await k.wait(0.5); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateOmegaBeam(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateOmegaBeam(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Charge up, fire a massive full-width beam that covers half the screen
|
// Charge up, fire a massive full-width beam that covers half the screen
|
||||||
atk.play('special'); sfxSpecial()
|
atk.play('special'); sfxSpecial()
|
||||||
// Charge particles converging on attacker
|
// Charge particles converging on attacker
|
||||||
@@ -5829,7 +5832,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.2); atk.play('idle')
|
await k.wait(0.2); atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateBeyondTheScreen(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateBeyondTheScreen(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Attacker punches defender so hard they "break" off-screen, then gravity brings them back
|
// Attacker punches defender so hard they "break" off-screen, then gravity brings them back
|
||||||
const contactX = origDX - dir * 45
|
const contactX = origDX - dir * 45
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
@@ -5860,7 +5863,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateMatrixDodge(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateMatrixDodge(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Matrix-style slow-mo: attacker fires 3 projectiles, "dodges" in slo-mo, then counters
|
// Matrix-style slow-mo: attacker fires 3 projectiles, "dodges" in slo-mo, then counters
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
// Fire 3 slow projectiles
|
// Fire 3 slow projectiles
|
||||||
@@ -5891,7 +5894,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateWorldEnder(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateWorldEnder(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// The ultimate ultimate: 3-phase attack — air combo, ground slam, finishing beam
|
// The ultimate ultimate: 3-phase attack — air combo, ground slam, finishing beam
|
||||||
// Phase 1: Launch defender into air
|
// Phase 1: Launch defender into air
|
||||||
const contactX = origDX - dir * 45
|
const contactX = origDX - dir * 45
|
||||||
@@ -5945,7 +5948,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
// THE CREATOR — Exclusive choreographies (bitcoin-themed)
|
// THE CREATOR — Exclusive choreographies (bitcoin-themed)
|
||||||
// ═══════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════
|
||||||
|
|
||||||
async function bitcoinRain(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function bitcoinRain(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.2)
|
await k.wait(0.2)
|
||||||
@@ -5987,7 +5990,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function lightningInvoice(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function lightningInvoice(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxZap()
|
sfxZap()
|
||||||
await k.wait(0.15)
|
await k.wait(0.15)
|
||||||
@@ -6031,7 +6034,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function satoshiStrike(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function satoshiStrike(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
await k.wait(0.1)
|
await k.wait(0.1)
|
||||||
@@ -6069,7 +6072,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hashRateOverload(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function hashRateOverload(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
// Screen fills with scrolling hash text (simulated with falling green/gold rects)
|
// Screen fills with scrolling hash text (simulated with falling green/gold rects)
|
||||||
@@ -6105,7 +6108,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function blockchainSlam(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function blockchainSlam(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
const contactX = origDX - dir * 60
|
const contactX = origDX - dir * 60
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
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)
|
||||||
@@ -6144,7 +6147,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function creatorMode(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function creatorMode(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
// Typing effect — rapid small flashes from attacker
|
// Typing effect — rapid small flashes from attacker
|
||||||
@@ -6181,7 +6184,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function morphStrike(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function morphStrike(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Flash golden — the morph is handled by the morph system
|
// Flash golden — the morph is handled by the morph system
|
||||||
// This move itself is a quick dash-hit with gold sparks
|
// This move itself is a quick dash-hit with gold sparks
|
||||||
screenFlash('#ffd700', 0.08)
|
screenFlash('#ffd700', 0.08)
|
||||||
@@ -6205,7 +6208,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
atk.play('idle')
|
atk.play('idle')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function divineIntervention(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function divineIntervention(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Ascend off screen
|
// Ascend off screen
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
atk.play('win')
|
atk.play('win')
|
||||||
@@ -6250,7 +6253,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// ── Creator ₿ Swarm Variations ──
|
// ── Creator ₿ Swarm Variations ──
|
||||||
|
|
||||||
async function bitcoinSwarm(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function bitcoinSwarm(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Swarm of ₿ letters fly from creator toward defender with zigzag paths
|
// Swarm of ₿ letters fly from creator toward defender with zigzag paths
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -6300,7 +6303,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function satoshiTornado(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function satoshiTornado(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// ₿ letters spiral around the defender in a tightening vortex then explode
|
// ₿ letters spiral around the defender in a tightening vortex then explode
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -6360,7 +6363,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hodlWave(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function hodlWave(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Wall of ₿ letters advancing across the screen like a tidal wave
|
// Wall of ₿ letters advancing across the screen like a tidal wave
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -6414,7 +6417,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bitcoinBarrage(atk: any, def: any, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
async function bitcoinBarrage(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, isCritical: boolean) {
|
||||||
// Rapid-fire ₿ letters shot from the creator's laptop like bullets
|
// Rapid-fire ₿ letters shot from the creator's laptop like bullets
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -6462,7 +6465,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// ── Creator Ultimates ──
|
// ── Creator Ultimates ──
|
||||||
|
|
||||||
async function ultimateGenesisBlock(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateGenesisBlock(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Screen goes dark, massive genesis block materializes and detonates
|
// Screen goes dark, massive genesis block materializes and detonates
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -6495,7 +6498,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateTheHalving(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateTheHalving(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Screen splits horizontally with a golden line
|
// Screen splits horizontally with a golden line
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxZap()
|
sfxZap()
|
||||||
@@ -6528,7 +6531,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateFullNodeCrush(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateFullNodeCrush(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Giant server rack drops from above
|
// Giant server rack drops from above
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -6567,7 +6570,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
await k.tween(def.pos.x, origDX, 0.3, (v: number) => { def.pos.x = v }, k.easings.easeInOutQuad)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ultimateSatoshiVision(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
async function ultimateSatoshiVision(atk: Fighter, def: Fighter, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||||
// Everything turns gold — defender trapped in bitcoin block
|
// Everything turns gold — defender trapped in bitcoin block
|
||||||
atk.play('special')
|
atk.play('special')
|
||||||
sfxSpecial()
|
sfxSpecial()
|
||||||
@@ -7179,7 +7182,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showSpeechBubble(side: 'a' | 'b', text: string, duration: number = 3) {
|
function showSpeechBubble(side: 'a' | 'b', text: string, duration: number = 3) {
|
||||||
const fighter = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0]
|
const fighter = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
if (!fighter || !text) return
|
if (!fighter || !text) return
|
||||||
|
|
||||||
// Kill previous bubble on this side
|
// Kill previous bubble on this side
|
||||||
@@ -7332,7 +7335,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
function startTalking(side: 'a' | 'b') {
|
function startTalking(side: 'a' | 'b') {
|
||||||
stopTalking(side) // cleanup any existing
|
stopTalking(side) // cleanup any existing
|
||||||
const fighter = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0]
|
const fighter = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
if (!fighter?.exists()) return
|
if (!fighter?.exists()) return
|
||||||
|
|
||||||
const mouthColor = side === 'a' ? '#00f0ff' : '#ff2d7b'
|
const mouthColor = side === 'a' ? '#00f0ff' : '#ff2d7b'
|
||||||
@@ -7510,7 +7513,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
async function humanJumpsIn(side: 'a' | 'b') {
|
async function humanJumpsIn(side: 'a' | 'b') {
|
||||||
const fighterTag = side === 'a' ? 'fighterA' : 'fighterB'
|
const fighterTag = side === 'a' ? 'fighterA' : 'fighterB'
|
||||||
const spriteId = side === 'a' ? 'humanA' : 'humanB'
|
const spriteId = side === 'a' ? 'humanA' : 'humanB'
|
||||||
const fighter = k.get(fighterTag)[0]
|
const fighter = k.get(fighterTag)[0] as Fighter
|
||||||
if (!fighter) return
|
if (!fighter) return
|
||||||
|
|
||||||
// Spawn human at fighter position
|
// Spawn human at fighter position
|
||||||
@@ -8628,8 +8631,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playEntrance() {
|
async playEntrance() {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
|
|
||||||
const entrances = [
|
const entrances = [
|
||||||
@@ -9170,8 +9173,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Helper: reset both fighters to home positions
|
// Helper: reset both fighters to home positions
|
||||||
async _resetPositions() {
|
async _resetPositions() {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
k.tween(fA.pos.x, HOME_A, 0.2, (v) => { fA.pos.x = v }, k.easings.easeOutQuad),
|
k.tween(fA.pos.x, HOME_A, 0.2, (v) => { fA.pos.x = v }, k.easings.easeOutQuad),
|
||||||
@@ -9185,8 +9188,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 0: RAPID BRAWL — close distance, trade rapid blows
|
// 0: RAPID BRAWL — close distance, trade rapid blows
|
||||||
async _brawlRapid(winningSide: 'a' | 'b' | null, intensity: number) {
|
async _brawlRapid(winningSide: 'a' | 'b' | null, intensity: number) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const midX = W / 2
|
const midX = W / 2
|
||||||
sfxZoomWhoosh()
|
sfxZoomWhoosh()
|
||||||
@@ -9211,8 +9214,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 1: KNOCKDOWN BRAWL — combo ends with loser knocked flat, has to get back up
|
// 1: KNOCKDOWN BRAWL — combo ends with loser knocked flat, has to get back up
|
||||||
async _brawlKnockdown(winningSide: 'a' | 'b' | null, intensity: number) {
|
async _brawlKnockdown(winningSide: 'a' | 'b' | null, intensity: number) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
||||||
const loser = winner === fA ? fB : fA
|
const loser = winner === fA ? fB : fA
|
||||||
@@ -9261,8 +9264,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 2: WALL BOUNCE — punch sends fighter flying into screen edge, bounces off
|
// 2: WALL BOUNCE — punch sends fighter flying into screen edge, bounces off
|
||||||
async _brawlWallBounce(winningSide: 'a' | 'b' | null, intensity: number) {
|
async _brawlWallBounce(winningSide: 'a' | 'b' | null, intensity: number) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
||||||
const loser = winner === fA ? fB : fA
|
const loser = winner === fA ? fB : fA
|
||||||
@@ -9320,8 +9323,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 3: DIZZY STAGGER — sustained combo leaves loser wobbling around the screen
|
// 3: DIZZY STAGGER — sustained combo leaves loser wobbling around the screen
|
||||||
async _brawlDizzyStagger(winningSide: 'a' | 'b' | null, intensity: number) {
|
async _brawlDizzyStagger(winningSide: 'a' | 'b' | null, intensity: number) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
||||||
const loser = winner === fA ? fB : fA
|
const loser = winner === fA ? fB : fA
|
||||||
@@ -9372,8 +9375,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 4: SUPLEX SLAM — grab, lift overhead, slam to ground with bounce
|
// 4: SUPLEX SLAM — grab, lift overhead, slam to ground with bounce
|
||||||
async _brawlSuplex(attackerSide: 'a' | 'b', intensity: number) {
|
async _brawlSuplex(attackerSide: 'a' | 'b', intensity: number) {
|
||||||
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!attacker || !defender) return
|
if (!attacker || !defender) return
|
||||||
const dir = attackerSide === 'a' ? 1 : -1
|
const dir = attackerSide === 'a' ? 1 : -1
|
||||||
|
|
||||||
@@ -9423,8 +9426,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 5: PING PONG VOLLEY — fighters knock each other back and forth across the screen
|
// 5: PING PONG VOLLEY — fighters knock each other back and forth across the screen
|
||||||
async _brawlPingPong(winningSide: 'a' | 'b' | null, intensity: number) {
|
async _brawlPingPong(winningSide: 'a' | 'b' | null, intensity: number) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const volleys = 3 + Math.floor(intensity * 3) + Math.floor(Math.random() * 2) // 3-8
|
const volleys = 3 + Math.floor(intensity * 3) + Math.floor(Math.random() * 2) // 3-8
|
||||||
|
|
||||||
@@ -9471,8 +9474,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 6: GROUND AND POUND — attacker pins defender, hits them on the ground
|
// 6: GROUND AND POUND — attacker pins defender, hits them on the ground
|
||||||
async _brawlGroundPound(attackerSide: 'a' | 'b', intensity: number) {
|
async _brawlGroundPound(attackerSide: 'a' | 'b', intensity: number) {
|
||||||
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!attacker || !defender) return
|
if (!attacker || !defender) return
|
||||||
const dir = attackerSide === 'a' ? 1 : -1
|
const dir = attackerSide === 'a' ? 1 : -1
|
||||||
|
|
||||||
@@ -9515,8 +9518,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 7: HAYMAKER — slow dramatic wind-up, pause, devastating single hit with knockdown
|
// 7: HAYMAKER — slow dramatic wind-up, pause, devastating single hit with knockdown
|
||||||
async _brawlHaymaker(attackerSide: 'a' | 'b', _intensity: number) {
|
async _brawlHaymaker(attackerSide: 'a' | 'b', _intensity: number) {
|
||||||
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!attacker || !defender) return
|
if (!attacker || !defender) return
|
||||||
const dir = attackerSide === 'a' ? 1 : -1
|
const dir = attackerSide === 'a' ? 1 : -1
|
||||||
|
|
||||||
@@ -9563,8 +9566,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// 8: BODY CHECK BOUNCER — both charge, collide, one bounces off the other
|
// 8: BODY CHECK BOUNCER — both charge, collide, one bounces off the other
|
||||||
async _brawlBodyCheck(winningSide: 'a' | 'b' | null, _intensity: number) {
|
async _brawlBodyCheck(winningSide: 'a' | 'b' | null, _intensity: number) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) return
|
if (!fA || !fB) return
|
||||||
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
const winner = winningSide === 'a' ? fA : winningSide === 'b' ? fB : (Math.random() > 0.5 ? fA : fB)
|
||||||
const loser = winner === fA ? fB : fA
|
const loser = winner === fA ? fB : fA
|
||||||
@@ -9625,8 +9628,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Clinch combo: grapple at close range, one fighter dominates with sustained contact
|
// Clinch combo: grapple at close range, one fighter dominates with sustained contact
|
||||||
async _clinchCombo(attackerSide: 'a' | 'b', intensity: number) {
|
async _clinchCombo(attackerSide: 'a' | 'b', intensity: number) {
|
||||||
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const defender = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!attacker || !defender) return
|
if (!attacker || !defender) return
|
||||||
const dir = attackerSide === 'a' ? 1 : -1
|
const dir = attackerSide === 'a' ? 1 : -1
|
||||||
const origAX = attackerSide === 'a' ? HOME_A : HOME_B
|
const origAX = attackerSide === 'a' ? HOME_A : HOME_B
|
||||||
@@ -9664,8 +9667,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Counter-attack: after a choreography hit, the defender retaliates briefly
|
// Counter-attack: after a choreography hit, the defender retaliates briefly
|
||||||
async _counterAttack(defenderSide: 'a' | 'b') {
|
async _counterAttack(defenderSide: 'a' | 'b') {
|
||||||
const defender = k.get(defenderSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const defender = k.get(defenderSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const attacker = k.get(defenderSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const attacker = k.get(defenderSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!defender || !attacker) return
|
if (!defender || !attacker) return
|
||||||
const dir = defenderSide === 'a' ? 1 : -1
|
const dir = defenderSide === 'a' ? 1 : -1
|
||||||
const origDX = defenderSide === 'a' ? HOME_A : HOME_B
|
const origDX = defenderSide === 'a' ? HOME_A : HOME_B
|
||||||
@@ -9683,8 +9686,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playAttack(side: 'a' | 'b', choreographyName: string, isCritical: boolean) {
|
async playAttack(side: 'a' | 'b', choreographyName: string, isCritical: boolean) {
|
||||||
const attacker = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0]
|
const attacker = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const defender = k.get(side === 'a' ? 'fighterB' : 'fighterA')[0]
|
const defender = k.get(side === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!attacker || !defender) return
|
if (!attacker || !defender) return
|
||||||
|
|
||||||
const origAX = side === 'a' ? HOME_A : HOME_B
|
const origAX = side === 'a' ? HOME_A : HOME_B
|
||||||
@@ -9705,8 +9708,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playRound(event: RoundEvent) {
|
async playRound(event: RoundEvent) {
|
||||||
const fA = k.get('fighterA')[0]
|
const fA = k.get('fighterA')[0] as Fighter
|
||||||
const fB = k.get('fighterB')[0]
|
const fB = k.get('fighterB')[0] as Fighter
|
||||||
if (!fA || !fB) { await k.wait(0.5); return } // Sprites missing (mobile load failure) — skip animation
|
if (!fA || !fB) { await k.wait(0.5); return } // Sprites missing (mobile load failure) — skip animation
|
||||||
|
|
||||||
const aWon = event.winnerId === event.botAId
|
const aWon = event.winnerId === event.botAId
|
||||||
@@ -9932,7 +9935,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
: (isUltimate || (exchangeCritical && intensity > 0.7 && Math.random() < 0.4))
|
: (isUltimate || (exchangeCritical && intensity > 0.7 && Math.random() < 0.4))
|
||||||
let morphRevert: (() => Promise<void>) | null = null
|
let morphRevert: (() => Promise<void>) | null = null
|
||||||
if (shouldMorph) {
|
if (shouldMorph) {
|
||||||
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const attacker = k.get(attackerSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
if (attacker) {
|
if (attacker) {
|
||||||
if (isCreatorFighter) {
|
if (isCreatorFighter) {
|
||||||
// Omni-morph: creator transforms into a random archetype (sprite swap)
|
// Omni-morph: creator transforms into a random archetype (sprite swap)
|
||||||
@@ -10010,7 +10013,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
}
|
}
|
||||||
// RGB glitch + hyperspeed lines on critical final blow
|
// RGB glitch + hyperspeed lines on critical final blow
|
||||||
glitchRGB(0.3)
|
glitchRGB(0.3)
|
||||||
const defPos = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const defPos = k.get(attackerSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (defPos) hyperSpeedLines(defPos.pos.x, defPos.pos.y - 30, 0.4)
|
if (defPos) hyperSpeedLines(defPos.pos.x, defPos.pos.y - 30, 0.4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10246,8 +10249,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
|
|
||||||
// Respect nod between fighters on close rounds (10% when margin <= 1)
|
// Respect nod between fighters on close rounds (10% when margin <= 1)
|
||||||
if (margin <= 1 && Math.random() < 0.1) {
|
if (margin <= 1 && Math.random() < 0.1) {
|
||||||
const fA2 = k.get('fighterA')[0]
|
const fA2 = k.get('fighterA')[0] as Fighter
|
||||||
const fB2 = k.get('fighterB')[0]
|
const fB2 = k.get('fighterB')[0] as Fighter
|
||||||
if (fA2 && fB2) {
|
if (fA2 && fB2) {
|
||||||
spawnEmoteText(fA2.pos.x, fA2.pos.y - 45, respectLines[Math.floor(Math.random() * respectLines.length)], '#88ccff')
|
spawnEmoteText(fA2.pos.x, fA2.pos.y - 45, respectLines[Math.floor(Math.random() * respectLines.length)], '#88ccff')
|
||||||
await k.wait(0.3)
|
await k.wait(0.3)
|
||||||
@@ -10265,7 +10268,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playTaunt(side: 'a' | 'b') {
|
async playTaunt(side: 'a' | 'b') {
|
||||||
const taunter = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0]
|
const taunter = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
if (!taunter) return
|
if (!taunter) return
|
||||||
// Creator taunts come with menacing voice lines
|
// Creator taunts come with menacing voice lines
|
||||||
const taunterArch = side === 'a' ? botA.archetype : botB.archetype
|
const taunterArch = side === 'a' ? botA.archetype : botB.archetype
|
||||||
@@ -10307,7 +10310,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playDodge(side: 'a' | 'b') {
|
async playDodge(side: 'a' | 'b') {
|
||||||
const dodger = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0]
|
const dodger = k.get(side === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
if (!dodger) return
|
if (!dodger) return
|
||||||
const origX = side === 'a' ? HOME_A : HOME_B
|
const origX = side === 'a' ? HOME_A : HOME_B
|
||||||
const origY = dodger.pos.y
|
const origY = dodger.pos.y
|
||||||
@@ -10324,8 +10327,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playKO(winningSide: 'a' | 'b', winnerName: string) {
|
async playKO(winningSide: 'a' | 'b', winnerName: string) {
|
||||||
const loser = k.get(winningSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const loser = k.get(winningSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
const winner = k.get(winningSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const winner = k.get(winningSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
if (!loser || !winner) return
|
if (!loser || !winner) return
|
||||||
|
|
||||||
const dir = winningSide === 'a' ? 1 : -1
|
const dir = winningSide === 'a' ? 1 : -1
|
||||||
@@ -11183,8 +11186,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async playPerfect(winningSide: 'a' | 'b', winnerName: string) {
|
async playPerfect(winningSide: 'a' | 'b', winnerName: string) {
|
||||||
const winner = k.get(winningSide === 'a' ? 'fighterA' : 'fighterB')[0]
|
const winner = k.get(winningSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
|
||||||
const loser = k.get(winningSide === 'a' ? 'fighterB' : 'fighterA')[0]
|
const loser = k.get(winningSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
|
||||||
if (!loser || !winner) return
|
if (!loser || !winner) return
|
||||||
|
|
||||||
// Judge goes wild for a perfect
|
// Judge goes wild for a perfect
|
||||||
|
|||||||
Reference in New Issue
Block a user