diff --git a/module/apps/rdd-import-stats.js b/module/apps/rdd-import-stats.js index 8f65c7ee..f199bbaa 100644 --- a/module/apps/rdd-import-stats.js +++ b/module/apps/rdd-import-stats.js @@ -2,6 +2,8 @@ import "./xregexp-all.js"; import { SystemCompendiums } from "../settings/system-compendiums.js"; import { RdDBaseActorReve } from "../actor/base-actor-reve.js"; +import { Grammar } from "../grammar.js"; +import { Misc } from "../misc.js"; /************************************************************************************/ // Some internal test strings @@ -76,7 +78,7 @@ Corps à corps (0) Esquive +8 -Escalade +4 / Saut +5 / Commerce +3 / Équitation +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 @@ -196,11 +198,14 @@ Vigilance +3 ` // Skill parser depending on the type of actor -const compParser = { personnage: "\\s+(?[\\+\\-]?\\d+)", creature: "\\s+(?\\d+)\\s+(?[\\+\\-]?\\d+)\\s?(?\\d+)?\\s+?(?\\+\\d+)?" }; +const compParser = { + personnage: "(\\D+)*\\s+(?[\\+\\-]?\\d+)", + creature: "\\s+(?\\d+)\\s+(?[\\+\\-]?\\d+)\\s?(?\\d+)?\\s+?(?\\+\\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", @@ -226,7 +231,7 @@ export class RdDStatBlockParser { }); dialog.render(true); } - + static fixWeirdPDF(statString) { // Split the statString into lines let lines = statString.split("\n"); @@ -239,16 +244,16 @@ export class RdDStatBlockParser { lines[i] = lines[i].trim(); // Is it text ? if (lines[i].match(/^[a-zA-Zéêè\s]+/)) { - if ( nextType == "string" ) { + if (nextType == "string") { newLines[index] = lines[i]; - nextType = "number"; + 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" ) { + if (nextType == "number") { newLines[index] = newLines[index] + lines[i]; nextType = "string"; index++; @@ -258,8 +263,8 @@ export class RdDStatBlockParser { } } - } - + } + static async parseStatBlock(statString, type = "npc") { //statString = statBlock03; @@ -278,17 +283,18 @@ export class RdDStatBlockParser { statString = statString.trim(); let actorType = "personnage"; - let perception = XRegExp.exec(statString.toLowerCase(), XRegExp("perception\\s+(?\\d+)", 'gi')); - if (perception?.value ) { + // TODO: check for entite + let perception = XRegExp.exec(statString, XRegExp("perception\\s+(?\\d+)", 'giu')) + if (perception?.value) { actorType = "creature"; } // 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) { let caracDef = game.model.Actor.personnage.carac[key]; // Parse the stat string for each caracteristic - let carac = XRegExp.exec(statString.toLowerCase(), XRegExp(caracDef.label.toLowerCase()+"\\s+(?\\d+)", 'gi')); + let carac = XRegExp.exec(statString, XRegExp(caracDef.label + "\\s+(?\\d+)", 'giu')); if (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 (actorType == "creature") { - let plusDom = XRegExp.exec(statString.toLowerCase(), XRegExp("\\+dom\\s+(?\\+\\d+)", 'gi')); + let plusDom = XRegExp.exec(statString, XRegExp("\\+dom\\s+(?\\+\\d+)", 'giu')); if (plusDom?.values) { actorData.attributs.plusdom.value = Number(plusDom.value); } - let protection = XRegExp.exec(statString.toLowerCase(), XRegExp("protection\\s+(?\\d+)", 'gi')); + let protection = XRegExp.exec(statString, XRegExp("protection\\s+(?\\d+)", 'giu')); if (protection?.value) { actorData.attributs.protection.value = Number(protection.value); } - let endurance = XRegExp.exec(statString.toLowerCase(), XRegExp("endurance\\s+(?\\d+)", 'gi')); + let endurance = XRegExp.exec(statString, XRegExp("endurance\\s+(?\\d+)", 'giu')); if (endurance?.value) { actorData.sante.endurance.value = Number(endurance.value); actorData.sante.endurance.max = Number(endurance.value); } - let vie = XRegExp.exec(statString.toLowerCase(), XRegExp("vie\\s+(?\\d+)", 'gi')); + let vie = XRegExp.exec(statString, XRegExp("vie\\s+(?\\d+)", 'giu')); if (vie.value) { actorData.sante.vie.value = Number(vie.value); actorData.sante.vie.max = Number(vie.value); } - let vitesse = XRegExp.exec(statString.toLowerCase(), XRegExp("vitesse\\s+(?[\\d\\/]+)", 'gi')); + let vitesse = XRegExp.exec(statString, XRegExp("vitesse\\s+(?[\\d\\/]+)", 'giu')); if (vitesse?.value) { actorData.attributs.vitesse.value = vitesse.value; } @@ -326,7 +332,8 @@ export class RdDStatBlockParser { //console.log("Competences : ", competences); let allComp = competences.map(i => i.toObject()) 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) { comp.system.niveau = Number(skill.value); if (actorType == "creature") { @@ -335,9 +342,11 @@ export class RdDStatBlockParser { comp.system.dommages = Number(skill.dommages); comp.system.iscombat = true; } - items.push(comp); // Only selective push } } + if (actorType == "creature" && skill.init) { + items.push(comp); // Only selective push + } if (actorType == "personnage") { items.push(comp); // Always push } @@ -346,27 +355,28 @@ export class RdDStatBlockParser { // 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.toLowerCase(), XRegExp(w.name.toLowerCase()+"\\s+(?\\+\\d+)", 'gi')); + let weapon = XRegExp.exec(statString, XRegExp(w.name + "\\s+(?\\+\\d+)", 'giu')); if (weapon) { - w.system.equipe = true + w.system.equipe = 'true' items.push(w.toObject()); // now process the skill - if ( w.system?.competence != "") { - let wComp = items.find(i => i.name.toLowerCase() == w.system.competence.toLowerCase()); + 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 => i.name.toLowerCase() == w.system.tir.toLowerCase()); + 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 => i.name.toLowerCase() == w.system.lancer.toLowerCase()); + if (w.system?.lancer != "") { + let wComp = items.find(i => Grammar.equalsInsensitive(i.name, w.system.lancer)) if (wComp) { wComp.system.niveau = Number(weapon.value); } @@ -376,58 +386,71 @@ export class RdDStatBlockParser { // Now process armors const armors = await SystemCompendiums.getWorldOrCompendiumItems("armure", "equipement") - for (let a of armors) { - let armor = XRegExp.exec(statString.toLowerCase(), XRegExp(a.name.toLowerCase(), 'gi')); + for (let a of armors) { + let armor = XRegExp.exec(statString, XRegExp(a.name, 'giu')); if (armor) { a.system.equipe = true items.push(a.toObject()); } } + let feminin = XRegExp.exec(statString, XRegExp("né(?e?) à", 'giu')); + actorData.sexe = (feminin?.value == 'e') ? 'féminin' : 'masculin' + // Get hour name : heure du XXXXX - let heure = XRegExp.exec(statString.toLowerCase(), XRegExp("heure du\\s+(?\\w+)", 'gi')); + let heure = XRegExp.exec(statString, XRegExp("heure (du|de la|des|de l\')\\s*(?[\\p{Letter}\s]+),", 'giu')); if (heure?.value) { - actorData.heure = heure.value; + actorData.heure = heure.value; } // Get age - let age = XRegExp.exec(statString.toLowerCase(), XRegExp("(?\\d+) ans", 'gi')); + let age = XRegExp.exec(statString, XRegExp("(?\\d+) ans", 'giu')); if (age?.value) { - actorData.age = Number(age.value); + actorData.age = Number(age.value); } // Get height - let taille = XRegExp.exec(statString.toLowerCase(), XRegExp("(?\\d+)m\\d+", 'gi')); + let taille = XRegExp.exec(statString, XRegExp("(?\\d+m\\d+)", 'giu')); if (taille?.value) { - actorData.taille = taille.value; + actorData.taille = taille.value; } // Get weight - let poids = XRegExp.exec(statString.toLowerCase(), XRegExp("(?\\d+) kg", 'gi')); + let poids = XRegExp.exec(statString, XRegExp("(?\\d+) kg", 'giu')); if (poids?.value) { - actorData.poids = poids.value; + actorData.poids = poids.value; } // Get beauty - let beaute = XRegExp.exec(statString.toLowerCase(), XRegExp("beauté\\s+(?\\d+)", 'gi')); + let beaute = XRegExp.exec(statString, XRegExp("beauté\\s+(?\\d+)", 'giu')); if (beaute?.value) { - actorData.beaute = Number(beaute.value); + actorData.beaute = Number(beaute.value); } // Name is all string before ', né' - let name - if (actorType == "personnage") { - name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?[\\w\\s\\d]+),", 'gi')); - if (!name?.value) { - name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?.+)\\s+taille", 'gi')); - } - name = name?.value || "Importé"; - } - if (actorType == "creature") { - name = XRegExp.exec(statString.toLowerCase(), XRegExp("(?.+)\\s+taille", 'gi')); - name = name?.value || "Importé"; - } - - let newActor = RdDBaseActorReve.create({name: name || "Importé", type:actorType, system: actorData, items: items}); + let name = RdDStatBlockParser.extractName(actorType, statString); + + let newActor = RdDBaseActorReve.create({ name: name || "Importé", type: actorType, system: actorData, items: items }); // DUmp.... 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("(?[\\p{Letter}\\s\\d]+),", 'giu')); + if (!name?.value) { + name = XRegExp.exec(statString, XRegExp("(?.+)\\s+taille", 'giu')); + } + return Misc.upperFirst(name?.value || "Importé"); + } + + static extractNameCreature(statString) { + const name = XRegExp.exec(statString, XRegExp("(?.+)\\s+taille", 'giu')); + return Misc.upperFirst(name?.value || "Importé"); + } }