diff --git a/module/actor-sheet.js b/module/actor-sheet.js index d32b70bb..15b7f27c 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -9,6 +9,7 @@ import { RdDItemArme } from "./item-arme.js"; import { RdDItemCompetence } from "./item-competence.js"; import { RdDBonus } from "./rdd-bonus.js"; import { Misc } from "./misc.js"; +import { RdDCombatManager } from "./rdd-combat.js"; /* -------------------------------------------- */ export class RdDActorSheet extends ActorSheet { @@ -41,11 +42,17 @@ export class RdDActorSheet extends ActorSheet { data.itemsByType = Misc.classify(data.items); // Competence per category + data.data.comptageArchetype = RdDUtility.getLimitesArchetypes(); data.data.competenceXPTotal = 0; data.competenceByCategory = Misc.classify( data.itemsByType.competence, item => item.data.categorie, item => { + let archetypeKey = (item.data.niveau_archetype < 0) ? 0 : item.data.niveau_archetype; + if (data.data.comptageArchetype[archetypeKey] == undefined) { + data.data.comptageArchetype[archetypeKey] = { "niveau": archetypeKey, "nombreMax": 0, "nombre": 0}; + } + data.data.comptageArchetype[archetypeKey].nombre = (data.data.comptageArchetype[archetypeKey]?.nombre??0) + 1; //Comptage archetype item.data.xpNext = RdDItemCompetence.getCompetenceNextXp(item.data.niveau); item.data.isLevelUp = item.data.xp >= item.data.xpNext; // Flag de niveau à MAJ //this.actor.checkCompetenceXP(item.name); // Petite vérification experience @@ -67,6 +74,7 @@ export class RdDActorSheet extends ActorSheet { currentCarac.xpNext = RdDUtility.getCaracNextXp(currentCarac.value); currentCarac.isLevelUp = (currentCarac.xp >= currentCarac.xpNext); } + sum += (data.data.beaute >= 0) ? (data.data.beaute - 10) : 0; data.data.caracSum = sum; // Force empty arme, at least for Esquive @@ -89,12 +97,12 @@ export class RdDActorSheet extends ActorSheet { // To avoid armour and so on... data.data.combat = duplicate(RdDUtility.checkNull(data.itemsByType['arme'])); - data.data.combat = RdDUtility._finalizeArmeList(data.data.combat, data.itemsByType.competence, data.data.carac); + data.data.combat = RdDCombatManager.finalizeArmeList(data.data.combat, data.itemsByType.competence, data.data.carac); data.esquive = { name: "Esquive", niveau: data.competenceByCategory?.melee.find(it => it.name == 'Esquive')?.data.niveau ?? -6}; let corpsACorps = data.competenceByCategory?.melee.find(it => it.name == 'Corps à corps'); if (corpsACorps) { - let cc_init = RdDUtility.calculInitiative(corpsACorps.data.niveau, caracList['melee'].value); + let cc_init = RdDCombatManager.calculInitiative(corpsACorps.data.niveau, data.data.carac['melee'].value); data.data.combat.push(RdDItemArme.mainsNues({ niveau: corpsACorps.data.niveau, initiative: cc_init })); } this.armesList = duplicate(data.data.combat); @@ -352,7 +360,7 @@ export class RdDActorSheet extends ActorSheet { if (combatant) { let armeName = event.currentTarget.attributes['data-arme-name'].value; let arme = this.armesList.find(a => a.name == armeName); - RdDUtility.rollInitiativeCompetence(combatant._id, arme); + RdDCombatManager.rollInitiativeCompetence(combatant._id, arme); } else { ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat."); } diff --git a/module/actor.js b/module/actor.js index cb4fadf2..b6c9ed62 100644 --- a/module/actor.js +++ b/module/actor.js @@ -114,6 +114,16 @@ export class RdDActor extends Actor { if (actorData.type === 'vehicule') this._prepareVehiculeData(actorData); } + /* -------------------------------------------- */ + setRollWindowsOpened( flag ) { + this.rollWindowsOpened = flag; + } + + /* -------------------------------------------- */ + isRollWindowsOpened( ) { + return this.rollWindowsOpened; + } + /* -------------------------------------------- */ _prepareCreatureData(actorData) { this.computeEncombrementTotalEtMalusArmure(); @@ -305,18 +315,19 @@ export class RdDActor extends Actor { } return duplicate(list[0]); } + getDemiReve() { + return this.data.data.reve.tmrpos.coord; + } /* -------------------------------------------- */ async deleteSortReserve(sortReserve) { let reserve = duplicate(this.data.data.reve.reserve); - let len = reserve.list.length; - let i = 0; - let newTable = []; - for (i = 0; i < len; i++) { - if (reserve.list[i].coord != sortReserve.coord && reserve.list[i].sort.name != sortReserve.sort.name) - newTable.push(reserve.list[i]); - } - if (newTable.length != len) { - reserve.list = newTable; + let tmr = TMRUtility.getTMR(sortReserve.coord); + let index = reserve.list.findIndex(tmr.type == 'fleuve' + ? sort => (TMRUtility.getTMR(sort.coord).type == 'fleuve' && sort.sort.name == sortReserve.sort.name) + : sort => (sort.coord == sortReserve.coord && sort.sort.name == sortReserve.sort.name) + ); + if (index >=0 ) { + reserve.list.splice(index,1); await this.update({ "data.reve.reserve": reserve }); } } @@ -486,7 +497,7 @@ export class RdDActor extends Actor { async dormir(heures = 1) { let message = { whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name), - content: this.name + ": Vous dormez " + heures + (heures > 1 ? " heures. " : "heure. ") + content: `${this.name}: Vous dormez ${heures == 1 ? 'une': heures} heure${heures == 1 ? '': 's'}.` }; await this.recupereEndurance(message); for (let i = 0; i < heures; i++) { @@ -1151,26 +1162,6 @@ export class RdDActor extends Actor { return tmrInnaccessibles.map(it => it.data.coord); } - /* -------------------------------------------- */ - displayTMRQueueSouffleInformation() { - let messages = []; - for (let item of this.data.items) { - if (EffetsDraconiques.isUrgenceDraconique(item)) { - messages.push("Vous souffrez d'une Urgence Draconique : " + item.data.description); - } - if (EffetsDraconiques.isPeriple(item)) { - messages.push("Vous souffrez du Souffle Périple. Vous devez gérer manuellement le détail du Périple.
" + item.data.description); - } - } - - if (messages.length > 0) { - ChatMessage.create({ - whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), - content: "RAPPEL !
" + messages.join('
') - }); - } - } - /* -------------------------------------------- */ getTMRRencontres() { return this.data.data.reve.rencontre; @@ -1184,7 +1175,7 @@ export class RdDActor extends Actor { //console.log("List", rencontres, len); let newTable = []; for (i = 0; i < len; i++) { - if (rencontres.list[i].coord != this.data.data.reve.tmrpos.coord) + if (rencontres.list[i].coord != this.getDemiReve()) newTable.push(rencontres.list[i]); } if (newTable.length != len) { @@ -1201,7 +1192,7 @@ export class RdDActor extends Actor { let i = 0; let already = false; for (i = 0; i < len; i++) { - if (rencontres.list[i].coord == this.data.data.reve.tmrpos.coord) + if (rencontres.list[i].coord == this.getDemiReve()) already = true; } if (!already) { @@ -1223,9 +1214,7 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ async updateCoordTMR(coord) { - let tmrPos = duplicate(this.data.data.reve.tmrpos); - tmrPos.coord = coord; - await this.update({ "data.reve.tmrpos": tmrPos }); + await this.update({ "data.reve.tmrpos.coord": coord }); } /* -------------------------------------------- */ @@ -2784,6 +2773,14 @@ export class RdDActor extends Actor { await this.setFlag('foundryvtt-reve-de-dragon', 'itemUse', {} ); } + /* -------------------------------------------- */ + async decItemUse( itemId ) { + let itemUse = duplicate(this.getFlag('foundryvtt-reve-de-dragon', 'itemUse') ?? {}); + itemUse[itemId] = (itemUse[itemId] ?? 0) - 1; + await this.setFlag( 'foundryvtt-reve-de-dragon', 'itemUse', itemUse); + console.log("ITEM USE DEC", itemUse); + } + /* -------------------------------------------- */ async incItemUse( itemId ) { let itemUse = duplicate(this.getFlag('foundryvtt-reve-de-dragon', 'itemUse') ?? {}); @@ -3186,6 +3183,9 @@ export class RdDActor extends Actor { case 'souffle': await this.onDeleteOwnedDraconique(item, options, id); break; + case 'casetmr': + await this.onDeleteOwnedCaseTmr(item, options, id); + break; } } @@ -3207,6 +3207,13 @@ export class RdDActor extends Actor { } } + async onDeleteOwnedCaseTmr(item, options, id) { + let draconique = Draconique.all().find(it => it.isCase(item)); + if (draconique) { + draconique.onActorDeleteCaseTmr(this, item) + } + } + notifyGestionTeteSouffleQueue(item, manualMessage=true){ ChatMessage.create({ whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), diff --git a/module/grammar.js b/module/grammar.js index df4162a2..7a35cebc 100644 --- a/module/grammar.js +++ b/module/grammar.js @@ -1,11 +1,12 @@ const articlesApostrophes = { - 'de' : 'd\'', - 'le' : 'l\'', - 'la' : 'l\'' + 'de': 'd\'', + 'le': 'l\'', + 'la': 'l\'' } export class Grammar { + /* -------------------------------------------- */ static apostrophe(article, word) { if (articlesApostrophes[article] && Grammar.startsWithVoyel(word)) { return articlesApostrophes[article] + word @@ -13,28 +14,58 @@ export class Grammar { return article + ' ' + word; } + /* -------------------------------------------- */ static startsWithVoyel(word) { return word.match(/^[aeiouy]/i) } + /* -------------------------------------------- */ static toLowerCaseNoAccent(words) { return words?.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") ?? words; } + + /* -------------------------------------------- */ static articleDetermine(genre) { - switch (genre?.toLowerCase()) { + switch (Grammar.toLowerCaseNoAccent(genre)) { case 'f': case 'feminin': return 'la'; - case 'p': case 'pluriel': return 'les'; + case 'p': case 'mp': case 'fp': case 'pluriel': return 'les'; default: case 'm': case 'masculin': return 'le'; } } - static articleIndétermine(genre) { - switch (genre?.toLowerCase()) { + + /* -------------------------------------------- */ + static articleIndetermine(genre) { + switch (Grammar.toLowerCaseNoAccent(genre)) { case 'f': case 'feminin': return 'une'; - case 'p': case 'pluriel': return 'des'; + case 'p': case 'fp': case 'mp': case 'pluriel': return 'des'; case 'n': case 'neutre': return 'du' default: case 'm': case 'masculin': return 'un'; } } + + /* -------------------------------------------- */ + /** + * renvoie un des mots en fonction du genre: + * + * - masculin/neutre/m/n : mots[0] + * - feminin/f : mots[1] + * - pluriel/mp/p : mots[2] + * - fp : mots[3] + * + * @param {*} genre + * @param {...any} mots + */ + static accord(genre, ...mots) { + switch (Grammar.toLowerCaseNoAccent(genre)) { + default: + case 'n': case 'neutre': + case 'm': case 'masculin': return mots[0]; + case 'f': case 'feminin': return mots[1]; + case 'p': case 'mp': case 'pluriel': return mots[2] + case 'fp': return mots[3]; + } + } + } \ No newline at end of file diff --git a/module/item-arme.js b/module/item-arme.js index 048543f0..68e3cafb 100644 --- a/module/item-arme.js +++ b/module/item-arme.js @@ -145,7 +145,7 @@ export class RdDItemArme extends Item { } static isArmeUtilisable(item) { - return item.type == 'arme' && item.data.resistance > 0; + return item.type == 'arme' && (item.data.resistance > 0 || item.data.portee_courte>0); } static mainsNues(actorData={}) { diff --git a/module/poetique.js b/module/poetique.js index 58127bf8..e448d6e7 100644 --- a/module/poetique.js +++ b/module/poetique.js @@ -4,25 +4,25 @@ const poesieHautReve = [ { reference: 'Le Ratier Bretonien', extrait: `Le courant du Fleuve -
Te domine et te Porte -
Avant que tu te moeuves -
Combat le, ou il t'emporte` +
Te domine et te Porte +
Avant que tu te moeuves +
Combat le, ou il t'emporte` }, { reference: 'Incompatibilité, Charles Beaudelaire', extrait: `Et lorsque par hasard une nuée errante -
Assombrit dans son vol le lac silencieux, -
On croirait voir la robe ou l'ombre transparente -
D'un esprit qui voyage et passe dans les cieux.` +
Assombrit dans son vol le lac silencieux, +
On croirait voir la robe ou l'ombre transparente +
D'un esprit qui voyage et passe dans les cieux.` }, { reference: 'Au fleuve de Loire, Joachim du Bellay', extrait: `Ô de qui la vive course -
Prend sa bienheureuse source, -
D’une argentine fontaine, -
Qui d’une fuite lointaine, -
Te rends au sein fluctueux -
De l’Océan monstrueux` +
Prend sa bienheureuse source, +
D’une argentine fontaine, +
Qui d’une fuite lointaine, +
Te rends au sein fluctueux +
De l’Océan monstrueux` }, { reference: 'Denis Gerfaud', @@ -61,10 +61,188 @@ const poesieHautReve = [ Nul ne sait qui est le créateur des Dragons, ni qui est leur maître. Mais l'on peut supposer qui est le maître du Rêve des Dragons, c'est Oniros»` }, + { + reference: "La chevelure, Charles Baudelaire", + extrait: `J'irai là-bas où l'arbre et l'homme, pleins de sève, +
Se pâment longuement sous l'ardeur des climats ; +
Fortes tresses, soyez la houle qui m'enlève !` + }, + { + reference: "Rêve de Dragon, Denis Gerfaud", + extrait: `En réalité, tous les éléments du rêve des Dragons expriment + le Draconic : chaque pierre, chaque fleur, chaque goutte d'eau, + chaque nuage est porteur d'un message dans la langue des Dragons` + }, + { + reference: "Femmes damnées (2), Charles Baudelaire", + extrait: `Comme je descendais des Fleuves impassibles, +
Je ne me sentis plus guidé par les haleurs : +
Des Peaux-Rouges criards les avaient pris pour cibles, +
Les ayant cloués nus aux poteaux de couleurs.` + }, + { + reference: "Le bateau ivre, Arthur Rimbaud", + extrait: `Loin des peuples vivants, errantes, condamnées, +
A travers les déserts courez comme les loups ; +
Faites votre destin, âmes désordonnées, +
Et fuyez l'infini que vous portez en vous !` + }, + { + reference: "L'Ennemi, Charles Baudelaire", + extrait: `Et qui sait si les fleurs nouvelles que je rêve +
Trouveront dans ce sol lavé comme une grève +
Le mystique aliment qui ferait leur vigueur ?` + }, + { + reference: "Une charogne, Charles Baudelaire", + extrait: `Et le ciel regardait la carcasse superbe +
Comme une fleur s'épanouir. +
La puanteur était si forte, que sur l'herbe +
Vous crûtes vous évanouir.` + }, + { + reference: "Conseil, Victor Hugo", + extrait: `Rois ! la bure est souvent jalouse du velours. +
Le peuple a froid l'hiver, le peuple a faim toujours. +
Rendez-lui son sort plus facile. +
Le peuple souvent porte un bien rude collier. +
Ouvrez l'école aux fils, aux pères l'atelier, +
À tous vos bras, auguste asile !` + }, + { + reference: "El Desdichado, Gérard de Nerval", + extrait: `Suis-je Amour ou Phébus ?... Lusignan ou Biron ? +
Mon front est rouge encor du baiser de la Reine ; +
J'ai rêvé dans la Grotte où nage la sirène...` + }, + { + reference: "Caligula - IIIème chant, Gérard de Nerval", + extrait: `Allez, que le caprice emporte +
Chaque âme selon son désir, +
Et que, close après vous, la porte +
Ne se rouvre plus qu'au plaisir.` + }, + { + reference: "Rêve de Dragon, Denis Gerfaud", + extrait: `Les sages ont encore coutume de dire : +
« Mais comment les Dragons peuvent-ils + être influencés par une créature qui, tout + bien considéré, n'existe pas vraiment pour eux, + qui n'est que le fantasme de leur activité nocturne ? »` + }, + { + reference: "Rêve de Dragon, Denis Gerfaud", + extrait: `La légende affirme que ce sont les Gnomes qui furent + les premiers haut-rêvants. En observant les pierres précieuses, + les gemmes qui sont les larmes de joie des Dragons, ils parvinrent à + en comprendre la langue. Et l'ayant comprise, ils purent s'en servir + pour influencer le cours du rêve`, + }, + { + reference: "Quand le rêve se brise, Cypora Sebagh", + extrait: `Quand le rêve se brise, +
Dans la plainte du jour, +
Ma mémoire devient grise +
Et sombre, tour à tour, +
Dans le puits du silence +
Et de la solitude ; +
Elle reprend son errance +
Parmi la multitude.` + } + , + { + reference: "Une charogne, Charles Baudelaire", + extrait: `Les formes s'effaçaient et n'étaient plus qu'un rêve, +
Une ébauche lente à venir +
Sur la toile oubliée, et que l'artiste achève +
Seulement par le souvenir.` + }, + { + reference: "La chevelure, Charles Baudelaire", + extrait: `Longtemps ! toujours ! ma main dans ta crinière lourde +
Sèmera le rubis, la perle et le saphir, +
Afin qu'à mon désir tu ne sois jamais sourde ! +
N'es-tu pas l'oasis où je rêve, et la gourde +
Où je hume à longs traits le vin du souvenir` + }, + { + reference: "Un Fou et un Sage, Jean de La Fontaine", + extrait: `Certain Fou poursuivait à coups de pierre un Sage. +
Le Sage se retourne et lui dit : Mon ami, +
C'est fort bien fait à toi ; reçois cet écu-ci : +
Tu fatigues assez pour gagner davantage.` + }, + { + reference: "Guitare, Victor Hugo", + extrait: `Je la voyais passer de ma demeure, +
Et c'était tout. +
Mais à présent je m'ennuie à toute heure, +
Plein de dégoût, +
Rêveur oisif, l'âme dans la campagne, +
La dague au clou ... – +
Le vent qui vient à travers la montagne +
M'a rendu fou !` + }, + { + reference: "Rêve de Dragon, Denis Gerfaud", + extrait: `Le Premier Âge fut appelé l'Âge des Dragons. Ce fut le commencement + des temps, le commencement des rêves. Durant cette période plus mythique + que réellement historique, les Dragons aimaient à se rêver eux-mêmes.` + }, + { + reference: "Les Djinns, Victor Hugo", + extrait: `C'est l'essaim des Djinns qui passe, +
Et tourbillonne en sifflant ! +
Les ifs, que leur vol fracasse, +
Craquent comme un pin brûlant.`}, + { + reference: "Rêve de Dragon, Denis Gerfaud", + extrait: `Car le Second Âge fut bel et bien celui des Magiciens. Durant cette période, les + Gnomes s'enfoncèrent profondément sous les montagnes et la magie passa aux + mains des Humains qui en usèrent et abusèrent, se croyant devenus les maîtres du monde` + }, + { + reference: "Lily, Pierre Perret", + extrait: `Elle aurait pas cru sans le voir +
Que la couleur du désespoir +
Là-bas aussi ce fût le noir.` + }, + + { + reference: "Qu'est-ce de votre vie ? une bouteille molle, Jean-Baptiste Chassignet", + extrait: `Qu'est-ce de votre vie ? un tourbillon rouant +
De fumière à flot gris, parmi l'air se jouant, +
Qui passe plus soudain que foudre meurtrière.` + }, + { + reference: "Les Djinns, poème Victor Hugo", + extrait: `Cris de l'enfer! voix qui hurle et qui pleure ! +
L'horrible essaim, poussé par l'aquilon, +
Sans doute, ô ciel ! s'abat sur ma demeure. +
Le mur fléchit sous le noir bataillon. +
La maison crie et chancelle penchée, +
Et l'on dirait que, du sol arrachée, +
Ainsi qu'il chasse une feuille séchée, +
Le vent la roule avec leur tourbillon !` + }, + { + reference: "Rêve de Dragon, Denis Gerfaud", + extrait: `Le monde est Rêve de Dragons, mais nous ne savons +
ni leur apparence ni qui sont les dragons. +
En dépit de l'iconographie qui les clame +
immenses créatures ailées crachant des flammes` + }, + { + reference: "El Desdichado, Gérard de Nerval", + extrait: `Je suis le Ténébreux, – le Veuf, – l'Inconsolé, +
Le Prince d'Aquitaine à la Tour abolie : +
Ma seule Etoile est morte, – et mon luth constellé +
Porte le Soleil noir de la Mélancolie.` + }, ] export class Poetique { - static getExtrait(){ + static getExtrait() { return Misc.rollOneOf(poesieHautReve); } diff --git a/module/rdd-combat.js b/module/rdd-combat.js index f78bfc02..dafda8ed 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -10,19 +10,47 @@ import { RdDRollTables } from "./rdd-rolltables.js"; import { ReglesOptionelles } from "./regles-optionelles.js"; /* -------------------------------------------- */ -export class RdDCombatManager extends Combat { +const premierRoundInit = [ + { pattern: 'hast', init: 3.90 }, + { pattern: 'lance', init: 3.85 }, + { pattern: 'baton', init: 3.80 }, + { pattern: 'doubledragonne', init: 3.75 }, + { pattern: 'esparlongue', init: 3.70 }, + { pattern: 'epeedragonne', init: 3.65 }, + { pattern: 'epeebatarde', init: 3.60 }, + { pattern: 'epeecyane', init: 3.55 }, + { pattern: 'epeesorde', init: 3.50 }, + { pattern: 'grandehache', init: 3.45 }, + { pattern: 'bataille', init: 3.40 }, + { pattern: 'epeegnome', init: 3.35 }, + { pattern: 'masse', init: 3.30 }, + { pattern: 'gourdin', init: 3.25 }, + { pattern: 'fléau', init: 3.20 }, + { pattern: 'dague', init: 3.15 }, + { pattern: 'autre', init: 3.10 }, +]; + +/* -------------------------------------------- */ +export class RdDCombatManager extends Combat { + + static init() { + /* -------------------------------------------- */ + Hooks.on("getCombatTrackerEntryContext", (html, options) => { + RdDCombatManager.pushInitiativeOptions(html, options); + }); + } /* -------------------------------------------- */ cleanItemUse() { - for(let turn of this.turns) { + for (let turn of this.turns) { turn.actor.resetItemUse() } } - + /* -------------------------------------------- */ - cleanSonne( ) { + cleanSonne() { for (let combatant of this.data.combatants) { - combatant.actor.verifierSonneRound( this.current.round ); + combatant.actor.verifierSonneRound(this.current.round); } } @@ -31,7 +59,269 @@ export class RdDCombatManager extends Combat { //console.log('New round !');s this.cleanItemUse(); this.cleanSonne(); + return super.nextRound(); } + + /************************************************************************************/ + async rollInitiative(ids, formula = undefined, messageOptions = {}) { + console.log(`${game.data.system.data.title} | Combat.rollInitiative()`, ids, formula, messageOptions); + // Structure input data + ids = typeof ids === "string" ? [ids] : ids; + const currentId = this.combatant._id; + // calculate initiative + for (let cId = 0; cId < ids.length; cId++) { + const c = this.getCombatant(ids[cId]); + //if (!c) return results; + + let rollFormula = formula; // Init per default + if (!rollFormula) { + let armeCombat, competence; + if (c.actor.data.type == 'creature' || c.actor.data.type == 'entite') { + for (const competenceItem of c.actor.data.items) { + if (competenceItem.data.iscombat) { + competence = duplicate(competenceItem); + } + } + rollFormula = "2+( (" + RdDCombatManager.calculInitiative(competence.data.niveau, competence.data.carac_value) + ")/100)"; + } else { + for (const item of c.actor.data.items) { + if (item.type == "arme" && item.data.equipe) { + armeCombat = duplicate(item); + } + } + let compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.data.competence; + competence = RdDItemCompetence.findCompetence(c.actor.data.items, compName); + let bonusEcaille = (armeCombat && armeCombat.data.magique) ? armeCombat.data.ecaille_efficacite : 0; + rollFormula = "2+( (" + RdDCombatManager.calculInitiative(competence.data.niveau, c.actor.data.data.carac[competence.data.defaut_carac].value, bonusEcaille) + ")/100)"; + } + } + //console.log("Combatat", c); + const roll = super._getInitiativeRoll(c, rollFormula); + if (roll.total <= 0) roll.total = 0.00; + console.log("Compute init for", rollFormula, roll.total); + await this.updateEmbeddedEntity("Combatant", { _id: c._id, initiative: roll.total }); + + // Send a chat message + let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode"); + let messageData = mergeObject( + { + speaker: { + scene: canvas.scene._id, + actor: c.actor ? c.actor._id : null, + token: c.token._id, + alias: c.token.name, + sound: CONFIG.sounds.dice, + }, + flavor: `${c.token.name} a fait son jet d'Initiative (${messageOptions.initInfo}) +
+ `, + }, + messageOptions + ); + roll.toMessage(messageData, { rollMode, create: true }); + + RdDCombatManager.processPremierRoundInit(); + } + return this; + }; + + /* -------------------------------------------- */ + static calculInitiative(niveau, caracValue, bonusEcaille = 0) { + let base = niveau + Math.floor(caracValue / 2); + base += bonusEcaille; + return "1d6" + (base >= 0 ? "+" : "") + base; + } + + /* -------------------------------------------- */ + /** Retourne une liste triée d'armes avec le split arme1 main / arme 2 main */ + static finalizeArmeList(armes, competences, carac) { + // Gestion des armes 1/2 mains + let armesEquipe = []; + for (const arme of armes) { + if (arme.data.equipe) { + armesEquipe.push(arme); + let comp = competences.find(c => c.name == arme.data.competence); + arme.data.initiative = RdDCombatManager.calculInitiative(arme.data.niveau, carac[comp.data.defaut_carac].value); + // Dupliquer les armes pouvant être à 1 main et 2 mains en patchant la compétence + if (arme.data.unemain && !arme.data.deuxmains) { + arme.data.mainInfo = "(1m)"; + } else if (!arme.data.unemain && arme.data.deuxmains) { + arme.data.mainInfo = "(2m)"; + } else if (arme.data.unemain && arme.data.deuxmains) { + arme.data.mainInfo = "(1m)"; + let arme2main = duplicate(arme); + arme2main.data.mainInfo = "(2m)"; + arme2main.data.dommages = arme2main.data.dommages.split("/")[1]; // Existence temporaire uniquement dans la liste des armes, donc OK + arme2main.data.competence = arme2main.data.competence.replace(" 1 main", " 2 mains"); // Replace ! + let comp = competences.find(c => c.name == arme2main.data.competence); + arme2main.data.niveau = comp.data.niveau; + arme2main.data.initiative = RdDCombatManager.calculInitiative(arme2main.data.niveau, carac[comp.data.defaut_carac].value); + armesEquipe.push(arme2main); + } + } + } + return armesEquipe.sort((a, b) => { + const nameA = a.name + (a.data.mainInfo ?? ''); + const nameB = b.name + (b.data.mainInfo ?? ''); + if (nameA > nameB) return 1; + if (nameA < nameB) return -1; + return 0; + }); + } + + /* -------------------------------------------- */ + static buildListeActionsCombat(combatant) { + const actor = combatant.actor; // Easy access + let items = actor.data.items; + let actions = [] + if (actor.isCreature()) { + actions = actions.concat(items.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it)) + .map(competence => RdDItemCompetenceCreature.toArme(competence))); + } else { + // Recupération des items 'arme' + let armes = items.filter(it => RdDItemArme.isArmeUtilisable(it)) + .map(arme => duplicate(arme)) /* pas de changements aux armes d'origine */ + .concat(RdDItemArme.mainsNues()); + + let competences = items.filter(it => it.type == 'competence'); + actions = actions.concat(RdDCombatManager.finalizeArmeList(armes, competences, actor.data.data.carac)); + + actions.push({ name: "Draconic", data: { initOnly: true, competence: "Draconic" } }); + } + + actions.push({ name: "Autre action", data: { initOnly: true, competence: "Autre action" } }); + for (let index = 0; index < actions.length; index++) { + actions[index].index = index; + } + return actions; + } + + /* -------------------------------------------- */ + static processPremierRoundInit() { + // Check if we have the whole init ! + if (game.user.isGM && game.combat.current.round == 1) { + let initMissing = game.combat.data.combatants.find(it => !it.initiative); + if (!initMissing) { // Premier round ! + for (let combatant of game.combat.data.combatants) { + let arme = combatant.initiativeData?.arme; + //console.log("Parsed !!!", combatant, initDone, game.combat.current, arme); + if (arme && arme.type == "arme") { + for (let initData of premierRoundInit) { + if (arme.data.initpremierround.toLowerCase().includes(initData.pattern)) { + let msg = `

L'initiative de ${combatant.actor.name} a été modifiée !

+
+
+ Etant donné son ${arme.name}, son initative pour ce premier round est désormais de ${initData.init}. +
` + ChatMessage.create({ content: msg }); + game.combat.setInitiative(combatant._id, initData.init); + } + } + } + } + } + } + } + + /* -------------------------------------------- */ + static incDecInit(combatantId, incDecValue) { + const combatant = game.combat.getCombatant(combatantId); + let initValue = combatant.initiative + incDecValue; + game.combat.setInitiative(combatantId, initValue); + } + + /* -------------------------------------------- */ + static pushInitiativeOptions(html, options) { + for (let i = 0; i < options.length; i++) { + let option = options[i]; + if (option.name == 'COMBAT.CombatantReroll') { // Replace ! + option.name = "Sélectionner l'initiative..."; + option.condition = true; + option.icon = ''; + option.callback = target => { + RdDCombatManager.displayInitiativeMenu(html, target.data('combatant-id')); + } + } + } + options = [ + { name: "Incrémenter initiative", condition: true, icon: '', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +0.01); } }, + { name: "Décrémenter initiative", condition: true, icon: '', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), -0.01); } } + ].concat(options); + } + /* -------------------------------------------- */ + static rollInitiativeCompetence(combatantId, arme) { + const combatant = game.combat.getCombatant(combatantId); + const actor = combatant.actor; + + let initInfo = ""; + let initOffset = 0; + let caracForInit = 0; + let compNiveau = 0; + let competence = { name: "Aucune" }; + if (actor.getSurprise() == "totale") { + initOffset = -1; // To force 0 + initInfo = "Surprise Totale" + } else if (actor.getSurprise() == "demi") { + initOffset = 0; + initInfo = "Demi Surprise" + } else if (arme.name == "Autre action") { + initOffset = 2; + initInfo = "Autre Action" + } else if (arme.name == "Draconic") { + initOffset = 7; + initInfo = "Draconic" + } else { + initOffset = 3; // Melée = 3.XX + competence = RdDItemCompetence.findCompetence(combatant.actor.data.items, arme.data.competence); + compNiveau = competence.data.niveau; + initInfo = arme.name + " / " + arme.data.competence; + + if (actor.data.type == 'creature' || actor.data.type == 'entite') { + caracForInit = competence.data.carac_value; + if (competence.data.categorie == "lancer") { + initOffset = 5; + } + } else { + caracForInit = actor.data.data.carac[competence.data.defaut_carac].value; + if (competence.data.categorie == "lancer") { // Offset de principe pour les armes de jet + initOffset = 4; + } + if (competence.data.categorie == "tir") { // Offset de principe pour les armes de jet + initOffset = 5; + } + if (competence.data.categorie == "melee") { // Offset de principe pour les armes de jet + initOffset = 3; + } + } + } + let malus = actor.getEtatGeneral(); // Prise en compte état général + // Cas des créatures et entités vs personnages + let rollFormula = initOffset + "+ ( (" + RdDCombatManager.calculInitiative(compNiveau, caracForInit) + " + " + malus + ") /100)"; + // Garder la trace de l'arme/compétence utilisée pour l'iniative + combatant.initiativeData = { arme: arme } // pour reclasser l'init au round 0 + game.combat.rollInitiative(combatantId, rollFormula, { initInfo: initInfo }); + } + + /* -------------------------------------------- */ + static displayInitiativeMenu(html, combatantId) { + console.log("Combatant ; ", combatantId); + const combatant = game.combat.getCombatant(combatantId); + let armesList = RdDCombatManager.buildListeActionsCombat(combatant); + + // Build the relevant submenu + if (armesList) { + let menuItems = []; + for (let arme of armesList) { + menuItems.push({ + name: arme.data.competence, + icon: "", + callback: target => { RdDCombatManager.rollInitiativeCompetence(combatantId, arme) } + }); + } + new ContextMenu(html, ".directory-list", menuItems).render(); + } + } + } /* -------------------------------------------- */ @@ -384,8 +674,8 @@ export class RdDCombat { let rollData = this._prepareAttaque(competence, arme); console.log("RdDCombat.attaque >>>", rollData); - this.attacker.incItemUse( arme._id ); // Usage - this.attacker.verifierForceMin( arme ); + this.attacker.incItemUse(arme._id); // Usage + this.attacker.verifierForceMin(arme); const dialog = await RdDRoll.create(this.attacker, rollData, { @@ -435,6 +725,7 @@ export class RdDCombat { /* -------------------------------------------- */ async _onAttaqueParticuliere(rollData) { RdDCombat._storeAttaque(this.attackerId, rollData); + this.attacker.decItemUse( rollData.arme._id ); // Usage décrémenté sur particulière // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum const isMeleeDiffNegative = rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0; @@ -490,7 +781,7 @@ export class RdDCombat { let esquiveUsage = 0; let esquive = this.defender.getCompetence("esquive"); if (esquive) { - esquiveUsage = this.defender.getItemUse( esquive._id); + esquiveUsage = this.defender.getItemUse(esquive._id); } const paramChatDefense = { @@ -550,8 +841,8 @@ export class RdDCombat { _filterArmesParade(defender, competence) { let items = defender.data.items; items = items.filter(item => RdDItemArme.isArmeUtilisable(item) || RdDItemCompetenceCreature.isCompetenceParade(item)); - for( let item of items) { - item.data.nbUsage = defender.getItemUse( item._id); // Ajout du # d'utilisation ce round + for (let item of items) { + item.data.nbUsage = defender.getItemUse(item._id); // Ajout du # d'utilisation ce round } switch (competence.data.categorie) { case 'tir': @@ -571,9 +862,8 @@ export class RdDCombat { RdDCombat._storeAttaque(this.attackerId, attackerRoll); - // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum ChatMessage.create({ - whisper: ChatMessage.getWhisperRecipients(this.attacker.name), + whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name), content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-etotal.html', { attackerId: this.attackerId, attacker: this.attacker, @@ -591,7 +881,7 @@ export class RdDCombat { const avecArme = arme?.data.categorie_parade != 'sans-armes'; const action = (rollData.attackerRoll ? (arme ? "la parade" : "l'esquive") : "l'attaque"); ChatUtility.createChatWithRollMode(this.defender.name, { - content: `Echec total à ${action}! ` + await RdDRollTables.getMaladresse({ arme: avecArme }) + content: `Maladresse à ${action}! ` + await RdDRollTables.getMaladresse({ arme: avecArme }) }); } @@ -616,7 +906,7 @@ export class RdDCombat { let arme = this.defender.getArmeParade(armeParadeId); console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme); - this.defender.incItemUse( armeParadeId ); // Usage + this.defender.incItemUse(armeParadeId); // Usage let rollData = this._prepareParade(attackerRoll, arme); @@ -724,7 +1014,7 @@ export class RdDCombat { } console.log("RdDCombat.esquive >>>", attackerRoll, esquive); let rollData = this._prepareEsquive(attackerRoll, esquive); - this.defender.incItemUse( esquive._id ); // Usage + this.defender.incItemUse(esquive._id); // Usage const dialog = await RdDRoll.create(this.defender, rollData, { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html' }, { @@ -803,10 +1093,10 @@ export class RdDCombat { const dmg = attackerRoll.dmg.dmgArme + attackerRoll.dmg.dmgActor; let arme = defenderRoll.arme; let msg = ""; - if ( arme.data.magique ) { + if (arme.data.magique) { defenderRoll.show.deteriorationArme = 'resiste'; // Par défaut if (arme.data.resistance_magique == undefined) arme.data.resistance_magique = 0; // Quick fix - if ( dmg > arme.data.resistance_magique) { // Jet uniquement si dommages supérieur à résistance magique (cf. 274) + if (dmg > arme.data.resistance_magique) { // Jet uniquement si dommages supérieur à résistance magique (cf. 274) let resistance = Misc.toInt(arme.data.resistance); // Jet de résistance de l'arme de parade (p.132) let resistRoll = await RdDResolutionTable.rollData({ @@ -814,14 +1104,14 @@ export class RdDCombat { finalLevel: - dmg, showDice: false }); - if ( !resistRoll.rolled.isSuccess) { - let perteResistance = ( dmg - arme.data.resistance_magique) + if (!resistRoll.rolled.isSuccess) { + let perteResistance = (dmg - arme.data.resistance_magique) resistance -= perteResistance; - defenderRoll.show.deteriorationArme = resistance <= 0 ? 'brise': 'perte'; + defenderRoll.show.deteriorationArme = resistance <= 0 ? 'brise' : 'perte'; defenderRoll.show.perteResistance = perteResistance; this.defender.updateEmbeddedEntity("OwnedItem", { _id: defenderRoll.arme._id, 'data.resistance': resistance }); } - } + } } else { let resistance = Misc.toInt(arme.data.resistance); // Jet de résistance de l'arme de parade (p.132) @@ -834,7 +1124,7 @@ export class RdDCombat { defenderRoll.show.deteriorationArme = 'resiste'; } else { resistance -= dmg; - defenderRoll.show.deteriorationArme = resistance <= 0 ? 'brise': 'perte'; + defenderRoll.show.deteriorationArme = resistance <= 0 ? 'brise' : 'perte'; defenderRoll.show.perteResistance = dmg; this.defender.updateEmbeddedEntity("OwnedItem", { _id: defenderRoll.arme._id, 'data.resistance': resistance }); } diff --git a/module/rdd-commands.js b/module/rdd-commands.js index b085c758..a10392ff 100644 --- a/module/rdd-commands.js +++ b/module/rdd-commands.js @@ -23,6 +23,8 @@ export class RdDCommands { rddCommands.registerCommand({ path: ["/aide"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" }); rddCommands.registerCommand({ path: ["/help"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" }); rddCommands.registerCommand({ path: ["/table", "queues"], func: (content, msg, params) => RdDRollTables.getQueue(true), descr: "Tire une Queue de Dragon" }); + rddCommands.registerCommand({ path: ["/table", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe(true), descr: "Tire une Idée fixe" }); + rddCommands.registerCommand({ path: ["/table", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant(true), descr: "Tire un Désir Lancinant" }); rddCommands.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre(true), descr: "Tire une Ombre de Dragon" }); rddCommands.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR(true), descr: "Tire une Tête de Dragon pour Hauts Revants" }); rddCommands.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete(true), descr: "Tire une Tête de Dragon" }); diff --git a/module/rdd-main.js b/module/rdd-main.js index 72ff8b3f..1c45db7a 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -34,77 +34,7 @@ import { EffetsDraconiques } from "./tmr/effets-draconiques.js"; /* Foundry VTT Initialization */ /* -------------------------------------------- */ -/************************************************************************************/ -const _patch_initiative = () => { - Combat.prototype.rollInitiative = async function ( - ids, - formula = undefined, - messageOptions = {} - ) { - console.log( - `${game.data.system.data.title} | Combat.rollInitiative()`, - ids, - formula, - messageOptions - ); - // Structure input data - ids = typeof ids === "string" ? [ids] : ids; - const currentId = this.combatant._id; - // calculate initiative - for (let cId = 0; cId < ids.length; cId++) { - const c = this.getCombatant(ids[cId]); - //if (!c) return results; - let rollFormula = formula; // Init per default - if (!rollFormula) { - let armeCombat, competence; - if (c.actor.data.type == 'creature' || c.actor.data.type == 'entite') { - for (const competenceItem of c.actor.data.items) { - if (competenceItem.data.iscombat) { - competence = duplicate(competenceItem); - } - } - rollFormula = "2+( ("+RdDUtility.calculInitiative(competence.data.niveau, competence.data.carac_value)+")/100)"; - } else { - for (const item of c.actor.data.items) { - if (item.type == "arme" && item.data.equipe) { - armeCombat = duplicate(item); - } - } - let compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.data.competence; - competence = RdDItemCompetence.findCompetence(c.actor.data.items, compName); - let bonusEcaille = (armeCombat && armeCombat.data.magique) ? armeCombat.data.ecaille_efficacite : 0; - rollFormula = "2+( ("+RdDUtility.calculInitiative(competence.data.niveau, c.actor.data.data.carac[competence.data.defaut_carac].value, bonusEcaille) + ")/100)"; - } - } - //console.log("Combatat", c); - const roll = this._getInitiativeRoll(c, rollFormula); - if (roll.total <= 0) roll.total = 0.00; - console.log("Compute init for", rollFormula, roll.total); - await this.updateEmbeddedEntity("Combatant", { _id: c._id, initiative: roll.total }); - - // Send a chat message - let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode"); - let messageData = mergeObject( - { - speaker: { - scene: canvas.scene._id, - actor: c.actor ? c.actor._id : null, - token: c.token._id, - alias: c.token.name, - sound: CONFIG.sounds.dice, - }, - flavor: `${c.token.name} a fait son jet d'Initiative (${messageOptions.initInfo})`, - }, - messageOptions - ); - roll.toMessage(messageData, { rollMode, create: true }); - - RdDUtility.processPremierRoundInit( ); - } - return this; - }; -} /************************************************************************************/ Hooks.once("init", async function () { @@ -224,15 +154,10 @@ Hooks.once("init", async function () { //CONFIG.Combat.entityClass = RdDCombatManager; CONFIG.Combat.documentClass = RdDCombatManager; - // Handlebar function pour container - Handlebars.registerHelper('buildConteneur', (objet) => { return RdDUtility.buildConteneur(objet); }); - - // Patch the initiative formula - _patch_initiative(); - // préparation des différents modules RdDCommands.init(); RdDCombat.init(); + RdDCombatManager.init(), RdDTokenHud.init(); RdDActor.init(); RddCompendiumOrganiser.init(); @@ -301,10 +226,6 @@ Hooks.on("chatMessage", (html, content, msg) => { return true; }); -/* -------------------------------------------- */ -Hooks.on("getCombatTrackerEntryContext", (html, options) => { - RdDUtility.pushInitiativeOptions(html, options); -}); /* -------------------------------------------- */ Hooks.on("renderChatMessage", async (app, html, msg) => { diff --git a/module/rdd-resolution-table.js b/module/rdd-resolution-table.js index e020deaf..ca5376a9 100644 --- a/module/rdd-resolution-table.js +++ b/module/rdd-resolution-table.js @@ -125,7 +125,7 @@ export class RdDResolutionTable { /* -------------------------------------------- */ static _updateChancesFactor(chances, diviseur) { - if (diviseur && diviseur > 1) { + if (chances.level > -11 && diviseur && diviseur > 1) { let newScore = Math.floor(chances.score / diviseur); mergeObject(chances, this._computeCell(null, newScore), { overwrite: true }); } diff --git a/module/rdd-roll.js b/module/rdd-roll.js index f08a5eb8..11a83bf4 100644 --- a/module/rdd-roll.js +++ b/module/rdd-roll.js @@ -18,6 +18,12 @@ export class RdDRoll extends Dialog { /* -------------------------------------------- */ static async create(actor, rollData, dialogConfig, ...actions) { + if (actor.isRollWindowsOpened() ) { + ui.notifications.warn("Vous avez déja une fenêtre de Test ouverte, il faut la fermer avant d'en ouvrir une autre.") + return; + } + actor.setRollWindowsOpened(true); + RdDRoll._ensureCorrectActions(actions); RdDRoll._setDefaultOptions(actor, rollData); @@ -102,6 +108,7 @@ export class RdDRoll extends Dialog { close() { if (this.rollData.canClose) { + this.actor.setRollWindowsOpened(false); return super.close(); } ui.notifications.info("Vous devez faire ce jet de dés!"); @@ -112,7 +119,8 @@ export class RdDRoll extends Dialog { async onAction(action, html) { await RdDResolutionTable.rollData(this.rollData); console.log("RdDRoll -=>", this.rollData, this.rollData.rolled); - + this.actor.setRollWindowsOpened(false); + if (action.callbacks) for (let callback of action.callbacks) { if (callback.condition == undefined || callback.condition(this.rollData)) { diff --git a/module/rdd-rolltables.js b/module/rdd-rolltables.js index 61b1fb19..74d06152 100644 --- a/module/rdd-rolltables.js +++ b/module/rdd-rolltables.js @@ -48,21 +48,29 @@ export class RdDRollTables { static async getQueue(toChat = false) { let queue = await RdDRollTables.drawItemFromRollTable("Queues de dragon", toChat); if (queue.name.toLowerCase().includes('lancinant') ) { - queue = await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat); + return await RdDRollTables.getDesirLancinant(toChat); } if (queue.name.toLowerCase().includes('fixe') ) { - queue = await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat); + return await RdDRollTables.getIdeeFixe(toChat); } return queue; } - /* -------------------------------------------- */ - static async getTete(toChat = false) { - return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour haut-rêvants", toChat); + static async getDesirLancinant(toChat = false) { + return await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat); + } + + static async getIdeeFixe(toChat = false) { + return await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat); } /* -------------------------------------------- */ static async getTeteHR(toChat = false) { + return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour haut-rêvants", toChat); + } + + /* -------------------------------------------- */ + static async getTete(toChat = false) { return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour tous personnages", toChat); } diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index 6677aad8..2312b1d7 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -108,6 +108,11 @@ export class RdDTMRDialog extends Dialog { this._createTokens(); } + removeToken(tmr, casetmr) { + this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id); + this.updateTokens() + } + /* -------------------------------------------- */ _getTokensCasesTmr() { return this.casesSpeciales.map(c => this._tokenCaseSpeciale(c)).filter(token => token); @@ -142,7 +147,7 @@ export class RdDTMRDialog extends Dialog { async activateListeners(html) { super.activateListeners(html); - document.getElementById("tmrrow1").insertCell(1).append(this.pixiApp.view); + document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view); if (this.viewOnly) { html.find('#lancer-sort').remove(); @@ -168,7 +173,6 @@ export class RdDTMRDialog extends Dialog { let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord); await this.manageRencontre(tmr, () => { this.postRencontre(tmr); - this.actor.displayTMRQueueSouffleInformation(); }); } @@ -498,9 +502,7 @@ export class RdDTMRDialog extends Dialog { } async _resultatMaitriseCaseHumide(rollData) { - if (rollData.rolled.isETotal) { - rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); - } + await this.souffleSiEchecTotal(rollData); this.toclose = rollData.rolled.isEchec; if (rollData.rolled.isSuccess && rollData.double) { rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements }; @@ -518,6 +520,12 @@ export class RdDTMRDialog extends Dialog { } } + async souffleSiEchecTotal(rollData) { + if (rollData.rolled.isETotal) { + rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); + } + } + /* -------------------------------------------- */ isCaseHumide(tmr) { if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) { @@ -558,16 +566,29 @@ export class RdDTMRDialog extends Dialog { await this._conquerir(tmr, { difficulte: -9, action: 'Conquérir la cité', - onConqueteReussie: r => EffetsDraconiques.fermetureCites.onConquete(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), - onConqueteEchec: r => this.close(), + onConqueteReussie: r => EffetsDraconiques.fermetureCites.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => { + this.souffleSiEchecTotal(rollData); + this.close() + }, canClose: false }); } } - - removeToken(tmr, casetmr) { - this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id); - this.updateTokens() + /* -------------------------------------------- */ + async purifierPeriple(tmr) { + if (EffetsDraconiques.periple.find(this.casesSpeciales, tmr.coord)) { + await this._conquerir(tmr, { + difficulte: EffetsDraconiques.periple.getDifficulte(tmr), + action: 'Purifier ' + TMRUtility.getTMRDescr(tmr.coord), + onConqueteReussie: r => EffetsDraconiques.periple.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => { + this.souffleSiEchecTotal(rollData); + this.close() + }, + canClose: false + }); + } } /* -------------------------------------------- */ @@ -576,8 +597,8 @@ export class RdDTMRDialog extends Dialog { await this._conquerir(tmr, { difficulte: -7, action: 'Conquérir', - onConqueteReussie: r => EffetsDraconiques.conquete.onConquete(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), - onConqueteEchec: r => { }, + onConqueteReussie: r => EffetsDraconiques.conquete.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => this.close(), canClose: false }); } @@ -639,8 +660,9 @@ export class RdDTMRDialog extends Dialog { dialog.render(true); } - async validerPelerinage(tmr) { - await EffetsDraconiques.pelerinage.onFinPelerinage(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)); + async validerVisite(tmr) { + await EffetsDraconiques.pelerinage.onVisiteSupprimer(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)); + await EffetsDraconiques.urgenceDraconique.onVisiteSupprimer(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)); } @@ -649,11 +671,12 @@ export class RdDTMRDialog extends Dialog { let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord); if (sortReserveList.length > 0) { - if (EffetsDraconiques.isSortImpossible(this.actor)) { + if (EffetsDraconiques.isSortReserveImpossible(this.actor)) { ui.notifications.error("Une queue ou un souffle vous empèche de déclencher de sort!"); return; } - if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord)) { + if (!EffetsDraconiques.isUrgenceDraconique(this.actor) && + (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord))) { let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête Reserve en Sécurité ou Réserve Exensible, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher :