Rend le boost du Vol d'Icare aléatoire et incontrôlable
Build and deploy / deploy (push) Successful in 35s
Build and deploy / deploy (push) Successful in 35s
Le nombre de charges (colonnes cassables) est désormais tiré entre 3 et 8 à chaque cueillette, au lieu d'un nombre fixe. Pendant que le bouclier est actif, Icare est immobile (gravité coupée, taps/barre d'espace sans effet) et le défilement accéléré (×1,7) : impossible d'agir sur la trajectoire jusqu'à épuisement des charges, où le contrôle reprend. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
BOOST_CHARGES,
|
||||
BOOST_DASH_DISTANCE,
|
||||
BOOST_DASH_MS,
|
||||
BOOST_MAX_CHARGES,
|
||||
BOOST_MAX_COLUMN_INTERVAL,
|
||||
BOOST_MIN_CHARGES,
|
||||
BOOST_MIN_COLUMN_INTERVAL,
|
||||
BOOST_RADIUS,
|
||||
BOOST_SPEED_MULTIPLIER,
|
||||
COLUMN_GAP,
|
||||
COLUMN_GAP_MARGIN,
|
||||
COLUMN_SPACING,
|
||||
@@ -68,6 +70,12 @@ function randomBoostInterval(): number {
|
||||
);
|
||||
}
|
||||
|
||||
// Nombre de colonnes cassables par ce boost, tiré à chaque cueillette
|
||||
// (tirage entier inclusif des deux bornes).
|
||||
function randomBoostCharges(): number {
|
||||
return BOOST_MIN_CHARGES + Math.floor(Math.random() * (BOOST_MAX_CHARGES - BOOST_MIN_CHARGES + 1));
|
||||
}
|
||||
|
||||
// Une aile, dessinée pointant vers -x ; side=-1 la reflète pour l'aile
|
||||
// opposée. scale(side,1) inverse déjà le sens de rotation pour le côté
|
||||
// reflété — passer le même wingAngle (sans le renverser à la main) aux deux
|
||||
@@ -342,17 +350,28 @@ export function IcarusGame({ onFinish }: { onFinish: (score: number) => void })
|
||||
flapRequestedRef.current = false;
|
||||
|
||||
while (accumulator >= FIXED_DT_MS) {
|
||||
if (shouldFlap) {
|
||||
icarusRef.current = { ...icarusRef.current, velocityY: FLAP_IMPULSE };
|
||||
lastFlapAtRef.current = now;
|
||||
// Tant que le bouclier est actif, Icare est immobile (pas de
|
||||
// gravité, les taps n'ont aucun effet) et le décor défile vite à
|
||||
// sa place — impossible de toucher au mouvement pendant cette
|
||||
// période.
|
||||
if (boostChargesRef.current > 0) {
|
||||
shouldFlap = false;
|
||||
} else {
|
||||
if (shouldFlap) {
|
||||
icarusRef.current = { ...icarusRef.current, velocityY: FLAP_IMPULSE };
|
||||
lastFlapAtRef.current = now;
|
||||
shouldFlap = false;
|
||||
}
|
||||
icarusRef.current = stepIcarus(icarusRef.current, FIXED_DT_MS);
|
||||
}
|
||||
icarusRef.current = stepIcarus(icarusRef.current, FIXED_DT_MS);
|
||||
|
||||
// Difficulté progressive : vitesse en hausse et écart resserré au
|
||||
// fil du score, jusqu'à un palier (voir difficultyProgress).
|
||||
const progress = difficultyProgress(scoreRef.current);
|
||||
const currentSpeed = FORWARD_SPEED * (1 + (SPEED_MAX_MULTIPLIER - 1) * progress);
|
||||
const baseSpeed = FORWARD_SPEED * (1 + (SPEED_MAX_MULTIPLIER - 1) * progress);
|
||||
// Défilement accéléré tant que le bouclier du boost est actif
|
||||
// (sans risque : une colonne touchée casse au lieu de tuer).
|
||||
const currentSpeed = boostChargesRef.current > 0 ? baseSpeed * BOOST_SPEED_MULTIPLIER : baseSpeed;
|
||||
const currentGap = COLUMN_GAP * (1 - (1 - GAP_MIN_MULTIPLIER) * progress);
|
||||
|
||||
for (const column of columnsRef.current) {
|
||||
@@ -399,12 +418,16 @@ export function IcarusGame({ onFinish }: { onFinish: (score: number) => void })
|
||||
const collectedIndex = boostsRef.current.findIndex((b) => collidesWithBoost(icarusRef.current.y, b));
|
||||
if (collectedIndex !== -1) {
|
||||
boostsRef.current.splice(collectedIndex, 1);
|
||||
// Active un bouclier de BOOST_CHARGES charges (voir plus bas) et
|
||||
// déclenche l'animation de bond en avant — la casse des
|
||||
// Active un bouclier d'un nombre aléatoire de charges (voir plus
|
||||
// bas) et déclenche l'animation de bond en avant — la casse des
|
||||
// colonnes ne se fait plus d'un coup à la cueillette, elle se
|
||||
// fait au contact, une par une, tant qu'il reste des charges.
|
||||
boostChargesRef.current = BOOST_CHARGES;
|
||||
// Icare se fige (vitesse verticale annulée, plus de gravité ni
|
||||
// de prise en compte des taps tant que dure le bouclier, voir
|
||||
// plus haut).
|
||||
boostChargesRef.current = randomBoostCharges();
|
||||
boostDashAtRef.current = now;
|
||||
icarusRef.current = { ...icarusRef.current, velocityY: 0 };
|
||||
}
|
||||
|
||||
// Colonnes réellement touchées (mortelles en temps normal). Tant
|
||||
@@ -437,7 +460,7 @@ export function IcarusGame({ onFinish }: { onFinish: (score: number) => void })
|
||||
}
|
||||
}
|
||||
|
||||
if (hasHitGround(icarusRef.current)) {
|
||||
if (boostChargesRef.current === 0 && hasHitGround(icarusRef.current)) {
|
||||
statusRef.current = "dead";
|
||||
setStatus("dead");
|
||||
onFinishRef.current(scoreRef.current);
|
||||
|
||||
Reference in New Issue
Block a user