2.4.0 : gestion du sac — déséquiper, goinfrer (MIAM/CLONK/GRRROUAR), jeter à terre, sac classé par type avec effets visibles

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
perco
2026-06-12 23:23:01 +02:00
co-authored by Claude Fable 5
parent 031d64830c
commit 0ac2971f03
8 changed files with 392 additions and 46 deletions
+49
View File
@@ -319,6 +319,55 @@ function makeWorld(over = {}) {
assert(!mp.newTroll(w, "Banni", "Skrim").error, "son nom redevient libre");
}
// Sac : déséquiper, goinfrer, jeter
{
const gearLib = require("../js/gear.js");
const w = makeWorld({ monsterTarget: 0, itemTarget: 0 });
const { troll: t } = mp.newTroll(w, "Sakatrõll", "Durakuir");
t.pa = 6;
const armure = gearLib.gearItemByName("armure", "Armure de bois"); // arm +6, pv +15
t.bag.push(armure);
// équiper puis déséquiper : l'objet revient au sac, PV max restaurés
const basePvMax = t.pvMax;
mp.action(w, t, { type: "use", idx: 0 });
assert.strictEqual(t.pvMax, basePvMax + 15, "PV max d'équipement appliqués");
assert(t.equip.armure, "armure équipée");
let r = mp.action(w, t, { type: "unequip", slot: "armure" });
assert(r.ok, "déséquipement accepté");
assert(!t.equip.armure, "emplacement vidé");
assert.strictEqual(t.bag.length, 1, "l'objet est revenu dans le sac");
assert.strictEqual(t.pvMax, basePvMax, "PV max restaurés");
assert.strictEqual(t.gearMods.arm, 0, "modificateurs recalculés");
assert(mp.action(w, t, { type: "unequip", slot: "casque" }).error, "emplacement vide refusé");
// jeter : l'objet se retrouve au sol sur la case du troll
r = mp.action(w, t, { type: "drop", idx: 0 });
assert(r.ok, "jet accepté");
assert.strictEqual(t.bag.length, 0, "sac vidé");
const ground = w.items.find(i => i.x === t.x && i.y === t.y);
assert(ground && ground.name === "Armure de bois", "l'objet est à terre");
// case occupée : on ne peut pas jeter par-dessus
t.bag.push(gearLib.gearItemByName("arme", "Torche"));
assert(mp.action(w, t, { type: "drop", idx: 0 }).error, "case occupée refusée");
// on peut le re-ramasser
r = mp.action(w, t, { type: "pickup" });
assert(r.ok && t.bag.some(i => i.name === "Armure de bois"), "objet re-ramassé");
// goinfrer : objet détruit, un des trois effets appliqué
t.pa = 6;
t.pv = 1; // pour voir un éventuel soin MIAM
const before = t.bag.length;
const idxTorche = t.bag.findIndex(i => i.name === "Torche");
r = mp.action(w, t, { type: "eat", idx: idxTorche });
assert(r.ok, "goinfre accepté");
assert.strictEqual(t.bag.length, before - 1, "objet détruit");
const fed = t.pv > 1 || (t.potionEffects || []).some(e => e.name.includes("Goinfre"));
assert(fed, "un effet MIAM/CLONK/GRRROUAR s'est appliqué");
const lastLogs = t.privLog.slice(-2).map(l => l.msg).join(" ");
assert(/MIAM|CLONK|GRRROUAR/.test(lastLogs), "cri du goinfre dans le rapport");
// on ne goinfre pas une potion
t.bag.push(require("../js/potions.js").makePotionItem("guerison", 3));
assert(mp.action(w, t, { type: "eat", idx: t.bag.length - 1 }).error, "potion non goinfrable");
}
// Base de référence : les retouches survivent au redémarrage (seed non destructif)
{
const os = require("os");
+37
View File
@@ -400,6 +400,43 @@ assert.strictEqual(g.itemFromSpec({ x: 1, y: 1, kind: "armor", idx: 0 }).name, "
}
}
// Goinfre : les trois issues existent, X borné 1-3, effets temporaires posés
{
const counts = { MIAM: 0, CLONK: 0, GRRROUAR: 0 };
for (let i = 0; i < 300; i++) {
const t = { pv: 10, pvMax: 100, potionEffects: [] };
const r = p.goinfreItem(t, g.rollDice);
counts[r.cry]++;
if (r.cry === "MIAM") {
assert(t.pv > 10 && t.pv <= 10 + 9, "MIAM soigne entre 1 et 3D3");
} else {
const fx = t.potionEffects[0];
assert(fx && fx.turnsLeft >= 1 && fx.turnsLeft <= 3, "effet de 1 à 3 tours");
if (r.cry === "CLONK") assert(fx.armor >= 1 && fx.armor <= 3, "CLONK : armure +1 à +3");
if (r.cry === "GRRROUAR") assert(fx.degFlat >= 1 && fx.degFlat <= 3, "GRRROUAR : DEG +1 à +3");
}
}
assert(counts.MIAM && counts.CLONK && counts.GRRROUAR, "les trois cris sortent : " + JSON.stringify(counts));
}
// Déséquiper : retour au sac, PV max et modificateurs recalculés
{
const gearLib = require("../js/gear.js");
const t = { att: 3, esq: 3, deg: 3, reg: 1, vue: 3, armor: 0, armorDice: 0, degBonus: 0, pvMax: 30, pv: 30,
potionEffects: [], blockCamoTurns: 0, tour: 1, bag: [],
equip: { arme: null, armure: null, casque: null, bouclier: null, talisman: null, bottes: null }, gearMods: null };
g.equipGear(t, gearLib.gearItemByName("armure", "Armure de bois")); // arm +6, pv +15
assert.strictEqual(t.pvMax, 45, "PV max +15");
t.pv = 45;
const old = g.unequipToBag(t, "armure", () => {});
t.gearMods = gearLib.gearMods(t.equip);
assert.strictEqual(old.name, "Armure de bois", "pièce retirée");
assert.strictEqual(t.bag.length, 1, "revenue au sac");
assert.strictEqual(t.pvMax, 30, "PV max restaurés");
assert.strictEqual(t.pv, 30, "PV bornés au nouveau max");
assert.strictEqual(t.gearMods.arm, 0, "modificateurs recalculés");
}
// Validation serveur des niveaux
const srv = require("../server.js");
const goodGrid = [];