<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mathieu Ledru</title>
    <description>The latest articles on DEV Community by Mathieu Ledru (@matyo91).</description>
    <link>https://dev.to/matyo91</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F182978%2F61c41098-10ce-4833-acf3-b00cc42f852c.jpeg</url>
      <title>DEV Community: Mathieu Ledru</title>
      <link>https://dev.to/matyo91</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matyo91"/>
    <language>en</language>
    <item>
      <title>💫 GitHub Creator - Darkwood: huggingface/transformers</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Wed, 12 Aug 2026 10:05:41 +0000</pubDate>
      <link>https://dev.to/matyo91/github-creator-darkwood-huggingfacetransformers-3dni</link>
      <guid>https://dev.to/matyo91/github-creator-darkwood-huggingfacetransformers-3dni</guid>
      <description>&lt;h2&gt;
  
  
  À propos de ce créateur
&lt;/h2&gt;

&lt;p&gt;Découvrez les statistiques du dépôt huggingface/transformers pour la période récente et ce que cela signifie pour la communauté open-source.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Premium content.&lt;/b&gt;&lt;br&gt;⬆️ Upgrade to access full analysis.&lt;br&gt;👉 Subscribe here : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>👾 J'ai demandé à un agent d'IA de porter Diablo sur PHP.</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Wed, 12 Aug 2026 06:42:09 +0000</pubDate>
      <link>https://dev.to/matyo91/jai-demande-a-un-agent-dia-de-porter-diablo-sur-php-46j</link>
      <guid>https://dev.to/matyo91/jai-demande-a-un-agent-dia-de-porter-diablo-sur-php-46j</guid>
      <description>&lt;p&gt;En fait, cela ouvre une fenêtre.&lt;/p&gt;

&lt;p&gt;Vous pouvez vous promener dans les rues de la ville.&lt;/p&gt;

&lt;p&gt;Cliquez sur une porte et elle s'ouvre.&lt;/p&gt;

&lt;p&gt;Frappez un squelette dans la cathédrale.&lt;/p&gt;

&lt;p&gt;Chargez les données de jeu originales à partir d'un fichier que vous possédez déjà.&lt;/p&gt;

&lt;p&gt;Mais ce n'est pas la partie intéressante.&lt;/p&gt;

&lt;p&gt;Le plus intéressant, c'est que l'ensemble constitue un véritable moteur de jeu écrit en PHP&amp;nbsp;: un lecteur d'archives MPQ, des décodeurs CEL et CL2, un pipeline de tuiles isométriques, un algorithme de recherche de chemin A*, des affixes d'objets, une IA pour les monstres, un inventaire, des marchands, des missiles, des tables d'éclairage et une interface SDL2, le tout assemblé avec FFI. Ce n'est pas une simple démo. Ce n'est pas un diaporama de sprites. C'est un environnement d'exécution qui démarre, simule et affiche des graphismes.&lt;/p&gt;

&lt;p&gt;Cet article est une visite technique de ce dépôt de code source github — &lt;a href="https://github.com/matyo91/diablo-php" rel="noopener noreferrer"&gt;https://github.com/matyo91/diablo-php&lt;/a&gt; — écrit pour les développeurs PHP, les programmeurs de moteurs et tous ceux qui pensent que « moteur de jeu » et « langage proche de Symfony » ne peuvent pas partager une phrase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qu'est-ce que Diablo ?
&lt;/h2&gt;

&lt;p&gt;Diablo 1 est le jeu de rôle d'action isométrique classique que ce projet vise. Le dépôt ne contient ni les illustrations ni les niveaux de Blizzard. Il inclut un environnement d'exécution PHP qui nécessite l'utilisation d'un fichier &lt;code&gt;DIABDAT.MPQ&lt;/code&gt; obtenu légalement&amp;nbsp;— l'archive de données originale de Diablo 1.&lt;/p&gt;

&lt;p&gt;Ce que le code source implémente réellement révèle le type de jeu. On y trouve une ville centrale avec des PNJ avec lesquels interagir et auprès desquels acheter des objets. Différents types de donjons (cathédrale, catacombes, grottes, enfer) sont générés à partir de graines via des classes nommées &lt;code&gt;DrlgL1&lt;/code&gt;, &lt;code&gt;DrlgL2&lt;/code&gt;, &lt;code&gt;DrlgL3&lt;/code&gt; et &lt;code&gt;DrlgL4&lt;/code&gt;. Le jeu propose également des monstres dotés de modes d'action (immobilité, marche, attaque, coup, mort), des portes dont les cases changent de place à l'ouverture, des coffres et des tonneaux contenant des objets, des projectiles tels que des carreaux de feu et des flèches, une grille d'inventaire, des potions à la ceinture et un panneau de contrôle en bas de l'écran occupant 128 pixels d'une image tampon de 640×480 pixels.&lt;/p&gt;

&lt;p&gt;L'atmosphère de ce jeu n'est pas un mood board. Elle est le résultat du remappage des index de palette par l'éclairage via des tables lumineuses, de la révélation des cellules de la carte automatique par la vision, et des signaux audio qui extraient des fichiers WAV du même MPQ que celui utilisé pour les graphismes. Lorsque le joueur meurt, &lt;code&gt;GameState::tick&lt;/code&gt; bascule le mode en «&amp;nbsp;mort&amp;nbsp;», lance une animation de mort et joue le signal de mort. Voilà l'ambiance du jeu, exprimée par le code.&lt;/p&gt;

&lt;p&gt;Le projet est transparent quant à son niveau de maturité. L'aide en ligne de commande indique toujours que l'environnement d'exécution n'est pas entièrement jouable pour une évaluation humaine. Des simulations automatisées de fumée et de scénarios existent&amp;nbsp;; la fidélité visuelle pour la vente au détail reste un objectif en constante évolution. Cette transparence est essentielle. Ce qui suit décrit le contenu actuel du projet, et non prétend que chaque pixel de la cathédrale correspond à un fichier binaire des années&amp;nbsp;1990.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi PHP ?
&lt;/h2&gt;

&lt;p&gt;Si vous ne connaissez PHP que derrière nginx, ce projet ressemble à un défi.&lt;/p&gt;

&lt;p&gt;Regardez de plus près.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PHP 8.5+&lt;/strong&gt; est la version minimale requise dans &lt;code&gt;composer.json&lt;/code&gt;. L'environnement d'exécution s'appuie sur des propriétés typées, &lt;code&gt;match&lt;/code&gt;, des énumérations comme constantes et une structure PSR-4 sous l'espace de noms &lt;code&gt;Diablo&lt;/code&gt;. Il s'agit de la même évolution du langage que celle utilisée par les développeurs Symfony, appliquée ici à une boucle de jeu plutôt qu'à un noyau HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L'interface de ligne de commande&lt;/strong&gt; est le vecteur de distribution. &lt;code&gt;bin/diablo.php&lt;/code&gt; est un processus de longue durée avec &lt;code&gt;max_execution_time&lt;/code&gt; désactivé et une limite de mémoire de 512 Mo. Il n'y a pas de cycle de requête. Il y a une fenêtre, un cycle d'exécution, un rendu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FFI&lt;/strong&gt; sert d'interface. &lt;code&gt;Diablo\Platform\Sdl&lt;/code&gt; charge SDL2 via &lt;code&gt;FFI::cdef&lt;/code&gt;, crée une fenêtre et un moteur de rendu, gère les événements, charge les textures RGBA et affiche les images. Le code natif reste confiné à une seule classe. Le reste du moteur demeure en PHP&amp;nbsp;: décodage, simulation et composition.&lt;/p&gt;

&lt;p&gt;La &lt;strong&gt;portabilité&lt;/strong&gt; découle de cette séparation. Le pipeline des ressources est entièrement en PHP (&lt;code&gt;MpqArchive&lt;/code&gt;, &lt;code&gt;Cel&lt;/code&gt;, &lt;code&gt;Cl2&lt;/code&gt;, &lt;code&gt;DunMicro&lt;/code&gt;). La couche d'affichage utilise la version de SDL2 compatible avec votre machine. Sous macOS, la gestion audio optionnelle fait même appel à &lt;code&gt;afplay&lt;/code&gt; pour lire les fichiers WAV extraits du MPQ&amp;nbsp;— une solution de contournement pratique en attendant le traitement de SDL_mixer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L'expérimentation&lt;/strong&gt; est ce qui rend cette forme intéressante pour l'ingénierie assistée par IA. On peut vider une palette, figer la première image, comparer la simulation, le rendu et le décodage, masquer des personnages ou composer le monde sur un framebuffer CPU avec &lt;code&gt;--software-compose&lt;/code&gt;. Le langage qui héberge WordPress peut également gérer une boucle de blit isométrique. Une fois ce fait admis, nombre d'idées reçues sur les limitations du PHP s'effondrent.&lt;/p&gt;

