Affiche la deuxième pièce à venir dans La Corne d'Abondance
Build and deploy / deploy (push) Successful in 36s

Petit badge "Puis" à côté du score, taille d'affichage fixe (juste
informatif, pas de position à viser) — la file glisse d'un cran à chaque
lâcher : ce qui était annoncé en second devient la pièce à viser.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Valentin ROBIN
2026-08-24 00:58:03 +02:00
parent 493ae444b4
commit 4dd3dbef85
2 changed files with 29 additions and 3 deletions
+28 -2
View File
@@ -50,6 +50,9 @@ export function MelonGame({ citizens, onFinish }: { citizens: MemberRow[]; onFin
const [score, setScore] = useState(0);
const [pieces, setPieces] = useState<Piece[]>([]);
const [nextTier, setNextTier] = useState(() => randomTier(DROP_MIN_TIER, dropMaxTier));
// Palier annoncé après nextTier — juste informatif (pas de position à
// viser), pour voir venir deux coups à l'avance au lieu d'un seul.
const [secondTier, setSecondTier] = useState(() => randomTier(DROP_MIN_TIER, dropMaxTier));
const onFinishRef = useRef(onFinish);
useEffect(() => {
@@ -59,6 +62,7 @@ export function MelonGame({ citizens, onFinish }: { citizens: MemberRow[]; onFin
const statusRef = useRef<Status>("playing");
const scoreRef = useRef(0);
const nextTierRef = useRef(nextTier);
const secondTierRef = useRef(secondTier);
const dropXRef = useRef(CONTAINER_WIDTH / 2);
const engineRef = useRef<Matter.Engine | null>(null);
const bodiesRef = useRef(new Map<number, Matter.Body>());
@@ -121,8 +125,11 @@ export function MelonGame({ citizens, onFinish }: { citizens: MemberRow[]; onFin
scoreRef.current = 0;
statusRef.current = "playing";
const firstNextTier = randomTier(DROP_MIN_TIER, dropMaxTier);
const firstSecondTier = randomTier(DROP_MIN_TIER, dropMaxTier);
nextTierRef.current = firstNextTier;
secondTierRef.current = firstSecondTier;
setNextTier(firstNextTier);
setSecondTier(firstSecondTier);
setPieces([]);
setScore(0);
setStatus("playing");
@@ -146,9 +153,14 @@ export function MelonGame({ citizens, onFinish }: { citizens: MemberRow[]; onFin
const dropped = spawnPiece(tier, x, radius + 6);
setPieces((current) => [...current, dropped]);
const upcoming = randomTier(DROP_MIN_TIER, dropMaxTier);
// La file glisse d'un cran : ce qui était annoncé en second devient la
// pièce à viser, et un nouveau palier est tiré pour la deuxième place.
const upcoming = secondTierRef.current;
const newSecond = randomTier(DROP_MIN_TIER, dropMaxTier);
nextTierRef.current = upcoming;
secondTierRef.current = newSecond;
setNextTier(upcoming);
setSecondTier(newSecond);
setDropX(dropXRef.current);
}, [spawnPiece, setDropX, tierRadii, dropMaxTier]);
@@ -343,7 +355,21 @@ export function MelonGame({ citizens, onFinish }: { citizens: MemberRow[]; onFin
return (
<div className="flex flex-col items-center gap-3">
<p className="font-heading text-2xl text-gold-bright">{score}</p>
<div className="flex items-center justify-between" style={{ width: CONTAINER_WIDTH }}>
<p className="font-heading text-2xl text-gold-bright">{score}</p>
{/* Deuxième pièce annoncée (après celle à viser) : juste
informatif, taille d'affichage fixe quel que soit le vrai
rayon du palier — pas de position à viser pour celle-ci. */}
<div className="flex items-center gap-1.5 opacity-70">
<span className="font-heading text-[0.6rem] tracking-wide text-marble/50 uppercase">Puis</span>
<Avatar
pseudo={citizens[secondTier]?.pseudo ?? "?"}
avatarUrl={citizens[secondTier]?.avatar_url ?? null}
size={32}
/>
</div>
</div>
<div
ref={containerRef}