diff --git a/ChroniquesOubliees/co.html b/ChroniquesOubliees/co.html index 60ca043502..ac1f1d38e6 100644 --- a/ChroniquesOubliees/co.html +++ b/ChroniquesOubliees/co.html @@ -1,5 +1,5 @@
- + @@ -16,7 +16,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -147,9 +147,9 @@ - + @@ -165,9 +165,9 @@ - + @@ -183,9 +183,9 @@ - + - + - + - +
- +
- +
- + @@ -203,9 +203,9 @@ - + @@ -223,9 +223,9 @@ - + @@ -326,7 +326,7 @@
@@ -1452,17 +1452,17 @@
y
- +  Qté - +
-  Effet : +  Effet :
- +
ÉQUIPEMENT : Divers
@@ -1954,10 +1954,10 @@ setAttrs({ type_personnage: 'PJ' }); - getAttrs(["CHARISME", "option_pc", "pc", "pc_max"], function(values) { + getAttrs(["charisme", "option_pc", "pc", "pc_max"], function(values) { var option_pc = values.option_pc; if (option_pc == 1 || option_pc == 'on') { - var charisme = parseInt(values.CHARISME); + var charisme = parseInt(values.charisme); if (!isNaN(charisme) && charisme > 0) { var pc_base = 3 + Math.floor((charisme - 10) / 2); var toSet = { @@ -2086,12 +2086,35 @@ setAttrs(sa); }); } + if (verfdp < 4.01) { //changement de nom des attributs de consommables + getSectionIDs('repeating_equipement', function(idArray) { + idArray.forEach(function(id) { + var prefix = 'repeating_equipement_' + id + '_'; + getAttrs([prefix + 'equip-nom', prefix + 'equip-qte', prefix + 'equip-effet'], function(values) { + var sa = {}; + sa[prefix + 'equip_nom'] = values[prefix + 'equip-nom']; + sa[prefix + 'equip_qte'] = values[prefix + 'equip-qte']; + sa[prefix + 'equip_effet'] = values[prefix + 'equip-effet']; + sa[prefix + 'equip-nom'] = ''; + sa[prefix + 'equip-qte'] = ''; + sa[prefix + 'equip-effet'] = ''; + setAttrs(sa); + }); + }); + }); + getAttrs(['equip-div'], function(value) { + if (values['equip-div'] !== undefined) + setAttrs({ + equip_div: values['equip-div'], + }); + }); + } setAttrs({ - version: 4.00 + version: 4.01 }); if (values.option_setting == 'arran') { //Quelques valeurs de départ selon la famille de profil par défaut - getAttrs(['famille_par_defaut', 'famille', 'CHARISME', 'SAGESSE', 'niveau'], function(values) { + getAttrs(['famille_par_defaut', 'famille', 'charisme', 'sagesse', 'niveau'], function(values) { if (values.famille_par_defaut == 1 && values.famille == 'aventurier') { var attr = { fanille_par_defaut: 0, @@ -2107,13 +2130,13 @@ titrecapaciteraciale: "Trait de peuple", formations_martiales_check: 1, }; - var charisme = parseInt(values.CHARISME); + var charisme = parseInt(values.charisme); if (!isNaN(charisme) && charisme > 0) { attr.pc_base = 4 + Math.floor((charisme - 10) / 2); attr.pc_max = attr.pc_base; attr.pc = attr.pc_base; } - var sagesse = parseInt(values.SAGESSE); + var sagesse = parseInt(values.sagesse); var niveau = parseInt(values.niveau); if (!isNaN(sagesse) && sagesse > 0 && !isNaN(niveau) && niveau >= 0) { attr.pm_max = niveau + Math.floor((sagesse - 10) / 2); @@ -2274,9 +2297,9 @@ //Conversion quand on passe de PJ à PNJ if (eventinfo.newValue == 'PNJ') { getAttrs( - ["force", "FOR_BONUS", "DEXTERITE", "DEX_BONUS", "CONSTITUTION", - "CON_BONUS", "INTELLIGENCE", "INT_BONUS", "SAGESSE", "SAG_BONUS", - "CHARISME", "CHA_BONUS", "INIT_DIV", "DEFARMURE", "DEFARMUREON", + ["force", "FOR_BONUS", "dexterite", "DEX_BONUS", "constitution", + "CON_BONUS", "intelligence", "INT_BONUS", "sagesse", "SAG_BONUS", + "charisme", "CHA_BONUS", "INIT_DIV", "DEFARMURE", "DEFARMUREON", "DEFBOUCLIER", "DEFBOUCLIERON", "DEFDIV", "PV_max", "FOR_SUP", "DEX_SUP", "CON_SUP", "INT_SUP", "SAG_SUP", "CHA_SUP", "PV", "RDS", "jets_caches", "pnj_default_jets_caches", @@ -2289,25 +2312,25 @@ if (isNaN(attrs.pnj_for)) attrs.pnj_for = 0; var bonus = parseInt(values.FOR_BONUS); if (!isNaN(bonus)) attrs.pnj_for += bonus; - attrs.pnj_init = parseInt(values.DEXTERITE); + attrs.pnj_init = parseInt(values.dexterite); if (isNaN(attrs.pnj_init)) attrs.pnj_init = 10; attrs.pnj_dex = Math.floor((attrs.pnj_init - 10) / 2); attrs.pnj_def = 10 + attrs.pnj_dex; bonus = parseInt(values.DEX_BONUS); if (!isNaN(bonus)) attrs.pnj_dex += bonus; - attrs.pnj_con = Math.floor((parseInt(values.CONSTITUTION) - 10) / 2); + attrs.pnj_con = Math.floor((parseInt(values.constitution) - 10) / 2); if (isNaN(attrs.pnj_con)) attrs.pnj_con = 0; bonus = parseInt(values.CON_BONUS); if (!isNaN(bonus)) attrs.pnj_con += bonus; - attrs.pnj_int = Math.floor((parseInt(values.INTELLIGENCE) - 10) / 2); + attrs.pnj_int = Math.floor((parseInt(values.intelligence) - 10) / 2); if (isNaN(attrs.pnj_int)) attrs.pnj_int = 0; bonus = parseInt(values.INT_BONUS); if (!isNaN(bonus)) attrs.pnj_int += bonus; - attrs.pnj_sag = Math.floor((parseInt(values.SAGESSE) - 10) / 2); + attrs.pnj_sag = Math.floor((parseInt(values.sagesse) - 10) / 2); if (isNaN(attrs.pnj_sag)) attrs.pnj_sag = 0; bonus = parseInt(values.SAG_BONUS); if (!isNaN(bonus)) attrs.pnj_sag += bonus; - attrs.pnj_cha = Math.floor((parseInt(values.CHARISME) - 10) / 2); + attrs.pnj_cha = Math.floor((parseInt(values.charisme) - 10) / 2); if (isNaN(attrs.pnj_cha)) attrs.pnj_cha = 0; bonus = parseInt(values.CHA_BONUS); if (!isNaN(bonus)) attrs.pnj_cha += bonus; @@ -2340,7 +2363,7 @@ getSectionIDs('repeating_armes', function(idArray) { idArray.forEach(function(id) { var prefix = 'repeating_armes_' + id + '_'; - getAttrs([prefix + 'armelabel', prefix + 'armenom', prefix + 'armeatk', prefix + 'armeatkdiv', prefix + 'armecrit', prefix + 'armedmnbde', prefix + 'armedmde', prefix + 'armedmcar', prefix + 'armedmdiv', prefix + 'armeportee', prefix + 'armespec', prefix + 'armeoptflag', 'niveau', 'force', 'DEXTERITE', 'CONSTITUTION', 'INTELLIGENCE', 'SAGESSE', 'CHARISME', 'ATKCAC_CARAC', 'ATKCAC_DIV', 'ATKTIR_CARAC', 'ATKTIR_DIV', 'ATKMAG_CARAC', 'ATKMAG_DIV'], function(values) { + getAttrs([prefix + 'armelabel', prefix + 'armenom', prefix + 'armeatk', prefix + 'armeatkdiv', prefix + 'armecrit', prefix + 'armedmnbde', prefix + 'armedmde', prefix + 'armedmcar', prefix + 'armedmdiv', prefix + 'armeportee', prefix + 'armespec', prefix + 'armeoptflag', 'niveau', 'force', 'dexterite', 'constitution', 'intelligence', 'sagesse', 'charisme', 'ATKCAC_CARAC', 'ATKCAC_DIV', 'ATKTIR_CARAC', 'ATKTIR_DIV', 'ATKMAG_CARAC', 'ATKMAG_DIV'], function(values) { var label = values[prefix + 'armelabel']; if (!pnjLabels[label]) { var attrs = {}; @@ -2370,19 +2393,19 @@ caracAtk = 'force'; break; case '@{DEX}': - caracAtk = 'DEXTERITE'; + caracAtk = 'dexterite'; break; case '@{CON}': - caracAtk = 'CONSTITUTION'; + caracAtk = 'constitution'; break; case '@{INT}': - caracAtk = 'INTELLIGENCE'; + caracAtk = 'intelligence'; break; case '@{SAG}': - caracAtk = 'SAGESSE'; + caracAtk = 'sagesse'; break; case '@{CHA}': - caracAtk = 'CHARISME'; + caracAtk = 'charisme'; break; default: return; @@ -2405,19 +2428,19 @@ caracAtk = 'force'; break; case '@{DEX}': - caracAtk = 'DEXTERITE'; + caracAtk = 'dexterite'; break; case '@{CON}': - caracAtk = 'CONSTITUTION'; + caracAtk = 'constitution'; break; case '@{INT}': - caracAtk = 'INTELLIGENCE'; + caracAtk = 'intelligence'; break; case '@{SAG}': - caracAtk = 'SAGESSE'; + caracAtk = 'sagesse'; break; case '@{CHA}': - caracAtk = 'CHARISME'; + caracAtk = 'charisme'; break; default: caracAtk = 0; @@ -2477,14 +2500,14 @@ if (!isNaN(bonus)) defense += bonus; bonus = parseInt(values.DEFBOUCLIER) * parseInt(values.DEFBOUCLIERON); if (!isNaN(bonus)) defense += bonus; - bonus = Math.floor((parseInt(values.DEXTERITE) - 10) / 2); + bonus = Math.floor((parseInt(values.dexterite) - 10) / 2); if (!isNaN(bonus)) defense += bonus; setAttrs({ DEFDIV: def - defense }); } setAttrs({ - DEXTERITE: carac, + dexterite: carac, }); }); }); @@ -2495,7 +2518,7 @@ var pre_bonus = parseInt(values.CON_BONUS); if (!isNaN(pre_bonus)) bonus_carac -= pre_bonus; setAttrs({ - CONSTITUTION: 10 + 2 * bonus_carac, + constitution: 10 + 2 * bonus_carac, }); }); }); @@ -2506,7 +2529,7 @@ var pre_bonus = parseInt(values.INT_BONUS); if (!isNaN(pre_bonus)) bonus_carac -= pre_bonus; setAttrs({ - INTELLIGENCE: 10 + 2 * bonus_carac, + intelligence: 10 + 2 * bonus_carac, }); }); }); @@ -2517,7 +2540,7 @@ var pre_bonus = parseInt(values.SAG_BONUS); if (!isNaN(pre_bonus)) bonus_carac -= pre_bonus; setAttrs({ - SAGESSE: 10 + 2 * bonus_carac, + sagesse: 10 + 2 * bonus_carac, }); }); }); @@ -2528,12 +2551,12 @@ var pre_bonus = parseInt(values.CHA_BONUS); if (!isNaN(pre_bonus)) bonus_carac -= pre_bonus; setAttrs({ - CHARISME: 10 + 2 * bonus_carac, + charisme: 10 + 2 * bonus_carac, }); }); }); on('change:pnj_def', function(eventinfo) { - getAttrs(["DEFARMURE", "DEFARMUREON", "DEFBOUCLIER", "DEFBOUCLIERON", "DEXTERITE", "pnj_def"], function(values) { + getAttrs(["DEFARMURE", "DEFARMUREON", "DEFBOUCLIER", "DEFBOUCLIERON", "dexterite", "pnj_def"], function(values) { var new_def = parseInt(values.pnj_def); if (isNaN(new_def)) { console.log("DEF pnj non définie"); @@ -2544,7 +2567,7 @@ if (!isNaN(bonus)) defense += bonus; bonus = parseInt(values.DEFBOUCLIER) * parseInt(values.DEFBOUCLIERON); if (!isNaN(bonus)) defense += bonus; - bonus = Math.floor((parseInt(values.DEXTERITE) - 10) / 2); + bonus = Math.floor((parseInt(values.dexterite) - 10) / 2); if (!isNaN(bonus)) defense += bonus; setAttrs({ DEFDIV: new_def - defense, @@ -2553,16 +2576,16 @@ }); }); on('change:pnj_init', function(eventinfo) { - getAttrs(["DEXTERITE", "pnj_init"], function(values) { + getAttrs(["dexterite", "pnj_init"], function(values) { var new_init = parseInt(values.pnj_init); if (isNaN(new_init)) { console.log("Initiative de PNJ incorrecte"); return; } - var dex = parseInt(values.DEXTERITE); + var dex = parseInt(values.dexterite); if (isNaN(dex)) { setAttrs({ - DEXTERITE: new_init, + dexterite: new_init, INIT_DIV: 0 }); return; @@ -2571,13 +2594,13 @@ if (new_init % 2 === 0) { dex--; setAttrs({ - DEXTERITE: dex + dexterite: dex }); } } else if (new_init % 2 == 1) { dex++; setAttrs({ - DEXTERITE: dex + dexterite: dex }); } setAttrs({ @@ -2696,13 +2719,13 @@ }); //Mise à jour des stats dérivées - on('change:CHARISME change:option_pc change:pc_bonus change:famille', function(eventinfo) { - getAttrs(["CHARISME", "option_pc", "pc_bonus", "pc_base", "pc", "pc_max", "option_setting", "famille"], function(values) { + on('change:charisme change:option_pc change:pc_bonus change:famille', function(eventinfo) { + getAttrs(['charisme', 'option_pc', 'pc_bonus', 'pc_base', 'pc', 'pc_max', 'option_setting', 'famille'], function(values) { var toSet = {}; var change; var option_pc = values.option_pc; if (option_pc == 1 || option_pc == 'on') { - var charisme = parseInt(values.CHARISME); + var charisme = parseInt(values.charisme); var pc_bonus = parseInt(values.pc_bonus); if (!isNaN(charisme) && charisme > 0) { var pc_base = 3 + Math.floor((charisme - 10) / 2); @@ -2738,7 +2761,7 @@ }); //Importation de statblocks - var statsReconnues = { + const statsReconnues = { for: 'pnj_for', dex: 'pnj_dex', con: 'pnj_con', @@ -2772,9 +2795,9 @@ return new RegExp(res, 'gi'); } - var patternStats = buildRegexp(statsReconnues); + const patternStats = buildRegexp(statsReconnues); - var listeArmes = [{ + const listeArmes = [{ keys: ['épée', 'epee', 'hache'], typeattaque: 'Arme 1 main', typedegats: 'tranchant', @@ -2806,12 +2829,39 @@ modificateurs: '' }]; - var mapArmes = listeArmes.reduce(function(res, s) { - s.keys.forEach(function(k) { - res[k] = s; + function keyListToMap(l) { + var res = {}; + l.forEach(function(s) { + s.keys.forEach(function(k) { + res[k] = s; + }); + if (s.variantes) { + s.variantes = keyListToMap(s.variantes); + } }); return res; - }, {}); + } + + const mapArmes = keyListToMap(listeArmes); + + function guessAttackType(nomAttaque, prefix, newAttrs) { + var na = nomAttaque.toLowerCase(); + na.split(' ').forEach(function(mot) { + mot = mot.trim(); + if (mot === '') return; + var s = mapArmes[mot]; + if (s) { + newAttrs[prefix + 'typeattaque'] = s.typeattaque; + newAttrs[prefix + 'modificateurs'] = s.modificateurs; + newAttrs[prefix + 'typedegats'] = s.typedegats; + } + }); + if (na.includes('2 mains') || na.includes('deux mains')) + newAttrs[prefix + 'typeattaque'] = 'Arme 2 mains'; + else if (na.includes('attaque magique')) { + newAttrs[prefix + 'typeattaque'] = 'Sortilège'; + } + } on('change:statblock', function(eventinfo) { getAttrs(['max_attack_label', 'scriptVersion'], function(values) { @@ -2944,19 +2994,7 @@ if (values.scriptVersion && values.scriptVersion !== '') newAttrs[prefix + 'optflag'] = 'on'; // On essaie de trouver des valeurs pour le type de l'attaque - var na = nomAttaque.toLowerCase(); - na.split(' ').forEach(function(mot) { - mot = mot.trim(); - if (mot === '') return; - var s = mapArmes[mot]; - if (s) { - newAttrs[prefix + 'typeattaque'] = s.typeattaque; - newAttrs[prefix + 'modificateurs'] = s.modificateurs; - newAttrs[prefix + 'typedegats'] = s.typedegats; - } - }); - if (na.includes('2 mains') || na.includes('deux mains')) - newAttrs[prefix + 'typeattaque'] = 'Arme 2 mains'; + guessAttackType(nomAttaque, prefix, newAttrs); lastPrefix = prefix; previousContainsDM = false; previousLine = ''; @@ -3022,19 +3060,19 @@ newAttrs.force = caracVal; break; case 'pnj_dex': - newAttrs.DEXTERITE = caracVal; + newAttrs.dexterite = caracVal; break; case 'pnj_con': - newAttrs.CONSTITUTION = caracVal; + newAttrs.constitution = caracVal; break; case 'pnj_int': - newAttrs.INTELLIGENCE = caracVal; + newAttrs.intelligence = caracVal; break; case 'pnj_sag': - newAttrs.SAGESSE = caracVal; + newAttrs.sagesse = caracVal; break; case 'pnj_cha': - newAttrs.CHARISME = caracVal; + newAttrs.charisme = caracVal; break; } } else { @@ -3120,7 +3158,7 @@ //Numérotaion des attaques on('change:repeating_armes:armenom', function(eventinfo) { - getAttrs(['max_attack_label', 'repeating_armes_armelabel', 'scriptVersion'], function(values) { + getAttrs(['max_attack_label', 'repeating_armes_armelabel', 'repeating_armes_armenom', 'scriptVersion'], function(values) { var currentLabel = parseInt(values.repeating_armes_armelabel); if (isNaN(currentLabel) || currentLabel === 0) { currentLabel = parseInt(values.max_attack_label); @@ -3130,6 +3168,7 @@ max_attack_label: currentLabel, repeating_armes_armelabel: currentLabel }; + guessAttackType(values.repeating_armes_armenom, 'repeating_armes_arme', sa); if (values.scriptVersion && values.scriptVersion !== '') sa.repeating_armes_armeoptflag = 'on'; setAttrs(sa); @@ -3137,7 +3176,7 @@ }); }); on('change:repeating_pnjatk:armenom', function(eventinfo) { - getAttrs(['max_attack_label', 'repeating_pnjatk_armelabel', 'scriptVersion'], function(values) { + getAttrs(['max_attack_label', 'repeating_pnjatk_armelabel', 'repeating_pnjatk_armenom', 'scriptVersion'], function(values) { var currentLabel = parseInt(values.repeating_pnjatk_armelabel); if (isNaN(currentLabel) || currentLabel === 0) { currentLabel = parseInt(values.max_attack_label); @@ -3147,6 +3186,7 @@ max_attack_label: currentLabel, repeating_pnjatk_armelabel: currentLabel }; + guessAttackType(values.repeating_pnjatk_armenom, 'repeating_pnjatk_arme', sa); if (values.scriptVersion && values.scriptVersion !== '') sa.repeating_pnjatk_armeoptflag = 'on'; setAttrs(sa); @@ -3466,16 +3506,85 @@ }); }); + //Consommables + var listeConsommables = [{ + keys: ['flèche', 'fleche'], + variantes: [{ + keys: ['enflammée', 'enflammee'], + effet: "!cof-attack @{selected|token_id} @{target|token_id} Flèche enflammée --toucher [[@{selected|ATKMAG}]] --dm 1d6 + [[@{selected|INT}]] --portee 30 --fx beam-fire --feu --sortilege --enflamme" + }], + },{ + keys: ['force'], + variantes: [{ + keys: ['géant', 'geant'], + effet: "!cof-effet-temp forceDeGeant 10", + }], + },{ + keys: ['soin', 'soins'], + effet: "!cof-soin 1d8", + variantes: [{ + keys: ['léger', 'leger', 'légers', 'legers'], + effet: "!cof-soin 1d8+@{selected|niveau}", + }, { + keys: ['modéré', 'modere', 'modérés', 'moderes'], + effet: "!cof-soin 2d8+@{selected|niveau}", + }], + }]; + + const mapConsommables = keyListToMap(listeConsommables); + + on('change:repeating_equipement:equip_nom', function(eventinf) { + getAttrs(['repeating_equipement_equip_nom', 'scriptVersion'], function(values) { + var tokens = values.repeating_equipement_equip_nom.toLowerCase().trim(); + if (tokens === '') return; + tokens = tokens.split(' '); + var parse = [ + [mapConsommables] + ]; + var effet; + tokens.forEach(function(t) {console.log("On traite le token "+t); + var updateParse = {}; + parse.forEach(function(pa, i) { + pa.forEach(function(p) { + var s = p[t]; + if (s === undefined) return; + if (s.effet && (effet === undefined || effet.rang < i)) + effet = { + rang: i, + effet: s.effet + }; + if (s.variantes) { + updateParse[i+1] = updateParse[i+1] || []; + updateParse[i+1].push(s.variantes); + } + }); + }); + console.log("On a regardé tous les parses"); + console.log(effet); + console.log(updateParse); + for (var i in updateParse) { + if (i == parse.length) parse.push([]); + parse[i] = parse[i].concat(updateParse[i]); + } + console.log(parse); + }); + if (effet === undefined) return; + setAttrs({ + repeating_equipement_equip_effet: effet.effet, + }); + }); + }); + //Fonctions spécifiques aux terres d'Arran on('change:option_setting', function(eventinfo) { - getAttrs(['option_setting', 'famille', 'CHARISME', 'pc_bonus'], function(values) { + getAttrs(['option_setting', 'famille', 'charisme', 'pc_bonus'], function(values) { var attrs = { option_setting_cac: values.option_setting, option_setting_tir: values.option_setting, option_setting_mag: values.option_setting, option_setting_init: values.option_setting, }; - var charisme = parseInt(values.CHARISME); + var charisme = parseInt(values.charisme); if (!isNaN(charisme) && charisme > 0) { attrs.pc_base = 3 + Math.floor((charisme - 10) / 2); } else attrs.pc_base = 0; @@ -3598,13 +3707,13 @@ }); //Mise à jour de la mana - on('change:SAGESSE change:option_pm change:niveau change:famille', function(eventinfo) { - getAttrs(["SAGESSE", "option_pm", "niveau", "pm", "pm_max", "option_setting", "famille"], function(values) { + on('change:sagesse change:option_pm change:niveau change:famille', function(eventinfo) { + getAttrs(["sagesse", "option_pm", "niveau", "pm", "pm_max", "option_setting", "famille"], function(values) { if (values.option_setting != 'arran') return; var toSet = {}; var option_pm = values.option_pm; if (option_pm == 1 || option_pm == 'on') { - var sagesse = parseInt(values.SAGESSE); + var sagesse = parseInt(values.sagesse); var niveau = parseInt(values.niveau); if (!isNaN(sagesse) && sagesse > 0 && !isNaN(niveau) && niveau >= 0) { var new_pm_max = niveau + Math.floor((sagesse - 10) / 2); diff --git a/ChroniquesOubliees/sheet.json b/ChroniquesOubliees/sheet.json index 737109a894..373fcdabde 100644 --- a/ChroniquesOubliees/sheet.json +++ b/ChroniquesOubliees/sheet.json @@ -4,7 +4,7 @@ "authors": "Natha, Ulti", "roll20userid": "75857, 1794854", "preview": "co_v3.jpg", - "instructions": "Feuille de Personnage (avec jets) pour [Chroniques Oubliées Fantasy](http://www.black-book-editions.fr/catalogue.php?id=13). Chroniques Oubliées Fantasy est un jeu de rôle médiéval-fantastique complet basé sur le système d20/OGL 3.5. Feuille version 4.00 (14/10/2020). [Lisez-moi](https://github.com/Roll20/roll20-character-sheets/blob/master/ChroniquesOubliees/ReadMe.md). Maintenant compatible avec le setting des terres d'Arran (voir les options de fiche)", + "instructions": "Feuille de Personnage (avec jets) pour [Chroniques Oubliées Fantasy](http://www.black-book-editions.fr/catalogue.php?id=13). Chroniques Oubliées Fantasy est un jeu de rôle médiéval-fantastique complet basé sur le système d20/OGL 3.5. Feuille version 4.01 (20/10/2020). [Lisez-moi](https://github.com/Roll20/roll20-character-sheets/blob/master/ChroniquesOubliees/ReadMe.md). Maintenant compatible avec le setting des terres d'Arran (voir les options de fiche)", "useroptions": [ { "attribute": "option_pc",