&lt;p&gt;Le moteur de rendu n'utilise pas WebGL. Il s'agit principalement d'un décodage logiciel d'images 8 bits en RGBA, puis d'un chargement sur le GPU via des textures SDL — avec une option de composition logicielle complète pour les diagnostics. Ce choix technique est délibéré&amp;nbsp;: permettre l'inspection des pixels en PHP avant de faire confiance au GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exécuter Diablo en PHP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Exigences
&lt;/h3&gt;

&lt;p&gt;Extrait du fichier README du dépôt&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP 8.5+ avec &lt;code&gt;ext-ffi&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bibliothèque SDL2 installée sur l'hôte&lt;/li&gt;
&lt;li&gt;Un fichier &lt;code&gt;DIABDAT.MPQ&lt;/code&gt; obtenu légalement (non distribué avec le projet)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les ressources du jeu Blizzard ne sont &lt;strong&gt;pas&lt;/strong&gt; incluses. Vous devez posséder Diablo et récupérer le fichier &lt;code&gt;DIABDAT.MPQ&lt;/code&gt; depuis votre installation. Les portions de code du lecteur MPQ sont adaptées de mpqfs sous licence MIT. Le fichier &lt;code&gt;LICENSE&lt;/code&gt; du projet est sous licence MIT.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le chargement automatique associe &lt;code&gt;Diablo\&lt;/code&gt; à &lt;code&gt;src/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lancement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-d&lt;/span&gt; ffi.enable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;bin/diablo.php &lt;span class="nt"&gt;--data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/DIABDAT.MPQ"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou via l'environnement&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DIABLO_DATA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/DIABDAT.MPQ php &lt;span class="nt"&gt;-d&lt;/span&gt; ffi.enable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;bin/diablo.php &lt;span class="nt"&gt;--smoke&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Points d'entrée utiles&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-d&lt;/span&gt; ffi.enable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;bin/diablo.php &lt;span class="nt"&gt;--data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/DIABDAT.MPQ"&lt;/span&gt; &lt;span class="nt"&gt;--town&lt;/span&gt;
php &lt;span class="nt"&gt;-d&lt;/span&gt; ffi.enable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;bin/diablo.php &lt;span class="nt"&gt;--data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/DIABDAT.MPQ"&lt;/span&gt; &lt;span class="nt"&gt;--level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;--seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L'option &lt;code&gt;--smoke&lt;/code&gt; permet de jouer en ville, à la cathédrale et de sauvegarder sans avoir à lancer une session complète. L'option &lt;code&gt;--town&lt;/code&gt; lance une nouvelle partie en ville sans passer par le menu. Les options &lt;code&gt;--level&lt;/code&gt; et &lt;code&gt;--seed&lt;/code&gt; vous placent dans une cathédrale pré-initialisée. L'option &lt;code&gt;--help&lt;/code&gt; affiche une longue liste d'options de débogage (calques de rendu, arrêt sur image, couverture des tuiles, capture des combats, etc.).&lt;/p&gt;

&lt;p&gt;L'interface FFI doit être activée. Le lanceur vérifie &lt;code&gt;ext-ffi&lt;/code&gt; et &lt;code&gt;ini_get('ffi.enable')&lt;/code&gt; avant de construire &lt;code&gt;Diablo&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commandes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entrée&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;↑/↓ Entrée&lt;/td&gt;
&lt;td&gt;Menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cliquer&lt;/td&gt;
&lt;td&gt;Marcher / attaquer / opérer / parler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clic droit&lt;/td&gt;
&lt;td&gt;Éclair de feu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1–4&lt;/td&gt;
&lt;td&gt;Potion de ceinture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I / C&lt;/td&gt;
&lt;td&gt;Inventaire / personnage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E&lt;/td&gt;
&lt;td&gt;Équipement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H / F&lt;/td&gt;
&lt;td&gt;Soin / Éclair de feu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S&lt;/td&gt;
&lt;td&gt;Enregistrer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Échap&lt;/td&gt;
&lt;td&gt;Enregistrer + menu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La fonction &lt;code&gt;GameState::handleInput&lt;/code&gt; gère également les touches de quête, de grimoire et de carte automatique en mode jeu. Le ciblage par clic constitue l'interface principale&amp;nbsp;: le curseur détermine une action (marcher, attaquer un monstre, interagir avec un objet, parler, ramasser un objet, lancer un sort ou attaquer à distance), puis la simulation l'exécute au cours des ticks suivants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture du référentiel
&lt;/h2&gt;

&lt;p&gt;En bref :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Chemin&lt;/th&gt;
&lt;th&gt;Rôle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bin/diablo.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Point d'entrée CLI, analyse des options, sonde MPQ / fumée&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Diablo.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Application&amp;nbsp;: MPQ → ressources → SDL → &lt;code&gt;GameState&lt;/code&gt; → boucle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/GameState.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~14k lignes&amp;nbsp;: simulation + orchestration du rendu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Engine/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ressources, chemin, éclairage, vision, animation, actions de destination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Engine/Render/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Scrollrt&lt;/code&gt;, &lt;code&gt;DunMicro&lt;/code&gt;, &lt;code&gt;IsoCoords&lt;/code&gt;, masques, FB logiciel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Levels/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DungeonMap&lt;/code&gt;, &lt;code&gt;DrlgL1&lt;/code&gt;–&lt;code&gt;DrlgL4&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Items/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ItemDat&lt;/code&gt;, outils d'aide à la grille d'inventaire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Monsters/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Monstdat&lt;/code&gt;, &lt;code&gt;AiProc&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/DiabloUI/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boîte de dialogue titre, liste de menus, sélecteur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/Platform/Sdl.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limite de l'interface FFI SDL2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;assets/txtdata/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tables TSV des objets et des monstres&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Petits objets JSON (objets, sorts, monstres)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maps/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Carte de la ville (JSON)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;saves/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;slot1.json&lt;/code&gt; emplacement de sauvegarde&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Séquence de démarrage
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Diablo&lt;/code&gt; construit la pile à un seul endroit&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mpq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MpqArchive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dataPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AssetStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mpq&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sdl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sdl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GameState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La taille logique de la fenêtre provient de &lt;code&gt;IsoCoords::SCREEN_W&lt;/code&gt; / &lt;code&gt;SCREEN_H&lt;/code&gt; — 640×480. La boucle de jeu se trouve dans &lt;code&gt;Diablo::run&lt;/code&gt;&amp;nbsp;: interroger les événements SDL dans &lt;code&gt;GameState::handleInput&lt;/code&gt;, avancer &lt;code&gt;GameState::tick&lt;/code&gt;, appeler &lt;code&gt;GameState::render&lt;/code&gt;, présenter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Colonne vertébrale de simulation
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GameState::tick&lt;/code&gt; est le signal de présence lorsque &lt;code&gt;mode === 'play'&lt;/code&gt;&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processPlayer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processMonsters&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processMissiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processObjects&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;advanceTownerAnims&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;checkTriggers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;updateLighting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La mort est gérée dans le même cycle : effacer le chemin, définir &lt;code&gt;pmode&lt;/code&gt; sur &lt;code&gt;DEATH&lt;/code&gt;, passer en mode d'interface utilisateur &lt;code&gt;'dead'&lt;/code&gt;, envoyer un message au joueur, lire l'audio.&lt;/p&gt;

