Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ scripts/ rapport d'équilibrage (npm run balance)

### Une arme
Ajoute une entrée dans `data/weapons.ts`. Choisis un `behavior.kind` existant (`projectile`, `beam`, `chain`, `pulse`, `strike`, `drone`, `flame`, `mine`) et ses paramètres (`count`, `spread`, `pierce`, `homing`, `explodeRadius`, `burn`, `slow`, `knockback`, `split`, `gravity`, `fireZone`, `autoTarget`…). Les `tags` comptent pour les synergies.
Bonus de niveau : `tech: { 2: {...}, 3: {...} }` (fusionnés dans `behavior` par `weaponBehavior(w)`) et `techNotes: ['Tech II', 'Tech III']` pour le texte affiché. Les dégâts et la cadence montent automatiquement via `TECH_MULTIPLIERS`.
Mesure ton arme avec `npm run bench:weapons` (DPS mono-cible, de foule, nombre de kills contre une nuée, part du temps sans surchauffe) et compare-la aux autres.
Pour un **nouveau type de tir**, ajoute la valeur dans `WeaponKind` (types.ts) et une routine dans `FIRE_HANDLERS` (WeaponSystem.ts).

### Un ennemi
Expand Down Expand Up @@ -100,9 +102,10 @@ Valeur dans `EnvEventType` (types.ts), définition dans `data/events.ts` (durée
- `addEnemy(state, type, dx, dy)` place un ennemi ;
- `run(state, secondes, { keys, mouse })` fait avancer la simulation ;
- `seedRandom(n)` rend l'aléatoire déterministe.
- `npm run bench:weapons` : banc d'essai des armes (Tech I et III) sur mannequins fixes ancrés et sur une nuée.
- `npm run balance` : le bot de `tests/bot.ts` joue N parties par vaisseau et affiche survie, vague, kills, dégâts, et les dégâts subis par source. Variables d'environnement : `BALANCE_RUNS`, `BALANCE_SECONDS`.

## Pièges connus

- Si Vite affiche « does not provide an export named … » après de grosses modifications, c'est un cache HMR périmé : redémarre `npm run dev`.
- Le HUD est conçu pour 1700×950 puis mis à l'échelle (`useHudScale`, `transform: scale`). La caméra dézoome sur petit écran (`viewScaleFor`).
- HUD (`components/HUD.tsx`) : données d'affichage calculées par des fonctions pures dans `components/hud/model.ts` (testées dans `tests/hud.test.ts`), briques visuelles dans `components/hud/widgets.tsx`. Disposition bureau dessinée pour 1280×720 puis agrandie (`hudLayout` : ×1.5 en 1080p, ×2 en 1440p) ; en dessous de ×0.9, disposition compacte (téléphone, petite fenêtre) qui laisse libres les coins des boutons tactiles. Polices : Orbitron (titres), Chakra Petch (`font-hud`, libellés), JetBrains Mono (`font-mono`, chiffres). Menus agrandis par paliers sur grand écran (classe `.ui-zoom`, `index.css`). La caméra dézoome sur petit écran (`viewScaleFor`).
26 changes: 19 additions & 7 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { triggerEvent } from './engine/EventSystem';
import { MainMenu } from './components/Menu/MainMenu';
import { TouchControls, isTouchDevice } from './components/TouchControls';
import { GameOverScreen } from './components/Menu/GameOverScreen';
import { PauseMenu } from './components/Menu/PauseMenu';
import { MetaSave, RunSummary, loadSave, writeSave, recordRun } from './engine/Meta';
import { calculateRuntimeStats, syncDefenseState, shipBaseStats } from './engine/StatsCalculator';

Expand Down Expand Up @@ -47,6 +48,18 @@ const App: React.FC = () => {
setUiState({ ...s });
};

/** Abandon depuis la pause : la partie est enregistrée comme une défaite. */
const [abandoned, setAbandoned] = useState(false);
const abandonRun = () => {
const s = engineState.current;
if (s.status !== 'paused') return;
s.status = 'gameover';
stopBGM();
setAbandoned(true);
finishRun(s);
setUiState({ ...s });
};

/** Fin de partie : enregistre records / déblocages. */
const finishRun = (s: GameState) => {
const { save: next, summary } = recordRun(saveRef.current, s);
Expand Down Expand Up @@ -82,6 +95,7 @@ const App: React.FC = () => {
freshState.autoFire = saveRef.current.settings.autoFire || isTouch;
freshState.autoAim = isTouch;
setRunSummary(null);
setAbandoned(false);
freshState.status = newStatus;
freshState.startTime = Date.now();
engineState.current = freshState;
Expand Down Expand Up @@ -312,7 +326,7 @@ const App: React.FC = () => {
<canvas ref={canvasRef} width={dimensions.width} height={dimensions.height} className="absolute inset-0" />

{uiState.isDebugMode && <DebugOverlay state={uiState} fps={fps} frameTime={frameTime} />}
{uiState.status !== 'menu' && uiState.status !== 'dev' && uiState.status !== 'lab' && <HUD state={uiState} />}
{uiState.status !== 'menu' && uiState.status !== 'dev' && uiState.status !== 'lab' && <HUD state={uiState} touch={isTouch} />}

{uiState.status === 'menu' && (
<MainMenu
Expand All @@ -335,6 +349,7 @@ const App: React.FC = () => {

{uiState.status === 'leveling' && (
<UpgradeMenu
state={uiState}
options={upgradeOptions}
onSelect={(u) => {
const s = engineState.current;
Expand All @@ -348,23 +363,20 @@ const App: React.FC = () => {
}
setUiState({ ...s });
}}
currentWeapons={uiState.activeWeapons}
/>
)}

{isTouch && uiState.status === 'playing' && <TouchControls onPause={togglePause} />}
{isTouch && uiState.status === 'playing' && <TouchControls onPause={togglePause} abilities={uiState.activeAbilities} />}

{uiState.status === 'paused' && (
<div onClick={togglePause} className="absolute inset-0 bg-black/60 backdrop-blur-sm flex flex-col items-center justify-center z-40 font-orbitron cursor-pointer">
<h2 className="text-[10vw] font-black text-white italic animate-pulse">SYSTEM_PAUSE</h2>
<p className="text-xs text-slate-400 uppercase tracking-[0.4em] mt-4">P / Échap / toucher pour reprendre</p>
</div>
<PauseMenu state={uiState} onResume={togglePause} onQuit={abandonRun} />
)}

{uiState.status === 'gameover' && (
<GameOverScreen
state={uiState}
summary={runSummary}
abandoned={abandoned}
onRetry={() => resetGame('playing', uiState.shipId)}
onMenu={() => resetGame('menu', uiState.shipId)}
/>
Expand Down
8 changes: 6 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
> puis la section « Journal » en bas pour savoir exactement où on s'est arrêté.

## Règles du propriétaire (Linkatplug)
- **Ne JAMAIS commit ni push** sans demande explicite : il veut un seul gros commit à la fin.
- **Ne JAMAIS commit ni push** sans demande explicite. Il préfère peu de commits, gros et propres.
- **Organisation multi-sessions** : la session « Space-InZader project review » est l'ORGANISATRICE GÉNÉRALE. Tout passe par elle : elle répartit le travail, intègre, vérifie et commite. Les autres sessions (ex. « Space InZader HUD refactor ») lui rendent compte en fin de tâche. Toutes travaillent dans le MÊME dossier `H:\Space InZader` : avant de modifier un fichier, vérifier qu'aucune autre session ne travaille dessus (le dire à l'organisatrice) ; ne pas changer de branche sans prévenir ; un onglet navigateur par session (`tabs_create`) car le panneau est partagé.
- Il parle français, réponses courtes et concrètes.
- Objectif : **continuer et finir le jeu**. Liberté sur les priorités.
- Exigences : code facile à étendre/modifier (piloté par les données), et **tests automatisés** (Vitest).
Expand Down Expand Up @@ -59,5 +60,8 @@
Astuce : sous Git Bash, les heredocs python longs avec apostrophes cassent parfois → écrire le script dans le scratchpad et l'exécuter.
- 2026-10-07 — 30 passifs (16 portés V1 : perforation, exécution, soin à l'élimination, cryo, multi-tir…, stats entières sans rendement dégressif `INTEGER_STATS`, chance → rareté `rarityWeight`). Événements data-driven (`data/events.ts` + `EVENT_HANDLERS` : météores qui blessent aussi les ennemis, trou noir qui mange l'XP, éruption solaire, tempête magnétique ; alerte → actif ; `playerModifiers` ; planification 40–70s ; bouton dev). Musique MP3 V1 dans `public/music` (playlist, repli procédural, M = muet, N = piste suivante, réglage sauvegardé). Mobile : `components/TouchControls.tsx` (joystick, dash/nova, pause), `state.autoAim`/`analogMove`, zoom caméra `viewScaleFor`, menus `justify-[safe_center]`. **166 tests verts**.
- 2026-10-07 — Bot d'équilibrage (`tests/bot.ts`, `npm run balance`, `vitest.balance.config.ts`) + suivi `damageBySource`/`lastHitBy` (« Détruit par » en fin de partie). Équilibrage : refroidissement 15→25, reprise après surchauffe à 50% (`OVERHEAT_RECOVERY`), Tireur/Sniper/Kamikaze adoucis, drones nerfés. Tailwind installé localement (v3, `tailwind.config.js`, `index.css`) au lieu du CDN, `base: './'`, workflow `.github/workflows/ci.yml` (tests + déploiement gh-pages). README + ARCHITECTURE réécrits (guides « ajouter du contenu »). HUD synchronisé à 30 Hz. **166 tests verts, build OK, vérifié en navigateur (dev + build prod + mobile)**.
- ÉTAT : jeu complet et jouable. RIEN N'EST COMMITÉ (attente de l'accord du propriétaire pour un seul gros commit ; `package-lock.json` doit être inclus pour `npm ci` en CI).
- 2026-10-07 — Tout fusionné dans main (PR #37), CI + déploiement gh-pages OK, site en ligne https://linkatplug.github.io/Space-InZader/.
- 2026-10-07 — Une AUTRE SESSION refait l'UI (HUD/menus, branche ui-lisibilite) : NE PAS toucher components/ ici. Cette session : branche `gameplay-equilibrage` — banc `npm run bench:weapons` (scripts/weapons.test.ts), harmonisation des 24 armes (chiffres, noms FR, descriptions), bonus de Tech par arme (`Weapon.tech`, `Weapon.techNotes`, `weaponBehavior()`), Onde de choc touche le bord des ennemis, mines 8s, tags passifs corrigés, rééquilibrage vaisseaux. 171 tests verts. À SIGNALER à la session UI : afficher `techNotes[level-1]` dans UpgradeMenu quand on améliore une arme.
- 2026-10-07 — **Refonte UI lisibilité** (branche `ui-lisibilite`, session séparée) : HUD réécrit (`components/hud/model.ts` pur + tests `tests/hud.test.ts`, `widgets.tsx`), échelle 1280×720 + disposition compacte mobile, barre de boss, alerte surchauffe centrale, keystones actives/inactives, synergies avec paliers + infobulles, menu pause avec build complet (+ abandon), level-up avec stats d'arme et progression de synergie (touches 1/2/3), polices Chakra Petch / JetBrains Mono. Preview sur port 5174 (config `dev-ui`).
- 2026-10-07 — Intégration (organisatrice) : travail UI + gameplay réunis sur `gameplay-equilibrage`. Constante unique `BOSS_WAVE_INTERVAL` / `isBossWave` (data/enemies.ts, utilisée par CoreEngine et le HUD). Arcs bouclier/chaleur autour du vaisseau (`drawPlayerGauges`, render/CombatRenderer.ts). **189 tests verts, build OK, vérifié en navigateur.** Commité sur `gameplay-equilibrage` (à fusionner dans main via PR → déclenche le déploiement).
- IDÉES SUITE : plus d'ennemis/boss, compétences actives supplémentaires (V2 n'en a que 2), écran d'options (volume), choix de keystone par vaisseau plus marqués, sprites/effets, traduction, `metadata.json` (reliquat AI Studio) à supprimer si inutile, nettoyage des branches copilot/* (demander).
Loading
Loading