diff --git a/src/app/corne/melon-game.tsx b/src/app/corne/melon-game.tsx index b7055c6..4ddf6b0 100644 --- a/src/app/corne/melon-game.tsx +++ b/src/app/corne/melon-game.tsx @@ -84,7 +84,14 @@ export function MelonGame({ const pendingMergesRef = useRef([]); const mergingBodyIdsRef = useRef(new Set()); const dangerSinceRef = useRef(null); - const runStartedAtRef = useRef(Date.now()); + // Pas Date.now() directement en argument de useRef (impur pendant le + // rendu, react-hooks/purity) : la vraie valeur est posée dans l'effet de + // montage juste en dessous — resetGame() (rejouer après une partie) la + // réécrit de toute façon à chaque nouvelle partie. + const runStartedAtRef = useRef(0); + useEffect(() => { + runStartedAtRef.current = Date.now(); + }, []); const dropCountRef = useRef(0); const mergeCountRef = useRef(0); @@ -326,12 +333,16 @@ export function MelonGame({ // Renvoie les corps qui dépassent la ligne de danger (au repos, pas un // simple rebond) — utilisé à la fois pour déclencher/prolonger le - // délai de grâce et pour savoir quelles pièces doivent clignoter. + // délai de grâce et pour savoir quelles pièces doivent clignoter. Le + // cercle doit avoir ENTIÈREMENT franchi la ligne (son bord bas, + // position.y + radius) plutôt que simplement y toucher (son bord haut, + // position.y - radius) : une pièce qui dépasse juste du sommet ne doit + // pas encore compter. function overflowingBodyIds(): Set { const ids = new Set(); for (const [id, body] of bodiesRef.current) { const radius = body.circleRadius ?? 0; - if (body.position.y - radius < DANGER_LINE_Y && body.speed < 0.5) { + if (body.position.y + radius < DANGER_LINE_Y && body.speed < 0.5) { ids.add(id); } } @@ -528,27 +539,35 @@ export function MelonGame({ {/* Échelle de repère : ordre de fusion complet, du plus petit au plus grand — taille d'affichage proportionnelle au vrai rayon du palier (mais réduite et plafonnée) pour que la progression se - voie d'un coup d'œil, sans dépendre du nombre de Citoyens. Une - seule ligne défilante (pas de flex-wrap). items-center (pas - items-end) : aligner sur le bas avec des tailles très variables - donnait un décalage qui grandissait avec la taille (la flèche, - à décalage fixe, n'était plus au bon endroit pour les gros - ronds) — centrer chaque rond sur son propre milieu reste - symétrique quelle que soit sa taille. */} -
- {citizens.map((citizen, tier) => ( -
- - {tier < citizens.length - 1 && } -
- ))} + voie d'un coup d'œil, sans dépendre du nombre de Citoyens. + flex-wrap (pas de scroll horizontal comme avant) : tout le monde + doit être visible sans interaction, l'intérêt d'une échelle de + repère est justement de tout voir d'un coup plutôt que de devoir + faire défiler. items-center (pas items-end) : aligner sur le bas + avec des tailles très variables donnait un décalage qui + grandissait avec la taille (la flèche, à décalage fixe, n'était + plus au bon endroit pour les gros ronds) — centrer chaque rond + sur son propre milieu reste symétrique quelle que soit sa + taille. Largeur plafonnée à max-w-sm (pas CONTAINER_WIDTH comme + avant) : la page (corne-view.tsx) est bien plus large que le bac + de jeu, la contrainte à 300px n'avait pas lieu d'être et forçait + le scroll pour rien. */} +
+

+ Ordre de fusion +

+
+ {citizens.map((citizen, tier) => ( +
+ + {tier < citizens.length - 1 && } +
+ ))} +
);