Files
tribunal-app/src/lib/melon/constants.ts
T
Valentin ROBIN 35704e50fc
Build and deploy / deploy (push) Successful in 36s
Resserre les plafonds de score, ajoute une vérif par temps réel écoulé sur Icare, corrige le contraste de l'Urne
5000 restait trop haut pour être un vrai plafond réaliste : Icare abaissé à 500 (calculé sur le rythme le plus rapide théoriquement atteignable dans le jeu), Corne à 2000 (estimation plus prudente, économie de score plus dure à borner). Ajoute icarus_runs/start_icarus_run() : le serveur enregistre l'instant réel de début de partie et rejette un score incohérent avec le temps écoulé, sans jamais faire confiance à une durée envoyée par le client. Corrige aussi le nombre de jetons de /urne, peu lisible en gold-bright sur fond marbre (passe à text-sea, même convention qu'Icare/Corne).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 11:28:02 +02:00

86 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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;
// Doit rester aligné avec la contrainte melon_scores_best_score_check
// (supabase/schema.sql) — celle-ci est la vraie garantie, cette constante
// n'est qu'une valeur de référence côté client.
export const MAX_SCORE = 2000;
// Positions figées à la main pour certains Citoyens (id de profil → index
// de palier, 0 = le plus petit) — les Citoyens restants remplissent les
// paliers non réservés, dans leur ordre existant (alphabétique par
// pseudo, voir corne/page.tsx). "Position N" telle que demandée par un
// humain (compte à partir de 1) correspond à l'index N-1 ici.
export const TIER_OVERRIDES: Record<number, string> = {
6: "7f0f24dc-ede0-4190-a64d-976393073c28", // Épicure, position 7
};
export function applyTierOverrides<T extends { id: string }>(
citizens: T[],
overrides: Record<number, string>,
): T[] {
const byId = new Map(citizens.map((c) => [c.id, c]));
const usedIds = new Set<string>();
const result: (T | undefined)[] = new Array(citizens.length);
for (const [tierKey, id] of Object.entries(overrides)) {
const tier = Number(tierKey);
const citizen = byId.get(id);
if (citizen && tier >= 0 && tier < citizens.length) {
result[tier] = citizen;
usedIds.add(id);
}
}
const remaining = citizens.filter((c) => !usedIds.has(c.id));
let remainingIndex = 0;
for (let tier = 0; tier < result.length; tier++) {
if (!result[tier]) {
result[tier] = remaining[remainingIndex++];
}
}
return result as T[];
}