2024-10-30 22:45:47 +01:00
|
|
|
|
/************************************************************************************/
|
|
|
|
|
import "./xregexp-all.js";
|
|
|
|
|
import { SystemCompendiums } from "../settings/system-compendiums.js";
|
|
|
|
|
import { RdDBaseActorReve } from "../actor/base-actor-reve.js";
|
2024-11-30 22:31:43 +01:00
|
|
|
|
import { Grammar } from "../grammar.js";
|
|
|
|
|
import { Misc } from "../misc.js";
|
2024-12-02 21:59:55 +01:00
|
|
|
|
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
|
2024-10-30 22:45:47 +01:00
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
const WHITESPACES = "\\s+"
|
|
|
|
|
const NUMERIC = "[\\+\\-]?\\d+"
|
|
|
|
|
const NUMERIC_VALUE = "(?<value>" + NUMERIC + ")"
|
2024-10-30 22:45:47 +01:00
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
const XREGEXP_NAME = "(?<name>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)"
|
|
|
|
|
const XREGEXP_COMP_CREATURE = WHITESPACES + "(?<carac>\\d+)"
|
|
|
|
|
+ WHITESPACES + NUMERIC_VALUE
|
|
|
|
|
+ "(" + WHITESPACES + "(?<init>\\d+)?\\s+?(?<dommages>[\\+\\-]?\\d+)?" + ")?"
|
2024-10-30 22:45:47 +01:00
|
|
|
|
|
|
|
|
|
// Skill parser depending on the type of actor
|
2024-11-30 22:31:43 +01:00
|
|
|
|
const compParser = {
|
2024-12-03 00:49:03 +01:00
|
|
|
|
personnage: "(\\D+)*" + WHITESPACES + NUMERIC_VALUE,
|
|
|
|
|
creature: XREGEXP_COMP_CREATURE,
|
|
|
|
|
entite: XREGEXP_COMP_CREATURE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const XREGEXP_SORT_VOIE = "[OHNT\\/]+"
|
|
|
|
|
const XREGEXP_SORT_CASE = "\\((?<case>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)\\)";
|
|
|
|
|
|
|
|
|
|
const XREGEXP_SORT = "(" + XREGEXP_SORT_VOIE
|
|
|
|
|
+ WHITESPACES + XREGEXP_NAME
|
|
|
|
|
+ WHITESPACES + XREGEXP_SORT_CASE
|
|
|
|
|
+ WHITESPACES + "R(?<diff>([\\-\\d]+|(\\w|\\s)+))"
|
|
|
|
|
+ WHITESPACES + "r(?<reve>(\\d+(\\+)?|\\s\\w+))"
|
|
|
|
|
+ "(" + WHITESPACES + "\\+(?<bonus>\\d+)\\s?%" + WHITESPACES + "en" + WHITESPACES + "(?<bonuscase>[A-M]\\d{1,2})" + ")?"
|
|
|
|
|
+ ")"
|
|
|
|
|
|
2024-10-30 22:45:47 +01:00
|
|
|
|
|
|
|
|
|
// Main class for parsing a stat block
|
|
|
|
|
export class RdDStatBlockParser {
|
2024-11-30 22:31:43 +01:00
|
|
|
|
|
2024-10-30 22:45:47 +01:00
|
|
|
|
static openInputDialog() {
|
|
|
|
|
let dialog = new Dialog({
|
|
|
|
|
title: "Import de stats de PNJ/Créatures",
|
|
|
|
|
content: `
|
|
|
|
|
<div>
|
|
|
|
|
<p>Coller le texte de la stat ici</p>
|
|
|
|
|
<textarea id="statBlock" style="width: 100%; height: 200px;"></textarea>
|
|
|
|
|
</div>
|
|
|
|
|
`,
|
|
|
|
|
buttons: {
|
|
|
|
|
ok: {
|
|
|
|
|
label: "OK",
|
|
|
|
|
callback: async (html) => {
|
|
|
|
|
let statBlock = html.find("#statBlock")[0].value;
|
|
|
|
|
await RdDStatBlockParser.parseStatBlock(statBlock);
|
|
|
|
|
dialog.close();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
cancel: {
|
|
|
|
|
label: "Cancel"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
dialog.render(true);
|
|
|
|
|
}
|
2024-11-30 22:31:43 +01:00
|
|
|
|
|
2024-10-31 23:36:16 +01:00
|
|
|
|
static fixWeirdPDF(statString) {
|
|
|
|
|
// Split the statString into lines
|
|
|
|
|
let lines = statString.split("\n");
|
|
|
|
|
let newLines = [];
|
|
|
|
|
let index = 0;
|
|
|
|
|
let nextType = "string";
|
|
|
|
|
// Loop through each line
|
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
|
|
// remove trailing spaces
|
|
|
|
|
lines[i] = lines[i].trim();
|
|
|
|
|
// Is it text ?
|
|
|
|
|
if (lines[i].match(/^[a-zA-Zéêè\s]+/)) {
|
2024-11-30 22:31:43 +01:00
|
|
|
|
if (nextType == "string") {
|
2024-10-31 23:36:16 +01:00
|
|
|
|
newLines[index] = lines[i];
|
2024-11-30 22:31:43 +01:00
|
|
|
|
nextType = "number";
|
2024-10-31 23:36:16 +01:00
|
|
|
|
} else {
|
|
|
|
|
console.log("Wrong sequence string detected...", lines[i], nextType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Is it a number ?
|
|
|
|
|
if (lines[i].match(/^[\d\s]+/)) {
|
2024-11-30 22:31:43 +01:00
|
|
|
|
if (nextType == "number") {
|
2024-10-31 23:36:16 +01:00
|
|
|
|
newLines[index] = newLines[index] + lines[i];
|
|
|
|
|
nextType = "string";
|
|
|
|
|
index++;
|
|
|
|
|
} else {
|
|
|
|
|
console.log("Wrong sequence number detected...", lines[i], nextType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-30 22:31:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-01 14:22:40 +01:00
|
|
|
|
static getHeureKey(heure) {
|
|
|
|
|
for (let h of game.system.rdd.config.heuresRdD) {
|
|
|
|
|
if (h.label.toLowerCase() == heure.toLowerCase()) {
|
|
|
|
|
return h.value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "vaisseau";
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-30 22:45:47 +01:00
|
|
|
|
static async parseStatBlock(statString, type = "npc") {
|
|
|
|
|
|
|
|
|
|
//statString = statBlock03;
|
|
|
|
|
if (!statString) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 23:36:16 +01:00
|
|
|
|
// Special function to fix strange/weird copy/paste from PDF readers
|
|
|
|
|
// Unused up to now : this.fixWeirdPDF(statString);
|
|
|
|
|
|
2024-10-30 22:45:47 +01:00
|
|
|
|
// Replace all endline by space in the statString
|
|
|
|
|
statString = statString.replace(/\n/g, " ");
|
|
|
|
|
// Remove all multiple spaces
|
|
|
|
|
statString = statString.replace(/\s{2,}/g, " ");
|
|
|
|
|
// Remove all leading and trailing spaces
|
|
|
|
|
statString = statString.trim();
|
|
|
|
|
|
2024-11-30 22:31:43 +01:00
|
|
|
|
// TODO: check for entite
|
2024-12-02 21:59:55 +01:00
|
|
|
|
let actorType = RdDStatBlockParser.parseActorType(statString);
|
2024-10-30 22:45:47 +01:00
|
|
|
|
|
|
|
|
|
// Now start carac
|
2024-11-30 22:31:43 +01:00
|
|
|
|
let actorData = foundry.utils.deepClone(game.model.Actor[actorType]);
|
2024-12-02 21:59:55 +01:00
|
|
|
|
for (let key in actorData.carac) {
|
|
|
|
|
let caracDef = actorData.carac[key];
|
2024-10-30 22:45:47 +01:00
|
|
|
|
// Parse the stat string for each caracteristic
|
2024-11-30 22:31:43 +01:00
|
|
|
|
let carac = XRegExp.exec(statString, XRegExp(caracDef.label + "\\s+(?<value>\\d+)", 'giu'));
|
2024-10-30 22:45:47 +01:00
|
|
|
|
if (carac?.value) {
|
|
|
|
|
actorData.carac[key].value = Number(carac.value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If creature we need to setup additionnal fields
|
2024-12-02 21:59:55 +01:00
|
|
|
|
switch (actorType) {
|
|
|
|
|
case "creature":
|
|
|
|
|
RdDStatBlockParser.parseCreature(statString, actorData)
|
|
|
|
|
break
|
|
|
|
|
case "entite":
|
|
|
|
|
RdDStatBlockParser.parseEntite(statString, actorData)
|
|
|
|
|
break
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let items = [];
|
|
|
|
|
// Get skills from compendium
|
|
|
|
|
const competences = await SystemCompendiums.getCompetences(actorType);
|
|
|
|
|
//console.log("Competences : ", competences);
|
2024-12-03 00:49:03 +01:00
|
|
|
|
for (let comp of competences) {
|
|
|
|
|
let compMatch = XRegExp.exec(statString, XRegExp(comp.name + compParser[actorType], 'giu'));
|
|
|
|
|
if (compMatch) {
|
|
|
|
|
comp = comp.toObject()
|
|
|
|
|
comp.system.niveau = Number(compMatch.value);
|
|
|
|
|
if (actorType == "creature" || actorType == "entite") {
|
|
|
|
|
comp.system.carac_value = Number(compMatch.carac);
|
|
|
|
|
if (compMatch.dommages != undefined) {
|
|
|
|
|
comp.system.dommages = Number(compMatch.dommages);
|
2024-10-30 22:45:47 +01:00
|
|
|
|
comp.system.iscombat = true;
|
|
|
|
|
}
|
2024-10-31 23:36:16 +01:00
|
|
|
|
}
|
2024-12-03 00:49:03 +01:00
|
|
|
|
items.push(comp)
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
2024-12-03 00:49:03 +01:00
|
|
|
|
else if (actorType == "personnage") {
|
|
|
|
|
comp = comp.toObject()
|
2024-12-02 21:59:55 +01:00
|
|
|
|
items.push(comp)
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now process weapons
|
|
|
|
|
const weapons = await SystemCompendiums.getWorldOrCompendiumItems("arme", "equipement")
|
|
|
|
|
//console.log("Equipement : ", equipment);
|
2024-11-30 22:31:43 +01:00
|
|
|
|
// TODO: les noms d'armes peuvent avoir un suffixe (à une main, lancée) qui détermine la compétence correspondante
|
|
|
|
|
// TODO: une arme peut être spécifique ("fourche"), ajouter une compétence dans ces cas là?
|
2024-12-03 00:49:03 +01:00
|
|
|
|
for (let weapon of weapons) {
|
|
|
|
|
let weapMatch = XRegExp.exec(statString, XRegExp(weapon.name + "\\s+(?<value>\\+\\d+)", 'giu'));
|
|
|
|
|
if (weapMatch) {
|
|
|
|
|
weapon = weapon.toObject()
|
|
|
|
|
weapon.system.equipe = 'true'
|
|
|
|
|
items.push(weapon)
|
2024-10-30 22:45:47 +01:00
|
|
|
|
// now process the skill
|
2024-12-03 00:49:03 +01:00
|
|
|
|
if (weapon.system?.competence != "") {
|
|
|
|
|
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, weapon.system.competence))
|
2024-10-30 22:45:47 +01:00
|
|
|
|
if (wComp) {
|
2024-12-03 00:49:03 +01:00
|
|
|
|
wComp.system.niveau = Number(weapMatch.value);
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-03 00:49:03 +01:00
|
|
|
|
if (weapon.system?.tir != "") {
|
|
|
|
|
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, weapon.system.tir))
|
2024-10-30 22:45:47 +01:00
|
|
|
|
if (wComp) {
|
2024-12-03 00:49:03 +01:00
|
|
|
|
wComp.system.niveau = Number(weapMatch.value);
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-03 00:49:03 +01:00
|
|
|
|
if (weapon.system?.lancer != "") {
|
|
|
|
|
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, weapon.system.lancer))
|
2024-10-30 22:45:47 +01:00
|
|
|
|
if (wComp) {
|
2024-12-03 00:49:03 +01:00
|
|
|
|
wComp.system.niveau = Number(weapMatch.value);
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now process armors
|
|
|
|
|
const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement")
|
2024-12-03 00:49:03 +01:00
|
|
|
|
for (let armor of armors) {
|
|
|
|
|
let matchArmor = XRegExp.exec(statString, XRegExp(armor.name, 'giu'));
|
|
|
|
|
if (matchArmor) {
|
|
|
|
|
armor = armor.toObject()
|
|
|
|
|
armor.system.equipe = true
|
|
|
|
|
items.push(armor);
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-01 14:22:40 +01:00
|
|
|
|
// Attemp to detect spell
|
|
|
|
|
let hautRevant = false
|
|
|
|
|
let sorts = await SystemCompendiums.getWorldOrCompendiumItems("sort", "sorts-oniros")
|
|
|
|
|
sorts = sorts.concat(await SystemCompendiums.getWorldOrCompendiumItems("sort", "sorts-hypnos"))
|
|
|
|
|
sorts = sorts.concat(await SystemCompendiums.getWorldOrCompendiumItems("sort", "sorts-narcos"))
|
|
|
|
|
sorts = sorts.concat(await SystemCompendiums.getWorldOrCompendiumItems("sort", "sorts-thanatos"))
|
2024-12-03 00:49:03 +01:00
|
|
|
|
|
|
|
|
|
XRegExp.forEach(statString, XRegExp(XREGEXP_SORT, 'giu'),
|
|
|
|
|
function (matchSort, i) {
|
|
|
|
|
let sort = sorts.find(s => Grammar.equalsInsensitive(s.name, matchSort.name))
|
|
|
|
|
if (sort) {
|
2024-12-01 14:22:40 +01:00
|
|
|
|
hautRevant = true
|
2024-12-03 00:49:03 +01:00
|
|
|
|
sort = sort.toObject();
|
|
|
|
|
if (matchSort.bonus && matchSort.bonuscase) {
|
|
|
|
|
sort.system.bonuscase = `${matchSort.bonuscase}:${matchSort.bonus}`
|
|
|
|
|
}
|
2024-12-01 14:22:40 +01:00
|
|
|
|
items.push(sort);
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-12-03 00:49:03 +01:00
|
|
|
|
|
2024-12-02 21:59:55 +01:00
|
|
|
|
if (hautRevant) {
|
2024-12-01 14:22:40 +01:00
|
|
|
|
let tetes = await SystemCompendiums.getWorldOrCompendiumItems("tete", "tetes-de-dragon-pour-tous-personnages")
|
|
|
|
|
let donHR = tetes.find(t => Grammar.equalsInsensitive(t.name, "Don de Haut-Rêve"))
|
2024-12-02 21:59:55 +01:00
|
|
|
|
if (donHR) {
|
2024-12-01 14:22:40 +01:00
|
|
|
|
items.push(donHR.toObject());
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-03 00:49:03 +01:00
|
|
|
|
if (actorType == "personnage") {
|
2024-12-01 14:22:40 +01:00
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
let feminin = XRegExp.exec(statString, XRegExp("né(?<value>e?) à", 'giu'));
|
|
|
|
|
actorData.sexe = (feminin?.value == 'e') ? 'féminin' : 'masculin'
|
2024-11-30 22:31:43 +01:00
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
// Get hour name : heure du XXXXX
|
|
|
|
|
let heure = XRegExp.exec(statString, XRegExp("heure (du|de la|des|de l\')\\s*(?<value>[A-Za-zÀ-ÖØ-öø-ÿ\\s]+),", 'giu'));
|
|
|
|
|
actorData.heure = this.getHeureKey(heure?.value || "Vaisseau");
|
2024-12-01 14:22:40 +01:00
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
// Get age
|
|
|
|
|
let age = XRegExp.exec(statString, XRegExp("(?<value>\\d+) ans", 'giu'));
|
|
|
|
|
if (age?.value) {
|
|
|
|
|
actorData.age = Number(age.value);
|
|
|
|
|
}
|
|
|
|
|
// Get height
|
|
|
|
|
let taille = XRegExp.exec(statString, XRegExp("(?<value>\\d+m\\d+)", 'giu'));
|
|
|
|
|
if (taille?.value) {
|
|
|
|
|
actorData.taille = taille.value;
|
|
|
|
|
}
|
|
|
|
|
// Get weight
|
|
|
|
|
let poids = XRegExp.exec(statString, XRegExp("(?<value>\\d+) kg", 'giu'));
|
|
|
|
|
if (poids?.value) {
|
|
|
|
|
actorData.poids = poids.value;
|
|
|
|
|
}
|
|
|
|
|
// Get cheveux
|
|
|
|
|
let cheveux = XRegExp.exec(statString, XRegExp("kg,\\s+(?<value>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+),\\s+yeux", 'giu'));
|
|
|
|
|
if (cheveux?.value) {
|
|
|
|
|
actorData.cheveux = cheveux.value;
|
|
|
|
|
}
|
|
|
|
|
// Get yeux
|
|
|
|
|
let yeux = XRegExp.exec(statString, XRegExp("yeux\\s+(?<value>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+), Beau", 'giu'));
|
|
|
|
|
if (yeux?.value) {
|
|
|
|
|
actorData.yeux = yeux.value;
|
|
|
|
|
}
|
2024-12-01 14:22:40 +01:00
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
// Get beauty
|
|
|
|
|
let beaute = XRegExp.exec(statString, XRegExp("beauté\\s+(?<value>\\d+)", 'giu'));
|
|
|
|
|
if (beaute?.value) {
|
|
|
|
|
actorData.beaute = Number(beaute.value);
|
|
|
|
|
}
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Name is all string before ', né'
|
2024-11-30 22:31:43 +01:00
|
|
|
|
let name = RdDStatBlockParser.extractName(actorType, statString);
|
|
|
|
|
|
|
|
|
|
let newActor = RdDBaseActorReve.create({ name: name || "Importé", type: actorType, system: actorData, items: items });
|
2024-10-30 22:45:47 +01:00
|
|
|
|
|
|
|
|
|
// DUmp....
|
|
|
|
|
console.log(actorData);
|
|
|
|
|
}
|
2024-11-30 22:31:43 +01:00
|
|
|
|
|
2024-12-02 21:59:55 +01:00
|
|
|
|
static parseCreature(statString, actorData) {
|
|
|
|
|
let plusDom = XRegExp.exec(statString, XRegExp("\\+dom\\s+(?<value>[\\+\\-]?\\d+)", 'giu'));
|
|
|
|
|
if (plusDom?.values) {
|
|
|
|
|
actorData.attributs.plusdom.value = Number(plusDom.value);
|
|
|
|
|
}
|
|
|
|
|
let protection = XRegExp.exec(statString, XRegExp("protection\\s+(?<value>[\\-]?\\d+)", 'giu'));
|
|
|
|
|
if (protection?.value) {
|
|
|
|
|
actorData.attributs.protection.value = Number(protection.value);
|
|
|
|
|
}
|
|
|
|
|
let endurance = XRegExp.exec(statString, XRegExp("endurance\\s+(?<value>\\d+)", 'giu'));
|
|
|
|
|
if (endurance?.value) {
|
|
|
|
|
actorData.sante.endurance.value = Number(endurance.value);
|
|
|
|
|
actorData.sante.endurance.max = Number(endurance.value);
|
|
|
|
|
}
|
|
|
|
|
let vie = XRegExp.exec(statString, XRegExp("vie\\s+(?<value>\\d+)", 'giu'));
|
|
|
|
|
if (vie.value) {
|
|
|
|
|
actorData.sante.vie.value = Number(vie.value);
|
|
|
|
|
actorData.sante.vie.max = Number(vie.value);
|
|
|
|
|
}
|
|
|
|
|
let vitesse = XRegExp.exec(statString, XRegExp("vitesse\\s+(?<value>[\\d\\/]+)", 'giu'));
|
|
|
|
|
if (vitesse?.value) {
|
|
|
|
|
actorData.attributs.vitesse.value = vitesse.value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static parseEntite(statString, actorData) {
|
|
|
|
|
let plusDom = XRegExp.exec(statString, XRegExp("\\+dom\\s+(?<value>[\\+\\-]?\\d+)", 'giu'));
|
|
|
|
|
if (plusDom?.values) {
|
|
|
|
|
actorData.attributs.plusdom.value = Number(plusDom.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
actorData.definition.categorieentite = 'cauchemar'
|
|
|
|
|
actorData.definition.typeentite = ENTITE_NONINCARNE
|
|
|
|
|
let endurance = XRegExp.exec(statString, XRegExp("endurance\\s+(?<value>\\d+)", 'giu'));
|
|
|
|
|
if (endurance?.value) {
|
|
|
|
|
actorData.sante.endurance.value = Number(endurance.value);
|
|
|
|
|
actorData.sante.endurance.max = Number(endurance.value);
|
|
|
|
|
actorData.definition.typeentite = ENTITE_INCARNE
|
|
|
|
|
}
|
|
|
|
|
let vitesse = XRegExp.exec(statString, XRegExp("vitesse\\s+(?<value>[\\d\\/]+)", 'giu'));
|
|
|
|
|
if (vitesse?.value) {
|
|
|
|
|
actorData.attributs.vitesse.value = vitesse.value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static parseActorType(statString) {
|
|
|
|
|
let niveau = XRegExp.exec(statString, XRegExp("Niveau\\s+(?<value>[\\+\\-]?\\d+)", 'giu'))
|
|
|
|
|
let perception = XRegExp.exec(statString, XRegExp("perception\\s+(?<value>\\d+)", 'giu'))
|
|
|
|
|
if (perception?.value) {
|
|
|
|
|
return "creature"
|
|
|
|
|
}
|
|
|
|
|
if (niveau?.value) {
|
|
|
|
|
return "entite"
|
|
|
|
|
}
|
|
|
|
|
return "personnage"
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-30 22:31:43 +01:00
|
|
|
|
static extractName(actorType, statString) {
|
|
|
|
|
switch (actorType) {
|
|
|
|
|
case "personnage": return RdDStatBlockParser.extractNamePersonnage(statString);
|
|
|
|
|
case "creature": return RdDStatBlockParser.extractNameCreature(statString);
|
|
|
|
|
}
|
|
|
|
|
return RdDStatBlockParser.extractNameCreature(statString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static extractNamePersonnage(statString) {
|
|
|
|
|
let name = XRegExp.exec(statString, XRegExp("(?<value>[\\p{Letter}\\s\\d]+),", 'giu'));
|
|
|
|
|
if (!name?.value) {
|
|
|
|
|
name = XRegExp.exec(statString, XRegExp("(?<value>.+)\\s+taille", 'giu'));
|
|
|
|
|
}
|
|
|
|
|
return Misc.upperFirst(name?.value || "Importé");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static extractNameCreature(statString) {
|
|
|
|
|
const name = XRegExp.exec(statString, XRegExp("(?<value>.+)\\s+taille", 'giu'));
|
|
|
|
|
return Misc.upperFirst(name?.value || "Importé");
|
|
|
|
|
}
|
2024-10-30 22:45:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-03 00:49:03 +01:00
|
|
|
|
/************************************************************************************/
|
|
|
|
|
// Some internal test strings
|
|
|
|
|
let statBlock01 = `+$16(/, baron de Sylvedire, né à l’heure du
|
|
|
|
|
Roseau, 40 ans, 1m78, 65 kg, Beauté 13.
|
|
|
|
|
TAILLE
|
|
|
|
|
10
|
|
|
|
|
Mêlée
|
|
|
|
|
14
|
|
|
|
|
APPARENCE
|
|
|
|
|
13
|
|
|
|
|
Tir
|
|
|
|
|
11
|
|
|
|
|
CONSTITUTION
|
|
|
|
|
12
|
|
|
|
|
Lancer
|
|
|
|
|
11
|
|
|
|
|
FORCE
|
|
|
|
|
12
|
|
|
|
|
Dérobée
|
|
|
|
|
13
|
|
|
|
|
AGILITÉ
|
|
|
|
|
16
|
|
|
|
|
Vie
|
|
|
|
|
11
|
|
|
|
|
DEXTÉRITÉ
|
|
|
|
|
13
|
|
|
|
|
Endurance
|
|
|
|
|
25
|
|
|
|
|
VUE
|
|
|
|
|
10
|
|
|
|
|
+dom
|
|
|
|
|
0
|
|
|
|
|
OUÏE
|
|
|
|
|
11
|
|
|
|
|
Protection
|
|
|
|
|
2 ou 4
|
|
|
|
|
ODO-GOÛT
|
|
|
|
|
9
|
|
|
|
|
cuir souple
|
|
|
|
|
VOLONTÉ
|
|
|
|
|
14
|
|
|
|
|
ou cuir / métal
|
|
|
|
|
INTELLECT
|
|
|
|
|
9
|
|
|
|
|
EMPATHIE
|
|
|
|
|
11
|
|
|
|
|
RÊVE
|
|
|
|
|
13
|
|
|
|
|
CHANCE
|
|
|
|
|
10
|
|
|
|
|
niv
|
|
|
|
|
init
|
|
|
|
|
+dom
|
|
|
|
|
Épée dragonne
|
|
|
|
|
+5
|
|
|
|
|
12
|
|
|
|
|
+3
|
|
|
|
|
Hache de bataille
|
|
|
|
|
+6
|
|
|
|
|
13
|
|
|
|
|
+3
|
|
|
|
|
Bouclier moyen
|
|
|
|
|
+5
|
|
|
|
|
Dague mêlée
|
|
|
|
|
+4
|
|
|
|
|
11
|
|
|
|
|
+1
|
|
|
|
|
Corps à corps
|
|
|
|
|
+4
|
|
|
|
|
11
|
|
|
|
|
(0)
|
|
|
|
|
Esquive
|
|
|
|
|
+8
|
|
|
|
|
Escalade, Saut +4 / Commerce +3 / Équitation
|
|
|
|
|
+6 / Chirurgie 0 / Survie en extérieur +4 / Survie fo-
|
|
|
|
|
rêt +6 / Acrobatie -2 / Métallurgie +2 / Natation +3 /
|
|
|
|
|
Légendes -1 / Écriture -4
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
let statBlock02 = `/HVJDUGHV
|
|
|
|
|
TAILLE
|
|
|
|
|
11
|
|
|
|
|
Mêlée
|
|
|
|
|
12
|
|
|
|
|
CONSTITUTION
|
|
|
|
|
11
|
|
|
|
|
Tir
|
|
|
|
|
11
|
|
|
|
|
FORCE
|
|
|
|
|
12
|
|
|
|
|
Lancer
|
|
|
|
|
11
|
|
|
|
|
AGILITÉ
|
|
|
|
|
12
|
|
|
|
|
Dérobée
|
|
|
|
|
11
|
|
|
|
|
DEXTERITÉ
|
|
|
|
|
11
|
|
|
|
|
Vie
|
|
|
|
|
11
|
|
|
|
|
VUE
|
|
|
|
|
11
|
|
|
|
|
Endurance
|
|
|
|
|
22
|
|
|
|
|
OUÏE
|
|
|
|
|
11
|
|
|
|
|
Vitesse
|
|
|
|
|
12
|
|
|
|
|
VOLONTÉ
|
|
|
|
|
10
|
|
|
|
|
+dom
|
|
|
|
|
0
|
|
|
|
|
Protection
|
|
|
|
|
4
|
|
|
|
|
cuir / métal
|
|
|
|
|
niv
|
|
|
|
|
init
|
|
|
|
|
+dom
|
|
|
|
|
Hache de bataille
|
|
|
|
|
+4
|
|
|
|
|
10
|
|
|
|
|
+3
|
|
|
|
|
Bouclier moyen
|
|
|
|
|
+4
|
|
|
|
|
Dague mêlée
|
|
|
|
|
+3
|
|
|
|
|
9
|
|
|
|
|
+1
|
|
|
|
|
Arc
|
|
|
|
|
+5
|
|
|
|
|
10
|
|
|
|
|
+2
|
|
|
|
|
Corps à corps
|
|
|
|
|
+3
|
|
|
|
|
9
|
|
|
|
|
(0)
|
|
|
|
|
Esquive avec armure
|
|
|
|
|
+2
|
|
|
|
|
Course +1/ Vigilance +4
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
let statBlock03 = `rencontres sont laissées à /HVFKLHQVORXSVGXEDURQ
|
|
|
|
|
chaque gardien des rêves.
|
|
|
|
|
TAILLE
|
|
|
|
|
8
|
|
|
|
|
Vie
|
|
|
|
|
10
|
|
|
|
|
CONSTITUTION FORCE
|
|
|
|
|
12
|
|
|
|
|
11
|
|
|
|
|
Endurance
|
|
|
|
|
Vitesse
|
|
|
|
|
12/38
|
|
|
|
|
21
|
|
|
|
|
/HVFKLHQV]RPELV
|
|
|
|
|
PERCEPTION 13
|
|
|
|
|
+dom
|
|
|
|
|
0
|
|
|
|
|
VOLONTÉ
|
|
|
|
|
10
|
|
|
|
|
Protection
|
|
|
|
|
0
|
|
|
|
|
Les « monstres » apparaîtront un soir, durant
|
|
|
|
|
RÊVE
|
|
|
|
|
10
|
|
|
|
|
l’heure du Serpent, et attaqueront les voya-
|
|
|
|
|
niv
|
|
|
|
|
init
|
|
|
|
|
+dom
|
|
|
|
|
geurs à leur campement. Si ces derniers ne
|
|
|
|
|
Morsure
|
|
|
|
|
13
|
|
|
|
|
+4
|
|
|
|
|
10
|
|
|
|
|
+1
|
|
|
|
|
campent pas, ils apparaîtront tout de même à
|
|
|
|
|
Esquive
|
|
|
|
|
11
|
|
|
|
|
+3
|
|
|
|
|
l’heure du Serpent. Le feu ne les effraie pas. Ils
|
|
|
|
|
Course, Saut
|
|
|
|
|
12
|
|
|
|
|
+3
|
|
|
|
|
ne sont pas très rapides, mais en revanche, très
|
|
|
|
|
Discrétion
|
|
|
|
|
12
|
|
|
|
|
+3
|
|
|
|
|
silencieux : ils n’aboient pas. Les voyageurs
|
|
|
|
|
Vigilance
|
|
|
|
|
13
|
|
|
|
|
+3
|
|
|
|
|
`
|