v2.10.0 : bestiaire (etape 1, donnees) - table BDD + 150 monstres + table d'ages
Bestiaire fourni (945 entrees monstre x age) parse et replie selon le modele demande par Perco : 1 ligne par monstre (js/bestiary.js, 150 monstres, stats de base = age le plus jeune en min/max + famille + capacites + drapeaux vol/distance/magie/voit-cache + vitesse + nb attaques + blason) + table d'ages (8 multiplicateurs derives des donnees). db.js : tables bestiary + monster_ages, seed, bestiaryAll/monsterAges, setBestiary/setMonsterAge, resetCategory. server.js : /api/reference/ bestiary + /ages. Non destructif (le jeu tourne encore sur l'ancien systeme). Etapes suivantes : encyclopedie, editeur admin, integration spawn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
28863303c6
commit
836837fa55
5 files changed
+70
-6
No files matched your search
@@ -234,7 +234,17 @@ non affilié au jeu original de Mountyhall SARL.
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
- **2.9.1** (2026-06-15) — Le **solo reçoit tout ce que le monde partagé a eu**
|
- **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
|
(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,
|
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
|
drop empilable). Le **menu déroulant de cible s'affiche toujours**, même quand
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ const g = require("./js/game.js");
|
|||||||
const p = require("./js/potions.js");
|
const p = require("./js/potions.js");
|
||||||
const sc = require("./js/scrolls.js");
|
const sc = require("./js/scrolls.js");
|
||||||
const gearLib = require("./js/gear.js");
|
const gearLib = require("./js/gear.js");
|
||||||
|
const bestiaryLib = require("./js/bestiary.js");
|
||||||
|
|
||||||
|
// Bestiaire complet (demande Perco) : 1 ligne par monstre (stats de base = âge
|
||||||
|
// le plus jeune, en plages min/max) + une table d'âges (multiplicateurs).
|
||||||
|
const BESTIARY_STATS = ["level", "pv", "att", "esq", "deg", "reg", "armPhys", "armMag", "vue", "mm", "rm"];
|
||||||
|
const BESTIARY_RANGE_KEYS = BESTIARY_STATS.flatMap(k => [k + "Min", k + "Max"]);
|
||||||
|
const BESTIARY_INT_KEYS = [...BESTIARY_RANGE_KEYS, "minAge", "maxAge", "fly", "ranged", "magic", "seesHidden", "nbAtt"];
|
||||||
|
const BESTIARY_TXT_KEYS = ["family", "speed", "capacities", "blason"];
|
||||||
|
const BESTIARY_KEYS = [...BESTIARY_INT_KEYS, ...BESTIARY_TXT_KEYS]; // hors `name` (clé)
|
||||||
|
|
||||||
const MONSTER_KEYS = ["level", "att", "attMag", "esq", "deg", "degMag", "pv", "armor", "armorMag", "vue"];
|
const MONSTER_KEYS = ["level", "att", "attMag", "esq", "deg", "degMag", "pv", "armor", "armorMag", "vue"];
|
||||||
const GEAR_KEYS = ["att", "attMag", "esq", "deg", "degMag", "reg", "arm", "armMag", "vue", "pv", "rmPct", "mmPct"];
|
const GEAR_KEYS = ["att", "attMag", "esq", "deg", "degMag", "reg", "arm", "armMag", "vue", "pv", "rmPct", "mmPct"];
|
||||||
@@ -129,6 +138,14 @@ function init(file = ":memory:") {
|
|||||||
CREATE TABLE IF NOT EXISTS templates (
|
CREATE TABLE IF NOT EXISTS templates (
|
||||||
id INTEGER PRIMARY KEY, name TEXT, ${TEMPLATE_KEYS.map(k => `${k} INTEGER`).join(", ")}
|
id INTEGER PRIMARY KEY, name TEXT, ${TEMPLATE_KEYS.map(k => `${k} INTEGER`).join(", ")}
|
||||||
);
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS bestiary (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
${BESTIARY_INT_KEYS.map(k => `${k} INTEGER`).join(", ")},
|
||||||
|
${BESTIARY_TXT_KEYS.map(k => `${k} TEXT`).join(", ")}
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS monster_ages (
|
||||||
|
age INTEGER PRIMARY KEY, mult REAL
|
||||||
|
);
|
||||||
`);
|
`);
|
||||||
const insM = DB.prepare(`INSERT OR IGNORE INTO monsters
|
const insM = DB.prepare(`INSERT OR IGNORE INTO monsters
|
||||||
(name, emoji, boss, isStatic, ${MONSTER_KEYS.join(", ")})
|
(name, emoji, boss, isStatic, ${MONSTER_KEYS.join(", ")})
|
||||||
@@ -149,6 +166,11 @@ function init(file = ":memory:") {
|
|||||||
const insT = DB.prepare(`INSERT OR IGNORE INTO templates (id, name, ${TEMPLATE_KEYS.join(", ")})
|
const insT = DB.prepare(`INSERT OR IGNORE INTO templates (id, name, ${TEMPLATE_KEYS.join(", ")})
|
||||||
VALUES (?, ?, ${TEMPLATE_KEYS.map(() => "?").join(", ")})`);
|
VALUES (?, ?, ${TEMPLATE_KEYS.map(() => "?").join(", ")})`);
|
||||||
for (const t of vanillaTemplates()) insT.run(t.id, t.name, ...TEMPLATE_KEYS.map(k => t[k]));
|
for (const t of vanillaTemplates()) insT.run(t.id, t.name, ...TEMPLATE_KEYS.map(k => t[k]));
|
||||||
|
const insB = DB.prepare(`INSERT OR IGNORE INTO bestiary (name, ${BESTIARY_KEYS.join(", ")})
|
||||||
|
VALUES (?, ${BESTIARY_KEYS.map(() => "?").join(", ")})`);
|
||||||
|
for (const b of bestiaryLib.BESTIARY) insB.run(b.name, ...BESTIARY_KEYS.map(k => b[k]));
|
||||||
|
const insA = DB.prepare("INSERT OR IGNORE INTO monster_ages (age, mult) VALUES (?, ?)");
|
||||||
|
bestiaryLib.AGE_MULT.forEach((mult, age) => insA.run(age, mult));
|
||||||
// Correctif des bases déjà créées : PufPuff avait été seedé à tort en [0,2]
|
// Correctif des bases déjà créées : PufPuff avait été seedé à tort en [0,2]
|
||||||
// (le niveau 0 d'une potion n'existe pas, minimum 1) → on le ramène à [1,3].
|
// (le niveau 0 d'une potion n'existe pas, minimum 1) → on le ramène à [1,3].
|
||||||
// Le garde `powerMin = 0` évite d'écraser une plage éditée volontairement.
|
// Le garde `powerMin = 0` évite d'écraser une plage éditée volontairement.
|
||||||
@@ -191,6 +213,14 @@ function templatesAll() {
|
|||||||
return db().prepare("SELECT * FROM templates ORDER BY id").all();
|
return db().prepare("SELECT * FROM templates ORDER BY id").all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bestiaryAll() {
|
||||||
|
return db().prepare("SELECT * FROM bestiary ORDER BY family, name").all();
|
||||||
|
}
|
||||||
|
|
||||||
|
function monsterAges() {
|
||||||
|
return db().prepare("SELECT * FROM monster_ages ORDER BY age").all();
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Écriture (page admin) ---------- */
|
/* ---------- Écriture (page admin) ---------- */
|
||||||
|
|
||||||
function setMonster(name, vals) {
|
function setMonster(name, vals) {
|
||||||
@@ -219,11 +249,24 @@ function setTemplate(id, vals) {
|
|||||||
.run(...keys.map(k => vals[k]), id);
|
.run(...keys.map(k => vals[k]), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setBestiary(name, vals) {
|
||||||
|
const keys = BESTIARY_KEYS.filter(k => vals[k] != null);
|
||||||
|
if (!keys.length) return;
|
||||||
|
db().prepare(`UPDATE bestiary SET ${keys.map(k => `${k} = ?`).join(", ")} WHERE name = ?`)
|
||||||
|
.run(...keys.map(k => vals[k]), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMonsterAge(age, mult) {
|
||||||
|
db().prepare("UPDATE monster_ages SET mult = ? WHERE age = ?").run(mult, age);
|
||||||
|
}
|
||||||
|
|
||||||
/* Remet une catégorie entière aux valeurs vanilla. */
|
/* Remet une catégorie entière aux valeurs vanilla. */
|
||||||
function resetCategory(cat) {
|
function resetCategory(cat) {
|
||||||
if (cat === "monsters") for (const m of vanillaMonsters()) setMonster(m.name, m);
|
if (cat === "monsters") for (const m of vanillaMonsters()) setMonster(m.name, m);
|
||||||
if (cat === "gear") for (const it of vanillaGear()) setGear(it.slot, it.name, it);
|
if (cat === "gear") for (const it of vanillaGear()) setGear(it.slot, it.name, it);
|
||||||
if (cat === "templates") for (const t of vanillaTemplates()) setTemplate(t.id, t);
|
if (cat === "templates") for (const t of vanillaTemplates()) setTemplate(t.id, t);
|
||||||
|
if (cat === "bestiary") for (const b of bestiaryLib.BESTIARY) setBestiary(b.name, b);
|
||||||
|
if (cat === "ages") bestiaryLib.AGE_MULT.forEach((mult, age) => setMonsterAge(age, mult));
|
||||||
if (cat === "potions" || cat === "scrolls") {
|
if (cat === "potions" || cat === "scrolls") {
|
||||||
for (const t of vanillaTreasures(cat)) setTreasureRange(cat, t.id, [t.powerMin, t.powerMax]);
|
for (const t of vanillaTreasures(cat)) setTreasureRange(cat, t.id, [t.powerMin, t.powerMax]);
|
||||||
}
|
}
|
||||||
@@ -231,8 +274,8 @@ function resetCategory(cat) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init, db,
|
init, db,
|
||||||
MONSTER_KEYS, GEAR_KEYS, TEMPLATE_KEYS, POTION_POWER_DEFAULTS, SCROLL_POWER_DEFAULTS,
|
MONSTER_KEYS, GEAR_KEYS, TEMPLATE_KEYS, BESTIARY_KEYS, POTION_POWER_DEFAULTS, SCROLL_POWER_DEFAULTS,
|
||||||
vanillaMonsters, vanillaGear, vanillaTreasures, vanillaTemplates,
|
vanillaMonsters, vanillaGear, vanillaTreasures, vanillaTemplates,
|
||||||
monsters, gearAll, gearRow, treasureRange, treasuresAll, templatesAll,
|
monsters, gearAll, gearRow, treasureRange, treasuresAll, templatesAll, bestiaryAll, monsterAges,
|
||||||
setMonster, setGear, setTreasureRange, setTemplate, resetCategory,
|
setMonster, setGear, setTreasureRange, setTemplate, setBestiary, setMonsterAge, resetCategory,
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/* Bestiaire MountyCrawl — généré depuis le bestiaire fourni par Perco.
|
||||||
|
* 1 ligne par monstre (stats de base = âge le plus jeune), + une table d'âges
|
||||||
|
* (multiplicateurs appliqués à la génération/croissance). Plages min/max tirées
|
||||||
|
* au hasard à chaque apparition. 150 monstres, 6 familles. */
|
||||||
|
"use strict";
|
||||||
|
const BESTIARY = [{"name":"Ankheg","family":"Insecte","minAge":0,"maxAge":5,"levelMin":10,"levelMax":12,"pvMin":100,"pvMax":120,"attMin":9,"attMax":11,"esqMin":5,"esqMax":7,"degMin":4,"degMax":6,"regMin":1,"regMax":2,"armPhysMin":18,"armPhysMax":20,"armMagMin":0,"armMagMax":2,"vueMin":2,"vueMax":4,"mmMin":300,"mmMax":600,"rmMin":300,"rmMax":500,"fly":0,"ranged":0,"magic":0,"seesHidden":0,"speed":"Normale","nbAtt":1,"capacities":"Acide","blason":"https://www.mountyhall.com/images/Monstres/VG_Ankheg.jpg"},{"name":"Anoploure Purpurin","family":"Insecte","minAge":0,"maxAge":7,"levelMin":34,"levelMax":36,"pvMin":170,"pvMax":190,"attMin":19,"attMax":21,"esqMin":18,"esqMax":20,"degMin":18,"degMax":20,"regMin":12,"regMax":13,"armPhysMin":11,"armPhysMax":13,"armMagMin":0,"armMagMax":2,"vueMin":3,"vueMax":5,"mmMin":2300,"mmMax":2600,"rmMin":1800,"rmMax":2100,"fly":0,"ranged":0,"magic":0,"seesHidden":0,"speed":"Normale","nbAtt":1,"capacities":"Maladie","blason":"https://www.mountyhall.com/images/Monstres/BO_AnolporPurpurin.jpg"},{"name":"Araignée Géante","family":"Insecte","minAge":0,"maxAge":3,"levelMin":2,"levelMax":4,"pvMin":20,"pvMax":40,"attMin":2,"attMax":4,"esqMin":2,"esqMax":4,"degMin":2,"degMax":3,"regMin":1,"regMax":2,"armPhysMin":0,"armPhysMax":2,"armMagMin":0,"armMagMax":2,"vueMin":2,"vueMax":4,"mmMin":0,"mmMax":300,"rmMin":0,"rmMax":300,"fly":0,"ranged":0,"magic":0,"seesHidden":0,"speed":"Normale","nbAtt":1,"capacities":"Venin","blason":"https://www.mountyhall.com/images/Monstres/AraigneeGeante.jpg"},{"name":"Aragnarok du Chaos","family":"Insecte","minAge":0,"maxAge":3,"levelMin":14,"levelMax":16,"pvMin":50,"pvMax":70,"attMin":9,"attMax":11,"esqMin":9,"esqMax":11,"degMin":12,"degMax":14,"regMin":1,"regMax":2,"armPhysMin":2,"armPhysMax":4,"armMagMin":0,"armMagMax":2,"vueMin":3,"vueMax":5,"mmMin":600,"mmMax":900,"rmMin":800,"rmMax":1100,"fly":0,"ranged":0,"magic":0,"seesHidden":1,"speed":"Normale","nbAtt":1,"capacities":"Attaque Paralysante","blason":""},{"name":"Coccicruelle","family":"Insecte","minAge":0,"maxAge":3,"levelMin":20,"levelMax":22,"pvMin":90,"pvMax":110,"attMin":16,"attMax":18,"esqMin":12,"esqMax":14,"degMin":12,"degMax":14,"regMin":2,"regMax":3,"armPhysMin":6,"armPhysMax":8,"armMagMin":0,"armMagMax":2,"vueMin":4,"vueMax":6,"mmMin":800,"mmMax":1100,"rmMin":300,"rmMax":600,"fly":1,"ranged":0,"magic":0,"seesHidden":0,"speed":"Normale","nbAtt":1,"capacities":"","blason":"https://www.mountyhall.com/images/Monstres/FF_coccicruelle.png"},{"name":"Essaim Cratérien","family":"Insecte","minAge":0,"maxAge":7,"levelMin":28,"levelMax":30,"pvMin":250,"pvMax":270,"attMin":23,"attMax":25,"esqMin":11,"esqMax":13,"degMin":26,"degMax":28,"regMin":7,"regMax":8,"armPhysMin":37,"armPhysMax":39,"armMagMin":0,"armMagMax":2,"vueMin":7,"vueMax":9,"mmMin":3300,"mmMax":3600,"rmMin":3100,"rmMax":3400,"fly":1,"ranged":1,"magic":0,"seesHidden":1,"speed":"Lente","nbAtt":1,"capacities":"Attaque Paralysante","blason":"-"},{"name":"Essaim Sanguinaire","family":"Insecte","minAge":0,"maxAge":7,"levelMin":23,"levelMax":25,"pvMin":110,"pvMax":130,"attMin":13,"attMax":15,"esqMin":8,"esqMax":10,"degMin":15,"degMax":17,"regMin":3,"regMax":4,"armPhysMin":4,"armPhysMax":6,"armMagMin":0,"armMagMax":2,"vueMin":6,"vueMax":8,"mmMin":2300,"mmMax":2600,"rmMin":1900,"rmMax":2200,"fly":1,"ranged":1,"magic":0,"seesHidden":1,"speed":"Lente","nbAtt":1,"capacities":"Attaque Paralysante","blason":"https://www.mountyhall.com/images/Monstres/FF_essaim-sanguinaire.png"},{"name":"Foudroyeur","family":"Insecte","minAge":0,"maxAge":6,"levelMin":36,"levelMax":38,"pvMin":80,"pvMax":100,"attMin":22,"attMax":24,"esqMin":15,"esqMax":17,"degMin":21,"degMax":23,"regMin":5,"regMax":6,"armPhysMin":19,"armPhysMax":21,"armMagMin":0,"armMagMax":2,"vueMin":3,"vueMax":5,"mmMin":2800,"mmMax":3100,"rmMin":1800,"rmMax":2100,"fly":0,"ranged":0,"magic":0,"seesHidden":0,"speed":"Rapide","nbAtt":1,"capacities":"Attaque Electrique","blason":"https://www.mountyhall.com/images/Monstres/TO_Foudroyeur.jpg"},{"name":"Labeilleux","family":"Insecte","minAge":0,"maxAge":7,"levelMin":24,"levelMax":26,"pvMin":150,"pvMax":170,"attMin":18,"attMax":20,"esqMin":16,"esqMax":18,"degMin":12,"degMax":14,"regMin":5,"regMax":6,"armPhysMin":10,"armPhysMax":12,"armMagMin":0,"armMagMax":2,"vueMin":4,"vueMax":6,"mmMin":1300,"mmMax":1600,"rmMin":1800,"rmMax":2100,"fly":1,"ranged":0,"magic":0,"seesHidden":0,"speed":"Normale","nbAtt":1,"capacities":"Venin","blason":"https://www.mountyhall.com/images/Monstres/OF_Labeilleux.jpg"},{"name":"Limace Géante","family":"Insecte","minAge":0,"maxAge":3,"levelMin":10,"levelMax":12,"pvMin":70,"pvMax":90,"attMin":12,"attMax":14,"esqMin":2,"esqMax":2,"degMin":2,"degMax":4,"regMin":1,"regMax":2,"armPhysMin":1,"armPhysMax":2,"armMagMin":0,"armMagMax":2,"vueMin":2,"vueMax":4,"mmMin":200,"mmMax":300,"rmMin":100,"rmMax":300,"fly":0,"ranged":1,"magic":0,"seesHidden":0,"speed":"Normale","nbAtt":1,"capacities":"Attaque Gluante","blason":"https://www.mountyhall.com/image
Line truncated
|
||||||
|
const 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 AGE_MULT = [1,1,1.13,1.15,1.19,1.33,1.35,1.35];
|
||||||
|
if (typeof module !== "undefined" && module.exports) module.exports = { BESTIARY, AGE_NAMES, AGE_MULT };
|
||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const APP_VERSION = "2.9.1";
|
const APP_VERSION = "2.10.0";
|
||||||
|
|
||||||
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
|
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
|
||||||
const START_COMP_PCT = 90;
|
const START_COMP_PCT = 90;
|
||||||
|
|||||||
@@ -276,8 +276,10 @@ function handleAPI(req, res, url) {
|
|||||||
potions: () => db.treasuresAll("potions"),
|
potions: () => db.treasuresAll("potions"),
|
||||||
scrolls: () => db.treasuresAll("scrolls"),
|
scrolls: () => db.treasuresAll("scrolls"),
|
||||||
templates: () => db.templatesAll(),
|
templates: () => db.templatesAll(),
|
||||||
|
bestiary: () => db.bestiaryAll(),
|
||||||
|
ages: () => db.monsterAges(),
|
||||||
};
|
};
|
||||||
const refMatch = url.pathname.match(/^\/api\/reference\/(gear|monsters|potions|scrolls|templates)$/);
|
const refMatch = url.pathname.match(/^\/api\/reference\/(gear|monsters|potions|scrolls|templates|bestiary|ages)$/);
|
||||||
if (req.method === "GET" && refMatch) {
|
if (req.method === "GET" && refMatch) {
|
||||||
return sendJSON(res, 200, REFERENCE[refMatch[1]]());
|
return sendJSON(res, 200, REFERENCE[refMatch[1]]());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user