feat: 30 comedy sound effects, replace 75% round-end TTS with SFX
Comedy sounds (all procedural Web Audio, no samples): - Vine Boom, Air Horn (MLG), Bruh, Fart, Record Scratch - Rubber Chicken, Squeaky Toy, Wet Slap, Bone Crack, Cartoon Run - Rim Shot (ba dum tss), Slide Whistle Up/Down, Yippee, Ding - Taco Bell Bong, Windows Error, Meme Thud - Sad Trombone, Emotional Damage, Dun Dun Dunnnn - Fail Horn, Price Is Right Fail, Buzzer, Sad Violin - Mission Failed, Crickets Wiring: - Round-end narration: 75% comedy SFX, 25% TTS (was 100% TTS) - Critical rounds get Emotional Damage, draws get random fail - Showboat loop: 40% chance of comedy sound punctuation after each - KO finish: 50% chance of comedy fail sound on loser - sfxRandomSilly expanded with comedy sounds - New exports: sfxRandomComedy, sfxRandomFail Also adds bulletTimeAttack combat choreography + ultimateBulletTimeBarrage (tier 3+ ultimate) to fight system. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d3f026d356
commit
a59b0748a7
+134
-19
@@ -15,6 +15,13 @@ import {
|
||||
announceCreatorEntrance, announceCreatorRound, announceCreatorKO,
|
||||
announceCreatorWin, announceCreatorLose, announceCreatorMorph,
|
||||
announceCreatorCameo, announceCreatorTaunt, announceCreatorDevastating,
|
||||
sfxRandomComedy, sfxRandomFail,
|
||||
sfxVineBoom, sfxAirHorn, sfxBruh, sfxFart, sfxRecordScratch,
|
||||
sfxRubberChicken, sfxSqueakyToy, sfxWetSlap, sfxBoneCrack, sfxCartoonRun,
|
||||
sfxRimShot, sfxSlideWhistleUp, sfxSlideWhistleDown, sfxYippee, sfxDing,
|
||||
sfxTacoBellBong, sfxWindowsError, sfxMemeThud,
|
||||
sfxSadTrombone, sfxFailHorn, sfxPriceIsRightFail, sfxBuzzer,
|
||||
sfxMissionFailed, sfxCrickets, sfxSadViolin, sfxEmotionalDamage, sfxDunDunDun,
|
||||
} from './sounds'
|
||||
|
||||
export interface FightSceneConfig {
|
||||
@@ -80,7 +87,7 @@ const spriteAnims = {
|
||||
|
||||
// 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'], generic: ['dashPunch', 'multiHit', 'fullScreenDash', 'dashThrough', 'zoomRush', 'katanaCombo', 'cycloneKick', 'helicopterArms', 'breakdanceSweep'] },
|
||||
speed_blitz: { themed: ['afterimageDash', 'lightningRush', 'rapidFlurry', 'tornadoSpin', 'corkscrewDive', 'warpDrive', 'backflipKick', 'bulletTimeAttack'], 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'] },
|
||||
@@ -95,8 +102,8 @@ const CHALLENGE_THEMED: Record<string, { themed: string[]; generic: string[] }>
|
||||
magic_duel: { themed: ['kamehameha', 'spiritBomb', 'arcaneBarrage', 'darkVoid', 'holySmite', 'shadowClone', 'hexCurse'], 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'], generic: ['laserSword', 'plasmaSword', 'asteroidBelt', 'satelliteLaser', 'plasmaBeam', 'solarFlare'] },
|
||||
hack_battle: { themed: ['hackerAttack', 'bugSwarm', 'stackOverflow', 'segfault', 'blueScreen', 'malwareInject'], generic: ['fourOhFour', 'ctrlAltDelete', 'aiUprising', 'captchaTrap', 'popupSpam', 'dataStream', 'glitchBeam'] },
|
||||
space_war: { themed: ['blackHole', 'photonTorpedo', 'tractor_beam', 'warpDrive', 'alienAbduction', 'ionCannon', 'cosmicRay', 'bulletTimeAttack'], generic: ['laserSword', 'plasmaSword', 'asteroidBelt', 'satelliteLaser', 'plasmaBeam', 'solarFlare'] },
|
||||
hack_battle: { themed: ['hackerAttack', 'bugSwarm', 'stackOverflow', 'segfault', 'blueScreen', 'malwareInject', 'bulletTimeAttack'], generic: ['fourOhFour', 'ctrlAltDelete', 'aiUprising', 'captchaTrap', 'popupSpam', 'dataStream', 'glitchBeam'] },
|
||||
meme_war: { themed: ['selfieStrike', 'dabAttack', 'flossAttack', 'yeetThrow', 'tPoseAssert', 'emojiBarrage', 'memeBeam'], 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'] },
|
||||
@@ -108,13 +115,13 @@ const CHALLENGE_THEMED: Record<string, { themed: string[]; generic: string[] }>
|
||||
// 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'],
|
||||
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']
|
||||
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 {
|
||||
@@ -172,6 +179,7 @@ function pickChoreography(challengeType: string, isCritical: boolean, _round: nu
|
||||
'alienAbduction', 'tsunamiWave', 'earthquakeStrike', 'dragonBreath', 'pianoDrop',
|
||||
'fridgeDrop', 'volcanoErupt', 'warpDrive', 'dubstepCannon', 'tornadoSpin',
|
||||
'trampolineBounce', 'watermelonBomb', 'nftRugPull', 'blueScreen', 'yeetThrow',
|
||||
'bulletTimeAttack',
|
||||
]
|
||||
return critMoves[Math.floor(Math.random() * critMoves.length)]
|
||||
}
|
||||
@@ -5507,6 +5515,110 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
const ultimateSuperSlam = makeGrab('up', 250)
|
||||
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
|
||||
async function ultimateBulletTimeBarrage(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||
// 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)])
|
||||
await k.tween(0, 0.55, 0.2, (v) => { dim.opacity = v })
|
||||
scanlineGlitch(0.15)
|
||||
glitchRGB(0.1)
|
||||
|
||||
// Phase 2: Defender fires a barrage of slow-mo bullets
|
||||
def.play('special')
|
||||
for (let i = 0; i < 8; i++) {
|
||||
sfxGunshot()
|
||||
const bx = def.pos.x - dir * 20
|
||||
const by = def.pos.y - 35 + (Math.random() - 0.5) * 30
|
||||
const bullet = k.add([
|
||||
k.rect(12, 3),
|
||||
k.pos(bx, by),
|
||||
k.color(safeColor(k, '#ff4444')),
|
||||
k.opacity(0.85),
|
||||
k.z(26),
|
||||
k.anchor('center'),
|
||||
])
|
||||
// Bullet trace
|
||||
const trace = k.add([
|
||||
k.rect(60, 1),
|
||||
k.pos(bx + dir * 30, by),
|
||||
k.color(safeColor(k, '#ff6644')),
|
||||
k.opacity(0.4),
|
||||
k.z(25),
|
||||
])
|
||||
setTimeout(() => { if (trace.exists()) trace.destroy() }, 300)
|
||||
// Bullet travels slowly
|
||||
k.tween(bullet.pos.x, atk.pos.x + (Math.random() - 0.5) * 30, 0.3, (v) => { if (bullet.exists()) bullet.pos.x = v }).then(() => { if (bullet.exists()) bullet.destroy() })
|
||||
await k.wait(0.05)
|
||||
}
|
||||
|
||||
// Phase 3: Attacker dodges each bullet with slow-mo lean (afterimages cascade)
|
||||
atk.play('idle')
|
||||
for (let lean = 0; lean < 3; lean++) {
|
||||
const leanDir = lean % 2 === 0 ? -1 : 1
|
||||
const ghost = k.add([
|
||||
k.rect(FRAME_SIZE * Math.abs(atk.scale.x) * 0.7, FRAME_SIZE * Math.abs(atk.scale.y)),
|
||||
k.pos(atk.pos.x, atk.pos.y),
|
||||
k.color(safeColor(k, '#ffd700')),
|
||||
k.opacity(0.3),
|
||||
k.z(24),
|
||||
k.anchor('bot'),
|
||||
])
|
||||
setTimeout(() => { if (ghost.exists()) ghost.destroy() }, 350)
|
||||
await k.tween(atk.pos.y, GROUND_Y + leanDir * 15, 0.08, (v) => { atk.pos.y = v }, k.easings.easeOutQuad)
|
||||
await k.tween(atk.pos.y, GROUND_Y, 0.08, (v) => { atk.pos.y = v }, k.easings.easeInQuad)
|
||||
}
|
||||
await k.wait(0.1)
|
||||
|
||||
// Phase 4: Time RESUMES — golden flash, attacker blazes forward
|
||||
dim.destroy()
|
||||
screenFlash('#ffd700', 0.15)
|
||||
sfxZoomWhoosh()
|
||||
sfxCritical()
|
||||
|
||||
// Afterimage rush trail (8 ghosts across the screen)
|
||||
const contactX = origDX - dir * 40
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const gx = origAX + ((contactX - origAX) * i / 8)
|
||||
const ghost = k.add([
|
||||
k.rect(FRAME_SIZE * Math.abs(atk.scale.x) * 0.6, FRAME_SIZE * Math.abs(atk.scale.y) * 0.9),
|
||||
k.pos(gx, GROUND_Y),
|
||||
k.color(safeColor(k, '#ffd700')),
|
||||
k.opacity(0.35 - i * 0.04),
|
||||
k.z(9),
|
||||
k.anchor('bot'),
|
||||
])
|
||||
setTimeout(() => { if (ghost.exists()) ghost.destroy() }, 300)
|
||||
}
|
||||
await k.tween(atk.pos.x, contactX, 0.06, (v) => { atk.pos.x = v }, k.easings.easeInQuad)
|
||||
|
||||
// Phase 5: 8-hit slow-mo combo — each hit with sparks and screen shake
|
||||
for (let i = 0; i < 8; i++) {
|
||||
atk.play(i % 3 === 0 ? 'attack' : i % 3 === 1 ? 'kick' : 'special')
|
||||
sfxPunch()
|
||||
def.play('hit')
|
||||
k.shake(5 + i * 2)
|
||||
spawnSparks(def.pos.x + (Math.random() - 0.5) * 15, def.pos.y - 15 - Math.random() * 25, 5, i % 2 === 0 ? '#ffd700' : '#ffffff')
|
||||
await k.wait(0.04)
|
||||
}
|
||||
|
||||
// Phase 6: Final devastating blow — full screen effects
|
||||
sfxExplosion()
|
||||
k.shake(30)
|
||||
screenFlash('#ffd700', 0.25)
|
||||
def.play('knockback')
|
||||
spawnShockwave(def.pos.x, GROUND_Y, '#ffd700')
|
||||
spawnShockwave(def.pos.x + dir * 30, GROUND_Y, '#ffaa00')
|
||||
spawnSparks(def.pos.x, def.pos.y - 25, 25, '#ffd700')
|
||||
glitchRGB(0.3)
|
||||
|
||||
await k.wait(0.4)
|
||||
await Promise.all([
|
||||
k.tween(atk.pos.x, origAX, 0.25, (v) => { atk.pos.x = v }, k.easings.easeOutQuad),
|
||||
k.tween(def.pos.x, origDX, 0.3, (v) => { def.pos.x = v }, k.easings.easeOutQuad),
|
||||
])
|
||||
atk.play('idle')
|
||||
}
|
||||
|
||||
// --- TIER 4+ ULTIMATES (Platinum) — full screen, multi-phase spectacles ---
|
||||
async function ultimateFinaleRush(atk: any, def: any, dir: number, origAX: number, origDX: number, _isCrit: boolean) {
|
||||
// Full-screen dash through, reverse, dash again, then combo finish
|
||||
@@ -6506,7 +6618,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
pocketCannon, grappleFlurry, bodySlam, pinballCombo, suplex,
|
||||
// Themed
|
||||
golfClubSmash, fireBreath, riddleBarrage, cloneStrike,
|
||||
coinShower, penStab, mathAttack, trapCardAttack, afterimageDash,
|
||||
coinShower, penStab, mathAttack, trapCardAttack, afterimageDash, bulletTimeAttack,
|
||||
// --- NEW: Food throws ---
|
||||
pizzaSlam, bananaFling, pieSmash, hotdogWhip, watermelonBomb,
|
||||
sushiBarrage, burgerToss, iceCreamFling, tacoStorm, donutBarrage,
|
||||
@@ -6589,7 +6701,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
ultimateBouncingFury, ultimateTripleBeam, ultimateMegaThrow, ultimateTornadoGrab, ultimateMeteorShower,
|
||||
// Tier 3+
|
||||
ultimateScreenNuke, ultimateBlackHoleVortex, ultimateDimensionalRift, ultimateTimeSlow, ultimateSummonArmy,
|
||||
ultimateHellfireRain, ultimateCrystalCage, ultimateGigaDrill, ultimateSuperSlam, ultimateAvalancheDrop,
|
||||
ultimateHellfireRain, ultimateCrystalCage, ultimateGigaDrill, ultimateSuperSlam, ultimateAvalancheDrop, ultimateBulletTimeBarrage,
|
||||
// Tier 4+
|
||||
ultimateFinaleRush, ultimateMeteorCrash, ultimateScreenShatter, ultimateGravityReverse, ultimateInfiniteCombo,
|
||||
ultimatePlasmaStorm, ultimateFrozenCoffin, ultimateLavaGeyser, ultimateChainGrab, ultimateRocketBarrage,
|
||||
@@ -7531,7 +7643,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
await k.tween(judge.pos.y, judgeOrigY, 0.08, (v) => { judge.pos.y = v }, k.easings.easeInQuad)
|
||||
judge.play(d % 2 === 0 ? 'call_left' : 'call_right')
|
||||
}
|
||||
sfxRandomSilly()
|
||||
sfxRandomComedy()
|
||||
judge.play('idle')
|
||||
} else if (funnyAction === 2) {
|
||||
// Holds up a 10 score card
|
||||
@@ -7604,7 +7716,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
flag.onUpdate(() => {
|
||||
flag.pos.y = judge.pos.y - 30 + Math.sin(k.time() * 10) * 3
|
||||
})
|
||||
sfxRandomSilly()
|
||||
sfxRandomComedy()
|
||||
await k.wait(1.5)
|
||||
flag.destroy(); pole.destroy()
|
||||
} else {
|
||||
@@ -8478,6 +8590,8 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
const idx = Math.floor(Math.random() * pool.length)
|
||||
try {
|
||||
await pool[idx](f, homeX, homeY, sx, sy, dir)
|
||||
// 40% chance of comedy sound punctuation after each showboat
|
||||
if (Math.random() < 0.4) sfxRandomComedy()
|
||||
} catch { break }
|
||||
// Restore state after each showboat
|
||||
const ff = k.get(tag)[0]
|
||||
@@ -9386,7 +9500,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
if (!isLastExchange && Math.random() < 0.15) {
|
||||
await this.playDodge(attackerSide === 'a' ? 'b' : 'a')
|
||||
sfxDodge()
|
||||
sfxBoing()
|
||||
if (Math.random() < 0.4) sfxRandomFail(); else sfxBoing()
|
||||
} else {
|
||||
await this.playAttack(attackerSide, choreo, exchangeCritical)
|
||||
}
|
||||
@@ -9792,7 +9906,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
await k.tween(loser.pos.y, loserOrigY, 0.3, (v) => { loser.pos.y = v }, k.easings.easeInQuad)
|
||||
sfxSplat(); k.shake(15); screenFlash('#ffe135', 0.15)
|
||||
spawnShockwave(loser.pos.x, GROUND_Y, '#ffe135')
|
||||
winner.play('win'); sfxRandomSilly()
|
||||
winner.play('win'); sfxRandomComedy()
|
||||
} else if (finishStyle === 5) {
|
||||
// ANVIL DROP — cartoon anvil from the sky
|
||||
fatalityTagline = 'HEAVY METAL!'
|
||||
@@ -9821,7 +9935,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
await k.tween(winner.pos.x, contactX, 0.12, (v) => { winner.pos.x = v }, k.easings.easeOutQuad)
|
||||
for (let i = 0; i < 8; i++) {
|
||||
winner.play(i % 2 === 0 ? 'attack' : 'kick')
|
||||
sfxBoing(); sfxRandomSilly()
|
||||
sfxBoing(); sfxRandomComedy()
|
||||
loser.play('hit'); k.shake(3 + i)
|
||||
for (const p of chicken) { p.pos.x = loser.pos.x + (Math.random() - 0.5) * 10; p.pos.y = loser.pos.y - 30 + (Math.random() - 0.5) * 10 }
|
||||
spawnEmoteText(loser.pos.x + (Math.random() - 0.5) * 30, loser.pos.y - 40 - Math.random() * 20, ['SQUEAK!', 'BAWK!', 'HONK!', 'SQUAWK!'][i % 4], '#ffdd44')
|
||||
@@ -9915,7 +10029,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
await k.wait(0.3)
|
||||
for (let i = 0; i < 6; i++) {
|
||||
loser.play(i % 3 === 0 ? 'attack' : i % 3 === 1 ? 'kick' : 'special')
|
||||
sfxRandomSilly(); screenFlash(discoColors[i], 0.04)
|
||||
sfxRandomComedy(); screenFlash(discoColors[i], 0.04)
|
||||
await k.wait(0.1)
|
||||
}
|
||||
sfxExplosion(); k.shake(22); screenFlash('#ffffff', 0.2)
|
||||
@@ -10060,7 +10174,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
announceFast('Rev it up!')
|
||||
const mower = spawnWeaponProp('lawn_mower', winner.pos.x + dir * 10, GROUND_Y - 8, dir < 0, 16)
|
||||
winner.play('special'); sfxPowerUp()
|
||||
for (let i = 0; i < 3; i++) { sfxRandomSilly(); await k.wait(0.1) }
|
||||
for (let i = 0; i < 3; i++) { sfxRandomComedy(); await k.wait(0.1) }
|
||||
sfxZoomWhoosh()
|
||||
for (const p of mower) {
|
||||
k.tween(p.pos.x, loser.pos.x + dir * 40, 0.2, (v) => { p.pos.x = v }, k.easings.easeInQuad)
|
||||
@@ -10109,7 +10223,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
loser.opacity = Math.random() > 0.5 ? 1 : 0.2
|
||||
loser.pos.x += (Math.random() - 0.5) * 15
|
||||
loser.play(['idle', 'attack', 'kick', 'hit', 'special'][Math.floor(Math.random() * 5)])
|
||||
screenFlash('#00ff00', 0.02); sfxRandomSilly()
|
||||
screenFlash('#00ff00', 0.02); sfxRandomFail()
|
||||
await k.wait(0.06)
|
||||
}
|
||||
screenFlash('#0000ff', 0.3); sfxFail(); k.shake(20)
|
||||
@@ -10175,7 +10289,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
for (let i = 0; i < 30; i++) {
|
||||
spawnProp(winner.pos.x + dir * 30, winner.pos.y - 25, loser.pos.x + (Math.random() - 0.5) * 80, loserOrigY - 80 - Math.random() * 60, 4, 3, confettiColors[i % 8], 400 + Math.random() * 200)
|
||||
}
|
||||
sfxBoing(); sfxRandomSilly()
|
||||
sfxBoing(); sfxRandomComedy()
|
||||
loser.play('hit'); k.shake(10); await k.wait(0.15)
|
||||
loser.play('knockback'); sfxCritical(); k.shake(15)
|
||||
await k.tween(loser.pos.x, loser.pos.x + dir * 50, 0.2, (v) => { loser.pos.x = v }, k.easings.easeOutQuad)
|
||||
@@ -10343,7 +10457,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
sfxPowerUp()
|
||||
const moves = ['attack', 'kick', 'special', 'kick', 'attack', 'special'] as const
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
winner.play(moves[i]); sfxRandomSilly()
|
||||
winner.play(moves[i]); sfxRandomComedy()
|
||||
spawnSparks(winner.pos.x, winner.pos.y - 30, 3, ['#ff00ff', '#00ffff', '#ffff00'][i % 3])
|
||||
await k.wait(0.08)
|
||||
}
|
||||
@@ -10366,7 +10480,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
announceSilly('What is that SMELL!?')
|
||||
const cushion = k.add([k.circle(20), k.pos(loser.pos.x, GROUND_Y - 5), k.color(safeColor(k, '#ff6688')), k.z(10), k.opacity(0.8)])
|
||||
sfxBoing(); await k.wait(0.2)
|
||||
sfxWomp(); sfxRandomSilly(); k.shake(8)
|
||||
sfxWomp(); sfxRandomComedy(); k.shake(8)
|
||||
await k.tween(20, 40, 0.15, (v) => { cushion.radius = v }, k.easings.easeOutQuad)
|
||||
sfxExplosion()
|
||||
for (let i = 0; i < 12; i++) {
|
||||
@@ -10466,6 +10580,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
k.shake(18); sfxExplosion()
|
||||
await k.wait(0.3)
|
||||
loser.play('ko')
|
||||
if (Math.random() < 0.5) setTimeout(() => sfxRandomFail(), 200)
|
||||
await k.wait(0.4) // pause for visual impact before fatality voice
|
||||
const winnerArch = winningSide === 'a' ? botA.archetype : botB.archetype
|
||||
const loserArch = winningSide === 'a' ? botB.archetype : botA.archetype
|
||||
@@ -10635,7 +10750,7 @@ export async function createFightScene(config: FightSceneConfig) {
|
||||
18,
|
||||
['#ff2d7b', '#00f0ff', '#ffe14d', '#b83dff', '#39ff14', '#ff6600', '#ffffff', '#ff2d2d'][i]
|
||||
)
|
||||
if (i % 2 === 0) sfxRandomSilly()
|
||||
if (i % 2 === 0) sfxRandomComedy()
|
||||
}, i * 200)
|
||||
}
|
||||
await k.wait(0.7)
|
||||
|
||||
Reference in New Issue
Block a user