/************************************************************************************/ import statParserFR from "./modules/import-stat-2.js"; /************************************************************************************/ var compmod = "wfrp4e"; const vo_conditions = { "ablaze": "Ablaze", "bleeding": "Bleeding", "blinded": "Blinded", "broken": "Broken", "deafened": "Deafened", "entangled": "Entangled", "fatigued": "Fatigued", "poisoned": "Poisoned", "prone": "Prone", "stunned": "Stunned", "surprised": "Surprised", "unconscious": "Unconscious", "grappling": "Grappling", "fear": "Fear", "defeated": "Defeated" } /************************************************************************************/ Hooks.once('init', () => { // Check various settings in the installation game.modules.forEach((module, name) => { if ( name == "wfrp4e-core" && module.active) { compmod = "wfrp4e-core"; } } ); game.wfrp4efr = { compmod: compmod, vo_conditions: vo_conditions } game.wfrp4e.apps.StatBlockParser.parseStatBlock = async function( statString, type = "npc") { return statParserFR( statString, type); } /*---------------------------------------------------------------------*/ game.wfrp4e.entities.ItemWfrp4e.prototype.computeSpellDamage = function(formula, isMagicMissile) { try { formula = formula.toLowerCase(); if (isMagicMissile) // If it's a magic missile, damage includes willpower bonus { formula += "+ " + this.actor.characteristics["wp"].bonus } // Specific case, to avoid wrong matching with "Force" if (formula.includes("toughness bonus")) { formula = formula.replace( "toughness bonus", this.actor.characteristics["t"].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", this.actor.characteristics["wp"].bonus); formula = formula.replace( "force mentale bonus", this.actor.characteristics["wp"].bonus); } else formula = formula.replace("force mentale", this.actor.characteristics["wp"].value); } // Iterate through characteristics for(let ch in this.actor.characteristics) { // If formula includes characteristic name while (formula.includes(this.actor.characteristics[ch].label.toLowerCase())) { // Determine if it's looking for the bonus or the value if (formula.includes('bonus')) { formula = formula.replace("bonus de " + game.wfrp4e.config.characteristics[ch].toLowerCase(), this.actor.characteristics[ch].bonus); formula = formula.replace(game.wfrp4e.config.characteristics[ch].toLowerCase() + " bonus", this.actor.characteristics[ch].bonus); } else formula = formula.replace(game.wfrp4e.config.characteristics[ch].toLowerCase(), this.actor.characteristics[ch].value); } } return eval(formula); } catch (e) { throw ui.notifications.error("Error: could not parse spell damage. See console for details") } } /*---------------------------------------------------------------------*/ game.wfrp4e.entities.ItemWfrp4e.prototype.computeSpellPrayerFormula = function(type, aoe=false, formulaOverride) { let formula = formulaOverride || this[type]?.value if (Number.isNumeric(formula)) return formula //console.log("Compute FR") formula = formula.toLowerCase(); // Do not process these special values if (formula != game.i18n.localize("You").toLowerCase() && formula != game.i18n.localize("Special").toLowerCase() && formula != game.i18n.localize("Instant").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", this.actor.characteristics["wp"].bonus); formula = formula.replace( "force mentale bonus", this.actor.characteristics["wp"].bonus); } else formula = formula.replace("force mentale", this.actor.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 this.actor.characteristics) { // If formula includes characteristic name //console.log("Testing :", ch, WFRP4E.characteristics[ch].toLowerCase()); if (formula.includes(game.wfrp4e.config.characteristics[ch].toLowerCase())) { // Determine if it's looking for the bonus or the value if (formula.includes('bonus')) { formula = formula.replace("bonus de " + game.wfrp4e.config.characteristics[ch].toLowerCase(), this.actor.characteristics[ch].bonus); formula = formula.replace(game.wfrp4e.config.characteristics[ch].toLowerCase() + " bonus", this.actor.characteristics[ch].bonus); } else formula = formula.replace(game.wfrp4e.config.characteristics[ch].toLowerCase(), this.actor.characteristics[ch].value); } } } // If AoE - wrap with AoE ( ) if (aoe) formula = "AoE (" + formula.capitalize() + ")"; //console.log("calculateSpellAttributes -> " + formula ); return formula.capitalize(); } /*---------------------------------------------------------------------*/ // Converters area if(typeof Babele !== 'undefined') { Babele.get().register({ module: 'wh4-fr-translation', lang: 'fr', dir: 'compendium' }); Babele.get().registerConverters({ "career_skills": (skills_list) => { //console.log( "Thru here ...", compendium, skills_list); if ( skills_list ) { var i; var len = skills_list.length; var re = /(.*)\((.*)\)/i; for (i = 0; i < len; i++) { skills_list[i] = skills_list[i].trim(); var transl = game.babele.translate(compmod+'.skills', { name: skills_list[i] }, true ).name; //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 = game.babele.translate(compmod+'.skills', { name: s1}, true ).name; if (translw != s1) { var res2 = re.exec(translw); transl = res2[1] + "(" + subword + ")"; } else { s1 = res[1].trim() + " ( )"; translw = game.babele.translate(compmod+'.skills', { name: s1}, true ).name; var res2 = re.exec(translw); transl = res2[1] + "(" + subword + ")"; } } } skills_list[i] = transl; } } return skills_list; }, "npc_details": (details) => { //console.log("DETAILS: ", details); let newDetails = duplicate(details); if (details.species && details.species.value ) newDetails.species.value = game.i18n.localize(details.species.value); if (details.gender && details.gender.value ) newDetails.gender.value = game.i18n.localize(details.gender.value); if (details.class && details.class.value ) newDetails.class.value = game.i18n.localize(details.class.value); return newDetails; }, "career_talents": (talents_list) => { var compendium = game.packs.find(p => p.collection === compmod+'.talents'); var i; if ( talents_list ) { var len = talents_list.length; var re = /(.*)\((.*)\)/i; for (i = 0; i < len; i++) { var transl = game.babele.translate(compmod+'.talents', { name: talents_list[i]}, true ).name; 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 = game.babele.translate(compmod+'.talents', { name: s1 }, true ).name; if (translw != s1) { transl = translw + " (" + subword + ")"; } else { s1 = res[1].trim() + " ( )"; translw = game.babele.translate(compmod+'.talents', { name: s1 }, true ).name; 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) { var char = chars[key]; //console.log("Was here !", key, char ); var abrev = char["abrev"]; let toTransl = "CHAR." + abrev; if ( game.i18n.localize( toTransl ) != toTransl) { // Manages unknown language char["label"] = game.i18n.localize( "CHAR." + abrev ); char["abrev"] = game.i18n.localize( "CHARAbbrev." + abrev ); } } return chars; }, "bestiary_traits": (beast_traits, translations) => { for (let trait_en of beast_traits) { var special = ""; var nbt = ""; var name_en = trait_en.name.trim(); // strip \r in some traits name if ( trait_en.type == "trait") { if ( name_en.includes("Tentacles") ) { // Process specific Tentacles case var re = /(.d*)x Tentacles/i; var res = re.exec( name_en ); if ( res && res[1] ) 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 = " (" + game.i18n.localize( res[2].trim() ) + ")"; // And the special keyword } var trait_fr = game.babele.translate( compmod+'.traits', { name: name_en }, true ); //console.log(">>>>> Trait ?", name_en, nbt, trait_fr, trait_fr.name, special); trait_en.name = nbt + trait_fr.name + special; if ( trait_fr.data && trait_fr.data.description && trait_fr.data.description.value ) { trait_en.data.description.value = trait_fr.data.description.value; } else if ( game.modules.get( 'wfrp4e-eis') ) { // No description in the FR compendium -> test other compendium if presenr trait_fr = game.babele.translate( 'wfrp4e-eis.eisitems', { name: name_en }, true); trait_en.name = nbt + trait_fr.name + special; if ( trait_fr.data && trait_fr.data.description && trait_fr.data.description.value ) trait_en.data.description.value = trait_fr.data.description.value; } if ( trait_en.data && trait_en.data.specification && 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() ); } } else if ( trait_en.type == "skill") { if ( name_en.includes("(") && name_en.includes(")") ) { // Then process specific skills name with (xxxx) inside var re = /(.*) +\((.*)\)/i; var res = re.exec( name_en ); name_en = res[1].trim(); // Get the root skill name special = " (" + game.i18n.localize( res[2].trim() ) + ")"; // And the special keyword } var trait_fr = game.babele.translate( compmod+'.skills', { name: name_en }, true ); //console.log(">>>>> Skill ?", name_en, special, trait_fr.name, trait_fr); if (trait_fr.name != name_en) { // Translation OK trait_en.name = trait_fr.name + special; if ( trait_fr.data ) { trait_en.data.description.value = trait_fr.data.description.value; } } } else if ( trait_en.type == "prayer") { var trait_fr = game.babele.translate( compmod+'.prayers', { name: name_en }, true); //console.log(">>>>> Prayer ?", name_en, special, trait_fr.name ); trait_en.name = trait_fr.name + special; if ( trait_fr.data && trait_fr.data.description && trait_fr.data.description.value ) trait_en.data.description.value = trait_fr.data.description.value; } else if ( trait_en.type == "spell") { var trait_fr = game.babele.translate( compmod+'.spells', { name: name_en }, true); if ( trait_fr.name == name_en ) { // If no translation, test eisspells trait_fr = game.babele.translate( 'wfrp4e-eis.eisspells', { name: name_en }, true); } if ( trait_fr.name == name_en ) { // If no translation, test unofficial grimoire trait_fr = game.babele.translate( 'wfrp4e-unofficial-grimoire.ug-spells', { name: name_en }, true); } //console.log(">>>>> Spell ?", name_en, special, trait_fr.name ); trait_en.name = trait_fr.name + special; if ( trait_fr.data && trait_fr.data.description && trait_fr.data.description.value ) trait_en.data.description.value = trait_fr.data.description.value; } else if ( trait_en.type == "talent") { if ( name_en.includes("(") && name_en.includes(")") ) { // Then process specific skills name with (xxxx) inside var re = /(.*) +\((.*)\)/i; var res = re.exec( name_en ); name_en = res[1].trim(); // Get the root talent name, no parenthesis this time... special = " (" + game.i18n.localize( res[2].trim() ) + ")"; // And the special keyword } var trait_fr = game.babele.translate( compmod+'.talents', { name: name_en }, true ); //console.log(">>>>> Talent ?", trait_fr, name_en, special, trait_fr.name); if ( trait_fr.name != "Sprinter" && trait_fr.name == name_en) { // If no translation, test ugtalents trait_fr = game.babele.translate( 'wfrp4e-unofficial-grimoire.ug-careerstalentstraits', { name: name_en }, true ); } if ( trait_fr.name == "Sprinter" || trait_fr.name != name_en) { // Talent translated! trait_en.name = trait_fr.name.trim() + special; if ( trait_fr.data ) { // Why ??? trait_en.data.description.value = trait_fr.data.description.value; } } } else if ( trait_en.type == "career") { var career_fr = game.babele.translate( compmod+'.careers', trait_en, true ); console.log(">>>>> Career ?", name_en, career_fr.name ); trait_en.name = career_fr.name; trait_en.data = duplicate(career_fr.data); } else if ( trait_en.type == "trapping" || trait_en.type == "weapon" || trait_en.type == "armour" || trait_en.type == "container" || trait_en.type == "money") { var trapping_fr = game.babele.translate( compmod+'.trappings', trait_en, true ); //console.log(">>>>> Trapping ?", name_en, trapping_fr.name); trait_en.name = trapping_fr.name; if ( trapping_fr.data) { trait_en.data.description = trapping_fr.data.description; } } } 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 ) { let newQF = []; //console.log("ATOUTS", value); var i=0; //var re = /(.*) (\d+)/i; for (i=0; i { // Manage exception - Dirty hack if ( value == 'Slayer' ) { return "Tueur"; } if ( value == 'Druidic Priest' ) { return "Druide"; } // Per default var compendium = game.packs.find(p => p.collection === compmod+'.careers'); if ( compendium ) return game.babele.translate(compmod+'.careers', { name: value } ).name; else ui.notifications.error("Impossible de trouver la carrière " + value + ". Elle n'est probablement pas traduite.", { permanent: true }) }, "mutations_modifier": (value) => { // This is really UGLYYYY i know, but i started like this and discovered afterward that many strings were not easy to automate... Sorry :) //console.log("Parsing mutation :", value); value = value.toLowerCase(); value = value.replace("gain a broken condition if you fail a test derived from ", "Gagnez un état Brisé si vous échouez à un test dérivé de "); value = value.replace("weapon skill" ,"Capacité de Combat"); value = value.replace("ballistic skill", "Capacité de Tir"); value = value.replace("strength", "Force"); value = value.replace("toughness", "Endurance"); value = value.replace("agility", "Agilité"); value = value.replace("dexterity", "Dextérité"); value = value.replace("willpower", "Force Mentale"); value = value.replace("fellowship", "Sociabilité"); value = value.replace("initiative", "Initiative"); value = value.replace("intelligence", "Intelligence"); value = value.replace("armor points to the head", "PA à la Tête"); value = value.replace("subject to frenzy", "Sujet à la Frénésie"); value = value.replace("you do not scar", "Aucune cicatrice"); value = value.replace("movement", "Mouvement"); value = value.replace("armor points to all locations", "PA sur tout le corps"); value = value.replace("to any test when alone", "à tout les tests lorsque seul"); value = value.replace("track", "Pistage"); value = value.replace("to any test not hurting another", "à tout les Tests n'aggressant pas autrui"); value = value.replace("on tests to hurt", "pour les tests impliquant une agression") value = value.replace("to all language tests when speaking", "à tout les Tests de Langue lorsque vous parlez"); value = value.replace("on perception tests involving sight", "aux Tests de Perception impliquant la Vue"); value = value.replace("to all Sociabilité tests", "à tout les Tests de Sociabilité"); return value; }, "talent_name": (name, translation) => { console.log("NAME !!!", name, translation); }, "effects": (effects, translations) => { if ( !effects) return; if ( !translations) return; for (let i=0; i { if ( !effects) return; for (let i=0; i { //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; } }); } } ); /* -------------------------------------------- */ // 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) /* -------------------------------------------- */ } } /*---------------------------------------------------------------------*/ Hooks.once('ready', () => { registerUsageCount("wh4-fr-translation") });