# 🧌 MountyCrawl — Le Monde Souterrain Un roguelike navigateur en hommage à [MountyHall](https://www.mountyhall.com), la Terre des Trõlls. Tu incarnes un Trõll qui descend dans le Monde Souterrain à la recherche de monstres, de trésors et de PX — jusqu'au Béhémoth qui rôde à la profondeur −5. 100 % vanilla HTML/CSS/JS, aucune dépendance, aucun build. Ouvrir `index.html` et jouer. ## Jouer 🎮 **https://mountycrawl.nas.percolouco.com** (déployé via Traefik, compose dans `/opt/container/mountycrawl`, image node:22-alpine construite depuis ce repo). En local : ```bash PORT=8080 LEVELS_FILE=/tmp/levels.json node server.js # → http://localhost:8080 ``` (Le jeu solo fonctionne aussi en ouvrant `index.html` directement ; seuls l'éditeur « Publier » et les niveaux communautaires ont besoin du serveur.) Après une modification, redéployer avec : ```bash cd /opt/container/mountycrawl && docker compose up -d --build ``` Paramètres d'URL pratiques : `index.html?autostart=1&race=Durakuir&name=Grosbill` lance directement une partie (utilisé pour les captures d'écran et les tests). ## Mécaniques reprises de MountyHall | Mécanique | Dans MountyHall | Dans MountyCrawl | |---|---|---| | Tour de jeu | DLA de 12 h, 6 PA | DLA instantanée quand les PA sont épuisés (ou bouton « Passer la DLA »), 6 PA | | Caractéristiques | ATT/ESQ en D6, DEG/REG en D3, PV, Vue | Identique, profils de base officiels des 5 races (`MH_Rules/Races_*.php`) | | Combat | Somme ATT D6 vs somme ESQ D6, dégâts DEG D3 − armure, coup critique (attaque ≥ 2 × esquive) aux dégâts doublés | Identique, avec un panneau « Détail du combat » au format des rapports MH (jets, Seuil de Résistance, jet de Résistance, maîtrise, butin, PX) à droite du journal | | Talents | Compétence + sortilège réservés par race, 15 % de maîtrise initiale, jet D100 sous le %, progression à l'usage (+1D6 % < 50, +1D3 % < 75, +1 % ensuite ; sous 50 %, un échec donne quand même +1 %), plafonds 90 % compétences / 80 % sortilèges, échec = PA partiellement remboursés | Identique | | Magie | Seuil de Résistance basé sur MM vs RM, borné 10–90 % | Identique : jet de résistance des monstres → effets de moitié | | Bonus/malus | Physiques (équipement) et magiques (potions, sortilèges…) distincts | Identique : équipement = physique (exceptions à venir), potions/parchemins = magique ; décomposition « P/M » affichée dans le profil | | Armure | L'armure réduit les dégâts physiques ; seule sa composante magique réduit les dégâts magiques | Identique : armure physique (naturelle en D3 + équipement) + armure magique (potions/parchemins) — troll et monstres | | Progression | PX → PI, le N-ième achat coûte N × coût de base racial (table de Rules_3.php) | Identique : 16 PI de base (12 pour la caractéristique favorite), REG 30 (Darkling 22), Armure naturelle 30 pour tous, PV par tranche de 10 | | Niveaux | 10 × N PI pour atteindre le niveau N | Identique | | Races | 5 races, chacune avec sort/compétence propre | Les 5 races avec leur capacité signature (voir ci-dessous) | | Coûts en PA | Déplacement 1–3, attaque, ramasser, équiper, potion… | Déplacement 1, attaque 3, ramasser 1, équiper 2, potion 1 | Simplifications assumées : les PX sont convertis en PI immédiatement (pas de phase d'entraînement), la DLA ne dure pas 12 h réelles (sinon la partie durerait trois mois), et le Monde Souterrain est généré procéduralement à chaque descente. ## Les 5 races (profils et talents officiels) Profils de base repris des pages officielles, compétence (🥋 2 PA) et sortilège (🔮 4 PA) réservés avec les effets décrits sur la Mountypedia, adaptés à la grille : | Race | Profil de base | 🥋 Compétence | 🔮 Sortilège | |---|---|---|---| | 🟢 Skrim | ATT 4D6 · ESQ 3D6 · DEG 3D3 · REG 1D3 · PV 30 · Vue 3 | **Botte Secrète** : attaque bonus 1/DLA (2D6 par 3 dés d'ATT, 1D3 par 2 dés d'ATT) | **Hypnotisme** : esquive ÷2 + la cible perd son tour | | 🟤 Durakuir | ATT 3D6 · ESQ 3D6 · DEG 3D3 · REG 1D3 · PV 40 · Vue 3 | **Régénération Accrue** : +1D3 PV par tranche de 15 PV max | **Rafale Psychique** : imparable, 1D3 par dé de DEG, ignore l'armure | | 🔴 Kastar | ATT 3D6 · ESQ 3D6 · DEG 4D3 · REG 1D3 · PV 30 · Vue 3 | **Accélération du Métabolisme** : sacrifie des PV (fatigue croissante) pour +4 PA | **Vampirisme** : dégâts magiques + vol de 50 % des dégâts | | 🟡 Tomawak | ATT 3D6 · ESQ 3D6 · DEG 3D3 · REG 1D3 · PV 30 · Vue 4 | **Camouflage** : invisible, jet sous 75 % de la maîtrise à chaque pas | **Projectile Magique** : à distance (portée = Vue), bonus de proximité | | 🟣 Darkling | ATT 3D6 · ESQ 3D6 · DEG 3D3 · REG 2D3 · PV 30 · Vue 3 | **Balayage** : déstabilise (la cible perd son tour) | **Siphon des Âmes** : 1D3 par dé de REG, ignore toute armure, nécrose −ATT | ### Page « Règles des talents » Accessible depuis l'accueil (📜 Règles des talents) et en jeu (bouton 📜 Règles dans l'en-tête) : les 10 fiches au format MountyHall — Coût en PA, Type, Jet de résistance, Effet de zone, limite par DLA, EFFET détaillé avec les formules de jets, et DESCRIPTION d'ambiance. En cours de partie, la fiche des talents de ta race affiche en plus un encart « **Mes infos** » : pourcentage actuel et plafond, taux de réussite sur tes jets, dernière utilisation, et tes jets calculés (ex. Vampirisme : Attaque 2 D6 · Dégâts 4 D3 · Vol de vie 50 %). ### Coûts d'amélioration (officiels, [Rules_3.php](https://www.mountyhall.com/MH_Rules/Rules_3.php)) Le N-ième achat d'une caractéristique coûte N × le coût de base : | Amélioration | Skrim | Durakuir | Kastar | Tomawak | Darkling | |---|---|---|---|---|---| | +1D6 Attaque | **12** | 16 | 16 | 16 | 16 | | +1D6 Esquive | 16 | 16 | 16 | 16 | 16 | | +1D3 Dégâts | 16 | 16 | **12** | 16 | 16 | | +1D3 Régénération | 30 | 30 | 30 | 30 | **22** | | +1D3 Armure naturelle | 30 | 30 | 30 | 30 | 30 | | +1 Vue | 16 | 16 | 16 | **12** | 16 | | +10 PV | 16 | **12** | 16 | 16 | 16 | (L'amélioration « 30 minutes de DLA » du jeu original n'a pas d'équivalent ici, la DLA étant instantanée.) ## Contrôles - **ZQSD / flèches** : se déplacer (1 PA) — marcher sur un monstre l'attaque (3 PA) - **Espace** : passer la DLA (les monstres agissent, tu régénères REG D3) - **E** : ramasser l'objet sous tes pieds (1 PA) - Boutons à l'écran pour la capacité raciale, l'équipement, les potions et l'entraînement (PI) ## Bestiaire Gobelin, Champignon Vénéneux, Araignée Géante, Gargouille, Momie, Sorcière, Golem de Pierre — déclinés en gabarits *Jeune / Vieux / Ancien / Mythique* selon la profondeur, et le **Béhémoth** comme boss final à la profondeur −5. Comme l'armure, l'ATT et les DEG des monstres existent en **physique** et en **magique** : un monstre doté d'une attaque magique (ATT mag et DEG mag > 0) alterne au hasard entre ses deux attaques ; l'attaque magique n'est réduite que par l'armure magique du troll. En vanilla toutes les valeurs magiques sont à 0 (comportement inchangé) — elles se règlent par type via l'admin du monde partagé. ## Monde partagé (multijoueur) Depuis l'accueil : **🧌🧌 Monde partagé**. Un monde souterrain persistant et commun à tous les joueurs, autoritaire côté serveur (`mp.js`) : - **Le monde vit en continu**, joueurs connectés ou non : chaque monstre a sa propre **DLA** (période tirée dans une fourchette paramétrable) et agit à son échéance — il erre, ou attaque le troll à portée de Vue. Les monstres ne s'attaquent jamais entre eux. La population se repeuple automatiquement. - **Chaque troll a 6 PA**, rechargés à sa DLA personnelle (paramétrable). Toutes les actions du solo sont disponibles : déplacement, attaque, compétence et sortilège de race, potions, parchemins, équipement, entraînement en PI. - **À MountyHall on ne meurt jamais vraiment** : un troll terrassé réapparaît après un délai paramétrable, PV pleins, en conservant tout son avoir. - **Connexion multi-appareils** : le troll vit côté serveur. À la création on choisit un mot de passe (conseillé) ; « 🔑 Retrouver mon troll » permet ensuite de le récupérer depuis n'importe quel appareil avec nom + mot de passe (les noms sont uniques, le mot de passe est stocké hashé+salé). Sans mot de passe, l'identité (id + clé secrète) reste gardée par le navigateur d'origine (localStorage), comme avant. - Le client se rafraîchit par **polling** (intervalle conseillé par le serveur, paramétrable) ; les autres trolls visibles apparaissent en bleu avec leur nom, les camouflés sont invisibles. - Persistance dans `WORLD_FILE` (défaut `/data/world.json`), sauvegarde périodique et au SIGTERM. ### Administration (`admin.html`) Page protégée par token (`MP_ADMIN_TOKEN`, sinon généré au premier démarrage dans `/data/admin-token.txt` et affiché dans les logs). Tous les réglages sont **appliqués à chaud** : DLA des monstres (min/max — les échéances déjà planifiées sont re-tirées si besoin), DLA des trolls, intervalle de rafraîchissement client, population de monstres et de trésors, délai de réapparition, profondeur du monde (puissance des monstres), taille de carte (à la prochaine régénération), limite de trolls. Plus : vue d'ensemble (trolls avec dernière activité, monstres avec leur DLA individuelle et leur prochaine action), derniers échos du monde, et bouton « Régénérer le monde » (trolls conservés et replacés). L'admin peut aussi **retoucher les règles du monde partagé**, à chaud (les nouveaux spawns/drops utilisent les valeurs retouchées ; le solo reste vanilla) : - **Tuning du bestiaire** : tableau éditable par type de monstre (niveau, ATT phy/mag, ESQ, DEG phy/mag, PV, armure phy/mag, VUE) — valeurs de base, avant gabarit d'âge (Jeune/Vieux/Ancien/Mythique). - **Tuning de l'équipement** : les 55 objets avec tous leurs bonus modifiables (ATT phy/mag, ESQ, DEG phy/mag, REG, armure phy/mag, VUE, PV fixes, RM/MM en %). - **Tuning des potions & parchemins** : fourchette de puissance « niveau X » (min/max) tirée à chaque drop, par trésor. Les cases modifiées sont surlignées et chaque catégorie se remet d'origine d'un clic. Depuis la 2.3.0, ces valeurs vivent dans une **base SQLite** (`DB_FILE`, défaut `/data/mountycrawl.db`, via le module natif `node:sqlite` — toujours zéro dépendance npm) : tables `monsters`, `gear`, `potions`, `scrolls`, seedées vanilla au premier démarrage puis jamais écrasées. On peut donc aussi les éditer directement avec n'importe quel outil SQLite (DB Browser, DBeaver, sqlite3) — chaque spawn/drop relit la base, c'est pris en compte à chaud. L'état vivant du monde (trolls, monstres actifs, objets au sol) reste dans `world.json` ; l'ancien `world.tuning` est migré automatiquement dans la base. ### API multijoueur - `POST /api/mp/join` `{name, race, password}` → `{id, secret, state}` - `POST /api/mp/login` `{name, password}` → `{id, secret, state}` (retrouver son troll depuis un autre appareil) - `GET /api/mp/state?id=&secret=` → état visible (vue du troll) - `POST /api/mp/action` `{id, secret, action}` — `move`, `attack`, `comp`, `sort`, `pickup`, `use`, `unequip`, `eat`, `drop`, `train` - `GET /api/mp/info` → compteurs publics (trolls en ligne…) - `GET /api/mp/admin` · `PUT /api/mp/admin/config` · `PUT /api/mp/admin/tuning` · `POST /api/mp/admin/reset` (header `X-Admin-Token`) ## Éditeur de niveaux & partage Depuis l'écran d'accueil : **🛠️ Éditeur de niveaux**. - Peins le terrain à la souris (mur, sol, sortie ▼), place le départ 🧌, les monstres (avec gabarit Jeune → Mythique) et les objets. Outils : tout sol / tout mur / caverne aléatoire / nouveau niveau. - **🚪 Portes** : une porte téléporte le troll vers un autre niveau publié, en conservant PV, PI et équipement — de quoi chaîner des niveaux en campagne. - **▶️ Tester** joue ton niveau immédiatement (retour à l'éditeur après la partie). - **🌍 Publier** l'envoie au serveur ; il apparaît dans « Niveaux de la communauté ». La publication renvoie une clé d'auteur gardée en localStorage : tes niveaux restent modifiables via la section « Mes niveaux publiés » (bouton devient « Mettre à jour »). - Victoire d'un niveau custom : atteindre la sortie ▼ si elle existe, sinon terrasser tous les monstres. Un niveau avec porte(s) sans sortie ne se termine pas sur place : la suite est derrière la porte. - Lien partageable : `https://mountycrawl.nas.percolouco.com/?level=`. ### API | Route | Description | |---|---| | `GET /api/levels` | Liste (id, nom, auteur, date, nb monstres, nb portes, nb parties) | | `GET /api/levels/` | Niveau complet (incrémente le compteur de parties ; la clé d'auteur n'est jamais renvoyée) | | `POST /api/levels` | Publie un niveau ; renvoie `{id, secret}` — le `secret` est la clé d'auteur | | `PUT /api/levels/` | Met à jour un niveau ; exige l'en-tête `X-Level-Secret` (403 sinon) | Validation serveur : grille 28×20, départ hors mur, max 60 monstres/objets et 10 portes (cible = id de niveau), au moins un objectif (monstre, porte ou sortie). Stockage : `/data/levels.json` dans le volume `/opt/container/mountycrawl/data`. ## Tests ```bash node test/smoke.js # cœur des règles : dés, combat, armures, talents, cavernes… node test/mp.js # moteur multijoueur : DLA, tick, actions, admin, persistance ``` Vérifie les dés, la résolution de combat (armures physique/magique), le Seuil de Résistance, les coûts d'amélioration, les niveaux, la connexité des cavernes générées, le bestiaire, et le monde partagé (DLA des monstres, mort/réapparition, visibilité, réglages admin bornés, sauvegarde/rechargement). ## Sources Mécaniques reconstituées depuis le [site officiel](https://www.mountyhall.com), les [règles](https://www.mountyhall.com/MH_Rules/Rules_4.php) et la [Mountypedia](https://mountypedia.mountyhall.com). MountyCrawl est un hommage non affilié au jeu original de Mountyhall SARL. ## Versions - **2.10.0** (2026-06-15) — **Nouveau bestiaire** (étape 1 : les données). Le bestiaire complet fourni (945 entrées monstre × âge) est replié, comme demandé, en **1 ligne par monstre** (150 monstres, stats de base = âge le plus jeune, en plages min/max) + une **table d'âges** (8 multiplicateurs appliqués à la génération/croissance). Tout est dans la base de référence (`js/bestiary.js` → tables `bestiary` et `monster_ages`), exposé sur `/api/reference/bestiary` et `/api/reference/ages`, et éditable comme le reste (sqlite-web). Chaque monstre porte sa famille, ses plages de stats, ses capacités (listées, effets à venir), ses drapeaux (vol, attaque à distance/magique, voit le caché), sa vitesse, son nombre d'attaques et son blason. *Prochaines étapes : encyclopédie, éditeur admin, puis bascule des apparitions du jeu sur ce bestiaire.* (empilement des cases, rendu par coins avec le coin **lieu** ↘ qui affiche enfin les portes 🚪 / la sortie ▼, cases ×2, attaque limitée à sa propre case, drop empilable). Le **menu déroulant de cible s'affiche toujours**, même quand il n'y a qu'un seul monstre/troll. Et comme les cases agrandies débordent du cadre, une **caméra suit le troll** (le panneau défile pour le garder en vue), dans les deux modes. - **2.9.0** (2026-06-15) — Monde partagé : **cases deux fois plus grandes** (48 px) pour la lisibilité de l'empilement par coins. **Portée d'attaque = même case** : on ne peut frapper (attaque de base et compétences/sortilèges d'attaque) que sur sa propre case ; seule exception, le **Projectile Magique** (sortilège du Tomawak) reste à distance (jusqu'à la Vue). **PvP** : on peut désormais attaquer les autres trolls comme les monstres (combat classique, mise à mort → réapparition habituelle ; +1 PX par coup porté, pas de butin transféré). Le menu déroulant de cible liste monstres **et** trolls présents sur la case. - **2.8.0** (2026-06-15) — **Empilement sur les cases** dans le monde partagé. Une case accueille maintenant **plusieurs trolls, plusieurs monstres et plusieurs trésors** (un seul « lieu » à terme). Se déplacer ne fait **plus attaquer** : on se pose sur la case (cohabitation), et l'**attaque** passe par le bouton ⚔️ — avec un **menu déroulant** pour choisir la cible quand plusieurs monstres sont sur ta case ou adjacents. Rendu **par coins** pour rester lisible : 🧌 trolls en haut-gauche, 👹 monstres en haut-droite, 💰 trésors en bas-gauche, lieux en bas-droite, avec un « ×N » quand il y en a plusieurs. (Le PvP — taper d'autres trolls — et le portage en solo restent à faire.) - **2.7.1** (2026-06-14) — Encyclopédie « Trésors du Hall » : nouvelle section **✨ Templates** (les 19 templates de drop, lus dans la base), un **menu de navigation** collant en haut pour sauter directement à chaque section (Potions, Parchemins, Équipement, Templates) et un bouton flottant **« remonter en haut »** qui apparaît dès qu'on a défilé — la page étant devenue longue. - **2.7.0** (2026-06-14) — **Templates de drop** façon MountyHall. 19 suffixes (« de l'Aigle », « des Mages », « des Béhémoths »…) stockés dans une nouvelle table `templates` de la base (seedés au démarrage, éditables comme le reste via sqlite-web / `GET /api/reference/templates`). À chaque **drop d'équipement du monde partagé**, l'objet reçoit jusqu'à **3 templates** (6 pour une arme à **2 mains**), chaque emplacement étant rempli avec une **probabilité réglable sur la page admin** (`templateChance`, 0–100 %). Les bonus des templates s'ajoutent aux mods de l'objet (nouveau mod `TOUR`, en minutes) et leurs suffixes au nom (« Épée Courte de l'Aigle des Mages »). Le solo reste vanilla. *Note : le bonus `TOUR` est affiché mais son effet de jeu (DLA) reste à câbler une fois la règle confirmée.* - **2.6.3** (2026-06-13) — **Cache-busting** des fichiers du jeu : les balises `