&lt;p&gt;L'arborescence contient des classes utilitaires plus légères (&lt;code&gt;Combat&lt;/code&gt;, &lt;code&gt;Player&lt;/code&gt;, &lt;code&gt;Monster&lt;/code&gt;, &lt;code&gt;Missile&lt;/code&gt;, &lt;code&gt;Spell&lt;/code&gt;). Le chemin actif concentre le comportement dans les tableaux et les méthodes de &lt;code&gt;GameState&lt;/code&gt;. Lors de la lecture de ce code, commencez par &lt;code&gt;GameState&lt;/code&gt;, puis explorez &lt;code&gt;Engine&lt;/code&gt; et &lt;code&gt;Levels&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface utilisateur
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DiabloUI\TitleDialog&lt;/code&gt; charge l'arrière-plan du titre et un logo multi-images au format PCX depuis le MPQ. &lt;code&gt;Menu&lt;/code&gt; gère les états tels que principal, créer, en cours de lecture, vendeur et mort. &lt;code&gt;UiList&lt;/code&gt; et &lt;code&gt;DrawSelector&lt;/code&gt; permettent de sélectionner l'élément. L'interface en jeu utilise &lt;code&gt;UiFont&lt;/code&gt; et des illustrations CEL du panneau de contrôle (&lt;code&gt;ctrlpan\panel8.cel&lt;/code&gt; est un des fichiers de test).&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture de rendu
&lt;/h2&gt;

&lt;p&gt;Le chemin de rendu en production n'utilise pas l'ancien assistant diamant dans &lt;code&gt;Renderer&lt;/code&gt; / &lt;code&gt;Iso&lt;/code&gt;. Le pipeline en direct est &lt;code&gt;GameState::render&lt;/code&gt; → rendu du donjon → &lt;code&gt;Scrollrt::drawGame&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Scrollrt&lt;/code&gt; documente son propre contrat&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DrawGame pipeline:
DrawFloor → DrawTileContent(DrawDungeon) → DrawOOB.
TILE 64×32, East +{1,-1}/+64px, zigzag rows, micro L/R, stack y-=32.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Espaces de coordonnées
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;IsoCoords&lt;/code&gt; désigne les espaces que le moteur gère :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cd"&gt;/**
 * Spaces:
 * - mega: dungeon[x][y] 40×40 mega tiles (DRLG)
 * - dPiece: dPiece[x][y] 112×112 piece tiles (ViewPosition lives here)
 * - micro: 32×32 (or triangle) CEL frames stacked on a piece
 * - screen: logical 640×(480−panel) framebuffer pixels
 * - ui: 640×480 DiabloUI rectangle
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Du monde à l'écran :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;worldToScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$dx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$dy&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dy&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$dx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$dx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La hauteur de la fenêtre d'affichage est de &lt;code&gt;480 - 128 = 352&lt;/code&gt; — le panneau possède la bande inférieure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Microtiles
&lt;/h3&gt;

&lt;p&gt;Un élément de donjon n'est pas une simple image bitmap. Il s'agit d'une pile de micro-éléments&amp;nbsp;: des blocs CEL de 32×32 (ou triangulaires) de types différents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_SQUARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_TRANSPARENT_SQUARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_LEFT_TRIANGLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_RIGHT_TRIANGLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_LEFT_TRAPEZOID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TYPE_RIGHT_TRAPEZOID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DunMicro::decode&lt;/code&gt; convertit les microoctets bruts et une &lt;code&gt;Palette&lt;/code&gt; (et une table lumineuse optionnelle) en RGBA. Les triangles de gauche sont décompressés avec un remplissage et des lignes élargies&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;decodeLeftTriangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$len&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Bottom-up 31 rows; widths 2,4,...32,...2 with 2 pad bytes before even rows&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$pos&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// padding&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nv"&gt;$width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$x0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$pos&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;$len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$x0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$pos&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Masques et transparence
&lt;/h3&gt;

&lt;p&gt;Après le décodage, &lt;code&gt;MaskType&lt;/code&gt; effectue un post-traitement RGBA&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SOLID&lt;/code&gt; — ne pas modifier les texels opaques&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TRANSPARENT&lt;/code&gt; — définir l'alpha à 128 pour le mélange&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GAUCHE&lt;/code&gt; / &lt;code&gt;DROITE&lt;/code&gt; — fusionner une région triangulaire préfixée pour que les murs se rejoignent proprement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le préfixe mathématique de gauche se développe à partir du bas de la tuile&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;leftTransparent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$fromBottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$fromBottom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$prefix&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prefix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dessiner une cellule
&lt;/h3&gt;

&lt;p&gt;La fonction &lt;code&gt;Scrollrt::drawCell&lt;/code&gt; détermine les bases du feuillage et des murs, sélectionne les masques gauche/droite à partir des indicateurs SOL et de la transparence de la pièce (&lt;code&gt;dTransVal&lt;/code&gt; / liste de transparence), puis trace les micro-images. Les sols peuvent dessiner des micro-images de feuillage décalées de -16 en Y. Les murs tracent les micro-images 0 et 1 côte à côte (&lt;code&gt;MICRO_WIDTH = 32&lt;/code&gt;), puis empilent les micro-images supérieures par groupes de 32 pixels.&lt;/p&gt;

&lt;p&gt;Les entités (joueurs, villageois, monstres, objets, éléments au sol, projectiles) sont affichées dans les passes de donjon grâce à leurs propres fonctions d'affichage. Des options comme &lt;code&gt;hideActors&lt;/code&gt;, &lt;code&gt;hideObjects&lt;/code&gt;, &lt;code&gt;hideItems&lt;/code&gt; et &lt;code&gt;hideHud&lt;/code&gt; permettent d'isoler la géométrie lors du débogage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deux chemins actuels
&lt;/h3&gt;

&lt;p&gt;Normalement, les images décodées deviennent des textures SDL (&lt;code&gt;createTextureRGBA&lt;/code&gt; / &lt;code&gt;updateTextureRGBA&lt;/code&gt;) et le moteur de rendu les présente.&lt;/p&gt;

&lt;p&gt;Avec l'option &lt;code&gt;--software-compose&lt;/code&gt;, les images sont d'abord déposées dans &lt;code&gt;SoftwareFramebuffer&lt;/code&gt;. Ce chemin permet d'analyser chaque pixel en PHP — y compris le JSON &lt;code&gt;RenderTrace&lt;/code&gt; indiquant qui a modifié quel pixel — avant le chargement final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Représentation mondiale
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Du générateur au réseau
&lt;/h3&gt;

&lt;p&gt;La génération du donjon commence dans un méga-espace (environ 40×40 cellules DRLG). Des générateurs comme &lt;code&gt;DrlgL1::createL5Dungeon&lt;/code&gt; placent les salles, les couloirs, les escaliers, les mini-décors (lampes, saletés, ombres), puis les déplacent par expansion vers &lt;code&gt;dPiece&lt;/code&gt; — une grille 112×112 d'identifiants de pièces. &lt;code&gt;DungeonMap&lt;/code&gt; contient&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dPiece&lt;/code&gt; — quelle pièce se trouve sur chaque tuile du monde&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dPieceMicros&lt;/code&gt; — définitions micro par pièce&lt;/li&gt;
&lt;li&gt;Drapeaux SOL — opaques, transparents, antimissile bloqué&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dTransVal&lt;/code&gt; — groupes de transparence des pièces/secteurs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dSpecial&lt;/code&gt; / indices d'éclairage&lt;/li&gt;
&lt;li&gt;des assistants comme &lt;code&gt;isWalkable&lt;/code&gt;, &lt;code&gt;isSolid&lt;/code&gt;, &lt;code&gt;blocksMissile&lt;/code&gt;, &lt;code&gt;isFloorTile&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La ville se charge via &lt;code&gt;DungeonMap::loadTown&lt;/code&gt;. La cathédrale, les catacombes, les grottes et l'enfer possèdent des chargeurs dédiés qui appellent le générateur &lt;code&gt;DrlgL*&lt;/code&gt; correspondant avec une graine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Occupation et vision
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Occupancy&lt;/code&gt; suit la position des personnages (joueurs, monstres, objets) grâce à des grilles avec des identifiants spécifiques pour les éléments en mouvement. &lt;code&gt;Vision&lt;/code&gt; optimise la visibilité pour la carte automatique. &lt;code&gt;Lighting&lt;/code&gt; gère une liste de sources lumineuses et crée des &lt;code&gt;LightTables&lt;/code&gt;&amp;nbsp;: l'ombrage est obtenu par &lt;strong&gt;réaffectation des index de palette&lt;/strong&gt;, et non par multiplication RVB. Cela correspond au fonctionnement de l'assombrissement dans les graphismes 8 bits d'origine&amp;nbsp;: inversion des index de couleur, puis recherche RVB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Défilement de la caméra et de la marche
&lt;/h3&gt;

