diff --git a/README.md b/README.md
index 353d022..69441b0 100644
--- a/README.md
+++ b/README.md
@@ -234,6 +234,12 @@ non affilié au jeu original de Mountyhall SARL.
## Versions
+- **2.10.2** (2026-06-15) — Bestiaire, étape 3 : **encyclopédie**. Nouvelle
+ section « 🐲 Bestiaire » dans « Trésors du Hall » (accessible par le menu) qui
+ liste les 150 monstres **groupés par famille**, avec leur **blason**, leurs
+ plages de stats, leur fourchette d'âges (noms par famille) et leurs
+ drapeaux/capacités. *Reste : la bascule des apparitions du jeu sur ce
+ bestiaire (en attente du choix d'équilibrage profondeur → niveaux).*
- **2.10.1** (2026-06-15) — Bestiaire, étape 2 : **éditeur admin**. Nouvelle
section « 🐲 Bestiaire » dans la page admin pour régler les **multiplicateurs
d'âge** (les 8 âges) et **éditer chaque monstre** (filtre par famille +
diff --git a/css/style.css b/css/style.css
index 3b2eaac..80423a5 100644
--- a/css/style.css
+++ b/css/style.css
@@ -640,6 +640,8 @@ header h1 { font-size: 1.3em; color: #FFFFCC; text-shadow: 1px 1px 2px #000; }
font-size: .92em;
}
.tz-name { color: #000000; font-weight: bold; }
+.tz-blason { width: 34px; height: 34px; object-fit: contain; vertical-align: middle; margin-right: 6px; border: 1px solid #000; background: #fff; }
+.tz-head { display: flex; align-items: center; }
.tz-meta { color: #666633; font-size: .88em; margin: 4px 0 8px; }
.tz-meta b { color: #000000; }
.tz-fx-list { display: flex; flex-wrap: wrap; gap: 4px; }
diff --git a/js/game.js b/js/game.js
index 815568f..b48158d 100644
--- a/js/game.js
+++ b/js/game.js
@@ -4,7 +4,7 @@
"use strict";
-const APP_VERSION = "2.10.1";
+const APP_VERSION = "2.10.2";
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
const START_COMP_PCT = 90;
diff --git a/js/treasures.js b/js/treasures.js
index e0f2153..141f370 100644
--- a/js/treasures.js
+++ b/js/treasures.js
@@ -61,6 +61,42 @@ function templateCard(t) {
`;
}
+/* Carte d'un monstre du bestiaire : blason + stats (plages) + âges + capacités. */
+function monsterCard(m) {
+ const rg = (a, b, s = "") => (a === b ? `${a}${s}` : `${a}-${b}${s}`);
+ const ages = BESTIARY_AGE_NAMES[m.family] || [];
+ const ageTxt = m.minAge === m.maxAge ? (ages[m.minAge] || "—")
+ : `${ages[m.minAge] || m.minAge} → ${ages[m.maxAge] || m.maxAge}`;
+ const fx = [
+ `PV ${rg(m.pvMin, m.pvMax)}`,
+ `ATT ${rg(m.attMin, m.attMax, "D6")}`,
+ `ESQ ${rg(m.esqMin, m.esqMax, "D6")}`,
+ `DEG ${rg(m.degMin, m.degMax, "D3")}`,
+ `REG ${rg(m.regMin, m.regMax, "D3")}`,
+ `Arm.phy ${rg(m.armPhysMin, m.armPhysMax)}`,
+ ];
+ if (m.armMagMax) fx.push(`Arm.mag ${rg(m.armMagMin, m.armMagMax)}`);
+ fx.push(`Vue ${rg(m.vueMin, m.vueMax)}`,
+ `MM ${rg(m.mmMin, m.mmMax)}`,
+ `RM ${rg(m.rmMin, m.rmMax)}`);
+ const tags = [];
+ if (m.fly) tags.push("Vole");
+ if (m.ranged) tags.push("À distance");
+ if (m.magic) tags.push("Att. magique");
+ if (m.seesHidden) tags.push("Voit le caché");
+ if (m.nbAtt > 1) tags.push(m.nbAtt + " att./tour");
+ if (m.speed && m.speed !== "Normale") tags.push("Vitesse " + m.speed);
+ for (const t of tags) fx.push(`${t}`);
+ if (m.capacities) fx.push(`${m.capacities}`);
+ const img = m.blason ? `` : "";
+ return `
+
Liste indisponible (base non joignable).
`; } + const bestiary = BESTIARY_DB || []; + html += `Les créatures du Hall. Chaque monstre est défini par ses + stats de base (l'âge le plus jeune, en plages) ; en vieillissant il gagne + en puissance (multiplicateurs d'âge réglables côté admin). Les stats affichées + sont des plages tirées au hasard à chaque apparition.
`; + if (bestiary.length) { + const fams = [...new Set(bestiary.map(m => m.family))].sort(); + for (const fam of fams) { + const list = bestiary.filter(m => m.family === fam); + html += `Bestiaire indisponible (base non joignable).
`; + } + html += `Effet de Zone : touche le lecteur et tous les monstres à 3 cases ou moins. « PV −2D3 ou −4D3 » (Rune des Foins) : la Mountypedia note « -2/4 D3 », interprété ici comme un tirage au sort entre 2D3 et 4D3.
`; @@ -167,7 +220,18 @@ let treasuresReturnTo = "create"; // Données de la base de référence (BDD), rechargées à chaque ouverture pour // refléter les éditions (admin OU sqlite-web) ; null tant qu'on n'a pas (ou pas // pu) charger → repli sur les valeurs statiques du code. -let GEAR_DB = null, POTIONS_DB = null, SCROLLS_DB = null, TEMPLATES_DB = null; +let GEAR_DB = null, POTIONS_DB = null, SCROLLS_DB = null, TEMPLATES_DB = null, BESTIARY_DB = null; + +// Noms d'âge par famille (table de Perco) — pour afficher la fourchette d'âges. +const BESTIARY_AGE_NAMES = { + Insecte: ["Larve", "Immature", "Juvénile", "Imago", "Développé", "Mûr", "Accompli", "Achevé"], + Animal: ["Bébé", "Enfançon", "Jeune", "Adulte", "Mature", "Chef de harde", "Ancien", "Ancêtre"], + "Démon": ["Initial", "Novice", "Mineur", "Favori", "Majeur", "Supérieur", "Suprême", "Ultime"], + Humanoide: ["Nouveau", "Jeune", "Adulte", "Vétéran", "Briscard", "Doyen", "Légendaire", "Mythique"], + Monstre: ["Nouveau", "Jeune", "Adulte", "Vétéran", "Briscard", "Doyen", "Légendaire", "Mythique"], + "Mort-Vivant": ["Naissant", "Récent", "Ancien", "Vénérable", "Séculaire", "Antique", "Ancestral", "Antédiluvien"], +}; +const FAMILY_EMOJI = { Insecte: "🐛", Animal: "🐾", "Démon": "👿", Humanoide: "🧝", Monstre: "👹", "Mort-Vivant": "💀" }; async function fetchRef(cat) { try { @@ -181,9 +245,10 @@ async function fetchRef(cat) { } async function loadReference() { - const [gear, potions, scrolls, templates] = await Promise.all([ - fetchRef("gear"), fetchRef("potions"), fetchRef("scrolls"), fetchRef("templates"), + const [gear, potions, scrolls, templates, bestiary] = await Promise.all([ + fetchRef("gear"), fetchRef("potions"), fetchRef("scrolls"), fetchRef("templates"), fetchRef("bestiary"), ]); + BESTIARY_DB = bestiary; GEAR_DB = gear ? gearBySlotFromRows(gear) : null; POTIONS_DB = potions ? indexPowerById(potions) : null; SCROLLS_DB = scrolls ? indexPowerById(scrolls) : null;