v2.13.0 : RM%/MM% d'equipement en plages (min/max) tirees au drop
db.js : GEAR_KEYS += rmPctMin, mmPctMin (les rmPct/mmPct existants = max) ; vanillaGear : min defaut = la valeur (pas de plage) ; migration ALTER gear ADD COLUMN rmPctMin/mmPctMin + UPDATE min=valeur. mp.js applyGearTuning : mods construits hors cles Min, RM%/MM% tires au hasard dans [min,max] a la creation (comme les potions). treasures.js : encyclopedie affiche la plage 'min a max %' (rmPctMin/mmPctMin portes sur le def). admin GEAR_LABELS : RM%/MM% min + max. Solo : valeurs fixes (la plage vit dans la base / monde partage). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -234,6 +234,15 @@ non affilié au jeu original de Mountyhall SARL.
|
||||
|
||||
## Versions
|
||||
|
||||
- **2.13.0** (2026-06-17) — **RM% et MM% de l'équipement en plages.** Les valeurs
|
||||
existantes deviennent les **maximums** et on ajoute un **minimum** par objet
|
||||
(`rmPctMin`/`mmPctMin`, réglables côté admin). À la **création d'un objet du
|
||||
monde partagé**, RM% et MM% sont **tirés au hasard dans leur plage** (comme la
|
||||
puissance des potions) — ex. min 5 / max 10 → un objet entre 5 et 10. Par
|
||||
défaut min = max (donc rien ne change tant qu'on n'abaisse pas le min).
|
||||
L'encyclopédie affiche la plage. *(Le solo garde ses valeurs fixes — la plage
|
||||
est une propriété de la base de référence, éditable et tirée côté monde
|
||||
partagé.)*
|
||||
- **2.12.0** (2026-06-17) — **L'éditeur de niveaux pose les monstres du
|
||||
bestiaire.** On choisit la **famille → le monstre → l'âge** (« Xorn [Mineur] »,
|
||||
« Nécrochore [Naissant] »…) et on place la créature voulue. L'**ancien Béhémoth
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ function renderOverview(ov) {
|
||||
|
||||
/* ---------- Tuning : bestiaire, trésors, équipement ---------- */
|
||||
|
||||
const GEAR_LABELS = { att: "ATT phy", attMag: "ATT mag", esq: "ESQ", deg: "DEG phy", degMag: "DEG mag", reg: "REG", arm: "Arm. phy", armMag: "Arm. mag", vue: "VUE", pv: "PV", rmPct: "RM %", mmPct: "MM %" };
|
||||
const GEAR_LABELS = { att: "ATT phy", attMag: "ATT mag", esq: "ESQ", deg: "DEG phy", degMag: "DEG mag", reg: "REG", arm: "Arm. phy", armMag: "Arm. mag", vue: "VUE", pv: "PV", rmPct: "RM% max", rmPctMin: "RM% min", mmPct: "MM% max", mmPctMin: "MM% min" };
|
||||
|
||||
function tuneInput(cat, id, key, def, cur) {
|
||||
const val = cur !== undefined ? cur : def;
|
||||
|
||||
@@ -30,7 +30,9 @@ const BESTIARY_TXT_KEYS = ["family", "speed", "capacities", "blason", "gender"];
|
||||
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 GEAR_KEYS = ["att", "attMag", "esq", "deg", "degMag", "reg", "arm", "armMag", "vue", "pv", "rmPct", "mmPct"];
|
||||
// rmPct/mmPct sont les MAX ; rmPctMin/mmPctMin les MIN → à la création d'un objet
|
||||
// du monde partagé, RM% et MM% sont tirés au hasard dans leur plage (cf. mp.js).
|
||||
const GEAR_KEYS = ["att", "attMag", "esq", "deg", "degMag", "reg", "arm", "armMag", "vue", "pv", "rmPct", "rmPctMin", "mmPct", "mmPctMin"];
|
||||
|
||||
/* Templates de drop façon MountyHall : des suffixes (« de l'Aigle », « des
|
||||
* Mages »…) ajoutés aléatoirement à l'équipement qui tombe, selon une proba
|
||||
@@ -85,10 +87,14 @@ function vanillaMonsters() {
|
||||
}
|
||||
|
||||
function vanillaGear() {
|
||||
return Object.entries(gearLib.GEAR).flatMap(([slot, list]) => list.map(def => ({
|
||||
slot, name: def.name, emoji: def.emoji, tier: def.tier, twoHanded: !!def.twoHanded,
|
||||
...Object.fromEntries(GEAR_KEYS.map(k => [k, def.mods[k] || 0])),
|
||||
})));
|
||||
return Object.entries(gearLib.GEAR).flatMap(([slot, list]) => list.map(def => {
|
||||
const row = { slot, name: def.name, emoji: def.emoji, tier: def.tier, twoHanded: !!def.twoHanded };
|
||||
for (const k of GEAR_KEYS) row[k] = def.mods[k] || 0;
|
||||
// par défaut, min = la valeur (pas de plage tant que l'admin ne l'abaisse pas)
|
||||
row.rmPctMin = def.mods.rmPct || 0;
|
||||
row.mmPctMin = def.mods.mmPct || 0;
|
||||
return row;
|
||||
}));
|
||||
}
|
||||
|
||||
function vanillaTemplates() {
|
||||
@@ -150,6 +156,11 @@ function init(file = ":memory:") {
|
||||
// Migration : la colonne `gender` du bestiaire a été ajoutée après coup → on
|
||||
// l'ajoute aux bases déjà créées AVANT tout INSERT/UPDATE qui la référence.
|
||||
try { DB.exec("ALTER TABLE bestiary ADD COLUMN gender TEXT"); } catch { /* déjà là */ }
|
||||
// Migration : RM%/MM% min de l'équipement (les valeurs existantes = max).
|
||||
try { DB.exec("ALTER TABLE gear ADD COLUMN rmPctMin INTEGER"); } catch {}
|
||||
try { DB.exec("ALTER TABLE gear ADD COLUMN mmPctMin INTEGER"); } catch {}
|
||||
DB.exec("UPDATE gear SET rmPctMin = rmPct WHERE rmPctMin IS NULL");
|
||||
DB.exec("UPDATE gear SET mmPctMin = mmPct WHERE mmPctMin IS NULL");
|
||||
const insM = DB.prepare(`INSERT OR IGNORE INTO monsters
|
||||
(name, emoji, boss, isStatic, ${MONSTER_KEYS.join(", ")})
|
||||
VALUES (?, ?, ?, ?, ${MONSTER_KEYS.map(() => "?").join(", ")})`);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const APP_VERSION = "2.12.0";
|
||||
const APP_VERSION = "2.13.0";
|
||||
|
||||
/* Alpha : maîtrise initiale haute pour les tests. Remettre 15 % / 15 % à la v1.0 officielle. */
|
||||
const START_COMP_PCT = 90;
|
||||
|
||||
@@ -162,6 +162,14 @@ function treasuresRender() {
|
||||
for (const def of (gearSource[slot] || [])) {
|
||||
const fx = [];
|
||||
for (const [key, label, unit] of GEAR_MOD_LABELS) {
|
||||
// RM%/MM% : affichés en plage [min, max] (tirés au hasard à la création)
|
||||
if (key === "rmPct" || key === "mmPct") {
|
||||
const max = def.mods[key] || 0, min = def[key + "Min"] != null ? def[key + "Min"] : max;
|
||||
if (!max && !min) continue;
|
||||
const txt = min === max ? `${max > 0 ? "+" : ""}${max}` : `${min} à ${max}`;
|
||||
fx.push(`<span class="tz-fx ${max < 0 ? "tz-bad" : "tz-good"}">${label} ${txt}${unit}</span>`);
|
||||
continue;
|
||||
}
|
||||
const v = def.mods[key];
|
||||
if (!v) continue;
|
||||
const cls = v > 0 ? "tz-fx tz-good" : "tz-fx tz-bad";
|
||||
@@ -272,6 +280,7 @@ function gearBySlotFromRows(rows) {
|
||||
(bySlot[r.slot] = bySlot[r.slot] || []).push({
|
||||
emoji: r.emoji, name: r.name, tier: r.tier, twoHanded: !!r.twoHanded,
|
||||
note: staticDef && staticDef.note, mods,
|
||||
rmPctMin: r.rmPctMin != null ? r.rmPctMin : r.rmPct, mmPctMin: r.mmPctMin != null ? r.mmPctMin : r.mmPct,
|
||||
});
|
||||
}
|
||||
return bySlot;
|
||||
|
||||
@@ -97,7 +97,16 @@ function applyGearTuning(item) {
|
||||
if (!item || item.kind !== "gear") return item;
|
||||
const row = db.gearRow(item.slot, item.name);
|
||||
if (row) {
|
||||
item.mods = Object.fromEntries(db.GEAR_KEYS.map(k => [k, row[k] || 0]).filter(([, v]) => v));
|
||||
const mods = {};
|
||||
for (const k of db.GEAR_KEYS) {
|
||||
if (k.endsWith("Min") || k === "rmPct" || k === "mmPct") continue; // bornes : traitées à part
|
||||
if (row[k]) mods[k] = row[k];
|
||||
}
|
||||
// RM% et MM% : tirés au hasard dans leur plage [min, max] à la création.
|
||||
const rr = (mn, mx) => { const lo = Math.min(mn || 0, mx || 0), hi = Math.max(mn || 0, mx || 0); return lo + Math.floor(Math.random() * (hi - lo + 1)); };
|
||||
const rm = rr(row.rmPctMin, row.rmPct); if (rm) mods.rmPct = rm;
|
||||
const mm = rr(row.mmPctMin, row.mmPct); if (mm) mods.mmPct = mm;
|
||||
item.mods = mods;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user