diff --git a/compendiums/deadlands-core-rules.deadlands-armor.json b/compendiums/deadlands-core-rules.deadlands-armor.json index 3724648..edc5a46 100644 --- a/compendiums/deadlands-core-rules.deadlands-armor.json +++ b/compendiums/deadlands-core-rules.deadlands-armor.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/deadlands-core-rules.deadlands-equipment.json b/compendiums/deadlands-core-rules.deadlands-equipment.json index a33056d..19dd1c1 100644 --- a/compendiums/deadlands-core-rules.deadlands-equipment.json +++ b/compendiums/deadlands-core-rules.deadlands-equipment.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/deadlands-core-rules.deadlands-infernal-devices.json b/compendiums/deadlands-core-rules.deadlands-infernal-devices.json index 18bd79e..94ecaa6 100644 --- a/compendiums/deadlands-core-rules.deadlands-infernal-devices.json +++ b/compendiums/deadlands-core-rules.deadlands-infernal-devices.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/deadlands-core-rules.deadlands-melee-weapons.json b/compendiums/deadlands-core-rules.deadlands-melee-weapons.json index b911455..e63972a 100644 --- a/compendiums/deadlands-core-rules.deadlands-melee-weapons.json +++ b/compendiums/deadlands-core-rules.deadlands-melee-weapons.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/deadlands-core-rules.deadlands-ranged-weapons.json b/compendiums/deadlands-core-rules.deadlands-ranged-weapons.json index 9546319..3124f95 100644 --- a/compendiums/deadlands-core-rules.deadlands-ranged-weapons.json +++ b/compendiums/deadlands-core-rules.deadlands-ranged-weapons.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/deadlands-core-rules.deadlands-relics.json b/compendiums/deadlands-core-rules.deadlands-relics.json index 37597de..369d51f 100644 --- a/compendiums/deadlands-core-rules.deadlands-relics.json +++ b/compendiums/deadlands-core-rules.deadlands-relics.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/swade-core-rules.swade-armor.json b/compendiums/swade-core-rules.swade-armor.json index 41e9fc6..ba75a50 100644 --- a/compendiums/swade-core-rules.swade-armor.json +++ b/compendiums/swade-core-rules.swade-armor.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/swade-core-rules.swade-equipment.json b/compendiums/swade-core-rules.swade-equipment.json index e9a7e5d..2c40e5c 100644 --- a/compendiums/swade-core-rules.swade-equipment.json +++ b/compendiums/swade-core-rules.swade-equipment.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/swade-core-rules.swade-modern-firearms.json b/compendiums/swade-core-rules.swade-modern-firearms.json index a5c64b9..e23809f 100644 --- a/compendiums/swade-core-rules.swade-modern-firearms.json +++ b/compendiums/swade-core-rules.swade-modern-firearms.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/swade-core-rules.swade-personal-weapons.json b/compendiums/swade-core-rules.swade-personal-weapons.json index c266b64..3e2eb1e 100644 --- a/compendiums/swade-core-rules.swade-personal-weapons.json +++ b/compendiums/swade-core-rules.swade-personal-weapons.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/compendiums/swade-core-rules.swade-skills.json b/compendiums/swade-core-rules.swade-skills.json index ff2d789..1d13b56 100644 --- a/compendiums/swade-core-rules.swade-skills.json +++ b/compendiums/swade-core-rules.swade-skills.json @@ -1,7 +1,11 @@ { "label": "SWADE Compétences", "mapping": { - "description_full": "system.description" + "description_full": "system.description", + "swid": { + "path": "system.swid", + "converter": "skill_swid" + } }, "entries": [ { diff --git a/compendiums/swade-core-rules.swade-special-weapons.json b/compendiums/swade-core-rules.swade-special-weapons.json index c13f357..2f8ab62 100644 --- a/compendiums/swade-core-rules.swade-special-weapons.json +++ b/compendiums/swade-core-rules.swade-special-weapons.json @@ -3,7 +3,7 @@ "mapping": { "description": "system.description", "skill": { - "path": "system.actions.skill", + "path": "system.actions.trait", "converter": "gear_skill" }, "range": { diff --git a/module.json b/module.json index 66884f0..8c0d817 100644 --- a/module.json +++ b/module.json @@ -57,7 +57,7 @@ } ], "url": "https://www.uberwald.me/gitea/public/swade-fr-content", - "version": "3.0.2", + "version": "3.2.0", "esmodules": [ "modules/swade-fr-init.js" ], @@ -73,14 +73,13 @@ } ], "manifest": "https://www.uberwald.me/gitea/public/swade-fr-content/raw/branch/master/module.json", - "download": "https://www.uberwald.me/gitea/public/swade-fr-content/archive/swade-fr-content-3.0.2.zip", + "download": "https://www.uberwald.me/gitea/public/swade-fr-content/archive/swade-fr-content-3.2.0.zip", "id": "swade-fr-content", "relationships": { "requires": [ { "id": "babele", "type": "module", - "manifest": "https://gitlab.com/riccisi/foundryvtt-babele/raw/master/module/module.json", "compatibility": {} } ], diff --git a/modules/sw-statblock-parser.js b/modules/sw-statblock-parser.js new file mode 100644 index 0000000..3a54be1 --- /dev/null +++ b/modules/sw-statblock-parser.js @@ -0,0 +1,116 @@ +const actorText = "OISEAU-TONNERRE\nCe sont les gardiens des Terres de chasse.Ils sont pra - tiquement aussi omniscients que tu pourrais l’être sans être le Créateur lui - même.Quatre grands oiseaux - ton - nerre anciens habitent quelque part dans le monde des esprits, mais on ne peut jamais les rencontrer, à moins qu’ils ne le veuillent.Les caractéristiques disponibles ici s’appliquent aux oiseaux - tonnerre plus jeunes qui volent haut dans le ciel des Terres de chasse.Ces créatures ont l’apparence d’immenses et puissants oiseaux de proie, d’une majesté absolument terrifiante.Et pourtant, ces gardiens sont l’unique raison qui a empêché les mani - tous de prendre le contrôle total du monde des esprits.Les oiseaux - tonnerre ont tendance à être lunatiques et imprévisibles.\nAllure: 8(dé de course d10) Charisme: 0 Trempe: 2\n" + + "TRAITS\n" + + "Agi Âme For Int Vig d10 d8 d10 d6 d8 Compétences : Combat d10, Discrétion d8, Équi- tation d10, Intimidation d8, Pistage d8, Survie d6, Tir d10, Tripes d8. Handicaps : Serment des Anciennes Traditions (Majeur), Têtu. Atouts : Blocage, Esprit totem (variable), Véloce.\nDÉFENSE Parade Résistance 8 7 CAPACITÉS SPÉCIALES Arcane : Chamanisme, Médecine tribale d10, PP 25. Don (étoile filante) : au début d’un combat, peut dépen- ser 1 Jeton pour augmenter la portée de ses Atouts de Commandement jusqu’à la fin du combat. Blanc : +Âme ; rouge : +Âme × 2 ; bleu : +Âme × 3. Esprit totem (chouette) : le coût en PP du pouvoir Ter- reur est divisé par 2 si Relance de Médecine tribale. Esquive : inflige -1 aux attaques à distance. Inspiration : +2 pour annuler Secoué pour les alliés dans un rayon de 5 cases. Leader naturel : peut donner des Jetons aux alliés dans un rayon de 5 cases. Tête froide : agit sur la meilleure de deux cartes d’Initiative. Volonté de fer : Intimidation +2, Sarcasme +2, y résister +2.\nACTIONS\nArmure : Médecine tribale d10, PP 2, T, Durée 3 (1/rd), +2 en Armure, -4 avec une Relance. Arc : Tir d10, 2d6, 12/24/48, CdT 1, Munitions 1, 20 flèches. Augmentation/Diminution de Trait : Médecine tri- bale d10, PP 2, Int, Durée 3 (1/rd), +/-1 au type de dé dans un Trait (+/-2 en cas de Relance). Bâton de coup de Crazy Horse : voir p. 66. Pioche un Je- ton la première fois qu’il touche un adversaire dange- reux, Marque le coup (nécessite Attaque pour toucher, que sur adversaire armé, conscient et dangereux, une seule fois, sans dégâts). Couteau : Combat d12, d10+d4, PA 1. Déflexion : Médecine tribale d10, PP 2, T, Durée 3 (1/rd), -2 pour toucher le sujet, -4 avec une Relance. Frappe : Médecine tribale d10, PP 2, T, Durée 3 (1 / Round), +2 de dégâts sur une arme, +4 avec Relance. Frénésie suprême : peut faire une attaque en Combat supplémentaire. Guérison : Médecine tribale d10, PP 3, T, Durée Inst., soigne une Blessure vieille de moins d’1 h. Masse de guerre à lame : Combat d12, d10+d8, PA 2, Parade -1, Maniement à deux mains. Poigne ferme : ignore la pénalité de Plateforme instable. Terreur : Médecine tribale d10, PP 2, Int × 2, Durée Inst., jet de Tripes pour cibles dans GG (-2 si Relance), Ex- tras paniqués, Jokers tirent sur la Table de Terreur. Tireur d’élite : Tir +2 si pas de mouvement (Visée).Visée : Médecine tribale d10, PP 1, T, Durée 3 (1/rd), pé- nalité d’attaque ciblée réduite de 1 (2 sur Relance).\nÉQUIPEMENT Sac médecine, cheval."; + + +const __attrMapping = {agility: 0, spirit: 1, smarts: 3, vigor:4, strength: 2 } + +export class SWDELUXStatBlockParser { + + constructor() { + + this.parsingTable = { + attributs: { regex: /TRAITS\s*(.*?)\s*Comp/, value: "", attributs: {}, elements: [] }, + competences: { regex: /Compétences\s*:\s*(.*?).\s+Handicaps/, value: "", elements: [] }, + atouts: { regex: /Atouts\s*:\s*(.*?).\s*DÉFENSE/, value: "", elements: [] }, + handicaps: { regex: /Handicaps\s*:\s*(.*?).\s+Atouts/, value: "", elements: [] }, + defense: { regex: /nDÉFENSE\s*Parade\s*Résistance\s+(\d+)\s+(\d+)/, parade: 0, resistance: 0 }, + capacites: { regex: /CAPACITÉS SPÉCIALES\s*(.*?)\s*ACTIONS|ÉQUIPEMENT|$/, value: "", elements: [] }, + actions: { regex: /ACTIONS\s*(.*?)\s*ÉQUIPEMENT|$/, value: "", elements: [] }, + equipement: { regex: /ÉQUIPEMENT\s*(.*)$/, value: "", elements: [] } + } + console.log("MODEL", this.model) + } + + async parse(actorText) { + let parsingTable = this.parsingTable; // Shortcut + + // Cleanup phase and format stat block + let splitted = actorText.split("Allure"); + let charDesc = splitted[0] + let statBlock = "Allure : " + splitted[1] + + let charName = charDesc.split("\n")[0] + statBlock = statBlock.replace(/\n/g, ' '); + statBlock = statBlock.replace(/- /g, ''); + // Add missing section headers, needed to easily parse the stat block + if (!statBlock.includes('CAPACITÉS SPÉCIALES')) { + statBlock += "\nCAPACITÉS SPÉCIALES : "; + } + if (!statBlock.includes('ACTIONS')) { + statBlock += "\nACTIONS : "; + } + if (!statBlock.includes('ÉQUIPEMENT')) { + statBlock += "\nÉQUIPEMENT : "; + } + + + for (let key in parsingTable) { + let parseData = parsingTable[key] + parseData.key = key; + let match = parseData.regex.exec(statBlock) + //console.log("MATCH", key, match); + if (match && match[1]) { + if (key == "attributs") { + const diceSide = match[1].match(/\d+/g); + parseData.traitMatch = diceSide + console.log("TRAITMATCH", parseData.traitMatch); + } + if (parseData.value == "") { + parseData.value = match[1] + } + if (key == "competences") { + // Utiliser une expression régulière pour extraire chaque compétence et sa valeur de dé + const regex = /([\w\séèàÉ'()-]+)\s+(d\d+)\s*,\s*/g; + let skillDef; + while ((skillDef = regex.exec(match[1])) !== null) { + const skill = skillDef[1].trim(); + const value = skillDef[2]; + parseData.elements.push({ name: skill, dice: value }); + } + console.log("SKILLS", parseData.skills); + } + if (key == "atouts" || key == "handicaps") { + const regex = /([\w\séèàÉ'()-]+)\s*,\s*/g; + let elemDef; + while ((elemDef = regex.exec(match[1])) !== null) { + const name = elemDef[1].trim(); + parseData.elements.push({ name }); + } + } + if (key == "capacites" || key == "actions") { + const regex = /([\w\séèàÉ'()-]+)\s*:\s*([\w\séèàÉ\d,'()-]+)\s*./g; + let elemDef; + while ((elemDef = regex.exec(match[1])) !== null) { + const name = elemDef[1].trim(); + const text = elemDef[2].trim(); + parseData.elements.push({ name, text }); + } + } + if (key == "equipement") { + const regex = /([\w\séèàÉ$\d'()-]+)\s*[,|.]\s*/g; + let elemDef; + while ((elemDef = regex.exec(match[1])) !== null) { + const name = elemDef[1].trim(); + parseData.elements.push({ name }); + } + } + if (key == "defense") { + parseData.parade = match[1]; + parseData.resistance = match[2]; + } + console.log("PARSED", parseData); + } + } + + let actor = await Actor.create({name: charName || "PNJ importé", type: "npc", items: []}) + let updates = {} + for (let key in __attrMapping) { + let index = __attrMapping[key] + updates[`system.attributes.${key}.die.sides`] = Number(this.parsingTable["attributs"].traitMatch[index]) + } + actor.update( updates ) + console.log("ACTOR", actor, updates); + } +} + diff --git a/modules/swade-fr-init.js b/modules/swade-fr-init.js index 591777b..eebff7d 100644 --- a/modules/swade-fr-init.js +++ b/modules/swade-fr-init.js @@ -1,3 +1,4 @@ +import { SWDELUXStatBlockParser } from "./sw-statblock-parser.js"; const __compFolderTranslation = [ { @@ -68,6 +69,7 @@ class swadeFrTranslator { /************************************************************************************/ Hooks.once('init', () => { + console.log("INIT !!!!!!") if (typeof Babele !== 'undefined') { console.log("BABELE LOADED !!!"); @@ -80,12 +82,18 @@ Hooks.once('init', () => { Babele.get().registerConverters({ "gear_skill": (skill) => { let transl = skill + //console.log("Testing", skill) if (skill) { let translated = game.babele.translate("swade-core-rules.swade-skills", { name: skill.trim() }, true) transl = translated.name || skill.trim() } return transl }, + "skill_swid": (swid) => { + if (swid == "fighting") return "combat"; + if (swid == "shooting") return "tir"; + return swid + }, "category_converter": (category) => { return game.i18n.localize(category) }, @@ -183,14 +191,17 @@ Hooks.once('init', () => { export class SwadeFRContentCommands { static init() { + if (!game.swadeFRContent) { const swadeFRCommands = new SwadeFRContentCommands() swadeFRCommands.registerCommand({ path: ["/montremonsecret"], func: (content, msg, params) => SwadeFRContentCommands.enableSecret(), descr: "Affiche les compendiums secrets" }); swadeFRCommands.registerCommand({ path: ["/cachemonsecret"], func: (content, msg, params) => SwadeFRContentCommands.disableSecret(), descr: "Cache les compendiums secrets" }); + swadeFRCommands.registerCommand({ path: ["/statparser"], func: (content, msg, params) => SwadeFRContentCommands.statParser(), descr: "Parse SW Delux stat block" }); game.swadeFRContent = { commands: swadeFRCommands } } Hooks.on("chatMessage", (html, content, msg) => { + console.log("CHAT PARSER!!!") if (content[0] == '/') { let regExp = /(\S+)/g; let commands = content.match(regExp); @@ -268,13 +279,32 @@ export class SwadeFRContentCommands { static disableSecret() { game.settings.set("world", "swade-fr-content-hidden-compendiums", false) } + /* -------------------------------------------- */ + static statParser() { + let dialogue = new Dialog({ + title: `Import`, + content: `
Stab block:
`, + buttons: { + one: { + icon: '', + label: 'Parser !', + callback: (html) => { + let text = html.find('#swdelux-stat-parser').val(); + let parser = new SWDELUXStatBlockParser(text); + parser.parse(text); + } + } + } + }) + dialogue.render(true) + } /* -------------------------------------------- */ _processCommand(commandsTable, name, params, content = '', msg = {}, path = "") { console.log("===> Processing command") let command = commandsTable[name]; path = path + name + " "; - if (command && command.subTable) { + if (command?.subTable) { if (params[0]) { return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path) } @@ -283,7 +313,7 @@ export class SwadeFRContentCommands { return true; } } - if (command && command.func) { + if (command?.func) { const result = command.func(content, msg, params); if (result == false) { BoLCommands._chatAnswer(msg, command.descr); @@ -366,6 +396,7 @@ async function loadSecretCompendiums() { /************************************************************************************/ Hooks.once('ready', () => { + console.log(">>>>>>>>>>") // add hidden register game.settings.register("world", "swade-fr-content-hidden-compendiums", { @@ -376,7 +407,7 @@ Hooks.once('ready', () => { type: Boolean, onChange: lang => window.location.reload() }) - + SwadeFRContentCommands.init() //FolderTranslate.translateFolders() @@ -386,5 +417,8 @@ Hooks.once('ready', () => { /************************************************************************************/ Hooks.once('babele.ready', () => { + console.log(">>>>>>>>>> BABELE READY") loadSecretCompendiums() }); + +console.log(">>>>>>>>>>>> LOADED")