v2.7.0 : templates de drop (table BDD, proba admin, application au drop)
- db.js : table templates + seed des 19 templates du CSV (mappes vers les mods + nouveau champ tour), templatesAll/setTemplate/resetCategory. - server.js : /api/reference/templates. - mp.js : config templateChance (0-100, bornee) ; applyGearTemplates applique jusqu'a 3 templates (6 si 2 mains), distincts, proba par emplacement, fusionne les mods (dont tour) et compose le nom ; applique au spawnItem. Solo vanilla. - gear.js : GEAR_MOD_LABELS += tour (affichage 'TOUR +X mn'). - admin.html : champ Proba de template / emplacement. - test/mp.js : test des templates (nb selon mains, distincts, proba 0). Note : effet gameplay de tour (DLA) non cable, en attente de la regle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -234,6 +234,17 @@ non affilié au jeu original de Mountyhall SARL.
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
|
- **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
|
- **2.6.3** (2026-06-13) — **Cache-busting** des fichiers du jeu : les balises
|
||||||
`<script>`/`<link>` de `index.html` portent `?v=__V__`, jeton que le serveur
|
`<script>`/`<link>` de `index.html` portent `?v=__V__`, jeton que le serveur
|
||||||
remplace par la version de l'appli (donc différent à chaque déploiement). Le
|
remplace par la version de l'appli (donc différent à chaque déploiement). Le
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ const FIELDS = [
|
|||||||
["deathRespawnSec", "Réapparition des trolls (s)", "délai après une mort"],
|
["deathRespawnSec", "Réapparition des trolls (s)", "délai après une mort"],
|
||||||
["worldDepth", "Profondeur du monde (1-5)", "puissance des monstres et des trésors"],
|
["worldDepth", "Profondeur du monde (1-5)", "puissance des monstres et des trésors"],
|
||||||
["maxTrolls", "Trolls max", "limite de personnages dans le monde"],
|
["maxTrolls", "Trolls max", "limite de personnages dans le monde"],
|
||||||
|
["templateChance", "Templates — proba / emplacement (%)", "chance qu'un emplacement de template soit rempli sur un drop (3 max en 1 main, 6 en 2 mains)"],
|
||||||
["mapW", "Largeur de carte", "appliqué à la prochaine régénération du monde"],
|
["mapW", "Largeur de carte", "appliqué à la prochaine régénération du monde"],
|
||||||
["mapH", "Hauteur de carte", "appliqué à la prochaine régénération du monde"],
|
["mapH", "Hauteur de carte", "appliqué à la prochaine régénération du monde"],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -23,6 +23,33 @@ const gearLib = require("./js/gear.js");
|
|||||||
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"];
|
||||||
|
|
||||||
|
/* Templates de drop façon MountyHall : des suffixes (« de l'Aigle », « des
|
||||||
|
* Mages »…) ajoutés aléatoirement à l'équipement qui tombe, selon une proba
|
||||||
|
* réglable côté admin. Leurs bonus s'ajoutent aux mods de l'objet ; `tour` est
|
||||||
|
* un modificateur de TOUR (DLA) propre aux templates. */
|
||||||
|
const TEMPLATE_KEYS = ["att", "attMag", "esq", "vue", "deg", "degMag", "arm", "armMag", "rmPct", "mmPct", "reg", "pv", "tour"];
|
||||||
|
const VANILLA_TEMPLATES = [
|
||||||
|
[1, "de l'Aigle", [0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[2, "des Béhémoths", [0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 30]],
|
||||||
|
[3, "des Cyclopes", [0, 1, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[4, "des Enragés", [0, 1, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[5, "de Feu", [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[6, "des Mages", [0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0]],
|
||||||
|
[7, "de l'Orage", [0, 0, 2, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[8, "de l'Ours", [0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 5, 30]],
|
||||||
|
[9, "du Pic", [0, 0, -1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0]],
|
||||||
|
[10, "du Rat", [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[11, "de Résistance", [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]],
|
||||||
|
[12, "de la Salamandre", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]],
|
||||||
|
[13, "du Temps", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -30]],
|
||||||
|
[14, "de la Terre", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 30]],
|
||||||
|
[15, "du Sable", [0, 0, 3, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0]],
|
||||||
|
[16, "des Vampires", [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]],
|
||||||
|
[17, "des Duellistes", [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[18, "des Champions", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
[19, "des Anciens", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
|
||||||
|
];
|
||||||
|
|
||||||
/* Fourchettes de puissance « niveau X » vanilla (Mountypedia). La Longue-Vue
|
/* Fourchettes de puissance « niveau X » vanilla (Mountypedia). La Longue-Vue
|
||||||
* vanilla tire dans {1,2,3,5,8} : tant que sa fourchette en base reste celle
|
* vanilla tire dans {1,2,3,5,8} : tant que sa fourchette en base reste celle
|
||||||
* d'origine, on garde le tirage officiel. */
|
* d'origine, on garde le tirage officiel. */
|
||||||
@@ -55,6 +82,12 @@ function vanillaGear() {
|
|||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function vanillaTemplates() {
|
||||||
|
return VANILLA_TEMPLATES.map(([id, name, vals]) => ({
|
||||||
|
id, name, ...Object.fromEntries(TEMPLATE_KEYS.map((k, i) => [k, vals[i]])),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function vanillaTreasures(cat) {
|
function vanillaTreasures(cat) {
|
||||||
const [ids, defs, ranges] = cat === "potions"
|
const [ids, defs, ranges] = cat === "potions"
|
||||||
? [p.POTION_IDS, p.POTION_DEFS, POTION_POWER_DEFAULTS]
|
? [p.POTION_IDS, p.POTION_DEFS, POTION_POWER_DEFAULTS]
|
||||||
@@ -93,6 +126,9 @@ function init(file = ":memory:") {
|
|||||||
id TEXT PRIMARY KEY, name TEXT, emoji TEXT,
|
id TEXT PRIMARY KEY, name TEXT, emoji TEXT,
|
||||||
powerMin INTEGER, powerMax INTEGER
|
powerMin INTEGER, powerMax INTEGER
|
||||||
);
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS templates (
|
||||||
|
id INTEGER PRIMARY KEY, name TEXT, ${TEMPLATE_KEYS.map(k => `${k} INTEGER`).join(", ")}
|
||||||
|
);
|
||||||
`);
|
`);
|
||||||
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(", ")})
|
||||||
@@ -110,6 +146,9 @@ function init(file = ":memory:") {
|
|||||||
const ins = DB.prepare(`INSERT OR IGNORE INTO ${cat} (id, name, emoji, powerMin, powerMax) VALUES (?, ?, ?, ?, ?)`);
|
const ins = DB.prepare(`INSERT OR IGNORE INTO ${cat} (id, name, emoji, powerMin, powerMax) VALUES (?, ?, ?, ?, ?)`);
|
||||||
for (const t of vanillaTreasures(cat)) ins.run(t.id, t.name, t.emoji, t.powerMin, t.powerMax);
|
for (const t of vanillaTreasures(cat)) ins.run(t.id, t.name, t.emoji, t.powerMin, t.powerMax);
|
||||||
}
|
}
|
||||||
|
const insT = DB.prepare(`INSERT OR IGNORE INTO templates (id, name, ${TEMPLATE_KEYS.join(", ")})
|
||||||
|
VALUES (?, ?, ${TEMPLATE_KEYS.map(() => "?").join(", ")})`);
|
||||||
|
for (const t of vanillaTemplates()) insT.run(t.id, t.name, ...TEMPLATE_KEYS.map(k => t[k]));
|
||||||
// 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.
|
||||||
@@ -148,6 +187,10 @@ function treasuresAll(cat) {
|
|||||||
return db().prepare(`SELECT * FROM ${cat === "scrolls" ? "scrolls" : "potions"}`).all();
|
return db().prepare(`SELECT * FROM ${cat === "scrolls" ? "scrolls" : "potions"}`).all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function templatesAll() {
|
||||||
|
return db().prepare("SELECT * FROM templates ORDER BY id").all();
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Écriture (page admin) ---------- */
|
/* ---------- Écriture (page admin) ---------- */
|
||||||
|
|
||||||
function setMonster(name, vals) {
|
function setMonster(name, vals) {
|
||||||
@@ -169,10 +212,18 @@ function setTreasureRange(cat, id, [min, max]) {
|
|||||||
.run(min, max, id);
|
.run(min, max, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setTemplate(id, vals) {
|
||||||
|
const keys = TEMPLATE_KEYS.filter(k => vals[k] != null);
|
||||||
|
if (!keys.length) return;
|
||||||
|
db().prepare(`UPDATE templates SET ${keys.map(k => `${k} = ?`).join(", ")} WHERE id = ?`)
|
||||||
|
.run(...keys.map(k => vals[k]), id);
|
||||||
|
}
|
||||||
|
|
||||||
/* 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 === "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]);
|
||||||
}
|
}
|
||||||
@@ -180,8 +231,8 @@ function resetCategory(cat) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init, db,
|
init, db,
|
||||||
MONSTER_KEYS, GEAR_KEYS, POTION_POWER_DEFAULTS, SCROLL_POWER_DEFAULTS,
|
MONSTER_KEYS, GEAR_KEYS, TEMPLATE_KEYS, POTION_POWER_DEFAULTS, SCROLL_POWER_DEFAULTS,
|
||||||
vanillaMonsters, vanillaGear, vanillaTreasures,
|
vanillaMonsters, vanillaGear, vanillaTreasures, vanillaTemplates,
|
||||||
monsters, gearAll, gearRow, treasureRange, treasuresAll,
|
monsters, gearAll, gearRow, treasureRange, treasuresAll, templatesAll,
|
||||||
setMonster, setGear, setTreasureRange, resetCategory,
|
setMonster, setGear, setTreasureRange, setTemplate, resetCategory,
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const APP_VERSION = "2.6.3";
|
const APP_VERSION = "2.7.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;
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ const GEAR_MOD_LABELS = [
|
|||||||
["arm", "Armure phy.", ""], ["armMag", "Armure mag.", ""],
|
["arm", "Armure phy.", ""], ["armMag", "Armure mag.", ""],
|
||||||
["vue", "VUE", ""], ["pv", "PV", ""],
|
["vue", "VUE", ""], ["pv", "PV", ""],
|
||||||
["rmPct", "RM", " %"], ["mmPct", "MM", " %"],
|
["rmPct", "RM", " %"], ["mmPct", "MM", " %"],
|
||||||
|
["tour", "TOUR", " mn"], // apporté par les templates de drop (modif de DLA)
|
||||||
];
|
];
|
||||||
|
|
||||||
function formatGearMods(mods, sep = " · ") {
|
function formatGearMods(mods, sep = " · ") {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const DEFAULT_CONFIG = {
|
|||||||
deathRespawnSec: 90, // délai de réapparition d'un troll terrassé
|
deathRespawnSec: 90, // délai de réapparition d'un troll terrassé
|
||||||
maxTrolls: 40,
|
maxTrolls: 40,
|
||||||
logSize: 150,
|
logSize: 150,
|
||||||
|
templateChance: 0, // proba (%) qu'un emplacement de template soit rempli sur un drop d'équipement
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONFIG_BOUNDS = {
|
const CONFIG_BOUNDS = {
|
||||||
@@ -50,6 +51,7 @@ const CONFIG_BOUNDS = {
|
|||||||
monsterTarget: [0, 120], repopSec: [10, 3600],
|
monsterTarget: [0, 120], repopSec: [10, 3600],
|
||||||
itemTarget: [0, 120], deathRespawnSec: [0, 3600],
|
itemTarget: [0, 120], deathRespawnSec: [0, 3600],
|
||||||
maxTrolls: [1, 200], logSize: [20, 500],
|
maxTrolls: [1, 200], logSize: [20, 500],
|
||||||
|
templateChance: [0, 100],
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------- Tuning : valeurs du bestiaire, de l'équipement et des trésors ----------
|
/* ---------- Tuning : valeurs du bestiaire, de l'équipement et des trésors ----------
|
||||||
@@ -99,6 +101,33 @@ function applyGearTuning(item) {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Templates de drop : un objet qui tombe reçoit jusqu'à 3 templates (6 pour une
|
||||||
|
* arme à 2 mains), chaque emplacement étant rempli avec la proba `chance` (%)
|
||||||
|
* par un template DISTINCT tiré au hasard en base. Les bonus (dont `tour`)
|
||||||
|
* s'ajoutent aux mods de l'objet, et les suffixes au nom (« Épée Courte de
|
||||||
|
* l'Aigle des Mages »). N'affecte que le monde partagé (drops) ; le solo reste
|
||||||
|
* vanilla. */
|
||||||
|
function applyGearTemplates(item, chance) {
|
||||||
|
if (!item || item.kind !== "gear" || !(chance > 0)) return item;
|
||||||
|
const pool = db.templatesAll();
|
||||||
|
if (!pool.length) return item;
|
||||||
|
const slots = item.twoHanded ? 6 : 3;
|
||||||
|
const picked = [];
|
||||||
|
for (let i = 0; i < slots && pool.length; i++) {
|
||||||
|
if (Math.random() * 100 >= chance) continue;
|
||||||
|
picked.push(pool.splice(Math.floor(Math.random() * pool.length), 1)[0]);
|
||||||
|
}
|
||||||
|
if (!picked.length) return item;
|
||||||
|
const mods = { ...(item.mods || {}) };
|
||||||
|
for (const t of picked) {
|
||||||
|
for (const k of db.TEMPLATE_KEYS) if (t[k]) mods[k] = (mods[k] || 0) + t[k];
|
||||||
|
}
|
||||||
|
item.mods = Object.fromEntries(Object.entries(mods).filter(([, v]) => v));
|
||||||
|
item.name = `${item.name} ${picked.map(t => t.name).join(" ")}`;
|
||||||
|
item.templates = picked.map(t => t.name);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Création du monde ---------- */
|
/* ---------- Création du monde ---------- */
|
||||||
|
|
||||||
function randomFloor(world, taken = null) {
|
function randomFloor(world, taken = null) {
|
||||||
@@ -153,7 +182,7 @@ function spawnItem(world) {
|
|||||||
const depth = world.config.worldDepth;
|
const depth = world.config.worldDepth;
|
||||||
const item = r < 0.34 ? tunedRandomPotion()
|
const item = r < 0.34 ? tunedRandomPotion()
|
||||||
: r < 0.48 ? tunedRandomScroll()
|
: r < 0.48 ? tunedRandomScroll()
|
||||||
: r < 0.78 ? applyGearTuning(gearLib.makeRandomGear(depth))
|
: r < 0.78 ? applyGearTemplates(applyGearTuning(gearLib.makeRandomGear(depth)), world.config.templateChance)
|
||||||
: { kind: "gold", gold: g.rollDice(depth, 6).total * 10, emoji: "💰" };
|
: { kind: "gold", gold: g.rollDice(depth, 6).total * 10, emoji: "💰" };
|
||||||
if (item.kind === "gold") item.name = `${item.gold} Mountyzédons`;
|
if (item.kind === "gold") item.name = `${item.gold} Mountyzédons`;
|
||||||
world.items.push({ ...item, x: pos.x, y: pos.y });
|
world.items.push({ ...item, x: pos.x, y: pos.y });
|
||||||
@@ -1038,5 +1067,5 @@ module.exports = {
|
|||||||
adminOverview, adminSetConfig, adminSetTuning, adminDefaults, adminResetWorld, adminKickTroll,
|
adminOverview, adminSetConfig, adminSetTuning, adminDefaults, adminResetWorld, adminKickTroll,
|
||||||
currentTuning, saveWorld, loadWorld,
|
currentTuning, saveWorld, loadWorld,
|
||||||
spawnMonster, spawnItem, monsterAct, trollDla, worldLog,
|
spawnMonster, spawnItem, monsterAct, trollDla, worldLog,
|
||||||
tunedRandomPotion, tunedRandomScroll, applyGearTuning,
|
tunedRandomPotion, tunedRandomScroll, applyGearTuning, applyGearTemplates,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -275,8 +275,9 @@ function handleAPI(req, res, url) {
|
|||||||
monsters: () => db.monsters(),
|
monsters: () => db.monsters(),
|
||||||
potions: () => db.treasuresAll("potions"),
|
potions: () => db.treasuresAll("potions"),
|
||||||
scrolls: () => db.treasuresAll("scrolls"),
|
scrolls: () => db.treasuresAll("scrolls"),
|
||||||
|
templates: () => db.templatesAll(),
|
||||||
};
|
};
|
||||||
const refMatch = url.pathname.match(/^\/api\/reference\/(gear|monsters|potions|scrolls)$/);
|
const refMatch = url.pathname.match(/^\/api\/reference\/(gear|monsters|potions|scrolls|templates)$/);
|
||||||
if (req.method === "GET" && refMatch) {
|
if (req.method === "GET" && refMatch) {
|
||||||
return sendJSON(res, 200, REFERENCE[refMatch[1]]());
|
return sendJSON(res, 200, REFERENCE[refMatch[1]]());
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -280,6 +280,20 @@ function makeWorld(over = {}) {
|
|||||||
assert.strictEqual(db.gearRow("arme", "Gourdin").att, 2, "reset gear → vanilla (gourdin)");
|
assert.strictEqual(db.gearRow("arme", "Gourdin").att, 2, "reset gear → vanilla (gourdin)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Templates de drop : nombre selon les mains, distincts, bonus fusionnés, proba 0
|
||||||
|
{
|
||||||
|
const one = mp.applyGearTemplates({ kind: "gear", slot: "arme", name: "Épée Courte", twoHanded: false, mods: { deg: 2 } }, 100);
|
||||||
|
assert.strictEqual(one.templates.length, 3, "1 main : 3 templates max");
|
||||||
|
assert.strictEqual(one.mods.deg >= 2, true, "mod de base conservé");
|
||||||
|
assert(one.name.startsWith("Épée Courte "), "suffixes de template ajoutés au nom");
|
||||||
|
const two = mp.applyGearTemplates({ kind: "gear", slot: "arme", name: "Épée Longue", twoHanded: true, mods: {} }, 100);
|
||||||
|
assert.strictEqual(two.templates.length, 6, "2 mains : 6 templates max");
|
||||||
|
assert.strictEqual(new Set(two.templates).size, 6, "templates tous distincts");
|
||||||
|
const none = mp.applyGearTemplates({ kind: "gear", slot: "arme", name: "Gourdin", twoHanded: false, mods: { att: 2 } }, 0);
|
||||||
|
assert(!none.templates, "proba 0 : aucun template");
|
||||||
|
assert.strictEqual(none.name, "Gourdin", "nom inchangé sans template");
|
||||||
|
}
|
||||||
|
|
||||||
// Tuning admin : puissance des potions/parchemins et bonus d'équipement
|
// Tuning admin : puissance des potions/parchemins et bonus d'équipement
|
||||||
{
|
{
|
||||||
const w = makeWorld({ monsterTarget: 0, itemTarget: 0 });
|
const w = makeWorld({ monsterTarget: 0, itemTarget: 0 });
|
||||||
|
|||||||
Reference in New Issue
Block a user