&lt;p&gt;La caméra suit la position du joueur (dPiece). Lors des déplacements, &lt;code&gt;WalkOffset&lt;/code&gt; interpole un décalage d'un pixel par rapport à la progression de l'animation, de sorte que le sprite (et éventuellement la caméra) glisse entre les cases sur huit images de déplacement&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;MOVING_OFFSET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SW&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;E&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nc"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SE&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fromAnimInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AnimationInfo&lt;/span&gt; &lt;span class="nv"&gt;$anim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$cameraMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$anim&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAnimationProgress&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$ox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$oy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MOVING_OFFSET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;intdiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ox&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BASE_VALUE_FRACTION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;intdiv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$oy&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BASE_VALUE_FRACTION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cameraMode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$y&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$y&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Scrollrt&lt;/code&gt; agrandit la fenêtre de tuiles dessinées avec un surbalayage afin que les bords vides ne soient pas visibles lors du déplacement. &lt;code&gt;--camera-fixed&lt;/code&gt; fige la vue lorsque vous souhaitez isoler le mouvement du personnage du défilement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chargement des ressources
&lt;/h2&gt;

&lt;p&gt;Tout ce qui est visuel et la plupart des éléments audio commencent par un chemin à l'intérieur de &lt;code&gt;DIABDAT.MPQ&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  MPQ
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;MpqArchive&lt;/code&gt; est un lecteur MPQ v1&amp;nbsp;: il permet de trouver l’en-tête, de charger les tables de hachage et de blocs, de déchiffrer les secteurs avec &lt;code&gt;MpqCrypto&lt;/code&gt; et de décompresser avec &lt;code&gt;MpqExplode&lt;/code&gt; (PKWARE) ou zlib (selon l’option choisie). API publique&amp;nbsp;: &lt;code&gt;hasFile&lt;/code&gt;, &lt;code&gt;readFile&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;. Les sondes Smoke/Inspect analysent les chemins connus, tels que&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;levels\towndata\town.pal&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ctrlpan\panel8.cel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;towners\butch\deadguy.cel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;plrgfx\warrior\wld\wldas.cl2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AssetStore
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssetStore&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @var array&amp;lt;string,string&amp;gt; */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\\'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mpq&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;loadPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Palette&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;loadCel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Cel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Cel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les séparateurs de chemin sont normalisés en barres obliques inverses&amp;nbsp;; la mise en cache s’effectue par clé en minuscules. Les palettes comportent 256 entrées RVB. Les fichiers CEL et CL2 sont décodés en chaînes RGBA chargées par le moteur de rendu.&lt;/p&gt;

&lt;h3&gt;
  
  
  CEL
&lt;/h3&gt;

