/* ────────────────────────────────────────────────────────────────
   Phase 6 Sprint 6.4 — Sports & Racing Templates
   
   Top-down Racing, Side-scroller Racing, Sports, Golf/Billiards, Rhythm
──────────────────────────────────────────────────────────────── */
const DEFAULT_TOP_DOWN_RACING_CONFIG = {
    maxSpeed: 15,
    acceleration: 8,
    braking: 12,
    friction: 3,
    turnSpeed: 3,
    driftFactor: 0.9,
    boostMultiplier: 1.5,
    boostDuration: 2,
    lapCount: 3,
    maxPlayers: 12,
    aiCount: 7,
    collisionBounce: 0.3,
};
function createRacerState(startPos, startAngle) {
    return {
        position: { ...startPos }, angle: startAngle, speed: 0,
        lateralSpeed: 0, lap: 0, checkpoint: 0,
        boostTimer: 0, finished: false, raceTime: 0,
    };
}
function updateRacer(state, input, config, dt) {
    // Boost
    if (input.boost && state.boostTimer <= 0)
        state.boostTimer = config.boostDuration;
    if (state.boostTimer > 0)
        state.boostTimer -= dt;
    const speedMult = state.boostTimer > 0 ? config.boostMultiplier : 1;
    // Acceleration
    if (input.throttle > 0) {
        state.speed += config.acceleration * input.throttle * dt;
    }
    if (input.brake) {
        state.speed -= config.braking * dt;
    }
    state.speed -= config.friction * dt;
    state.speed = Math.max(0, Math.min(config.maxSpeed * speedMult, state.speed));
    // Steering
    if (state.speed > 0.1) {
        state.angle += input.steer * config.turnSpeed * dt;
    }
    // Position
    state.position.x += Math.cos(state.angle) * state.speed * dt;
    state.position.y += Math.sin(state.angle) * state.speed * dt;
    state.raceTime += dt;
}
const DEFAULT_SIDE_SCROLLER_RACING_CONFIG = {
    scrollSpeed: 8,
    laneCount: 3,
    laneWidth: 60,
    playerSpeed: 10,
    obstacleFrequency: 1.5,
    coinFrequency: 3,
    powerUpFrequency: 0.2,
    difficultyScale: 0.1,
    maxLives: 3,
    iFrameDuration: 1,
};
const DEFAULT_SOCCER_CONFIG = {
    sport: 'soccer',
    fieldWidth: 120,
    fieldHeight: 80,
    teamSize: 5,
    matchDuration: 180,
    halves: 2,
    ballSpeed: 15,
    playerSpeed: 6,
    sprintMultiplier: 1.5,
    staminaDrain: 10,
    staminaRegen: 5,
    goalWidth: 8,
    shotPower: 20,
    passAccuracy: 0.85,
    aiDifficulty: 0.5,
};
const DEFAULT_BASKETBALL_CONFIG = {
    sport: 'basketball',
    fieldWidth: 28,
    fieldHeight: 15,
    teamSize: 5,
    matchDuration: 120,
    halves: 4,
    ballSpeed: 12,
    playerSpeed: 7,
    sprintMultiplier: 1.3,
    staminaDrain: 8,
    staminaRegen: 6,
    goalWidth: 1.2,
    shotPower: 15,
    passAccuracy: 0.9,
    aiDifficulty: 0.5,
};
function createSportsState(config) {
    return {
        score: [0, 0],
        currentHalf: 1,
        timeRemaining: config.matchDuration / config.halves,
        possession: 0,
        ballPosition: { x: config.fieldWidth / 2, y: config.fieldHeight / 2 },
        ballVelocity: { x: 0, y: 0 },
    };
}
const DEFAULT_GOLF_CONFIG = {
    maxPower: 20,
    ballFriction: 0.98,
    ballRestitution: 0.6,
    windEnabled: true,
    maxWind: 3,
    parPerHole: 4,
    holes: 9,
    strokeLimit: 10,
};
const DEFAULT_BILLIARDS_CONFIG = {
    tableWidth: 254,
    tableHeight: 127,
    ballRadius: 2.85,
    friction: 0.985,
    restitution: 0.9,
    maxPower: 30,
    pocketRadius: 5,
    gameType: '8ball',
};
function updateBilliardsBalls(balls, config, dt) {
    // Move
    for (const b of balls) {
        if (b.pocketed)
            continue;
        b.x += b.vx * dt;
        b.y += b.vy * dt;
        b.vx *= config.friction;
        b.vy *= config.friction;
        if (Math.abs(b.vx) < 0.01)
            b.vx = 0;
        if (Math.abs(b.vy) < 0.01)
            b.vy = 0;
    }
    // Wall bouncing
    const r = config.ballRadius;
    for (const b of balls) {
        if (b.pocketed)
            continue;
        if (b.x - r < 0) {
            b.x = r;
            b.vx = Math.abs(b.vx) * config.restitution;
        }
        if (b.x + r > config.tableWidth) {
            b.x = config.tableWidth - r;
            b.vx = -Math.abs(b.vx) * config.restitution;
        }
        if (b.y - r < 0) {
            b.y = r;
            b.vy = Math.abs(b.vy) * config.restitution;
        }
        if (b.y + r > config.tableHeight) {
            b.y = config.tableHeight - r;
            b.vy = -Math.abs(b.vy) * config.restitution;
        }
    }
    // Ball-ball collision
    for (let i = 0; i < balls.length; i++) {
        for (let j = i + 1; j < balls.length; j++) {
            const a = balls[i];
            const b = balls[j];
            if (a.pocketed || b.pocketed)
                continue;
            const dx = b.x - a.x;
            const dy = b.y - a.y;
            const dist = Math.sqrt(dx * dx + dy * dy);
            const minDist = r * 2;
            if (dist < minDist && dist > 0) {
                const nx = dx / dist;
                const ny = dy / dist;
                const dvx = a.vx - b.vx;
                const dvy = a.vy - b.vy;
                const impulse = (dvx * nx + dvy * ny) * config.restitution;
                a.vx -= impulse * nx;
                a.vy -= impulse * ny;
                b.vx += impulse * nx;
                b.vy += impulse * ny;
                // Separate
                const overlap = (minDist - dist) / 2;
                a.x -= nx * overlap;
                a.y -= ny * overlap;
                b.x += nx * overlap;
                b.y += ny * overlap;
            }
        }
    }
}
const DEFAULT_RHYTHM_CONFIG = {
    laneCount: 4,
    noteSpeed: 1.5,
    hitWindowPerfect: 0.05,
    hitWindowGreat: 0.1,
    hitWindowGood: 0.15,
    scorePerfect: 300,
    scoreGreat: 200,
    scoreGood: 100,
    comboMultiplierStep: 10,
    maxMultiplier: 4,
    healthDrain: 10,
    healthRegen: 2,
    healthMax: 100,
};
function createRhythmState(config) {
    return {
        score: 0, combo: 0, maxCombo: 0, multiplier: 1,
        health: config.healthMax, perfects: 0, greats: 0,
        goods: 0, misses: 0, songTime: 0,
    };
}
function judgeHit(noteTime, hitTime, config) {
    const diff = Math.abs(noteTime - hitTime);
    if (diff <= config.hitWindowPerfect)
        return 'perfect';
    if (diff <= config.hitWindowGreat)
        return 'great';
    if (diff <= config.hitWindowGood)
        return 'good';
    return 'miss';
}
function applyHitResult(state, result, config) {
    if (result === 'miss') {
        state.combo = 0;
        state.multiplier = 1;
        state.health = Math.max(0, state.health - config.healthDrain);
        state.misses++;
        return;
    }
    state.combo++;
    if (state.combo > state.maxCombo)
        state.maxCombo = state.combo;
    state.multiplier = Math.min(config.maxMultiplier, 1 + Math.floor(state.combo / config.comboMultiplierStep));
    state.health = Math.min(config.healthMax, state.health + config.healthRegen);
    let baseScore = 0;
    if (result === 'perfect') {
        baseScore = config.scorePerfect;
        state.perfects++;
    }
    else if (result === 'great') {
        baseScore = config.scoreGreat;
        state.greats++;
    }
    else {
        baseScore = config.scoreGood;
        state.goods++;
    }
    state.score += baseScore * state.multiplier;
}

export { DEFAULT_BASKETBALL_CONFIG, DEFAULT_BILLIARDS_CONFIG, DEFAULT_GOLF_CONFIG, DEFAULT_RHYTHM_CONFIG, DEFAULT_SIDE_SCROLLER_RACING_CONFIG, DEFAULT_SOCCER_CONFIG, DEFAULT_TOP_DOWN_RACING_CONFIG, applyHitResult, createRacerState, createRhythmState, createSportsState, judgeHit, updateBilliardsBalls, updateRacer };
//# sourceMappingURL=SportsTemplates.js.map
