class ActorWfrp4e_fr extends ActorWfrp4e { /** * Calculates a weapon's range or damage formula. * * Takes a weapon formula for Damage or Range (SB + 4 or SBx3) and converts to a numeric value. * * @param {String} formula formula to be processed (SBx3 => 9). * * @return {Number} Numeric formula evaluation */ calculateRangeOrDamage(formula) { //console.log("FR function calculateRangeOrDamage !", formula); let actorData = this.data try { formula = formula.toLowerCase(); // Iterate through characteristics for(let ch in actorData.data.characteristics) { // Determine if the formula includes the characteristic's abbreviation + B (SB, WPB, etc.) if (formula.includes(ch.concat('b'))) { // Replace that abbreviation with the Bonus value formula = formula.replace(ch.concat('b'), actorData.data.characteristics[ch].bonus.toString()); } } if (formula.includes("yard") ) formula = formula.replace('yard', "mètre" ); if (formula.includes("yds") ) formula = formula.replace('yds', "m." ); // To evaluate multiplication, replace x with * formula = formula.replace('x', '*'); return eval(formula); } catch { return formula } } /** * Turns a formula into a processed string for display * * Processes damage formula based - same as calculateSpellAttributes, but with additional * consideration to whether its a magic missile or not * * @param {String} formula Formula to process - "Willpower Bonus + 4" * @param {boolean} isMagicMissile Whether or not it's a magic missile - used in calculating additional damage * @returns {String} Processed formula */ calculateSpellDamage(formula, isMagicMissile) { let actorData = this.data formula = formula.toLowerCase(); if (isMagicMissile) // If it's a magic missile, damage includes willpower bonus { formula += "+ " + actorData.data.characteristics["wp"].bonus } // Specific case, to avoid wrong matching with "Force" if (formula.includes("force mentale")) { // Determine if it's looking for the bonus or the value if (formula.includes('bonus')) formula = formula.replace( "bonus de force mentale", ); else formula = formula.replace("force mentale", actorData.data.characteristics["wp"].value); } // Iterate through characteristics for(let ch in actorData.data.characteristics) { // If formula includes characteristic name while (formula.includes(actorData.data.characteristics[ch].label.toLowerCase())) { // Determine if it's looking for the bonus or the value if (formula.includes('bonus')) formula = formula.replace("bonus de " + WFRP4E.characteristics[ch].toLowerCase(), actorData.data.characteristics[ch].bonus); else formula = formula.replace(WFRP4E.characteristics[ch].toLowerCase(), actorData.data.characteristics[ch].value); } } //console.log("calculateSpellDamage -> " + formula ); return eval(formula); } /** * Turns a formula into a processed string for display * * Turns a spell attribute such as "Willpower Bonus Rounds" into a more user friendly, processed value * such as "4 Rounds". If the aoe is checked, it wraps the result in AoE (Result). * * @param {String} formula Formula to process - "Willpower Bonus Rounds" * @param {boolean} aoe Whether or not it's calculating AoE (changes string return) * @returns {String} formula processed formula */ calculateSpellAttributes(formula, aoe=false) { let actorData = this.data formula = formula.toLowerCase(); // Do not process these special values if (formula != game.i18n.localize("Vous").toLowerCase() && formula != game.i18n.localize("Special").toLowerCase() && formula != game.i18n.localize("Instantané").toLowerCase()) { // Specific case, to avoid wrong matching with "Force" if (formula.includes("force mentale")) { // Determine if it's looking for the bonus or the value if (formula.includes('bonus')) formula = formula.replace( "bonus de force mentale", actorData.data.characteristics["wp"].bonus); else formula = formula.replace("force mentale", actorData.data.characteristics["wp"].value); } if (formula.includes("yard") ) formula = formula.replace('yard', "mètre" ); if (formula.includes("yds") ) formula = formula.replace('yds', "m." ); // Iterate through remaining characteristics for(let ch in actorData.data.characteristics) { // If formula includes characteristic name //console.log("Testing :", ch, WFRP4E.characteristics[ch].toLowerCase()); if (formula.includes(WFRP4E.characteristics[ch].toLowerCase())) { // Determine if it's looking for the bonus or the value if (formula.includes('bonus')) formula = formula.replace("bonus de " + WFRP4E.characteristics[ch].toLowerCase(), actorData.data.characteristics[ch].bonus); else formula = formula.replace(WFRP4E.characteristics[ch].toLowerCase(), actorData.data.characteristics[ch].value); } } } // If AoE - wrap with AoE ( ) if (aoe) formula = "AoE (" + formula.capitalize() + ")"; //console.log("calculateSpellAttributes -> " + formula ); return formula.capitalize(); } } Hooks.on("chatMessage", async (html, content, msg) => { // Split input into arguments let command = content.split(" ").map(function(item) { return item.trim(); }) if (command[0] == "/auberge") { msg["type"] = 0; msg["rollMode"] = "gmroll"; var compendium = game.packs.find(p => p.collection === 'WH4-fr-translation.plats-dauberges'); let rollList = []; await compendium.getIndex().then(index => rollList = index); //console.log("Got compendium...", rollList.length); for (var i=0; i< rollList.length; i++) { var rollTab = rollList[i]; if ( rollTab.name.toLowerCase().includes(command[1].toLowerCase()) ) { let my_rollTable; await compendium.getEntity(rollTab._id).then(mytab => my_rollTable = mytab); var result = my_rollTable.roll(); console.log("RES: ", result[0], result[1] ); msg.content = my_rollTable.name + " : " + result[1].text; //my_rollTable.draw(); ChatMessage.create(msg); return false; } } msg["content"] = "Syntaxe : /auberge MOT_CLE, avec MOT_CLE parmi:
BoissonsBase, BoissonsFortes, Desserts, PlatsCommuns, PlatsExcellents, PlatsMaritimes, PlatsMédiocres, PlatsQualité, PlatsRivières
Des raccourcis sont possibles avec une partie du nom : /auberge Base (correspond à BoissonBase) ou /auberge Mari (correspond à PlatsMaritimes), etc." ChatMessage.create(msg); return false; } } ); var __mywait = ms => new Promise((r, j)=>setTimeout(r, ms)) Hooks.once('init', () => { // Replace to manage specific bonuses/char. computations CONFIG.Actor.entityClass = ActorWfrp4e_fr; WFRP4E.talentBonuses = { "perspicace": "int", "affable": "fel", "tireur de précision": "bs", "très fort": "s", "vivacité": "i", "reflexes foudroyants": "ag", "imperturbable": "wp", "très résistant": "t", "doigts de fée": "dex", "guerrier né": "ws" } WFRP4E.speciesSkills = { "human": [ "Soins aux animaux", "Charme", "Calme", "Evaluation", "Ragot", "Marchandage", "Langue (Bretonnien)", "Langue (Wastelander)", "Commandement", "Savoir (Reikland)", "Corps à corps (Base)", "Projectiles (Arc)" ], "dwarf": [ "Résistance à l'alcool", "Calme", "Endurance", "Divertissement (Raconter)", "Evaluation", "Intimidation", "Langue (Khazalid)", "Savoir (Nains)", "Savoir (Geologie)", "Savoir (Metallurgie)", "Corps à corps (Base)", "Métier (Au choix)" ], "halfling": [ "Charme", "Résistance à l'alcool", "Esquive", "Pari", "Marchandage", "Intuition", "Langue (Mootland)", "Savoir (Reikland)", "Perception", "Escamotage", "Discrétion (Au choix)", "Métier (Cuisinier)" ], "helf": [ "Calme", "Divertissement (Chant)", "Evaluation", "Langue (Eltharin)", "Commandement", "Corps à corps (Base)", "Navigation", "Perception", "Musicien (Au choix)", "Projectiles (Arc)", "Voile", "Natation" ], "welf": [ "Athlétisme", "Escalade", "Endurance", "Divertissement (Chant)", "Intimidation", "Langue (Eltharin)", "Corps à corps (Base)", "Survie en extérieur", "Perception", "Projectiles (Arc)", "Discrétion (Rural)", "Pistage" ], } WFRP4E.speciesTalents = { "human": [ "Destinée", "Affable, Perspicace", 3 ], "dwarf": [ "Résistance à la Magie", "Vision Nocturne", "Lire/Ecrire, Impitoyable", "Déterminé, Obstiné", "Costaud", 0 ], "halfling": [ "Sens Aiguisé (Gout)", "Vision Nocturne", "Résistance (Chaos)", "Petit", 0 ], "helf": [ "Sens Aiguisé (Vue)", "Imperturbable, Perspicace", "Vision Nocturne", "Seconde Vue, Sixième Sens", "Lire/Ecrire", 0 ], "welf": [ "Sens Aiguisé (Sight)", "Dur à cuire, Seconde Vue", "Vision Nocturne", "Seconde Vue, Sixth Sense", "Lire/Ecrire", 0 ], } if(typeof Babele !== 'undefined') { Babele.get().register({ module: 'WH4-fr-translation', lang: 'fr', dir: 'compendium' }); Babele.get().registerConverters({ "career_skills": (skills_list) => { var compendium = game.packs.find(p => p.collection === 'wfrp4e.skills'); //console.log( "Thru here ...", compendium, skills_list); var i; var len = skills_list.length; var re = /(.*)\((.*)\)/i; for (i = 0; i < len; i++) { var transl = compendium.i18nName( { name: skills_list[i] } ); //console.log("List ...", skills_list[i]); if ( transl == skills_list[i] ) { var res = re.exec( skills_list[i]); if (res) { //console.log("Matched/split:", res[1], res[2]); var subword = game.i18n.localize(res[2].trim() ); var s1 = res[1].trim() + " ()"; var translw = compendium.i18nName( { name: s1} ); if (translw != s1) { var res2 = re.exec(translw); transl = res2[1] + "(" + subword + ")"; } else { s1 = res[1].trim() + " ( )"; translw = compendium.i18nName( { name: s1} ); var res2 = re.exec(translw); transl = res2[1] + "(" + subword + ")"; } } } skills_list[i] = transl; } return skills_list; }, "career_talents": (talents_list) => { var compendium = game.packs.find(p => p.collection === 'wfrp4e.talents'); var i; var len = talents_list.length; var re = /(.*)\((.*)\)/i; for (i = 0; i < len; i++) { var transl = compendium.i18nName( { name: talents_list[i]} ); if ( transl == talents_list[i] ) { var res = re.exec( talents_list[i]); if (res) { //console.log("Matched/split:", res[1], res[2]); var subword = game.i18n.localize(res[2].trim() ); var s1 = res[1].trim(); // No () in talents table var translw = compendium.i18nName( { name: s1 } ); if (translw != s1) { transl = translw + "(" + subword + ")"; } else { s1 = res[1].trim() + " ( )"; translw = compendium.i18nName( { name: s1 } ); var res2 = re.exec(translw); transl = res2[1] + "(" + subword + ")"; } } } talents_list[i] = transl; } return talents_list; }, "npc_characteristics": (chars) => { // Auto-convert char names in the sheet for (var key in chars) { //console.log("Was here !", key, chars[key].label); var char = chars[key] var abrev = char["abrev"]; char["label"] = game.i18n.localize( "CHAR." + abrev ); char["abrev"] = game.i18n.localize( "CHARAbbrev." + abrev); } return chars; }, "bestiary_traits": (beast_traits, translations) => { var compendium = game.packs.find(p => p.collection === 'wfrp4e.traits'); for (let trait_en of beast_traits) { var nbt = ""; var special = ""; var name_en = trait_en.name.trim(); // strip \r in some traits name if ( name_en.includes("Tentacles") ) { // Process specific Tentacles case var re = /(.d*)x Tentacles/i; var res = re.exec( name_en ); nbt = res[1] + "x "; name_en = "Tentacles"; } else if ( name_en.includes("(") && name_en.includes(")") ) { // Then process specific traits name with (xxxx) inside var re = /(.*) \((.*)\)/i; var res = re.exec( name_en ); name_en = res[1]; // Get the root traits name special = " (" + res[2] + ")"; // And the special keyword } var trait_fr = compendium.translate( { name: name_en } ); //console.log(">>>>> Trait ?", name_en, nbt, trait_fr.name); trait_en.name = nbt + trait_fr.name + special; trait_en.data.description.value = trait_fr.data.description.value; if ( isNaN(trait_en.data.specification.value) ) { // This is a string, so translate it //console.log("Translating : ", trait_en.data.specification.value); trait_en.data.specification.value = game.i18n.localize( trait_en.data.specification.value.trim() ); } } return beast_traits; }, // To avoid duplicateing class for all careers "generic_localization": (value) => { if ( value ) return game.i18n.localize( value.trim() ); }, "trapping_qualities_flaws": (value) => { if ( value ) { var list = value.split( "," ); var i=0; var re = /(.*) (\d+)/i; for (i=0; i { var compendium = game.packs.find(p => p.collection === 'wfrp4e.careers'); return compendium.i18nName( { name: value } ); }, // Auto-translate duration "spells_duration_range_target_damage": (value) => { //console.log("Spell duration/range/damage/target :", value); if ( value == "" ) return ""; // Hop ! if ( value == "Touch" ) return "Contact"; // Hop ! if ( value == "You" ) return "Vous"; // Hop ! if ( value == "Instant" ) return "Instantané"; // Hop ! var translw = value; var re = /(.*) Bonus (\w*)/i; var res = re.exec( value ); var unit = ""; if ( res ) { // Test " Bonus " pattern if ( res[1] ) { // We have char name, then convert it translw = "Bonus de " + game.i18n.localize( res[1].trim() ); } unit = res[2]; } else { re = /(\d+) (\w+)/i; res = re.exec( value ); if (res) { // Test : " " pattern translw = res[1]; unit = res[2]; } else { // Test re = /(\w+) (\w+)/i; res = re.exec( value ); if (res) { // Test : " " pattern translw = game.i18n.localize( res[1].trim() ); unit = res[2]; } } } if ( unit == "hour") unit = "heure"; if ( unit == "hours") unit = "heures"; if ( unit == "days") unit = "jours"; if ( unit == "yard") unit = "mètre"; if ( unit == "yards") unit = "mètres"; translw += " " + unit; return translw; } }); } } );