/* -------------------------------------------- */ //import { YggdrasillCombat } from "./yggdrasill-combat.js"; /* -------------------------------------------- */ const dureeGaldrText = { "1d5a": "Actions", "1d10t": "Tours", "1d10m": "Minutes", "1d10h": "Heures", "1d5j": "Jours"}; const ciblesGaldrText = { "1": "1", "2_4": "2 à 4", "5_9": "5 à 9", "10_49": "10 à 49", "50plus": "50 et plus"}; /* -------------------------------------------- */ export class YggdrasillUtility { /* -------------------------------------------- */ static async preloadHandlebarsTemplates() { const templatePaths = [ 'systems/fvtt-yggdrasill/templates/actor-sheet.html', 'systems/fvtt-yggdrasill/templates/editor-notes-gm.html', 'systems/fvtt-yggdrasill/templates/hud-actor-attaque.html', 'systems/fvtt-yggdrasill/templates/hud-actor-sort.html' ] return loadTemplates(templatePaths); } /* -------------------------------------------- */ static templateData(it) { return YggdrasillUtility.data(it)?.data ?? {} } /* -------------------------------------------- */ static data(it) { if (it instanceof Actor || it instanceof Item || it instanceof Combatant) { return it.data; } return it; } /* -------------------------------------------- */ static createDirectSortedOptionList( min, max) { let options = []; for(let i=min; i<=max; i++) { options.push( {value:i, text: `${i}` } ); } return options; } /* -------------------------------------------- */ static createDirectOptionList( min, max) { let options = {}; for(let i=min; i<=max; i++) { options[`${i}`] = `${i}`; } return options; } /* -------------------------------------------- */ static buildListOptions(min, max) { let options = "" for (let i = min; i <= max; i++) { options += `` } return options; } /* -------------------------------------------- */ static buildSROptions( ) { let options = "" options += `` options += `` options += `` options += `` options += `` options += `` options += `` options += `` options += `` options += `` return options; } /* -------------------------------------------- */ static onSocketMesssage( msg ) { if( !game.user.isGM ) return; // Only GM if (msg.name == 'msg_attack' ) { this.performAttack( msg.data ); } } /* -------------------------------------------- */ static async loadCompendiumData(compendium) { const pack = game.packs.get(compendium); return await pack?.getDocuments() ?? []; } /* -------------------------------------------- */ static async loadCompendium(compendium, filter = item => true) { let compendiumData = await YggdrasillUtility.loadCompendiumData(compendium); return compendiumData.filter(filter); } /* -------------------------------------------- */ static async specificYggRoll( nbDice ) { let rawDices = [] let rolls = [] let maxTab = [] maxTab[0] = {idx: 0, value: 0} maxTab[1] = {idx: 0, value: 0} for (let i=0; i maxTab[0].value) { if ( nbDice > 1 && maxTab[0].value > maxTab[1].value) { maxTab[1].value = maxTab[0].value } maxTab[0].value = rolls[i].total } else { if ( nbDice > 1 && rolls[i].total > maxTab[1].value) { maxTab[1].value = rolls[i].total } } } return { rawDices: rawDices, maxTab: maxTab, rolls: rolls} } /* -------------------------------------------- */ static async rollAttribute( rollData ) { // Init stuff let isCritical = false; let isFailure = false; let isSuccess = false; let marge = 0; let niveau = rollData.subAttr.value; // Bonus/Malus total rollData.finalBM = rollData.bonusMalus; // Gestion cas blessé (malus de -3) if ( rollData.isBlesse) { // Cas blesse : malus de -3 rollData.finalBM -= 3; } let results = await this.specificYggRoll( 2 ) rollData.rawDices = results.rawDices rollData.maxTab = results.maxTab rollData.rolls = results.rolls rollData.bonus = niveau + rollData.finalBM rollData.finalTotal = rollData.maxTab[0].value + rollData.maxTab[1].value; rollData.finalTotal += rollData.bonus // Compute total SR rollData.srFinal = rollData.sr; if ( rollData.bonusdefense ) { rollData.srFinal += rollData.bonusdefense; } if ( rollData.srFinal > 0 ) { isCritical = rollData.finalTotal >= rollData.srFinal*2; isSuccess = rollData.finalTotal >= rollData.srFinal; marge = rollData.finalTotal - rollData.srFinal; } if (rollData.rolls[0].dice[0].results[0].result == 1 && rollData.rolls[1].dice[0].results[0].result == 1) { isFailure = true; } // Dégats if ( isSuccess && rollData.subAttr.degats ) { rollData.degatsExplain = `Marge(${marge}) + Physique(${rollData.valuePhysique}) + 1d10`; rollData.rollDegats = new Roll("1d10+"+marge+"+"+rollData.valuePhysique).roll( { async: false} ); await this.showDiceSoNice(rollData.rollDegats, game.settings.get("core", "rollMode") ); rollData.degats = rollData.rollDegats.total; } // Stockage resultats rollData.isFailure = isFailure; rollData.isSuccess = isSuccess; rollData.isCritical = isCritical; rollData.marge = marge; console.log("ROLLLL ATTR!!!!", rollData); this.createChatWithRollMode( rollData.alias, { content: await renderTemplate(`systems/fvtt-yggdrasill/templates/chat-generic-result.html`, rollData) }); //myRoll.toMessage(); } /* -------------------------------------------- */ static async rollYggdrasill( rollData ) { let sumDice = ( rollData.isEpuise | rollData.isMeurtri) ? 1 : 2; // Init stuff let isCritical = false; let isFailure = false; let isSuccess = false; let marge = 0; let nbDice = rollData.selectedCarac.value; let niveauCompetence = 0; // Select niveau de competence/arme/carac if ( rollData.mode != "carac" ) { niveauCompetence = rollData.competence.data.niveau; } else { niveauCompetence = rollData.selectedCarac.value; } // Bonus/Malus total rollData.finalBM = rollData.bonusMalus; if ( rollData.attackDef) { rollData.finalBM -= rollData.attackDef.malus; } if ( rollData.sort && rollData.sort.data.malus ) { rollData.finalBM += rollData.sort.data.malus; } // Gestion cas blessé (malus de -3) if ( rollData.isBlesse) { // Cas blesse : malus de -3 rollData.finalBM -= 3; } if (sumDice > nbDice) sumDice = nbDice; let results = await this.specificYggRoll( nbDice ) rollData.rawDices = results.rawDices rollData.maxTab = results.maxTab rollData.rolls = results.rolls console.log("RES", results, nbDice, sumDice) if ( rollData.furorUsage > 0 ) { results = await this.specificYggRoll( rollData.furorUsage ) rollData.furorRawDices = results.rawDices rollData.furorMaxTab = results.maxTab rollData.furorRolls = results.rolls let actor = game.actors.get(rollData.actorId); actor.decrementFuror( rollData.furorUsage); } rollData.bonusTotal = niveauCompetence + rollData.finalBM rollData.finalTotal = (sumDice ==1) ? rollData.maxTab[0].value : rollData.maxTab[0].value + rollData.maxTab[1].value; rollData.furorResult = 0 for (let i=0; i 0 ) { isCritical = rollData.finalTotal >= rollData.srFinal*2; isSuccess = rollData.finalTotal >= rollData.srFinal; marge = rollData.finalTotal - rollData.srFinal; } if (nbDice == 1 && rollData.rolls[0].dice[0].results[0].result == 1) { isFailure = true; } if (nbDice == 2 && rollData.rolls[0].dice[0].results[0].result == 1 && rollData.rolls[1].dice[0].results[0].result == 1) { isFailure = true; } if (nbDice >= 3 ) { let nbOnes = 0 for (let roll of rollData.rolls) { if (roll.dice[0].results[0].result == 1 ) nbOnes++; } isFailure = nbOnes >= 3; } // Dégats if ( isSuccess && (rollData.mode == "armecc" || rollData.mode == "armedist") ) { rollData.degatsExplain = `Marge(${marge}) + Degats Arme(${rollData.arme.data.degat}) + Bonus Attaque(${rollData.attackDef.bonusdegats})`; rollData.degats = marge + rollData.arme.data.degat + rollData.attackDef.bonusdegats; } // Stockage resultats rollData.sumDice = sumDice; rollData.isFailure = isFailure; rollData.isSuccess = isSuccess; rollData.isCritical = isCritical; rollData.marge = marge; // Specific GALDR if ( rollData.sort?.type == "sortgaldr" && rollData.isSuccess) { let galdrRoll = new Roll( rollData.dureeGaldr.substring(0, rollData.dureeGaldr.length - 1) ).roll( { async: false} ); await this.showDiceSoNice(galdrRoll, game.settings.get("core", "rollMode") ); rollData.dureeGaldrText = galdrRoll.total + " " + dureeGaldrText[rollData.dureeGaldr]; if ( rollData.sort.data.voie == "illusion") { let volume = rollData.zoneGaldr.substring(3, rollData.zoneGaldr.length); rollData.zoneGaldrText = rollData.instinctCarac.value + " x " + volume; } else { rollData.ciblesGaldrText = ciblesGaldrText[rollData.nbCibles]; } } console.log("ROLLLL!!!!", rollData); this.createChatWithRollMode( rollData.alias, { content: await renderTemplate(`systems/fvtt-yggdrasill/templates/chat-generic-result.html`, rollData) }); //myRoll.toMessage(); } /* -------------------------------------------- */ static getUsers(filter) { return game.users.filter(filter).map(user => user.data._id); } /* -------------------------------------------- */ static getWhisperRecipients(rollMode, name) { switch (rollMode) { case "blindroll": return this.getUsers(user => user.isGM); case "gmroll": return this.getWhisperRecipientsAndGMs(name); case "selfroll": return [game.user.id]; } return undefined; } /* -------------------------------------------- */ static getWhisperRecipientsAndGMs(name) { return ChatMessage.getWhisperRecipients(name) .concat(ChatMessage.getWhisperRecipients('GM')); } /* -------------------------------------------- */ static blindMessageToGM(chatOptions) { let chatGM = duplicate(chatOptions); chatGM.whisper = this.getUsers(user => user.isGM); chatGM.content = "Message aveugle de " + game.user.name + "
" + chatOptions.content; console.log("blindMessageToGM", chatGM); game.socket.emit("system.foundryvtt-yggdrasill", { msg: "msg_gm_chat_message", data: chatGM }); } /* -------------------------------------------- */ static createChatMessage(name, rollMode, chatOptions) { switch (rollMode) { case "blindroll": // GM only if (!game.user.isGM) { this.blindMessageToGM(chatOptions); chatOptions.whisper = [game.user.id]; chatOptions.content = "Message envoyé en aveugle au Gardien"; } else { chatOptions.whisper = this.getUsers(user => user.isGM); } break; default: chatOptions.whisper = this.getWhisperRecipients(rollMode, name); break; } chatOptions.alias = chatOptions.alias || name; ChatMessage.create(chatOptions); } /* -------------------------------------------- */ static createChatWithRollMode(name, chatOptions) { this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions); } /* -------------------------------------------- */ static async confirmDelete(actorSheet, li) { let itemId = li.data("item-id"); let msgTxt = "

Etes vous certain de souhaiter supprimer cet item ?"; let buttons = { delete: { icon: '', label: "Oui, à supprimer", callback: () => { actorSheet.actor.deleteEmbeddedDocuments( "Item", [itemId] ); li.slideUp(200, () => actorSheet.render(false)); } }, cancel: { icon: '', label: "Annuler" } } msgTxt += "

"; let d = new Dialog({ title: "Confirmer la suppression", content: msgTxt, buttons: buttons, default: "cancel" }); d.render(true); } /* -------------------------------------------- */ static async showDiceSoNice(roll, rollMode) { if (game.modules.get("dice-so-nice")?.active) { if (game.dice3d) { let whisper = null; let blind = false; rollMode = rollMode ?? game.settings.get("core", "rollMode"); switch (rollMode) { case "blindroll": //GM only blind = true; case "gmroll": //GM + rolling player whisper = this.getUsers(user => user.isGM); break; case "roll": //everybody whisper = this.getUsers(user => user.active); break; case "selfroll": whisper = [game.user.id]; break; } await game.dice3d.showForRoll(roll, game.user, true, whisper, blind); } } } }