From a0316bf467d9c088894c550436cac8c15f42e032 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Fri, 17 Jul 2026 22:04:43 -0600 Subject: [PATCH] feat: add mini-turbo boost mechanic to drifting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Track drift charge duration while sliding and payout a speed boost when the drift is released cleanly (not stalled or reversed) - Boost strength scales with drift duration, capped at maximum charge - Added configurable mini-turbo parameters (min/max ms, boost ms, boost multiplier range) to superkart-rules.json - Increase drift visual rotation angle from 6° to 14° for more pronounced drift feedback - Reset charge on respawn, spin, and new drift start --- data/superkart-rules.json | 5 +++++ src/games/superkart/SuperKartGame.js | 3 +-- src/games/superkart/SuperKartLogic.js | 21 ++++++++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/data/superkart-rules.json b/data/superkart-rules.json index 23789ac..2fecb8f 100644 --- a/data/superkart-rules.json +++ b/data/superkart-rules.json @@ -25,6 +25,11 @@ "driftTurnMult": 1.6, "driftSlipRate": 240, "driftGripSpread": 170, + "miniTurboMinMs": 500, + "miniTurboMaxMs": 1800, + "miniTurboBoostMs": 650, + "miniTurboBoostMultMin": 1.1, + "miniTurboBoostMultMax": 1.4, "slipDecay": 3.2, "hopMs": 260, "hopCooldownMs": 160, diff --git a/src/games/superkart/SuperKartGame.js b/src/games/superkart/SuperKartGame.js index 73a35e0..c7b195c 100644 --- a/src/games/superkart/SuperKartGame.js +++ b/src/games/superkart/SuperKartGame.js @@ -713,8 +713,7 @@ export default class SuperKartGame extends Phaser.Scene { if (kart.spinMs > 0) { frame = Math.floor((this.raceState.timeMs / 60)) % KART_ROT_FRAMES; } else if (kart.drifting) { - frame = kart.driftDir > 0 ? 1 : KART_ROT_FRAMES - 1; - angle = kart.driftDir * 6; + angle = kart.driftDir * 14; } else if (inputs.steer !== 0 && hasLean) { frame = inputs.steer < 0 ? KART_ROT_FRAMES : KART_ROT_FRAMES + 1; } else if (inputs.steer !== 0) { diff --git a/src/games/superkart/SuperKartLogic.js b/src/games/superkart/SuperKartLogic.js index bb902de..eb3860c 100644 --- a/src/games/superkart/SuperKartLogic.js +++ b/src/games/superkart/SuperKartLogic.js @@ -91,6 +91,7 @@ function makeKart(index, racer, isPlayer, slot, physics) { airborne: false, drifting: false, driftDir: 0, + driftChargeMs: 0, // mini-turbo charge accrued while committed to the slide prevHop: false, prevItem: false, surface: SURFACE.ROAD, @@ -201,6 +202,7 @@ function respawnAtCheckpoint(state, kart) { kart.velAngle = kart.heading; kart.speed = 0; kart.drifting = false; + kart.driftChargeMs = 0; kart.airborne = false; kart.hopMs = 0; kart.offroadMs = 0; @@ -214,6 +216,7 @@ function startSpin(state, kart, cause) { kart.spinMs = state.physics.spinMs; kart.spinSpin = 0; kart.drifting = false; + kart.driftChargeMs = 0; const dropped = Math.min(kart.coins, state.physics.coinsLostOnHit); kart.coins -= dropped; emit(state, 'spin', { kart: kart.index, cause }); @@ -359,14 +362,30 @@ function stepKart(state, kart, inputs, dt) { if (inputs.hop && steer !== 0 && kart.speed > effTopSpeed(state, kart) * 0.3) { kart.drifting = true; kart.driftDir = Math.sign(steer); + kart.driftChargeMs = 0; emit(state, 'drift-start', { kart: kart.index }); } } } if (kart.drifting) { + kart.driftChargeMs += STEP_MS; const top = effTopSpeed(state, kart); - if (!inputs.hop || kart.speed < top * 0.25 || (steer !== 0 && Math.sign(steer) !== kart.driftDir)) { + const speedDropped = kart.speed < top * 0.25; + const steerReversed = steer !== 0 && Math.sign(steer) !== kart.driftDir; + if (!inputs.hop || speedDropped || steerReversed) { kart.drifting = false; + // Mini-turbo: only a clean release (hop let go, not a stall/reversal + // cutting the slide short) with enough committed charge pays out — + // stronger the longer the slide, capped at miniTurboMaxMs. + if (!speedDropped && !steerReversed && kart.driftChargeMs >= physics.miniTurboMinMs) { + const span = Math.max(1, physics.miniTurboMaxMs - physics.miniTurboMinMs); + const t = Math.min(1, (kart.driftChargeMs - physics.miniTurboMinMs) / span); + kart.boostMs = physics.miniTurboBoostMs; + kart.boostMult = physics.miniTurboBoostMultMin + + (physics.miniTurboBoostMultMax - physics.miniTurboBoostMultMin) * t; + emit(state, 'boost', { kart: kart.index, pad: false }); + } + kart.driftChargeMs = 0; emit(state, 'drift-end', { kart: kart.index }); } }