Compare commits

..

5 Commits
9.4.5 ... 9.5.1

Author SHA1 Message Date
df1e0b9952 Amélioration robustess et vérification sur import des modules
All checks were successful
Validation JSON / validate (push) Successful in 16s
Release Creation / build (release) Successful in 48s
2026-04-14 08:26:08 +02:00
b1e96af421 UPdsate pour support v14
All checks were successful
Validation JSON / validate (push) Successful in 15s
Release Creation / build (release) Successful in 49s
2026-04-11 20:23:24 +02:00
17d865b60b Fix sur maladies again
All checks were successful
Validation JSON / validate (push) Successful in 17s
Release Creation / build (release) Successful in 1m12s
2026-03-24 11:33:50 +01:00
5f4e0c7ce5 Nombreuses corrections sur les maladies et symptomes
All checks were successful
Validation JSON / validate (push) Successful in 24s
2026-03-22 20:41:58 +01:00
8862698262 Nombreuses corections de scripts...
All checks were successful
Validation JSON / validate (push) Successful in 15s
Release Creation / build (release) Successful in 57s
2026-03-07 15:02:18 +01:00
95 changed files with 10009 additions and 35630 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.history/
.idea/
.github/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{
"label": "Items (Lustria)",
"folders": {
"folders": {
"Careers": "Carrières",
"Spells": "Sorts",
"Trappings": "Possessions",
@@ -69,16 +69,10 @@
"converter": "generic_localization"
},
"durationValue": "system.duration.value",
"durationUnit": {
"path": "system.duration.unit",
"converter": "disease_duration_unit"
},
"durationUnit": "system.duration.unit",
"contraction": "system.contraction.value",
"incubationValue": "system.incubation.value",
"incubationUnit": {
"path": "system.incubation.unit",
"converter": "disease_duration_unit"
},
"incubationUnit": "system.incubation.unit",
"symptoms": "system.symptoms.value",
"permanent": "system.permanent.value",
"special": "system.special.value",

View File

@@ -1,6 +1,6 @@
{
"label": "Items (Old World Bundle II)",
"folders": {
"folders": {
"Lore of the Beasts": "Domaine des Bêtes",
"Lore of Death": "Domaine de la Mort",
"Lore of Fire": "Domaine du Feu",
@@ -78,16 +78,10 @@
"converter": "generic_localization"
},
"durationValue": "system.duration.value",
"durationUnit": {
"path": "system.duration.unit",
"converter": "disease_duration_unit"
},
"durationUnit": "system.duration.unit",
"contraction": "system.contraction.value",
"incubationValue": "system.incubation.value",
"incubationUnit": {
"path": "system.incubation.unit",
"converter": "disease_duration_unit"
},
"incubationUnit": "system.incubation.unit",
"symptoms": "system.symptoms.value",
"permanent": "system.permanent.value",
"special": "system.special.value",

View File

@@ -1,6 +1,6 @@
{
"label": "Items (Imperial Zoo)",
"folders": {
"folders": {
"Ammunition": "Munitions",
"Careers": "Carrières",
"Prayers": "Prières",
@@ -70,16 +70,10 @@
"converter": "generic_localization"
},
"durationValue": "system.duration.value",
"durationUnit": {
"path": "system.duration.unit",
"converter": "disease_duration_unit"
},
"durationUnit": "system.duration.unit",
"contraction": "system.contraction.value",
"incubationValue": "system.incubation.value",
"incubationUnit": {
"path": "system.incubation.unit",
"converter": "disease_duration_unit"
},
"incubationUnit": "system.incubation.unit",
"symptoms": "system.symptoms.value",
"permanent": "system.permanent.value",
"special": "system.special.value",

View File

@@ -807,6 +807,9 @@
"Infinite": "Infini",
"any one": "Au choix",
"Sing": "Chant",
"Months":"mois",
"Years":"années",
"ENTITY.Actor": "Acteur",
"BROWSER.ItemCategories": "Catégorie d'items",
"BROWSER.IncludeWorld": "Inclure les objets du monde ?",
@@ -1884,6 +1887,7 @@
"Drunken Vomit": "Vomi Alcoolisé",
"Thin People": "Gens maigres",
"Moderate": "Modéré",
"Severe": "Grave",
"Greenskins": "Peaux vertes",
"Elves": "Elfes",
"Rich folks": "Gens riches",
@@ -2620,7 +2624,7 @@
"SHEET.Randomize": "Aléatoire",
"SHEET.RequiredTrappingsError": "Impossible de lancer les revenus sans avoir tous les équipements requis dans cette carrière !",
"Sheet.RollIncome": "Revenu",
"WFRP4E.Symptom.CoughsandSneezes": "Toux et Éternuements",
"WFRP4E.Symptom.CoughsandSneezes": "Toux et éternuements",
"WFRP4E.Symptom.OrganFailure": "Défaillance Organique",
"WFRP4E.SymptomDescriptions.OrganFailure": "Une partie de votre corps est gravement affectée par la maladie. Durant l'évolution de la maladie, tous les Tests de Perception reposant sur la vue subissent une pénalité de 3 DS.",
"WFRP4E.SymptomTreatment.OrganFailure": "Des onguents apaisants peuvent être achetés chez tout bon apothicaire — dix doses pour un shilling. L'application d'une dose atténue le symptôme pendant [[/r 1d10]] heures.",

View File

@@ -124,7 +124,7 @@
"id": "wh4-fr-translation",
"compatibility": {
"minimum": "13",
"verified": "13"
"verified": "14"
},
"relationships": {
"systems": [

View File

@@ -16,30 +16,27 @@ const _patch_eis = () => {
game.wfrp4e.config.symptomTreatment["swelling"] = "La plupart des traitements consistent à plonger la partie affectée, ou parfois tout le corps, dans un bain d'eau glacée pour réduire la chaleur qui accompagne les gonflements. Un <b> Test de Guérison Difficile (-20) étendu </b> nécessitant +3 DR réduit le renflement de <b> <a class ='chat-roll'> 2d10 </a> </b> heures. Chaque test dure une heure. Le patient se retrouve avec l'Etat Exténué +1 pour chaque test effectué au cours du processus. <br> <br> A la place, certains médecins saignent le patient avec une lame ou des sangsues. Un <b>Test de Guérison étendu </b> réussi nécessitant +4 SL et des Outils (médecin) réduit le renflement de (<a class ='chat-roll'> 1d10 </a> + Bonus d'Endurance du patient) heures. Chaque test a une difficulté de base <b> impossible (-50) </b> et dure une demi-heure.";
game.wfrp4e.config.loreEffects["tzeentch"] = {
label: "Domaine de Tzeentch",
icon: "modules/wfrp4e-core/icons/spells/tzeentch.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "apply",
"effectTrigger": "oneTime",
"lore": true,
"script": `
if (this.actor.isOwner)
args.actor.setupSkill("Résistance", {context : {failure: "1 Point de Corruption reçu", success : "1 Point de Chance gagné"}}).then(setupData => {
args.actor.basicTest(setupData).then(test =>
{
if (test.result.result == "success" && args.actor.type == "character")
{
args.actor.update({"system.status.fortune.value" : args.actor.system.status.fortune.value + 1})
}
else if (test.result.result == "failure" && args.actor.type == "character")
{
args.actor.update({"system.status.corruption.value" : args.actor.system.status.corruption.value + 1})
}
})
})`
}
name: "Domaine de Tzeentch",
img: "modules/wfrp4e-core/icons/spells/tzeentch.png",
system: {
transferData: {
type: "target"
},
scriptData: [{
trigger: "immediate",
label: "Test d'Endurance",
script: `
this.actor.setupSkill(game.i18n.localize("NAME.Endurance"), {appendTitle : " - " + this.effect.name, context : {failure: "1 Point de Corruption reçu", success : "1 Point de Chance gagné"}}).then(setupData => {
this.actor.basicTest(setupData).then(test => {
if (test.succeeded && this.actor.type == "character") {
this.actor.update({"system.status.fortune.value" : this.actor.system.status.fortune.value + 1})
} else if (test.failed && this.actor.type == "character") {
this.actor.update({"system.status.corruption.value" : this.actor.system.status.corruption.value + 1})
}
})
})
return false;`
}]
}
}
}
@@ -404,6 +401,56 @@ Hooks.on('ready', () => {
// Patch function for effects
game.wfrp4e.utility.findKey = warhammer.utility.findKey
// Patch SpellModel.computeSpellDamage to handle English characteristic bonus names.
// Babele instantiates actors TWICE: first with untranslated data (English formulas like
// "Willpower Bonus+4"), then with translated data. The first pass fails because
// characteristicsBonus values are already French ("Bonus de Force Mentale").
// This patch pre-replaces English bonus names with numeric values before the original
// formula evaluation runs, preventing SyntaxErrors and notification spam.
const __EN_BONUS_TO_CHAR = {
"weapon skill bonus": "ws", "ballistic skill bonus": "bs",
"strength bonus": "s", "toughness bonus": "t",
"initiative bonus": "i", "agility bonus": "ag",
"dexterity bonus": "dex", "intelligence bonus": "int",
"willpower bonus": "wp", "fellowship bonus": "fel"
};
const SpellModel = CONFIG.Item.dataModels?.["spell"];
if (SpellModel?.prototype?.computeSpellDamage) {
const _origComputeSpellDamage = SpellModel.prototype.computeSpellDamage;
SpellModel.prototype.computeSpellDamage = function(formula, options) {
if (typeof formula === "string") {
const actor = options?.actor || this.parent?.actor;
if (actor?.system?.characteristics) {
let f = formula.toLowerCase();
for (const [enName, ch] of Object.entries(__EN_BONUS_TO_CHAR)) {
if (f.includes(enName)) {
const bonus = actor.system.characteristics[ch]?.bonus ?? 0;
f = f.replace(enName, bonus);
}
}
formula = f;
}
}
return _origComputeSpellDamage.call(this, formula, options);
};
}
// Patch postSymptom to handle English symptom names in @Symptom[...] links.
// After i18nInit, config.symptoms values are French strings (e.g. "Fièvre"), so
// findKey("Fever", config.symptoms) fails. We normalize via game.i18n.localize first.
const _origPostSymptom = game.wfrp4e.utility.postSymptom.bind(game.wfrp4e.utility);
game.wfrp4e.utility.postSymptom = async function(symptom) {
const baseName = symptom.split("(")[0].trim();
const symkey = warhammer.utility.findKey(baseName, game.wfrp4e.config.symptoms);
if (!symkey) {
const localizedBase = game.i18n.localize(baseName);
if (localizedBase !== baseName) {
symptom = symptom.replace(baseName, localizedBase);
}
}
return _origPostSymptom(symptom);
};
/** New modifiers */
game.wfrp4e.config.difficultyModifiers = {
"veasy": 60,

View File

@@ -30,6 +30,20 @@ const __SELECT_BONUS_PREFIX_D = {
"agilité": 1
}
// Map English characteristic names (as they appear in spell formulas) to WFRP4e abbreviations
const __CHAR_EN_TO_ABBR = {
"weapon skill": "ws",
"ballistic skill": "bs",
"strength": "s",
"toughness": "t",
"initiative": "i",
"agility": "ag",
"dexterity": "dex",
"intelligence": "int",
"willpower": "wp",
"fellowship": "fel"
}
/************************************************************************************/
export class WFRP4FrTranslation {
@@ -57,16 +71,23 @@ export class WFRP4FrTranslation {
if (value == "You") return "Vous"; // Hop !
if (value == "Instant") return "Instantané"; // Hop !
let translw = value;
let re = /(.*)\s+[Bb]onus\s*(\w*)/i;
let re = /(.*)\s+[Bb]onus\s*(.*)/i; // (.*) at end captures modifiers like "+4"
let res = re.exec(value);
let unit = "";
if (res) { // Test "<charac> Bonus <unit>" pattern
if (res) { // Test "<charac> Bonus <modifier>" pattern
if (res[1]) { // We have char name, then convert it
translw = game.i18n.localize(res[1].trim());
let bonusPrefix = (translw.toLowerCase() in __SELECT_BONUS_PREFIX_D) ? "Bonus d'" : "Bonus de ";
translw = bonusPrefix + translw
const charEN = res[1].trim().toLowerCase();
const abbr = __CHAR_EN_TO_ABBR[charEN];
if (abbr && game.wfrp4e?.config?.characteristicsBonus?.[abbr]) {
// Use the localized French bonus name from config (already resolved by localizeConfig at i18nInit)
translw = game.wfrp4e.config.characteristicsBonus[abbr];
} else {
translw = game.i18n.localize(res[1].trim());
let bonusPrefix = (translw.toLowerCase() in __SELECT_BONUS_PREFIX_D) ? "Bonus d'" : "Bonus de ";
translw = bonusPrefix + translw;
}
}
unit = res[2];
unit = res[2]; // may be "+4", "-2", "2", "yards", etc.
} else {
re = /(\d+) (\w+)/i;
res = re.exec(value);
@@ -90,6 +111,8 @@ export class WFRP4FrTranslation {
if (unit == "Bonus") { // Another weird management
console.log("Translating bonus", unit);
translw = "Bonus de " + translw;
} else if (unit && /^[+\-*\/]/.test(unit)) {
translw += unit; // No space before operators like "+4"
} else {
translw += " " + unit;
}
@@ -278,8 +301,25 @@ Hooks.once('init', () => {
"process_effects": (effectsData, translations, data, tc, tc_translations) => {
//console.log("Effects :", effectsData, translations, data, tc, tc_translations)
for (let e of effectsData) {
// Foundry v13 requires name; migrate legacy data where only label was stored
if (e.name == null) e.name = e.label || "";
let origName = e.name
e.name = tc_translations.name || game.i18n.localize(e.name)
// Symptom effects have their own name (Fever, Malaise, etc.) — don't overwrite with the parent item name
if (e.flags?.wfrp4e?.symptom) {
let symName = e.name;
let gravity = "";
if (symName.includes("(") && symName.includes(")")) {
let re = /(.*) +\((.*)\)/i;
let res = re.exec(symName);
if (res) {
symName = res[1].trim();
gravity = " (" + game.i18n.localize(res[2].trim()) + ")";
}
}
e.name = game.i18n.localize(symName) + gravity;
} else {
e.name = tc_translations.name || game.i18n.localize(e.name) || e.label || ""
}
if ( e.flags?.wfrp4e?.scriptData) {
for (let script of e.flags.wfrp4e.scriptData) {
if (script?.label) {
@@ -396,10 +436,18 @@ Hooks.once('init', () => {
return beast_traits
}
//console.log("TRANS:", beast_traits)
// Normalize: Foundry v13 requires ActiveEffect.name; migrate legacy v12 data
for (let trait_en of beast_traits) {
if (trait_en.effects) {
for (let eff of trait_en.effects) {
if (eff.name == null) eff.name = eff.label || "";
}
}
}
for (let trait_en of beast_traits) {
let special = "";
let nbt = "";
let name_en = trait_en.name.trim(); // strip \r in some traits name
let name_en = trait_en.name.replace(/\r/g, '').trim(); // strip \r (including internal) in some traits name
if (!trait_en.name || trait_en.name.length == 0) {
console.log("Wrong item name found!!!!")
continue
@@ -415,8 +463,11 @@ Hooks.once('init', () => {
} else if (name_en.includes("(") && name_en.includes(")")) { // Then process specific traits name with (xxxx) inside
let re = /(.*) \((.*)\)/i;
let res = re.exec(name_en);
name_en = res[1]; // Get the root traits name
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
if (!res) { console.warn("WFRP4E-FR | bestiary_traits: regex failed for trait:", name_en); }
else {
name_en = res[1]; // Get the root traits name
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
}
}
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("trait")
for (let compData of validCompendiums) {
@@ -438,8 +489,11 @@ Hooks.once('init', () => {
if (name_en.includes("(") && name_en.includes(")")) { // Then process specific skills name with (xxxx) inside
let re = /(.*) +\((.*)\)/i;
let res = re.exec(name_en);
name_en = res[1].trim(); // Get the root skill name
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
if (!res) { console.warn("WFRP4E-FR | bestiary_traits: regex failed for skill:", name_en); }
else {
name_en = res[1].trim(); // Get the root skill name
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
}
}
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("skill")
for (let compData of validCompendiums) {
@@ -484,8 +538,11 @@ Hooks.once('init', () => {
if (name_en.includes("(") && name_en.includes(")")) { // Then process specific skills name with (xxxx) inside
let re = /(.*) +\((.*)\)/i;
let res = re.exec(name_en);
name_en = res[1].trim(); // Get the root talent name, no parenthesis this time...
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
if (!res) { console.warn("WFRP4E-FR | bestiary_traits: regex failed for talent:", name_en); }
else {
name_en = res[1].trim(); // Get the root talent name, no parenthesis this time...
special = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
}
}
let validCompendiums = game.wfrp4e.tags.getPacksWithTag("talent")
for (let compData of validCompendiums) {
@@ -650,15 +707,15 @@ Hooks.once('init', () => {
if (!effects) return;
for (const element of effects) {
let effect = element;
let label = effect.label;
let name = effect.name || effect.label;
let gravity = "";
if (label.includes("(") && label.includes(")")) { // Then process specific skills name with (xxxx) inside
if (name.includes("(") && name.includes(")")) {
let re = /(.*) +\((.*)\)/i;
let res = re.exec(label);
label = res[1].trim(); // Get the gravity
gravity = " (" + game.i18n.localize(res[2].trim()) + ")"; // And the special keyword
let res = re.exec(name);
name = res[1].trim();
gravity = " (" + game.i18n.localize(res[2].trim()) + ")";
}
effect.label = game.i18n.localize(label) + gravity;
effect.name = game.i18n.localize(name) + gravity;
}
},
// Auto-translate duration

View File

@@ -145,6 +145,39 @@ export class WH4FRPatchConfig {
}
}
/************************************************************************************/
static patch_symptom_severity() {
// The core module's symptomEffects scripts check for English severity terms
// ("Moderate", "Severe") in this.effect.name. French disease compendiums use
// translated severity ("Modéré(e)", "Grave"), so the checks would fail.
// We patch the relevant scriptData to also check for French severity terms.
const effects = game.wfrp4e.config.symptomEffects;
if (!effects) return;
// Patch blight: checks Moderate → easy, Severe → average, else → veasy
for (const key of ["blight", "gangrene"]) {
const effect = effects[key];
if (!effect?.system?.scriptData) continue;
for (const sd of effect.system.scriptData) {
if (sd.script && sd.script.includes('.includes("Moderate")')) {
sd.script = sd.script
.replace('includes("Moderate")', 'includes("Moderate") || this.effect.name.includes("Modéré")')
.replace('includes("Severe")', 'includes("Severe") || this.effect.name.includes("Grave")');
}
}
}
// Patch convulsions: checks Moderate → -20, else → -10
if (effects.convulsions?.system?.scriptData) {
for (const sd of effects.convulsions.system.scriptData) {
if (sd.script && sd.script.includes('.includes("Moderate")')) {
sd.script = sd.script
.replace('includes("Moderate")', 'includes("Moderate") || this.effect.name.includes("Modéré")');
}
}
}
}
/************************************************************************************/
static fixSpeciesTable() {
@@ -192,16 +225,15 @@ export class WH4FRPatchConfig {
}
if (game.wfrp4e.config.loreEffects) {
game.wfrp4e.config.loreEffects["beasts"].label = "Domaine des Bêtes"
game.wfrp4e.config.loreEffects["death"].label = "Domaine de la Mort"
game.wfrp4e.config.loreEffects["fire"].label = "Domaine du Feu"
game.wfrp4e.config.loreEffects["metal"].label = "Domaine du Métal"
game.wfrp4e.config.loreEffects["heavens"].label = "Domaine des Cieux"
game.wfrp4e.config.loreEffects["life"].label = "Domaine de la Vie"
game.wfrp4e.config.loreEffects["light"].label = "Domaine de la Lumière"
game.wfrp4e.config.loreEffects["shadow"].label = "Domaine des Ombres"
game.wfrp4e.config.loreEffects["hedgecraft"].label = "Domaine de la Magie de Village"
game.wfrp4e.config.loreEffects["hedgecraft"].label = "Domaine de la Sorcellerie"
game.wfrp4e.config.loreEffects["beasts"].name = "Domaine des Bêtes"
game.wfrp4e.config.loreEffects["death"].name = "Domaine de la Mort"
game.wfrp4e.config.loreEffects["fire"].name = "Domaine du Feu"
game.wfrp4e.config.loreEffects["metal"].name = "Domaine du Métal"
game.wfrp4e.config.loreEffects["heavens"].name = "Domaine des Cieux"
game.wfrp4e.config.loreEffects["life"].name = "Domaine de la Vie"
game.wfrp4e.config.loreEffects["light"].name = "Domaine de la Lumière"
game.wfrp4e.config.loreEffects["shadow"].name = "Domaine des Ombres"
game.wfrp4e.config.loreEffects["hedgecraft"].name = "Domaine de la Sorcellerie"
}
if (game.wfrp4e.config.species) {
@@ -240,276 +272,10 @@ export class WH4FRPatchConfig {
this.patch_career();
game.wfrp4e.config.symptomEffects = {
"blight": {
label: "Toxine",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "invoke",
"symptom": true,
"script": `
let difficulty = ""
if (this.effect.label.includes("Modéré"))
difficulty = "easy"
else if (this.effect.label.includes("Sévère"))
difficulty = "average"
else
difficulty = "veasy"
if (args.actor.isOwner)
{
args.actor.setupSkill("Résistance", {absolute: {difficulty}}).then(setupData => {
args.actor.basicTest(setupData).then(test =>
{
if (test.result.outcome == "failure")
args.actor.addCondition("dead")
})
})
}`
}
}
},
"buboes": {
label: "Bubons",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "prefillDialog",
"symptom": true,
"script": `
let applicableCharacteristics = ["ws", "bs", "s", "fel", "ag", "t", "dex"]
if (args.type == "weapon")
args.prefillModifiers.modifier -= 10
else if (args.type == "characteristic")
{
if (applicableCharacteristics.includes(args.item))
args.prefillModifiers.modifier -= 10
}
else if (args.type == "skill")
{
if (applicableCharacteristics.includes(args.item.characteristic.value))
args.prefillModifiers.modifier -= 10
}
`}
}
},
"convulsions": {
label: "Convulsions",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "prefillDialog",
"symptom": true,
"script": `
let modifier = 0
if (this.effect.label.includes("Modéré"))
modifier = -20
else
modifier = -10
let applicableCharacteristics = ["ws", "bs", "s", "ag", "t", "dex"]
if (args.type == "weapon")
args.prefillModifiers.modifier += modifier
else if (args.type == "characteristic")
{
if (applicableCharacteristics.includes(args.item))
args.prefillModifiers.modifier += modifier
}
else if (args.type == "skill")
{
if (applicableCharacteristics.includes(args.item.characteristic.value))
args.prefillModifiers.modifier += modifier
}
}`
}
}
},
"fever": {
label: "Fièvre",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "prefillDialog",
"symptom": true,
"script": `
let applicableCharacteristics = ["ws", "bs", "s", "fel", "ag", "t", "dex"]
if (args.type == "weapon")
args.prefillModifiers.modifier -= 10
else if (args.type == "characteristic")
{
if (applicableCharacteristics.includes(args.item))
args.prefillModifiers.modifier -= 10
}
else if (args.type == "skill")
{
if (applicableCharacteristics.includes(args.item.characteristic.value))
args.prefillModifiers.modifier -= 10
}`,
"otherEffects": ["blight", "wounded"]
}
}
},
"flux": {
label: "Intoxication Alimentaire",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"symptom": true
}
}
},
"lingering": {
label: "Persistant",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"symptom": true
}
}
},
"coughsAndSneezes": {
label: "Toux et éternuements",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"symptom": true
}
}
},
"gangrene": {
label: "Gangrène",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "prefillDialog",
"symptom": true,
"script": `
if (args.type == "characteristic" && args.item == "fel")
{
if (args.item == "fel")
args.prefillModifiers.modifier -= 10
}
else if (args.type == "skill")
{
if (args.item.characteristic.value == "fel")
args.prefillModifiers.modifier -= 10
}
}`
}
}
},
"malaise": {
label: "Malaise",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "prepareData",
"symptom": true,
"script": `
if (game.user.isUniqueGM)
{
let fatigued = args.actor.hasCondition("fatigued")
if (!fatigued)
{
args.actor.addCondition("fatigued")
ui.notifications.notify("Etat Extenué ajouté à " + args.actor.name + ", qui ne peut pas être enlevé tant que le symptôme Malaise est présent.")
}
}
`
}
}
},
"nausea": {
label: "Nausée",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "rollTest",
"symptom": true,
"script": `
if (this.actor.isOwner && args.test.result.outcome == "failure")
{
let applicableCharacteristics = ["ws", "bs", "s", "fel", "ag", "t", "dex"]
if (applicableCharacteristics.includes(args.test.result.characteristic))
this.actor.addCondition("stunned")
else if (args.test.result.skill && applicableCharacteristics.includes(args.test.result.skill.system.characteristic.value))
this.actor.addCondition("stunned")
else if (args.test.result.weapon)
this.actor.addCondition("stunned")
}
`
}
}
},
"pox": {
label: "Démangeaisons",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "prefillDialog",
"symptom": true,
"script": `
if (args.type == "characteristic" && args.item == "fel")
args.prefillModifiers.modifier -= 10
else if (args.type == "skill")
{
if (args.item.characteristic.value == "fel")
args.prefillModifiers.modifier -= 10
}
}`
}
}
},
"wounded": {
label: "Blessé",
icon: "modules/wfrp4e-core/icons/diseases/disease.png",
transfer: true,
flags: {
wfrp4e: {
"effectApplication": "actor",
"effectTrigger": "invoke",
"symptom": true,
"script": `
if (args.actor.isOwner)
{
args.actor.setupSkill("Résistance", {absolute: {difficulty : "average"}}).then(setupData => {
args.actor.basicTest(setupData).then(test =>
{
if (test.result.outcome == "failure")
fromUuid("Compendium.wfrp4e-core.diseases.kKccDTGzWzSXCBOb").then(disease => {
args.actor.createEmbeddedDocuments("Item", [disease.toObject()])
})
})
})
}`
}
}
}
}
// Patch symptom severity scripts to support French severity terms
// The core module scripts check for "Moderate"/"Severe" in effect.name,
// but French disease compendiums use "Modéré(e)"/"Grave" instead.
this.patch_symptom_severity();
game.wfrp4e.config.effectApplication = {
"actor": "Acteur",

View File

@@ -155,7 +155,7 @@ export default class InnRoller {
}
// Extraire les informations du résultat
const resultText = rollResult.results[0]?.text || "Résultat inconnu";
const resultText = rollResult.results[0]?.name || rollResult.results[0]?.description || "Résultat inconnu";
const rollFormula = rollResult.roll?.formula || "1d100";
const rollTotal = rollResult.roll?.total || 0;
@@ -349,7 +349,7 @@ export default class InnRoller {
if (rollTable) {
try {
const roll = await rollTable.draw({ displayChat: false });
const resultText = roll.results[0]?.text || "Résultat inconnu";
const resultText = roll.results[0]?.name || roll.results[0]?.description || "Résultat inconnu";
results.push({
category: this.getCategoryName(tableName),
name: resultText,

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
MANIFEST-001311
MANIFEST-001339

View File

@@ -1,7 +1,7 @@
2026/03/07-00:45:53.808506 7fe8c37fe6c0 Recovering log #1309
2026/03/07-00:45:53.819278 7fe8c37fe6c0 Delete type=3 #1307
2026/03/07-00:45:53.819338 7fe8c37fe6c0 Delete type=0 #1309
2026/03/07-01:11:49.557068 7fe8c2ffd6c0 Level-0 table #1314: started
2026/03/07-01:11:49.557102 7fe8c2ffd6c0 Level-0 table #1314: 0 bytes OK
2026/03/07-01:11:49.563625 7fe8c2ffd6c0 Delete type=0 #1312
2026/03/07-01:11:49.577541 7fe8c2ffd6c0 Manual compaction at level-0 from '!journal!3IgmiprzLB6Lwenc' @ 72057594037927935 : 1 .. '!journal.pages!suuYN87Al1ZZWtQQ.jhgNnhWhrkOpKs1B' @ 0 : 0; will stop at (end)
2026/04/13-23:10:07.603169 7fddd97be6c0 Recovering log #1337
2026/04/13-23:10:07.613544 7fddd97be6c0 Delete type=3 #1335
2026/04/13-23:10:07.613619 7fddd97be6c0 Delete type=0 #1337
2026/04/13-23:14:52.439353 7fddca1c26c0 Level-0 table #1342: started
2026/04/13-23:14:52.439375 7fddca1c26c0 Level-0 table #1342: 0 bytes OK
2026/04/13-23:14:52.447401 7fddca1c26c0 Delete type=0 #1340
2026/04/13-23:14:52.447538 7fddca1c26c0 Manual compaction at level-0 from '!journal!3IgmiprzLB6Lwenc' @ 72057594037927935 : 1 .. '!journal.pages!suuYN87Al1ZZWtQQ.jhgNnhWhrkOpKs1B' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2026/03/05-20:38:02.743870 7f7930fff6c0 Recovering log #1305
2026/03/05-20:38:02.754744 7f7930fff6c0 Delete type=3 #1303
2026/03/05-20:38:02.754879 7f7930fff6c0 Delete type=0 #1305
2026/03/05-20:42:36.083534 7f78e15b46c0 Level-0 table #1310: started
2026/03/05-20:42:36.083606 7f78e15b46c0 Level-0 table #1310: 0 bytes OK
2026/03/05-20:42:36.090385 7f78e15b46c0 Delete type=0 #1308
2026/03/05-20:42:36.111635 7f78e15b46c0 Manual compaction at level-0 from '!journal!3IgmiprzLB6Lwenc' @ 72057594037927935 : 1 .. '!journal.pages!suuYN87Al1ZZWtQQ.jhgNnhWhrkOpKs1B' @ 0 : 0; will stop at (end)
2026/04/13-23:01:32.388566 7fddd97be6c0 Recovering log #1333
2026/04/13-23:01:32.399282 7fddd97be6c0 Delete type=3 #1331
2026/04/13-23:01:32.399346 7fddd97be6c0 Delete type=0 #1333
2026/04/13-23:07:12.207745 7fddca1c26c0 Level-0 table #1338: started
2026/04/13-23:07:12.207766 7fddca1c26c0 Level-0 table #1338: 0 bytes OK
2026/04/13-23:07:12.214487 7fddca1c26c0 Delete type=0 #1336
2026/04/13-23:07:12.214671 7fddca1c26c0 Manual compaction at level-0 from '!journal!3IgmiprzLB6Lwenc' @ 72057594037927935 : 1 .. '!journal.pages!suuYN87Al1ZZWtQQ.jhgNnhWhrkOpKs1B' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-001313
MANIFEST-001341

View File

@@ -1,7 +1,7 @@
2026/03/07-00:45:53.822265 7fe9111ff6c0 Recovering log #1311
2026/03/07-00:45:53.832029 7fe9111ff6c0 Delete type=3 #1309
2026/03/07-00:45:53.832089 7fe9111ff6c0 Delete type=0 #1311
2026/03/07-01:11:49.549791 7fe8c2ffd6c0 Level-0 table #1316: started
2026/03/07-01:11:49.549814 7fe8c2ffd6c0 Level-0 table #1316: 0 bytes OK
2026/03/07-01:11:49.556940 7fe8c2ffd6c0 Delete type=0 #1314
2026/03/07-01:11:49.577524 7fe8c2ffd6c0 Manual compaction at level-0 from '!folders!3uquYH73ttCdoH0I' @ 72057594037927935 : 1 .. '!items!ylFhk7mGZOnAJTUT' @ 0 : 0; will stop at (end)
2026/04/13-23:10:07.616013 7fddcbfff6c0 Recovering log #1339
2026/04/13-23:10:07.625710 7fddcbfff6c0 Delete type=3 #1337
2026/04/13-23:10:07.625777 7fddcbfff6c0 Delete type=0 #1339
2026/04/13-23:14:52.426320 7fddca1c26c0 Level-0 table #1344: started
2026/04/13-23:14:52.426354 7fddca1c26c0 Level-0 table #1344: 0 bytes OK
2026/04/13-23:14:52.432796 7fddca1c26c0 Delete type=0 #1342
2026/04/13-23:14:52.447521 7fddca1c26c0 Manual compaction at level-0 from '!folders!3uquYH73ttCdoH0I' @ 72057594037927935 : 1 .. '!items!ylFhk7mGZOnAJTUT' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2026/03/05-20:38:02.758975 7f78e37fe6c0 Recovering log #1307
2026/03/05-20:38:02.770688 7f78e37fe6c0 Delete type=3 #1305
2026/03/05-20:38:02.770801 7f78e37fe6c0 Delete type=0 #1307
2026/03/05-20:42:36.097432 7f78e15b46c0 Level-0 table #1312: started
2026/03/05-20:42:36.097481 7f78e15b46c0 Level-0 table #1312: 0 bytes OK
2026/03/05-20:42:36.104419 7f78e15b46c0 Delete type=0 #1310
2026/03/05-20:42:36.111679 7f78e15b46c0 Manual compaction at level-0 from '!folders!3uquYH73ttCdoH0I' @ 72057594037927935 : 1 .. '!items!ylFhk7mGZOnAJTUT' @ 0 : 0; will stop at (end)
2026/04/13-23:01:32.401911 7fddd8fbd6c0 Recovering log #1335
2026/04/13-23:01:32.411157 7fddd8fbd6c0 Delete type=3 #1333
2026/04/13-23:01:32.411219 7fddd8fbd6c0 Delete type=0 #1335
2026/04/13-23:07:12.194188 7fddca1c26c0 Level-0 table #1340: started
2026/04/13-23:07:12.194235 7fddca1c26c0 Level-0 table #1340: 0 bytes OK
2026/04/13-23:07:12.201298 7fddca1c26c0 Delete type=0 #1338
2026/04/13-23:07:12.214646 7fddca1c26c0 Manual compaction at level-0 from '!folders!3uquYH73ttCdoH0I' @ 72057594037927935 : 1 .. '!items!ylFhk7mGZOnAJTUT' @ 0 : 0; will stop at (end)

View File

@@ -1 +1 @@
MANIFEST-001311
MANIFEST-001339

View File

@@ -1,7 +1,7 @@
2026/03/07-00:45:53.847464 7fe8c3fff6c0 Recovering log #1309
2026/03/07-00:45:53.857503 7fe8c3fff6c0 Delete type=3 #1307
2026/03/07-00:45:53.857563 7fe8c3fff6c0 Delete type=0 #1309
2026/03/07-01:11:49.577693 7fe8c2ffd6c0 Level-0 table #1314: started
2026/03/07-01:11:49.577722 7fe8c2ffd6c0 Level-0 table #1314: 0 bytes OK
2026/03/07-01:11:49.583840 7fe8c2ffd6c0 Delete type=0 #1312
2026/03/07-01:11:49.612617 7fe8c2ffd6c0 Manual compaction at level-0 from '!journal!cZtNgayIw2QFhC9u' @ 72057594037927935 : 1 .. '!journal.pages!cZtNgayIw2QFhC9u.ts265H1XkisLgdow' @ 0 : 0; will stop at (end)
2026/04/13-23:10:07.641322 7fddd9fbf6c0 Recovering log #1337
2026/04/13-23:10:07.651537 7fddd9fbf6c0 Delete type=3 #1335
2026/04/13-23:10:07.651623 7fddd9fbf6c0 Delete type=0 #1337
2026/04/13-23:14:52.478583 7fddca1c26c0 Level-0 table #1342: started
2026/04/13-23:14:52.478613 7fddca1c26c0 Level-0 table #1342: 0 bytes OK
2026/04/13-23:14:52.484781 7fddca1c26c0 Delete type=0 #1340
2026/04/13-23:14:52.484989 7fddca1c26c0 Manual compaction at level-0 from '!journal!cZtNgayIw2QFhC9u' @ 72057594037927935 : 1 .. '!journal.pages!cZtNgayIw2QFhC9u.ts265H1XkisLgdow' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2026/03/05-20:38:02.790472 7f78e3fff6c0 Recovering log #1305
2026/03/05-20:38:02.801425 7f78e3fff6c0 Delete type=3 #1303
2026/03/05-20:38:02.801553 7f78e3fff6c0 Delete type=0 #1305
2026/03/05-20:42:36.090593 7f78e15b46c0 Level-0 table #1310: started
2026/03/05-20:42:36.090646 7f78e15b46c0 Level-0 table #1310: 0 bytes OK
2026/03/05-20:42:36.097257 7f78e15b46c0 Delete type=0 #1308
2026/03/05-20:42:36.111659 7f78e15b46c0 Manual compaction at level-0 from '!journal!cZtNgayIw2QFhC9u' @ 72057594037927935 : 1 .. '!journal.pages!cZtNgayIw2QFhC9u.ts265H1XkisLgdow' @ 0 : 0; will stop at (end)
2026/04/13-23:01:32.426243 7fddcbfff6c0 Recovering log #1333
2026/04/13-23:01:32.436951 7fddcbfff6c0 Delete type=3 #1331
2026/04/13-23:01:32.437016 7fddcbfff6c0 Delete type=0 #1333
2026/04/13-23:07:12.201427 7fddca1c26c0 Level-0 table #1338: started
2026/04/13-23:07:12.201453 7fddca1c26c0 Level-0 table #1338: 0 bytes OK
2026/04/13-23:07:12.207634 7fddca1c26c0 Delete type=0 #1336
2026/04/13-23:07:12.214659 7fddca1c26c0 Manual compaction at level-0 from '!journal!cZtNgayIw2QFhC9u' @ 72057594037927935 : 1 .. '!journal.pages!cZtNgayIw2QFhC9u.ts265H1XkisLgdow' @ 0 : 0; will stop at (end)

View File

View File

View File

@@ -1 +1 @@
MANIFEST-001311
MANIFEST-001339

View File

@@ -1,7 +1,7 @@
2026/03/07-00:45:53.792556 7fe8c3fff6c0 Recovering log #1309
2026/03/07-00:45:53.805674 7fe8c3fff6c0 Delete type=3 #1307
2026/03/07-00:45:53.805724 7fe8c3fff6c0 Delete type=0 #1309
2026/03/07-01:11:49.563712 7fe8c2ffd6c0 Level-0 table #1314: started
2026/03/07-01:11:49.563733 7fe8c2ffd6c0 Level-0 table #1314: 0 bytes OK
2026/03/07-01:11:49.570212 7fe8c2ffd6c0 Delete type=0 #1312
2026/03/07-01:11:49.577555 7fe8c2ffd6c0 Manual compaction at level-0 from '!journal!50u8VAjdmovyr0hx' @ 72057594037927935 : 1 .. '!journal.pages!yzw9I0r3hCK7PJnz.sPNCYj2nR3Cp3jHd' @ 0 : 0; will stop at (end)
2026/04/13-23:10:07.590923 7fddd8fbd6c0 Recovering log #1337
2026/04/13-23:10:07.600770 7fddd8fbd6c0 Delete type=3 #1335
2026/04/13-23:10:07.600832 7fddd8fbd6c0 Delete type=0 #1337
2026/04/13-23:14:52.419409 7fddca1c26c0 Level-0 table #1342: started
2026/04/13-23:14:52.419440 7fddca1c26c0 Level-0 table #1342: 0 bytes OK
2026/04/13-23:14:52.426167 7fddca1c26c0 Delete type=0 #1340
2026/04/13-23:14:52.447511 7fddca1c26c0 Manual compaction at level-0 from '!journal!50u8VAjdmovyr0hx' @ 72057594037927935 : 1 .. '!journal.pages!yzw9I0r3hCK7PJnz.sPNCYj2nR3Cp3jHd' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2026/03/05-20:38:02.727495 7f78e2ffd6c0 Recovering log #1305
2026/03/05-20:38:02.739900 7f78e2ffd6c0 Delete type=3 #1303
2026/03/05-20:38:02.740017 7f78e2ffd6c0 Delete type=0 #1305
2026/03/05-20:42:36.060514 7f78e15b46c0 Level-0 table #1310: started
2026/03/05-20:42:36.060577 7f78e15b46c0 Level-0 table #1310: 0 bytes OK
2026/03/05-20:42:36.067949 7f78e15b46c0 Delete type=0 #1308
2026/03/05-20:42:36.083144 7f78e15b46c0 Manual compaction at level-0 from '!journal!50u8VAjdmovyr0hx' @ 72057594037927935 : 1 .. '!journal.pages!yzw9I0r3hCK7PJnz.sPNCYj2nR3Cp3jHd' @ 0 : 0; will stop at (end)
2026/04/13-23:01:32.376215 7fddcbfff6c0 Recovering log #1333
2026/04/13-23:01:32.386265 7fddcbfff6c0 Delete type=3 #1331
2026/04/13-23:01:32.386335 7fddcbfff6c0 Delete type=0 #1333
2026/04/13-23:07:12.172988 7fddca1c26c0 Level-0 table #1338: started
2026/04/13-23:07:12.173015 7fddca1c26c0 Level-0 table #1338: 0 bytes OK
2026/04/13-23:07:12.180381 7fddca1c26c0 Delete type=0 #1336
2026/04/13-23:07:12.187032 7fddca1c26c0 Manual compaction at level-0 from '!journal!50u8VAjdmovyr0hx' @ 72057594037927935 : 1 .. '!journal.pages!yzw9I0r3hCK7PJnz.sPNCYj2nR3Cp3jHd' @ 0 : 0; will stop at (end)

View File

View File

View File

@@ -1 +1 @@
MANIFEST-001311
MANIFEST-001339

View File

@@ -1,7 +1,7 @@
2026/03/07-00:45:53.774458 7fe8c37fe6c0 Recovering log #1309
2026/03/07-00:45:53.787633 7fe8c37fe6c0 Delete type=3 #1307
2026/03/07-00:45:53.787702 7fe8c37fe6c0 Delete type=0 #1309
2026/03/07-01:11:49.543331 7fe8c2ffd6c0 Level-0 table #1314: started
2026/03/07-01:11:49.543361 7fe8c2ffd6c0 Level-0 table #1314: 0 bytes OK
2026/03/07-01:11:49.549597 7fe8c2ffd6c0 Delete type=0 #1312
2026/03/07-01:11:49.549676 7fe8c2ffd6c0 Manual compaction at level-0 from '!tables!4l60Lxv8cpsyy2Cg' @ 72057594037927935 : 1 .. '!tables.results!tfaYKDZqu7kgZvRG.yvbwKursaixh2dby' @ 0 : 0; will stop at (end)
2026/04/13-23:10:07.578503 7fddd9fbf6c0 Recovering log #1337
2026/04/13-23:10:07.588788 7fddd9fbf6c0 Delete type=3 #1335
2026/04/13-23:10:07.588855 7fddd9fbf6c0 Delete type=0 #1337
2026/04/13-23:14:52.432890 7fddca1c26c0 Level-0 table #1342: started
2026/04/13-23:14:52.432912 7fddca1c26c0 Level-0 table #1342: 0 bytes OK
2026/04/13-23:14:52.439225 7fddca1c26c0 Delete type=0 #1340
2026/04/13-23:14:52.447529 7fddca1c26c0 Manual compaction at level-0 from '!tables!4l60Lxv8cpsyy2Cg' @ 72057594037927935 : 1 .. '!tables.results!tfaYKDZqu7kgZvRG.yvbwKursaixh2dby' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2026/03/05-20:38:02.710364 7f7930fff6c0 Recovering log #1305
2026/03/05-20:38:02.722794 7f7930fff6c0 Delete type=3 #1303
2026/03/05-20:38:02.722917 7f7930fff6c0 Delete type=0 #1305
2026/03/05-20:42:36.068193 7f78e15b46c0 Level-0 table #1310: started
2026/03/05-20:42:36.068246 7f78e15b46c0 Level-0 table #1310: 0 bytes OK
2026/03/05-20:42:36.074966 7f78e15b46c0 Delete type=0 #1308
2026/03/05-20:42:36.083182 7f78e15b46c0 Manual compaction at level-0 from '!tables!4l60Lxv8cpsyy2Cg' @ 72057594037927935 : 1 .. '!tables.results!tfaYKDZqu7kgZvRG.yvbwKursaixh2dby' @ 0 : 0; will stop at (end)
2026/04/13-23:01:32.363321 7fddd9fbf6c0 Recovering log #1333
2026/04/13-23:01:32.373224 7fddd9fbf6c0 Delete type=3 #1331
2026/04/13-23:01:32.373285 7fddd9fbf6c0 Delete type=0 #1333
2026/04/13-23:07:12.180494 7fddca1c26c0 Level-0 table #1338: started
2026/04/13-23:07:12.180514 7fddca1c26c0 Level-0 table #1338: 0 bytes OK
2026/04/13-23:07:12.186759 7fddca1c26c0 Delete type=0 #1336
2026/04/13-23:07:12.187045 7fddca1c26c0 Manual compaction at level-0 from '!tables!4l60Lxv8cpsyy2Cg' @ 72057594037927935 : 1 .. '!tables.results!tfaYKDZqu7kgZvRG.yvbwKursaixh2dby' @ 0 : 0; will stop at (end)

View File

View File

View File

@@ -1 +1 @@
MANIFEST-000954
MANIFEST-000982

View File

@@ -1,7 +1,7 @@
2026/03/07-00:45:53.835083 7fe8c37fe6c0 Recovering log #952
2026/03/07-00:45:53.844848 7fe8c37fe6c0 Delete type=3 #950
2026/03/07-00:45:53.844893 7fe8c37fe6c0 Delete type=0 #952
2026/03/07-01:11:49.570293 7fe8c2ffd6c0 Level-0 table #957: started
2026/03/07-01:11:49.570316 7fe8c2ffd6c0 Level-0 table #957: 0 bytes OK
2026/03/07-01:11:49.577407 7fe8c2ffd6c0 Delete type=0 #955
2026/03/07-01:11:49.577567 7fe8c2ffd6c0 Manual compaction at level-0 from '!journal!056ILNNrLiPq3Gi3' @ 72057594037927935 : 1 .. '!journal.pages!yfZxl4I7XAuUF6r3.apXmOlZRmGT4GreB' @ 0 : 0; will stop at (end)
2026/04/13-23:10:07.628261 7fddd97be6c0 Recovering log #980
2026/04/13-23:10:07.638743 7fddd97be6c0 Delete type=3 #978
2026/04/13-23:10:07.638813 7fddd97be6c0 Delete type=0 #980
2026/04/13-23:14:52.447639 7fddca1c26c0 Level-0 table #985: started
2026/04/13-23:14:52.447658 7fddca1c26c0 Level-0 table #985: 0 bytes OK
2026/04/13-23:14:52.454058 7fddca1c26c0 Delete type=0 #983
2026/04/13-23:14:52.484949 7fddca1c26c0 Manual compaction at level-0 from '!journal!056ILNNrLiPq3Gi3' @ 72057594037927935 : 1 .. '!journal.pages!yfZxl4I7XAuUF6r3.apXmOlZRmGT4GreB' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,7 @@
2026/03/05-20:38:02.775184 7f7930fff6c0 Recovering log #948
2026/03/05-20:38:02.786747 7f7930fff6c0 Delete type=3 #946
2026/03/05-20:38:02.786856 7f7930fff6c0 Delete type=0 #948
2026/03/05-20:42:36.104620 7f78e15b46c0 Level-0 table #953: started
2026/03/05-20:42:36.104679 7f78e15b46c0 Level-0 table #953: 0 bytes OK
2026/03/05-20:42:36.111433 7f78e15b46c0 Delete type=0 #951
2026/03/05-20:42:36.111701 7f78e15b46c0 Manual compaction at level-0 from '!journal!056ILNNrLiPq3Gi3' @ 72057594037927935 : 1 .. '!journal.pages!yfZxl4I7XAuUF6r3.apXmOlZRmGT4GreB' @ 0 : 0; will stop at (end)
2026/04/13-23:01:32.413810 7fddd97be6c0 Recovering log #976
2026/04/13-23:01:32.423395 7fddd97be6c0 Delete type=3 #974
2026/04/13-23:01:32.423455 7fddd97be6c0 Delete type=0 #976
2026/04/13-23:07:12.187249 7fddca1c26c0 Level-0 table #981: started
2026/04/13-23:07:12.187284 7fddca1c26c0 Level-0 table #981: 0 bytes OK
2026/04/13-23:07:12.194022 7fddca1c26c0 Delete type=0 #979
2026/04/13-23:07:12.214628 7fddca1c26c0 Manual compaction at level-0 from '!journal!056ILNNrLiPq3Gi3' @ 72057594037927935 : 1 .. '!journal.pages!yfZxl4I7XAuUF6r3.apXmOlZRmGT4GreB' @ 0 : 0; will stop at (end)

View File

@@ -14,7 +14,7 @@
let updateObj = this.actor.toObject();
let talents = (await Promise.tout([game.wfrp4e.tables.rollTable("talents"), game.wfrp4e.tables.rollTable("talents"), game.wfrp4e.tables.rollTable("talents")])).map(i => i.text)
let talents = (await Promise.all([game.wfrp4e.tables.rollTable("talents"), game.wfrp4e.tables.rollTable("talents"), game.wfrp4e.tables.rollTable("talents")])).map(i => i.text)
for (let ch in characteristics)
{

View File

@@ -1,6 +1,6 @@
let test = await args.actor.setupCharacteristic("wp", {skipTargets: true, appendTitle : " - " + this.effect.name, context : {failure: "Gain de 1 état Sonné"}})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
args.actor.addCondition("stunned")
}

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupCharacteristic("ag", {skipTargets: true, appendTitle : ` - ${this.effect.name}`});
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
this.actor.addCondition("bleeding");
}

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupCharacteristic("wp", {skipTargets: true, appendTitle : " - " + this.effect.name, context : {failure: "GAin d'1 état Sonné"}})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
this.actor.addCondition("stunned");
}

View File

@@ -22,7 +22,7 @@ let Test = await actor.setupSkill('Calme', {
fields: {difficulty: 'easy'},
characteristic: 'wp',
});
await Test.roll();
await test.roll();
if (!Test.succeeded) {
Test.result.other.push(`<b>${actor.name}</b> est devenu @Condition[Stunned] par la vue.`);

View File

@@ -10,12 +10,12 @@ const test = await this.actor.setupSkill(game.i18n.localize("NAME.Résistance"),
}
});
await Test.roll();
await test.roll();
if (Test.Échoué) {
if (test.failed) {
await this.actor.addEffectItems(bloodyFluxUUID, this.effet);
} else {
const SL = Test.result.SL;
const SL = test.result.SL;
const heal = 1 + SL;
await this.actor.modifyWounds(heal);
this.script.message(`Butcher a soigné ${heal} Blessures.`);

View File

@@ -1,12 +1,12 @@
// Imbibing this substance grants the user the utilisateur d Creature Trait.
const hasutilisateur d = this.actor.has("Insensible à la douleur");
if (hasutilisateur d === undefined)
// Imbibing this substance grants the user the Painless Creature Trait.
const hasPainless = this.actor.has("Insensible à la douleur");
if (hasPainless === undefined)
{
let item = await fromUuid("Compendium.wfrp4e-core.items.wMwSRDmgiF2IdCJr");
let data = item.toObject()
this.actor.createEmbeddedDocuments("Item", [data], {fromEffect: this.effect.id})
this.script.message(
this.script.scriptMessage(
`<p><strong>${this.actor.prototypeToken.name}</strong> a acquis le Trait de Créature Insensible à la douleur. Cet
effet dure une heure, après quoi il se dissipe et l'effet complet
de toutes les blessures du buveur s'abat d'un coup.</p>

View File

@@ -7,9 +7,9 @@ const test = await args.actor.setupSkill(game.i18n.localize("NAME.Résistance"),
}
});
await Test.roll();
await test.roll();
if (Test.Échoué) {
if (test.failed) {
args.actor.addCondition("poisoned");
const speaker = ChatMessage.getSpeaker({actor: args.actor});
this.script.message(`<p>${speaker.alias} a reçu 1 état @Condition[Poisoned] de Venin d'Araignée.</p><p>Les victimes réduites à 0 blessures et qui souffrent d'un état @Condition[Poisoned] de ces flèches deviennent @Condition[Unconcious], mais ne risquent pas la mort à cause des états @Condition[Poisoned] restants comme ce serait normalement le cas.</p>`);

View File

@@ -1,7 +1,7 @@
if (args.test.result.SL < 0)
{
this.script.message(`Gain de ${Math.abs(args.test.result.SL)} Points de Corruption`, {whisper : ChatMessage.getWhisperRecipients("GM")})
if (args.Test.Échoué && this.actor.type == "character")
if (args.test.failed && this.actor.type == "character")
{
this.actor.update({"system.status.corruption.value" : parseInt(this.actor.status.corruption.value) + Math.abs(args.test.result.SL)})
}

View File

@@ -13,9 +13,9 @@ let test = await actor.setupCharacteristic("s", {
}
});
await Test.roll();
if (Test.succeeded) {
let SL = parseInt(Test.result.SL);
await test.roll();
if (test.succeeded) {
let SL = parseInt(test.result.SL);
let name = this.effect.name.replace(/\d+/, rating => parseInt(rating) - SL);
await this.effect.update({name});
}

View File

@@ -1,11 +1,11 @@
let test = await this.actor.setupCharacteristic("s", {appendTitle : ` - ${this.effect.name}`, fields : {difficulty : "difficult"}});
await Test.roll();
await test.roll();
if (Test.Échoué)
if (test.failed)
{
if (Test.isCriticalFumble == "fumble")
if (test.isCriticalFumble == "fumble")
{
return this.script.message(`<strong>${this.actor.name}</strong> meurt alors qu'il est entraîné dans l'Aethyr (à moins qu'il ne dépense un point de Destinée pour éviter cela).`);
}

View File

@@ -22,7 +22,7 @@ let Test = await actor.setupSkill('Calme', {
fields: {difficulty: 'easy'},
characteristic: 'wp',
});
await Test.roll();
await test.roll();
if (!Test.succeeded) {
Test.result.other.push(`<b>${actor.name}</b> est devenu fasciné par la vue et incapable d'effectuer une quelconque action autre que de se déplacer vers la lumière.`);

View File

@@ -1,4 +1,4 @@
if (args.test.characteristicKey == "wp" && args.Test.Échoué && args.Test.result.SL <= -3)
if (args.test.characteristicKey == "wp" && args.test.failed && args.test.result.SL <= -3)
{
this.script.notification("Ajout de A Terre");
this.actor.addCondition("prone")

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupCharacteristic("wp", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})
await Test.roll();
if (Test.succeeded)
await test.roll();
if (test.succeeded)
{
this.script.message("Peut réaliser une Action ou un Déplacement (choisissez-en un)")
}

View File

@@ -1 +1 @@
this.script.scirptMessage(await this.actor.applyBasicDamage(20, {suppressMsg: true});
this.script.scriptMessage(await this.actor.applyBasicDamage(20, {suppressMsg: true}));

View File

@@ -1,7 +1,7 @@
let test = await this.actor.setupSkill(game.i18n.localize("NAME.Dodge"), {fields : {difficulty : "hard"}, appendTitle : ` - ${this.effect.name}`})
await Test.roll();
await test.roll();
if (Test.Échoué) {
if (test.failed) {
this.script.scriptMessage(await this.actor.applyBasicDamage(20, {suppressMsg: true}));
this.script.scriptMessage(`${this.actor.name} est victime de @UUID[Compendium.wfrp4e-core.journals.JournalEntry.NS3YGlJQxwTggjRX.JournalEntryPage.WCivInLZrqEtZzF4#drowning-and-suffocation]{Suffocation}`);
}

View File

@@ -1,6 +1,6 @@
if (args.test.characteristicKey == "wp")
{
if (args.Test.Échoué)
if (args.test.failed)
{
this.actor.addSystemEffect("convulsions")
this.script.message(`Test de FM échoué, <b>${this.actor.prototypeToken.name}</b> reçoit @Symptom[Convulsions] pour [[1d10]] heures`)

View File

@@ -9,7 +9,7 @@ if (god)
{
let prayers = await warhammer.utility.findAllItems("prayer", "Chargement des Prières", true, ["system.type.value", "system.god.value"])
let blessings = prayers.filter(p => p.system.god.value.split(",").map(i => i.trim().toLowerCase()).includes(god.toLowerCase()) && p.system.type.value == "blessing")
let configBlessings = await Promise.tout((game.wfrp4e.config.godBlessings[god.toLowerCase()] || []).map(fromUuid));
let configBlessings = await Promise.all((game.wfrp4e.config.godBlessings[god.toLowerCase()] || []).map(fromUuid));
if (god == "Foi Antique")
{
blessings = await ItemDialog.create(prayers.filter(i => i.system.type.value == "blessing"), 6, {text : "Sélectionnez 6 Bénédictions", title : "Béni"})

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupCharacteristic("wp", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
this.script.message(await game.wfrp4e.tables.formatChatRoll("enrage-beast"))
}

View File

@@ -1,7 +1,7 @@
if (this.actor.Species.toLowerCase() != "skaven") {
this.actor.setupCharacteristic("t", {skipTargets: true, appendTitle : ` - Utilise ${this.effect.name}`, fields: { difficulty: "difficult" } }).then(async Test => {
await Test.roll()
if (Test.Échoué)
await test.roll()
if (test.failed)
{
let toughnessLost = Math.ceil(CONFIG.Dice.randomUniform() * 10)
this.actor.update({ "system.characteristics.t.initial": this.actor.characteristics.t.initial - toughnessLost })

View File

@@ -1,7 +1,7 @@
let test = await this.actor.setupCharacteristic("wp", {fields: {difficulty : "average"}, skipTargets: true, appendTitle : ` - ${this.effect.name}`})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
let stuns = Math.max(1, Math.abs(Test.result.SL))
let stuns = Math.max(1, Math.abs(test.result.SL))
this.actor.addCondition("stunned", stuns)
}

View File

@@ -5,8 +5,8 @@ if (this.actor.uuid == this.effect.sourceActor.uuid)
if (this.actor.has("À Sang Froid") && !this.actor.hasSystemEffect("nausea")) {
let test = await this.actor.setupSkill(game.i18n.localize("NAME.Résistance"), {appendTitle : `- ${this.effect.name}`})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
let myRoll = await new Roll("1d10").roll({allowInteractive : false});

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupSkill("Résistance", {fields : {difficulty : "difficult"}, appendTitle : ` - ${this.effect.name}`});
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
await this.actor.addCondition("blinded");
}

View File

@@ -1,7 +1,7 @@
const test = await this.actor.setupCharacteristic("int", {fields: {difficulty: "easy"}, skipTargets: true, appendTitle : ` - ${this.effect.name}`});
await Test.roll();
await test.roll();
if (Test.Échoué) {
if (test.failed) {
this.actor.addCondition('stunned');
}

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupCharacteristic("ag", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, context: { failure: "Goes Prone" }})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
this.actor.addCondition("prone");
}

View File

@@ -7,9 +7,9 @@ const test = await this.actor.setupSkill(game.i18n.localize("NAME.Résistance"),
}
});
await Test.roll();
await test.roll();
if (Test.Échoué) {
const SL = Number(Test.result.SL);
if (test.failed) {
const SL = Number(test.result.SL);
this.script.message(`Butcher perd ${SL} dents.`);
}

View File

@@ -1 +1 @@
,args.fields.slBonus++;,args.fields.slBonus++;,args.fields.slBonus++;
args.fields.slBonus++;

View File

@@ -7,8 +7,8 @@ let difficulty = ""
difficulty = "veasy"
let test = await this.actor.setupSkill(game.i18n.localize("NAME.Résistance"), {context : {failure : this.actor.name + " meurt de la Pourriture"}, fields: {difficulty}, appendTitle : " - Pourriture"})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
this.actor.addCondition("dead");
}

View File

@@ -7,8 +7,8 @@ let difficulty = ""
difficulty = "veasy"
let test = await this.actor.setupSkill(game.i18n.localize("NAME.Résistance"), {context : {failure : this.actor.name + " meurt de la Pourriture"}, fields: {difficulty}, appendTitle : " - Pourriture"})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
this.actor.addCondition("dead");
}

View File

@@ -1,7 +1,7 @@
let test = await this.actor.setupCharacteristic("i", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields : {difficulty : "easy"}})
await Test.roll();
await test.roll();
if (!Test.succeeded)
if (!test.succeeded)
{
this.actor.addCondition("stunned");
}

View File

@@ -1,7 +1,7 @@
let test = await this.actor.setupCharacteristic("ag", {fields : {difficulty : "hard"}});
await Test.roll();
await test.roll();
if (Test.Échoué)
if (test.failed)
{
await this.actor.addCondition("bleeding")
await this.actor.addCondition("entangled")

View File

@@ -9,8 +9,8 @@ if (SL >= 3)
let test = await args.actor.setupCharacteristic("wp", {fields: {difficulty}, skipTargets: true, appendTitle : " - " + this.effect.name, context : {failure: "Reçoit un état Sonné"}})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
args.actor.addCondition("stunned");
}

View File

@@ -12,7 +12,7 @@ const test = await this.actor.setupCharacteristic("t", {
}
});
await Test.roll();
if (Test.failure) {
await test.roll();
if (test.failure) {
await this.actor.addCondition("prone");
}

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupSkill("Résistance", {fields : {difficulty : "difficult", appendTitle : ` - ${this.effect.name}`}});
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
await this.actor.addCondition("blinded");
}

View File

@@ -1,8 +1,8 @@
let test = await this.actor.setupCharacteristic("wp", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})
await Test.roll();
await test.roll();
let opposedResult = Test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result
let opposedResult = test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result
if (opposedResult?.winner == "attacker")
{

View File

@@ -1,7 +1,7 @@
let test = await this.actor.setupCharacteristic("t", { appendTitle: ` - ${this.effect.name}`, fields: { difficulty: "challenging" } })
await Test.roll();
await test.roll();
if (Test.Échoué)
if (test.failed)
{
let ageAdded = Math.ceil(CONFIG.Dice.randomUniform() * 10) + Math.ceil(CONFIG.Dice.randomUniform() * 10)
let ws = Math.ceil(CONFIG.Dice.randomUniform() * 10)

View File

@@ -1,5 +1,5 @@
if (args.opposedTest.result.differenceSL >= 0 && args.opposedTest.result.differenceSL <= 2 && args.opposedTest.result.winner == "attacker")
{
this.script.message(`Becomes lodged in the armour or flesh of the opponent. See @UUID[${this.item.uuid}]{${this.item.name}}.`, speaker : {alias : this.item.name}, {blind: true, whisper : ChatMessage.getWhisperRecipients("GM")})
this.script.message(`Becomes lodged in the armour or flesh of the opponent. See @UUID[${this.item.uuid}]{${this.item.name}}.`, {speaker : {alias : this.item.name}, blind: true, whisper : ChatMessage.getWhisperRecipients("GM")})
}

View File

@@ -1,7 +1,7 @@
let test = await this.actor.setupCharacteristic("wp", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})
await Test.roll();
await test.roll();
// Kind of insane but whatever
let opposedResult = Test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result
let opposedResult = test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result
return opposedResult?.winner == "attacker";

View File

@@ -1,6 +1,6 @@
if (args.test.characteristicKey == "wp")
{
if (args.Test.Échoué)
if (args.test.failed)
{
let item = await fromUuid("Compendium.wfrp4e-core.items.AGcJl5rHjkyIQBPP")
let data = item.toObject();

View File

@@ -1,9 +1,9 @@
let location = this.item.system.location.key
let test = await this.actor.setupCharacteristic("dex", {context : {failure : `<strong>${this.effect.name}</strong>: Lâchez l'objet!`}, skipTargets: true, appendTitle : " - " + this.effect.name, fields : {difficulty : "average"}})
await Test.roll();
await test.roll();
if (location && Test.Échoué)
if (location && test.failed)
{
let dropped = this.item.system.weaponsAtLocation;

View File

@@ -1,6 +1,6 @@
if (args.test.characteristicKey == "wp")
{
if (args.Test.Échoué)
if (args.test.failed)
{
this.actor.createEmbeddedDocuments("ActiveEffect", [game.wfrp4e.config.symptomEffects["malaise"]])
this.script.message(`Test de FM échoué, <b>${this.actor.prototypeToken.name}</b> gains @Condition[Malaise] for [[1d10]] hours`, {whisper: ChatMessage.getWhisperRecipients("GM")})

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupSkill(game.i18n.localize("NAME.Cool"), {appendTitle : ` - ${this.effect.name}`, fields : {difficulty : "difficult", slBonus : -1 * this.effect.sourceTest.result.SL}})
await Test.roll();
if (Test.succeeded)
await test.roll();
if (test.succeeded)
{
this.script.notification(`Résistance à ${this.effect.name}`);
}

View File

@@ -1,8 +1,8 @@
if (args.totalWoundLoss > 0)
{
args.actor.setupCharacteristic("t", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields: { difficulty: "difficult" } }).then(async Test => {
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
await args.actor.addCondition("poisoned")
this.script.message(await args.actor.applyBasicDamage(3, {suppressMsg : true, damageType: game.wfrp4e.config.DAMAGE_TYPE.IGNORE_ALL }))

View File

@@ -1,6 +1,6 @@
let test = await this.actor.setupCharacteristic("t", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields : {difficulty : "hard"}})
await Test.roll();
if (Test.Échoué)
await test.roll();
if (test.failed)
{
let roll = await new Roll("1d10").roll({allowInteractive : false});
roll.toMessage(this.script.getChatData())

View File

@@ -1,2 +1,2 @@
args.totalWoundLoss = Math.max(0, args.totalWoundLoss - 1)
args.modifiers.other.push({label : this.effect.name, value : -1)
args.modifiers.other.push({label : this.effect.name, value : -1})

View File

@@ -1,8 +1,8 @@
let test = await this.actor.setupCharacteristic("t", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields : {difficulty : "vhard"}});
await Test.roll();
await test.roll();
CorruptionMessageModel.createCorruptionMessage("minor", this.script.getChatData())
if (Test.Échoué)
if (test.failed)
{
this.actor.addCondition("unconscious");
}