Ajoute La Corne d'Abondance, jeu de fusion façon Watermelon Game
Build and deploy / deploy (push) Successful in 35s

Nouveau mini-jeu à /corne : des avatars de Citoyens tombent dans un bac
(physique matter-js), deux avatars de même taille qui se touchent
fusionnent en un plus gros, jusqu'au débordement soutenu (5s de grâce,
pièces clignotantes pour prévenir) qui met fin à la partie. Même
mécanique de gloires que Le Vol d'Icare (record personnel, classement,
gel + attribution automatique au Tribunal via pg_cron).

Un palier de taille par Citoyen (pas les Archontes) plutôt qu'un nombre
arbitraire : citizens[tier] donne l'association palier → Citoyen, fixe
pour tout le monde (les Citoyens sont triés par pseudo côté serveur, un
ordre déterministe). Rendu en DOM pour réutiliser le composant Avatar
existant (étendu pour accepter une taille en pixels arbitraire) plutôt
que de redessiner des images sur canvas — position et rotation de chaque
pièce appliquées en impératif depuis la boucle physique, jamais via
setState.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Valentin ROBIN
2026-08-24 00:52:07 +02:00
parent c3a3577769
commit 3b64e1cdb5
13 changed files with 955 additions and 4 deletions
+45
View File
@@ -0,0 +1,45 @@
// Zone de jeu en pixels CSS fixes (pas de canvas redimensionné comme Icare
// — un conteneur DOM de taille fixe suffit et évite toute logique de
// "world scale").
export const CONTAINER_WIDTH = 300;
export const CONTAINER_HEIGHT = 420;
export const WALL_THICKNESS = 20;
// Un palier par Citoyen existant (chacun associé à une taille fixe pour
// toute la partie, voir melon-game.tsx) plutôt qu'un nombre de paliers
// arbitraire : le nombre de tailles suit donc le nombre de Citoyens, pas
// une constante fixe. Rayon minimal + croissance géométrique assez marquée
// (×1.25 par palier — un ×1.19 précédent rendait deux paliers consécutifs
// trop proches en taille : une fusion semblait "changer le visage d'une
// pièce déjà posée" plutôt que "remplacer deux petites pièces par une
// nettement plus grosse"), plafonnée à MAX_RADIUS pour rester dans
// CONTAINER_WIDTH même avec beaucoup de Citoyens.
const MIN_RADIUS = 15;
const RADIUS_GROWTH = 1.25;
const MAX_RADIUS = 110;
export function buildTierRadii(tierCount: number): number[] {
const count = Math.max(1, tierCount);
return Array.from({ length: count }, (_, index) =>
Math.min(MAX_RADIUS, Math.round(MIN_RADIUS * RADIUS_GROWTH ** index)),
);
}
// Palier de départ pour la pièce à lâcher : parmi les plus petits
// uniquement (sinon les premières fusions seraient trop rares/tardives).
export const DROP_MIN_TIER = 0;
export const DROP_MAX_TIER_CAP = 3;
export const DROP_COOLDOWN_MS = 450;
// Ligne de débordement (depuis le haut du bac) + délai de grâce avant game
// over : évite qu'un simple rebond juste après une fusion ne déclenche une
// fin de partie instantanée.
export const DANGER_LINE_Y = CONTAINER_HEIGHT * 0.15;
export const GAME_OVER_GRACE_MS = 5000;
export const FIXED_DT_MS = 1000 / 60;
export const MAX_FRAME_DT_MS = 250;
export const MIN_SCORE = 0;
export const MAX_SCORE = 1000000;