Compare commits

..
Author SHA1 Message Date
koka c8531aac4b docs: organiser la production TikTok 2026-09-01 03:50:29 +02:00
koka 3ffa9b889f docs: ajouter les 100 premiers scripts TikTok 2026-09-01 02:35:12 +02:00
224 changed files with 1829 additions and 11259 deletions
-204
View File
@@ -6,210 +6,6 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve
- Depuis `26.2.0-alpha.6`, le pack et les modules ont des versions indépendantes. I Like To Move It et Only Fun deviennent les cinquième et sixième modules actifs dans l'alpha 46.
- Les versions ci-dessous ont été reconstituées à partir des JAR archivés, des packs locaux et du code source actuel. Les dates n'étant pas enregistrées de manière fiable, elles ne sont pas inventées.
## Non publié
Aucun changement non publié.
## `26.2.0-alpha.244`
Pull request intégrée : [#176](https://git.botsu.net/koka/sanctuary/pulls/176).
- ajoute huit feux dartifice à portraits : ChrisM, Kaninscheun, Poupoutain, lBeast, CreveForest, SacDChips, Vaselline et Duncan ;
- dessine pour chacun un visage 8×8 et sa capuche en particules orientées vers le joueur, en taille normale ou agrandie ;
- ajoute les huit recettes sans forme, les objets, les textures et les traductions EN/FR/RU correspondantes ;
- conserve le feu dartifice Nourra et son identifiant existant ;
- conserve les sauvegardes, les données OnlyFun v2 et le protocole réseau 3 ; OnlyFun passe à `0.0.0-alpha.29`.
## `26.2.0-alpha.243`
Pull requests intégrées : [#172](https://git.botsu.net/koka/sanctuary/pulls/172), [#173](https://git.botsu.net/koka/sanctuary/pulls/173) et [#174](https://git.botsu.net/koka/sanctuary/pulls/174).
- ajoute une barre de recherche dans longlet catalogue, avec repli des accents et filtrage local ;
- permet de choisir la grille du catalogue : **3×3**, **4×4** ou **5×5** maximum, mémorisée entre les sessions ;
- affiche le numéro de page du catalogue (`2/5`) à côté de la navigation ;
- conserve la grille 3×3 et les neuf emplacements des ventes flash ;
- resynchronise le texte des panneaux de quêtes après une rotation survenue hors chunk et garantit quun rafraîchissement opérateur génère un nouveau contrat ;
- ajoute le feu dartifice Nourra, dont lexplosion dessine le visage et la capuche du skin en particules, en taille normale ou agrandie ;
- conserve les sauvegardes et les protocoles réseau existants ; Sanctuary passe à `0.0.0-alpha.140` et OnlyFun à `0.0.0-alpha.28`.
## `26.2.0-alpha.242`
Dépendance mise à jour : **MyDisplay `1.0.0` (quatrième build fourni)**.
- remplace le troisième JAR MyDisplay sous un nom distinct afin de forcer sa mise à jour par Packwiz ;
- actualise les composants client de performance, de session multimédia et de lecture WaterMedia ;
- conserve ladaptation du manifeste pour le serveur dédié et les traductions EN/FR/RU ;
- conserve WaterMedia `3.0.0.23`, ses binaires `3.0.0.6`, les écrans existants, les sauvegardes et les protocoles ;
- ne modifie aucun des dix modules Sanctuary.
## `26.2.0-alpha.241`
Dépendance mise à jour : **MyDisplay `1.0.0` (troisième build fourni)**.
- remplace le second JAR MyDisplay sous un nom distinct pour forcer sa mise à jour par Packwiz ;
- accélère la copie ARGB vers RGBA avec un `IntBuffer` et adapte la résolution de décodage WaterMedia à la distance ;
- plafonne et priorise les uploads vidéo par proximité, sans masquer les écrans qui attendent leur prochaine image ;
- suspend les uploads hors champ, coupe le rendu au-delà de 128 blocs et regroupe le rendu du mur ;
- partage Streamlink entre les écrans identiques et échelonne le démarrage des lecteurs ;
- évite de créer les décodeurs audio OpenAL/FFmpeg lorsque l’écran est muet et sécurise les captures F2 ;
- conserve WaterMedia `3.0.0.23`, ses binaires `3.0.0.6`, les écrans existants, les sauvegardes et les protocoles.
## `26.2.0-alpha.240`
Dépendance mise à jour : **MyDisplay `1.0.0` (second build fourni)**.
- remplace le premier JAR MyDisplay par une nouvelle révision sous un nom distinct pour contourner les caches Packwiz ;
- ajoute la sélection côté client de l’écran MyDisplay actuellement visé et met à jour la gestion multimédia associée ;
- conserve WaterMedia `3.0.0.23` et WaterMedia Binaries `3.0.0.6` côté client ;
- conserve ladaptation du manifeste pour le serveur dédié et complète toujours EN/FR avec le russe ;
- rend le résolveur des dépendances Packwiz compatible avec les téléchargements directs privés épinglés ;
- conserve les écrans MyDisplay existants, les dix modules Sanctuary, leurs sauvegardes et leurs protocoles.
## `26.2.0-alpha.239`
Dépendances remplacées : **MyDisplay `1.0.0`**, **WaterMedia `3.0.0.23`** et **WaterMedia Binaries `3.0.0.6`**.
- retire Dream Displays et son second build de test audio du client comme du serveur ;
- ajoute MyDisplay côté client et serveur pour les blocs-écrans, leur configuration et leur synchronisation ;
- ajoute WaterMedia et ses binaires natifs côté client uniquement pour la lecture et le rendu multimédia ;
- adapte seulement le manifeste de MyDisplay afin que ses dépendances client optionnelles ne bloquent pas le serveur dédié, et complète ses traductions EN/FR avec le russe ;
- ne convertit pas les écrans Dream Displays déjà placés, qui doivent être remplacés par des écrans MyDisplay ;
- conserve les dix modules Sanctuary, leurs versions, sauvegardes et protocoles sans changement ;
- garde le module temporaire ZEVENT entièrement absent du workspace et du pack.
## `26.2.0-alpha.238`
Dépendance remplacée : **Dream Displays `1.10.0-dev` (audio-buffer-test, révision 2)**.
- remplace le premier JAR de test Dream Displays par la nouvelle révision fournie ;
- utilise un nouveau nom de fichier afin de contourner les caches et de garantir son téléchargement ;
- distribue ce build côté client et serveur avec ses nouvelles empreintes vérifiées ;
- conserve les dix modules Sanctuary, leurs versions, sauvegardes et protocoles sans changement ;
- garde le module temporaire ZEVENT entièrement absent du workspace et du pack.
## `26.2.0-alpha.237`
Dépendance remplacée : **Dream Displays `1.10.0-dev` (audio-buffer-test)**.
- remplace Dream Displays `1.9.6` par le JAR de test fourni pour corriger la gestion du tampon audio ;
- utilise un nom de fichier distinct afin que Packwiz supprime lancien JAR et ne réutilise pas son cache ;
- distribue le nouveau build côté client et serveur depuis le dépôt de téléchargement Sanctuary avec empreintes vérifiées ;
- conserve les dix modules Sanctuary, leurs versions, sauvegardes et protocoles sans changement ;
- garde le module temporaire ZEVENT entièrement absent du workspace et du pack.
## `26.2.0-alpha.236`
Module temporaire supprimé : **ZEVENT `0.0.0-alpha.3`**.
- retire entièrement le sous-projet, le JAR client/serveur et les identifiants `zevent:*` devenus redondants ;
- conserve Dream Displays `1.9.6`, qui fournit directement les écrans et la lecture des flux vidéo ;
- retire automatiquement `zevent-0.0.0-alpha.3.jar` des installations gérées par Packwiz ;
- ne convertit pas les anciens écrans muraux ZEVENT : ils doivent être remplacés par des écrans Dream Displays ;
- conserve sans changement les sauvegardes, inventaires, monnaies et protocoles des dix modules Sanctuary restants.
## `26.2.0-alpha.235`
Module modifié temporairement : **ZEVENT `0.0.0-alpha.3`**.
- remplace MCEF Modern, dont Chromium ne sait pas décoder les directs Twitch en H.264/AAC, par Dream Displays `1.9.6` et son pipeline vidéo natif ;
- conserve le bloc-écran 9 × 5, les chaînes enregistrées, linterface et le protocole réseau ZEVENT 1 sans migration de monde ;
- réutilise directement la texture GPU décodée dans le renderer ZEVENT, avec une limite de 720p et deux flux visibles au maximum par client ;
- maintient le volume dans la catégorie Disques, latténuation jusqu’à 64 blocs et un seul écran audible à la fois ;
- réessaie automatiquement les chaînes momentanément indisponibles et affiche désormais un message explicite lorsquune chaîne est hors ligne ;
- valide le démarrage client natif sur macOS Apple Silicon ainsi que la résolution dun direct Twitch actif en cinq qualités H.264 jusquen 1080p avec audio AAC.
## `26.2.0-alpha.234`
Module ajouté temporairement : **ZEVENT `0.0.0-alpha.2`**.
- ajoute pour le week-end le bloc-écran ZEVENT, une surface multibloc de 9 × 5 qui diffuse une chaîne Twitch officielle dans le monde ;
- permet de saisir un nom de chaîne ou une URL Twitch par clic droit, puis enregistre et synchronise la chaîne côté serveur ;
- relie le volume à la catégorie Disques, latténue jusqu’à 64 blocs et limite chaque client à deux lecteurs visibles ;
- ajoute MCEF Modern `0.3.3+mc26.2.jcef146.0.10` uniquement côté client, sans imposer Chromium au serveur dédié ;
- conserve les données et protocoles des dix modules Sanctuary existants ; ZEVENT démarre en données v1 et protocole 1, avec mise à jour simultanée du client et du serveur.
## `26.2.0-alpha.233`
Modules modifiés : **Sanctuary `0.0.0-alpha.139`** et **Red-Stoner `0.0.0-alpha.20`**.
- remplace le bloc-écran `redstoner:trophy_panel` par un vrai panneau Vanilla en chêne pâle, posable au sol ou au mur, sans changer lID de litem déjà présent dans les inventaires et recettes ;
- ouvre à la première pose un éditeur blanc permettant de rechercher une statistique Vanilla, composer et réordonner jusqu’à seize pages, puis choisir entre noms et valeurs, noms seuls ou valeurs seules ;
- verrouille la configuration et la destruction au créateur ou à un opérateur, tout en laissant le clic droit public faire défiler les pages ;
- conserve lancien bloc uniquement comme alias de migration : chaque plaque déjà posée devient automatiquement un panneau mural en gardant propriétaire, statistiques et page courante ;
- répare les classements importés qui montraient les huit premiers caractères de lUUID : Sanctuary récupère désormais le vrai pseudo depuis le cache local du serveur et utilise `?` plutôt quun fragment dUUID si aucun nom nest connu ;
- ajoute les payloads bornés de l’éditeur, fait passer Red-Stoner aux données v5 et au protocole réseau 9, sans changer les données v15 ni le protocole 24 de Sanctuary. Client et serveur Red-Stoner doivent être mis à jour ensemble.
## `26.2.0-alpha.232`
Modules modifiés : **Another World `0.0.0-alpha.58`**, **Sanctuary `0.0.0-alpha.138`** et **Red-Stoner `0.0.0-alpha.19`**.
- ferme le ticket #146 en enregistrant chaque cliché pris avec un appareil Canon dans le dossier `screenshots` de linstance, à côté des captures F2 ;
- exporte le carré cadré à sa résolution native avec le filtre de la pellicule, sans le réduire aux 128 × 128 pixels de la carte en jeu ;
- écrit le PNG hors du thread de rendu et réserve un nom horodaté unique afin d’éviter les freezes comme les écrasements lors de plusieurs clichés rapprochés ;
- remplace le volume de la cape à motifs par les deux faces opposées de l’îlot UV exact du bouclier, redimensionnées aux proportions 10 × 16 de la cape, afin de corriger la projection et le culling sans afficher le manche ni l’épaisseur du bouclier ;
- laisse la touche dinventaire fermer le terminal tant que la recherche nest pas focalisée, mais la traite comme du texte dans le champ de recherche afin que la lettre `E` ne ferme plus l’écran ;
- utilise les huit lignes du panneau de trophées pour afficher le titre et jusqu’à sept rangs, avec une page de noms suivie dune page de valeurs pour chaque statistique afin que les longues durées de jeu restent lisibles ;
- fait configurer par un œuf dapparition le classement vanilla des créatures correspondantes tuées, par exemple les zombies tués avec l’œuf de zombie ;
- conserve les IDs et les sauvegardes Sanctuary v15/Red-Stoner v4 ; le snapshot client du panneau passant de trois à sept entrées, le protocole Red-Stoner passe de 7 à 8 et exige la mise à jour du client et du serveur.
## `26.2.0-alpha.231`
Modules modifiés : **Sanctuary `0.0.0-alpha.137`** et **Red-Stoner `0.0.0-alpha.18`**.
- transforme le panneau de trophées en plaque verticale de chêne, corrige le texte auparavant masqué et utilise les quatre lignes pour le titre paginé ainsi que les trois places du podium ;
- permet de passer à la page suivante dun clic droit et de revenir à la précédente avec `Maj` + clic droit, sans changer les IDs, les sauvegardes Red-Stoner v4 ni le protocole réseau 7.
- garantit sur chaque niveau de la Backroom un coffre par cellule de 3 × 3 chunks dans les nouveaux chunks, au lieu dun coffre aléatoire réparti entre les cinq niveaux ;
- resserre aussi la grille des lits de sortie de 10 × 10 à 3 × 3 chunks sur chacun des cinq niveaux afin quils soient réellement trouvables ;
- actualise JEI vers la bêta Fabric `30.29.0.201`, qui corrige un crash avec les mods utilisant latlas de textures ;
- ne change ni le butin ordinaire des coffres, ni les données Sanctuary v15/Red-Stoner v4, ni leurs protocoles réseau 24/7 ; les nouveaux coffres et lits apparaissent dans les chunks qui nont pas encore été générés.
## `26.2.0-alpha.230`
Modules modifiés : **Sanctuary `0.0.0-alpha.136`** et **Red-Stoner `0.0.0-alpha.17`**.
- attribue les demandes de quêtes personnalisées terminées à leur preneur directement sur le panneau et dans les détails au clic, avec son pseudonyme et sa couleur conservés ;
- répartit les lits de sortie rares sur chacun des cinq niveaux de la Backroom, avec exactement un lit par cellule de 10 × 10 chunks et par niveau dans les nouveaux chunks ;
- conserve les IDs, les données Sanctuary v14 et le protocole réseau 24 ; les chunks Backroom déjà générés restent inchangés.
- ajoute lAPI publique Sanctuary de métriques et de classements persistants par UUID, avec import monotone des statistiques vanilla, podium borné et exclusion des actions créatives, spectateur ou simulées ;
- ajoute `redstoner:trophy_panel`, panneau mural configurable par son propriétaire avec un item non consommé, pages publiques, rendu en monde et rafraîchissement serveur limité à une fois par seconde ;
- passe Sanctuary aux données v15 et Red-Stoner aux données v4 sans nouveau payload ni changement de leurs protocoles réseau 24 et 7.
- corrige la cape blanche décorée en projetant sa couleur et ses motifs avec les sprites de bouclier, aux proportions exactes de la cape ;
- permet d’équiper une poule adulte avec larmure de loup vanilla en écailles de tatou, de la réparer avec des écailles et de la retirer aux cisailles ; larmure absorbe les dégâts et conserve son usure, ses teintures et ses enchantements ;
- conserve les sauvegardes Sanctuary v15 et le protocole réseau 24 : larmure utilise exclusivement le slot d’équipement vanilla de la poule.
## `26.2.0-alpha.229`
Module modifié : **Red-Stoner `0.0.0-alpha.16`**.
- ajoute au terminal les tris alphabétique, par quantité croissante et par quantité décroissante, appliqués explicitement sans déplacer les objets sous le curseur lors des rafraîchissements périodiques ;
## `26.2.0-alpha.228`
Modules modifiés : **Sanctuary `0.0.0-alpha.132`** et **Red-Stoner `0.0.0-alpha.15`**.
- étend `sanctuary:backroom` à toute la hauteur Y -64 → 383 et empile cinq niveaux thématiques de haut en bas : seuil modulaire, archives, ville, services et glitch ;
- remplace les volumes ouverts par des pièces et couloirs raccordés, quatre angles de salle déclinés en cinq matériaux, des bibliothèques immenses, des tours plus serrées, des autoroutes continues et de rares parcs ;
- ajoute au niveau inférieur une grille de prismes 3D et un labyrinthe déterministe connecté, avec portes physiquement ouvertes, salles fusionnées et trous volontaires vers le vide ;
- fait correspondre exactement chaque ouverture entre le sol supérieur et le plafond inférieur, place une torche récupérable à chaque arrivée et raréfie fortement les lits de sortie ;
- supprime le ciel, les astres, la lumière ambiante et tous les monstres, y compris ceux injectés par un autre système ;
- conserve les IDs, les données Sanctuary v14 et le protocole réseau 24 ; seuls les nouveaux chunks Backroom utilisent la nouvelle architecture complète ;
- rend le champ de recherche du terminal inactif à louverture puis focalisable au clic, libère le focus au clic extérieur et laisse toujours la touche dinventaire fermer le terminal ;
- permet à la recherche du terminal de retrouver une boîte de Shulker par le nom ou lidentifiant dun objet quelle contient, tout en conservant la boîte comme résultat afin que son aperçu vanilla reste directement consultable ;
## `26.2.0-alpha.227`
Modules modifiés : **Sanctuary `0.0.0-alpha.131`** et **Another World `0.0.0-alpha.57`**.
- ajoute la dimension persistante `sanctuary:backroom`, accessible en saccroupissant devant un lit et quittée uniquement par un lit, avec arrivées variées dans une zone architecturale de 2 000 blocs ;
- transfère atomiquement vers la Mailbox tout l’équipement dentrée, y compris les rangées étendues, l’œuf compagnon et la cape, avec reprise idempotente après interruption ;
- conserve les objets perdus au dépop, dans le vide et lors des morts en Backroom dans un registre borné et non duplicable ;
- transforme tous les coffres de la dimension en butin renouvelable, pauvre la plupart du temps, qui privilégie les pertes personnelles dans les premiers coffres avant les pertes anciennes partagées ;
- génère des villes, bureaux, plaines, agrégats flottants et pièces visuellement retournées à partir de palettes composées, en réservant les matériaux dAnother World aux anomalies et les blocs précieux à de rares cachettes ;
- fait enregistrer la mousse turquoise par Another World via lAPI publique de matériaux Backroom, sans dépendance circulaire ;
- actualise Fabric API vers `0.159.0+26.2` et JEI vers `30.29.0.199`, avec empreintes Packwiz et Prism synchronisées ;
- migre Sanctuary vers les données v14 sans changer le protocole réseau 24, sans réinitialiser les sauvegardes ni effacer les constructions.
## `26.2.0-alpha.226`
Module modifié : **Sanctuary `0.0.0-alpha.130`**. Les neuf autres modules conservent leur version issue de l'alpha.225.
+7 -45
View File
@@ -1,6 +1,6 @@
# Sanctuary 26.2
Workspace Fabric multi-projet de Sanctuary 2.0. Il construit dix JAR, sur Minecraft 26.2, Fabric Loader 0.19.3, Fabric API 0.159.0+26.2, Loom 1.17, Gradle 9.5.1 et Java 25.
Workspace Fabric multi-projet de Sanctuary 2.0. Il construit dix JAR indépendants sur Minecraft 26.2, Fabric Loader 0.19.3, Fabric API 0.158.0+26.2, Loom 1.17, Gradle 9.5.1 et Java 25.
Auteur : **KOKA99CAB**
Licence : **GNU GPL 3.0 or Later** (`GPL-3.0-or-later`)
@@ -12,53 +12,17 @@ Licence : **GNU GPL 3.0 or Later** (`GPL-3.0-or-later`)
| `anotherworld` | Another World | choses matérielles | matière, photographie, instruments météo et 108 biomes natifs |
| `itsalive` | It's Alive ! | vivant | Creepers, créatures, agriculture et gameplay serveur complet de Canaplia |
| `ambiance` | Ambiance | perception | atmosphère, météo, lumières dynamiques, audio positionnel, calendrier et composition visuelle accessible |
| `redstoner` | Red-Stoner | calcul et signaux | ordinateurs, identité serveur, terminal de stockage, panneau de trophées, Drawer et Particuleur |
| `redstoner` | Red-Stoner | calcul et signaux | ordinateurs, identité serveur, terminal de stockage, Drawer et Particuleur |
| `iliketomoveit` | I Like To Move It | déplacement | portails, véhicules, grappin, poulies et waystones locaux |
| `ouch` | Ouch | combat et destruction | arsenal actif (6 armes) et TNT météo |
| `onlyfun` | Only Fun | règles ludiques absurdes | lootboxes, Lucky Block, raretés et règles absurdes |
| `sanctuary` | Sanctuary | progression et aventure | progression globale, classements publics, Atlas et monde principal flottant |
| `sanctuary` | Sanctuary | progression et aventure | progression globale, Atlas et monde principal flottant |
| `events` | Events | événements serveur | socle vide |
| `masterkey` | MasterKey | administration et diagnostic | laboratoire minimal |
Les descriptions FR/EN/RU sont déclarées dans chaque `fabric.mod.json` et dans les fichiers de langue. Les images fournies servent d'icônes Fabric de leur module.
Version courante du pack distribué : **`26.2.0-alpha.244`**. Cette release conserve les dix modules Sanctuary, notamment Another World **`0.0.0-alpha.58`**, Sanctuary **`0.0.0-alpha.140`**, OnlyFun **`0.0.0-alpha.29`** et Red-Stoner **`0.0.0-alpha.20`**, garde ZEVENT entièrement retiré et met à jour MyDisplay **`1.0.0`** vers le quatrième build fourni, avec WaterMedia **`3.0.0.23`** et WaterMedia Binaries **`3.0.0.6`**.
Lalpha.244 intègre la PR #176. OnlyFun ajoute huit feux dartifice à portraits pour ChrisM, Kaninscheun, Poupoutain, lBeast, CreveForest, SacDChips, Vaselline et Duncan. Chaque explosion reproduit le visage et la capuche de son skin en particules orientées vers le joueur, en taille normale ou agrandie. Les recettes, objets, textures et traductions EN/FR/RU sont inclus, tandis que Nourra, les sauvegardes et le protocole restent compatibles.
Lalpha.243 intègre les PR #172, #173 et #174. Le catalogue du Shop gagne une recherche tolérante aux accents, des grilles 3×3, 4×4 et 5×5 mémorisées ainsi quun compteur de pages, sans modifier les ventes flash. Les panneaux de quêtes resynchronisent leur texte après une rotation hors chunk et les rafraîchissements opérateur produisent bien un nouveau contrat. OnlyFun ajoute enfin le feu dartifice Nourra, dont lexplosion dessine le visage et la capuche du skin en particules. Les sauvegardes et protocoles restent compatibles.
Lalpha.242 remplace le troisième JAR MyDisplay par `mydisplay-1.0.0-sanctuary-4.jar`. Cette révision actualise exclusivement le pipeline client de performance, de session et de lecture WaterMedia, sans changer les blocs, les données ni le protocole réseau. Le manifeste reste compatible avec le serveur dédié, les traductions EN/FR/RU sont alignées, les écrans déjà placés restent compatibles et le nom distinct force Packwiz à retirer le build précédent.
Lalpha.241 remplace le second JAR MyDisplay par `mydisplay-1.0.0-sanctuary-3.jar`. Le rendu garde tous les écrans visibles tout en réduisant la charge client : copie vidéo ARGB vers RGBA par `IntBuffer`, résolution WaterMedia adaptative selon la distance, priorité et budget des uploads, gel de limage hors champ, distance de rendu limitée à 128 blocs, rendu mural groupé et démarrage échelonné. Les flux Streamlink identiques sont partagés, laudio muet ne crée plus de décodeur OpenAL/FFmpeg et les captures F2 réinitialisent proprement le tampon en suspendant les uploads. Le manifeste reste compatible avec le serveur dédié, les traductions EN/FR/RU sont alignées et les écrans déjà placés restent compatibles.
Lalpha.240 remplace le premier JAR MyDisplay par `mydisplay-1.0.0-sanctuary-2.jar`, dont le nom distinct force Packwiz à télécharger le nouveau build et à supprimer le précédent. Cette révision ajoute notamment la sélection côté client dun écran MyDisplay visé. Le manifeste conserve ladaptation serveur dédié et les traductions EN/FR/RU restent alignées. Les écrans MyDisplay déjà placés restent compatibles.
Lalpha.239 distribue MyDisplay côté client et serveur, et réserve WaterMedia ainsi que ses binaires natifs au client. Packwiz retire automatiquement Dream Displays ; ses anciens écrans déjà posés ne sont pas convertis et doivent être remplacés par des écrans MyDisplay.
Lalpha.238 distribue la seconde révision `dreamdisplays-fabric-26.2-audio-buffer-test-2.jar` côté client et serveur.
Lalpha.237 remplace Dream Displays `1.9.6` par le premier build de test `1.10.0-dev` destiné à corriger la gestion du tampon audio.
Lalpha.236 supprime le JAR et le code ZEVENT devenus redondants. Packwiz retire automatiquement `zevent-0.0.0-alpha.3.jar` du client et du serveur, tandis que Dream Displays reste installé des deux côtés. Les écrans `zevent:zevent_screen` et leurs chaînes enregistrées ne sont pas convertibles : ils doivent être remplacés par des écrans Dream Displays. Les sauvegardes et données des dix autres modules restent inchangées.
Lalpha.235 corrige l’écran noir de ZEVENT. MCEF chargeait correctement linterface et les liens Twitch, mais son Chromium ne prenait pas en charge les codecs des directs. L’écran utilise désormais le résolveur HLS et le décodeur natif de Dream Displays, conserve ses options et sa synchronisation serveur, réessaie automatiquement un flux indisponible et indique clairement lorsquune chaîne est hors ligne. Le volume suit toujours la catégorie Disques, décroît jusqu’à 64 blocs et seul l’écran visible le plus proche reste audible.
Lalpha.234 ajoute pour le week-end l’écran Twitch ZEVENT de 9 × 5 blocs. Un clic droit permet de choisir une chaîne officielle ; la sélection est enregistrée par le serveur, tandis que limage et le son sont rendus localement avec MCEF Modern. Le volume suit la catégorie Disques et décroît jusqu’à 64 blocs. Les mondes et les dix modules Sanctuary existants restent compatibles ; le client et le serveur doivent toutefois recevoir le JAR ZEVENT, et seul le client reçoit MCEF.
Lalpha.233 remplace le bloc-écran des trophées par un véritable panneau Vanilla en chêne pâle, posable au sol ou au mur. Son éditeur permet de rechercher une statistique, composer jusqu’à seize pages et choisir laffichage des noms et valeurs ; le propriétaire ou un opérateur garde le contrôle de sa configuration tandis que le changement de page reste public. Litem `redstoner:trophy_panel` conserve son ID et les anciens panneaux posés sont migrés automatiquement. Les classements affichent désormais le vrai pseudo connu du joueur au lieu dun fragment dUUID. Les sauvegardes Sanctuary restent en données v15/protocole 24 ; Red-Stoner passe aux données v5 et au protocole 9, ce qui exige la même version côté client et serveur.
Lalpha.232 enregistre automatiquement chaque cliché Canon dans le dossier `screenshots` de linstance, à côté des captures F2. Le PNG conserve le carré réellement cadré, sa résolution et le filtre de la pellicule ; l’écriture asynchrone et les noms uniques évitent les freezes et les écrasements. Cette release corrige aussi la cape blanche décorée, la touche `E` du terminal et étend le panneau de trophées à huit lignes. Chaque statistique y possède une page de noms puis une page de valeurs ; un œuf dapparition ajoute directement le classement des créatures correspondantes tuées. Les sauvegardes restent en données Another World v5, Sanctuary v15 et Red-Stoner v4 ; le snapshot du panneau passant à sept entrées, le protocole Red-Stoner passe à 8.
Lalpha.231 rend les sorties et le butin de la Backroom réellement trouvables sur ses cinq niveaux. Chaque niveau garantit désormais un coffre et un lit de sortie par cellule de 3 × 3 chunks dans les nouveaux chunks, sans ajouter de livres au butin. Le panneau de trophées devient une plaque verticale de chêne avec quatre lignes lisibles : titre et numéro de page, puis les trois places du podium. Un clic droit avance dune page et `Maj` + clic droit revient à la précédente. JEI passe à la bêta Fabric `30.29.0.201`, qui corrige un crash lié à latlas de textures. Les sauvegardes et les chunks déjà générés restent compatibles ; Sanctuary conserve les données v15/protocole 24 et Red-Stoner les données v4/protocole 7.
Lalpha.230 attribue les quêtes communautaires terminées à leur preneur et répartit les lits de sortie rares sur chacun des cinq niveaux de la Backroom. Elle ajoute aussi les classements publics et le panneau mural `redstoner:trophy_panel`, corrige la projection des couleurs et motifs de la cape blanche avec les sprites de bouclier, et permet d’équiper une poule adulte avec larmure de loup vanilla en écailles de tatou. Les sauvegardes restent compatibles ; Sanctuary passe aux données v15 et Red-Stoner aux données v4 sans changer leurs protocoles réseau 24 et 7.
Lalpha.229 ajoute au terminal les tris alphabétique, par quantité croissante et par quantité décroissante. Le tri ne change que sur demande explicite et les rafraîchissements périodiques conservent lordre ainsi que les objets sous le curseur. Les sauvegardes, les données Red-Stoner v3 et le protocole réseau 7 restent compatibles.
Lalpha.228 reconstruit la Backroom sur cinq niveaux empilés entre Y -64 et Y 383. Le monde devient entièrement noir et sans monstres ; larrivée est signalée par une torche récupérable. Les nouveaux chunks composent seuils modulaires, bibliothèques et bureaux, ville dense avec autoroutes et parcs, couloirs techniques, puis une grille 3D et un labyrinthe connecté dont certains trous mènent au vide. Red-Stoner corrige aussi le focus du terminal et permet de retrouver une boîte de Shulker par son contenu. Les chunks déjà générés et les sauvegardes existantes restent compatibles.
Lalpha.227 ajoute la dimension persistante `sanctuary:backroom`. On y entre accroupi devant un lit et on nen ressort que par un lit. L’équipement dentrée est transféré atomiquement vers la Mailbox, tandis que les objets perdus ou abandonnés dans la dimension alimentent des coffres renouvelables sans duplication. Les anciennes sauvegardes sont migrées vers Sanctuary data v14 sans réinitialisation ; le protocole réseau reste en version 24.
Version courante du pack distribué : **`26.2.0-alpha.226`**. Cette release distribue Sanctuary **`0.0.0-alpha.130`** ; les neuf autres modules conservent leur version issue de l'alpha.225.
Lalpha.225 corrige quatre tickets : posséder un coffre-fort devient obligatoire pour en braquer un autre, les familiers temporaires ne donnent plus de butin, dexpérience, de lootbox ni de progression de quête, les seize variantes Canaplia survivent à tout leur cycle de culture, et le Magic Carpet retrouve la texture historique du Totem de Notch à la place de la laine violette. Les IDs, sauvegardes, formats de données et protocoles restent compatibles.
@@ -76,7 +40,7 @@ Lalpha.220 transforme le terminal de stockage en véritable menu de conteneur
Lalpha.219 porte l'île finie Minecraft Indev dans `sanctuary:alpha` : 128 × 128 × 64 blocs déterministes, plages, cavernes, minerais, fluides, végétation et maison historique, le tout borné sans chunks parasites. Le cadre 4 × 5 en cobblestone moussue est maintenant actif et crée une paire persistante à une arrivée sûre hors de la maison. Ambiance peut enfin composer localement Golden Days Base puis Alpha uniquement dans cette dimension, sans redistribuer leurs textures et en restaurant exactement la pile précédente. Voir le [contrat Alpha](sanctuary/ALPHA.md), le [contrat des portails](iliketomoveit/PORTALS.md) et le [guide Golden Days](ambiance/GOLDEN_DAYS.md).
Lalpha.218 ajoute le terminal de stockage Red-Stoner : cette demi-dalle verticale se fixe sur un coffre, un coffre piégé ou un baril et donne accès, par pages défilantes de 54 types, à un réseau contigu borné à 128 conteneurs chargés. Les retraits et dépôts sont validés par le serveur, les composants ditems restent strictement séparés et le terminal endgame est exclu du Shop, du Black Market et des récompenses Only Fun. La recherche retrouve aussi une boîte de Shulker daprès son contenu direct ; la boîte reste affichée dans la grille afin que son aperçu vanilla soit consultable sans modifier les règles de retrait. Un bouton permet enfin de trier explicitement les résultats par nom, quantité croissante ou quantité décroissante.
Lalpha.218 ajoute le terminal de stockage Red-Stoner : cette demi-dalle verticale se fixe sur un coffre, un coffre piégé ou un baril et donne accès, par pages défilantes de 54 types, à un réseau contigu borné à 128 conteneurs chargés. Les retraits et dépôts sont validés par le serveur, les composants ditems restent strictement séparés et le terminal endgame est exclu du Shop, du Black Market et des récompenses Only Fun.
Lalpha.217 tentait de compenser la hauteur trop grave du disque blanc par un facteur OpenAL de `4/3`. Cette compensation est retirée par lalpha.221, qui corrige directement la conversion stéréo vers mono.
@@ -349,9 +313,7 @@ Le guide détaillé est dans [`masterkey/tools/PANORAMA.md`](masterkey/tools/PAN
## Red-Stoner et photographie
Red-Stoner possède ses appareils sous son namespace ainsi que la paire de panneaux Vanilla `trophy_sign`/`trophy_wall_sign`. Litem historique `redstoner:trophy_panel` est conservé afin que les inventaires et recettes existants restent valides ; il place désormais cette paire, tandis que lancien bloc mural du même ID ne subsiste que le temps de sa migration automatique. Son onglet créatif expose le panneau de trophées avec les ordinateurs, le Particuleur et le terminal de stockage.
Le panneau de trophées consomme lAPI publique de classement de Sanctuary sans conserver les scores. Sa première pose ouvre un éditeur blanc : le créateur choisit dans le catalogue Vanilla jusqu’à seize statistiques, leur ordre et un affichage noms+valeurs, noms seuls ou valeurs seules. `Maj` + clic droit rouvre ensuite cet éditeur pour le créateur ou un opérateur ; le clic droit ordinaire fait défiler publiquement les pages. La configuration et la destruction sont protégées par UUID. Chaque page utilise les quatre lignes natives du panneau — titre puis trois rangs — et se rafraîchit côté serveur. Sanctuary importe une fois les statistiques Vanilla existantes, conserve les classements par UUID — champion déconnecté compris — et résout les vrais pseudos depuis le cache du serveur au lieu dafficher un début dUUID.
Red-Stoner possède quatre blocs actifs sous son namespace : `eight_bit_computer`, `controller`, `personal_computer` et `drawer`. Son onglet créatif expose aussi les trois appareils Canon et les seize styles de pellicule.
L'ordinateur 8 bits et le contrôleur exécutent la machine virtuelle historique : programme assembleur borné à 4096 caractères, RAM, inventaire, six ports redstone directionnels, impulsions et sortie comparateur. Le contrôleur dispose de 1024 cellules de RAM contre 256 pour l'ordinateur. L'instruction destinée à la matrice d'affichage renvoie volontairement une erreur claire tant que ce périphérique reste différé.
@@ -440,7 +402,7 @@ Le menu pause est lui aussi possédé par Sanctuary. Il ne conserve que Retour a
## Versions du modpack et mises à jour Modrinth
`pack_version` dans `gradle.properties` est la version du pack complet. `mod_version=0.0.0-alpha.0` et `module_lifecycle=standby` sont les valeurs par défaut des modules en veille ; les propriétés `<module>_version` et `<module>_lifecycle` nactivent et ne font avancer que le module concerné. Le contrat de distribution est dans `pack/release.json`. Depuis lalpha.236, le module temporaire `zevent` nest plus construit ni distribué ; lalpha.240 utilise le second build MyDisplay fourni et son moteur client WaterMedia à la place de Dream Displays.
`pack_version` dans `gradle.properties` est la version du pack complet. `mod_version=0.0.0-alpha.0` et `module_lifecycle=standby` sont les valeurs par défaut des modules en veille ; les propriétés `<module>_version` et `<module>_lifecycle` nactivent et ne font avancer que le module concerné. Le contrat de distribution est dans `pack/release.json`.
La version globale du pack nest pas injectée dans les JAR inchangés : leur contenu et leur numéro restent immuables. Le lanceur possède la composition exacte du pack, tandis que chaque manifest conserve la version effective de son module.
+2 -41
View File
@@ -15,14 +15,6 @@ tasks.register("vaultRulesSmoke", JavaExec) {
mainClass = "fr.koka99cab.sanctuary26.anotherworld.vault.VaultRulesSmoke"
}
tasks.register("canonScreenshotFilesSmoke", JavaExec) {
group = "verification"
description = "Checks Canon photo exports use the vanilla screenshots directory and collision-safe names."
dependsOn tasks.named("testClasses")
classpath = sourceSets.test.runtimeClasspath
mainClass = "fr.koka99cab.canon.client.CanonScreenshotFilesSmoke"
}
tasks.named("test") {
failOnNoDiscoveredTests = false
}
@@ -1964,7 +1956,7 @@ tasks.register("verifyAnotherWorldCatalog") {
"src/main/resources/assets/redstoner/textures/item/weather_gauge.png",
"src/main/resources/data/redstoner/recipe/weather_gauge.json"
]
if (project.version.toString() != "0.0.0-alpha.58"
if (project.version.toString() != "0.0.0-alpha.56"
|| sulfurMigration.target?.modules?.anotherworld != "0.0.0-alpha.44"
|| sulfurMigration.sulfur_caves?.sanctuary?.dripstone_cell_period != 2
|| !javaText.contains('Identifier.parse("sanctuary:sulfur_caves")')
@@ -3269,7 +3261,6 @@ tasks.register("verifyAnotherWorldCatalog") {
tasks.named("check") {
dependsOn tasks.named("verifyAnotherWorldCatalog")
dependsOn tasks.named("vaultRulesSmoke")
dependsOn tasks.named("canonScreenshotFilesSmoke")
}
tasks.register("verifyClimateBiomeMixer") {
@@ -3282,7 +3273,7 @@ tasks.register("verifyClimateBiomeMixer") {
"pack/migrations/26.2.0-alpha.188-beta-climate-provinces-to-alpha.189.json"))
def mixer = file(
"src/main/java/fr/koka99cab/sanctuary26/anotherworld/mixin/MultiNoiseBiomeSourceParameterListPresetMixin.java").text
if (project.version.toString() != "0.0.0-alpha.58"
if (project.version.toString() != "0.0.0-alpha.56"
|| migration.source?.pack_version != "26.2.0-alpha.188"
|| migration.target?.pack_version != "26.2.0-alpha.189"
|| migration.target?.modules?.anotherworld != "0.0.0-alpha.48"
@@ -3309,33 +3300,3 @@ tasks.register("verifyClimateBiomeMixer") {
tasks.named("check") {
dependsOn tasks.named("verifyClimateBiomeMixer")
}
tasks.register("verifyBackroomMaterialIntegration") {
group = "verification"
description = "Checks the one-way Another World material integration with Sanctuary Backroom."
inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/anotherworld/AnotherWorldMod.java"))
inputs.file(file("src/main/java/fr/koka99cab/sanctuary26/anotherworld/registry/AnotherWorldBlocks.java"))
inputs.file(rootProject.file(
"sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/backroom/BackroomMaterialApi.java"))
doLast {
def initializer = file("src/main/java/fr/koka99cab/sanctuary26/anotherworld/AnotherWorldMod.java").text
def blocks = file("src/main/java/fr/koka99cab/sanctuary26/anotherworld/registry/AnotherWorldBlocks.java").text
def api = rootProject.file(
"sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/backroom/BackroomMaterialApi.java").text
def rootBuild = rootProject.file("build.gradle").text
def manifest = new JsonSlurper().parse(file("src/main/resources/fabric.mod.json"))
if (project.version.toString() != "0.0.0-alpha.58"
|| !initializer.contains("BackroomMaterialApi.registerAnomalyMaterial(AnotherWorldBlocks.TURQUOISE_MOSS)")
|| !blocks.contains('"turquoise_moss"')
|| !api.contains("public static void registerAnomalyMaterial")
|| manifest.depends?.sanctuary != ">=0.0.0-alpha.131"
|| !rootBuild.contains('project(":anotherworld")')
|| !rootBuild.contains('implementation project(":sanctuary")')) {
throw new GradleException("Another World alpha.57 Backroom material integration is incomplete")
}
}
}
tasks.named("check") {
dependsOn tasks.named("verifyBackroomMaterialIntegration")
}
@@ -1,38 +0,0 @@
package fr.koka99cab.canon.client;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
final class CanonScreenshotFiles {
private static final String SCREENSHOT_DIRECTORY = "screenshots";
private static final int MAX_SUFFIX = 10_000;
private CanonScreenshotFiles() {
}
static Path reserve(Path gameDirectory, String timestamp) throws IOException {
Path screenshots = gameDirectory.resolve(SCREENSHOT_DIRECTORY);
Files.createDirectories(screenshots);
for (int suffix = 1; suffix <= MAX_SUFFIX; suffix++) {
Path candidate = screenshots.resolve(fileName(timestamp, suffix));
try {
return Files.createFile(candidate);
} catch (FileAlreadyExistsException ignored) {
// Keep the vanilla timestamp readable while avoiding same-second collisions.
}
}
throw new IOException("Could not reserve a unique Canon screenshot filename");
}
static String fileName(String timestamp, int suffix) {
if (timestamp == null || timestamp.isBlank()) {
throw new IllegalArgumentException("timestamp must not be blank");
}
if (suffix < 1) {
throw new IllegalArgumentException("suffix must be positive");
}
return timestamp + "-canon" + (suffix == 1 ? "" : "_" + suffix) + ".png";
}
}
@@ -8,15 +8,12 @@ import fr.koka99cab.canon.network.CapturePhotoPayload;
import fr.koka99cab.canon.photo.PhotoMapPalette;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.platform.Window;
import java.nio.file.Files;
import java.nio.file.Path;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Screenshot;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.ARGB;
import net.minecraft.util.Util;
import net.minecraft.world.item.ItemStack;
public final class ClientPhotoCapture {
@@ -68,7 +65,6 @@ public final class ClientPhotoCapture {
ClientPlayNetworking.send(new CapturePhotoPayload(colors));
client.player.getCooldowns().addCooldown(capture.cameraStack(), CanonConstants.CAMERA_COOLDOWN_TICKS);
client.player.playSound(SoundEvents.SPYGLASS_USE, 0.7F, 1.35F);
exportScreenshot(client, screenshot, capture.style(), offsetX, offsetY, cropWidth, cropHeight);
} catch (Exception exception) {
CanonMod.LOGGER.warn("Failed to capture photo", exception);
if (client.player != null) {
@@ -78,72 +74,6 @@ public final class ClientPhotoCapture {
});
}
private static void exportScreenshot(
Minecraft client,
NativeImage screenshot,
PhotoStyle style,
int offsetX,
int offsetY,
int cropWidth,
int cropHeight
) {
NativeImage exported = null;
Path target = null;
try {
exported = new NativeImage(cropWidth, cropHeight, false);
screenshot.copyRect(exported, offsetX, offsetY, 0, 0, cropWidth, cropHeight, false, false);
target = CanonScreenshotFiles.reserve(client.gameDirectory.toPath(), Util.getFilenameFormattedDateTime());
} catch (Exception exception) {
if (exported != null) exported.close();
if (target != null) deleteReservedFile(target);
notifyScreenshotFailure(client, exception);
return;
}
NativeImage image = exported;
Path output = target;
Util.ioPool().execute(() -> {
try (image) {
applyStyle(image, style);
image.writeToFile(output);
client.execute(() -> {
if (client.player != null) {
client.player.sendSystemMessage(Component.translatable(
"message.canon.camera.screenshot_saved", output.getFileName().toString()));
}
});
} catch (Exception exception) {
deleteReservedFile(output);
notifyScreenshotFailure(client, exception);
}
});
}
private static void applyStyle(NativeImage image, PhotoStyle style) {
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
image.setPixel(x, y, style.applyFilter(image.getPixel(x, y)));
}
}
}
private static void notifyScreenshotFailure(Minecraft client, Exception exception) {
CanonMod.LOGGER.warn("Failed to save Canon photo in the screenshots directory", exception);
client.execute(() -> {
if (client.player != null) {
client.player.sendSystemMessage(Component.translatable("message.canon.camera.screenshot_failed"));
}
});
}
private static void deleteReservedFile(Path target) {
try {
Files.deleteIfExists(target);
} catch (Exception cleanupException) {
CanonMod.LOGGER.debug("Could not remove incomplete Canon screenshot {}", target, cleanupException);
}
}
private static int clamp(int value, int min, int max) {
return Math.max(min, Math.min(max, value));
}
@@ -4,9 +4,7 @@ import fr.koka99cab.canon.CanonMod;
import fr.koka99cab.sanctuary26.anotherworld.item.WeatherCompassItem;
import fr.koka99cab.sanctuary26.anotherworld.network.AnotherWorldNetworking;
import fr.koka99cab.sanctuary26.anotherworld.registry.AnotherWorldRegistries;
import fr.koka99cab.sanctuary26.anotherworld.registry.AnotherWorldBlocks;
import fr.koka99cab.sanctuary26.anotherworld.vault.VaultService;
import fr.koka99cab.sanctuary26.sanctuary.api.backroom.BackroomMaterialApi;
import net.fabricmc.api.ModInitializer;
import net.minecraft.resources.Identifier;
import org.slf4j.Logger;
@@ -28,7 +26,6 @@ public final class AnotherWorldMod implements ModInitializer {
@Override
public void onInitialize() {
AnotherWorldRegistries.initialize();
BackroomMaterialApi.registerAnomalyMaterial(AnotherWorldBlocks.TURQUOISE_MOSS);
VaultService.initialize();
WeatherCompassItem.registerFrameInteraction();
CanonMod.initialize();
@@ -24,8 +24,6 @@
"message.canon.camera.full": "The camera is already full.",
"message.canon.camera.captured": "Photo captured.",
"message.canon.camera.capture_failed": "Could not capture the current frame.",
"message.canon.camera.screenshot_saved": "Photo saved to screenshots: %s",
"message.canon.camera.screenshot_failed": "The in-game photo was captured, but its PNG could not be saved to screenshots.",
"style.canon.white": "Neutral",
"style.canon.white.effect": "Neutral stock with soft paper whites.",
"style.canon.light_gray": "Matte",
@@ -24,8 +24,6 @@
"message.canon.camera.full": "L'appareil photo est deja plein.",
"message.canon.camera.captured": "Photo prise.",
"message.canon.camera.capture_failed": "Impossible de capturer l'image actuelle.",
"message.canon.camera.screenshot_saved": "Photo sauvegardee dans screenshots : %s",
"message.canon.camera.screenshot_failed": "La photo en jeu est prise, mais son PNG n'a pas pu etre sauvegarde dans screenshots.",
"style.canon.white": "Neutre",
"style.canon.white.effect": "Rendu neutre avec des blancs doux.",
"style.canon.light_gray": "Mat",
@@ -24,8 +24,6 @@
"message.canon.camera.full": "Фотоаппарат уже полностью заряжен.",
"message.canon.camera.captured": "Фотография сделана.",
"message.canon.camera.capture_failed": "Не удалось снять текущий кадр.",
"message.canon.camera.screenshot_saved": "Фотография сохранена в screenshots: %s",
"message.canon.camera.screenshot_failed": "Снимок в игре сделан, но PNG не удалось сохранить в screenshots.",
"style.canon.white": "Нейтральный",
"style.canon.white.effect": "Нейтральная плёнка с мягкими белыми тонами.",
"style.canon.light_gray": "Матовый",
@@ -31,7 +31,7 @@
"java": ">=25",
"fabric-api": ">=0.154.2+26.2",
"ambiance": "*",
"sanctuary": ">=0.0.0-alpha.131"
"sanctuary": ">=0.0.0-alpha.117"
},
"custom": {
"sanctuary26": {
@@ -1,33 +0,0 @@
package fr.koka99cab.canon.client;
import java.nio.file.Files;
import java.nio.file.Path;
public final class CanonScreenshotFilesSmoke {
private CanonScreenshotFilesSmoke() {
}
public static void main(String[] args) throws Exception {
String timestamp = "2026-09-03_01.23.45";
require(CanonScreenshotFiles.fileName(timestamp, 1).equals("2026-09-03_01.23.45-canon.png"),
"first screenshot filename");
require(CanonScreenshotFiles.fileName(timestamp, 2).equals("2026-09-03_01.23.45-canon_2.png"),
"same-second screenshot filename");
Path gameDirectory = Files.createTempDirectory("canon-screenshot-smoke-");
Path first = CanonScreenshotFiles.reserve(gameDirectory, timestamp);
Path second = CanonScreenshotFiles.reserve(gameDirectory, timestamp);
require(first.getParent().equals(gameDirectory.resolve("screenshots")),
"screenshots directory");
require(first.getFileName().toString().equals("2026-09-03_01.23.45-canon.png"),
"first reserved screenshot");
require(second.getFileName().toString().equals("2026-09-03_01.23.45-canon_2.png"),
"collision-safe screenshot");
require(Files.isRegularFile(first) && Files.isRegularFile(second),
"reserved screenshot files");
}
private static void require(boolean condition, String contract) {
if (!condition) throw new IllegalStateException("Invalid Canon screenshot contract: " + contract);
}
}
+316 -1983
View File
File diff suppressed because it is too large Load Diff
+404
View File
@@ -0,0 +1,404 @@
# Sanctuary TikTok — organisation de réalisation
Ce document transforme les [100 premiers scripts](VIDEOS_001_100.md) en chaîne de production solo, reproductible et extensible. L'objectif est de produire vite sans perdre la sobriété, la précision technique ni la cohérence de Sanctuary.
## Objectif de la saison 001
- Produire les 100 vidéos en **10 à 12 semaines**.
- Publier **5 vidéos par semaine**, soit 20 semaines de diffusion.
- Maintenir en permanence **au moins 15 vidéos terminées d'avance**.
- Valider le langage visuel sur 12 pilotes avant d'industrialiser les 88 suivantes.
- Utiliser les performances pour choisir les prochains sujets, sans réécrire l'identité de la série à chaque publication.
Charge solo réaliste : **14 à 18 heures par semaine**, soit environ **140 à 180 heures** pour la saison complète. Les vidéos d'interface peuvent demander moins d'une heure ; les scènes multijoueur, worldgen ou avant/après authentiques peuvent en demander deux à quatre.
## Principe directeur
La publication suit les numéros. La réalisation suit les décors.
Tourner dix vidéos partageant la même ferme, le même laboratoire ou la même lumière est plus efficace que de réaliser les épisodes 001 à 010 dans l'ordre. Le numéro reste l'identité publique de la vidéo ; le lot reste son identité de production.
## Les neuf plateaux de tournage
| Code | Plateau | Épisodes principaux | Préparation indispensable |
|---|---|---|---|
| `UI` | Laboratoire interface | 003019, 083086, 097 | profils de progression, inventaires remplis, terminal et Shop |
| `WRLD` | Archipel et worldgen | 002, 020027, 053, 072079 | seed verrouillée, points de vue, météo et heures reproductibles |
| `ALPH` | Alpha / maison Indev | 028031 | portail, maison intacte, état de Notch et récompense réinitialisable |
| `MOVE` | Terrain de déplacement | 032039 | tours, deux niveaux de Waystones, ligne de poulies et trajectoire de grappin |
| `PASS` | Bestiaire calme | 040049, 053 | enclos invisibles ou hors champ, variantes préparées, fleurs et reproduction |
| `HOST` | Bestiaire hostile | 050059 | arène sombre, protections opérateur, cibles mobiles et second joueur si nécessaire |
| `FARM` | Ferme et cuisine | 060071 | parcelles par stade, six postes, pupitre, stocks d'ingrédients et deux joueurs |
| `TECH` | Atelier technologique | 080090 | Canon, terminal, ordinateurs, réseau de coffres et deux coffres-forts appartenant à des joueurs distincts |
| `OUCH` | Stand de tir / place serveur | 091100 | cibles, munitions, protections, Shop, panneaux, Mailboxes et second joueur |
Les commandes MasterKey servent à préparer les acteurs et les états, jamais à apparaître dans l'image finale. Les scènes doivent ensuite montrer le comportement de jeu normal.
## Les six vagues de production
### P — Pilote, 12 vidéos
`001, 003, 006, 014, 020, 031, 041, 053, 062, 077, 086, 099`
But : éprouver le manifeste, l'interface, le monde, une mécanique spectaculaire, une créature, l'agriculture, l'ambiance et le multijoueur.
Livrable : 12 exports finalisés, mais seulement les quatre premiers sont programmés immédiatement. Les huit autres restent disponibles pour corriger le système avant une diffusion complète.
### A — Progression et interfaces, 20 vidéos
`002, 004, 005, 007013, 015019, 082085, 097`
But : rentabiliser le laboratoire UI et verrouiller la manière de filmer les menus verticaux.
### B — Monde et Ambiance, 19 vidéos
`021030, 072076, 078081`
But : tourner toutes les variations de jour, nuit, météo, shader et dimension pendant que la seed et les cadrages sont verrouillés.
### C — Déplacement et bestiaire, 20 vidéos
`032040, 042052`
But : mutualiser les acteurs, les enclos, les caméras latérales et les scènes nécessitant du mouvement.
### D — Infectés, agriculture et Canaplia, 17 vidéos
`054061, 063071`
But : partager l'arène hostile, la ferme à stades contrôlés, les ingrédients et le deuxième joueur.
### E — Technologie, combat et systèmes sociaux, 12 vidéos
`087096, 098, 100`
But : terminer avec les scènes demandant le plus de préparation matérielle et de règles serveur.
Ces six listes couvrent les 100 épisodes une seule fois. Une vidéo pilote ne doit pas être retournée pendant sa vague thématique sauf si le test révèle un défaut concret.
## Calendrier conseillé
### Semaine 0 — Installer l'usine
- Verrouiller la seed, la version du pack et les réglages vidéo.
- Créer les neuf plateaux ou sauvegardes de référence.
- Choisir la typographie, la taille du texte et l'unique animation de carton final.
- Créer un projet de montage modèle avec trois pistes vidéo, voix, sons du jeu, ambiance et sous-titres.
- Exporter un test de dix secondes et le regarder sur un téléphone réel.
- Préparer les douze fiches du pilote et leurs commandes de laboratoire.
### Semaine 1 — Produire le pilote
- Jour 1 : préparer les scènes `003, 006, 014, 086`.
- Jour 2 : capturer `001, 020, 053, 077` dans le monde.
- Jour 3 : capturer `031, 041, 062, 099` et les scènes multijoueur.
- Jour 4 : monter les douze versions brutes.
- Jour 5 : voix, texte, son, exports et contrôle qualité.
- Jour 6 : corrections et programmation des quatre premières publications.
### Semaine 2 — Publier et corriger le système
- Publier quatre vidéos espacées.
- Relever les performances à J+1 sans changer les vidéos déjà programmées au premier commentaire négatif.
- Identifier les problèmes répétés de lisibilité, de rythme ou de compréhension.
- Corriger uniquement le modèle de production concerné : accroche, taille de texte, cadrage, son ou durée.
- Commencer la vague A pendant la diffusion.
### Semaines 3 à 11 — Régime de croisière
- Produire environ dix nouvelles vidéos par semaine.
- Publier cinq vidéos par semaine.
- Consacrer une semaine complète aux grandes vagues A, B et C lorsqu'elles exigent davantage de scènes.
- Garder la semaine 11 pour les reprises, les scènes multijoueur manquantes et les exports finaux.
### Semaines 12 à 20 — Diffuser avec une saison d'avance
- Continuer la publication des 100 vidéos.
- Concevoir la saison 101200 à partir des formats qui fonctionnent, pas seulement des sujets qui ont fait le plus de vues.
- Maintenir un tampon minimum de quinze exports prêts.
## Semaine type en régime de croisière
| Jour | Travail | Durée cible | Livrable |
|---|---|---:|---|
| Lundi | verrouillage de dix scripts, vérification des sources, liste des accessoires | 2 h | dix fiches `LOCKED` |
| Mardi | préparation des scènes, acteurs, inventaires, heure et météo | 3 h | dix scènes `READY` |
| Mercredi | capture groupée du lot | 4 h | rushes nommés et vérifiés |
| Jeudi | montage image des dix vidéos | 4 h | dix montages `V1` |
| Vendredi | voix, texte, mixage, export et QC | 3 à 4 h | dix masters `APPROVED` |
| Samedi | programmation et revue des publications précédentes | 1 h | calendrier et décisions J+7 |
Ne pas imposer cette répartition lorsque la scène dicte autre chose. Une nuit de sang, un combat multijoueur ou une météo rare doivent être capturés en série pendant que leur état est disponible.
## Pipeline d'une vidéo
Chaque vidéo passe par un seul état à la fois :
```text
BACKLOG
→ LOCKED
→ READY
→ CAPTURED
→ V1
→ TEXT_AUDIO
→ QC
→ APPROVED
→ SCHEDULED
→ PUBLISHED
→ REVIEWED
```
- `BACKLOG` : sujet présent dans le catalogue.
- `LOCKED` : accroche, plans, texte et source validés pour la version distribuée du pack.
- `READY` : décor, acteurs, objets et état du monde préparés.
- `CAPTURED` : tous les rushes existent, sont lisibles et sauvegardés.
- `V1` : montage image complet sans finition.
- `TEXT_AUDIO` : texte exact, voix et sons finalisés.
- `QC` : contrôle sur ordinateur puis téléphone.
- `APPROVED` : master immuable prêt à publier.
- `SCHEDULED` : date, légende et couverture enregistrées.
- `PUBLISHED` : URL et date réelles ajoutées au suivi.
- `REVIEWED` : performances J+1 et J+7 enregistrées, décision de suite prise.
Une vidéo qui échoue au contrôle revient à l'état concerné. Elle ne saute jamais directement de `CAPTURED` à `SCHEDULED`.
## Arborescence locale des médias
Les rushes et exports sont trop lourds pour Git. Les conserver hors du dépôt, par exemple sous `/Users/koka/Movies/Sanctuary-TikTok/Season-001/` :
```text
Season-001/
├── 00-shared/
│ ├── brand/
│ ├── music-and-sfx/
│ ├── project-template/
│ └── export-presets/
├── 01-pilot/
├── 02-wave-a-ui/
├── 03-wave-b-world/
├── 04-wave-c-movement-living/
├── 05-wave-d-food-canaplia/
├── 06-wave-e-tech-combat-social/
└── episodes/
└── SNC-TT-001/
├── brief/
├── capture/
├── audio/
├── project/
├── exports/
└── cover/
```
Le dépôt Git conserve uniquement les scripts, le plan, les éventuels manifests texte et les décisions éditoriales. Il ne doit recevoir ni rushes, ni projets de montage lourds, ni exports vidéo.
## Convention de fichiers
Identifiant canonique : `SNC-TT-<numéro sur trois chiffres>`.
Exemples :
```text
SNC-TT-041__capture-01__wide.mov
SNC-TT-041__capture-02__flower.mov
SNC-TT-041__voice-fr__v02.wav
SNC-TT-041__master-fr__v03.mp4
SNC-TT-041__cover-fr__v01.png
SNC-TT-041__metadata.yml
```
Règles :
- ne jamais employer `final-final`, `nouveau` ou `ok2` ;
- incrémenter `v01`, `v02`, `v03` ;
- ne jamais remplacer un master `APPROVED` ; créer une nouvelle version ;
- conserver la langue dans le nom du master pour préparer EN et RU ;
- inscrire la version exacte du pack capturé dans `metadata.yml` ;
- conserver les musiques, sons et images avec leur provenance et leurs droits.
## Fiche de préparation obligatoire
Avant la capture, chaque épisode reçoit ces champs :
```yaml
id: SNC-TT-041
status: LOCKED
pack_version: 26.2.0-alpha.226
capture_batch: PASS
format: interaction
target_duration_seconds: 11
source_verified: true
actors: 1
multiplayer_required: false
world_snapshot: passive-mobs-v1
shots:
- dangerous-approach
- flower-given
- happy-follow
rights_checked: true
```
Le numéro de version sert à empêcher la publication tardive d'une règle devenue fausse après une mise à jour. Avant programmation, comparer le comportement capturé à la version actuellement distribuée.
## Préparation des scènes
### Décors
- Utiliser des volumes simples, de grandes surfaces lisibles et un fond sans détails concurrents.
- Réserver la pièce blanche ou gris très clair aux vidéos de laboratoire.
- Réserver le décor naturel aux découvertes et chroniques.
- Conserver les coordonnées et l'orientation de chaque caméra dans la fiche de scène.
- Pour un avant/après, réutiliser exactement la même caméra, la même heure et la même action.
### Caméras
N'utiliser que quatre familles de plans :
1. **preuve FPS** : interaction telle que le joueur la vit ;
2. **caméra latérale fixe** : mouvement, créature ou mécanisme ;
3. **plan large lent** : monde, météo ou révélation ;
4. **gros plan d'interface** : valeur, état ou changement de rangée.
Un cinquième type de plan doit justifier son existence. Aucun mouvement de caméra ne doit concurrencer la mécanique montrée.
### Portrait
- Capturer directement en portrait lorsque l'interface est la feature.
- Si la source est horizontale, cadrer toute l'action dans la zone verticale centrale dès le tournage.
- Tester la taille du GUI et les sous-titres sur téléphone avant de tourner tout un lot.
- Garder le texte loin des zones extrêmes supérieure et inférieure de l'écran.
### Multijoueur
Réserver une session acteur de deux à trois heures toutes les deux semaines. Y regrouper au minimum : `031, 037, 039, 048, 057, 065, 071, 090, 098 et 099`.
Préparer avant l'arrivée du second joueur : inventaires, permissions, Mailboxes, objectifs, position de caméra et état de sauvegarde. Le temps acteur doit servir à jouer la scène, pas à construire son décor.
## Montage minimaliste
### Structure par défaut
```text
0,01,5 s preuve ou anomalie visible
1,55,0 s action principale
5,010,0 s conséquence
10,014,0 s règle ou précision
dernier 0,51,0 s carton Sanctuary
```
Ce rythme est un point de départ. Une chronique contemplative peut respirer davantage ; une interaction évidente peut s'arrêter après huit secondes.
### Texte
- Une phrase d'accroche.
- Une phrase de compréhension ou de conséquence.
- Deux lignes maximum à la fois.
- Aucun paragraphe explicatif.
- Les nombres et noms techniques doivent être composés dans le logiciel de montage, jamais générés dans une image.
- Les sous-titres reprennent la voix sans ajouter une deuxième narration concurrente.
### Son
- Conserver le son de l'action : clic, déplacement, impact, ouverture, souffle ou ambiance.
- Utiliser le silence comme contraste avant une révélation.
- Éviter une musique différente à chaque vidéo ; une petite famille sonore suffit à créer la série.
- Vérifier les droits de tout son extérieur au jeu.
- Pour le disque blanc, n'utiliser qu'un extrait dont la publication est autorisée.
## Contrôle qualité avant publication
Une vidéo est `APPROVED` seulement si toutes les réponses sont oui :
- La feature est-elle compréhensible sans lire la légende TikTok ?
- La première preuve apparaît-elle dans les deux premières secondes ?
- Une seule idée principale est-elle défendue ?
- Les valeurs et comportements correspondent-ils encore à la version distribuée ?
- Le HUD est-il présent uniquement lorsqu'il sert de preuve ?
- Les textes restent-ils lisibles sur téléphone et limités à deux lignes ?
- L'action reste-t-elle visible sous les textes et les surfaces de l'application ?
- Le montage contient-il trois plans ou moins, sauf justification écrite ?
- Les sons, musiques et images peuvent-ils être publiés légalement ?
- Les commandes de laboratoire, messages opérateur et éléments privés sont-ils absents ?
- Le fichier porte-t-il le bon ID, la langue et la bonne version ?
- Le master a-t-il été regardé intégralement après export ?
## Publication
Cadence initiale : cinq vidéos par semaine.
| Jour | Type conseillé |
|---|---|
| Lundi | règle simple ou interface |
| Mardi | créature ou interaction |
| Mercredi | problème / correction |
| Vendredi | feature spectaculaire |
| Dimanche | chronique calme, monde ou détail absurde |
Ne pas publier dix épisodes du même module à la suite même s'ils ont été capturés ensemble. Alterner les plateaux donne l'impression d'un monde cohérent plutôt que de neuf mini-campagnes séparées.
La légende doit compléter la vidéo, pas répéter son texte. Structure recommandée :
```text
Une phrase de contexte.
La règle exacte en une phrase.
Sanctuary — épisode 041.
```
Limiter les appels à l'action. Une question réelle peut être utilisée lorsqu'elle ouvre le sujet suivant ; éviter les demandes génériques de like ou d'abonnement sur chaque publication.
## Revue des performances
Enregistrer à J+1 puis J+7 :
- vues ;
- rétention dans les deux premières secondes ;
- durée moyenne regardée ;
- taux de complétion ;
- relectures ;
- partages et enregistrements ;
- visites de profil ;
- questions ou incompréhensions récurrentes.
Comparer chaque vidéo à la médiane de son propre format : une chronique de 25 secondes ne doit pas être jugée comme une interaction de huit secondes.
Décisions :
- accroche forte et bonne complétion : produire une suite dans les sept jours ;
- accroche faible mais bonne complétion chez les spectateurs restants : recouper les deux premières secondes pour un futur sujet proche ;
- chute au moment d'une explication : raccourcir ou déplacer la phrase en légende ;
- question répétée : créer une vidéo-réponse, sans alourdir la vidéo originale ;
- bon sujet mais preuve peu visible : retourner la scène, pas seulement changer le texte ;
- résultat faible isolé : ne pas abandonner immédiatement tout le format.
## Tableau de suivi minimal
Le suivi peut rester dans un tableur ou un outil Kanban, avec une ligne par ID et ces colonnes :
| Champ | Usage |
|---|---|
| `id` | `SNC-TT-001` à `SNC-TT-100` |
| `title` | titre canonique du script |
| `batch` | `UI`, `WRLD`, `ALPH`, `MOVE`, `PASS`, `HOST`, `FARM`, `TECH` ou `OUCH` |
| `status` | état unique du pipeline |
| `pack_version` | version réellement capturée |
| `source_verified` | oui/non et date |
| `scene_ready` | oui/non |
| `multiplayer` | oui/non |
| `capture_date` | date réelle |
| `master_path` | chemin du master approuvé |
| `rights_checked` | oui/non |
| `scheduled_at` | date de programmation |
| `published_url` | URL réelle |
| `review_j1` | métriques et note courte |
| `review_j7` | métriques et décision |
| `follow_up` | nouvel ID ou action décidée |
## Prochaine action concrète
Commencer par la semaine 0, puis ne préparer que les douze pilotes. Le système n'est considéré comme verrouillé qu'après :
1. un export regardé sur téléphone ;
2. quatre publications réelles ;
3. une revue J+1 de ces quatre vidéos ;
4. une décision explicite sur la taille du texte, le rythme, le son et le carton final.
Après cette validation, les 88 vidéos restantes peuvent entrer en production industrielle sans devoir être remontées à cause d'un défaut de format découvert trop tard.
+950
View File
@@ -0,0 +1,950 @@
# Sanctuary TikTok — vidéos 001 à 100
Première saison éditoriale de TikTok sobres et minimalistes présentant Sanctuary comme une réinterprétation personnelle de Minecraft, une règle à la fois. La chaîne de production complète est décrite dans le [plan de réalisation](PRODUCTION_PLAN.md).
## Contrat de réalisation
- Format vertical 9:16, 1080 × 1920, 30 images par seconde.
- Une seule idée par vidéo, généralement en 10 à 20 secondes.
- Trois plans maximum, coupes franches, caméra fixe ou mouvement très lent.
- HUD masqué sauf lorsque l'interface constitue la preuve.
- Sons du jeu prioritaires ; musique et voix off facultatives.
- Deux lignes de texte maximum simultanément.
- Carton final de 0,5 à 1 seconde : `SANCTUARY / <SÉRIE> <NUMÉRO>`.
- Les valeurs, limites et comportements annoncés doivent rester identiques aux sources liées.
- Pour les correctifs, montrer le symptôme puis la correction ; ne jamais fabriquer un faux « avant ».
Les textes proposés sont une base de tournage. Ils peuvent être lus en voix off ou affichés à l'écran, mais ne doivent pas être doublés intégralement sur les deux canaux.
## Vue d'ensemble
| Épisodes | Axe principal |
|---|---|
| 001 | Manifeste de la série |
| 002019 | Progression, inventaire et Atlas |
| 020031 | Monde flottant, biomes et Alpha |
| 032039 | Waystones, grappin et poulies |
| 040059 | Creepers, animaux, monstres et familiers |
| 060071 | Agriculture, cuisine et Canaplia |
| 072081 | Temps, météo, lumière, image et audio |
| 082090 | Red-Stoner, stockage et coffre-fort |
| 091096 | Arsenal Ouch |
| 097100 | Shop, marché, quêtes et Only Fun |
## 001 — Je réinterprète Minecraft, un détail à la fois
- **Format / durée :** manifeste, 15 s.
- **Accroche :** « Je ne voulais pas seulement ajouter des mods à Minecraft. »
- **Plans :** îles flottantes au loin ; Creeper portant une fleur ; inventaire qui gagne une nouvelle rangée ; coupe noire.
- **Texte / voix :** « Je voulais réinterpréter ses règles, son monde et ses créatures. Un détail à la fois. »
- **Sortie :** `SANCTUARY / MANIFESTE 001`.
- **Source :** [présentation des dix modules](../../README.md#modules).
## 002 — Ici, tu commences réellement faible
- **Format / durée :** une règle, 12 s.
- **Accroche :** « Trois cœurs. Trois morceaux de faim. »
- **Plans :** apparition sur l'île ; gros plan propre sur le HUD ; tentative de minage volontairement lente.
- **Texte / voix :** « Dans Sanctuary, le personnage vanilla complet est quelque chose que tu reconstruis. »
- **Sortie :** `SANCTUARY / PROGRESSION 002`.
- **Source :** [départ de partie et statistiques initiales](../../sanctuary/PROGRESSION.md#2-départ-de-partie).
## 003 — Tu ne commences qu'avec ta hotbar
- **Format / durée :** une règle, 10 s.
- **Accroche :** « Au début, ceci est tout ton inventaire. »
- **Plans :** ramasser plusieurs objets ; ouvrir l'inventaire ; révéler uniquement les neuf cases disponibles.
- **Texte / voix :** « Les autres rangées ne sont pas cachées. Elles ne sont pas encore à toi. »
- **Sortie :** `SANCTUARY / INVENTAIRE 003`.
- **Source :** [inventaire extensible](../../README.md#progression-sanctuary-alpha).
## 004 — La place se gagne
- **Format / durée :** interaction, 14 s.
- **Accroche :** « Ton XP peut agrandir ton inventaire. »
- **Plans :** inventaire à une rangée ; achat du rang suivant dans Progression ; réouverture avec deux rangées.
- **Texte / voix :** « Cinq achats permettent de passer d'une seule rangée à six. Les déblocages restent acquis après la mort. »
- **Sortie :** `SANCTUARY / INVENTAIRE 004`.
- **Source :** [inventaire extensible et coûts](../../sanctuary/PROGRESSION.md#44-inventaire-extensible-alpha74).
## 005 — Tab change réellement de hotbar
- **Format / durée :** interaction cachée, 10 s.
- **Accroche :** « Ta deuxième rangée peut devenir ta hotbar. »
- **Plans :** objet distinct dans deux rangées ; pression sur `Tab` ; échange visible de la rangée active sans ouvrir de menu.
- **Texte / voix :** « `Tab` avance. `Maj + Tab` revient. Le serveur échange la vraie hotbar vanilla. »
- **Sortie :** `SANCTUARY / INVENTAIRE 005`.
- **Source :** [rangées de hotbar](../../README.md#progression-sanctuary-alpha).
## 006 — Miner 256 blocs sans tricher
- **Format / durée :** démonstration, 15 s.
- **Accroche :** « Le minage groupé grandit avec ton personnage. »
- **Plans :** cible composée de blocs identiques ; maintien de `Mouse 4` et sélection à la molette ; destruction bornée de la veine.
- **Texte / voix :** « Les rangs passent de 4 à 256 blocs. Portée, permissions, outils, usure et drops restent vérifiés par le serveur. »
- **Sortie :** `SANCTUARY / MINAGE 006`.
- **Source :** [rangs de minage groupé](../../sanctuary/PROGRESSION.md#minage--sept-améliorations).
## 007 — Construire jusqu'à 15 × 15
- **Format / durée :** démonstration, 15 s.
- **Accroche :** « Un clic peut poser un plan entier. »
- **Plans :** prévisualisation 3 × 3 ; sélection d'un rang supérieur à la molette ; placement final d'un plan 15 × 15.
- **Texte / voix :** « `B` et clic droit construisent de 3 × 3 à 15 × 15. Chaque bloc reste placé par une action Minecraft normale. »
- **Sortie :** `SANCTUARY / CONSTRUCTION 007`.
- **Source :** [construction groupée](../../sanctuary/PROGRESSION.md#construction--sept-améliorations).
## 008 — Ta portée devient une statistique
- **Format / durée :** avant / après, 12 s.
- **Accroche :** « Au départ, ce bloc est trop loin. »
- **Plans :** interaction refusée à trois blocs dépassés ; achat de Construction ; même caméra, interaction réussie jusqu'au plafond de 5,5 blocs.
- **Texte / voix :** « La portée de construction progresse. La portée d'attaque, elle, ne change pas. »
- **Sortie :** `SANCTUARY / PROGRESSION 008`.
- **Source :** [rangs de Construction](../../sanctuary/PROGRESSION.md#construction--sept-améliorations).
## 009 — Les bons repas donnent de vrais cœurs temporaires
- **Format / durée :** une règle, 13 s.
- **Accroche :** « Manger peut te protéger sans augmenter ta vie permanente. »
- **Plans :** HUD avant le repas ; consommation ; apparition des cœurs orange ; disparition d'une portion après sa durée.
- **Texte / voix :** « Nutrition, saturation et effets déterminent jusqu'à trois cœurs de repas pendant 15 à 60 secondes. »
- **Sortie :** `SANCTUARY / REPAS 009`.
- **Source :** [cœurs de repas](../../README.md#progression-sanctuary-alpha).
## 010 — Une barre pleine ne suffit pas pour sprinter
- **Format / durée :** problème / règle, 12 s.
- **Accroche :** « Six points de faim. Barre pleine. Sprint impossible. »
- **Plans :** tentative de sprint au rang initial ; achat du premier rang de Faim ; sprint réussi avec plus de six points.
- **Texte / voix :** « Sanctuary garde le seuil absolu de Minecraft. Ton premier rang de Faim débloque réellement la course. »
- **Sortie :** `SANCTUARY / FAIM 010`.
- **Source :** [faim et seuil de sprint](../../sanctuary/PROGRESSION.md#faim--sept-améliorations).
## 011 — Le souffle n'encombre plus l'écran
- **Format / durée :** avant / après, 10 s.
- **Accroche :** « Pourquoi afficher ton air quand tu es sur terre ? »
- **Plans :** HUD terrestre sans bulles ; entrée dans l'eau ; apparition de trois à dix bulles selon le rang.
- **Texte / voix :** « La réserve de souffle n'apparaît que lorsqu'elle devient utile. »
- **Sortie :** `SANCTUARY / HUD 011`.
- **Source :** [HUD de souffle](../../README.md#progression-sanctuary-alpha).
## 012 — L'expérience achète des capacités permanentes
- **Format / durée :** une règle, 14 s.
- **Accroche :** « Dans Sanctuary, l'XP ne sert pas qu'aux enchantements. »
- **Plans :** niveaux disponibles ; écran Progression ; achat du rang suivant ; mort rapide puis retour du rang.
- **Texte / voix :** « Vie, faim, minage, souffle, construction et inventaire progressent avec les niveaux vanilla. Les achats survivent à la mort. »
- **Sortie :** `SANCTUARY / PROGRESSION 012`.
- **Source :** [améliorations et récompenses](../../sanctuary/PROGRESSION.md#4-améliorations-et-récompenses).
## 013 — La progression appartient au serveur
- **Format / durée :** note de laboratoire, 16 s.
- **Accroche :** « Le client demande. Le serveur décide. »
- **Plans :** achat valide ; tentative sans assez d'XP ; action refusée sans désynchronisation.
- **Texte / voix :** « Le prix n'est jamais choisi par le client. Le serveur vérifie le rang, le solde et applique chaque capacité. »
- **Sortie :** `SANCTUARY / LABO 013`.
- **Source :** [contrat serveur de progression](../../README.md#progression-sanctuary-alpha).
## 014 — Une carte composée de 160 000 points
- **Format / durée :** révélation, 15 s.
- **Accroche :** « L'Atlas n'est pas une image vue du ciel. »
- **Plans :** ouverture avec `M` ; rotation lente du nuage de points ; zoom sur le relief d'un chunk.
- **Texte / voix :** « C'est une vue GPU orientable, construite depuis la hauteur et la couleur du terrain exploré. »
- **Sortie :** `SANCTUARY / ATLAS 014`.
- **Source :** [Atlas en nuage de points](../../README.md#progression-sanctuary-alpha).
## 015 — Le monde calculé n'est pas le monde découvert
- **Format / durée :** une règle, 14 s.
- **Accroche :** « L'Atlas connaît les biomes. Pas tes souvenirs. »
- **Plans :** fond de biomes discret ; marche vers une zone inconnue ; apparition du profil détaillé après chargement du chunk.
- **Texte / voix :** « Le fog of war sépare le monde prévisible des lieux que tu as réellement visités. »
- **Sortie :** `SANCTUARY / ATLAS 015`.
- **Source :** [fog of war persistant](../../README.md#progression-sanctuary-alpha).
## 016 — La carte peut exister dans le monde
- **Format / durée :** interaction, 12 s.
- **Accroche :** « Maintiens `Mouse 5`. »
- **Plans :** vue FPS normale ; apparition de la projection Atlas dans le monde ; regard vers l'horizon et disparition progressive.
- **Texte / voix :** « Son opacité suit ton regard pour ne pas masquer le paysage. »
- **Sortie :** `SANCTUARY / ATLAS 016`.
- **Source :** [projection temps réel de l'Atlas](../../README.md#progression-sanctuary-alpha).
## 017 — Une mini-carte à neuf positions
- **Format / durée :** interaction cachée, 13 s.
- **Accroche :** « La même carte peut devenir une mini-carte. »
- **Plans :** activation avec `Mouse 5` ; déplacement entre trois des neuf ancrages ; réglage du zoom à la molette.
- **Texte / voix :** « Permanente, maintenue ou désactivée. Placée là où ton interface en a besoin. »
- **Sortie :** `SANCTUARY / ATLAS 017`.
- **Source :** [modes et placements de mini-carte](../../README.md#progression-sanctuary-alpha).
## 018 — L'Atlas observe les traces du monde
- **Format / durée :** découverte, 15 s.
- **Accroche :** « Certains lieux se cartographient sans marqueur manuel. »
- **Plans :** passage près d'un coffre, d'un portail et d'un générateur ; ouverture de l'Atlas ; trois pictogrammes fixes visibles.
- **Texte / voix :** « Coffres, portails, générateurs et rails souterrains deviennent des points d'intérêt locaux. »
- **Sortie :** `SANCTUARY / ATLAS 018`.
- **Source :** [détection des lieux Atlas](../../README.md#progression-sanctuary-alpha).
## 019 — Maison et Arbre ne sont jamais devinés
- **Format / durée :** une règle, 14 s.
- **Accroche :** « Sanctuary refuse de décider où se trouve ta maison. »
- **Plans :** Personal Computer posé ; clic sur « Maison ici » ; apparition du pictogramme et de son libellé dans l'Atlas.
- **Texte / voix :** « Ces lieux sont personnels. Tu les déclares toi-même depuis ton ordinateur. »
- **Sortie :** `SANCTUARY / ATLAS 019`.
- **Source :** [Personal Computer et lieux personnels](../../README.md#red-stoner-et-photographie).
## 020 — Le monde principal est un archipel flottant
- **Format / durée :** chronique du monde, 18 s.
- **Accroche :** aucun texte pendant les deux premières secondes.
- **Plans :** départ sur l'île centrale ; travelling très lent au-dessus de la couronne de vide ; révélation des îles procédurales extérieures.
- **Texte / voix :** « Sanctuary remplace le monde par défaut : un refuge central, du vide, puis un archipel sans fin. »
- **Sortie :** `SANCTUARY / MONDE 020`.
- **Source :** [génération du monde principal](../../README.md#génération-du-monde-principal).
## 021 — Le vide protège le début de partie
- **Format / durée :** une règle du monde, 15 s.
- **Accroche :** « Cette absence de terrain est volontaire. »
- **Plans :** bord de l'île ; longue focale sur le vide ; îles lointaines reprenant au-delà de la couronne.
- **Texte / voix :** « Le refuge initial est séparé du monde procédural. S'en échapper devient une étape de progression. »
- **Sortie :** `SANCTUARY / MONDE 021`.
- **Source :** [départ de partie](../../sanctuary/PROGRESSION.md#2-départ-de-partie).
## 022 — 108 biomes, sans charger Terralith
- **Format / durée :** preuve visuelle, 18 s.
- **Accroche :** « Sanctuary distribue 108 biomes natifs. »
- **Plans :** trois biomes très contrastés en coupes franches ; survol d'une transition ; écran final sobre avec `108`.
- **Texte / voix :** « Huit biomes historiques et cent créations Terramix, générés sous les règles climatiques natives du pack. »
- **Sortie :** `SANCTUARY / BIOMES 022`.
- **Source :** [biomes natifs d'Another World](../../anotherworld/README.md#biomes-natifs).
## 023 — Le climat dessine des provinces
- **Format / durée :** note de conception, 17 s.
- **Accroche :** « Les biomes ne sont pas tirés au hasard chunk par chunk. »
- **Plans :** travelling continu traversant une zone chaude et sèche ; transition vers une zone humide ; carte large montrant les grands contours.
- **Texte / voix :** « Température et humidité forment de grandes provinces. Une variation locale casse ensuite leurs frontières. »
- **Sortie :** `SANCTUARY / CLIMAT 023`.
- **Source :** [prototype climatique de l'alpha.189](../../CHANGELOG.md#2620-alpha189).
## 024 — Les Sulfur Caves s'annoncent dans la pierre
- **Format / durée :** découverte, 15 s.
- **Accroche :** « Avant la caverne, la roche te prévient. »
- **Plans :** tunnel de deepslate ; première amorce de soufre ; révélation des piscines et pointes de la Sulfur Cave.
- **Texte / voix :** « Une trace visible précède la formation principale. L'exploration peut se lire dans les parois. »
- **Sortie :** `SANCTUARY / CAVERNES 024`.
- **Source :** [Sulfur Caves de l'alpha.179](../../CHANGELOG.md#2620-alpha179).
## 025 — Un donjon peut être suspendu sous une île
- **Format / durée :** chronique du monde, 17 s.
- **Accroche :** « Certains donjons n'ont pas de sol. »
- **Plans :** île flottante vue de côté ; descente verticale ; révélation d'un donjon de cobblestone accroché par des chaînes.
- **Texte / voix :** « S'il trouve de la matière au-dessus, le donjon s'y suspend. Sans ancrage, il n'apparaît pas. »
- **Sortie :** `SANCTUARY / DONJONS 025`.
- **Source :** [donjons suspendus de l'alpha.188](../../CHANGELOG.md#2620-alpha188).
## 026 — Une ravine tapissée de seize couleurs
- **Format / durée :** révélation, 14 s.
- **Accroche :** « Ce biome est volontairement presque introuvable. »
- **Plans :** marche dans un paysage sombre ; arrivée au bord ; plongée lente dans une Rainbow Ravine verticale.
- **Texte / voix :** « Les seize blocs arc-en-ciel suivent les entailles irrégulières du relief, du sommet jusqu'au fond. »
- **Sortie :** `SANCTUARY / BIOMES 026`.
- **Source :** [Rainbow Ravine de l'alpha.114](../../CHANGELOG.md#2620-alpha114).
## 027 — Un monde Minecraft fini en 128 × 128 × 64
- **Format / durée :** chronique historique, 18 s.
- **Accroche :** « Ce monde possède une vraie fin. »
- **Plans :** panorama de l'île Indev ; plage, caverne et maison en coupes courtes ; approche de la limite extérieure.
- **Texte / voix :** « Alpha reconstruit un volume déterministe de 128 × 128 × 64 blocs. Rien ne se génère au-delà. »
- **Sortie :** `SANCTUARY / ALPHA 027`.
- **Source :** [contrat de génération Alpha](../../sanctuary/ALPHA.md#contrat-de-génération).
## 028 — Le portail d'Alpha se construit en cobblestone moussue
- **Format / durée :** interaction, 15 s.
- **Accroche :** « Quatre sur cinq. Deux sur trois à l'intérieur. »
- **Plans :** construction du cadre extérieur 4 × 5 ; allumage au briquet ; arrivée sûre hors de la maison Indev.
- **Texte / voix :** « Le serveur valide le cadre, la dimension et l'arrivée. La première traversée crée une paire persistante. »
- **Sortie :** `SANCTUARY / PORTAIL 028`.
- **Source :** [accès à Alpha](../../sanctuary/ALPHA.md#accès).
## 029 — Les textures Alpha n'existent que dans Alpha
- **Format / durée :** avant / après, 16 s.
- **Accroche :** « Un resource pack peut appartenir à une dimension. »
- **Plans :** monde normal avec la pile habituelle ; traversée vers Alpha et rechargement ; retour et restauration exacte de l'apparence précédente.
- **Texte / voix :** « Ambiance compose Golden Days Base puis Alpha uniquement pendant la visite, sans redistribuer leurs fichiers. »
- **Sortie :** `SANCTUARY / AMBIANCE 029`.
- **Source :** [composition Golden Days](../../ambiance/GOLDEN_DAYS.md).
## 030 — Notch habite encore dans la maison Indev
- **Format / durée :** chronique, 16 s.
- **Accroche :** « Dans ce monde, Notch est un résident. Pas un spawn aléatoire. »
- **Plans :** approche silencieuse de la maison ; Notch à l'intérieur ; même maison après un redémarrage de la scène.
- **Texte / voix :** « Il existe en un seul exemplaire persistant. S'il disparaît, Sanctuary sait le ramener chez lui. »
- **Sortie :** `SANCTUARY / ALPHA 030`.
- **Source :** [résident d'Alpha](../../sanctuary/ALPHA.md#résident-et-jalon-mondial).
## 031 — La première victoire crée un seul Magic Carpet
- **Format / durée :** récompense, 18 s.
- **Accroche :** « Ce coffre n'existe qu'après la première victoire mondiale. »
- **Plans :** fin du combat de Notch ; feu d'artifice près de la maison ; ouverture du coffre avec le tapis placé au centre.
- **Texte / voix :** « La réservation et la livraison sont sauvegardées séparément : même un redémarrage ne duplique pas la récompense. »
- **Sortie :** `SANCTUARY / RÉCOMPENSE 031`.
- **Source :** [première récompense du Magic Carpet](../../iliketomoveit/MAGIC_CARPET.md#première-récompense).
## 032 — Une Waystone doit être découverte physiquement
- **Format / durée :** une règle, 13 s.
- **Accroche :** « Poser une pierre ne suffit pas pour la connaître. »
- **Plans :** menu sans destination ; arrivée à pied et contact avec la pierre ; destination maintenant visible.
- **Texte / voix :** « Chaque joueur possède son propre réseau de lieux réellement visités. »
- **Sortie :** `SANCTUARY / WAYSTONE 032`.
- **Source :** [Waystones locales](../../README.md#waystones-locaux).
## 033 — Le voyage coûte la distance parcourue
- **Format / durée :** une règle, 12 s.
- **Accroche :** « Une téléportation lointaine ne coûte pas comme un saut de voisinage. »
- **Plans :** deux destinations à des distances différentes ; affichage des coûts ; voyage vers la plus lointaine.
- **Texte / voix :** « Le serveur facture un niveau d'XP par tranche commencée de 512 blocs et recherche une arrivée sûre. »
- **Sortie :** `SANCTUARY / WAYSTONE 033`.
- **Source :** [coût des Waystones](../../README.md#waystones-locaux).
## 034 — Une pierre peut être renommée sans commande
- **Format / durée :** interaction cachée, 10 s.
- **Accroche :** « Ouvre une Waystone. Appuie sur `R`. »
- **Plans :** ouverture du réseau ; saisie d'un nom court ; validation avec `Entrée` puis affichage immédiat.
- **Texte / voix :** « `Échap` annule. Le nouveau nom reste attaché à la pierre. »
- **Sortie :** `SANCTUARY / WAYSTONE 034`.
- **Source :** [renommage des Waystones](../../README.md#waystones-locaux).
## 035 — Un ascenseur peut descendre vers l'inconnu
- **Format / durée :** interaction cachée, 13 s.
- **Accroche :** « Tu peux descendre vers un étage que tu n'as jamais visité. »
- **Plans :** deux Waystones alignées en X/Z ; accroupissement sur celle du haut ; arrivée en bas et découverte de la destination.
- **Texte / voix :** « La montée reste interdite tant que l'étage supérieur n'a pas été découvert. »
- **Sortie :** `SANCTUARY / ASCENSEUR 035`.
- **Source :** [ascenseur vertical Waystone](../../README.md#waystones-locaux).
## 036 — Le grappin conserve ton élan
- **Format / durée :** démonstration, 14 s.
- **Accroche :** « Ce grappin ne te téléporte pas vers son crochet. »
- **Plans :** lancement ; balancement en arc lisible ; lâcher au point bas et conservation de la vitesse.
- **Texte / voix :** « Charge, longueur de corde, rappel, descente et restitution de l'élan composent le mouvement. »
- **Sortie :** `SANCTUARY / GRAPPIN 036`.
- **Source :** [grappin et poulies](../../README.md#grappin-et-poulies).
## 037 — Un grappin fluide, même quand le serveur décide
- **Format / durée :** problème / Sanctuary, 16 s.
- **Accroche :** « En multijoueur, un grappin peut devenir une suite de corrections. »
- **Plans :** capture authentique d'un ancien mouvement saccadé si disponible ; version actuelle fluide ; très bref arrêt sur la position réconciliée.
- **Texte / voix :** « Le client prédit le même pendule. Le serveur reste l'autorité et ne corrige que dans une limite bornée. »
- **Sortie :** `SANCTUARY / LABO 037`.
- **Source :** [prédiction du grappin, alpha.196](../../CHANGELOG.md#2620-alpha196).
## 038 — Deux poulies créent une tyrolienne
- **Format / durée :** démonstration, 12 s.
- **Accroche :** « Deux points. Jusqu'à 128 blocs. »
- **Plans :** liaison des deux têtes ; vue latérale de la ligne ; traversée complète à vitesse constante.
- **Texte / voix :** « Les collisions sont vérifiées pendant tout le trajet. »
- **Sortie :** `SANCTUARY / POULIE 038`.
- **Source :** [poulies et tyrolienne](../../README.md#grappin-et-poulies).
## 039 — La tyrolienne peut piloter une machine
- **Format / durée :** interaction entre systèmes, 12 s.
- **Accroche :** « Le passage d'un joueur devient un signal. »
- **Plans :** lampe redstone éteinte reliée à la poulie ; joueur traversant ; signal 15 et lampe allumée pendant le passage.
- **Texte / voix :** « Le déplacement peut devenir une entrée d'automatisation. »
- **Sortie :** `SANCTUARY / SYSTÈMES 039`.
- **Source :** [signal redstone des poulies](../../README.md#grappin-et-poulies).
## 040 — Un Creeper peut naître avec sept apparences
- **Format / durée :** collection, 15 s.
- **Accroche :** « C'est toujours le Creeper vanilla. »
- **Plans :** même cadre, coupes rapides entre classic, simple, pig, business, better, shiny et starry night ; arrêt sur le dernier.
- **Texte / voix :** « L'entité, la charge et l'œuf restent vanilla. L'apparence et le tempérament deviennent persistants. »
- **Sortie :** `SANCTUARY / CREEPER 040`.
- **Source :** [apparences de Creepers](../../itsalive/README.md#creepers).
## 041 — Une fleur peut rendre un Creeper heureux
- **Format / durée :** interaction, 11 s.
- **Accroche :** « Essaie de lui donner une fleur. »
- **Plans :** approche dangereuse ; remise de la fleur ; fleur visible sur la tête et Creeper suivant le joueur.
- **Texte / voix :** « Le changement n'est pas cosmétique : son tempérament devient heureux. »
- **Sortie :** `SANCTUARY / CREEPER 041`.
- **Source :** [tempérament heureux](../../itsalive/README.md#creepers).
## 042 — Le bonheur d'un Creeper est réversible
- **Format / durée :** interaction cachée, 10 s.
- **Accroche :** « Tu peux reprendre la fleur. »
- **Plans :** Creeper heureux ; tonte ; fleur rendue au joueur et retour immédiat du tempérament dangereux.
- **Texte / voix :** « Rien n'est supprimé silencieusement. Même la fleur revient. »
- **Sortie :** `SANCTUARY / CREEPER 042`.
- **Source :** [tonte du Creeper](../../itsalive/README.md#creepers).
## 043 — Une poule sur 256 peut pondre ceci
- **Format / durée :** découverte rare, 12 s.
- **Accroche :** plan fixe sur l'œuf doré, sans texte pendant une seconde.
- **Plans :** poule vanilla ; ponte d'un œuf doré mise en scène avec une capture authentique ou une reproduction de laboratoire clairement identifiée ; lancer de l'œuf.
- **Texte / voix :** « Une poule vanilla possède une chance sur 256 de pondre l'entrée vers une nouvelle lignée. »
- **Sortie :** `SANCTUARY / ŒUF DORÉ 043`.
- **Source :** [œuf doré et poules à gemmes](../../itsalive/README.md#poules-à-gemmes).
## 044 — Les poules peuvent pondre des gemmes
- **Format / durée :** collection, 15 s.
- **Accroche :** « Verte, rouge ou bleue. Trois cadences différentes. »
- **Plans :** bébé issu de l'œuf doré ; trois variantes adultes ; émeraude, rubis et saphir pondus en coupes distinctes.
- **Texte / voix :** « L'émeraude est la plus rapide. Le saphir, la plus lente. »
- **Sortie :** `SANCTUARY / POULLES 044`.
- **Source :** [variantes et cadences des poules à gemmes](../../itsalive/README.md#poules-à-gemmes).
## 045 — Une vache sur 64 se tient debout
- **Format / durée :** découverte absurde, 11 s.
- **Accroche :** plan fixe d'une vache normale qui sort du cadre, puis révélation de la vache debout.
- **Plans :** contraste des deux silhouettes ; marche de la variante ; plan final frontal.
- **Texte / voix :** « La mutation est rare, persistante et peut se transmettre par l'élevage. »
- **Sortie :** `SANCTUARY / VACHE 045`.
- **Source :** [vaches debout rares](../../itsalive/README.md#vaches-debout-rares).
## 046 — Chaque villageois reçoit un nom stable
- **Format / durée :** une règle, 12 s.
- **Accroche :** « Ce villageois ne changera pas de nom au redémarrage. »
- **Plans :** trois villageois avec noms différents ; sortie puis retour dans le monde ; mêmes noms visibles.
- **Texte / voix :** « Le nom vient de son UUID. Un nom donné manuellement n'est jamais remplacé. »
- **Sortie :** `SANCTUARY / VILLAGE 046`.
- **Source :** [villageois nommés](../../itsalive/README.md#villageois-nommés).
## 047 — Il existe seize Mooblooms
- **Format / durée :** collection, 16 s.
- **Accroche :** « Une seule créature. Seize fleurs. »
- **Plans :** travelling devant une ligne de variantes ; gros plan sur les fleurs du dos ; gros plan sur celle qui suit la tête.
- **Texte / voix :** « Chaque variante est synchronisée, sauvegardée et possède son propre œuf de test. »
- **Sortie :** `SANCTUARY / MOOBLOOM 047`.
- **Source :** [seize variantes de Moobloom](../../itsalive/README.md#mooblooms).
## 048 — Un veau Moobloom hérite d'un de ses parents
- **Format / durée :** expérience, 13 s.
- **Accroche :** « Deux fleurs différentes. Une seule pour le veau. »
- **Plans :** deux parents de variantes distinctes ; reproduction ; révélation de la variante héritée.
- **Texte / voix :** « Le veau choisit aléatoirement la fleur de l'un de ses deux parents. »
- **Sortie :** `SANCTUARY / HÉRITAGE 048`.
- **Source :** [élevage des Mooblooms](../../itsalive/README.md#mooblooms).
## 049 — Les papillons se nourrissent de fleurs
- **Format / durée :** chronique calme, 14 s.
- **Accroche :** aucun texte pendant le premier vol.
- **Plans :** groupe de papillons autour d'un biome fleuri ; joueur tenant une fleur ; reproduction puis réaction agressive après une attaque.
- **Texte / voix :** « Ils suivent les fleurs, se reproduisent avec elles et se défendent lorsqu'on les attaque. »
- **Sortie :** `SANCTUARY / PAPILLONS 049`.
- **Source :** [papillons](../../itsalive/README.md#papillons).
## 050 — Le Ghost ne reste jamais jusqu'au matin
- **Format / durée :** horreur minimale, 15 s.
- **Accroche :** murmure hors champ, sans texte.
- **Plans :** Ghost traversant un mur ; charge courte vers le joueur ; lever du jour et disparition.
- **Texte / voix :** « La nuit, il hante, murmure, frappe, puis disparaît. Le jour le supprime. »
- **Sortie :** `SANCTUARY / GHOST 050`.
- **Source :** [Real Ghost](../../itsalive/README.md#real-ghost).
## 051 — Bracken recule quand tu le regardes
- **Format / durée :** horreur minimale, 15 s.
- **Accroche :** « Ne détourne pas les yeux. Mais ne le fixe pas trop longtemps. »
- **Plans :** Bracken approchant dans le dos ; regard du joueur et recul ; rage après un regard prolongé.
- **Texte / voix :** « Il préfère attaquer par derrière. Les haches et le feu lui font davantage de dégâts. »
- **Sortie :** `SANCTUARY / BRACKEN 051`.
- **Source :** [comportement de Bracken](../../itsalive/README.md#real-ghost).
## 052 — Sanctuary supprime les Phantoms
- **Format / durée :** une règle, 9 s.
- **Accroche :** « Les Phantoms n'existent pas ici. »
- **Plans :** nuit après plusieurs jours sans dormir ; ciel vide ; tentative de chargement d'un Phantom immédiatement supprimé en laboratoire.
- **Texte / voix :** « C'est la politique héritée de Real Ghost et No Phantoms. »
- **Sortie :** `SANCTUARY / RÈGLE 052`.
- **Source :** [suppression des Phantoms](../../itsalive/README.md#real-ghost).
## 053 — Une baleine de 36 blocs traverse le ciel
- **Format / durée :** chronique du monde, 18 s.
- **Accroche :** garder les deux premières secondes entièrement silencieuses.
- **Plans :** ombre passant sur le terrain ; révélation latérale de la baleine ; plan large avec son sillage nuageux.
- **Texte / voix :** « Elle est géante, intangible, invulnérable et ne donne aucun butin. Elle existe seulement pour traverser le monde. »
- **Sortie :** `SANCTUARY / BALEINE 053`.
- **Source :** [baleine volante géante](../../itsalive/README.md#baleine-volante-géante).
## 054 — Cinq Infectés, une seule entité persistante
- **Format / durée :** collection, 15 s.
- **Accroche :** « Rouge, jaune, orange, violet, vert. »
- **Plans :** ligne des cinq variantes ; gros plans sur les t-shirts ; attaque simultanée puis coupe noire.
- **Texte / voix :** « Infecté, vomiteur, cracheur, hunter et chargeur partagent la même base, mais pas le même comportement. »
- **Sortie :** `SANCTUARY / INFECTÉS 054`.
- **Source :** [cinq variantes d'Infectés](../../itsalive/README.md#infectés).
## 055 — Le Vomiteur transforme la vision en danger
- **Format / durée :** démonstration, 13 s.
- **Accroche :** attaque reçue avant l'apparition du texte.
- **Plans :** Vomiteur ; overlay de projections ; désorientation et monstres proches attirés vers le joueur.
- **Texte / voix :** « Neuf secondes de vision masquée. Douze secondes de désorientation. Et le bruit attire les monstres. »
- **Sortie :** `SANCTUARY / INFECTÉS 055`.
- **Source :** [attaque du Vomiteur](../../itsalive/README.md#infectés).
## 056 — Le Cracheur combat à distance
- **Format / durée :** démonstration, 11 s.
- **Accroche :** « Celui-ci n'a pas besoin de te toucher. »
- **Plans :** projectile acide en trajectoire ; impact ; effets Poison et Lenteur visibles.
- **Texte / voix :** « Une boule d'acide blesse, empoisonne et ralentit. »
- **Sortie :** `SANCTUARY / INFECTÉS 056`.
- **Source :** [attaque du Cracheur](../../itsalive/README.md#infectés).
## 057 — Quatre pressions pour repousser le Hunter
- **Format / durée :** règle de survie, 16 s.
- **Accroche :** écran noir au moment de la capture.
- **Plans :** bond corrigé vers une cible mobile ; Hunter assis sur la tête ; quatre pressions distinctes d'accroupissement puis libération.
- **Texte / voix :** « Il verrouille sa victime pendant six secondes. Résister quatre fois le repousse. »
- **Sortie :** `SANCTUARY / INFECTÉS 057`.
- **Source :** [capture du Hunter](../../itsalive/README.md#infectés).
## 058 — Le Chargeur devient vulnérable après l'impact
- **Format / durée :** règle de combat, 12 s.
- **Accroche :** « Esquiver est aussi une attaque. »
- **Plans :** préparation lisible ; charge et esquive latérale ; courte récupération pendant laquelle le joueur riposte.
- **Texte / voix :** « Il percute et repousse. Puis il doit récupérer. »
- **Sortie :** `SANCTUARY / INFECTÉS 058`.
- **Source :** [comportement du Chargeur](../../itsalive/README.md#infectés).
## 059 — Un œuf équipé devient un pouvoir
- **Format / durée :** manifeste de système, 16 s.
- **Accroche :** « Les œufs d'apparition ne servent plus seulement à faire apparaître des créatures. »
- **Plans :** œuf équipé dans le slot de familier ; apparition du familier visuel ; activation avec `G` de trois pouvoirs contrastés en coupes rapides.
- **Texte / voix :** « Une essence à la fois. Un passif. Un actif choisi et validé par le serveur. »
- **Sortie :** `SANCTUARY / FAMILIERS 059`.
- **Source :** [contrat des pouvoirs de familiers](../../sanctuary/COMPANION_POWERS.md#contrat-de-jeu).
## 060 — Certaines cultures poussent sur deux blocs
- **Format / durée :** time-lapse, 12 s.
- **Accroche :** « Le maïs ne reste pas une texture plate. »
- **Plans :** plant bas ; accéléré des huit stades ; maïs, tomate ou concombre devenant réellement haut de deux blocs.
- **Texte / voix :** « La moitié supérieure reste synchronisée avec le plant inférieur. »
- **Sortie :** `SANCTUARY / AGRICULTURE 060`.
- **Source :** [agriculture It's Alive](../../itsalive/README.md#agriculture).
## 061 — Récolter sans arracher la plante
- **Format / durée :** interaction, 11 s.
- **Accroche :** « Certaines récoltes sont pérennes. »
- **Plans :** plant mûr de tomate ; clic de récolte donnant les fruits ; retour visuel du plant à un stade plus jeune.
- **Texte / voix :** « Fraise, tomate, concombre, poivron, aubergine et raisin repoussent après la récolte. »
- **Sortie :** `SANCTUARY / AGRICULTURE 061`.
- **Source :** [cultures pérennes](../../itsalive/README.md#agriculture).
## 062 — Le riz exige une vraie rizière
- **Format / durée :** une règle, 12 s.
- **Accroche :** « Pas de riz sur une simple terre labourée. »
- **Plans :** plantation refusée sur une parcelle ordinaire ; source d'eau pleine posée sur de la boue ; croissance complète du riz immergé.
- **Texte / voix :** « Le décor et la règle agricole racontent la même chose. »
- **Sortie :** `SANCTUARY / CUISINE 062`.
- **Source :** [riz immergé](../../itsalive/FOOD_WIKI.md#riz--une-vraie-rizière).
## 063 — Le haricot change selon le moment de récolte
- **Format / durée :** comparaison, 12 s.
- **Accroche :** « Récolté jeune ou récolté sec, ce n'est pas le même ingrédient. »
- **Plans :** récolte d'un plant jeune ; même cadrage sur le plant mûr ; comparaison des deux objets.
- **Texte / voix :** « Le temps de récolte devient un choix culinaire. »
- **Sortie :** `SANCTUARY / CUISINE 063`.
- **Source :** [haricot vert ou sec](../../itsalive/FOOD_WIKI.md#haricot--vert-ou-sec).
## 064 — Six machines, six gestes culinaires
- **Format / durée :** collection, 18 s.
- **Accroche :** « Presser. Moudre. Poêler. Bouillir. Cuire. Fermenter. »
- **Plans :** une action lisible par poste, toujours avec la même caméra et la même table de travail ; résultat final aligné.
- **Texte / voix :** « La cuisine de Sanctuary n'est pas un unique menu de recettes. »
- **Sortie :** `SANCTUARY / CUISINE 064`.
- **Source :** [postes de cuisine](../../itsalive/FOOD_WIKI.md#postes-de-cuisine).
## 065 — Le livre culinaire possède 64 pages à découvrir
- **Format / durée :** découverte, 15 s.
- **Accroche :** « Les recettes ne sont pas toutes connues au départ. »
- **Plans :** livre presque vide ; obtention d'une page ; nouvelle recette révélée ; livre posé sur un pupitre et lu par un autre joueur.
- **Texte / voix :** « Le catalogue est personnel dans l'inventaire et temporairement partageable sur un pupitre. »
- **Sortie :** `SANCTUARY / LIVRE 065`.
- **Source :** [64 pages et partage du livre](../../itsalive/FOOD_WIKI.md#les-64-pages-de-recettes).
## 066 — Canaplia possède seize familles
- **Format / durée :** collection, 16 s.
- **Accroche :** « Seize plantes. Seize découvertes. Seize effets. »
- **Plans :** grille ou travelling de cultures matures ; trois têtes et résines en gros plan ; Livre interdit refermé.
- **Texte / voix :** « Chaque famille possède sa culture, sa progression et son profil serveur. »
- **Sortie :** `SANCTUARY / CANAPLIA 066`.
- **Source :** [migration Canaplia](../../CANAPLIA.md).
## 067 — Le climat décide où découvrir Canaplia
- **Format / durée :** exploration, 15 s.
- **Accroche :** « Toutes les plantes ne peuvent pas apparaître partout. »
- **Plans :** recherche dans un biome incompatible ; voyage vers le bon climat ; découverte de la variante sauvage.
- **Texte / voix :** « Les sources sont distribuées par climat. Ember reste au Nether. Indigo reste dans l'End. »
- **Sortie :** `SANCTUARY / CANAPLIA 067`.
- **Source :** [découvertes climatiques de l'alpha.208](../../CHANGELOG.md#2620-alpha208).
## 068 — Une hybridation vérifie réellement ses parents
- **Format / durée :** expérience, 15 s.
- **Accroche :** « Deux plantes côte à côte ne suffisent pas. »
- **Plans :** hybridation refusée dans de mauvaises conditions ; correction de l'humidité, de la lumière ou de l'heure ; résultat obtenu.
- **Texte / voix :** « Le serveur revalide les parents matures, l'eau, le climat, la lumière et le moment. »
- **Sortie :** `SANCTUARY / CANAPLIA 068`.
- **Source :** [six hybridations validées côté serveur](../../CHANGELOG.md#2620-alpha208).
## 069 — Dix têtes deviennent une résine
- **Format / durée :** transformation, 12 s.
- **Accroche :** « Dix têtes identiques. Une seule résine. »
- **Plans :** dix objets réunis ; transformation ; application et aura persistante visualisée sobrement.
- **Texte / voix :** « L'aura dure 90 secondes et reste attachée à son état serveur. »
- **Sortie :** `SANCTUARY / CANAPLIA 069`.
- **Source :** [résines Canaplia de l'alpha.208](../../CHANGELOG.md#2620-alpha208).
## 070 — Un bad trip doit rester accessible
- **Format / durée :** note de conception, 16 s.
- **Accroche :** « Un effet psychédélique ne doit pas rendre le jeu injouable. »
- **Plans :** profil normal ; profil bad trip à intensité modérée ; écran des réglages montrant réduction des mouvements, wobble et ghosting.
- **Texte / voix :** « Ambiance compose un seul effet à la fois et laisse réduire ses mouvements visuels. »
- **Sortie :** `SANCTUARY / ACCESSIBILITÉ 070`.
- **Source :** [profils Canaplia et réglages Ambiance](../../CHANGELOG.md#2620-alpha208).
## 071 — Le Livre interdit est personnel, puis partageable
- **Format / durée :** interaction, 14 s.
- **Accroche :** « Ton livre connaît tes découvertes. Pas celles de tout le serveur. »
- **Plans :** deux joueurs avec catalogues différents ; pose sur pupitre ; consultation temporaire par le second joueur ; retrait du livre.
- **Texte / voix :** « Sur un pupitre, le savoir se partage. Dans l'inventaire, il redevient personnel. »
- **Sortie :** `SANCTUARY / CANAPLIA 071`.
- **Source :** [Livre interdit de Canaplia](../../CANAPLIA.md).
## 072 — Le ciel suit l'heure réelle du serveur
- **Format / durée :** time-lapse, 15 s.
- **Accroche :** « Ici, midi dans le jeu peut être midi dehors. »
- **Plans :** horloge système ou carton d'heure sobre ; ciel correspondant ; accéléré contrôlé montrant la continuité du cycle.
- **Texte / voix :** « Ambiance peut synchroniser le cycle du ciel sur le fuseau du serveur ou sur un décalage UTC fixe. »
- **Sortie :** `SANCTUARY / TEMPS 072`.
- **Source :** [runtime Realtime de l'alpha.126](../../CHANGELOG.md#2620-alpha126).
## 073 — Le calendrier commence le 17 mai 2009
- **Format / durée :** détail d'auteur, 11 s.
- **Accroche :** « Le premier jour de ce monde a une date. »
- **Plans :** joueur tenant une horloge ; apparition du HUD calendrier ; gros plan sur la date et le mois.
- **Texte / voix :** « L'époque minecraftienne de Sanctuary commence le 17 mai 2009 et possède douze mois. »
- **Sortie :** `SANCTUARY / CALENDRIER 073`.
- **Source :** [calendrier Realtime](../../CHANGELOG.md#2620-alpha126).
## 074 — La météo de la journée est déterministe
- **Format / durée :** une règle du monde, 15 s.
- **Accroche :** « La météo n'est choisie qu'une fois par jour. »
- **Plans :** matin clair ; transition vers brouillard ou pluie légère ; consultation d'un appareil de prévision.
- **Texte / voix :** « Éclaircies, ciel couvert, brouillard, pluie légère ou pluie normale composent le profil quotidien. »
- **Sortie :** `SANCTUARY / MÉTÉO 074`.
- **Source :** [profils météo de l'alpha.128](../../CHANGELOG.md#2620-alpha128) ; [appareils de prévision de l'alpha.131](../../CHANGELOG.md#2620-alpha131).
## 075 — Une pleine lune sur quatre devient rouge
- **Format / durée :** chronique nocturne, 16 s.
- **Accroche :** lune blanche pendant deux secondes, puis coupe vers la lune de sang.
- **Plans :** pleine lune et nuit blanche ; autre cycle en rouge ; plan du paysage sous la nouvelle ambiance.
- **Texte / voix :** « Le cycle vanilla reste sur huit jours. La première nuit de sang arrive au jour 24. »
- **Sortie :** `SANCTUARY / LUNE 075`.
- **Source :** [cycle lunaire de l'alpha.128](../../CHANGELOG.md#2620-alpha128).
## 076 — Le premier jour refuse la mauvaise météo
- **Format / durée :** une règle du monde, 10 s.
- **Accroche :** « Le premier jour doit rester lisible. »
- **Plans :** première apparition ; ciel clair ; accéléré jusqu'au coucher sans pluie ni brouillard.
- **Texte / voix :** « Le jour zéro est toujours clair. L'orage est rare et interdit pendant cette première journée. »
- **Sortie :** `SANCTUARY / MÉTÉO 076`.
- **Source :** [premier jour de l'alpha.128](../../CHANGELOG.md#2620-alpha128).
## 077 — Tout bloc lumineux tenu éclaire réellement
- **Format / durée :** avant / après, 12 s.
- **Accroche :** « La torche n'a pas besoin d'être posée. »
- **Plans :** grotte noire ; torche prise en main principale ; deuxième lumière tenue en main secondaire avec le terrain et les entités éclairés.
- **Texte / voix :** « La lumière locale reprend le niveau vanilla du bloc et décroît d'un niveau par bloc. »
- **Sortie :** `SANCTUARY / LUMIÈRE 077`.
- **Source :** [Dynamic Torches de l'alpha.130](../../CHANGELOG.md#2620-alpha130).
## 078 — La pluie ne doit pas simplement assombrir l'image
- **Format / durée :** avant / après conceptuel, 14 s.
- **Accroche :** « Une météo différente n'a pas besoin d'un filtre gris. »
- **Plans :** paysage normal ; même cadrage sous pluie légère ; même cadrage sous orage avec seulement la portée du brouillard transformée.
- **Texte / voix :** « Les profils météo modifient la visibilité sans écraser la colorimétrie du monde. »
- **Sortie :** `SANCTUARY / AMBIANCE 078`.
- **Source :** [signature visuelle de l'alpha.129](../../CHANGELOG.md#2620-alpha129).
## 079 — Un shader remplace proprement l'étalonnage final
- **Format / durée :** note de laboratoire, 14 s.
- **Accroche :** « Deux traitements d'image superposés donnent rarement une meilleure image. »
- **Plans :** Ambiance sans shader ; activation d'Iris ; rendu final sans double étalonnage.
- **Texte / voix :** « Quand Iris est actif, son traitement final remplace celui d'Ambiance au lieu de s'y empiler. »
- **Sortie :** `SANCTUARY / LABO 079`.
- **Source :** [compatibilité Iris de l'alpha.129](../../CHANGELOG.md#2620-alpha129).
## 080 — L'appareil photo capture l'image après le shader
- **Format / durée :** problème / Sanctuary, 14 s.
- **Accroche :** « Une photo doit ressembler à ce que le joueur voit. »
- **Plans :** scène avec shader ; visée Canon ; carte photo finale comparée au cadre avant le HUD.
- **Texte / voix :** « Canon capture après la composition finale d'Iris et avant l'interface. »
- **Sortie :** `SANCTUARY / PHOTO 080`.
- **Source :** [capture Canon compatible shaders](../../CHANGELOG.md#2620-alpha129).
## 081 — Le disque n'était pas trop grave à cause du pitch
- **Format / durée :** bug / correction, 18 s.
- **Accroche :** « Ce disque jouait trop lentement et trop grave. »
- **Plans :** extrait de test autorisé du symptôme historique ; carton très bref `stéréo 48 kHz → mono` ; lecture corrigée au même endroit.
- **Texte / voix :** « La source stéréo était mal convertie. Sanctuary moyenne maintenant chaque paire gauche-droite, avec une vitesse OpenAL normale. »
- **Sortie :** `SANCTUARY / LABO 081`.
- **Source :** [correction audio de l'alpha.221](../../CHANGELOG.md#2620-alpha221).
## 082 — Une demi-dalle peut ouvrir 128 conteneurs
- **Format / durée :** révélation fonctionnelle, 15 s.
- **Accroche :** « Ce terminal ne contient aucun objet. »
- **Plans :** terminal fixé sur un coffre ; recul révélant le réseau contigu ; ouverture de la grille agrégée.
- **Texte / voix :** « Il donne accès à un réseau borné de 128 coffres, coffres piégés ou barils chargés. »
- **Sortie :** `SANCTUARY / STOCKAGE 082`.
- **Source :** [terminal Red-Stoner de l'alpha.218](../../CHANGELOG.md#2620-alpha218).
## 083 — Le terminal affiche 54 types par page
- **Format / durée :** interaction, 12 s.
- **Accroche :** « Une page. Cinquante-quatre types. »
- **Plans :** première page ; défilement ; changement de page en conservant l'ordre des emplacements.
- **Texte / voix :** « La grille agrège les types d'objets, pas les cases physiques des coffres. »
- **Sortie :** `SANCTUARY / STOCKAGE 083`.
- **Source :** [pagination du terminal, alpha.218](../../CHANGELOG.md#2620-alpha218).
## 084 — La recherche du terminal comprend Unicode
- **Format / durée :** démonstration d'interface, 13 s.
- **Accroche :** « Accents et alphabets Unicode restent recherchables. »
- **Plans :** saisie d'un nom Unicode réellement présent dans le catalogue ; filtrage instantané ; retrait d'une pile sans déplacement des résultats restants.
- **Texte / voix :** « Les quantités deviennent compactes et les emplacements ne glissent plus lorsqu'une pile est vidée. »
- **Sortie :** `SANCTUARY / STOCKAGE 084`.
- **Source :** [recherche et stabilité du terminal, alpha.222](../../CHANGELOG.md#2620-alpha222).
## 085 — Le terminal se comporte comme un vrai conteneur
- **Format / durée :** problème / Sanctuary, 15 s.
- **Accroche :** « Pas de bouton propriétaire pour fermer. Pas de clics réinventés. »
- **Plans :** clic gauche ; clic droit ; shift-clic depuis une sixième rangée d'inventaire ; fermeture vanilla.
- **Texte / voix :** « Les gestes de Minecraft fonctionnent sur toutes les rangées débloquées. Le serveur décide où déposer chaque pile. »
- **Sortie :** `SANCTUARY / STOCKAGE 085`.
- **Source :** [menu de conteneur du terminal, alpha.220](../../CHANGELOG.md#2620-alpha220).
## 086 — Un clic molette trie sans toucher à la hotbar
- **Format / durée :** interaction cachée, 10 s.
- **Accroche :** « Ouvre un coffre. Clique sur la molette. »
- **Plans :** coffre volontairement désordonné ; clic molette ; rangement immédiat, puis gros plan prouvant que la hotbar n'a pas bougé.
- **Texte / voix :** « Coffres, barils, Shulkers, entonnoirs, distributeurs et inventaire joueur utilisent la même règle. »
- **Sortie :** `SANCTUARY / RANGEMENT 086`.
- **Source :** [tri natif de l'alpha.224](../../CHANGELOG.md#2620-alpha224).
## 087 — Un ordinateur Minecraft avec six ports redstone
- **Format / durée :** démonstration technique, 17 s.
- **Accroche :** « Ce bloc exécute réellement un programme assembleur. »
- **Plans :** saisie d'un programme court ; impulsion sur un port directionnel ; résultat sur lampe et comparateur.
- **Texte / voix :** « Programme borné, RAM, inventaire, six ports, impulsions et sortie comparateur : la machine virtuelle historique reste jouable. »
- **Sortie :** `SANCTUARY / RED-STONER 087`.
- **Source :** [ordinateur 8 bits](../../README.md#red-stoner-et-photographie).
## 088 — Seize pellicules, une photo sur carte
- **Format / durée :** collection, 16 s.
- **Accroche :** « L'appareil ne crée pas un fichier hors du jeu. »
- **Plans :** choix de trois pellicules contrastées parmi les seize ; visée ; trois cartes photo exposées côte à côte.
- **Texte / voix :** « Canon transforme la scène en objet Minecraft que l'on peut conserver et montrer. »
- **Sortie :** `SANCTUARY / CANON 088`.
- **Source :** [Canon et styles de pellicule](../../README.md#red-stoner-et-photographie).
## 089 — Un coffre-fort est une coque creuse de 26 blocs
- **Format / durée :** construction, 15 s.
- **Accroche :** « Ni un mur. Ni un cube plein. »
- **Plans :** structure incorrecte refusée ; coque 3 × 3 × 3 avec centre libre ; activation des textures sur les six faces.
- **Texte / voix :** « Les 26 Safe Blocks doivent appartenir au même joueur. Le centre peut cacher n'importe quel bloc. »
- **Sortie :** `SANCTUARY / COFFRE-FORT 089`.
- **Source :** [coque du coffre-fort, alpha.205](../../CHANGELOG.md#2620-alpha205).
## 090 — Pour braquer un coffre, il faut déjà en posséder un
- **Format / durée :** règle de braquage, 18 s.
- **Accroche :** « Un voleur doit mettre sa propre banque en jeu. »
- **Plans :** forage refusé sans coffre personnel ; construction ou preuve du coffre ; forage continu de 60 secondes et transfert final.
- **Texte / voix :** « Le propriétaire doit être connecté. Une réussite vole 20 %, consomme la foreuse, alerte la victime et protège le coffre pendant 24 heures. »
- **Sortie :** `SANCTUARY / BRAQUAGE 090`.
- **Source :** [forage et transfert de l'alpha.202](../../CHANGELOG.md#2620-alpha202) ; [condition de propriété de l'alpha.225](../../CHANGELOG.md#2620-alpha225).
## 091 — Le Creeperlock explose sans casser le monde
- **Format / durée :** arsenal, 12 s.
- **Accroche :** « Une explosion de combat, pas une explosion de terrain. »
- **Plans :** tir sur plusieurs cibles ; souffle et recul ; même décor intact après l'impact.
- **Texte / voix :** « Le Creeperlock consomme une poudre à canon et inflige des dégâts selon la distance, sans détruire de bloc. »
- **Sortie :** `SANCTUARY / OUCH 091`.
- **Source :** [Creeperlock](../../ouch/README.md#arsenal).
## 092 — La Mitraillette tire les métaux de ton inventaire
- **Format / durée :** arsenal, 14 s.
- **Accroche :** « La munition décide des dégâts. »
- **Plans :** pépites cuivre, fer, or, argent et titane alignées ; tir continu ; pépite tenue en main secondaire choisie en priorité.
- **Texte / voix :** « Cinq métaux, cinq puissances. Un tir toutes les cinq ticks jusqu'à 18 blocs. »
- **Sortie :** `SANCTUARY / OUCH 092`.
- **Source :** [Mitraillette](../../ouch/README.md#arsenal).
## 093 — L'Expulseur transforme ton inventaire en projectiles
- **Format / durée :** arsenal absurde, 13 s.
- **Accroche :** « Ce qui se trouve dans ton sac peut devenir une arme. »
- **Plans :** inventaire principal contenant les munitions improvisées ; jusqu'à huit objets projetés ; hotbar montrée intacte.
- **Texte / voix :** « Les objets restent physiques et récupérables. Les neuf cases de la barre d'action ne sont jamais consommées. »
- **Sortie :** `SANCTUARY / OUCH 093`.
- **Source :** [Expulseur](../../ouch/README.md#arsenal).
## 094 — L'Aspirateur retire aussi le bloc visé
- **Format / durée :** arsenal utilitaire, 13 s.
- **Accroche :** « Attirer les objets n'était que la moitié de la fonction. »
- **Plans :** objets au sol attirés dans un rayon de dix blocs ; visée d'un bloc cassable ; extraction et récupération.
- **Texte / voix :** « Il refuse les fluides, les block entities et toute interaction sans permission. »
- **Sortie :** `SANCTUARY / OUCH 094`.
- **Source :** [Aspirateur](../../ouch/README.md#arsenal).
## 095 — Chaque minerai brut change le Mining Rifle
- **Format / durée :** arsenal, 17 s.
- **Accroche :** « Cuivre, fer, or, argent ou titane : le fusil ne tire pas la même chose. »
- **Plans :** cinq munitions ; rechargement plus rapide avec l'or ; bonus argent sur un mort-vivant ; titane traversant une seconde cible.
- **Texte / voix :** « L'argent ajoute des dégâts aux morts-vivants. Le titane conserve 75 % des dégâts sur une seconde cible. »
- **Sortie :** `SANCTUARY / OUCH 095`.
- **Source :** [Mining Rifle](../../ouch/README.md#arsenal).
## 096 — L'Arc bêta tire quatre flèches par seconde
- **Format / durée :** arsenal historique, 13 s.
- **Accroche :** « Avant l'animation de charge, l'arc tirait immédiatement. »
- **Plans :** premier clic instantané ; maintien produisant quatre tirs par seconde ; relâchement sans projectile supplémentaire.
- **Texte / voix :** « Chaque tir consomme une vraie flèche vanilla et conserve ses effets compatibles. »
- **Sortie :** `SANCTUARY / OUCH 096`.
- **Source :** [Arc bêta](../../ouch/README.md#arsenal).
## 097 — Le Shop commence avec trois offres
- **Format / durée :** progression économique, 14 s.
- **Accroche :** « Le magasin n'est pas entièrement ouvert au début. »
- **Plans :** trois cartes actives et six verrouillées ; achat d'un emplacement ; grille progressivement étendue jusqu'à neuf offres.
- **Texte / voix :** « Les six emplacements supplémentaires coûtent 2, 4, 8, 12, 18 puis 25 niveaux. »
- **Sortie :** `SANCTUARY / SHOP 097`.
- **Source :** [progression du Shop, alpha.193](../../CHANGELOG.md#2620-alpha193).
## 098 — Le marché noir est construit avec de vrais panneaux
- **Format / durée :** interaction sociale, 18 s.
- **Accroche :** « Ici, une annonce existe parce qu'un panneau existe dans le monde. »
- **Plans :** vendeur tenant un objet et cliquant sur un panneau ; prix et pseudonyme visibles ; achat par un second joueur puis livraison Mailbox.
- **Texte / voix :** « L'objet, le paiement et les composants sont séquestrés et livrés par le serveur. Casser le panneau obéit à son propriétaire. »
- **Sortie :** `SANCTUARY / MARCHÉ 098`.
- **Source :** [Black Market physique](../../sanctuary/BLACK_MARKET.md#utilisation).
## 099 — Les joueurs peuvent écrire leurs propres quêtes
- **Format / durée :** interaction sociale, 18 s.
- **Accroche :** « KILL. BRING. PAY. Une quête écrite par un autre joueur. »
- **Plans :** création sur le panneau rose ; lecture et confirmation par un second joueur ; complétion puis arrivée de la récompense dans la Mailbox.
- **Texte / voix :** « La récompense est déposée dès la publication. Le serveur suit l'objectif et ne la livre qu'une fois. »
- **Sortie :** `SANCTUARY / QUÊTES 099`.
- **Source :** [demandes communautaires, alpha.201](../../CHANGELOG.md#2620-alpha201).
## 100 — Appuie sur J
- **Format / durée :** absurdité sèche, 9 s.
- **Accroche :** plan parfaitement sérieux du joueur, immobile au centre d'une pièce blanche.
- **Plans :** pression sur `J` ; action de six secondes ; boule créée, puis transformation de neuf boules en cube dans une coupe séparée.
- **Texte / voix :** « Sanctuary réinterprète aussi les mauvaises idées. »
- **Sortie :** `SANCTUARY / ONLY FUN 100`.
- **Source :** [action et cube](../../onlyfun/ABJECTION.md#abj-01--actions-serveur) ; [raccourci configurable](../../onlyfun/ABJECTION.md#abj-03--raccourci-configurable).
## Ordre de production recommandé
Ne pas tourner les vidéos dans leur ordre de publication. Les regrouper par décor réduit fortement le temps de capture :
1. **Laboratoire interface :** 003 à 019, 083 à 086 et 097.
2. **Archipel et worldgen :** 002, 020 à 027, 053 et 072 à 079.
3. **Alpha / maison Indev :** 028 à 031.
4. **Terrain de déplacement :** 032 à 039.
5. **Bestiaire contrôlé :** 040 à 059.
6. **Ferme et cuisine :** 060 à 071.
7. **Atelier Red-Stoner / Another World :** 080 à 090.
8. **Stand de tir Ouch :** 091 à 096.
9. **Place de serveur :** 098 à 100.
## Premier lot public conseillé
Avant de produire les cent vidéos, valider la grammaire avec douze épisodes très différents : **001, 003, 006, 014, 020, 031, 041, 053, 062, 077, 086 et 099**. Ils couvrent le manifeste, l'interface, une mécanique spectaculaire, l'exploration, une créature, l'agriculture, l'ambiance et le multijoueur sans nécessiter cent tournages pour apprendre ce qui fonctionne.
+6 -6
View File
@@ -6,7 +6,7 @@ org.gradle.configuration-cache=false
minecraft_version=26.2
loader_version=0.19.3
loom_version=1.17-SNAPSHOT
fabric_api_version=0.159.0+26.2
fabric_api_version=0.158.0+26.2
# Sanctuary 26.2
# Standby modules stay at zero until their migration really begins.
@@ -14,9 +14,9 @@ mod_version=0.0.0-alpha.0
module_lifecycle=standby
ambiance_version=0.0.0-alpha.22
ambiance_lifecycle=active
redstoner_version=0.0.0-alpha.20
redstoner_version=0.0.0-alpha.13
redstoner_lifecycle=active
anotherworld_version=0.0.0-alpha.58
anotherworld_version=0.0.0-alpha.56
anotherworld_lifecycle=active
itsalive_version=0.0.0-alpha.38
itsalive_lifecycle=active
@@ -26,9 +26,9 @@ masterkey_version=0.0.0-alpha.9
masterkey_lifecycle=active
iliketomoveit_version=0.0.0-alpha.18
iliketomoveit_lifecycle=active
onlyfun_version=0.0.0-alpha.29
onlyfun_version=0.0.0-alpha.27
onlyfun_lifecycle=active
sanctuary_version=0.0.0-alpha.140
sanctuary_version=0.0.0-alpha.130
sanctuary_lifecycle=active
pack_version=26.2.0-alpha.244
pack_version=26.2.0-alpha.226
maven_group=fr.koka99cab.sanctuary26
-1
View File
@@ -1261,7 +1261,6 @@ tasks.named("processResources") {
tasks.named("sourcesJar") {
dependsOn tasks.named("generateAgricultureResources")
dependsOn tasks.named("generateCanapliaResources")
}
tasks.register("verifyCulinaryReliability") {
-174
View File
@@ -1,174 +0,0 @@
from pathlib import Path
from PIL import Image
TRANSPARENT = -1
PEOPLE = [
("chrism", "ChrisM", "red_dye"),
("kaninscheun", "Kaninscheun", "yellow_dye"),
("poupoutain", "Poupoutain", "light_gray_dye"),
("lbeast", "lBeast", "brown_dye"),
("creveforest", "CreveForest", "green_dye"),
("sacdchips", "SacDChips", "orange_dye"),
("vaselline", "Vaselline", "white_dye"),
("duncan", "Duncan", "pink_dye"),
]
ROOT = Path(__file__).resolve().parents[2]
def rgb(pixel):
red, green, blue, _alpha = pixel
return (red << 16) | (green << 8) | blue
def extract(path):
image = Image.open(path).convert("RGBA")
face = []
hood = []
hood_top = []
inner_top = []
for y in range(8, 16):
for x in range(8, 16):
face.append(rgb(image.getpixel((x, y))))
for y in range(8, 16):
for x in range(40, 48):
red, green, blue, alpha = image.getpixel((x, y))
hood.append(TRANSPARENT if alpha < 128 else (red << 16) | (green << 8) | blue)
for y in range(6, 8):
for x in range(8, 16):
inner_top.append(rgb(image.getpixel((x, y))))
for y in range(6, 8):
for x in range(40, 48):
red, green, blue, alpha = image.getpixel((x, y))
if alpha < 128:
hood_top.append(inner_top[(y - 6) * 8 + (x - 40)])
else:
hood_top.append((red << 16) | (green << 8) | blue)
return face, hood, hood_top
def fmt(values):
lines = []
for index in range(0, len(values), 8):
chunk = values[index:index + 8]
parts = ["-1" if value == TRANSPARENT else f"0x{value:06X}" for value in chunk]
comma = "," if index + 8 < len(values) else ""
lines.append("\t\t" + ", ".join(parts) + comma)
return "\n".join(lines)
NOURRA_FACE = [
0x000000, 0x020202, 0x0A0A0A, 0x111111, 0x111111, 0x0A0A0A, 0x020202, 0x000000,
0x020202, 0x0A0A0A, 0x111111, 0x181818, 0x181818, 0x111111, 0x0A0A0A, 0x020202,
0x0A0A0A, 0x111111, 0x181818, 0x212121, 0x212121, 0x181818, 0x111111, 0x0A0A0A,
0x111111, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0x111111,
0x111111, 0x000000, 0x000000, 0xFFF0E1, 0xFFF0E1, 0x000000, 0x000000, 0x111111,
0x0A0A0A, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFFF0E1, 0x4A9DFC, 0xFFFFFF, 0x0A0A0A,
0x020202, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFDE8D7, 0x4A9DFC, 0xFFFFFF, 0x020202,
0x000000, 0xF0C6A5, 0xF7D5B9, 0xFCE1CA, 0xFCE1CA, 0xF7D5B9, 0xF0C6A5, 0x000000,
]
NOURRA_HOOD = [
0x000000, 0x0D0D0D, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0x0D0D0D, 0x000000,
0x0D0D0D, 0x707070, 0xD8D8D8, 0xE5E5E5, 0xE5E5E5, 0xD8D8D8, 0x707070, 0x0D0D0D,
0xCBCBCB, 0xD8D8D8, 0xFFFFFF, 0x212121, 0x212121, 0x181818, 0xD8D8D8, 0xCBCBCB,
0xD8D8D8, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0xD8D8D8,
0x111111, 0x181818, 0x212121, 0x2B2B2B, TRANSPARENT, TRANSPARENT, TRANSPARENT, 0x111111,
0x0A0A0A, 0x111111, 0x181818, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
0x020202, 0x0A0A0A, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
TRANSPARENT, 0x020202, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
]
NOURRA_TOP = [
0xBEBEBE, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xBEBEBE,
0xB8B8B8, 0xB8B8B8, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xB8B8B8, 0xB8B8B8,
]
def main():
entries = [("nourra", NOURRA_FACE, NOURRA_HOOD, NOURRA_TOP)]
for ident, _display, _dye in PEOPLE:
face, hood, top = extract(ROOT / "art" / "skins" / f"{ident}.png")
entries.append((ident, face, hood, top))
print(ident, "hood opaque", sum(1 for value in hood if value != TRANSPARENT))
out = ROOT / "src" / "main" / "java" / "fr" / "koka99cab" / "sanctuary26" / "onlyfun" / "item" / "SkinFacePortrait.java"
chunks = [
"package fr.koka99cab.sanctuary26.onlyfun.item;\n\n",
"import net.minecraft.world.item.ItemStack;\n\n",
"/** Baked 8x8 inner face, hood overlay and hood-top strip for each skin firework. */\n",
"public enum SkinFacePortrait {\n",
]
for index, (ident, face, hood, top) in enumerate(entries):
comma = "," if index + 1 < len(entries) else ";"
chunks.append(
f"\t{ident.upper()}(\"{ident}\", new int[] {{\n{fmt(face)}\n\t}}, new int[] {{\n{fmt(hood)}\n\t}}, new int[] {{\n{fmt(top)}\n\t}}){comma}\n"
)
chunks.append("""
public static final int WIDTH = 8;
public static final int HEIGHT = 8;
public static final int TRANSPARENT = -1;
private final String id;
private final int[] face;
private final int[] hood;
private final int[] hoodTop;
SkinFacePortrait(String id, int[] face, int[] hood, int[] hoodTop) {
this.id = id;
this.face = face;
this.hood = hood;
this.hoodTop = hoodTop;
}
public String id() {
return id;
}
public String itemId() {
return id + \"_firework\";
}
public int[] face() {
return face;
}
public int[] hood() {
return hood;
}
public int[] hoodTop() {
return hoodTop;
}
public int opaqueHoodPixels() {
int count = 0;
for (int color : hood) {
if (color != TRANSPARENT) count++;
}
return count;
}
public int[] portrait() {
int[] pixels = new int[WIDTH * (HEIGHT + 2)];
System.arraycopy(hoodTop, 0, pixels, 0, hoodTop.length);
for (int index = 0; index < face.length; index++) {
pixels[hoodTop.length + index] = hood[index] == TRANSPARENT ? face[index] : hood[index];
}
return pixels;
}
public int portraitHeight() {
return HEIGHT + hoodTop.length / WIDTH;
}
public static SkinFacePortrait of(ItemStack stack) {
if (stack.isEmpty() || !(stack.getItem() instanceof SkinFaceFireworkItem item)) return null;
return item.portrait();
}
}
""")
out.write_text("".join(chunks), encoding="utf-8")
print("wrote", out)
if __name__ == "__main__":
main()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1,47 +0,0 @@
from pathlib import Path
IDS = [
("chrism", "red_dye"),
("kaninscheun", "yellow_dye"),
("poupoutain", "light_gray_dye"),
("lbeast", "brown_dye"),
("creveforest", "green_dye"),
("sacdchips", "orange_dye"),
("vaselline", "white_dye"),
("duncan", "pink_dye"),
]
ROOT = Path(__file__).resolve().parents[2] / "src" / "main" / "resources"
def main():
for ident, dye in IDS:
(ROOT / "assets" / "onlyfun" / "items" / f"{ident}_firework.json").write_text(
'{\n\t"model": {\n\t\t"type": "minecraft:model",\n\t\t"model": "onlyfun:item/%s_firework"\n\t}\n}\n' % ident,
encoding="utf-8",
)
(ROOT / "assets" / "onlyfun" / "models" / "item" / f"{ident}_firework.json").write_text(
'{\n\t"parent": "minecraft:item/generated",\n\t"textures": {\n\t\t"layer0": "onlyfun:item/%s_firework"\n\t}\n}\n' % ident,
encoding="utf-8",
)
(ROOT / "data" / "onlyfun" / "recipe" / f"{ident}_firework.json").write_text(
"""{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:%s",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:%s_firework"
}
}
""" % (dye, ident),
encoding="utf-8",
)
print("wrote", ident)
if __name__ == "__main__":
main()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

@@ -1,102 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.client;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFacePortrait;
import fr.koka99cab.sanctuary26.onlyfun.mixin.client.ParticleLifetimeAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.core.particles.ColorParticleOption;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.phys.Vec3;
/** Paints the baked skin as a firework burst of coloured sparks, facing the player. */
public final class SkinFaceFireworkClient {
public static final double PIXEL = 1.85D;
public static final double LARGE_PIXEL = 2.7D;
public static final float SPARK_SIZE = 3.6F;
public static final float LARGE_SPARK_SIZE = 5.15F;
public static final int SPARK_LIFETIME_TICKS = 38;
private static final int SUBPIXELS = 2;
private SkinFaceFireworkClient() {
}
public static void explode(FireworkRocketEntity rocket) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || minecraft.particleEngine == null) return;
Vec3 origin = rocket.position();
RandomSource random = rocket.level().getRandom();
minecraft.level.playLocalSound(origin.x, origin.y, origin.z,
SoundEvents.FIREWORK_ROCKET_BLAST, SoundSource.AMBIENT,
20.0F, 0.95F + random.nextFloat() * 0.1F, true);
minecraft.level.addParticle(ColorParticleOption.create(ParticleTypes.FLASH, 0xFFFFFFFF),
origin.x, origin.y, origin.z, 0.0D, 0.0D, 0.0D);
Vec3 look = facing(minecraft, origin);
Vec3 right = look.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (right.lengthSqr() < 1.0E-6D) right = new Vec3(1.0D, 0.0D, 0.0D);
right = right.normalize();
Vec3 up = new Vec3(0.0D, 1.0D, 0.0D);
SkinFacePortrait portrait = SkinFacePortrait.of(rocket.getItem());
if (portrait == null) return;
int[] pixels = portrait.portrait();
int width = SkinFacePortrait.WIDTH;
int height = portrait.portraitHeight();
boolean large = largeBurst(rocket);
double pixel = burstPixel(large);
float sparkSize = burstSparkSize(large);
double midX = (width - 1) / 2.0D;
double midY = (height - 1) / 2.0D;
double step = 1.0D / SUBPIXELS;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int color = visibleSpark(pixels[row * width + col]);
DustParticleOptions dust = new DustParticleOptions(color, sparkSize);
for (int subY = 0; subY < SUBPIXELS; subY++) {
for (int subX = 0; subX < SUBPIXELS; subX++) {
double px = (col - midX) + (subX + 0.5D) * step - 0.5D;
double py = (midY - row) - (subY + 0.5D) * step + 0.5D;
Vec3 offset = right.scale(px * pixel).add(up.scale(py * pixel));
Vec3 pos = origin.add(offset);
Vec3 velocity = offset.scale(0.05D);
Particle spark = minecraft.particleEngine.createParticle(
dust, pos.x, pos.y, pos.z, velocity.x, velocity.y, velocity.z);
if (spark != null) {
((ParticleLifetimeAccessor) spark).onlyfun$setLifetime(SPARK_LIFETIME_TICKS);
}
}
}
}
}
}
public static boolean largeBurst(FireworkRocketEntity rocket) {
return (rocket.getUUID().getLeastSignificantBits() & 1L) != 0L;
}
public static double burstPixel(boolean large) {
return large ? LARGE_PIXEL : PIXEL;
}
public static float burstSparkSize(boolean large) {
return large ? LARGE_SPARK_SIZE : SPARK_SIZE;
}
private static Vec3 facing(Minecraft minecraft, Vec3 origin) {
Vec3 camera = minecraft.gameRenderer.mainCamera().position();
Vec3 horizontal = new Vec3(origin.x - camera.x, 0.0D, origin.z - camera.z);
if (horizontal.lengthSqr() < 1.0E-4D) return new Vec3(0.0D, 0.0D, 1.0D);
return horizontal.normalize();
}
private static int visibleSpark(int rgb) {
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;
if (red + green + blue < 24) return 0x2A2A2A;
return rgb;
}
}
@@ -1,23 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.item;
import java.util.function.Consumer;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.FireworkRocketItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipDisplay;
/** Firework rocket that bursts into the baked Nourra skin face. */
public final class NourraFireworkItem extends FireworkRocketItem {
public NourraFireworkItem(Properties properties) {
super(properties);
}
@Override
public void appendHoverText(ItemStack stack, TooltipContext context, TooltipDisplay display,
Consumer<Component> tooltip, TooltipFlag flag) {
tooltip.accept(Component.translatable("tooltip.onlyfun.nourra_firework").withStyle(ChatFormatting.AQUA));
super.appendHoverText(stack, context, display, tooltip, flag);
}
}
@@ -1,76 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.item;
import java.util.List;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.Fireworks;
/**
* Baked Nourra head (NameMC 6166a439e9354904): inner face, hood overlay, hood top.
* Other skin fireworks reuse the same flight rules via {@link SkinFacePortrait}.
*/
public final class SkinFaceFireworkExplosions {
public static final int WIDTH = 8;
public static final int HEIGHT = 8;
public static final int TRANSPARENT = -1;
/** Inner head front, top-left origin, row-major RGB. */
public static final int[] FACE = {
0x000000, 0x020202, 0x0A0A0A, 0x111111, 0x111111, 0x0A0A0A, 0x020202, 0x000000,
0x020202, 0x0A0A0A, 0x111111, 0x181818, 0x181818, 0x111111, 0x0A0A0A, 0x020202,
0x0A0A0A, 0x111111, 0x181818, 0x212121, 0x212121, 0x181818, 0x111111, 0x0A0A0A,
0x111111, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0x111111,
0x111111, 0x000000, 0x000000, 0xFFF0E1, 0xFFF0E1, 0x000000, 0x000000, 0x111111,
0x0A0A0A, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFFF0E1, 0x4A9DFC, 0xFFFFFF, 0x0A0A0A,
0x020202, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFDE8D7, 0x4A9DFC, 0xFFFFFF, 0x020202,
0x000000, 0xF0C6A5, 0xF7D5B9, 0xFCE1CA, 0xFCE1CA, 0xF7D5B9, 0xF0C6A5, 0x000000
};
/** Hat/hood overlay on the front; {@link #TRANSPARENT} keeps the inner face visible. */
public static final int[] HOOD = {
0x000000, 0x0D0D0D, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0x0D0D0D, 0x000000,
0x0D0D0D, 0x707070, 0xD8D8D8, 0xE5E5E5, 0xE5E5E5, 0xD8D8D8, 0x707070, 0x0D0D0D,
0xCBCBCB, 0xD8D8D8, 0xFFFFFF, 0x212121, 0x212121, 0x181818, 0xD8D8D8, 0xCBCBCB,
0xD8D8D8, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0xD8D8D8,
0x111111, 0x181818, 0x212121, 0x2B2B2B, TRANSPARENT, TRANSPARENT, TRANSPARENT, 0x111111,
0x0A0A0A, 0x111111, 0x181818, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
0x020202, 0x0A0A0A, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
TRANSPARENT, 0x020202, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT
};
/** Front edge of the hood top (skin overlay top rows closest to the face). */
public static final int[] HOOD_TOP = {
0xBEBEBE, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xBEBEBE,
0xB8B8B8, 0xB8B8B8, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xB8B8B8, 0xB8B8B8
};
/** Vanilla flight 3 minus half a gunpowder step, so the burst sits between 2 and 3. */
public static final int LIFETIME_TRIM_TICKS = 5;
private SkinFaceFireworkExplosions() {
}
public static Fireworks defaultFireworks() {
return new Fireworks(3, List.of());
}
public static int midFlightLifetime(int vanillaLifetime) {
return Math.max(1, vanillaLifetime - LIFETIME_TRIM_TICKS);
}
public static boolean isSkinFaceRocket(ItemStack stack) {
return SkinFacePortrait.of(stack) != null;
}
public static int opaqueHoodPixels() {
int count = 0;
for (int color : HOOD) {
if (color != TRANSPARENT) count++;
}
return count;
}
/** Hood top (2 rows) then the 8×8 face with the hood overlay composited. */
public static int[] portrait() {
return SkinFacePortrait.NOURRA.portrait();
}
public static int portraitHeight() {
return SkinFacePortrait.NOURRA.portraitHeight();
}
}
@@ -1,30 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.item;
import java.util.function.Consumer;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.FireworkRocketItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipDisplay;
/** Firework rocket that bursts into a baked skin face. */
public final class SkinFaceFireworkItem extends FireworkRocketItem {
private final SkinFacePortrait portrait;
public SkinFaceFireworkItem(SkinFacePortrait portrait, Properties properties) {
super(properties);
this.portrait = portrait;
}
public SkinFacePortrait portrait() {
return portrait;
}
@Override
public void appendHoverText(ItemStack stack, TooltipContext context, TooltipDisplay display,
Consumer<Component> tooltip, TooltipFlag flag) {
tooltip.accept(Component.translatable("tooltip.onlyfun.skin_face_firework").withStyle(ChatFormatting.AQUA));
super.appendHoverText(stack, context, display, tooltip, flag);
}
}
@@ -1,269 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.item;
import net.minecraft.world.item.ItemStack;
/** Baked 8x8 inner face, hood overlay and hood-top strip for each skin firework. */
public enum SkinFacePortrait {
NOURRA("nourra", new int[] {
0x000000, 0x020202, 0x0A0A0A, 0x111111, 0x111111, 0x0A0A0A, 0x020202, 0x000000,
0x020202, 0x0A0A0A, 0x111111, 0x181818, 0x181818, 0x111111, 0x0A0A0A, 0x020202,
0x0A0A0A, 0x111111, 0x181818, 0x212121, 0x212121, 0x181818, 0x111111, 0x0A0A0A,
0x111111, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0x111111,
0x111111, 0x000000, 0x000000, 0xFFF0E1, 0xFFF0E1, 0x000000, 0x000000, 0x111111,
0x0A0A0A, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFFF0E1, 0x4A9DFC, 0xFFFFFF, 0x0A0A0A,
0x020202, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFDE8D7, 0x4A9DFC, 0xFFFFFF, 0x020202,
0x000000, 0xF0C6A5, 0xF7D5B9, 0xFCE1CA, 0xFCE1CA, 0xF7D5B9, 0xF0C6A5, 0x000000
}, new int[] {
0x000000, 0x0D0D0D, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0x0D0D0D, 0x000000,
0x0D0D0D, 0x707070, 0xD8D8D8, 0xE5E5E5, 0xE5E5E5, 0xD8D8D8, 0x707070, 0x0D0D0D,
0xCBCBCB, 0xD8D8D8, 0xFFFFFF, 0x212121, 0x212121, 0x181818, 0xD8D8D8, 0xCBCBCB,
0xD8D8D8, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0xD8D8D8,
0x111111, 0x181818, 0x212121, 0x2B2B2B, -1, -1, -1, 0x111111,
0x0A0A0A, 0x111111, 0x181818, -1, -1, -1, -1, -1,
0x020202, 0x0A0A0A, -1, -1, -1, -1, -1, -1,
-1, 0x020202, -1, -1, -1, -1, -1, -1
}, new int[] {
0xBEBEBE, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xBEBEBE,
0xB8B8B8, 0xB8B8B8, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xB8B8B8, 0xB8B8B8
}),
CHRISM("chrism", new int[] {
0xF59589, 0xF89C90, 0xFEA398, 0xFEA398, 0xFCA297, 0xF89B8F, 0xF2978B, 0xF49488,
0xF59589, 0xFB9D91, 0xFA9D92, 0xFCA095, 0xFCA095, 0xFCA095, 0xF89B8F, 0xF49488,
0xF59589, 0xFB9A8E, 0xFD9E93, 0xFDA196, 0xFDA196, 0xFD9E93, 0xFB9A8E, 0xFF988B,
0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E,
0xFE8B7E, 0x000000, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0x000000, 0xFD8B7D,
0xF67D70, 0xF68375, 0xFA897C, 0xFE8B7E, 0xFE8B7E, 0xFE8B7E, 0xF68375, 0xF67D70,
0x000000, 0xFFFFFF, 0x000000, 0x000000, 0x000000, 0x000000, 0xFFFFFF, 0x000000,
0xEE6858, 0x000000, 0x000000, 0xFF1408, 0xFC0C00, 0x000000, 0x000000, 0xEE6858
}, new int[] {
0xFF8071, 0xFF8274, 0xFF796A, 0xFA7B6C, 0xFE7E70, 0xF97769, 0xFB7F70, 0xFF7A6B,
0xF56E5E, 0xFF9185, 0xFF8375, 0xFF9084, 0xFB8A7E, 0xFE9286, 0xFF8B7E, 0xF68274,
0xFA7263, 0xFF9488, 0xFE9084, 0xFE8C80, 0xFF9286, 0xF77F72, 0xF89084, 0xF97465,
0xFF8273, 0xFF8E82, 0xFA8D81, 0xF68477, 0xF69186, 0xFF8F83, 0xFD9286, 0xFF7767,
0xFF8173, 0xFB887B, 0xFC9084, 0xFF9083, 0xFF978B, 0xFF8375, 0xF48A7E, 0xFF8173,
0xFD7D6E, -1, 0xF5897D, 0xFF8C7F, 0xF3877A, 0xFF897C, -1, 0xFF7060,
0xFC7667, 0xFF897B, 0xFF9488, -1, -1, 0xF37F72, 0xF89186, 0xFD7566,
0xFF8375, 0xFF7F70, 0xF88072, 0xF88173, 0xFC7869, 0xFF8173, 0xFF8678, 0xFF7C6D
}, new int[] {
0xF97566, 0xFF8C7F, 0xF4867A, 0xFF877A, 0xF4786A, 0xFF8678, 0xF97E70, 0xFF7F71,
0xFF897B, 0xFB7566, 0xFF8C7F, 0xF77F72, 0xF77769, 0xFF7C6E, 0xF88578, 0xF87B6D
}),
KANINSCHEUN("kaninscheun", new int[] {
0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D,
0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xFFCC66, 0xEAD900, 0xEDDF2D,
0xEAD900, 0xEDDF2D, 0xEAD900, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xEDDF2D,
0xEAD900, 0x000000, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0x000000, 0xEDDF2D,
0xEAD900, 0x000000, 0x000000, 0xFFCC66, 0xFFCC66, 0x000000, 0x000000, 0xEDDF2D,
0xEAD900, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xFFCC66, 0xEDDF2D,
0xEAD900, 0xFFCC66, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xFFCC66, 0xEDDF2D,
0xEAD900, 0xEAD900, 0xEDDF2D, 0xFFCC66, 0xFFCC66, 0xEAD900, 0xEAD900, 0xEDDF2D
}, new int[] {
-1, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, -1,
0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900,
0xEDDF2D, 0xEAD900, 0xEDDF2D, -1, -1, -1, 0xEDDF2D, 0xEAD900,
0xEDDF2D, 0xEAD900, -1, -1, -1, -1, -1, 0xEAD900,
0xEDDF2D, -1, -1, -1, -1, -1, -1, 0xEAD900,
0xEDDF2D, -1, -1, -1, -1, -1, -1, 0xEAD900,
0xEDDF2D, -1, -1, -1, -1, -1, -1, 0xEAD900,
-1, 0xEAD900, -1, -1, -1, -1, 0xEDDF2D, -1
}, new int[] {
0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900,
0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900, 0xEDDF2D, 0xEAD900
}),
POUPOUTAIN("poupoutain", new int[] {
0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456,
0x4B5456, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0x4B5456,
0x4B5456, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0x342404, 0xE7E6E4, 0x4B5456,
0x4B5456, 0xE7E6E4, 0x342404, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0x4B5456,
0x4B5456, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0x4B5456,
0x4B5456, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0x342404, 0xE0DFDE, 0xE7E6E4, 0x4B5456,
0x4B5456, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0xE7E6E4, 0x4B5456,
0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456
}, new int[] {
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
}, new int[] {
0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456, 0x4B5456,
0x414B4C, 0x414B4C, 0x414B4C, 0x414B4C, 0x414B4C, 0x414B4C, 0x414B4C, 0x414B4C
}),
LBEAST("lbeast", new int[] {
0x8A6C30, 0x8A6B31, 0x644B1A, 0x5D4B1E, 0x896F37, 0x80672A, 0x614B1A, 0x4C3910,
0x957538, 0x816C34, 0x6D5521, 0x715B20, 0xA88C44, 0x9C7E3B, 0x7F6625, 0x7D6325,
0xB59F6D, 0xC1AC74, 0xC8B378, 0xD0BB7C, 0xD9C382, 0xE7CF8A, 0xD2BC7E, 0xC6A574,
0xB39C6C, 0x2B2324, 0x2B2324, 0xD5BA80, 0xD7BE80, 0x2B2324, 0x2B2324, 0xCBA977,
0xDCC285, 0xBAA0A6, 0x42597E, 0xF1D491, 0xD7BD7F, 0x42597E, 0xBAA0A6, 0xECC48B,
0xEACC8C, 0xFAF3F5, 0x87A6D2, 0xFDDB98, 0xDCBE81, 0x87A6D2, 0xFAF3F5, 0xF1C88E,
0xE4C082, 0xF6D695, 0xFBDD96, 0xFBE197, 0xFBE197, 0xFDDC95, 0xF6D695, 0xEBC288,
0xE9C183, 0xEFC68A, 0xF6D695, 0xFDDC94, 0xFBE197, 0xF6D695, 0xF4C98C, 0xDFB67D
}, new int[] {
0x7E652E, 0xC5A253, 0xC5A253, 0xBB984A, 0xD0A64E, 0xBB984A, 0xC5A253, 0x715B29,
0x947438, 0xEAC269, 0x96772C, 0xB69045, 0xD0A64E, 0xBB984A, 0xC5A253, 0x7E652E,
0xB49043, 0xD0A64E, -1, 0xD0A64E, 0xE4C368, 0xBB984A, 0xD0A64E, 0x8A6E32,
0x96772C, -1, -1, -1, -1, 0xBB9853, 0xD1AD5C, 0xB18D46,
0xD0A64E, -1, -1, -1, -1, -1, -1, 0x9C7D39,
-1, -1, -1, -1, -1, -1, -1, 0x9C7D39,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
}, new int[] {
0x846932, 0x907336, 0xA18340, 0xAE8E45, 0xAE8E45, 0x886F36, 0x7B6431, 0x6E5A2C,
0x7A6029, 0x886F36, 0x95793B, 0xA18340, 0xA18340, 0x95793B, 0x886F36, 0x7B6431
}),
CREVEFOREST("creveforest", new int[] {
0x2D4C54, 0x2D4C54, 0x2D4C54, 0x2D4C54, 0x355C5E, 0x2D4C54, 0x2D4C54, 0x2D4C54,
0x2D4C54, 0x2D4C54, 0x355C5E, 0x355C5E, 0x355C5E, 0x355C5E, 0x2D4C54, 0x2D4C54,
0x2D4C54, 0x355C5E, 0x79A88F, 0x8FC49B, 0x79A88F, 0x79A88F, 0x355C5E, 0x2D4C54,
0x355C5E, 0x79A88F, 0x8FC49B, 0x9ED69C, 0x8FC49B, 0x8FC49B, 0x79A88F, 0x355C5E,
0x355C5E, 0x1E2E33, 0x1E2E33, 0xB7E8A9, 0x9ED69C, 0x1E2E33, 0x1E2E33, 0x355C5E,
0x1E2E33, 0x9BB0AC, 0x344C46, 0xD3F5BA, 0xB7E8A9, 0x344C46, 0x9BB0AC, 0x1E2E33,
0x2D4C54, 0xEBEBEB, 0x79B8AB, 0xD3F5BA, 0xB7E8A9, 0x79B8AB, 0xEBEBEB, 0x355C5E,
0x2D4C54, 0x8FC49B, 0xB7E8A9, 0x9ED69C, 0xB7E8A9, 0xB7E8A9, 0x8FC49B, 0x355C5E
}, new int[] {
-1, 0x549C62, 0x4D8C64, 0x4D8C64, 0x549C62, 0x3D785D, 0x4D8C64, -1,
0x549C62, 0x549C62, 0x4D8C64, 0x4D8C64, 0x4D8C64, 0x3D785D, 0x4D8C64, 0x549C62,
0x4D8C64, 0x4D8C64, 0x3D785D, 0x3D785D, 0x3D785D, 0x355C5E, 0x3D785D, 0x4D8C64,
0x3D785D, 0x3D785D, 0x355C5E, -1, 0x355C5E, 0x2D4C54, 0x355C5E, 0x3D785D,
0x3D785D, -1, 0x2D4C54, -1, -1, -1, 0x355C5E, 0x3D785D,
0x355C5E, -1, -1, -1, -1, -1, -1, 0x355C5E,
0x2D4C54, -1, -1, -1, -1, -1, -1, 0x2D4C54,
-1, -1, -1, -1, -1, -1, -1, -1
}, new int[] {
0x355C5E, 0x355C5E, 0x355C5E, 0x355C5E, 0x3D785D, 0x355C5E, 0x2D4C54, 0x355C5E,
0x2D4C54, 0x549C62, 0x4D8C64, 0x4D8C64, 0x549C62, 0x3D785D, 0x4D8C64, 0x355C5E
}),
SACDCHIPS("sacdchips", new int[] {
0x3E1A05, 0x421D05, 0x391904, 0x421C06, 0x391904, 0x632C08, 0x3D1B05, 0x552607,
0x421D05, 0x421D05, 0x391904, 0xF1DACD, 0xF2DDD1, 0xEFD7C9, 0xEED4C5, 0x632C08,
0xEBCCBA, 0xECCFBE, 0xEDD1C1, 0xE7C3AE, 0xEDD1C1, 0xEED4C5, 0xF2DDD1, 0xECCFBE,
0xECCFBE, 0x341704, 0x341704, 0xE7C3AE, 0xEBCCBA, 0x341704, 0x341704, 0xF1DACD,
0xEBCCBA, 0xEEEEEE, 0x602F10, 0xEDD1C1, 0xF2DDD1, 0x602F10, 0xEEEEEE, 0xEDD1C1,
0xE7C3AE, 0xEDD1C1, 0xE7C3AE, 0xF2DDD1, 0xF1DACD, 0xEDD1C1, 0xE7C3AE, 0xEDD1C1,
0xE6C0AA, 0xECCFBE, 0xEBCCBA, 0x000000, 0x000000, 0xEDD1C1, 0xEAC9B6, 0xEBCCBA,
0xEAC9B6, 0xECCFBE, 0xF2DDD1, 0xECCFBE, 0xEAC9B6, 0xF1DACD, 0xECCFBE, 0xF1DACD
}, new int[] {
-1, 0x180A02, 0x261003, 0x2B1204, 0x341604, 0x210E03, 0x2F1404, -1,
-1, -1, -1, 0x2B1204, 0x210E03, 0x471E06, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, 0x261003, 0x180A02, 0x210E03, 0x1D0C02, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
}, new int[] {
0x3E1A05, 0x3E1A05, 0x210E03, 0x1D0C02, 0x341604, 0x180A02, 0x2F1404, 0x1D0C02,
0x391805, 0x421C06, 0x421C06, 0x3E1A05, 0x471E06, 0x1D0C02, 0x341604, 0x341604
}),
VASELLINE("vaselline", new int[] {
0xEBE176, 0xEBE176, 0xEBE176, 0xE8DC5F, 0xE8DC5F, 0xE8DC5F, 0xEBE176, 0xEBE176,
0xEBE176, 0xE8DC5F, 0xE8D7B1, 0xE8D7B1, 0xE8D7B1, 0xE8D7B1, 0xE8DC5F, 0xEBE176,
0xE8DC5F, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xE8DC5F,
0xE8DC5F, 0xFFFFFF, 0x6F9EE8, 0xE8D7B1, 0xE8D7B1, 0x6F9EE8, 0xFFFFFF, 0xE8DC5F,
0xE8DC5F, 0xE8D7B1, 0xE8D7B1, 0xEEE1C5, 0xEEE1C5, 0xE8D7B1, 0xE8D7B1, 0xE8DC5F,
0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5,
0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xE2CD9D, 0xE2CD9D, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5,
0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5, 0xEEE1C5
}, new int[] {
0xEBE176, 0xEBE176, 0xE8DC5F, 0xE8DC5F, 0xE8DC5F, 0xE8DC5F, 0xEBE176, 0xEBE176,
0xEBE176, 0xE8DC5F, -1, -1, -1, -1, 0xE8DC5F, 0xEBE176,
0xEBE176, -1, -1, -1, -1, -1, -1, 0xE8DC5F,
0xE8DC5F, -1, -1, -1, -1, -1, -1, 0xE8DC5F,
0xE8DC5F, -1, -1, -1, -1, -1, -1, 0xE8DC5F,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
}, new int[] {
0xEBE176, 0xE8DC5F, 0xE8DC5F, 0xEBE176, 0xEBE176, 0xE8DC5F, 0xE8DC5F, 0xE8DC5F,
0xEBE176, 0xEBE176, 0xEBE176, 0xEBE176, 0xEBE176, 0xEBE176, 0xEBE176, 0xEBE176
}),
DUNCAN("duncan", new int[] {
0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330,
0xFFD6AE, 0xFFD7AE, 0xFFD7AE, 0xFFD6AE, 0xFFD6AE, 0xFED7AE, 0xFFD7AF, 0xFFD7AE,
0xFFD7AE, 0xAD3B11, 0xAC3A10, 0xFFD7AE, 0xFFD7AE, 0xAD3B10, 0xAC3B11, 0xFFD6AF,
0xFED6AF, 0xFEFFFF, 0x783018, 0xFFD7AE, 0xFFD7AE, 0xFEFFFE, 0x793019, 0xFFD6AF,
0xFFD6AE, 0x783018, 0x010000, 0xFFD7AF, 0xFED7AE, 0x010100, 0x783018, 0xFFD7AF,
0xFED7AF, 0xFFD7AE, 0xFFD7AF, 0xFED6AF, 0xFED7AE, 0xFED7AE, 0xFED7AF, 0xFFD6AE,
0xFED7AE, 0xFED7AF, 0xFED7AE, 0xCB97A0, 0xCB96A0, 0xFED7AE, 0xFFD7AE, 0xFFD6AE,
0xFFD7AE, 0xFED7AE, 0xFFD7AE, 0xFFD7AF, 0xFFD7AE, 0xFFD7AE, 0xFFD7AE, 0xFED7AE
}, new int[] {
0x9D5330, 0xBC5D30, 0x9D5330, 0x88492A, 0x9D5330, 0x9D5330, 0x88492A, 0x9D5330,
0x9D5330, -1, 0x88492A, -1, -1, 0xBC5D30, -1, 0x9D5330,
-1, 0xE6AB35, 0xE7AA34, -1, -1, 0xE6AA34, 0xE7AA34, -1,
0xE6AB34, -1, -1, 0xE7AB35, 0xE7AB35, -1, -1, 0xE6AB35,
0xE7AB35, -1, -1, 0xE6AB35, 0xE6AA34, -1, -1, 0xE6AB35,
-1, 0xE6AB34, 0xE6AB35, -1, -1, 0xE6AB35, 0xE6AB34, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
}, new int[] {
0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330, 0x88492A, 0x9D5330, 0x9D5330, 0x88492A,
0x9D5330, 0x9D5330, 0x9D5330, 0xBC5D30, 0x9D5330, 0x9D5330, 0x9D5330, 0x9D5330
});
public static final int WIDTH = 8;
public static final int HEIGHT = 8;
public static final int TRANSPARENT = -1;
private final String id;
private final int[] face;
private final int[] hood;
private final int[] hoodTop;
SkinFacePortrait(String id, int[] face, int[] hood, int[] hoodTop) {
this.id = id;
this.face = face;
this.hood = hood;
this.hoodTop = hoodTop;
}
public String id() {
return id;
}
public String itemId() {
return id + "_firework";
}
public int[] face() {
return face;
}
public int[] hood() {
return hood;
}
public int[] hoodTop() {
return hoodTop;
}
public int opaqueHoodPixels() {
int count = 0;
for (int color : hood) {
if (color != TRANSPARENT) count++;
}
return count;
}
public int[] portrait() {
int[] pixels = new int[WIDTH * (HEIGHT + 2)];
System.arraycopy(hoodTop, 0, pixels, 0, hoodTop.length);
for (int index = 0; index < face.length; index++) {
pixels[hoodTop.length + index] = hood[index] == TRANSPARENT ? face[index] : hood[index];
}
return pixels;
}
public int portraitHeight() {
return HEIGHT + hoodTop.length / WIDTH;
}
public static SkinFacePortrait of(ItemStack stack) {
if (stack.isEmpty()) return null;
if (stack.getItem() instanceof SkinFaceFireworkItem item) return item.portrait();
if (stack.getItem() instanceof NourraFireworkItem) return NOURRA;
return null;
}
}
@@ -1,25 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.mixin;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(FireworkRocketEntity.class)
public abstract class FireworkRocketLifetimeMixin {
@Shadow
private int life;
@Shadow
private int lifetime;
@Inject(method = "tick", at = @At("HEAD"))
private void onlyfun$midFlightLifetime(CallbackInfo callback) {
if (this.life != 0) return;
FireworkRocketEntity rocket = (FireworkRocketEntity) (Object) this;
if (!SkinFaceFireworkExplosions.isSkinFaceRocket(rocket.getItem())) return;
this.lifetime = SkinFaceFireworkExplosions.midFlightLifetime(this.lifetime);
}
}
@@ -1,22 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.mixin.client;
import fr.koka99cab.sanctuary26.onlyfun.client.SkinFaceFireworkClient;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(FireworkRocketEntity.class)
public abstract class FireworkRocketEntityMixin {
@Inject(method = "handleEntityEvent", at = @At("HEAD"), cancellable = true)
private void onlyfun$drawSkinFace(byte id, CallbackInfo callback) {
if (id != 17) return;
FireworkRocketEntity rocket = (FireworkRocketEntity) (Object) this;
if (!rocket.level().isClientSide()) return;
if (!SkinFaceFireworkExplosions.isSkinFaceRocket(rocket.getItem())) return;
SkinFaceFireworkClient.explode(rocket);
callback.cancel();
}
}
@@ -1,11 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.mixin.client;
import net.minecraft.client.particle.Particle;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Particle.class)
public interface ParticleLifetimeAccessor {
@Accessor("lifetime")
void onlyfun$setLifetime(int lifetime);
}
@@ -8,7 +8,6 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
public final class OnlyFunCreativeTab {
@@ -29,10 +28,6 @@ public final class OnlyFunCreativeTab {
output.accept(OnlyFunRegistries.CAPE_LOOTBOX);
output.accept(OnlyFunRegistries.SPAWN_EGG_LOOTBOX);
output.accept(OnlyFunRegistries.ANOMALY_LOOTBOX);
output.accept(OnlyFunRegistries.NOURRA_FIREWORK);
for (Item firework : OnlyFunRegistries.skinFireworks()) {
if (firework != OnlyFunRegistries.NOURRA_FIREWORK) output.accept(firework);
}
output.accept(AbjectionRegistries.SHIT_BALL);
output.accept(AbjectionRegistries.SHIT_CUBE_ITEM);
})
@@ -3,10 +3,6 @@ package fr.koka99cab.sanctuary26.onlyfun.registry;
import fr.koka99cab.sanctuary26.onlyfun.OnlyFunMod;
import fr.koka99cab.sanctuary26.onlyfun.block.LuckyBlockBlock;
import fr.koka99cab.sanctuary26.onlyfun.item.LootboxItem;
import fr.koka99cab.sanctuary26.onlyfun.item.NourraFireworkItem;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkItem;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFacePortrait;
import fr.koka99cab.sanctuary26.onlyfun.loot.OnlyFunGenerator;
import fr.koka99cab.sanctuary26.onlyfun.loot.OnlyFunSource;
import java.util.function.Function;
@@ -36,17 +32,6 @@ public final class OnlyFunRegistries {
public static final Item CAPE_LOOTBOX = registerLootbox("cape_lootbox", OnlyFunSource.CAPE_LOOTBOX);
public static final Item SPAWN_EGG_LOOTBOX = registerLootbox("spawn_egg_lootbox", OnlyFunSource.SPAWN_EGG_LOOTBOX);
public static final Item ANOMALY_LOOTBOX = registerLootbox("anomaly_lootbox", OnlyFunSource.ANOMALY_LOOTBOX);
public static final Item NOURRA_FIREWORK = registerItem("nourra_firework",
properties -> new NourraFireworkItem(properties.stacksTo(64)
.component(DataComponents.FIREWORKS, SkinFaceFireworkExplosions.defaultFireworks())));
public static final Item CHRISM_FIREWORK = registerSkinFirework(SkinFacePortrait.CHRISM);
public static final Item KANINSCHEUN_FIREWORK = registerSkinFirework(SkinFacePortrait.KANINSCHEUN);
public static final Item POUPOUTAIN_FIREWORK = registerSkinFirework(SkinFacePortrait.POUPOUTAIN);
public static final Item LBEAST_FIREWORK = registerSkinFirework(SkinFacePortrait.LBEAST);
public static final Item CREVEFOREST_FIREWORK = registerSkinFirework(SkinFacePortrait.CREVEFOREST);
public static final Item SACDCHIPS_FIREWORK = registerSkinFirework(SkinFacePortrait.SACDCHIPS);
public static final Item VASELLINE_FIREWORK = registerSkinFirework(SkinFacePortrait.VASELLINE);
public static final Item DUNCAN_FIREWORK = registerSkinFirework(SkinFacePortrait.DUNCAN);
private static boolean initialized;
private OnlyFunRegistries() {
@@ -62,13 +47,6 @@ public final class OnlyFunRegistries {
};
}
public static Item[] skinFireworks() {
return new Item[] {
NOURRA_FIREWORK, CHRISM_FIREWORK, KANINSCHEUN_FIREWORK, POUPOUTAIN_FIREWORK,
LBEAST_FIREWORK, CREVEFOREST_FIREWORK, SACDCHIPS_FIREWORK, VASELLINE_FIREWORK, DUNCAN_FIREWORK
};
}
public static void initialize() {
if (initialized) {
return;
@@ -83,16 +61,6 @@ public final class OnlyFunRegistries {
OnlyFunMod.LOGGER.debug("[{}] Item and block registries initialized.", OnlyFunMod.DISPLAY_NAME);
}
private static Item registerSkinFirework(SkinFacePortrait portrait) {
return registerItem(portrait.itemId(), properties -> new SkinFaceFireworkItem(portrait,
properties.stacksTo(64).component(DataComponents.FIREWORKS, SkinFaceFireworkExplosions.defaultFireworks())));
}
private static Item registerItem(String path, Function<Item.Properties, Item> factory) {
ResourceKey<Item> key = ResourceKey.create(Registries.ITEM, OnlyFunMod.id(path));
return Registry.register(BuiltInRegistries.ITEM, key, factory.apply(new Item.Properties().setId(key)));
}
private static Item registerLootbox(String path, OnlyFunSource source) {
ResourceKey<Item> key = ResourceKey.create(Registries.ITEM, OnlyFunMod.id(path));
return Registry.register(BuiltInRegistries.ITEM, key,
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/chrism_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/creveforest_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/duncan_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/kaninscheun_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/lbeast_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/nourra_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/poupoutain_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/sacdchips_firework"
}
}
@@ -1,6 +0,0 @@
{
"model": {
"type": "minecraft:model",
"model": "onlyfun:item/vaselline_firework"
}
}
@@ -6,17 +6,6 @@
"item.onlyfun.cape_lootbox": "Cape Lootbox",
"item.onlyfun.spawn_egg_lootbox": "Spawn Egg Lootbox",
"item.onlyfun.anomaly_lootbox": "Anomaly Lootbox",
"item.onlyfun.nourra_firework": "Nourra Firework",
"item.onlyfun.chrism_firework": "ChrisM Firework",
"item.onlyfun.kaninscheun_firework": "Kaninscheun Firework",
"item.onlyfun.poupoutain_firework": "Poupoutain Firework",
"item.onlyfun.lbeast_firework": "lBeast Firework",
"item.onlyfun.creveforest_firework": "CreveForest Firework",
"item.onlyfun.sacdchips_firework": "SacDChips Firework",
"item.onlyfun.vaselline_firework": "Vaselline Firework",
"item.onlyfun.duncan_firework": "Duncan Firework",
"tooltip.onlyfun.nourra_firework": "The burst paints the skin face, at normal or larger size.",
"tooltip.onlyfun.skin_face_firework": "The burst paints the skin face, at normal or larger size.",
"item.onlyfun.quality_gear": "Quality Gear",
"item.onlyfun.impossible_tool": "Impossible Tool",
"item.onlyfun.impossible_book": "Impossible Book",
@@ -6,17 +6,6 @@
"item.onlyfun.cape_lootbox": "Lootbox de capes",
"item.onlyfun.spawn_egg_lootbox": "Lootbox d'œufs",
"item.onlyfun.anomaly_lootbox": "Lootbox anomalie",
"item.onlyfun.nourra_firework": "Feu d'artifice Nourra",
"item.onlyfun.chrism_firework": "Feu d'artifice ChrisM",
"item.onlyfun.kaninscheun_firework": "Feu d'artifice Kaninscheun",
"item.onlyfun.poupoutain_firework": "Feu d'artifice Poupoutain",
"item.onlyfun.lbeast_firework": "Feu d'artifice lBeast",
"item.onlyfun.creveforest_firework": "Feu d'artifice CreveForest",
"item.onlyfun.sacdchips_firework": "Feu d'artifice SacDChips",
"item.onlyfun.vaselline_firework": "Feu d'artifice Vaselline",
"item.onlyfun.duncan_firework": "Feu d'artifice Duncan",
"tooltip.onlyfun.nourra_firework": "L'explosion dessine le visage, en taille normale ou plus grande.",
"tooltip.onlyfun.skin_face_firework": "L'explosion dessine le visage, en taille normale ou plus grande.",
"item.onlyfun.quality_gear": "Équipement de qualité",
"item.onlyfun.impossible_tool": "Outil impossible",
"item.onlyfun.impossible_book": "Livre impossible",
@@ -6,17 +6,6 @@
"item.onlyfun.cape_lootbox": "Лутбокс плащей",
"item.onlyfun.spawn_egg_lootbox": "Лутбокс яиц призыва",
"item.onlyfun.anomaly_lootbox": "Лутбокс-аномалия",
"item.onlyfun.nourra_firework": "Фейерверк Нурры",
"item.onlyfun.chrism_firework": "Фейерверк ChrisM",
"item.onlyfun.kaninscheun_firework": "Фейерверк Kaninscheun",
"item.onlyfun.poupoutain_firework": "Фейерверк Poupoutain",
"item.onlyfun.lbeast_firework": "Фейерверк lBeast",
"item.onlyfun.creveforest_firework": "Фейерверк CreveForest",
"item.onlyfun.sacdchips_firework": "Фейерверк SacDChips",
"item.onlyfun.vaselline_firework": "Фейерверк Vaselline",
"item.onlyfun.duncan_firework": "Фейерверк Duncan",
"tooltip.onlyfun.nourra_firework": "Взрыв рисует лицо скина обычного или увеличенного размера.",
"tooltip.onlyfun.skin_face_firework": "Взрыв рисует лицо скина обычного или увеличенного размера.",
"item.onlyfun.quality_gear": "Качественное снаряжение",
"item.onlyfun.impossible_tool": "Невозможный инструмент",
"item.onlyfun.impossible_book": "Невозможная книга",
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/chrism_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/creveforest_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/duncan_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/kaninscheun_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/lbeast_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/nourra_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/poupoutain_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/sacdchips_firework"
}
}
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "onlyfun:item/vaselline_firework"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:red_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:chrism_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:green_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:creveforest_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:pink_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:duncan_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:yellow_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:kaninscheun_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:brown_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:lbeast_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:light_blue_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:nourra_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:light_gray_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:poupoutain_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:orange_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:sacdchips_firework"
}
}
@@ -1,13 +0,0 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"minecraft:firework_rocket",
"minecraft:white_dye",
"minecraft:paper"
],
"result": {
"count": 1,
"id": "onlyfun:vaselline_firework"
}
}
@@ -21,13 +21,6 @@
"fr.koka99cab.sanctuary26.onlyfun.client.OnlyFunClient"
]
},
"mixins": [
"onlyfun.mixins.json",
{
"config": "onlyfun.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.19.3",
"minecraft": "~26.2",
@@ -1,12 +0,0 @@
{
"required": true,
"package": "fr.koka99cab.sanctuary26.onlyfun.mixin.client",
"compatibilityLevel": "JAVA_25",
"client": [
"FireworkRocketEntityMixin",
"ParticleLifetimeAccessor"
],
"injectors": {
"defaultRequire": 1
}
}
@@ -1,11 +0,0 @@
{
"required": true,
"package": "fr.koka99cab.sanctuary26.onlyfun.mixin",
"compatibilityLevel": "JAVA_25",
"mixins": [
"FireworkRocketLifetimeMixin"
],
"injectors": {
"defaultRequire": 1
}
}
@@ -1,76 +0,0 @@
package fr.koka99cab.sanctuary26.onlyfun.item;
import fr.koka99cab.sanctuary26.onlyfun.client.SkinFaceFireworkClient;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.world.item.component.Fireworks;
/** Executable check for baked skin-face firework portraits. */
public final class SkinFaceFireworkSmoke {
private SkinFaceFireworkSmoke() {
}
public static void main(String[] args) {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
verify();
}
public static void verify() {
require(SkinFacePortrait.values().length == 9, "Skin firework roster drifted");
require(SkinFacePortrait.WIDTH == 8 && SkinFacePortrait.HEIGHT == 8,
"Face grid is no longer 8×8");
for (SkinFacePortrait portrait : SkinFacePortrait.values()) {
require(portrait.face().length == 64, portrait.id() + " face palette length drifted");
require(portrait.hood().length == 64, portrait.id() + " hood overlay length drifted");
require(portrait.hoodTop().length == 16, portrait.id() + " hood top strip length drifted");
int[] pixels = portrait.portrait();
require(pixels.length == 80 && portrait.portraitHeight() == 10,
portrait.id() + " portrait grid drifted");
require(portrait.itemId().equals(portrait.id() + "_firework"),
portrait.id() + " item id drifted");
}
require(SkinFacePortrait.NOURRA.face()[5 * 8 + 2] == 0x4A9DFC
&& SkinFacePortrait.NOURRA.face()[5 * 8 + 5] == 0x4A9DFC,
"Blue eyes are missing from the Nourra face");
require(SkinFacePortrait.NOURRA.face()[4 * 8 + 3] == 0xFFF0E1,
"Skin tone is missing from the baked Nourra face");
require(SkinFacePortrait.NOURRA.hood()[2] == 0xCBCBCB,
"Hood overlay is missing from the top of the Nourra face");
require(SkinFacePortrait.NOURRA.hood()[5 * 8 + 2] == 0x181818,
"Nourra hood no longer covers the left eye");
require(SkinFacePortrait.NOURRA.hood()[5 * 8 + 5] == SkinFacePortrait.TRANSPARENT,
"Nourra hood opening no longer leaves the right eye visible");
require(SkinFacePortrait.NOURRA.opaqueHoodPixels() == 43,
"Opaque Nourra hood pixel count drifted");
int[] nourra = SkinFacePortrait.NOURRA.portrait();
require(nourra[16 + 5 * 8 + 5] == 0x4A9DFC,
"Right eye is missing from the composited Nourra portrait");
require(nourra[2] == 0xD8D8D8,
"Hood top is missing from the Nourra portrait");
require(SkinFacePortrait.CHRISM.face()[6 * 8 + 1] == 0xFFFFFF,
"ChrisM eyes are missing from the baked face");
require(SkinFacePortrait.DUNCAN.face().length == 64,
"Duncan face is missing");
Fireworks fireworks = SkinFaceFireworkExplosions.defaultFireworks();
require(fireworks.flightDuration() == 3 && fireworks.explosions().isEmpty(),
"Default firework burst contract changed");
require(SkinFaceFireworkExplosions.LIFETIME_TRIM_TICKS == 5
&& SkinFaceFireworkExplosions.midFlightLifetime(45) == 40,
"Mid-flight lifetime trim drifted");
require(SkinFaceFireworkClient.PIXEL == 1.85D, "Portrait scale drifted");
require(SkinFaceFireworkClient.LARGE_PIXEL == 2.7D, "Large portrait scale drifted");
require(SkinFaceFireworkClient.burstPixel(false) == SkinFaceFireworkClient.PIXEL
&& SkinFaceFireworkClient.burstSparkSize(false) == SkinFaceFireworkClient.SPARK_SIZE,
"Normal burst scale drifted");
require(SkinFaceFireworkClient.burstPixel(true) == SkinFaceFireworkClient.LARGE_PIXEL
&& SkinFaceFireworkClient.burstSparkSize(true) == SkinFaceFireworkClient.LARGE_SPARK_SIZE,
"Large burst scale drifted");
require(SkinFaceFireworkClient.SPARK_LIFETIME_TICKS == 38, "Spark lifetime drifted");
System.out.println("Only Fun skin fireworks: 9 baked faces with hood overlay ready.");
}
private static void require(boolean condition, String message) {
if (!condition) throw new IllegalStateException(message);
}
}
@@ -1,6 +1,5 @@
package fr.koka99cab.sanctuary26.onlyfun.loot;
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkSmoke;
import fr.koka99cab.sanctuary26.onlyfun.shop.OnlyFunShopCatalogSmoke;
import fr.koka99cab.sanctuary26.onlyfun.world.CobbleDungeonPlacementSmoke;
import java.util.EnumMap;
@@ -19,7 +18,6 @@ public final class OnlyFunGeneratorSmoke {
Bootstrap.bootStrap();
OnlyFunShopCatalogSmoke.verify();
CobbleDungeonPlacementSmoke.verify();
SkinFaceFireworkSmoke.verify();
OnlyFunGenerator.initialize();
require(OnlyFunGenerator.ANOMALY_DENOMINATOR == 256, "Anomaly odds changed from 1/256");
require(OnlyFunGenerator.PITY_START == 24, "Pity threshold changed");
@@ -1,60 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.226",
"modules": {
"anotherworld": "0.0.0-alpha.56",
"sanctuary": "0.0.0-alpha.130"
}
},
"target": {
"pack_version": "26.2.0-alpha.227",
"modules": {
"anotherworld": "0.0.0-alpha.57",
"sanctuary": "0.0.0-alpha.131"
}
},
"backroom": {
"owner": "sanctuary",
"dimension": "sanctuary:backroom",
"entry_requires_sneaking_at_bed": true,
"exit_requires_bed": true,
"arrival_radius_blocks": 2000,
"entry_inventory_to_mailbox": true,
"entry_transfer_atomic": true,
"entry_transfer_idempotent": true,
"lost_items_persisted": true,
"lost_item_registry_bounded": true,
"renewable_chests": true,
"personal_losses_prioritized": true,
"shared_old_losses_supported": true,
"anotherworld_material_api": "BackroomMaterialApi"
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"sanctuary_source_data_version": 13,
"sanctuary_target_data_version": 14,
"sanctuary_network_protocol": 24,
"network_protocol_changed": false,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"fabric_api": {
"source": "0.158.0+26.2",
"target": "0.159.0+26.2",
"modrinth_version_id": "BgeCGgGZ"
},
"jei": {
"source": "30.29.0.198",
"target": "30.29.0.199",
"modrinth_version_id": "astSvyl6"
}
}
}
@@ -1,58 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.227",
"modules": {
"redstoner": "0.0.0-alpha.13",
"sanctuary": "0.0.0-alpha.131"
}
},
"target": {
"pack_version": "26.2.0-alpha.228",
"modules": {
"redstoner": "0.0.0-alpha.15",
"sanctuary": "0.0.0-alpha.132"
}
},
"backroom": {
"level_count": 5,
"minimum_y": -64,
"maximum_y_exclusive": 384,
"entry_floor_y": 367,
"monsters_disabled": true,
"arrival_torch": true,
"matching_vertical_transitions": true,
"connected_maze": true,
"dense_city_and_highways": true,
"three_dimensional_brick_grid": true,
"intentional_void_holes": true
},
"storage_terminal": {
"click_to_focus": true,
"inventory_key_closes": true,
"shulker_contents_searchable": true,
"shulker_remains_result": true,
"vanilla_shulker_preview_preserved": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": false,
"redstoner_data_version": 3,
"redstoner_network_protocol": 7,
"sanctuary_data_version": 14,
"sanctuary_network_protocol": 24,
"client_server_update_required": true,
"existing_backroom_chunks_preserved": true,
"new_backroom_generation_applies_to_new_chunks": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true
}
}
@@ -1,45 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.228",
"modules": {
"redstoner": "0.0.0-alpha.15"
}
},
"target": {
"pack_version": "26.2.0-alpha.229",
"modules": {
"redstoner": "0.0.0-alpha.16"
}
},
"storage_terminal_sorting": {
"owner": "redstoner",
"modes": [
"alphabetical",
"count_ascending",
"count_descending"
],
"server_authoritative": true,
"explicit_user_action": true,
"returns_to_first_page": true,
"periodic_refresh_preserves_order": true,
"periodic_refresh_preserves_hovered_stack": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": false,
"redstoner_data_version": 3,
"redstoner_network_protocol": 7,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true
}
}
@@ -1,44 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.229",
"modules": {
"sanctuary": "0.0.0-alpha.132",
"redstoner": "0.0.0-alpha.16"
}
},
"target": {
"pack_version": "26.2.0-alpha.230",
"modules": {
"sanctuary": "0.0.0-alpha.136",
"redstoner": "0.0.0-alpha.17"
}
},
"sanctuary_polish": {
"quest_completion_attribution": true,
"backroom_exit_beds_on_every_level": true,
"persistent_public_rankings": true,
"redstoner_trophy_panel": true,
"white_cape_shield_pattern_projection": true,
"adult_chicken_wolf_armor": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": true,
"network_protocols_changed": false,
"sanctuary_data_version": 15,
"sanctuary_network_protocol": 24,
"redstoner_data_version": 4,
"redstoner_network_protocol": 7,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true
}
}
@@ -1,56 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.230",
"modules": {
"sanctuary": "0.0.0-alpha.136",
"redstoner": "0.0.0-alpha.17"
}
},
"target": {
"pack_version": "26.2.0-alpha.231",
"modules": {
"sanctuary": "0.0.0-alpha.137",
"redstoner": "0.0.0-alpha.18"
}
},
"backroom_landmarks": {
"level_count": 5,
"loot_chest_cell_size_chunks": 3,
"exit_bed_source_cell_size_chunks": 10,
"exit_bed_target_cell_size_chunks": 3,
"books_added_to_loot": false,
"new_chunks_only": true
},
"trophy_panel": {
"wall_plate_model": "oak",
"readable_line_count": 4,
"page_title_and_three_podium_rows": true,
"right_click_next_page": true,
"sneak_right_click_previous_page": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": false,
"sanctuary_data_version": 15,
"sanctuary_network_protocol": 24,
"redstoner_data_version": 4,
"redstoner_network_protocol": 7,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"jei": {
"source": "30.29.0.199",
"target": "30.29.0.201",
"packwiz_and_prism_synchronized": true
}
}
}
@@ -1,58 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.231",
"modules": {
"anotherworld": "0.0.0-alpha.57",
"sanctuary": "0.0.0-alpha.137",
"redstoner": "0.0.0-alpha.18"
}
},
"target": {
"pack_version": "26.2.0-alpha.232",
"modules": {
"anotherworld": "0.0.0-alpha.58",
"sanctuary": "0.0.0-alpha.138",
"redstoner": "0.0.0-alpha.19"
}
},
"fixed_issues": [146],
"canon_photo_screenshots": {
"directory": "screenshots",
"viewfinder_crop_only": true,
"film_filter_preserved": true,
"full_crop_resolution": true,
"asynchronous_png_write": true,
"collision_safe_names": true
},
"alpha231_follow_up": {
"migration": "alpha231-follow-up-cape-terminal-trophy.json",
"banner_cape_fixed": true,
"terminal_inventory_key_fixed": true,
"trophy_panel_visible_lines": 8,
"trophy_panel_entries_per_view": 7,
"spawn_egg_kill_ranking": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": true,
"anotherworld_data_version": 5,
"anotherworld_network_protocol": 2,
"sanctuary_data_version": 15,
"sanctuary_network_protocol": 24,
"redstoner_data_version": 4,
"redstoner_source_network_protocol": 7,
"redstoner_target_network_protocol": 8,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true
}
}
@@ -1,51 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.234",
"temporary_until": "2026-09-07",
"pack": {
"source_version": "26.2.0-alpha.233",
"target_version": "26.2.0-alpha.234"
},
"modules": {
"zevent": {
"source_module_version": null,
"target_module_version": "0.0.0-alpha.2",
"data_version": 1,
"network_protocol": 1,
"client_and_server_required": true
}
},
"screen": {
"block_id": "zevent:zevent_screen",
"width_blocks": 9,
"height_blocks": 5,
"server_authoritative_channel": true,
"maximum_channel_input_length": 128,
"maximum_channel_name_length": 25,
"maximum_visible_browsers_per_client": 2,
"audible_distance_blocks": 64
},
"dependencies": {
"mcef_modern": {
"version": "0.3.3+mc26.2.jcef146.0.10",
"modrinth_project_id": "YJFfu5pH",
"modrinth_version_id": "FOXbUPNd",
"environment": "client_only"
}
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"existing_module_data_versions_changed": false,
"existing_module_network_protocols_changed": false,
"zevent_client_server_update_required": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"mcef_client_only": true
}
}
@@ -1,51 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.235",
"temporary_until": "2026-09-07",
"pack": {
"source_version": "26.2.0-alpha.234",
"target_version": "26.2.0-alpha.235"
},
"modules": {
"zevent": {
"source_module_version": "0.0.0-alpha.2",
"target_module_version": "0.0.0-alpha.3",
"data_version": 1,
"network_protocol": 1,
"client_and_server_required": true
}
},
"playback": {
"mcef_removed": true,
"native_hls_decoder": true,
"h264_video": true,
"aac_audio": true,
"maximum_quality_height": 720,
"automatic_retry_seconds": 30,
"offline_feedback": true
},
"dependencies": {
"dream_displays": {
"version": "1.9.6",
"modrinth_project_id": "4IIKQyBu",
"modrinth_version_id": "GxyW2ksW",
"environment": "client_and_server"
}
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"data_version_changed": false,
"network_protocol_changed": false,
"zevent_client_server_update_required": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"mcef_removed": true,
"dream_displays_required": true
}
}
@@ -1,43 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.236",
"pack": {
"source_version": "26.2.0-alpha.235",
"target_version": "26.2.0-alpha.236"
},
"removed_module": {
"id": "zevent",
"source_module_version": "0.0.0-alpha.3",
"target_module_version": null,
"source_directory_removed": true,
"client_jar_removed": true,
"server_jar_removed": true,
"registry_ids_removed": [
"zevent:zevent_screen"
]
},
"replacement": {
"module": "dreamdisplays",
"version": "1.9.6",
"modrinth_project_id": "4IIKQyBu",
"modrinth_version_id": "GxyW2ksW",
"client_and_server_required": true,
"automatic_screen_conversion": false
},
"compatibility": {
"world_reset_required": false,
"world_backup_recommended": true,
"existing_zevent_screens_must_be_replaced": true,
"remaining_module_data_preserved": true,
"remaining_network_protocols_changed": false,
"client_and_server_must_remove_zevent": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"zevent_jar_absent": true,
"dream_displays_required": true,
"distributed_module_count": 10
}
}
@@ -1,33 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.237",
"pack": {
"source_version": "26.2.0-alpha.236",
"target_version": "26.2.0-alpha.237"
},
"dependency": {
"id": "dreamdisplays",
"source_version": "1.9.6",
"target_version": "1.10.0-dev",
"target_filename": "dreamdisplays-fabric-26.2-audio-buffer-test.jar",
"sha256": "cea9efcea0401ebb44a471f43539c582ffae02fbd9b36d393c7aacdfc198ec74",
"client_and_server_required": true,
"pinned_test_build": true,
"audio_buffer_fix": true
},
"compatibility": {
"world_reset_required": false,
"save_migration_required": false,
"sanctuary_module_versions_changed": false,
"network_protocols_changed": false,
"client_and_server_must_update": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"old_dreamdisplays_jar_removed": true,
"zevent_jar_absent": true,
"distributed_module_count": 10
}
}
@@ -1,34 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.238",
"pack": {
"source_version": "26.2.0-alpha.237",
"target_version": "26.2.0-alpha.238"
},
"dependency": {
"id": "dreamdisplays",
"source_version": "1.10.0-dev",
"target_version": "1.10.0-dev",
"source_filename": "dreamdisplays-fabric-26.2-audio-buffer-test.jar",
"target_filename": "dreamdisplays-fabric-26.2-audio-buffer-test-2.jar",
"sha256": "fae5681de3209dba98d6359fb878e7cf230cde08e96c08fb71394aed35823188",
"client_and_server_required": true,
"pinned_test_build": true,
"audio_buffer_fix": true
},
"compatibility": {
"world_reset_required": false,
"save_migration_required": false,
"sanctuary_module_versions_changed": false,
"network_protocols_changed": false,
"client_and_server_must_update": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"old_dreamdisplays_jar_removed": true,
"zevent_jar_absent": true,
"distributed_module_count": 10
}
}
@@ -1,57 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.239",
"pack": {
"source_version": "26.2.0-alpha.238",
"target_version": "26.2.0-alpha.239"
},
"removed_dependencies": [
{
"id": "dreamdisplays",
"version": "1.10.0-dev",
"filename": "dreamdisplays-fabric-26.2-audio-buffer-test-2.jar"
}
],
"added_dependencies": [
{
"id": "mydisplay",
"version": "1.0.0",
"filename": "mydisplay-1.0.0-sanctuary.jar",
"sha256": "22cd63386bc7ba6961f586949f65b4a2a6cb335d75aa155bcdf0a633f1f9d907",
"side": "both",
"manifest_client_dependencies_optional": true,
"languages": ["en_us", "fr_fr", "ru_ru"]
},
{
"id": "watermedia",
"version": "3.0.0.23",
"filename": "watermedia-3.0.0.23.jar",
"sha256": "c3baefc8fbb005e349b3424f7df8f9d89b18524d5f7a358c6df73ca5e1d98b38",
"side": "client"
},
{
"id": "watermedia_binaries",
"version": "3.0.0.6",
"filename": "watermedia-binaries-3.0.0.6.jar",
"sha256": "2d9333061c3d2330033fe68837a31f546dbf58984455b0cfec0deeff6b1f121e",
"side": "client"
}
],
"compatibility": {
"world_reset_required": false,
"save_migration_required": false,
"sanctuary_module_versions_changed": false,
"network_protocols_changed": false,
"client_and_server_must_update": true,
"existing_dreamdisplays_blocks_converted": false
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"old_dreamdisplays_jar_removed": true,
"watermedia_client_only": true,
"zevent_jar_absent": true,
"distributed_module_count": 10
}
}
@@ -1,38 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.240",
"pack": {
"source_version": "26.2.0-alpha.239",
"target_version": "26.2.0-alpha.240"
},
"dependency": {
"id": "mydisplay",
"version": "1.0.0",
"source_filename": "mydisplay-1.0.0-sanctuary.jar",
"target_filename": "mydisplay-1.0.0-sanctuary-2.jar",
"source_sha256": "22cd63386bc7ba6961f586949f65b4a2a6cb335d75aa155bcdf0a633f1f9d907",
"target_sha256": "9ff1645731dc2dfdc248e3c07ac923aba9dfe001a1580a981767f948d4f7427f",
"side": "both",
"manifest_client_dependencies_optional": true,
"languages": ["en_us", "fr_fr", "ru_ru"],
"screen_picker_added": true
},
"compatibility": {
"world_reset_required": false,
"save_migration_required": false,
"sanctuary_module_versions_changed": false,
"network_protocols_changed": false,
"client_and_server_must_update": true,
"existing_mydisplay_blocks_compatible": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"old_mydisplay_jar_removed": true,
"direct_download_dependencies_skipped_by_modrinth_updater": true,
"watermedia_client_only": true,
"zevent_jar_absent": true,
"distributed_module_count": 10
}
}
@@ -1,53 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.241",
"pack": {
"source_version": "26.2.0-alpha.240",
"target_version": "26.2.0-alpha.241"
},
"dependency": {
"id": "mydisplay",
"version": "1.0.0",
"source_filename": "mydisplay-1.0.0-sanctuary-2.jar",
"target_filename": "mydisplay-1.0.0-sanctuary-3.jar",
"source_sha256": "9ff1645731dc2dfdc248e3c07ac923aba9dfe001a1580a981767f948d4f7427f",
"provided_sha256": "45a7dc0f9852ef776bce1ee81f1683b6333e773299b7e5eb96363cf885c8c6ef",
"target_sha256": "97a8b7fc79bb68d661e6d0563c54b90179e6a34339c22a433f858cdff6862cbc",
"side": "both",
"manifest_client_dependencies_optional": true,
"languages": ["en_us", "fr_fr", "ru_ru"]
},
"optimizations": {
"argb_to_rgba_int_buffer": true,
"distance_based_decode_lod": true,
"distant_live_quality_cap_after_five": true,
"proximity_prioritized_upload_budget": true,
"last_frame_kept_when_deferred": true,
"offscreen_cpu_upload_paused": true,
"draw_distance_blocks": 128,
"single_wall_render_pass": true,
"shared_streamlink_by_channel_and_quality": true,
"staggered_player_start": true,
"muted_audio_decoder_disabled": true,
"screenshot_render_buffer_reset": true,
"screenshot_upload_pause": true
},
"compatibility": {
"world_reset_required": false,
"save_migration_required": false,
"sanctuary_module_versions_changed": false,
"network_protocols_changed": false,
"client_and_server_must_update": true,
"existing_mydisplay_blocks_compatible": true,
"all_screens_remain_visible": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"old_mydisplay_jar_removed": true,
"watermedia_client_only": true,
"zevent_jar_absent": true,
"distributed_module_count": 10
}
}
@@ -1,43 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.242",
"pack": {
"source_version": "26.2.0-alpha.241",
"target_version": "26.2.0-alpha.242"
},
"dependency": {
"id": "mydisplay",
"version": "1.0.0",
"source_filename": "mydisplay-1.0.0-sanctuary-3.jar",
"target_filename": "mydisplay-1.0.0-sanctuary-4.jar",
"source_sha256": "97a8b7fc79bb68d661e6d0563c54b90179e6a34339c22a433f858cdff6862cbc",
"provided_sha256": "4c1bf4aa7140e613ff520b04263cb4733561c2e04b39a21b3cb7783f610119b9",
"target_sha256": "6c3bb4e7f562c6730a6cfa23a162a1fa0641aed7105cb6b809bb5a097aa2e559",
"side": "both",
"manifest_client_dependencies_optional": true,
"languages": ["en_us", "fr_fr", "ru_ru"],
"changed_classes": [
"com.mydisplay.client.media.DisplayPerf",
"com.mydisplay.client.media.DisplaySession",
"com.mydisplay.client.media.WaterMediaBackend",
"com.mydisplay.client.media.WaterMediaBackend$Playback"
]
},
"compatibility": {
"world_reset_required": false,
"save_migration_required": false,
"sanctuary_module_versions_changed": false,
"network_protocols_changed": false,
"client_and_server_must_update": true,
"existing_mydisplay_blocks_compatible": true
},
"distribution": {
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"old_mydisplay_jar_removed": true,
"watermedia_client_only": true,
"zevent_jar_absent": true,
"distributed_module_count": 10
}
}
@@ -1,58 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.242",
"modules": {
"sanctuary": "0.0.0-alpha.139",
"onlyfun": "0.0.0-alpha.27"
}
},
"target": {
"pack_version": "26.2.0-alpha.243",
"modules": {
"sanctuary": "0.0.0-alpha.140",
"onlyfun": "0.0.0-alpha.28"
}
},
"integrated_pull_requests": [172, 173, 174],
"shop_catalog": {
"local_search": true,
"accent_folding": true,
"grid_sizes": [3, 4, 5],
"grid_preference_persisted": true,
"page_counter": true,
"flash_sales_unchanged": true
},
"quest_board": {
"stale_sign_resynchronized_after_unloaded_rotation": true,
"operator_refresh_rerolls_contract": true,
"displayed_contract_matches_claimed_contract": true
},
"nourra_firework": {
"registry_id": "onlyfun:nourra_firework",
"skin_face_and_hood_particles": true,
"normal_and_large_bursts": true,
"vanilla_colored_burst_suppressed": true,
"languages": ["en_us", "fr_fr", "ru_ru"]
},
"compatibility": {
"existing_saves_supported": true,
"world_reset_required": false,
"save_migration_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": false,
"sanctuary_data_version": 15,
"sanctuary_network_protocol": 24,
"onlyfun_data_version": 2,
"onlyfun_network_protocol": 3,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"distributed_module_count": 10
}
}
@@ -1,62 +0,0 @@
{
"schema_version": 1,
"status": "released",
"source": {
"pack_version": "26.2.0-alpha.243",
"modules": {
"onlyfun": "0.0.0-alpha.28"
}
},
"target": {
"pack_version": "26.2.0-alpha.244",
"modules": {
"onlyfun": "0.0.0-alpha.29"
}
},
"integrated_pull_requests": [176],
"skin_fireworks": {
"existing_portrait_preserved": "onlyfun:nourra_firework",
"new_portraits": [
"onlyfun:chrism_firework",
"onlyfun:kaninscheun_firework",
"onlyfun:poupoutain_firework",
"onlyfun:lbeast_firework",
"onlyfun:creveforest_firework",
"onlyfun:sacdchips_firework",
"onlyfun:vaselline_firework",
"onlyfun:duncan_firework"
],
"portrait_size": [8, 8],
"hood_overlay": true,
"camera_facing_particles": true,
"normal_and_large_bursts": true,
"shapeless_recipes": {
"chrism": "minecraft:red_dye",
"kaninscheun": "minecraft:yellow_dye",
"poupoutain": "minecraft:light_gray_dye",
"lbeast": "minecraft:brown_dye",
"creveforest": "minecraft:green_dye",
"sacdchips": "minecraft:orange_dye",
"vaselline": "minecraft:white_dye",
"duncan": "minecraft:pink_dye"
},
"languages": ["en_us", "fr_fr", "ru_ru"]
},
"compatibility": {
"existing_saves_supported": true,
"world_reset_required": false,
"save_migration_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": false,
"onlyfun_data_version": 2,
"onlyfun_network_protocol": 3,
"client_server_update_required": true
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"distributed_module_count": 10
}
}
@@ -1,37 +0,0 @@
{
"schema_version": 1,
"status": "released",
"released_in": "26.2.0-alpha.232",
"modules": {
"sanctuary": {
"source_module_version": "0.0.0-alpha.137",
"target_module_version": "0.0.0-alpha.138",
"data_version": 15,
"network_protocol": 24
},
"redstoner": {
"source_module_version": "0.0.0-alpha.18",
"target_module_version": "0.0.0-alpha.19",
"data_version": 4,
"source_network_protocol": 7,
"target_network_protocol": 8
}
},
"fixes": {
"banner_cape_flat_shield_uv_faces": true,
"banner_cape_double_sided_culling": true,
"terminal_inventory_key_captured_while_searching": true,
"trophy_panel_visible_lines": 8,
"trophy_panel_entries_per_view": 7,
"trophy_panel_names_then_values": true,
"spawn_egg_maps_to_entity_killed_statistic": true
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"persistent_data_formats_changed": false,
"network_protocols_changed": true,
"client_server_update_required": true
}
}
@@ -1,38 +0,0 @@
{
"schema_version": 1,
"status": "released",
"module": "sanctuary",
"source_module_version": "0.0.0-alpha.130",
"target_module_version": "0.0.0-alpha.131",
"integration_modules": {
"anotherworld": {
"source": "0.0.0-alpha.56",
"target": "0.0.0-alpha.57",
"data_version_changed": false,
"network_protocol_changed": false
}
},
"source_data_version": 13,
"target_data_version": 14,
"migration": {
"kind": "monotone_addition",
"save_key": "sanctuary:backroom",
"existing_player_progress_preserved": true,
"existing_shop_data_preserved": true,
"existing_mailboxes_preserved": true,
"mailbox_transaction_history_default": [],
"backroom_loss_registry_default": [],
"backroom_chest_registry_default": []
},
"compatibility": {
"existing_saves_supported": true,
"new_world_required": false,
"save_reset_required": false,
"network_protocol_changed": false,
"registry_ids_added": [
"sanctuary:backroom",
"sanctuary:backroom_loss_id",
"sanctuary:backroom_architecture"
]
}
}

Some files were not shown because too many files have changed in this diff Show More