# NumPlay 1.10.0 — version Calcade NumPlay (le lanceur de mini-jeux de Mason Chen / Mason363, licence GNU GPL v3) reçoit l'habillage commun des jeux Calcade : l'intro animée du studio, l'écran de mise à jour obligatoire tous les 20 lancements et la sauvegarde `numplay.sys` au format CLC2 du site **https://jeux.w-production.fr/**. Depuis le 19 octobre 2026, ses **19 jeux sont en règle** (voir la section suivante). ## Mise en règle des jeux (19 octobre 2026) Chaque jeu a été vérifié : la licence de son code, l'origine de ses images, polices, niveaux et textes, et les noms ou marques qu'il montre. Les jeux de mécanique commune (empiler des pièces, un labyrinthe à points, un serpent, un casse-briques...) restent ; ce qui appartient à d'autres (noms, logos, personnages, images, niveaux, textes, code non libre) est refait. ### Refaits de fond en comble (6 jeux) Aucun jeu n'est retiré : ceux qui reprenaient le contenu d'un jeu du commerce sont refaits jusqu'à ce qu'il n'en reste rien (images, niveaux, textes, polices, code), en gardant la façon de jouer. | Avant | Maintenant | Ce qui venait du jeu d'origine | Ce qui l'a remplacé | |---|---|---|---| | NumDash | **NumDash** | les 12 niveaux officiels de Geometry Dash (RobTop) et leurs noms, le titre, les astuces, l'aspect du jeu, des pentes portées de gd3ds, les numéros d'objets de GD | 12 niveaux neufs prouvés jouables (`tools/conformite/numdash_niveaux.py`, `tools/solve.c`), dessins (`tools/sprites_nd.py`), moteur et physique réécrits avec nos valeurs, numérotation et éditeur à NumDash, police Archivo (OFL) | | NumBlocks | **NumBlocks** | textures, police, écrans, peaux et sons de Minecraft 1.8.8 (`client.jar`), noms des blocs, générateur et comportements calqués sur le code décompilé | 391 images dessinées par script (`tools/conformite/numblocks_art.py`), générateur de monde, physique, créatures, feu, liquides, recettes, menus et textes réécrits avec nos règles et nos chiffres | | NumDrive | **NumDrive** | niveaux, modèles 3D, scripts et palette extraits du fichier de jeu de Drive Mad (Fancade), police de Fancade | moteur physique 2D neuf, 7 voitures, 157 circuits générés et tous vérifiés finissables (`tools/conformite/numdrive_levels.py`), police Poppins (OFL) | | Balatro | **Jokeria** | 385 images du jeu, police m6x11, noms et textes des cartes, code Lua et shaders traduits en C | règles écrites pour Jokeria, 26 Jokers inventés, images et police dessinées par script (`tools/conformite/jokeria_art.py`, `jokeria_font.py`) | | Buckshot Roulette | **Chevrotine** | 370 images rendues depuis les scènes 3D du jeu, le croupier, ses répliques, les polices Fake Receipt et Dot Matrix | une fête foraine de nuit face à l'Automate, objets et règles à nous, images dessinées par script (`tools/conformite/chevrotine_art.py`), polices Nunito et Archivo (OFL) | | Portal Returns | **Warp Lab** | les 78 salles, images, police et textes du jeu TI-84 de MateoConLechuga (sans licence), l'univers de Portal (Valve), code traduit de l'assembleur | 32 salles neuves toutes résolues par `make test`, moteur, robot Pip, labo et police à nous (`tools/conformite/warplab_*.py`), passages rose et menthe | Les sauvegardes gardent leur nom de fichier (sauf NumDrive : `numdrive.sav` au lieu de `drivemad.sav`) ; une ancienne progression d'un jeu refait n'est pas reprise, le jeu repart proprement. `numplay.sys` ne change pas. Les grands jeux autonomes du dépôt d'origine (Celeste, Hollow Knight, Champion Island, The Final Earth 2, NumPowder), qui n'étaient ni dans NumPlay ni sur le site, restent hors des sources de cette version. ### Renommés et redessinés | Avant | Maintenant | Ce qui a changé | |---|---|---| | Crossy Road | **Piou Road** | nom et logo (droit, sur une ligne), police à lui (`tools/conformite/piou_font.py`) à la place des chiffres « décodés du jeu », couleurs décalées, le poulet devient un poussin jaune | | Block Blast | **Block Boom** | nom, couleurs des blocs | | Tetris | **Stackfall** | nom (menu, appli), blocs en galets et couleurs à lui (plus celles du « Tetris Guideline »), icône sans le mot TETRIS (`tools/conformite/stackfall_art.py`) | | Flappy Bird | **Flappix** | nom, un oiseau à lui (`tools/conformite/flappix_sprite.py`), colonnes de terre cuite au lieu des tuyaux verts, sol turquoise | | Pac-Man | **Munchy Maze** | nom et logo MUNCHY, héros carré menthe avec un œil, monstres à antennes et à un œil, noms des monstres (Rocko, Mimi, Zigzag, Boubou), murs violets, une étoile au lieu du vaisseau de Galaxian, sans les intermèdes de l'arcade | | Connect Four / Puissance 4 | **Line Up 4 / Aligne 4** | nom et titre, grille violette, jetons corail et menthe | | Block Breaker | **Brick Breaker / Casse-briques** | nom, couleurs, premier mur | ### Mêmes noms, aspect à eux | Jeu | Ce qui a changé | |---|---| | Snake | couleurs (plus celles du Snake de Google), noms de modes (Trous, Échange, Saut, Miroir, Pierre) | | Solitaire | dos de carte en treillis au lieu du soleil bleu et de l'anneau aux quatre couleurs de Google, bleu à lui | | Échecs | couleurs de l'échiquier et des menus (plus le thème vert de Chess.com) | | Démineur | couleurs (plus le gris et le bureau sarcelle de Windows 95), visage menthe à antenne, texte « comme Windows » retiré | | 2048 | inchangé : le jeu et son aspect sont sous licence MIT (Gabriele Cirulli) : la notice est ajoutée (`games/g2048/LICENSE-2048.txt`) | | NumVisuals | inchangé (tout est à NumPlay) | ### Licences ajoutées - `games/g2048/LICENSE-2048.txt` : MIT de Gabriele Cirulli. - `games/tetris/numworks_utils/LICENSE-storage.txt` : BSD 3-Clause du code de stockage de numworks-extapp-storage. - `LICENSES/OFL-1.1-Archivo-Caveat.txt` : les polices de l'intro Calcade. - `games/numdrive/LICENSES/OFL-1.1-Poppins.txt` : la police de NumDrive. - Retirées car plus utilisées : PixelMplus, Rammetto One (NumDash), m6x11 (Balatro), Fake Receipt et Dot Matrix (Buckshot Roulette), Baloo 2 (NumDrive). Les images d'aperçu (carrousel, vignettes du site, README) sont refaites à partir des jeux modifiés (`tools/conformite/captures.py`, `tools/conformite/icones.py`). Les anciennes captures, qui montraient les anciens noms et l'aspect des jeux d'origine, sont supprimées. ## Les deux versions | Fichier | Jeux | Taille du .nwa | Installé | |---|---|---:|---:| | `NumPlay.nwa` (`NumPlay-complet.nwa`) | les 19 jeux | 1 393 668 o | 1 164 664 o | | `NumPlay-leger.nwa` | les mêmes 19 jeux | 1 393 668 o | 1 164 664 o | Refaits, les 6 gros jeux sont beaucoup plus légers : les 19 jeux tiennent ensemble en moins de 1,2 Mo installés, donc la version légère n'a plus besoin d'enlever de jeux et les deux fichiers sont identiques (le site propose toujours les deux noms). Les listes sont dans `tools/calcade_build.sh` (`FULL` et `LITE`). ## Nom de l'appli : Calcade (20 octobre 2026) Sur la calculatrice, l'appli s'appelle maintenant **Calcade** et porte le logo Calcade (icône de l'écran d'accueil et en-tête du carrousel) ; les textes des jeux et des réglages disent « Calcade ». Les noms internes ne changent pas (`numplay.sys`, `numplay_saves.py`, `NumPlay-coeur.nwa`, modules `np_*`), pour que le site et les sauvegardes continuent de marcher. La page « À propos » des réglages garde le crédit de l'auteur d'origine (Mason Chen, github.com/Mason363/NumPlay), comme le demande la GPL. ## 1. Intro (≈ 4,3 s) Elle reprend celle de Voie Libre, avec la même chronologie (0,3 s logo, onde de choc, 30 éclats ; 1,0 s lettres C‑A‑L‑C‑A‑D‑E toutes les 75 ms ; 1,55 s / 1,95 s reflets ; 2,05 s « présente » ; 2,4 s `jeux.w-production.fr` ; 3,95 s fondu). **OK** ou **Retour** la passe après 0,5 s. **Home** quitte NumPlay. - Images générées par `tools/calcade_art.py` à partir du kit (`tools/calcade/` : logo SVG, Archivo Expanded Black, Caveat) : 13,8 Ko d'images, rangées compressées en DEFLATE (6,2 Ko dans la calculatrice) et décompressées en RAM au démarrage. - Les textes utilisent les polices de NumPlay (Nunito). Seules les lettres accentuées qui manquaient sont ajoutées (à é è —, 0,6 Ko). - Code dans `launcher/src/calcade.c` : fond animé, sprites mis à l'échelle avec teinte, opacité et reflet, anneaux, poussières. Tout est dessiné par bandes de 16 lignes, avec le moteur de NumPlay (`gfx.c`). - Mesure dans l'émulateur ARM (216 MHz, sans la vitesse réelle de la N0120) : 21,6 images/s pour l'intro et 18,7 images/s pour l'écran de mise à jour. - Avec le déguisement « Matrices » de NumPlay, l'intro ne se joue qu'après le code secret. ## 2. Mise à jour obligatoire (tous les 20 lancements) À chaque ouverture de NumPlay, juste après la lecture de `numplay.sys` : ``` compte = launches & 0x7FFFFFFF ; compte++ si compte % 20 == 0 : bit 31 = 1 (20e, 40e, 60e… ouverture) launches = (bit 31) | compte ; somme de contrôle recalculée ; fichier écrit tout de suite ``` - Si le bit 31 vaut 1, l'intro se joue puis l'écran **« Mise à jour »** s'affiche et reste à l'écran. Aucune touche ne le quitte (OK, EXE, Retour, flèches, chiffres, effacer…). Le texte indique « Mise à jour obligatoire — va sur jeux.w-production.fr/#/mise-a-jour pour continuer à jouer. » Seule **Home** (ou On/Off) quitte NumPlay. - Le jeu ne remet **jamais** ce bit à 0. Les ouvertures continuent d'être comptées pendant le blocage. - C'est le site qui le remet à 0, une fois la mise à jour installée (voir § 4). NumPlay se débloque au lancement suivant. ## 3. Sauvegarde `numplay.sys` (CLC2, 116 octets, petit-boutiste) Fichier invisible du stockage des scripts Python, lu et écrit avec les fonctions d'enregistrements de NumPlay (`games/common/epsilon_files.h`, même méthode que `save.c` du kit : en-tête userland, `[u16 taille][nom\0][données]`). Il est aussi copié dans `numplay_saves.py`, la copie de sauvegarde de NumPlay, ce qui lui permet de survivre à une mise à jour faite sur le site officiel NumWorks. | Octet | Taille | Champ | Sens | |---:|---:|---|---| | 0 | 4 | `magic` | `"CLC2"` (0x32434C43) | | 4 | 2 | `fmt` | 2 | | 6 | 2 | `size` | 116 | | 8 | 12 | `game` | `numplay` (ASCII, terminé par 0) | | 20 | 8 | `ver` | `1.10.0` | | 28 | 4 | `launches` | bits 0‑30 : ouvertures de NumPlay ; **bit 31 : mise à jour exigée** | | 32 | 4 | `plays_total` | parties lancées depuis NumPlay (tous jeux) | | 36 | 12 | `last_game` | identifiant du dernier jeu lancé (`numdash`…), vide si aucun | | 48 | 1 | `ngames` | nombre de jeux de cette version de NumPlay (19 dans les deux versions) | | 49 | 1 | `last_index` | place du dernier jeu dans la liste ci-dessous, 0xFF si aucun | | 50 | 2 | `reserved0` | 0 | | 52 | 56 | `plays[28]` | u16 : nombre de lancements de chaque jeu, par sa place ci-dessous | | 108 | 4 | `reserved1` | 0 | | 112 | 4 | `check` | somme de contrôle des octets 0 à 111 (algorithme `hash_bytes` du kit) | Places de `plays[]` (ordre de NumPlay 1.10.0, le même dans les deux versions) : | 0 numdash (NumDash) | 1 crossyroad (Piou Road) | 2 numblocks (NumBlocks) | 3 numdrive (NumDrive) | |---|---|---|---| | 4 balatro (Jokeria) | 5 buckshot (Chevrotine) | 6 blockblast (Block Boom) | 7 portal (Warp Lab) | | 8 tetris (Stackfall) | 9 chess (Échecs) | 10 flappybird (Flappix) | 11 pacman (Munchy Maze) | | 12 snake (Snake) | 13 connectfour (Aligne 4) | 14 solitaire (Solitaire) | 15 g2048 (2048) | | 16 minesweeper (Démineur) | 17 breakout (Casse-briques) | 18 numvisuals (NumVisuals) | | Les identifiants restent ceux de NumPlay 1.10.0 (liste figée dans `tools/calcade_art.py`), même pour les jeux renommés : les compteurs déjà enregistrés gardent leur place. `ngames` vaut 19 dans les deux versions. Un fichier absent, trop court, d'un autre jeu ou dont la somme est fausse ne fait jamais planter NumPlay : on repart de zéro. Il n'existe pas d'ancien format à convertir (c'est la première version Calcade de NumPlay). **La progression des jeux** (niveaux, records…) reste dans les fichiers de chaque jeu, et NumPlay en garde une copie dans le script `numplay_saves.py`. Elle n'est pas recopiée dans `numplay.sys`, car le stockage ne fait que 42 Ko. Pour qu'une mise à jour par le site ne la perde pas, il faut que le site **sauvegarde et remette aussi `numplay_saves.py`** (s'il ne garde pas déjà les scripts Python). Au lancement suivant, NumPlay remet tout seul les sauvegardes manquantes à partir de cette copie. ## 4. À faire côté site (`calcade-sauvegardes.js`) 1. **Compter sans le bit 31** : partout où `lancements` sert à comparer deux copies, utiliser `lancements & 0x7FFFFFFF` (sinon une copie bloquée paraît toujours « plus récente »). 2. **Lever le drapeau après l'installation**, en recalculant la somme de contrôle, et écrire le fichier même si la calculatrice a déjà une copie avec le même compte : ```js function leverDrapeauMaj(octets) { const d = new DataView(octets.buffer, octets.byteOffset, octets.byteLength); const taille = d.getUint16(6, true); d.setUint32(28, d.getUint32(28, true) & 0x7fffffff, true); d.setUint32(taille - 4, sommeControle(octets, taille - 4), true); return octets; } // dans decoder() : r.lancements_reels = r.lancements & 0x7fffffff; r.maj_exigee = (r.lancements >>> 31) === 1; if (r.jeu === "numplay" && octets.length >= 116) { r.details = { parties: d.getUint32(32, true), dernier_jeu: texte(octets, 36, 12), jeux_dans_version: octets[48], lancements_par_jeu: Array.from({ length: 28 }, (_, i) => d.getUint16(52 + 2 * i, true)), }; } ``` ## 5. Pack NumPlay (19 octobre 2026) : le cœur et les mini-jeux en modules > **Mise en règle (20 octobre 2026) :** le cœur et les 19 modules sont refabriqués à partir des sources mises > en règle (`tools/calcade_pack.py` dans l'arbre français). Les identifiants ne changent pas (`np_balatro` est > Jokeria, `np_buckshot` Chevrotine, `np_portal` Warp Lab) ; les cartes des grands jeux restent valables. Sur le site, « Tout dans NumPlay » : le joueur coche des mini-jeux de NumPlay et des grands jeux Calcade, et le navigateur lie le tout en **une seule application NumPlay** (`lieur.js`, GNU ld en WebAssembly : `eadk.o + NumPlay-coeur.nwa + module1.cmod + module2.cmod + …`). Chaque jeu ne prend que sa vraie taille (pas d'arrondi à 64 Ko par jeu, une seule fois la bibliothèque du lanceur). | Fichier | Rôle | |---|---| | `NumPlay-coeur.nwa` | le lanceur seul (`-DNP_COEUR=1`) : intro Calcade, carrousel, réglages, compteur et mise à jour obligatoire (`numplay.sys` inchangé). Aucun jeu dedans. | | `modules/np_.cmod` | un mini-jeu = un module Calcade (fiche ABI 1, id `np_`) | | `modules/np_.json` / `.png` | sa présentation sur le site (nom, description, catégorie, ordre) et sa capture 160×120 | **Le cœur** (`launcher/src/coeur.c`) construit la table des jeux au démarrage à partir des fiches liées, dans l'ordre du site : - fiche `np_…` : il pose la *question NumPlay* à sa fonction `lancer()` (`ctx->abi == NUMPLAY_QUESTION`, voir `launcher/src/numplay_pack.h`) et reçoit la description complète du jeu (captures, couleurs, fichiers de sauvegarde) : le jeu se comporte exactement comme dans NumPlay ; - toute autre fiche valide : un grand jeu Calcade (Voie Libre, Turbo Rush…). Sa carte montre sa vignette 80×60 (ou une cartouche à sa couleur avec son initiale). Il reçoit `ctx->ram` = toute la RAM libre (alignée sur 8, remise à zéro avant chaque lancement) ; `CALCADE_QUITTER` ferme NumPlay ; - aucun module : écran « Aucun jeu dans NumPlay — choisis tes jeux sur jeux.w-production.fr ». **La RAM.** L'arène de NumPlay n'est plus un tableau du lanceur : c'est la RAM libre de l'appli, de `_heap_start` à `_heap_end` (symboles du script de liaison du site). Un mini-jeu y range sa `.data` (à +0) et sa `.bss` (juste après, alignée sur 8) : `tools/npmodule.py --arena-symbol _heap_start` réécrit ses références ; le cœur recopie l'image de `.data` et met la `.bss` à zéro avant chaque partie. Si la RAM libre ne suffit pas, la carte l'écrit (« Il manque N Ko… ») et le jeu ne se lance pas. **Un module mini-jeu** (`tools/calcade_pack.py`) = le jeu tel que NumPlay le fabrique, ses captures, sa description, sa fiche, et sa propre libc/libm/libgcc ; tout est rendu local sauf la fiche. Il ne réclame que `eadk_*`, des fonctions du cœur (`np_keyboard_scan`, `gfx_*`…, liste dans `dist-pack/exports-coeur.txt`, que le cœur garde visibles) et les symboles du script de liaison. Stackfall (ex-Tetris) est en Rust : quand la cible `thumbv7em-none-eabihf` n'est pas installable, il est repris tel quel d'un `NumPlay.nwa` déjà fabriqué (`tools/calcade_extraire.py`, option `--extraire`). **Les cartes des grands jeux** (19 octobre, soir) : `tools/calcade_cartes.py` fabrique, pour un grand jeu Calcade, un petit module à fiche `carte:` qui ne contient que deux captures au format du carrousel (192×144, 256 couleurs). Le site l'ajoute au pack avec le jeu (`jeux valides/NumPlay/cartes/.cmod`) ; le cœur l'accroche au jeu (même id de fiche, ou même nom) et la carte du carrousel montre ces captures au lieu de la vignette 80×60. Les captures des mini-jeux sont aussi en 256 couleurs (au lieu de 16 dans la version française), avec un léger renforcement de netteté après la réduction (`tools/shots.py`). Dans un pack, la désinstallation d'un jeu depuis les réglages est désactivée (les jeux ne sont pas calés sur les secteurs de la flash) : on refait son pack sur le site. ``` python3 tools/lang.py tree fr ../fr && cd ../fr # NumPlay en français python3 tools/calcade_pack.py --extraire NumPlay.nwa # dist-pack/NumPlay-coeur.nwa et dist-pack/modules/ python3 tools/calcade_pack_test.py --lieur lieur.js --eadk eadk.o --out essai \ --keys "6000-6100:ok" --shots 7000 dist-pack/NumPlay-coeur.nwa dist-pack/modules/np_snake.cmod ``` `calcade_pack_test.py` lie le pack avec le script de `lieur.js` du site et le fait tourner dans l'émulateur (`tools/emu.py`, qui accepte maintenant un `.elf` déjà lié). Fichiers ajoutés : `launcher/src/coeur.c`, `numplay_pack.h`, `calcade_module.h` (contrat du kit Calcade), `tools/calcade_pack.py`, `calcade_pack_test.py`, `calcade_extraire.py`. Modifiés (tout sous `#if NP_COEUR`, sauf `np_game_run` qui rend maintenant un int) : `np.h`, `main.c`, `games.c`, `home.c`, `gfx.[ch]`, `flash.c`, `calcade.c`, `tools/npmodule.py` (option `--arena-symbol`), `tools/emu.py` (`.elf` accepté). ## Compiler ``` tools/calcade_build.sh # dist/NumPlay-complet.nwa et dist/NumPlay-leger.nwa python3 tools/calcade_art.py launcher/src/calcade_art.c # régénère images et polices python3 tests/test_calcade.py dist/NumPlay-complet.nwa # vérifications dans l'émulateur python3 tools/calcade_shots.py dist/NumPlay-complet.nwa captures/ ``` Outils : arm-none-eabi-gcc 15.2 (xPack), nwlink 1.0.0, Python 3 avec Pillow, zopfli, pyelftools, unicorn, cairosvg, pngquant, et Rust pour Stackfall (cible `thumbv7em-none-eabihf`). Fichiers modifiés par rapport à NumPlay 1.10.0 : `launcher/src/main.c` (4 lignes), `launcher/src/storage.c` (1 ligne) et `Makefile` (option `ONLY`). Fichiers ajoutés : `launcher/src/calcade.[ch]`, `calcade_art.c`, `tools/calcade_art.py`, `tools/calcade_shots.py`, `tools/calcade_build.sh`, `tools/calcade/`, `tests/test_calcade.py`. ## Licence NumPlay © 2026 Mason Chen est distribué sous **GNU GPL v3** (fichier `LICENSE`). Cette version est une **version modifiée par Calcade (16 et 19 octobre 2026)** : elle reste sous GPL v3, et son code source doit être fourni avec chaque `.nwa` distribué. Le site le propose sur `jeux.w-production.fr/licences/numplay/` : `NumPlay-1.10.0-calcade.patch` (à appliquer avec `git apply` sur la v1.10.0), puis supprimer les fichiers de `NumPlay-1.10.0-calcade-fichiers-retires.txt`. - Polices (dossier `LICENSES/`, et `games/numdrive/LICENSES/`) : Nunito, Fusion Pixel, Archivo, Caveat et Poppins, toutes sous SIL OFL 1.1. - Le code repris de Tatone26 (Stackfall et la base de plusieurs mini-jeux) est dans le domaine public (`UNLICENSE`). - 2048 : licence MIT de Gabriele Cirulli (`games/g2048/LICENSE-2048.txt`). - Code de stockage de Stackfall : BSD 3-Clause (`games/tetris/numworks_utils/LICENSE-storage.txt`). Les idées de jeu sont communes ; les noms, personnages, images et niveaux de cette version sont ceux de NumPlay. Les marques citées (Tetris, Minecraft, Geometry Dash, Balatro, Buckshot Roulette, Portal, Drive Mad, Pac-Man, Crossy Road, Block Blast!, Puissance 4, Flappy Bird, Google, Windows) appartiennent à leurs propriétaires, qui ne sont pas liés à NumPlay ni à Calcade.