refactor: extract fight types and constants
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
954b64ebe9
commit
020ab3f1f8
@@ -1,8 +1,10 @@
|
||||
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 type { Fighter, FightSceneConfig, RoundEvent } from './fight/types'
|
||||
export type { FightSceneConfig, RoundEvent } from './fight/types'
|
||||
import { ARENA_THEMES, spriteAnims, CHALLENGE_THEMED, TIER_ULTIMATES, CREATOR_MOVES, CREATOR_ULTIMATES } from './fight/constants'
|
||||
import {
|
||||
sfxPunch, sfxKick, sfxSpecial, sfxCritical, sfxGunshot, sfxBulletHit,
|
||||
sfxJetpack, sfxExplosion, sfxKO, sfxWin, sfxWinAnnounce, sfxPerfect,
|
||||
@@ -27,120 +29,12 @@ import {
|
||||
sfxMissionFailed, sfxCrickets, sfxSadViolin, sfxEmotionalDamage, sfxDunDunDun,
|
||||
} from './audio'
|
||||
|
||||
export interface FightSceneConfig {
|
||||
canvas: HTMLCanvasElement
|
||||
botA: { name: string; seed: string; tier: number; archetype?: string; customization?: SpriteCustomization; wins?: number; losses?: number }
|
||||
botB: { name: string; seed: string; tier: number; archetype?: string; customization?: SpriteCustomization; wins?: number; losses?: number }
|
||||
arena: string
|
||||
onReady?: () => void
|
||||
}
|
||||
|
||||
export interface RoundEvent {
|
||||
round: number
|
||||
challengeType: string
|
||||
winnerId: string | null
|
||||
botAId: string
|
||||
botBId: string
|
||||
narration: string
|
||||
isCritical: boolean
|
||||
botAScore: number
|
||||
botBScore: number
|
||||
botAResponse?: string
|
||||
botBResponse?: string
|
||||
}
|
||||
|
||||
const ARENA_THEMES: Record<string, { bg: string; ground: string; accent: string }> = {
|
||||
datacenter: { bg: '#0a0a1a', ground: '#1a1a3a', accent: '#00f0ff' },
|
||||
stackoverflow_ruins: { bg: '#1a0f00', ground: '#2a1f10', accent: '#f48024' },
|
||||
gpu_graveyard: { bg: '#0a0a0a', ground: '#1a1a1a', accent: '#76b900' },
|
||||
prompt_dungeon: { bg: '#0f0a1a', ground: '#1f1a2a', accent: '#b83dff' },
|
||||
silicon_valley_dojo: { bg: '#0a1a0a', ground: '#1a2a1a', accent: '#00ff41' },
|
||||
paper_mill: { bg: '#1a1a10', ground: '#2a2a20', accent: '#f0e68c' },
|
||||
localhost: { bg: '#000000', ground: '#111111', accent: '#00ff41' },
|
||||
the_cloud: { bg: '#0a0f1a', ground: '#1a1f2a', accent: '#4488ff' },
|
||||
hacker_news: { bg: '#1a0f00', ground: '#2a1f10', accent: '#ff6600' },
|
||||
the_singularity: { bg: '#1a0020', ground: '#2a0030', accent: '#ff00ff' },
|
||||
beach: { bg: '#0a2040', ground: '#c2a060', accent: '#ffcc00' },
|
||||
desert: { bg: '#1a1000', ground: '#a08040', accent: '#ff8800' },
|
||||
forest: { bg: '#051005', ground: '#1a3010', accent: '#44cc22' },
|
||||
jungle: { bg: '#081808', ground: '#1a3818', accent: '#33ff66' },
|
||||
outer_space: { bg: '#000008', ground: '#0a0a20', accent: '#8888ff' },
|
||||
colosseum: { bg: '#1a0f08', ground: '#3a2a18', accent: '#d4a030' },
|
||||
kitchen_stadium: { bg: '#1a0a0a', ground: '#2a1a10', accent: '#ff4400' },
|
||||
haunted_mansion: { bg: '#0a0510', ground: '#1a1020', accent: '#8844cc' },
|
||||
meme_dimension: { bg: '#1a0820', ground: '#2a1830', accent: '#ff44ff' },
|
||||
space_station: { bg: '#000010', ground: '#0a0a28', accent: '#44aaff' },
|
||||
junkyard: { bg: '#0f0f0a', ground: '#2a2a1a', accent: '#aa8833' },
|
||||
concert_hall: { bg: '#10001a', ground: '#20102a', accent: '#ff22aa' },
|
||||
savanna: { bg: '#1a1808', ground: '#8a7830', accent: '#dd9922' },
|
||||
server_room: { bg: '#000a05', ground: '#0a1a10', accent: '#00ff88' },
|
||||
sports_arena: { bg: '#0a0a1a', ground: '#1a2a4a', accent: '#ff3333' },
|
||||
bitcoin_mine: { bg: '#0a0808', ground: '#1a1210', accent: '#f7931a' },
|
||||
lightning_node: { bg: '#0a0018', ground: '#1a1030', accent: '#ffdd00' },
|
||||
satoshis_garage: { bg: '#0f0f0a', ground: '#1f1f14', accent: '#a0a080' },
|
||||
mempool: { bg: '#100a18', ground: '#201828', accent: '#cc44ff' },
|
||||
silk_road_ruins: { bg: '#0a0a0a', ground: '#181818', accent: '#338833' },
|
||||
block_height_tower: { bg: '#080810', ground: '#181828', accent: '#4488dd' },
|
||||
hodl_cave: { bg: '#080604', ground: '#1a1410', accent: '#f7931a' },
|
||||
whale_pool: { bg: '#001020', ground: '#0a2040', accent: '#0088ff' },
|
||||
node_farm: { bg: '#000a00', ground: '#0a1a0a', accent: '#22ff44' },
|
||||
genesis_block: { bg: '#0a0a00', ground: '#1a1a10', accent: '#ffd700' },
|
||||
pizza_day_parlor: { bg: '#1a0a00', ground: '#2a1a08', accent: '#ff6622' },
|
||||
difficulty_adjustment: { bg: '#100810', ground: '#201820', accent: '#ff4488' },
|
||||
fifty_one_percent: { bg: '#1a0000', ground: '#2a0808', accent: '#ff0000' },
|
||||
timechain_temple: { bg: '#08080a', ground: '#18182a', accent: '#88aaff' },
|
||||
nostr_relay_room: { bg: '#0a0014', ground: '#1a1028', accent: '#8844ff' },
|
||||
}
|
||||
|
||||
const spriteAnims = {
|
||||
idle: { from: 0, to: ANIMATIONS.idle.frames - 1, loop: true, speed: 6 },
|
||||
attack: { from: MAX_FRAMES, to: MAX_FRAMES + ANIMATIONS.attack.frames - 1, loop: false, speed: 12 },
|
||||
kick: { from: MAX_FRAMES * 2, to: MAX_FRAMES * 2 + ANIMATIONS.kick.frames - 1, loop: false, speed: 10 },
|
||||
special: { from: MAX_FRAMES * 3, to: MAX_FRAMES * 3 + ANIMATIONS.special.frames - 1, loop: false, speed: 8 },
|
||||
hit: { from: MAX_FRAMES * 4, to: MAX_FRAMES * 4 + ANIMATIONS.hit.frames - 1, loop: false, speed: 8 },
|
||||
knockback: { from: MAX_FRAMES * 5, to: MAX_FRAMES * 5 + ANIMATIONS.knockback.frames - 1, loop: false, speed: 8 },
|
||||
ko: { from: MAX_FRAMES * 6, to: MAX_FRAMES * 6 + ANIMATIONS.ko.frames - 1, loop: false, speed: 6 },
|
||||
win: { from: MAX_FRAMES * 7, to: MAX_FRAMES * 7 + ANIMATIONS.win.frames - 1, loop: true, speed: 6 },
|
||||
}
|
||||
|
||||
// Challenge type -> themed choreography (shown ~60% of the time) + generic fallbacks
|
||||
const CHALLENGE_THEMED: Record<string, { themed: string[]; generic: string[] }> = {
|
||||
speed_blitz: { themed: ['afterimageDash', 'lightningRush', 'rapidFlurry', 'tornadoSpin', 'corkscrewDive', 'warpDrive', 'backflipKick', 'bulletTimeAttack', 'lightningBolt', 'channelForceClose', 'segwitSlash', 'doubleSpend'], generic: ['dashPunch', 'multiHit', 'fullScreenDash', 'dashThrough', 'zoomRush', 'katanaCombo', 'cycloneKick', 'helicopterArms', 'breakdanceSweep'] },
|
||||
riddle: { themed: ['riddleBarrage', 'portalPunch', 'cloneStrike', 'mindBlast', 'hexCurse'], generic: ['teleportStrike', 'projectile', 'zoomRush', 'whipCrack', 'bombThrow', 'captchaTrap', 'glitchBeam'] },
|
||||
code_golf: { themed: ['golfClubSmash', 'golfCartDrive', 'stackOverflow', 'segfault', 'blueScreen'], generic: ['projectile', 'flyingKick', 'dashPunch', 'rapidFlurry', 'hammerSmash', 'bodySlam', 'homeRunSwing'] },
|
||||
roast_battle: { themed: ['fireBreath', 'dragonBreath', 'fireball', 'lavaSplash', 'heatVision', 'volcanoErupt'], generic: ['gunBurst', 'jetpackDive', 'multiHit', 'zoomRush', 'laserBeam', 'chainsawRev', 'rocketLauncher', 'dynamiteBlast'] },
|
||||
hallucination_check: { themed: ['cloneStrike', 'portalPunch', 'shadowClone', 'glitchBeam', 'voidRift'], generic: ['teleportStrike', 'dashThrough', 'riddleBarrage', 'rapidFlurry', 'sniperShot', 'darkVoid', 'memeBeam'] },
|
||||
token_economy: { themed: ['coinShower', 'bitcoinCrash', 'nftRugPull', 'vendingDrop', 'popupSpam', 'blockReward', 'utxoScatter', 'feeSpike', 'satPerByte', 'stackSatsBlast', 'rugPullYank', 'dustAttack', 'multisigStrike'], generic: ['dashPunch', 'flyingKick', 'uppercut', 'zoomRush', 'bombThrow', 'minigunSpray', 'pocketCannon'] },
|
||||
creative_writing: { themed: ['penStab', 'scrollBlast', 'bookStorm', 'enchantedArrow', 'newspaperRoll'], generic: ['jetpackDive', 'aerialSlam', 'projectile', 'rapidFlurry', 'katanaCombo', 'arcaneBarrage', 'memeBeam'] },
|
||||
math_blitz: { themed: ['mathAttack', 'dataStream', 'pixelBeam', 'hackerAttack', 'chainLightning', 'merkleRoot', 'difficultyRetarget', 'halvingStrike'], generic: ['multiHit', 'dashPunch', 'fullScreenDash', 'rapidFlurry', 'laserBeam', 'hammerSmash', 'pinballCombo'] },
|
||||
trap_card: { themed: ['trapCardAttack', 'captchaTrap', 'spiderWeb', 'flashBang', 'smokeBomb'], generic: ['gunBurst', 'teleportStrike', 'groundPound', 'zoomRush', 'sniperShot', 'rocketLauncher', 'minigunSpray'] },
|
||||
food_fight: { themed: ['pizzaSlam', 'bananaFling', 'pieSmash', 'hotdogWhip', 'watermelonBomb', 'sushiBarrage', 'burgerToss', 'tacoStorm', 'eggBombard', 'tomatoBarrage', 'sushiRoll', 'tacoSlam', 'iceCreamFreeze', 'wokToss', 'cakeSmash', 'curryBlast', 'lobsterClaw', 'steakSlap', 'watermelonBowl'], generic: ['popcornBlast', 'iceCreamFling', 'donutBarrage', 'cookieFling', 'nachoVolley', 'meatballStorm', 'candyBarrage'] },
|
||||
wrestling_match: { themed: ['piledriver', 'powerbomb', 'chokeslam', 'tombstone', 'rko', 'clothesline', 'suplex', 'germanSuplex'], generic: ['bodySlam', 'grappleFlurry', 'hurricanrana', 'moonsault', 'elbowDrop', 'spear', 'stunner'] },
|
||||
music_battle: { themed: ['bassDrop', 'guitarSolo', 'drumSolo', 'dubstepCannon', 'beatboxBlast', 'vinylScratch'], generic: ['airHornBlast', 'vuvuzelaBlast', 'sonicWave', 'bassDropBeam', 'karaokeAttack', 'airGuitar'] },
|
||||
magic_duel: { themed: ['kamehameha', 'spiritBomb', 'arcaneBarrage', 'darkVoid', 'holySmite', 'shadowClone', 'hexCurse', 'vortexPull', 'gravityCrush', 'darkPulse', 'prismBeam'], generic: ['fireball', 'iceLance', 'thunderStrike', 'portalPunch', 'crystalShards', 'scrollBlast', 'potionThrow'] },
|
||||
sports_showdown: { themed: ['homeRunSwing', 'slapShot', 'servingAce', 'fieldGoalKick', 'bodyCheck', 'soccerKick', 'basketballDunk'], generic: ['baseballBat', 'tennisRacket', 'hockeyStick', 'bowlingBallRoll', 'tennisBallVolley', 'dropKick'] },
|
||||
nature_clash: { themed: ['treeSmash', 'icebergDrop', 'lavaSplash', 'tornadoFling', 'tsunamiWave', 'avalanche', 'earthquakeStrike'], generic: ['vineWhip', 'sandstorm', 'thornBarrage', 'pollenCloud', 'windSlash', 'meteorStrike'] },
|
||||
space_war: { themed: ['blackHole', 'photonTorpedo', 'tractor_beam', 'warpDrive', 'alienAbduction', 'ionCannon', 'cosmicRay', 'bulletTimeAttack', 'novaBlast', 'photonCannon', 'sonicBoom', 'glacialRay'], generic: ['laserSword', 'plasmaSword', 'asteroidBelt', 'satelliteLaser', 'plasmaBeam', 'solarFlare'] },
|
||||
hack_battle: { themed: ['hackerAttack', 'bugSwarm', 'stackOverflow', 'segfault', 'blueScreen', 'malwareInject', 'bulletTimeAttack', 'hashSmash', 'miningRig', 'nodeSync', 'nonceGrind', 'proofOfWork'], generic: ['fourOhFour', 'ctrlAltDelete', 'aiUprising', 'captchaTrap', 'popupSpam', 'dataStream', 'glitchBeam'] },
|
||||
meme_war: { themed: ['selfieStrike', 'dabAttack', 'flossAttack', 'yeetThrow', 'tPoseAssert', 'emojiBarrage', 'memeBeam', 'selfieStun', 'vibeCheck', 'rubberDucky', 'confettiPop', 'touchGrassSmack'], generic: ['rubberChicken', 'fingerGuns', 'micDrop', 'ratioAttack', 'capThrow', 'touchGrass', 'noScope'] },
|
||||
animal_kingdom: { themed: ['sharkBite', 'bearSwipe', 'eagleDive', 'bullCharge', 'gorillaSlam', 'wolfPack', 'batSwarm'], generic: ['snakeLunge', 'scorpionSting', 'crabPinch', 'spiderWeb', 'beeSwarm', 'catScratch', 'dogPile', 'dolphinFlip'] },
|
||||
demolition: { themed: ['dynamiteBlast', 'c4Detonation', 'nukeStrike', 'grenadeBlast', 'rocketLauncher', 'volcanoErupt'], generic: ['fireworksBurst', 'partyPopper', 'pinataSmash', 'cherryBomb', 'confettiCannon', 'anvilDrop', 'pianoDrop'] },
|
||||
vehicle_mayhem: { themed: ['motorbikeCharge', 'carSmash', 'tankRoll', 'helicopterStrike', 'zamboniCrush', 'tractorPlow'], generic: ['shoppingCart', 'forkliftCharge', 'airplaneSwoop', 'rocketRide', 'unicycleRun', 'golfCartDrive', 'boatCannon'] },
|
||||
medieval_combat: { themed: ['swordSlash', 'katanaCombo', 'battleAxe', 'maceSwing', 'crossbowBolt', 'shieldBash', 'nunchakuSpin', 'tridentThrust', 'morningstar', 'scytheSlash', 'warhammerCrush'], generic: ['flailSwing', 'holyWater', 'dragonBreath', 'enchantedArrow', 'laserSword', 'scrollBlast'] },
|
||||
retro_mode: { themed: ['dashPunch', 'uppercut', 'rapidFlurry', 'flyingKick', 'multiHit', 'fullScreenDash', 'backflipKick', 'cycloneKick'], generic: ['bodySlam', 'grappleFlurry', 'suplex', 'katanaCombo', 'breakdanceSweep', 'corkscrewDive', 'pinballCombo'] },
|
||||
}
|
||||
|
||||
// Tier-gated ultimate move pools — cumulative (tier 3 bot can use tier 2+3 moves)
|
||||
const TIER_ULTIMATES: Record<number, string[]> = {
|
||||
2: ['ultimateRapidBarrage', 'ultimateOrbitalStrike', 'ultimateCloneSurround', 'ultimateGatlingFury', 'ultimateEarthquake', 'ultimateBouncingFury', 'ultimateTripleBeam', 'ultimateMegaThrow', 'ultimateTornadoGrab', 'ultimateMeteorShower'],
|
||||
3: ['ultimateScreenNuke', 'ultimateBlackHoleVortex', 'ultimateDimensionalRift', 'ultimateTimeSlow', 'ultimateSummonArmy', 'ultimateHellfireRain', 'ultimateCrystalCage', 'ultimateGigaDrill', 'ultimateSuperSlam', 'ultimateAvalancheDrop', 'ultimateBulletTimeBarrage'],
|
||||
4: ['ultimateFinaleRush', 'ultimateMeteorCrash', 'ultimateScreenShatter', 'ultimateGravityReverse', 'ultimateInfiniteCombo', 'ultimatePlasmaStorm', 'ultimateFrozenCoffin', 'ultimateLavaGeyser', 'ultimateChainGrab', 'ultimateRocketBarrage'],
|
||||
5: ['ultimateArmageddon', 'ultimateOmegaBeam', 'ultimateBeyondTheScreen', 'ultimateMatrixDodge', 'ultimateWorldEnder', 'ultimateAbsoluteZero', 'ultimateSolarCannon', 'ultimatePhantomStrike', 'ultimateGodPunch', 'ultimateVoidCollapse'],
|
||||
}
|
||||
|
||||
// The Creator — exclusive moves (50% ultimate chance, 100% on crits, custom pool)
|
||||
const CREATOR_MOVES = ['bitcoinRain', 'lightningInvoice', 'satoshiStrike', 'hashRateOverload', 'blockchainSlam', 'creatorMode', 'morphStrike', 'divineIntervention', 'bitcoinSwarm', 'satoshiTornado', 'hodlWave', 'bitcoinBarrage', 'bulletTimeAttack']
|
||||
const CREATOR_ULTIMATES = ['ultimateGenesisBlock', 'ultimateTheHalving', 'ultimateFullNodeCrush', 'ultimateSatoshiVision']
|
||||
|
||||
function pickChoreography(challengeType: string, isCritical: boolean, _round: number, attackerTier: number = 0, attackerArchetype?: string): string {
|
||||
// THE CREATOR: special move selection — 50% ultimate, 60% creator moves, rest normal
|
||||
@@ -10693,8 +10587,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
}
|
||||
|
||||
// Parse and animate each bot's combo moves
|
||||
const movesA = (event.botAResponse || '').split('|').map(s => s.trim()).filter(Boolean).slice(0, 3)
|
||||
const movesB = (event.botBResponse || '').split('|').map(s => s.trim()).filter(Boolean).slice(0, 3)
|
||||
const movesA = (event.botAResponse || '').split('|').map((s: string) => s.trim()).filter(Boolean).slice(0, 3)
|
||||
const movesB = (event.botBResponse || '').split('|').map((s: string) => s.trim()).filter(Boolean).slice(0, 3)
|
||||
|
||||
// Flash moves in parallel for both pads
|
||||
const animateCombo = async (side: 'a' | 'b', moves: string[]) => {
|
||||
|
||||
Reference in New Issue
Block a user