Amélioration du parser:

- correction de la taille (ne prennait que avant le 'm'
- ajout du sexe masculin/féminin
- correction heure de naissance (support heure composée, articles)
- support Unicode (nom, HN)
- support groupes de compétences (saut, danse +1)
- ignorer parenthèses dans ccompétences
- armes avec compétences accentuées
This commit is contained in:
Vincent Vandemeulebrouck 2024-11-30 22:31:43 +01:00
parent 37c281b300
commit c481bad81a

View File

@ -2,6 +2,8 @@
import "./xregexp-all.js"; import "./xregexp-all.js";
import { SystemCompendiums } from "../settings/system-compendiums.js"; import { SystemCompendiums } from "../settings/system-compendiums.js";
import { RdDBaseActorReve } from "../actor/base-actor-reve.js"; import { RdDBaseActorReve } from "../actor/base-actor-reve.js";
import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
/************************************************************************************/ /************************************************************************************/
// Some internal test strings // Some internal test strings
@ -76,7 +78,7 @@ Corps à corps
(0) (0)
Esquive Esquive
+8 +8
Escalade +4 / Saut +5 / Commerce +3 / Équitation Escalade, Saut +4 / Commerce +3 / Équitation
+6 / Chirurgie 0 / Survie en extérieur +4 / Survie fo- +6 / Chirurgie 0 / Survie en extérieur +4 / Survie fo-
rêt +6 / Acrobatie -2 / Métallurgie +2 / Natation +3 / rêt +6 / Acrobatie -2 / Métallurgie +2 / Natation +3 /
Légendes -1 / Écriture -4 Légendes -1 / Écriture -4
@ -196,11 +198,14 @@ Vigilance
+3 +3
` `
// Skill parser depending on the type of actor // Skill parser depending on the type of actor
const compParser = { personnage: "\\s+(?<value>[\\+\\-]?\\d+)", creature: "\\s+(?<carac>\\d+)\\s+(?<value>[\\+\\-]?\\d+)\\s?(?<init>\\d+)?\\s+?(?<dommages>\\+\\d+)?" }; 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 // Main class for parsing a stat block
export class RdDStatBlockParser { export class RdDStatBlockParser {
static openInputDialog() { static openInputDialog() {
let dialog = new Dialog({ let dialog = new Dialog({
title: "Import de stats de PNJ/Créatures", title: "Import de stats de PNJ/Créatures",
@ -226,7 +231,7 @@ export class RdDStatBlockParser {
}); });
dialog.render(true); dialog.render(true);
} }
static fixWeirdPDF(statString) { static fixWeirdPDF(statString) {
// Split the statString into lines // Split the statString into lines
let lines = statString.split("\n"); let lines = statString.split("\n");
@ -239,16 +244,16 @@ export class RdDStatBlockParser {
lines[i] = lines[i].trim(); lines[i] = lines[i].trim();
// Is it text ? // Is it text ?
if (lines[i].match(/^[a-zA-Zéêè\s]+/)) { if (lines[i].match(/^[a-zA-Zéêè\s]+/)) {
if ( nextType == "string" ) { if (nextType == "string") {
newLines[index] = lines[i]; newLines[index] = lines[i];
nextType = "number"; nextType = "number";
} else { } else {
console.log("Wrong sequence string detected...", lines[i], nextType); console.log("Wrong sequence string detected...", lines[i], nextType);
} }
} }
// Is it a number ? // Is it a number ?
if (lines[i].match(/^[\d\s]+/)) { if (lines[i].match(/^[\d\s]+/)) {
if ( nextType == "number" ) { if (nextType == "number") {
newLines[index] = newLines[index] + lines[i]; newLines[index] = newLines[index] + lines[i];
nextType = "string"; nextType = "string";
index++; index++;
@ -258,8 +263,8 @@ export class RdDStatBlockParser {
} }
} }
} }
static async parseStatBlock(statString, type = "npc") { static async parseStatBlock(statString, type = "npc") {
//statString = statBlock03; //statString = statBlock03;
@ -278,17 +283,18 @@ export class RdDStatBlockParser {
statString = statString.trim(); statString = statString.trim();
let actorType = "personnage"; let actorType = "personnage";
let perception = XRegExp.exec(statString.toLowerCase(), XRegExp("perception\\s+(?<value>\\d+)", 'gi')); // TODO: check for entite
if (perception?.value ) { let perception = XRegExp.exec(statString, XRegExp("perception\\s+(?<value>\\d+)", 'giu'))
if (perception?.value) {
actorType = "creature"; actorType = "creature";
} }
// Now start carac // Now start carac
let actorData = foundry.utils.deepClone(game.model.Actor[actorType]); let actorData = foundry.utils.deepClone(game.model.Actor[actorType]);
for (let key in game.model.Actor.personnage.carac) { for (let key in game.model.Actor.personnage.carac) {
let caracDef = game.model.Actor.personnage.carac[key]; let caracDef = game.model.Actor.personnage.carac[key];
// Parse the stat string for each caracteristic // Parse the stat string for each caracteristic
let carac = XRegExp.exec(statString.toLowerCase(), XRegExp(caracDef.label.toLowerCase()+"\\s+(?<value>\\d+)", 'gi')); let carac = XRegExp.exec(statString, XRegExp(caracDef.label + "\\s+(?<value>\\d+)", 'giu'));
if (carac?.value) { if (carac?.value) {
actorData.carac[key].value = Number(carac.value); actorData.carac[key].value = Number(carac.value);
} }
@ -296,25 +302,25 @@ export class RdDStatBlockParser {
// If creature we need to setup additionnal fields // If creature we need to setup additionnal fields
if (actorType == "creature") { if (actorType == "creature") {
let plusDom = XRegExp.exec(statString.toLowerCase(), XRegExp("\\+dom\\s+(?<value>\\+\\d+)", 'gi')); let plusDom = XRegExp.exec(statString, XRegExp("\\+dom\\s+(?<value>\\+\\d+)", 'giu'));
if (plusDom?.values) { if (plusDom?.values) {
actorData.attributs.plusdom.value = Number(plusDom.value); actorData.attributs.plusdom.value = Number(plusDom.value);
} }
let protection = XRegExp.exec(statString.toLowerCase(), XRegExp("protection\\s+(?<value>\\d+)", 'gi')); let protection = XRegExp.exec(statString, XRegExp("protection\\s+(?<value>\\d+)", 'giu'));
if (protection?.value) { if (protection?.value) {
actorData.attributs.protection.value = Number(protection.value); actorData.attributs.protection.value = Number(protection.value);
} }
let endurance = XRegExp.exec(statString.toLowerCase(), XRegExp("endurance\\s+(?<value>\\d+)", 'gi')); let endurance = XRegExp.exec(statString, XRegExp("endurance\\s+(?<value>\\d+)", 'giu'));
if (endurance?.value) { if (endurance?.value) {
actorData.sante.endurance.value = Number(endurance.value); actorData.sante.endurance.value = Number(endurance.value);
actorData.sante.endurance.max = Number(endurance.value); actorData.sante.endurance.max = Number(endurance.value);
} }
let vie = XRegExp.exec(statString.toLowerCase(), XRegExp("vie\\s+(?<value>\\d+)", 'gi')); let vie = XRegExp.exec(statString, XRegExp("vie\\s+(?<value>\\d+)", 'giu'));
if (vie.value) { if (vie.value) {
actorData.sante.vie.value = Number(vie.value); actorData.sante.vie.value = Number(vie.value);
actorData.sante.vie.max = Number(vie.value); actorData.sante.vie.max = Number(vie.value);
} }
let vitesse = XRegExp.exec(statString.toLowerCase(), XRegExp("vitesse\\s+(?<value>[\\d\\/]+)", 'gi')); let vitesse = XRegExp.exec(statString, XRegExp("vitesse\\s+(?<value>[\\d\\/]+)", 'giu'));
if (vitesse?.value) { if (vitesse?.value) {
actorData.attributs.vitesse.value = vitesse.value; actorData.attributs.vitesse.value = vitesse.value;
} }
@ -326,7 +332,8 @@ export class RdDStatBlockParser {
//console.log("Competences : ", competences); //console.log("Competences : ", competences);
let allComp = competences.map(i => i.toObject()) let allComp = competences.map(i => i.toObject())
for (let comp of allComp) { for (let comp of allComp) {
let skill = XRegExp.exec(statString.toLowerCase(), XRegExp(comp.name.toLowerCase()+compParser[actorType], 'gi')); const compName = comp.name;
let skill = XRegExp.exec(Grammar.toLowerCaseNoAccent(statString), XRegExp(Grammar.toLowerCaseNoAccent(compName) + compParser[actorType], 'giu'));
if (skill) { if (skill) {
comp.system.niveau = Number(skill.value); comp.system.niveau = Number(skill.value);
if (actorType == "creature") { if (actorType == "creature") {
@ -335,9 +342,11 @@ export class RdDStatBlockParser {
comp.system.dommages = Number(skill.dommages); comp.system.dommages = Number(skill.dommages);
comp.system.iscombat = true; comp.system.iscombat = true;
} }
items.push(comp); // Only selective push
} }
} }
if (actorType == "creature" && skill.init) {
items.push(comp); // Only selective push
}
if (actorType == "personnage") { if (actorType == "personnage") {
items.push(comp); // Always push items.push(comp); // Always push
} }
@ -346,27 +355,28 @@ export class RdDStatBlockParser {
// Now process weapons // Now process weapons
const weapons = await SystemCompendiums.getWorldOrCompendiumItems("arme", "equipement") const weapons = await SystemCompendiums.getWorldOrCompendiumItems("arme", "equipement")
//console.log("Equipement : ", equipment); //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) { for (let w of weapons) {
let weapon = XRegExp.exec(statString, XRegExp(w.name + "\\s+(?<value>\\+\\d+)", 'giu'));
let weapon = XRegExp.exec(statString.toLowerCase(), XRegExp(w.name.toLowerCase()+"\\s+(?<value>\\+\\d+)", 'gi'));
if (weapon) { if (weapon) {
w.system.equipe = true w.system.equipe = 'true'
items.push(w.toObject()); items.push(w.toObject());
// now process the skill // now process the skill
if ( w.system?.competence != "") { if (w.system?.competence != "") {
let wComp = items.find(i => i.name.toLowerCase() == w.system.competence.toLowerCase()); let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.competence))
if (wComp) { if (wComp) {
wComp.system.niveau = Number(weapon.value); wComp.system.niveau = Number(weapon.value);
} }
} }
if ( w.system?.tir != "") { if (w.system?.tir != "") {
let wComp = items.find(i => i.name.toLowerCase() == w.system.tir.toLowerCase()); let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.tir))
if (wComp) { if (wComp) {
wComp.system.niveau = Number(weapon.value); wComp.system.niveau = Number(weapon.value);
} }
} }
if ( w.system?.lancer != "") { if (w.system?.lancer != "") {
let wComp = items.find(i => i.name.toLowerCase() == w.system.lancer.toLowerCase()); let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.lancer))
if (wComp) { if (wComp) {
wComp.system.niveau = Number(weapon.value); wComp.system.niveau = Number(weapon.value);
} }
@ -376,58 +386,71 @@ export class RdDStatBlockParser {
// Now process armors // Now process armors
const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement") const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement")
for (let a of armors) { for (let a of armors) {
let armor = XRegExp.exec(statString.toLowerCase(), XRegExp(a.name.toLowerCase(), 'gi')); let armor = XRegExp.exec(statString, XRegExp(a.name, 'giu'));
if (armor) { if (armor) {
a.system.equipe = true a.system.equipe = true
items.push(a.toObject()); items.push(a.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 // Get hour name : heure du XXXXX
let heure = XRegExp.exec(statString.toLowerCase(), XRegExp("heure du\\s+(?<value>\\w+)", 'gi')); let heure = XRegExp.exec(statString, XRegExp("heure (du|de la|des|de l\')\\s*(?<value>[\\p{Letter}\s]+),", 'giu'));
if (heure?.value) { if (heure?.value) {
actorData.heure = heure.value; actorData.heure = heure.value;
} }
// Get age // Get age
let age = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>\\d+) ans", 'gi')); let age = XRegExp.exec(statString, XRegExp("(?<value>\\d+) ans", 'giu'));
if (age?.value) { if (age?.value) {
actorData.age = Number(age.value); actorData.age = Number(age.value);
} }
// Get height // Get height
let taille = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>\\d+)m\\d+", 'gi')); let taille = XRegExp.exec(statString, XRegExp("(?<value>\\d+m\\d+)", 'giu'));
if (taille?.value) { if (taille?.value) {
actorData.taille = taille.value; actorData.taille = taille.value;
} }
// Get weight // Get weight
let poids = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>\\d+) kg", 'gi')); let poids = XRegExp.exec(statString, XRegExp("(?<value>\\d+) kg", 'giu'));
if (poids?.value) { if (poids?.value) {
actorData.poids = poids.value; actorData.poids = poids.value;
} }
// Get beauty // Get beauty
let beaute = XRegExp.exec(statString.toLowerCase(), XRegExp("beauté\\s+(?<value>\\d+)", 'gi')); let beaute = XRegExp.exec(statString, XRegExp("beauté\\s+(?<value>\\d+)", 'giu'));
if (beaute?.value) { if (beaute?.value) {
actorData.beaute = Number(beaute.value); actorData.beaute = Number(beaute.value);
} }
// Name is all string before ', né' // Name is all string before ', né'
let name let name = RdDStatBlockParser.extractName(actorType, statString);
if (actorType == "personnage") {
name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>[\\w\\s\\d]+),", 'gi')); let newActor = RdDBaseActorReve.create({ name: name || "Importé", type: actorType, system: actorData, items: items });
if (!name?.value) {
name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>.+)\\s+taille", 'gi'));
}
name = name?.value || "Importé";
}
if (actorType == "creature") {
name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?<value>.+)\\s+taille", 'gi'));
name = name?.value || "Importé";
}
let newActor = RdDBaseActorReve.create({name: name || "Importé", type:actorType, system: actorData, items: items});
// DUmp.... // DUmp....
console.log(actorData); console.log(actorData);
} }
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é");
}
} }