&lt;p&gt;CEL est le langage principal pour les panneaux d'interface utilisateur, les objets et de nombreux éléments graphiques du monde. &lt;code&gt;Cel::parse&lt;/code&gt; lit une table d'images (et gère les CEL groupés en supprimant l'en-tête de groupe lorsque les décalages ne correspondent pas à la taille du fichier). &lt;code&gt;decodeFrame&lt;/code&gt; parcourt le RLE&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;octets &lt;code&gt;≥ 0x80&lt;/code&gt; — séquence transparente (longueur signée)&lt;/li&gt;
&lt;li&gt;sinon — suite littérale des indices de palette&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les images sont stockées de bas en haut et inversées lors de la construction du RGBA. Les valeurs d'index 0 et nulles deviennent totalement transparentes. La fonction &lt;code&gt;lightTable&lt;/code&gt; (optionnelle) permet de réaffecter les index avant &lt;code&gt;Palette::rgb&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CL2
&lt;/h3&gt;

&lt;p&gt;CL2 est le format des feuilles d'animation pour les personnages et les monstres. Les fichiers peuvent être multi-groupes (généralement huit directions). &lt;code&gt;Cl2::parse&lt;/code&gt; détecte les groupes&amp;nbsp;; &lt;code&gt;selectGroup&lt;/code&gt; permet de changer la direction active&amp;nbsp;; &lt;code&gt;decodeFrame&lt;/code&gt; utilise un RLE (Relative Lineage Encode) à octets de contrôle différent de celui de CEL. Les tables lumineuses fonctionnent de la même manière.&lt;/p&gt;

&lt;h3&gt;
  
  
  PCX et polices
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Pcx&lt;/code&gt; décode les titres et les éléments graphiques de l'interface utilisateur, y compris les listes de sprites. &lt;code&gt;UiFont&lt;/code&gt; charge des bandes de polices de caractères à différentes tailles (&lt;code&gt;load42&lt;/code&gt;, &lt;code&gt;load24&lt;/code&gt;, &lt;code&gt;load16&lt;/code&gt;), mesure les chaînes de caractères et affiche les glyphes pour les menus et le texte de l'interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Des octets aux textures
&lt;/h3&gt;

&lt;p&gt;Pipeline en une phrase&amp;nbsp;: &lt;strong&gt;Lecture MPQ → (décryptage/décompression) → cache → analyse → décodage avec palette/lumière → masque optionnel → texture SDL ou transfert logiciel.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Les tables de données situées en dehors du MPQ se trouvent dans le répertoire &lt;code&gt;assets/txtdata/&lt;/code&gt; au format TSV&amp;nbsp;: &lt;code&gt;itemdat.tsv&lt;/code&gt;, les tables de préfixes/suffixes, &lt;code&gt;unique_itemdat.tsv&lt;/code&gt; et &lt;code&gt;monstdat.tsv&lt;/code&gt;. Ce sont des données de jeu appartenant à l’environnement d’exécution PHP&amp;nbsp;; les éléments graphiques restent dans le MPQ de l’utilisateur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Système d'animation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Timing
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;AnimationInfo&lt;/code&gt; est l'horloge partagée&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;BASE_VALUE_FRACTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setNewAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$numberOfFrames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$ticksPerFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$numSkippedFrames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;numberOfFrames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$numberOfFrames&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ticksPerFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ticksPerFrame&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;numberOfFrames&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$numSkippedFrames&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tickCounterOfCurrentFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;processAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$reverse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tickCounterOfCurrentFrame&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tickCounterOfCurrentFrame&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;ticksPerFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tickCounterOfCurrentFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;currentFrame&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or wrap / reverse&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La fonction &lt;code&gt;getAnimationProgress&lt;/code&gt; renvoie une valeur comprise entre 0 et 128, utilisée pour le décalage des déplacements et le défilement fluide. L'équipement d'attaque rapide/récupération rapide peut sauter des images lors du lancement des animations de coup ou d'attaque&amp;nbsp;; le système d'affixes gère cela via &lt;code&gt;GameState&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fiches de joueur
&lt;/h3&gt;

&lt;p&gt;L'affichage du joueur est géré par &lt;code&gt;plrgfx\{class}\…&lt;/code&gt;, les caractères d'armure et d'arme étant encodés dans le préfixe du chemin, suivis des suffixes de mode tels que se tenir debout/marcher en ville ou dans un donjon, attaquer, toucher, mourir, lancer un sort, bloquer. &lt;code&gt;GameState&lt;/code&gt; conserve des descripteurs CL2 distincts pour ces modes et met en cache les images décodées par direction. &lt;code&gt;warmRenderCaches&lt;/code&gt; pré-décode les actions se tenir debout, marcher, attaquer, toucher, bloquer, lancer un sort et mourir pour les huit directions après le chargement, afin d'éviter tout problème d'affichage lors du premier coup porté en combat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monstres
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Monstdat&lt;/code&gt; ingère &lt;code&gt;monstdat.tsv&lt;/code&gt; et construit des chemins CL2 comme &lt;code&gt;monsters\{suffix}{n|w|a|h|d}.cl2&lt;/code&gt; pour les actions suivantes&amp;nbsp;: se tenir debout, marcher, attaquer, toucher et mourir. &lt;code&gt;GameState::tryLoadMonsterCl2&lt;/code&gt; inverse la lettre du mode et met en cache les feuilles de calcul du tableau des monstres. Le nombre d'images et les images de réussite des attaques proviennent des colonnes d'images/fréquence du fichier TSV.&lt;/p&gt;

&lt;h3&gt;
  
  
  Citadins et objets
&lt;/h3&gt;

&lt;p&gt;Les PNJ de la ville et de nombreux objets interactifs utilisent des animations CEL avancées grâce à &lt;code&gt;advanceTownerAnims&lt;/code&gt; / traitement des objets. Les portes sont particulières&amp;nbsp;: leur ouverture modifie souvent l’élément sous-jacent au lieu de simplement jouer une animation CEL décorative&amp;nbsp;; &lt;code&gt;setDoorStateOpen&lt;/code&gt; / &lt;code&gt;setDoorStateClosed&lt;/code&gt; permettent de gérer correctement les collisions avec le monde.&lt;/p&gt;

&lt;h3&gt;
  
  
  Machine à états
&lt;/h3&gt;

&lt;p&gt;Les modes de jeu incluent la position debout, différentes variantes de marche, l'attaque, l'attaque à distance, toucher, bloquer, lancer des sorts et la mort. Les monstres proposent un ensemble similaire. L'action de déplacement (DestAction) se situe au-dessus du mouvement&amp;nbsp;: l'intention principale (attaquer ce monstre, actionner cette porte) est conservée d'une itération à l'autre, tandis que les trajectoires et les animations de déplacement s'exécutent en arrière-plan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DestAction&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;NONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;WALK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;ATTACK_MON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;OPERATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;TALK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;PICKUP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;SPELL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;RATTACK_MON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Systèmes de jeu
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mouvement et recherche de chemin
&lt;/h3&gt;

&lt;p&gt;Click-to-move construit un chemin avec &lt;code&gt;Diablo\Engine\Path&lt;/code&gt; — A* avec un coût d'axe de 100, une diagonale de 101, une longueur maximale de 25, un rappel de coupe de coin optionnel&amp;nbsp;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$sx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$sy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$gx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$gy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;callable&lt;/span&gt; &lt;span class="nv"&gt;$isWalkable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;?callable&lt;/span&gt; &lt;span class="nv"&gt;$canStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$maxPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MAX_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// open set sorted by g+h, eight neighbors, reconstruct when goal reached&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La fonction &lt;code&gt;GameState&lt;/code&gt; convertit la liste des tuiles en un chemin, lance les animations de déplacement, positionne le joueur sur la tuile suivante à la dernière image de &lt;code&gt;doWalk&lt;/code&gt;, puis tente de ramasser un objet et d'exécuter l'action suivante dans la file d'attente. La logique de poursuite actualise les chemins lorsqu'un monstre ciblé se déplace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combat
&lt;/h3&gt;

&lt;p&gt;Attaque au corps à corps&amp;nbsp;: &lt;code&gt;startAttack&lt;/code&gt; → animation d'attaque → frame d'impact → &lt;code&gt;hitMonster&lt;/code&gt; avec calcul des chances de toucher l'armure (y compris la perforation par &lt;code&gt;TARGAC&lt;/code&gt; / &lt;code&gt;plEnAc&lt;/code&gt;), jet de dégâts, vol de vie, repoussement et durabilité de l'arme. Attaque à distance&amp;nbsp;: &lt;code&gt;startRangeAttack&lt;/code&gt; / &lt;code&gt;RATTACK_MON&lt;/code&gt; invoque des projectiles de flèches. Les monstres ripostent via &lt;code&gt;applyMeleeHitToPlayer&lt;/code&gt;, avec animations de blocage et absorption du bouclier de mana lorsqu'il est actif.&lt;/p&gt;

&lt;p&gt;Les résistances sont plafonnées à 75 dans &lt;code&gt;recalc&lt;/code&gt;. Les résistances au feu, à la foudre et à la magie réduisent les dégâts élémentaires subis. Les dégâts des pièges réduits de moitié dépendent de l'équipement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Objets et portes
&lt;/h3&gt;

&lt;p&gt;Dans les thèmes Cathédrale (et autres), les portes occupent des cases et bloquent le passage lorsqu'elles sont fermées. Ouvrir ou fermer une porte modifie l'état de l'élément et déclenche un signal sonore. Les coffres, tonneaux, sanctuaires, pièges et éléments de décoration disposent d'aides au placement et de la fonction &lt;code&gt;operateObject&lt;/code&gt;. Les déclencheurs sur les escaliers permettent de passer d'un niveau à l'autre entre la ville et le donjon (&lt;code&gt;checkTriggers&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Articles et inventaire
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ItemDat&lt;/code&gt; charge les tables TSV et implémente la génération de drop&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filtrer par taux d'obtention et niveau des monstres&lt;/li&gt;
&lt;li&gt;Tirages uniques rares contre &lt;code&gt;unique_itemdat.tsv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sinon, objets de base ; possibilité de préfixe ou de suffixe magique&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;applyAffixPower&lt;/code&gt; associe les noms de pouvoirs aux champs &lt;code&gt;pl*&lt;/code&gt; et &lt;code&gt;effects[]&lt;/code&gt; (résistance au feu, précision, dégâts %, attributs, vol de vie, repoussement, attaque rapide, sorts de bâton, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La fonction &lt;code&gt;GameState::recalc&lt;/code&gt; additionne les bonus identifiés (ou de qualité normale) et les intègre aux statistiques du joueur, plafonne les résistances, ajuste le rayon de lumière et remet le mana à zéro lorsque &lt;code&gt;NOMANA&lt;/code&gt; est équipé. L'inventaire se compose d'un sac de 40 emplacements, plus la ceinture et les emplacements d'équipement&amp;nbsp;; &lt;code&gt;InventoryGrid&lt;/code&gt; prend en charge l'encombrement des objets. Le curseur contient l'image de l'objet provenant de &lt;code&gt;objcurs.cel&lt;/code&gt; via &lt;code&gt;Cursor&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sorts et projectiles
&lt;/h3&gt;

&lt;p&gt;Un clic droit et &lt;code&gt;F&lt;/code&gt; lancent un trait de feu&amp;nbsp;; &lt;code&gt;H&lt;/code&gt; soigne. Les charges de bâton peuvent remplacer le sort principal avec &lt;code&gt;castPrimarySpell&lt;/code&gt; / &lt;code&gt;tryCastNamedSpell&lt;/code&gt;. Les projectiles (&lt;code&gt;flèche&lt;/code&gt;, trait de feu, boule de feu, foudre&lt;code&gt;, …) se déplacent à chaque itération en vérifiant qu'ils ne traversent pas les cases pleines ou les blocs de projectiles. Les sorts Apocalypse, Enfer, Malédiction de pierre, Éclair, Passage à travers les murs et autres sorts associés sont disponibles dans&lt;/code&gt;GameState` pour les livres et les parchemins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commerçants et ville
&lt;/h3&gt;

&lt;p&gt;Le mode vendeur permet de gérer les interactions entre guérisseurs, marchands, forgerons, sorcières, Caïn et tavernes&amp;nbsp;: réparation, recharge, achat/vente. Les personnages restent immobiles pendant que vous parcourez les rues isométriques.&lt;/p&gt;

&lt;h3&gt;
  
  
  IA des monstres
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;AiProc::tick&lt;/code&gt; ne réagit que lorsqu'un monstre n'est pas déjà en train de marcher&amp;nbsp;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;php&lt;br&gt;
return match ($ai) {&lt;br&gt;
    'Skeleton', 'SkeletonBow', 'BoneDemon' =&amp;gt; self::skeletonAi(...),&lt;br&gt;
    'GoatMc', 'GoatBow', 'GoatLord' =&amp;gt; self::goatAi(...),&lt;br&gt;
    'Fallen' =&amp;gt; self::fallenAi(...),&lt;br&gt;
    'Scavenger' =&amp;gt; self::scavengerAi(...),&lt;br&gt;
    default =&amp;gt; self::zombieAi(...),&lt;br&gt;
};&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Vérifications sensorielles&amp;nbsp;: même transparence de la pièce &lt;em&gt;ou&lt;/em&gt; ligne de mire dégagée via &lt;code&gt;LineClear::notSolid&lt;/code&gt;. Les IA à distance nécessitent une ligne de mire dégagée pour les missiles. La planification des déplacements utilise &lt;code&gt;Path&lt;/code&gt; avec une longueur maximale courte&amp;nbsp;; le déplacement dure &lt;code&gt;WALK_FRAMES&lt;/code&gt; (8) avant que la case ne soit validée.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sauvegarde et audio
&lt;/h3&gt;

&lt;p&gt;La commande &lt;code&gt;saveGame&lt;/code&gt; enregistre la version &lt;strong&gt;2&lt;/strong&gt; du JSON dans &lt;code&gt;saves/slot1.json&lt;/code&gt;&amp;nbsp;: nom, classe, position, caractéristiques, or, XP, statistiques, inventaire, ceinture, équipement, graine du donjon, type de niveau, indicateurs de quête, bouclier de mana, temps d'infra/de recherche, niveaux de sorts. La commande &lt;code&gt;load&lt;/code&gt; restaure la partie et permet de revenir à la carte correspondante.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Audio&lt;/code&gt; associe les repères aux chemins MPQ WAV (&lt;code&gt;sfx\misc\walk1.wav&lt;/code&gt;, &lt;code&gt;swing.wav&lt;/code&gt;, &lt;code&gt;bfire.wav&lt;/code&gt;, …), met en cache les fichiers temporaires et, sous Darwin, peut les &lt;code&gt;afplay&lt;/code&gt; avec une limitation de 50 ms afin que le combat ne divise pas une centaine de joueurs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Défis de rendu
&lt;/h2&gt;

&lt;p&gt;Ces concepts n'ont rien d'exotique une fois qu'on a travaillé avec un moteur isométrique. Il est néanmoins facile de se tromper en PHP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Palette et lumière.&lt;/strong&gt; L'art est en 8 bits. Beauté et obscurité coexistent dans l'espace des index. &lt;code&gt;Lighting::makeLightTables&lt;/code&gt; génère des remappages&amp;nbsp;; les chemins de blit doivent s'aligner sur la bonne ligne, sinon tout paraît plat (&lt;code&gt;--fullbright&lt;/code&gt; sert précisément à déboguer la géométrie sans ombrage).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Topologie de microtuiles.&lt;/strong&gt; Carrés, carrés transparents, triangles gauche/droite, trapèzes, zones de feuillage, piles supérieures&amp;nbsp;: une erreur de remplissage et un mur se retrouve avec une dent noire. &lt;code&gt;ReencodeDungeonCels&lt;/code&gt; sert à normaliser les données des triangles avant le décodage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ordre des calques.&lt;/strong&gt; Sol d'abord, puis murs et entités, puis éléments spéciaux, puis remplissage hors limites. Si vous dessinez un joueur avant le mur derrière lequel il se trouve, la scène s'effondre. Les compteurs de passes de &lt;code&gt;Scrollrt&lt;/code&gt; (&lt;code&gt;DrawFloor_tiles&lt;/code&gt;, &lt;code&gt;DrawDungeon_ents&lt;/code&gt;, …) existent car les bugs d'ordre sont subtils.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transparence.&lt;/strong&gt; La transparence de la pièce et les masques gauche/droite ne correspondent pas aux zéros RLE de CEL. Le mélange Alpha 128 reproduit approximativement le résultat du blit original. Désactivez les masques avec &lt;code&gt;--debug-disable-masks&lt;/code&gt; lors de l'isolation des bandes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animation versus caméra.&lt;/strong&gt; Les décalages de marche déplacent les sprites et peuvent être inversés en mode caméra. Désynchronisez-les et les pieds glisseront à travers les tuiles ou le monde se comportera comme un élastique. La progression de &lt;code&gt;AnimationInfo&lt;/code&gt; en 128èmes est le langage commun entre la simulation et le rendu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unicité des acteurs.&lt;/strong&gt; Les indicateurs de tracé garantissent qu'un acteur n'est pas dessiné deux fois dans une même image. Un surdessin ressemble à un scintillement&amp;nbsp;; un sous-dessin ressemble à une téléportation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Panneau versus monde.&lt;/strong&gt; L'interface utilisateur a une résolution de 640×480&amp;nbsp;; la caméra du monde n'occupe que 352&amp;nbsp;pixels de hauteur. Le mappage des clics doit utiliser la fonction &lt;code&gt;Scrollrt::screenToTile&lt;/code&gt; qui prend en compte les décalages de défilement et le surbalayage lors des déplacements, sinon l'action «&amp;nbsp;cliquer sur cette porte&amp;nbsp;» ciblera la mauvaise case.&lt;/p&gt;

&lt;p&gt;Ce sont des problèmes de moteur ordinaires. Ce qui est inhabituel, c'est de les résoudre dans un langage dont les outils standard sont HTTP et SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;PHP n'est pas du C. Le projet considère cela comme une contrainte technique, et non comme un défaut de personnalité.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache d'octets.&lt;/strong&gt; &lt;code&gt;AssetStore&lt;/code&gt; met en mémoire les lectures MPQ. L'ouverture du même CEL à deux reprises est gratuite après la première occurrence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache de décodage.&lt;/strong&gt; &lt;code&gt;GameState&lt;/code&gt; conserve &lt;code&gt;$playerFrameCache&lt;/code&gt; et &lt;code&gt;$microCache&lt;/code&gt; (avec les métadonnées). &lt;code&gt;warmRenderCaches&lt;/code&gt; prend en charge le coût de décodage en amont pour les modes de jeu. Les feuilles CL2 des monstres sont conservées dans le tableau des monstres une fois chargées.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tables lumineuses.&lt;/strong&gt; Conçues une seule fois, réutilisées pour le remappage des index — moins chères que l'ombrage RVB par pixel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Téléchargement FFI.&lt;/strong&gt; Le principal facteur de consommation de ressources est souvent le chargement et l'affichage des textures, et non les calculs PHP. L'option &lt;code&gt;--profile-frame&lt;/code&gt; affiche les FPS périodiques et détaille les statistiques de simulation, de rendu, de décodage et de chargement (y compris les percentiles élevés) afin d'identifier les processus les plus gourmands en ressources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compilation logicielle.&lt;/strong&gt; Plus lente, mais transforme la question «&amp;nbsp;Quel pixel a été modifié&amp;nbsp;?&amp;nbsp;» en une réponse côté PHP via &lt;code&gt;RenderTrace&lt;/code&gt;. À utiliser pour le débogage, et non pour déployer le chemin critique.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mémoire.&lt;/strong&gt; &lt;code&gt;bin/diablo.php&lt;/code&gt; définit &lt;code&gt;memory_limit&lt;/code&gt; à 512M. Le décodage RGBA pour une scène de cathédrale complexe représente une quantité importante de mémoire&amp;nbsp;; les caches impliquent un échange de RAM contre du temps d'affichage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rythme logique.&lt;/strong&gt; Chaque «&amp;nbsp;tick&amp;nbsp;» fait avancer le joueur, les monstres, les missiles, les objets, les déclencheurs et les lumières. Le rendu peut s'exécuter plus fréquemment que le rythme logique selon la durée de la boucle dans &lt;code&gt;Diablo::run&lt;/code&gt;, mais la précision des déplacements et des combats est basée sur le rythme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Détails d'implémentation intéressants
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Les pistes transparentes CEL sont signées
&lt;/h3&gt;

&lt;p&gt;Le décodeur CEL ne traite pas &lt;code&gt;≥ 0x80&lt;/code&gt; comme un simple « saut N ». Il réinterprète l'octet comme une longueur signée&amp;nbsp;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;php&lt;br&gt;
if ($val &amp;gt;= 0x80) {&lt;br&gt;
    $n = -$this-&amp;gt;toInt8($val);&lt;br&gt;
    for ($i = 0; $i &amp;lt; $n; $i++) {&lt;br&gt;
        $row[] = null;&lt;br&gt;
        // ...&lt;br&gt;
    }&lt;br&gt;
} else {&lt;br&gt;
    for ($i = 0; $i &amp;lt; $val; $i++) {&lt;br&gt;
        $idx = ord($src[$pos++]);&lt;br&gt;
        $row[] = $idx;&lt;br&gt;
        // ...&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Si le panneau est incorrect, chaque sprite se retrouve couvert de trous à pois — ou pire, il consomme les octets de l'image suivante.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les diagonales de recherche de chemin sont légèrement plus chères
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;php&lt;br&gt;
public const AXIS_COST = 100;&lt;br&gt;
public const DIAG_COST = 101;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Cette pénalité d'un point pour les diagonales favorise les déplacements le long des axes sans interdire les diagonales, et se combine avec une règle d'angle &lt;code&gt;canStep&lt;/code&gt; pour éviter de traverser les angles fermés. L'IA des monstres utilise la même classe &lt;code&gt;Path&lt;/code&gt; avec une longueur maximale plus courte afin que les groupes ne planifient pas des itinéraires traversant toute la carte à chaque tick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les affixes sont des données, le combat est &lt;code&gt;recalc&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Les préfixes et suffixes sont des lignes TSV. &lt;code&gt;ItemDat::applyAffixPower&lt;/code&gt; écrit des champs comme &lt;code&gt;plFireRes&lt;/code&gt;, &lt;code&gt;plToHit&lt;/code&gt;, &lt;code&gt;plEnAc&lt;/code&gt;, &lt;code&gt;plFastAttack&lt;/code&gt;. &lt;code&gt;GameState::recalc&lt;/code&gt; est le point d'agrégation unique&amp;nbsp;—&amp;nbsp;ce que les développeurs Symfony pourraient considérer comme une «&amp;nbsp;reconstruction du modèle de vue des statistiques du joueur&amp;nbsp;». Les objets magiques non identifiés ne contiennent pas &lt;code&gt;pl*&lt;/code&gt; jusqu'à leur identification&amp;nbsp;; les objets normaux sont toujours pris en compte. Cette règle permet d'éviter des bugs du type «&amp;nbsp;j'ai équipé une épée mystérieuse et mes dégâts ont soudainement augmenté&amp;nbsp;».&lt;/p&gt;

&lt;h3&gt;
  
  
  L'audio est une table de repérage, pas un graphique de mixage
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;php&lt;br&gt;
private const CUES = [&lt;br&gt;
    'death' =&amp;gt; 'sfx\\misc\\dead.wav',&lt;br&gt;
    'player_hit' =&amp;gt; 'sfx\\misc\\swing2.wav',&lt;br&gt;
    'swing' =&amp;gt; 'sfx\\misc\\swing.wav',&lt;br&gt;
    'door' =&amp;gt; 'sfx\\items\\invgrab.wav',&lt;br&gt;
    'pickup' =&amp;gt; 'sfx\\items\\invpot.wav',&lt;br&gt;
    'missile' =&amp;gt; 'sfx\\misc\\bfire.wav',&lt;br&gt;
    'cast' =&amp;gt; 'sfx\\misc\\cast1.wav',&lt;br&gt;
    'heal' =&amp;gt; 'sfx\\misc\\healing.wav',&lt;br&gt;
    'walk' =&amp;gt; 'sfx\\misc\\walk1.wav',&lt;br&gt;
];&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Le jeu appelle la fonction &lt;code&gt;play('swing')&lt;/code&gt;. Les plateformes capables d'émettre du son le font&amp;nbsp;; les autres reçoivent un journal d'instructions. Le moteur ne bloque pas la simulation au niveau audio.&lt;/p&gt;

&lt;h3&gt;
  
  
  La limite FFI est intentionnellement mince
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Platform\Sdl&lt;/code&gt; gère la résolution de la bibliothèque, les définitions de contenu, la fenêtre, le moteur de rendu, les événements et le chargement des textures. &lt;code&gt;GameState&lt;/code&gt; n'appelle jamais directement &lt;code&gt;SDL_*&lt;/code&gt;. C'est le même principe que celui qui consiste à conserver Doctrine dans un dépôt&amp;nbsp;: remplacer ou simuler les composants externes sans réécrire le générateur de la cathédrale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Les sauvegardes sont volontairement au format JSON ennuyeux.
&lt;/h3&gt;

&lt;p&gt;Fichier à un seul emplacement, versionné et formaté. Pas de format binaire. Possibilité de comparer une sauvegarde, de la corrompre volontairement ou d'utiliser des chargeurs de scripts. Pour un environnement d'exécution de recherche, la simplicité est un atout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scénarios de déploiement sous forme de spécifications exécutables
&lt;/h3&gt;

&lt;p&gt;L'option &lt;code&gt;--scenario=ALL&lt;/code&gt; exécute des vérifications déterministes de déplacement, de combat, d'ouverture de porte, de ramassage d'objets, d'accès aux escaliers et d'attaques à distance, affichant &lt;code&gt;PASS&lt;/code&gt; ou &lt;code&gt;FAIL&lt;/code&gt;. Ces vérifications ne remplacent pas l'analyse visuelle, mais elles empêchent les refactorisations de casser silencieusement le comportement «&amp;nbsp;cliquer sur une porte, la porte s'ouvre&amp;nbsp;». Des tests de sécurité préliminaires explorent la file d'attente des joueurs et simulent le parcours ville → cathédrale → sauvegarde. Ensemble, ils forment un filet de sécurité autour d'un objet d'état de 14&amp;nbsp;000 lignes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;PHP peut exécuter bien plus que les applications web classiques.&lt;/p&gt;

&lt;p&gt;Ce dépôt présente une vue d'ensemble complète d'un environnement d'exécution ARPG isométrique 2D&amp;nbsp;: entrées/sorties d'archives, codecs de sprites, câblage procédural des donjons, recherche de chemin, combats, objets, IA, interface utilisateur et une interface SDL2 — le tout assemblé sous forme de classes PSR-4 ordinaires que vous pouvez lire avec les mêmes yeux que vous utilisez sur une base de code Symfony.&lt;/p&gt;

&lt;p&gt;Cela illustre aussi un aspect plus discret&amp;nbsp;: les jeux anciens survivent grâce à la possibilité de charger leurs données et d'expliquer leur fonctionnement dans un langage moderne. Vous fournissez votre propre fichier &lt;code&gt;DIABDAT.MPQ&lt;/code&gt;. Le projet inclut le décodeur, le simulateur et l'interface. Vous conservez la propriété légale&amp;nbsp;; les connaissances techniques deviennent une source de partage.&lt;/p&gt;

&lt;p&gt;Est-ce terminé&amp;nbsp;? Le texte d'aide refuse de le qualifier de prêt sans confirmation humaine. Est-ce intéressant&amp;nbsp;? Allez en ville, ouvrez une porte, descendez dans une cathédrale reconstituée et observez PHP charger des microtuiles en 640×480.&lt;/p&gt;

&lt;p&gt;Voilà la demande : porter Diablo sur PHP.&lt;/p&gt;

&lt;p&gt;La surprise n'est pas qu'un agent d'IA ait contribué à la rédaction de milliers de lignes.&lt;/p&gt;

&lt;p&gt;La surprise, c'est que ces lignes forment un moteur — et que le moteur tourne.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code source du jeu
&lt;/h2&gt;

&lt;p&gt;Le code source se trouve ici&amp;nbsp;: &lt;a href="https://github.com/matyo91/diablo-php" rel="noopener noreferrer"&gt;github.com/matyo91/diablo-php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Point d'entrée&amp;nbsp;: &lt;code&gt;bin/diablo.php&lt;/code&gt; · Espace de noms&amp;nbsp;: &lt;code&gt;Diablo\&lt;/code&gt; · Licence&amp;nbsp;: MIT (code) · Ressources&amp;nbsp;: apportez votre propre fichier &lt;code&gt;DIABDAT.MPQ&lt;/code&gt;*&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🤖 Veille Darkwood - 2026-08-12</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Wed, 12 Aug 2026 05:02:56 +0000</pubDate>
      <link>https://dev.to/matyo91/veille-darkwood-2026-08-12-45i7</link>
      <guid>https://dev.to/matyo91/veille-darkwood-2026-08-12-45i7</guid>
      <description>&lt;h2&gt;
  
  
  Points saillants
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Supreme Computation (SC)&lt;/em&gt; propose un cadre de gouvernance exécutable tourné vers la validation des exécutions.&lt;/li&gt;
&lt;li&gt;Un &lt;em&gt;checklist CISO&lt;/em&gt; pour une gouvernance des IA qui garantit la conformité et le contrôle.&lt;/li&gt;
&lt;li&gt;L'importance des comptes bancaires pour agents IA, en vue de leur autonomie financière.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🚨 &lt;b&gt;Premium content.&lt;/b&gt;&lt;br&gt;⬆️ Upgrade to access full analysis.&lt;br&gt;👉 Subscribe here : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 Hacker News Creator - Alors que l'IA dévore le web, la mémoire collective d'Internet disparaît</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Tue, 11 Aug 2026 14:12:42 +0000</pubDate>
      <link>https://dev.to/matyo91/hacker-news-creator-alors-que-lia-devore-le-web-la-memoire-collective-dinternet-disparait-6aj</link>
      <guid>https://dev.to/matyo91/hacker-news-creator-alors-que-lia-devore-le-web-la-memoire-collective-dinternet-disparait-6aj</guid>
      <description>&lt;h2&gt;
  
  
  À propos de cette histoire
&lt;/h2&gt;

&lt;p&gt;Cet article met en lumière les conséquences de la curation de contenu pilotée par l'IA, soulevant des inquiétudes quant à la dégradation des archives historiques d'Internet.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Contenu premium.&lt;/b&gt;&lt;br&gt; ⬆️ Passez à la version supérieure pour accéder à l'analyse complète.&lt;br&gt; 👉 Abonnez-vous ici : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 Créateur Reddit - r/opensource : mermaid-maker/action : Une action Github qui génère des diagrammes de sirène pour vos applications Web !</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:03:18 +0000</pubDate>
      <link>https://dev.to/matyo91/createur-reddit-ropensource-mermaid-makeraction-une-action-github-qui-genere-des-5ej2</link>
      <guid>https://dev.to/matyo91/createur-reddit-ropensource-mermaid-makeraction-une-action-github-qui-genere-des-5ej2</guid>
      <description>&lt;p&gt;Découvrez comment l'action GitHub mermaid-maker/action simplifie la génération de diagrammes Mermaid pour vos applications web. Cet outil améliore considérablement la productivité des développeurs en automatisant la création de diagrammes, permettant ainsi aux équipes de se concentrer sur un code de qualité plutôt que sur la documentation manuelle.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Contenu premium.&lt;/b&gt;&lt;br&gt; ⬆️ Passez à la version supérieure pour accéder à l'analyse complète.&lt;br&gt; 👉 Abonnez-vous ici : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 Créateur arXiv - cs.LG : Comparaison de modèles multimodaux pour la découverte et le contrôle de caractéristiques</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Tue, 11 Aug 2026 11:03:03 +0000</pubDate>
      <link>https://dev.to/matyo91/createur-arxiv-cslg-comparaison-de-modeles-multimodaux-pour-la-decouverte-et-le-controle-de-2ngp</link>
      <guid>https://dev.to/matyo91/createur-arxiv-cslg-comparaison-de-modeles-multimodaux-pour-la-decouverte-et-le-controle-de-2ngp</guid>
      <description>&lt;p&gt;Comprendre et contrôler les grands modèles de langage multimodaux (MLLM) représente un défi. Cet article présente MMDiff, un nouveau framework permettant aux développeurs de gérer efficacement les fonctionnalités des MLLM.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Contenu premium.&lt;/b&gt;&lt;br&gt; ⬆️ Passez à la version supérieure pour accéder à l'analyse complète.&lt;br&gt; 👉 Abonnez-vous ici : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 GitHub Creator - Darkwood: kubernetes/kubernetes</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Tue, 11 Aug 2026 10:04:00 +0000</pubDate>
      <link>https://dev.to/matyo91/github-creator-darkwood-kuberneteskubernetes-4jp3</link>
      <guid>https://dev.to/matyo91/github-creator-darkwood-kuberneteskubernetes-4jp3</guid>
      <description>&lt;h2&gt;
  
  
  À propos de ce créateur
&lt;/h2&gt;

&lt;p&gt;En date du 11 août 2026, le dépôt kubernetes/kubernetes continue de démontrer une activité notable dans la gestion des conteneurs. Des statistiques récentes révèlent des tendances clés dans l'engagement des contributeurs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Visibilité accrue&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contributions soutenues&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ces données mettent en lumière l'importance de ce projet dans l'écosystème open-source.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Premium content.&lt;/b&gt;&lt;br&gt;⬆️ Upgrade to access full analysis.&lt;br&gt;👉 Subscribe here : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🤖 Veille Darkwood - 2026-08-11</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Tue, 11 Aug 2026 05:04:58 +0000</pubDate>
      <link>https://dev.to/matyo91/veille-darkwood-2026-08-11-40o3</link>
      <guid>https://dev.to/matyo91/veille-darkwood-2026-08-11-40o3</guid>
      <description>&lt;h2&gt;
  
  
  Points saillants
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;L'IA BugZ transforme la gestion des alertes SAST avec des correctifs automatiques.&lt;/li&gt;
&lt;li&gt;Le codage par vibe pourrait redéfinir les métiers des développeurs d'ici 2026.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour plus de détails, passez à notre contenu premium.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Premium content.&lt;/b&gt;&lt;br&gt;⬆️ Upgrade to access full analysis.&lt;br&gt;👉 Subscribe here : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 Créateur de Hacker News - Parce que ce n'est pas assez amusant : pourquoi les langages échouent</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Mon, 10 Aug 2026 14:07:29 +0000</pubDate>
      <link>https://dev.to/matyo91/createur-de-hacker-news-parce-que-ce-nest-pas-assez-amusant-pourquoi-les-langages-echouent-29h1</link>
      <guid>https://dev.to/matyo91/createur-de-hacker-news-parce-que-ce-nest-pas-assez-amusant-pourquoi-les-langages-echouent-29h1</guid>
      <description>&lt;p&gt;Cet article explore les subtilités des langages de programmation et leurs aspects souvent négligés en matière d'engagement des utilisateurs. Il met en lumière les raisons pour lesquelles certains langages peinent à trouver un écho auprès des développeurs, laissant entrevoir des implications plus profondes pour l'écosystème technologique.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Contenu premium.&lt;/b&gt;&lt;br&gt; ⬆️ Passez à la version supérieure pour accéder à l'analyse complète.&lt;br&gt; 👉 Abonnez-vous ici : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 Créateur Reddit - r/opensource : Jeu de cartes Hermes/Codex, similaire à Codex Micro</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Mon, 10 Aug 2026 13:03:34 +0000</pubDate>
      <link>https://dev.to/matyo91/createur-reddit-ropensource-jeu-de-cartes-hermescodex-similaire-a-codex-micro-1c1d</link>
      <guid>https://dev.to/matyo91/createur-reddit-ropensource-jeu-de-cartes-hermescodex-similaire-a-codex-micro-1c1d</guid>
      <description>&lt;p&gt;Les récentes mises à jour du micro-deck Hermes/Codex apportent des améliorations significatives aux développeurs open source. Cet article explore les nouvelles fonctionnalités et leurs implications pour la conception de systèmes de contrôle robustes.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Contenu premium.&lt;/b&gt;&lt;br&gt; ⬆️ Passez à la version supérieure pour accéder à l'analyse complète.&lt;br&gt; 👉 Abonnez-vous ici : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🤖 Veille Darkwood - 2026-08-10</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Mon, 10 Aug 2026 05:02:57 +0000</pubDate>
      <link>https://dev.to/matyo91/veille-darkwood-2026-08-10-3g0</link>
      <guid>https://dev.to/matyo91/veille-darkwood-2026-08-10-3g0</guid>
      <description>&lt;h2&gt;
  
  
  Points saillants
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;L'impact du reset de SaaS sur les startups.&lt;/li&gt;
&lt;li&gt;La nécessité de méthodes claires dans le développement de drivers.&lt;/li&gt;
&lt;li&gt;Les défis de sécurité des agents de navigateur AI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour plus de détails, continuez à lire.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Premium content.&lt;/b&gt;&lt;br&gt;⬆️ Upgrade to access full analysis.&lt;br&gt;👉 Subscribe here : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>💫 Hacker News Creator - Les lycéens danois devront défendre oralement leurs travaux écrits</title>
      <dc:creator>Mathieu Ledru</dc:creator>
      <pubDate>Sun, 09 Aug 2026 14:03:15 +0000</pubDate>
      <link>https://dev.to/matyo91/hacker-news-creator-les-lyceens-danois-devront-defendre-oralement-leurs-travaux-ecrits-408d</link>
      <guid>https://dev.to/matyo91/hacker-news-creator-les-lyceens-danois-devront-defendre-oralement-leurs-travaux-ecrits-408d</guid>
      <description>&lt;p&gt;La récente décision du Danemark d'exiger des lycéens qu'ils défendent oralement leurs travaux écrits pourrait remodeler les normes éducatives et les méthodes d'évaluation. Cette mesure vise à favoriser une compréhension plus approfondie et une meilleure expression orale des idées chez les élèves.&lt;/p&gt;

&lt;p&gt;🚨 &lt;b&gt;Contenu premium.&lt;/b&gt;&lt;br&gt; ⬆️ Passez à la version supérieure pour accéder à l'analyse complète.&lt;br&gt; 👉 Abonnez-vous ici : &lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php" rel="noopener noreferrer"&gt;https://www.bonzai.pro/matyo91/shop/48ov_2168/automation-avec-flow-en-php&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
