refactor: extract pickChoreography to fight/choreography/picker.ts
Moves 103 lines of choreography selection logic out of FightScene.ts into a dedicated module. FightScene re-exports for backward compat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
38d7236be3
commit
b6033a90e5
@@ -0,0 +1,110 @@
|
||||
import { CHALLENGE_THEMED, TIER_ULTIMATES, CREATOR_MOVES, CREATOR_ULTIMATES } from '../constants'
|
||||
import {
|
||||
CREATOR_ULTIMATE_CHANCE, CREATOR_THEMED_MOVE_RATIO,
|
||||
CRIT_THEMED_RATIO, CHOREOGRAPHY_THEMED_RATIO, CHOREOGRAPHY_GENERIC_CUTOFF,
|
||||
TIER_ULTIMATE_CHANCES,
|
||||
} from '../config'
|
||||
|
||||
export function pickChoreography(challengeType: string, isCritical: boolean, _round: number, attackerTier: number = 0, attackerArchetype?: string): string {
|
||||
// THE CREATOR: special move selection
|
||||
if (attackerArchetype === 'the_creator') {
|
||||
const ultimateChance = isCritical ? 1.0 : CREATOR_ULTIMATE_CHANCE
|
||||
if (Math.random() < ultimateChance) {
|
||||
// Creator gets ALL ultimates + their 4 exclusive ones
|
||||
const all: string[] = [...CREATOR_ULTIMATES]
|
||||
for (let t = 2; t <= 5; t++) {
|
||||
if (TIER_ULTIMATES[t]) all.push(...TIER_ULTIMATES[t])
|
||||
}
|
||||
return all[Math.floor(Math.random() * all.length)]
|
||||
}
|
||||
if (Math.random() < CREATOR_THEMED_MOVE_RATIO) {
|
||||
return CREATOR_MOVES[Math.floor(Math.random() * CREATOR_MOVES.length)]
|
||||
}
|
||||
// Fall through to normal selection
|
||||
}
|
||||
|
||||
// Tier-gated ultimates: higher tier = higher chance of spectacular ultimate moves
|
||||
if (attackerTier >= 2) {
|
||||
const ultimateChance = TIER_ULTIMATE_CHANCES[attackerTier] ?? TIER_ULTIMATE_CHANCES[5]
|
||||
// On crits, double the chance
|
||||
const roll = isCritical ? ultimateChance * 2 : ultimateChance
|
||||
if (Math.random() < roll) {
|
||||
// Collect all ultimates available at this tier (cumulative — higher tiers get lower tier moves too)
|
||||
const available: string[] = []
|
||||
for (let t = 2; t <= attackerTier; t++) {
|
||||
if (TIER_ULTIMATES[t]) available.push(...TIER_ULTIMATES[t])
|
||||
}
|
||||
if (available.length > 0) {
|
||||
return available[Math.floor(Math.random() * available.length)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Critical hits always get BIG moves (but can still be themed)
|
||||
if (isCritical) {
|
||||
const entry = CHALLENGE_THEMED[challengeType]
|
||||
if (entry && Math.random() < CRIT_THEMED_RATIO) {
|
||||
return entry.themed[Math.floor(Math.random() * entry.themed.length)]
|
||||
}
|
||||
const critMoves = [
|
||||
'groundPound', 'jetpackDive', 'gunBurst', 'fullScreenDash', 'multiHit', 'fireBreath',
|
||||
'lightningRush', 'zoomRush', 'rapidFlurry', 'rocketLauncher', 'laserBeam', 'katanaCombo',
|
||||
'hammerSmash', 'sniperShot', 'minigunSpray', 'chainsawRev', 'carSmash', 'motorbikeCharge',
|
||||
'anvilDrop', 'pocketCannon', 'bodySlam', 'suplex', 'pinballCombo', 'grappleFlurry',
|
||||
// New epic crits
|
||||
'kamehameha', 'nukeStrike', 'tankRoll', 'helicopterStrike', 'chokeslam', 'tombstone',
|
||||
'rko', 'piledriver', 'powerbomb', 'meteorStrike', 'spiritBomb', 'blackHole',
|
||||
'alienAbduction', 'tsunamiWave', 'earthquakeStrike', 'dragonBreath', 'pianoDrop',
|
||||
'fridgeDrop', 'volcanoErupt', 'warpDrive', 'dubstepCannon', 'tornadoSpin',
|
||||
'trampolineBounce', 'watermelonBomb', 'nftRugPull', 'blueScreen', 'yeetThrow',
|
||||
'bulletTimeAttack',
|
||||
]
|
||||
return critMoves[Math.floor(Math.random() * critMoves.length)]
|
||||
}
|
||||
|
||||
const entry = CHALLENGE_THEMED[challengeType]
|
||||
if (!entry) {
|
||||
const fallback = ['dashPunch', 'flyingKick', 'aerialSlam', 'backflipKick', 'fingerGuns', 'rubberChicken', 'pizzaSlam', 'fryingPan', 'snowballFight']
|
||||
return fallback[Math.floor(Math.random() * fallback.length)]
|
||||
}
|
||||
|
||||
const roll = Math.random()
|
||||
if (roll < CHOREOGRAPHY_THEMED_RATIO) {
|
||||
return entry.themed[Math.floor(Math.random() * entry.themed.length)]
|
||||
} else if (roll < CHOREOGRAPHY_GENERIC_CUTOFF) {
|
||||
return entry.generic[Math.floor(Math.random() * entry.generic.length)]
|
||||
} else {
|
||||
const wild = [
|
||||
'jetpackDive', 'gunBurst', 'groundPound', 'teleportStrike', 'fullScreenDash', 'fireBreath',
|
||||
'golfClubSmash', 'zoomRush', 'rapidFlurry', 'swordSlash', 'hammerSmash', 'laserBeam',
|
||||
'rocketLauncher', 'bombThrow', 'minigunSpray', 'sniperShot', 'whipCrack', 'katanaCombo',
|
||||
'chainsawRev', 'motorbikeCharge', 'carSmash', 'boatCannon', 'anvilDrop', 'pocketCannon',
|
||||
'grappleFlurry', 'bodySlam', 'pinballCombo', 'suplex',
|
||||
// New wild cards — food
|
||||
'pizzaSlam', 'bananaFling', 'pieSmash', 'burgerToss', 'tomatoBarrage', 'fishSlap',
|
||||
// New wild cards — weapons
|
||||
'fryingPan', 'baseballBat', 'guitarSmash', 'umbrellaWhack', 'plungerSlam', 'wrenchSmash',
|
||||
// New wild cards — drops
|
||||
'pianoDrop', 'toiletDrop', 'tvDrop', 'chandelierDrop', 'micDrop',
|
||||
// New wild cards — vehicles
|
||||
'shoppingCart', 'zamboniCrush', 'unicycleRun', 'tankRoll',
|
||||
// New wild cards — spins/flips
|
||||
'tornadoSpin', 'backflipKick', 'corkscrewDive', 'barrelRoll', 'skateTrick',
|
||||
// New wild cards — beams
|
||||
'kamehameha', 'freezeRay', 'heatVision', 'rainbowBeam', 'glitchBeam',
|
||||
// New wild cards — silly
|
||||
'rubberChicken', 'selfieStrike', 'fingerGuns', 'yeetThrow', 'emojiBarrage', 'touchGrass',
|
||||
// New wild cards — wrestling
|
||||
'clothesline', 'rko', 'stunner', 'piledriver',
|
||||
// New wild cards — magic
|
||||
'fireball', 'portalPunch', 'crystalShards', 'spiritBomb',
|
||||
// New wild cards — tech
|
||||
'stackOverflow', 'blueScreen', 'aiUprising', 'popupSpam',
|
||||
// New wild cards — music
|
||||
'bassDrop', 'dubstepCannon', 'vinylScratch',
|
||||
// New wild cards — nature
|
||||
'avalanche', 'lavaSplash', 'tornadoFling',
|
||||
]
|
||||
return wild[Math.floor(Math.random() * wild.length)]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user