Améliorations parser
- armure équipée - armes équipées - ajout des bonus de cases - dommages des créatures - ajout des esquives spéciales - fix édition feuilles de compétences créatures - déplacement des valeurs de test en fin de fichier
This commit is contained in:
parent
a27e3894a0
commit
fa375f8092
@ -6,6 +6,370 @@ import { Grammar } from "../grammar.js";
|
|||||||
import { Misc } from "../misc.js";
|
import { Misc } from "../misc.js";
|
||||||
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
|
import { ENTITE_INCARNE, ENTITE_NONINCARNE } from "../constants.js";
|
||||||
|
|
||||||
|
const WHITESPACES = "\\s+"
|
||||||
|
const NUMERIC = "[\\+\\-]?\\d+"
|
||||||
|
const NUMERIC_VALUE = "(?<value>" + NUMERIC + ")"
|
||||||
|
|
||||||
|
const XREGEXP_NAME = "(?<name>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)"
|
||||||
|
const XREGEXP_COMP_CREATURE = WHITESPACES + "(?<carac>\\d+)"
|
||||||
|
+ WHITESPACES + NUMERIC_VALUE
|
||||||
|
+ "(" + WHITESPACES + "(?<init>\\d+)?\\s+?(?<dommages>[\\+\\-]?\\d+)?" + ")?"
|
||||||
|
|
||||||
|
// Skill parser depending on the type of actor
|
||||||
|
const compParser = {
|
||||||
|
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})" + ")?"
|
||||||
|
+ ")"
|
||||||
|
|
||||||
|
|
||||||
|
// Main class for parsing a stat block
|
||||||
|
export class RdDStatBlockParser {
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]+/)) {
|
||||||
|
if (nextType == "string") {
|
||||||
|
newLines[index] = lines[i];
|
||||||
|
nextType = "number";
|
||||||
|
} else {
|
||||||
|
console.log("Wrong sequence string detected...", lines[i], nextType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Is it a number ?
|
||||||
|
if (lines[i].match(/^[\d\s]+/)) {
|
||||||
|
if (nextType == "number") {
|
||||||
|
newLines[index] = newLines[index] + lines[i];
|
||||||
|
nextType = "string";
|
||||||
|
index++;
|
||||||
|
} else {
|
||||||
|
console.log("Wrong sequence number detected...", lines[i], nextType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getHeureKey(heure) {
|
||||||
|
for (let h of game.system.rdd.config.heuresRdD) {
|
||||||
|
if (h.label.toLowerCase() == heure.toLowerCase()) {
|
||||||
|
return h.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "vaisseau";
|
||||||
|
}
|
||||||
|
|
||||||
|
static async parseStatBlock(statString, type = "npc") {
|
||||||
|
|
||||||
|
//statString = statBlock03;
|
||||||
|
if (!statString) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special function to fix strange/weird copy/paste from PDF readers
|
||||||
|
// Unused up to now : this.fixWeirdPDF(statString);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// TODO: check for entite
|
||||||
|
let actorType = RdDStatBlockParser.parseActorType(statString);
|
||||||
|
|
||||||
|
// Now start carac
|
||||||
|
let actorData = foundry.utils.deepClone(game.model.Actor[actorType]);
|
||||||
|
for (let key in actorData.carac) {
|
||||||
|
let caracDef = actorData.carac[key];
|
||||||
|
// Parse the stat string for each caracteristic
|
||||||
|
let carac = XRegExp.exec(statString, XRegExp(caracDef.label + "\\s+(?<value>\\d+)", 'giu'));
|
||||||
|
if (carac?.value) {
|
||||||
|
actorData.carac[key].value = Number(carac.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If creature we need to setup additionnal fields
|
||||||
|
switch (actorType) {
|
||||||
|
case "creature":
|
||||||
|
RdDStatBlockParser.parseCreature(statString, actorData)
|
||||||
|
break
|
||||||
|
case "entite":
|
||||||
|
RdDStatBlockParser.parseEntite(statString, actorData)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
// Get skills from compendium
|
||||||
|
const competences = await SystemCompendiums.getCompetences(actorType);
|
||||||
|
//console.log("Competences : ", competences);
|
||||||
|
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);
|
||||||
|
comp.system.iscombat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.push(comp)
|
||||||
|
}
|
||||||
|
else if (actorType == "personnage") {
|
||||||
|
comp = comp.toObject()
|
||||||
|
items.push(comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now process weapons
|
||||||
|
const weapons = await SystemCompendiums.getWorldOrCompendiumItems("arme", "equipement")
|
||||||
|
//console.log("Equipement : ", equipment);
|
||||||
|
// 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à?
|
||||||
|
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)
|
||||||
|
// now process the skill
|
||||||
|
if (weapon.system?.competence != "") {
|
||||||
|
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, weapon.system.competence))
|
||||||
|
if (wComp) {
|
||||||
|
wComp.system.niveau = Number(weapMatch.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (weapon.system?.tir != "") {
|
||||||
|
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, weapon.system.tir))
|
||||||
|
if (wComp) {
|
||||||
|
wComp.system.niveau = Number(weapMatch.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (weapon.system?.lancer != "") {
|
||||||
|
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, weapon.system.lancer))
|
||||||
|
if (wComp) {
|
||||||
|
wComp.system.niveau = Number(weapMatch.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now process armors
|
||||||
|
const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement")
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"))
|
||||||
|
|
||||||
|
XRegExp.forEach(statString, XRegExp(XREGEXP_SORT, 'giu'),
|
||||||
|
function (matchSort, i) {
|
||||||
|
let sort = sorts.find(s => Grammar.equalsInsensitive(s.name, matchSort.name))
|
||||||
|
if (sort) {
|
||||||
|
hautRevant = true
|
||||||
|
sort = sort.toObject();
|
||||||
|
if (matchSort.bonus && matchSort.bonuscase) {
|
||||||
|
sort.system.bonuscase = `${matchSort.bonuscase}:${matchSort.bonus}`
|
||||||
|
}
|
||||||
|
items.push(sort);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hautRevant) {
|
||||||
|
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"))
|
||||||
|
if (donHR) {
|
||||||
|
items.push(donHR.toObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (actorType == "personnage") {
|
||||||
|
|
||||||
|
let feminin = XRegExp.exec(statString, XRegExp("né(?<value>e?) à", 'giu'));
|
||||||
|
actorData.sexe = (feminin?.value == 'e') ? 'féminin' : 'masculin'
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get beauty
|
||||||
|
let beaute = XRegExp.exec(statString, XRegExp("beauté\\s+(?<value>\\d+)", 'giu'));
|
||||||
|
if (beaute?.value) {
|
||||||
|
actorData.beaute = Number(beaute.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name is all string before ', né'
|
||||||
|
let name = RdDStatBlockParser.extractName(actorType, statString);
|
||||||
|
|
||||||
|
let newActor = RdDBaseActorReve.create({ name: name || "Importé", type: actorType, system: actorData, items: items });
|
||||||
|
|
||||||
|
// DUmp....
|
||||||
|
console.log(actorData);
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
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é");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/************************************************************************************/
|
/************************************************************************************/
|
||||||
// Some internal test strings
|
// Some internal test strings
|
||||||
let statBlock01 = `+$16(/, baron de Sylvedire, né à l’heure du
|
let statBlock01 = `+$16(/, baron de Sylvedire, né à l’heure du
|
||||||
@ -197,337 +561,4 @@ Discrétion
|
|||||||
Vigilance
|
Vigilance
|
||||||
13
|
13
|
||||||
+3
|
+3
|
||||||
`
|
`
|
||||||
// Skill parser depending on the type of actor
|
|
||||||
const compParser = {
|
|
||||||
personnage: "(\\D+)*\\s+(?<value>[\\+\\-]?\\d+)",
|
|
||||||
creature: "\\s+(?<carac>\\d+)\\s+(?<value>[\\+\\-]?\\d+)\\s?(?<init>\\d+)?\\s+?(?<dommages>\\+\\d+)?"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main class for parsing a stat block
|
|
||||||
export class RdDStatBlockParser {
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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]+/)) {
|
|
||||||
if (nextType == "string") {
|
|
||||||
newLines[index] = lines[i];
|
|
||||||
nextType = "number";
|
|
||||||
} else {
|
|
||||||
console.log("Wrong sequence string detected...", lines[i], nextType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Is it a number ?
|
|
||||||
if (lines[i].match(/^[\d\s]+/)) {
|
|
||||||
if (nextType == "number") {
|
|
||||||
newLines[index] = newLines[index] + lines[i];
|
|
||||||
nextType = "string";
|
|
||||||
index++;
|
|
||||||
} else {
|
|
||||||
console.log("Wrong sequence number detected...", lines[i], nextType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static getHeureKey(heure) {
|
|
||||||
for (let h of game.system.rdd.config.heuresRdD) {
|
|
||||||
if (h.label.toLowerCase() == heure.toLowerCase()) {
|
|
||||||
return h.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "vaisseau";
|
|
||||||
}
|
|
||||||
|
|
||||||
static async parseStatBlock(statString, type = "npc") {
|
|
||||||
|
|
||||||
//statString = statBlock03;
|
|
||||||
if (!statString) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special function to fix strange/weird copy/paste from PDF readers
|
|
||||||
// Unused up to now : this.fixWeirdPDF(statString);
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
// TODO: check for entite
|
|
||||||
let actorType = RdDStatBlockParser.parseActorType(statString);
|
|
||||||
|
|
||||||
// Now start carac
|
|
||||||
let actorData = foundry.utils.deepClone(game.model.Actor[actorType]);
|
|
||||||
for (let key in actorData.carac) {
|
|
||||||
let caracDef = actorData.carac[key];
|
|
||||||
// Parse the stat string for each caracteristic
|
|
||||||
let carac = XRegExp.exec(statString, XRegExp(caracDef.label + "\\s+(?<value>\\d+)", 'giu'));
|
|
||||||
if (carac?.value) {
|
|
||||||
actorData.carac[key].value = Number(carac.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If creature we need to setup additionnal fields
|
|
||||||
switch (actorType) {
|
|
||||||
case "creature":
|
|
||||||
RdDStatBlockParser.parseCreature(statString, actorData)
|
|
||||||
break
|
|
||||||
case "entite":
|
|
||||||
RdDStatBlockParser.parseEntite(statString, actorData)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
let items = [];
|
|
||||||
// Get skills from compendium
|
|
||||||
const competences = await SystemCompendiums.getCompetences(actorType);
|
|
||||||
//console.log("Competences : ", competences);
|
|
||||||
let allComp = competences.map(i => i.toObject())
|
|
||||||
for (let comp of allComp) {
|
|
||||||
const compName = comp.name;
|
|
||||||
let skill = XRegExp.exec(Grammar.toLowerCaseNoAccent(statString), XRegExp(Grammar.toLowerCaseNoAccent(compName) + compParser[actorType], 'giu'));
|
|
||||||
if (skill) {
|
|
||||||
comp.system.niveau = Number(skill.value);
|
|
||||||
if (actorType == "creature") {
|
|
||||||
comp.system.carac_value = Number(skill.carac);
|
|
||||||
if (skill.init) {
|
|
||||||
comp.system.dommages = Number(skill.dommages);
|
|
||||||
comp.system.iscombat = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (actorType == "personnage" || skill!= undefined){
|
|
||||||
items.push(comp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now process weapons
|
|
||||||
const weapons = await SystemCompendiums.getWorldOrCompendiumItems("arme", "equipement")
|
|
||||||
//console.log("Equipement : ", equipment);
|
|
||||||
// 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à?
|
|
||||||
for (let w of weapons) {
|
|
||||||
let weapon = XRegExp.exec(statString, XRegExp(w.name + "\\s+(?<value>\\+\\d+)", 'giu'));
|
|
||||||
if (weapon) {
|
|
||||||
w.system.equipe = 'true'
|
|
||||||
items.push(w.toObject());
|
|
||||||
// now process the skill
|
|
||||||
if (w.system?.competence != "") {
|
|
||||||
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.competence))
|
|
||||||
if (wComp) {
|
|
||||||
wComp.system.niveau = Number(weapon.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (w.system?.tir != "") {
|
|
||||||
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.tir))
|
|
||||||
if (wComp) {
|
|
||||||
wComp.system.niveau = Number(weapon.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (w.system?.lancer != "") {
|
|
||||||
let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.lancer))
|
|
||||||
if (wComp) {
|
|
||||||
wComp.system.niveau = Number(weapon.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now process armors
|
|
||||||
const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement")
|
|
||||||
for (let a of armors) {
|
|
||||||
let armor = XRegExp.exec(statString, XRegExp(a.name, 'giu'));
|
|
||||||
if (armor) {
|
|
||||||
a.system.equipe = true
|
|
||||||
items.push(a.toObject());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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"))
|
|
||||||
XRegExp.forEach(statString, XRegExp("[OHNT]\\s+(?<name>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)\\s+\\((?<case>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)\\)\\s+R(?<diff>[\\-\\d]+)\\s+r(?<reve>\\d+)", 'giu'),
|
|
||||||
function (match, i) {
|
|
||||||
let sortCompendium = sorts.find(s => Grammar.equalsInsensitive(s.name, match.name))
|
|
||||||
if (sortCompendium) {
|
|
||||||
hautRevant = true
|
|
||||||
let sort = sortCompendium.toObject();
|
|
||||||
items.push(sort);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (hautRevant) {
|
|
||||||
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"))
|
|
||||||
if (donHR) {
|
|
||||||
items.push(donHR.toObject());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let feminin = XRegExp.exec(statString, XRegExp("né(?<value>e?) à", 'giu'));
|
|
||||||
actorData.sexe = (feminin?.value == 'e') ? 'féminin' : 'masculin'
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get beauty
|
|
||||||
let beaute = XRegExp.exec(statString, XRegExp("beauté\\s+(?<value>\\d+)", 'giu'));
|
|
||||||
if (beaute?.value) {
|
|
||||||
actorData.beaute = Number(beaute.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name is all string before ', né'
|
|
||||||
let name = RdDStatBlockParser.extractName(actorType, statString);
|
|
||||||
|
|
||||||
let newActor = RdDBaseActorReve.create({ name: name || "Importé", type: actorType, system: actorData, items: items });
|
|
||||||
|
|
||||||
// DUmp....
|
|
||||||
console.log(actorData);
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
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é");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ export class RdDItemCompetenceCreature extends Item {
|
|||||||
|
|
||||||
static isParade(item) {
|
static isParade(item) {
|
||||||
if (item.type == ITEM_TYPES.competencecreature) {
|
if (item.type == ITEM_TYPES.competencecreature) {
|
||||||
return armeData.system.categorie_parade || armeData.system.isparade
|
return item.system.categorie_parade || item.system.isparade
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
name: Esquive eau
|
||||||
|
type: competencecreature
|
||||||
|
img: systems/foundryvtt-reve-de-dragon/icons/competence_esquive.webp
|
||||||
|
effects: []
|
||||||
|
system:
|
||||||
|
description: ''
|
||||||
|
descriptionmj: ''
|
||||||
|
carac_value: 10
|
||||||
|
niveau: 1
|
||||||
|
default_diffLibre: 0
|
||||||
|
categorie: generale
|
||||||
|
categorie_parade: ''
|
||||||
|
iscombat: false
|
||||||
|
isnaturelle: true
|
||||||
|
ispossession: false
|
||||||
|
dommages: 0
|
||||||
|
mortalite: mortel
|
||||||
|
isparade: false
|
||||||
|
ownership:
|
||||||
|
default: 0
|
||||||
|
folder: null
|
||||||
|
_stats:
|
||||||
|
compendiumSource: null
|
||||||
|
duplicateSource: null
|
||||||
|
coreVersion: '12.331'
|
||||||
|
systemId: foundryvtt-reve-de-dragon
|
||||||
|
systemVersion: 12.0.24
|
||||||
|
createdTime: 1733182927319
|
||||||
|
modifiedTime: 1733182927336
|
||||||
|
lastModifiedBy: Hp9ImM4o9YRTSdfu
|
||||||
|
flags: {}
|
||||||
|
_id: Qrg3ADB9LOhGthaO
|
||||||
|
sort: 1300000
|
||||||
|
_key: '!items!Qrg3ADB9LOhGthaO'
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
name: Esquive sol
|
||||||
|
type: competencecreature
|
||||||
|
img: systems/foundryvtt-reve-de-dragon/icons/competence_esquive.webp
|
||||||
|
effects: []
|
||||||
|
system:
|
||||||
|
description: ''
|
||||||
|
descriptionmj: ''
|
||||||
|
carac_value: 10
|
||||||
|
niveau: 1
|
||||||
|
default_diffLibre: 0
|
||||||
|
categorie: generale
|
||||||
|
categorie_parade: ''
|
||||||
|
iscombat: false
|
||||||
|
isnaturelle: true
|
||||||
|
ispossession: false
|
||||||
|
dommages: 0
|
||||||
|
mortalite: mortel
|
||||||
|
isparade: false
|
||||||
|
ownership:
|
||||||
|
default: 0
|
||||||
|
folder: null
|
||||||
|
_stats:
|
||||||
|
compendiumSource: null
|
||||||
|
duplicateSource: null
|
||||||
|
coreVersion: '12.331'
|
||||||
|
systemId: foundryvtt-reve-de-dragon
|
||||||
|
systemVersion: 12.0.24
|
||||||
|
createdTime: 1733182930109
|
||||||
|
modifiedTime: 1733182930109
|
||||||
|
lastModifiedBy: Hp9ImM4o9YRTSdfu
|
||||||
|
flags: {}
|
||||||
|
_id: siPvW3hAMCJbrYvq
|
||||||
|
sort: 0
|
||||||
|
_key: '!items!siPvW3hAMCJbrYvq'
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
name: Esquive vol
|
||||||
|
type: competencecreature
|
||||||
|
img: systems/foundryvtt-reve-de-dragon/icons/competence_esquive.webp
|
||||||
|
effects: []
|
||||||
|
system:
|
||||||
|
description: ''
|
||||||
|
descriptionmj: ''
|
||||||
|
carac_value: 10
|
||||||
|
niveau: 1
|
||||||
|
default_diffLibre: 0
|
||||||
|
categorie: generale
|
||||||
|
categorie_parade: ''
|
||||||
|
iscombat: false
|
||||||
|
isnaturelle: true
|
||||||
|
ispossession: false
|
||||||
|
dommages: 0
|
||||||
|
mortalite: mortel
|
||||||
|
isparade: false
|
||||||
|
ownership:
|
||||||
|
default: 0
|
||||||
|
folder: null
|
||||||
|
_stats:
|
||||||
|
compendiumSource: null
|
||||||
|
duplicateSource: null
|
||||||
|
coreVersion: '12.331'
|
||||||
|
systemId: foundryvtt-reve-de-dragon
|
||||||
|
systemVersion: 12.0.24
|
||||||
|
createdTime: 1733182932133
|
||||||
|
modifiedTime: 1733182932133
|
||||||
|
lastModifiedBy: Hp9ImM4o9YRTSdfu
|
||||||
|
flags: {}
|
||||||
|
_id: 3ScoGTkQ0VBOpaX6
|
||||||
|
sort: 0
|
||||||
|
_key: '!items!3ScoGTkQ0VBOpaX6'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user