From 618c0e6e768f1022bd7ec0ce789039a712ad9f85 Mon Sep 17 00:00:00 2001 From: perco Date: Mon, 15 Jun 2026 19:03:43 +0200 Subject: [PATCH] v2.10.2 : bestiaire etape 3 - encyclopedie (150 monstres par famille + blasons) treasures.js : section 'Bestiaire' (fetch /api/reference/bestiary, BESTIARY_DB), monsterCard (blason image lazy + plages de stats + fourchette d'ages via BESTIARY_AGE_NAMES + drapeaux/capacites), groupee par famille, + lien de nav. CSS .tz-blason. Reste : integration spawn (en attente du choix profondeur->niveaux de Perco). Co-Authored-By: Claude Opus 4.8 --- README.md | 6 +++++ css/style.css | 2 ++ js/game.js | 2 +- js/treasures.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 77 insertions(+), 4 deletions(-) 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 ` +
+
${img}${m.name}
+
Niveau ${rg(m.levelMin, m.levelMax)} · Âge : ${ageTxt}
+
${fx.join("")}
+
`; +} + function treasureCard(def, x, duration, fx) { const fxHtml = fx.map(f => { const neg = f.includes("−"); @@ -93,6 +129,7 @@ function treasuresRender() { 📜 Parchemins ⚔️ Équipement ✨ Templates + 🐲 Bestiaire `; html += `

🧪 Potions (${TREASURE_POTIONS.length})

`; @@ -155,6 +192,22 @@ function treasuresRender() { html += `

Liste indisponible (base non joignable).

`; } + const bestiary = BESTIARY_DB || []; + html += `

🐲 Bestiaire (${bestiary.length})

+

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 += `

${FAMILY_EMOJI[fam] || "🐾"} ${fam} (${list.length})

${list.map(monsterCard).join("")}
`; + } + } else { + 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;