Compare commits

...

3 Commits

Author SHA1 Message Date
LeRatierBretonnien b55754941e Enhance stats 2024-02-08 12:58:44 +01:00
LeRatierBretonnien 00bc950479 Fix SWID again 2023-10-25 21:27:00 +02:00
LeRatierBretonnien b5ec752537 Auto-patch skill and swid 2023-10-20 22:25:59 +02:00
16 changed files with 216 additions and 46 deletions

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

View File

@ -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": [
{

View File

@ -3,7 +3,7 @@
"mapping": {
"description": "system.description",
"skill": {
"path": "system.actions.skill",
"path": "system.actions.trait",
"converter": "gear_skill"
},
"range": {

10
fr.json
View File

@ -88,5 +88,13 @@
"Personal Defense" : "Autodéfense",
"Surveillance": "Surveillance",
"Transportation": "Transport",
"Weapon": "Arme"
"Weapon": "Arme",
"vigor": "Vigueur",
"smarts": "Intelligence",
"agility": "Agilité",
"spirit": "Âme",
"strength": "Force",
"Health": "Santé",
"SWADE.Wounds": "Blessures"
}

View File

@ -57,7 +57,7 @@
}
],
"url": "https://www.uberwald.me/gitea/public/swade-fr-content",
"version": "3.0.2",
"version": "3.2.2",
"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.2.zip",
"id": "swade-fr-content",
"relationships": {
"requires": [
{
"id": "babele",
"type": "module",
"manifest": "https://gitlab.com/riccisi/foundryvtt-babele/raw/master/module/module.json",
"compatibility": {}
}
],

View File

@ -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 quils ne le veuillent.Les caractéristiques disponibles ici sappliquent aux oiseaux - tonnerre plus jeunes qui volent haut dans le ciel des Terres de chasse.Ces créatures ont lapparence dimmenses et puissants oiseaux de proie, dune majesté absolument terrifiante.Et pourtant, ces gardiens sont lunique 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 dun 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 dInitiative. 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 quil 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 d1 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é dattaque 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);
}
}

View File

@ -1,4 +1,5 @@
import { SWDELUXStatBlockParser } from "./sw-statblock-parser.js";
import { ClassCounter} from "https://www.uberwald.me/fvtt_appcount/count-class-ready.js"
const __compFolderTranslation = [
{
compendium: 'swade-core-rules.swade-vehicles',
@ -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 == "combat") return "fighting";
if (swid == "tir") return "shooting";
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: `<p>Stab block: <textarea id="swdelux-stat-parser" rows="20" cols="40"></textarea></p>`,
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);
@ -311,30 +341,6 @@ const subFolder = {
'Vehicular Weapons': 'Armes de véhicules'
}
/************************************************************************************/
// Register world usage statistics
function registerUsageCount(registerKey) {
if (game.user.isGM) {
game.settings.register(registerKey, "world-key", {
name: "Unique world key",
scope: "world",
config: false,
default: "",
type: String
});
let worldKey = game.settings.get(registerKey, "world-key")
if (worldKey == undefined || worldKey == "") {
worldKey = randomID(32)
game.settings.set(registerKey, "world-key", worldKey)
}
// Simple API counter
let regURL = `https://www.uberwald.me/fvtt_appcount/count.php?name="${registerKey}"&worldKey="${worldKey}"&version="${game.release.generation}.${game.release.build}"&system="${game.system.id}"&systemversion="${game.system.version}"`
$.ajax(regURL)
/* -------------------------------------------- */
}
}
/************************************************************************************/
async function loadSecretCompendiums() {
@ -364,8 +370,41 @@ async function loadSecretCompendiums() {
}
}
/************************************************************************************/
function processItemSwid(actor, item) {
let swid = item.system.swid
let newSwid = swid
if (swid == "combat") newSwid = "fighting";
if (swid == "tir") newSwid = "shooting";
if (swid != newSwid) {
console.log("Item to update", item.name, swid, newSwid)
actor.updateEmbeddedDocuments('Item', [{ _id: item.id, "system.swid": newSwid }])
}
}
/************************************************************************************/
function migrateSwid() {
for (let actor of game.actors) {
for (let item of actor.items) {
if (item.system?.swid) {
processItemSwid(actor, item)
}
}
}
}
/************************************************************************************/
function checkSwidSettings() {
let swidFighting = game.settings.get('swade', 'parryBaseSwid')
if (swidFighting != "fighting") {
ui.notifications.warn("Votre compétence de parade n'est pas positionnée à 'fighting' dans les paramètres de SWADE. Nous vous recommandons de le faire pour éviter des problèmes de calcul de la parade.")
//game.settings.set('swade', 'parryBaseSwid', "fighting")
}
}
/************************************************************************************/
Hooks.once('ready', () => {
console.log(">>>>>>>>>>")
// add hidden register
game.settings.register("world", "swade-fr-content-hidden-compendiums", {
@ -376,15 +415,19 @@ Hooks.once('ready', () => {
type: Boolean,
onChange: lang => window.location.reload()
})
SwadeFRContentCommands.init()
migrateSwid()
checkSwidSettings()
//FolderTranslate.translateFolders()
registerUsageCount("swade-fr-content")
ClassCounter.registerUsageCount("swade-fr-content")
});
/************************************************************************************/
Hooks.once('babele.ready', () => {
console.log(">>>>>>>>>> BABELE READY")
loadSecretCompendiums()
});
console.log(">>>>>>>>>>>> LOADED")