Files
percoandClaude Opus 4.8 00a4669928 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>
2026-06-14 00:17:53 +02:00

160 lines
9.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Équipement MountyHall — 6 emplacements, objets dropables actuels de la Mountypedia :
* https://mountypedia.mountyhall.com/Mountyhall/{Arme,Armure,Casque,Bouclier,Talisman,Bottes}
* Valeurs de base (sans templates) : TOUS les bonus sont fixes — ATT/ESQ/DEG/REG
* s'ajoutent aux jets (jamais en dés), ARM/VUE/PV sont fixes, RM/MM en %.
* Comme l'armure, l'ATT et les DEG existent en physique et en magique :
* `att`/`deg`/`arm` = bonus physiques (appliqués aux attaques physiques,
* armure physique), `attMag`/`degMag`/`armMag` = bonus magiques (appliqués
* aux sortilèges, armure magique). ESQ/PV/VUE restent simples. Les valeurs
* magiques de la Mountypedia n'existant pas, elles sont à 0 en vanilla et se
* règlent via l'admin du monde partagé.
* `tier` (1-5) borne la profondeur de drop.
* Les armes à deux mains (twoHanded) sont incompatibles avec un bouclier. */
"use strict";
const GEAR_SLOTS = {
arme: { label: "Arme", emoji: "🗡️" },
armure: { label: "Armure", emoji: "🦺" },
casque: { label: "Casque", emoji: "🪖" },
bouclier: { label: "Bouclier", emoji: "🛡️" },
talisman: { label: "Talisman", emoji: "📿" },
bottes: { label: "Bottes", emoji: "🥾" },
};
const GEAR = {
arme: [
{ name: "Torche", emoji: "🔥", tier: 1, mods: { vue: 1 } },
{ name: "Lame en os", emoji: "🦴", tier: 1, mods: { mmPct: 5 } },
{ name: "Épée Courte", emoji: "🗡️", tier: 1, mods: { deg: 2, esq: 2 } },
{ name: "Gantelet", emoji: "🥊", tier: 1, mods: { att: -1, esq: 1, deg: 1, arm: 2 } },
{ name: "Lame en pierre", emoji: "🪨", tier: 2, mods: { att: -1, deg: 4, reg: 2 } },
{ name: "Épée Longue", emoji: "⚔️", tier: 2, mods: { att: -1, deg: 4, esq: 1 } },
{ name: "Gourdin", emoji: "🏏", tier: 2, mods: { att: 2, esq: -2, deg: 5 } },
{ name: "Boulet et chaîne", emoji: "⛓️", tier: 2, mods: { att: -1, deg: 5 } },
{ name: "Grosse racine", emoji: "🌿", tier: 2, mods: { esq: -1, arm: 2, pv: 5, rmPct: 5 } },
{ name: "Coutelas d'obsidienne", emoji: "🔪", tier: 3, mods: { att: 2, deg: 3, esq: 2, reg: -2, rmPct: -5, mmPct: -15 } },
{ name: "Fouet", emoji: "🪢", tier: 4, mods: { att: 7, deg: -1 } },
{ name: "Lame d'obsidienne", emoji: "🖤", tier: 4, mods: { att: 3, deg: 7, reg: -3, rmPct: -30, mmPct: -10 } },
{ name: "Bâton de mage", emoji: "🪄", tier: 3, twoHanded: true, mods: { mmPct: 15 } },
{ name: "Hallebarde", emoji: "🪓", tier: 4, twoHanded: true, mods: { att: -5, deg: 12 } },
{ name: "Hache à deux mains d'Obsidienne", emoji: "⚒️", tier: 5, twoHanded: true, mods: { att: -8, deg: 16, reg: -4, rmPct: -45, mmPct: -15 } },
],
armure: [
{ name: "Culotte en Cuir", emoji: "🩲", tier: 1, mods: { esq: 5, rmPct: 60 } },
{ name: "Pagne en Cuir", emoji: "🧵", tier: 1, mods: { esq: 4, arm: 2, rmPct: 50 } },
{ name: "Armure de cuir", emoji: "🦺", tier: 2, mods: { esq: 3, arm: 4, rmPct: 40 } },
{ name: "Robe de mage", emoji: "🥻", tier: 2, mods: { esq: 2, arm: 1, rmPct: 10, mmPct: 10 } },
{ name: "Fourrures", emoji: "🐻", tier: 2, mods: { esq: 2, arm: 6, rmPct: 30 } },
{ name: "Cuir bouilli", emoji: "🟤", tier: 3, mods: { esq: 1, arm: 8, rmPct: 20 } },
{ name: "Armure de peaux", emoji: "🐗", tier: 3, mods: { esq: -3, arm: 9, rmPct: 70 } },
{ name: "Armure de bois", emoji: "🪵", tier: 3, mods: { esq: -3, arm: 6, pv: 15, rmPct: 50 } },
{ name: "Cuirasse d'Ossements", emoji: "💀", tier: 4, mods: { esq: -3, arm: 6, rmPct: 50, mmPct: 20 } },
{ name: "Armure d'Anneaux", emoji: "⭕", tier: 4, mods: { esq: -4, arm: 8, rmPct: 90 } },
{ name: "Haubert de mailles", emoji: "🥋", tier: 4, mods: { esq: -3, arm: 9, rmPct: 70 } },
{ name: "Armure de plates", emoji: "🛡️", tier: 5, mods: { esq: -2, arm: 10, rmPct: 50 } },
{ name: "Armure de pierre", emoji: "🗿", tier: 5, mods: { esq: -6, arm: 12, reg: 3, rmPct: 75 } },
],
casque: [
{ name: "Chapeau pointu", emoji: "🎩", tier: 1, mods: { esq: 3, mmPct: 5 } },
{ name: "Lorgnons", emoji: "👓", tier: 1, mods: { vue: 1, mmPct: 5 } },
{ name: "Couronne de ronces", emoji: "🌹", tier: 2, mods: { deg: 3, pv: -5 } },
{ name: "Couronne d'obsidienne", emoji: "👑", tier: 2, mods: { reg: -1, arm: 1, rmPct: 5 } },
{ name: "Casque à Cornes", emoji: "🐮", tier: 3, mods: { deg: 1, arm: 3, vue: -1, rmPct: 5 } },
{ name: "Casque à Pointes", emoji: "🦔", tier: 3, mods: { att: 1, deg: 1, arm: 3, vue: -1 } },
{ name: "Couronne de Cristal", emoji: "💎", tier: 3, mods: { vue: 3, mmPct: 5 } },
{ name: "Heaume", emoji: "🪖", tier: 4, mods: { att: -1, arm: 4, vue: -2, rmPct: 10 } },
{ name: "Turban", emoji: "👳", tier: 4, mods: { rmPct: 15, mmPct: 15 } },
],
bouclier: [
{ name: "Targe", emoji: "🎯", tier: 1, mods: { esq: 3, att: 1 } },
{ name: "Grimoire", emoji: "📖", tier: 2, mods: { mmPct: 5 }, note: "DLA +15 mn (non simulé)" },
{ name: "Rondache en Bois", emoji: "🪵", tier: 2, mods: { esq: 1, arm: 3, pv: 5 } },
{ name: "Bouclier à Pointes", emoji: "🛡️", tier: 3, mods: { att: 1, deg: 1, arm: 4 } },
{ name: "Gros'Porte", emoji: "🚪", tier: 4, mods: { arm: 5, rmPct: 10 } },
],
talisman: [
{ name: "Collier à Pointes", emoji: "📿", tier: 1, mods: { deg: 2, arm: 1 } },
{ name: "Talisman de pierre", emoji: "🪨", tier: 2, mods: { reg: 2, rmPct: 10, mmPct: 10 } },
{ name: "Gorgeron en métal", emoji: "⚙️", tier: 2, mods: { reg: -1, arm: 3, rmPct: 10 } },
{ name: "Œil de sang", emoji: "🩸", tier: 3, mods: { pv: -5, mmPct: 10 } },
{ name: "Pendentif Incandescent", emoji: "🔥", tier: 3, mods: { esq: 3, rmPct: 10, mmPct: 10 } },
{ name: "Torque en pierre", emoji: "🌀", tier: 4, mods: { reg: -2, rmPct: 20, mmPct: 20 } },
{ name: "Talisman d'Obsidienne", emoji: "🖤", tier: 5, mods: { att: 1, deg: 2, reg: -4, rmPct: 22, mmPct: 22 } },
],
bottes: [
{ name: "Bottes", emoji: "🥾", tier: 1, mods: { esq: 3 } },
{ name: "Jambière en cuir", emoji: "🦵", tier: 2, mods: { esq: 1, arm: 2, rmPct: 5 } },
{ name: "Jambière en os", emoji: "🦴", tier: 2, mods: { esq: -1, arm: 2, rmPct: 5, mmPct: 5 } },
{ name: "Souliers dorés", emoji: "👞", tier: 3, mods: { esq: -1, arm: 1 } },
{ name: "Jambière en mailles", emoji: "⛓️", tier: 3, mods: { arm: 3, rmPct: 5 } },
{ name: "Jambière en métal", emoji: "🦿", tier: 4, mods: { esq: -1, arm: 4, rmPct: 5 } },
],
};
/* Anciennes listes (v1 « bonus simple ») : mapping pour les niveaux déjà publiés. */
const LEGACY_WEAPON_NAMES = ["Épée Courte", "Gourdin", "Épée Longue", "Lame d'obsidienne"];
const LEGACY_ARMOR_NAMES = ["Armure de cuir", "Haubert de mailles", "Armure de plates"];
const GEAR_MOD_LABELS = [
["att", "ATT", ""], ["attMag", "ATT mag.", ""], ["esq", "ESQ", ""],
["deg", "DEG", ""], ["degMag", "DEG mag.", ""], ["reg", "REG", ""],
["arm", "Armure phy.", ""], ["armMag", "Armure mag.", ""],
["vue", "VUE", ""], ["pv", "PV", ""],
["rmPct", "RM", " %"], ["mmPct", "MM", " %"],
["tour", "TOUR", " mn"], // apporté par les templates de drop (modif de DLA)
];
function formatGearMods(mods, sep = " · ") {
const parts = [];
for (const [key, label, unit] of GEAR_MOD_LABELS) {
const v = mods[key];
if (!v) continue;
parts.push(`${label} ${v > 0 ? "+" : ""}${Math.abs(v)}${unit}`);
}
return parts.join(sep);
}
function makeGearItem(slot, idx) {
const list = GEAR[slot];
if (!list) return null;
const def = list[((idx % list.length) + list.length) % list.length];
return {
kind: "gear", slot, gearIdx: list.indexOf(def),
name: def.name, emoji: def.emoji,
mods: { ...def.mods }, twoHanded: !!def.twoHanded, note: def.note,
};
}
function gearItemByName(slot, name) {
const idx = (GEAR[slot] || []).findIndex(g => g.name === name);
return idx >= 0 ? makeGearItem(slot, idx) : null;
}
/* Tire une pièce d'équipement adaptée à la profondeur (tier ≤ depth + 1). */
function makeRandomGear(depth, slot = null) {
const slots = Object.keys(GEAR);
const s = slot && GEAR[slot] ? slot : slots[Math.floor(Math.random() * slots.length)];
const pool = GEAR[s].filter(g => g.tier <= (depth || 1) + 1);
const list = pool.length ? pool : GEAR[s];
const def = list[Math.floor(Math.random() * list.length)];
return makeGearItem(s, GEAR[s].indexOf(def));
}
/* Somme des modificateurs de l'équipement porté ({ arme: item|null, … }). */
function gearMods(equip) {
const total = { att: 0, attMag: 0, esq: 0, deg: 0, degMag: 0, reg: 0, arm: 0, armMag: 0, vue: 0, pv: 0, rmPct: 0, mmPct: 0 };
for (const item of Object.values(equip || {})) {
if (!item || !item.mods) continue;
for (const k of Object.keys(total)) total[k] += item.mods[k] || 0;
}
return total;
}
if (typeof module !== "undefined" && module.exports) {
module.exports = {
GEAR, GEAR_SLOTS, GEAR_MOD_LABELS, LEGACY_WEAPON_NAMES, LEGACY_ARMOR_NAMES,
makeGearItem, gearItemByName, makeRandomGear, gearMods, formatGearMods,
};
}