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 :
";
for (let sortReserve of sortReserveList) {
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 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 + "+ ( (" + RdDUtility.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 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(this._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 displayInitiativeMenu(html, combatantId) {
- const combatant = game.combat.getCombatant(combatantId);
- let armesList = this.buildListeActionsCombat(combatant);
-
- // Build the relevant submenu
- if (armesList) {
- let menuItems = [];
- for (let arme of armesList) {
- menuItems.push({
- name: arme.data.competence,
- icon: "",
- callback: target => { RdDUtility.rollInitiativeCompetence(combatantId, arme) }
- });
- }
- new ContextMenu(html, ".directory-list", menuItems).render();
- }
- }
-
- /* -------------------------------------------- */
- 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 => {
- RdDUtility.displayInitiativeMenu(html, target.data('combatant-id'));
- }
- }
- }
- options.push({
- name: "Incrémenter initiative",
- condition: true,
- icon: '',
- callback: target => {
- RdDUtility.incDecInit(target.data('combatant-id'), +0.01);
- }
- });
- options.push({
- name: "Décrémenter initiative",
- condition: true,
- icon: '',
- callback: target => {
- RdDUtility.incDecInit(target.data('combatant-id'), -0.01);
- }
- });
- }
/* -------------------------------------------- */
static async chatListeners(html) {
diff --git a/module/tmr-utility.js b/module/tmr-utility.js
index 3aa49a99..cd2d3387 100644
--- a/module/tmr-utility.js
+++ b/module/tmr-utility.js
@@ -1,5 +1,6 @@
import { TMRRencontres } from "./tmr-rencontres.js";
import { Misc } from "./misc.js";
+import { Grammar } from "./grammar.js";
/* -------------------------------------------- */
const TMRMapping = {
@@ -210,24 +211,21 @@ const TMRMapping = {
export const TMRType = {
cite: { name: "cité", genre: "f" },
- sanctuaire: { name: "sanctuaire" },
- plaines: { name: "plaines", genre: "p" },
+ sanctuaire: { name: "sanctuaire", genre: 'm' },
+ plaines: { name: "plaines", genre: "fp" },
pont: { name: "pont", genre: "m" },
- collines: { name: "collines", genre: "p" },
+ collines: { name: "collines", genre: "fp" },
foret: { name: "forêt", genre: "f" },
- monts: { name: "monts", genre: "p" },
+ monts: { name: "monts", genre: "mp" },
desert: { name: "désert", genre: "m" },
fleuve: { name: "fleuve", genre: "m" },
lac: { name: "lac", genre: "m" },
- marais: { name: "marais", genre: "m" },
+ marais: { name: "marais", genre: "mp" },
gouffre: { name: "gouffre", genre: "m" },
necropole: { name: "nécropole", genre: "f" },
desolation: { name: "désolation", genre: "f" }
}
-/* -------------------------------------------- */
-const caseSpecificModes = ["attache", "trounoir", "debordement", "reserve_extensible", "maitrisee"];
-
/* -------------------------------------------- */
const tmrRandomMovePatten =
[{ name: 'top', x: 0, y: -1 },
@@ -282,6 +280,7 @@ export const tmrTokenZIndex = {
rencontre: 50,
trounoir: 60,
demireve: 70,
+ tooltip: 100,
}
/* -------------------------------------------- */
@@ -331,7 +330,12 @@ export class TMRUtility {
}
static getTMRLabel(coord) {
- return TMRMapping[coord]?.label ?? (coord+": case inconnue");
+ return TMRMapping[coord]?.label ?? (coord + ": case inconnue");
+ }
+
+ static getTMRDescr(coord) {
+ const tmr = TMRMapping[coord];
+ return Grammar.articleDetermine(tmr.genre) + ' ' + tmr.label;
}
static isCaseHumide(tmr) {
@@ -383,7 +387,7 @@ export class TMRUtility {
currentPos.x = currentPos.x + direction.x;
currentPos.y = currentPos.y + direction.y;
if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Sortie de carte ! Ré-insertion aléatoire
- coord = TMRUtility.getTMR(TMRUtility.convertToTMRCoord(currentPos));
+ coord = TMRUtility.getTMR(TMRUtility.convertToTMRCoord(currentPos));
} else {
coord = await actor.reinsertionAleatoire('Sortie de carte');
}
@@ -434,7 +438,7 @@ export class TMRUtility {
return reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve');
}
// Reserve sur un case "normale"
- return reserveList.filter(it => it.coord == coord);
+ return reserveList.filter(it => it.coord == coord);
}
/* -------------------------------------------- */
@@ -449,9 +453,8 @@ export class TMRUtility {
for (let dy = -portee; dy <= portee; dy++) { // Loop thru lines
const currentPos = { x: centerPos.x + dx, y: centerPos.y + dy };
if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Coordinate is valie
- let posPicNow = this.computeRealPictureCoordinates(currentPos, tmrConstants);
- let dist = Math.sqrt(Math.pow(posPicNow.x - posPic.x, 2) + Math.pow(posPicNow.y - posPic.y, 2)) / tmrConstants.cellw;
- if (dist < portee + 0.5) {
+ let dist = this.distancePosTMR(centerPos, currentPos);
+ if (dist <= portee) {
caseList.push(this.convertToTMRCoord(currentPos)); // Inside the area
}
}
@@ -459,5 +462,21 @@ export class TMRUtility {
}
return caseList;
}
+
+ static distanceTMR(coord1, coord2) {
+ let pos1 = this.convertToCellPos(coord1);
+ let pos2 = this.convertToCellPos(coord2);
+ return this.distancePosTMR(pos1, pos2);
+ }
+
+ static distancePosTMR(pos1, pos2) {
+ const dx = pos2.x - pos1.x;
+ const dy = pos2.y - pos1.y;
+ const abs_dx = Math.abs(dx);
+ const abs_dy = Math.abs(dy);
+ const distance = Math.sign(dx) == Math.sign(dy) ? Math.max(abs_dx, abs_dy) : (abs_dx + abs_dy);
+ return distance;
+ }
+
}
diff --git a/module/tmr/conquete.js b/module/tmr/conquete.js
index 92d665c4..ba7dffeb 100644
--- a/module/tmr/conquete.js
+++ b/module/tmr/conquete.js
@@ -35,11 +35,8 @@ export class Conquete extends Draconique {
await this.createCaseTmr(actor, 'Conquête: ' + conquete.label, conquete, queue._id);
}
- async onConquete(actor, tmr, onRemoveToken) {
- let existants = actor.data.items.filter(it => this.isCase(it, tmr.coord));
- for (let casetmr of existants) {
- await actor.deleteOwnedItem(casetmr.data.sourceid);
- onRemoveToken(tmr, casetmr);
- }
+ async onActorDeleteCaseTmr(actor, casetmr) {
+ await actor.deleteOwnedItem(casetmr.data.sourceid);
}
+
}
diff --git a/module/tmr/desorientation.js b/module/tmr/desorientation.js
index f2dad665..1dcec96a 100644
--- a/module/tmr/desorientation.js
+++ b/module/tmr/desorientation.js
@@ -24,10 +24,6 @@ export class Desorientation extends Draconique {
return Object.keys(TMRType).filter(it => !dejaDesorientes.includes(it));
}
- async onActorDeleteOwned(actor, souffle) {
- await this._supprimerCasesTmr(actor, souffle);
- }
-
code() { return 'desorientation' }
tooltip(linkData) { return `Désorientation, cette case n'existe plus !` }
img() { return 'icons/svg/explosion.svg' }
@@ -51,11 +47,5 @@ export class Desorientation extends Draconique {
}
}
- async _supprimerCasesTmr(actor, souffle) {
- let caseTmrs = actor.data.items.filter(it => it.data.sourceId == souffle._id);
- for (let casetmr of caseTmrs) {
- await actor.deleteOwnedItem(casetmr._id);
- }
- }
}
diff --git a/module/tmr/draconique.js b/module/tmr/draconique.js
index 9af391ec..4bf8a34d 100644
--- a/module/tmr/draconique.js
+++ b/module/tmr/draconique.js
@@ -7,8 +7,7 @@ const registeredEffects = [
/**
* Définition des informations d'une "draconique" (queue, ombre, tête, souffle) qui influence les TMR
*/
-export class Draconique
-{
+export class Draconique {
static isCaseTMR(element) { return element.type == 'casetmr'; }
static isQueueDragon(element) { return element.type == 'queue' || element.type == 'ombre'; }
static isSouffleDragon(element) { return element.type == 'souffle'; }
@@ -16,6 +15,7 @@ export class Draconique
static isQueueSouffle(it) { return Draconique.isQueueDragon(it) || Draconique.isSouffleDragon(it); }
tmrLabel(linkData) { return TMRUtility.getTMRLabel(linkData.data.coord); }
+ tmrDescr(linkData) { return TMRUtility.getTMRDescr(linkData.data.coord); }
static register(draconique) {
registeredEffects[draconique.code()] = draconique;
@@ -28,6 +28,7 @@ export class Draconique
static all() {
return Object.values(registeredEffects);
}
+
static get(code) {
return registeredEffects[code];
}
@@ -56,10 +57,11 @@ export class Draconique
}
async onActorDeleteOwned(actor, item) {
- let caseTmrs = actor.data.items.filter(it => this.isCase(it) && it.data.sourceid == item._id);
- for (let casetmr of caseTmrs) {
- await actor.deleteOwnedItem(casetmr._id);
- }
+ this.deleteCasesTmr(actor, item);
+ return false;
+ }
+
+ async onActorDeleteCaseTmr(actor, casetmr) {
return false;
}
/**
@@ -72,7 +74,7 @@ export class Draconique
* @returns un tooltip à afficher au dessus du token
*/
tooltip(linkData) { return undefined }
-
+
/**
* @param {*} img l'url du fichier image à utiliser pour le token. Si indéfini (et si createSprite n'est pas surchargé),
* un disque est utilisé.
@@ -102,7 +104,7 @@ export class Draconique
if (this.img()) {
return pixiTMR.sprite(this.code());
}
- else{
+ else {
return pixiTMR.circle()
}
}
@@ -120,11 +122,25 @@ export class Draconique
return list.find(c => this.isCase(c, coord));
}
- async createCaseTmr(actor, label, tmr, sourceId=undefined) {
+ async createCaseTmr(actor, label, tmr, sourceId = undefined) {
await actor.createOwnedItem({
name: label, type: 'casetmr', img: this.img(), _id: randomID(16),
- data: { coord: tmr.coord, specific: this.code(), sourceid:sourceId }
+ data: { coord: tmr.coord, specific: this.code(), sourceid: sourceId }
});
}
+ async deleteCasesTmr(actor, draconique) {
+ let caseTmrs = actor.data.items.filter(it => this.isCase(it) && it.data.sourceid == draconique._id);
+ for (let casetmr of caseTmrs) {
+ await actor.deleteOwnedItem(casetmr._id);
+ }
+ }
+
+ async onVisiteSupprimer(actor, tmr, onRemoveToken) {
+ let existants = actor.data.items.filter(it => this.isCase(it, tmr.coord));
+ for (let casetmr of existants) {
+ await actor.deleteOwnedItem(casetmr._id);
+ onRemoveToken(tmr, casetmr);
+ }
+ }
}
\ No newline at end of file
diff --git a/module/tmr/effets-draconiques.js b/module/tmr/effets-draconiques.js
index ab8bdc3d..6eefe7f3 100644
--- a/module/tmr/effets-draconiques.js
+++ b/module/tmr/effets-draconiques.js
@@ -14,6 +14,8 @@ import { PresentCites } from "./present-cites.js";
import { Desorientation } from "./desorientation.js";
import { Conquete } from "./conquete.js";
import { Pelerinage } from "./pelerinage.js";
+import { Periple } from "./periple.js";
+import { UrgenceDraconique } from "./urgence-draconique.js";
export class EffetsDraconiques {
@@ -32,6 +34,8 @@ export class EffetsDraconiques {
static desorientation = new Desorientation();
static conquete = new Conquete();
static pelerinage = new Pelerinage();
+ static periple = new Periple();
+ static urgenceDraconique = new UrgenceDraconique();
static init() {
Draconique.register(EffetsDraconiques.carteTmr);
@@ -49,6 +53,8 @@ export class EffetsDraconiques {
Draconique.register(EffetsDraconiques.desorientation);
Draconique.register(EffetsDraconiques.conquete);
Draconique.register(EffetsDraconiques.pelerinage);
+ Draconique.register(EffetsDraconiques.periple);
+ Draconique.register(EffetsDraconiques.urgenceDraconique);
}
/* -------------------------------------------- */
@@ -112,8 +118,7 @@ export class EffetsDraconiques {
}
static isPeriple(element) {
- // TODO
- return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && ir.name.toLowerCase() == 'périple');
+ return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.periple.match(it));
}
static isDesorientation(element) {
@@ -122,7 +127,19 @@ export class EffetsDraconiques {
/* -------------------------------------------- */
static isSortImpossible(element) {
- return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.conquete.match(it) || EffetsDraconiques.pelerinage.match(it));
+ return EffetsDraconiques.isMatching(element, it =>
+ EffetsDraconiques.conquete.match(it) ||
+ EffetsDraconiques.periple.match(it) ||
+ EffetsDraconiques.urgenceDraconique.match(it) ||
+ EffetsDraconiques.pelerinage.match(it)
+ );
+ }
+ static isSortReserveImpossible(element) {
+ return EffetsDraconiques.isMatching(element, it =>
+ EffetsDraconiques.conquete.match(it) ||
+ EffetsDraconiques.periple.match(it) ||
+ EffetsDraconiques.pelerinage.match(it)
+ );
}
static isConquete(element) {
@@ -138,7 +155,7 @@ export class EffetsDraconiques {
}
static isUrgenceDraconique(element) {
- return EffetsDraconiques.isMatching(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase() == 'urgence draconique');
+ return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.urgenceDraconique.match(it));
}
/* -------------------------------------------- */
diff --git a/module/tmr/fermeture-cites.js b/module/tmr/fermeture-cites.js
index 6a8602d3..8d695ae6 100644
--- a/module/tmr/fermeture-cites.js
+++ b/module/tmr/fermeture-cites.js
@@ -35,11 +35,4 @@ export class FermetureCites extends Draconique {
await this.createCaseTmr(actor, 'Fermeture: ' + tmr.label, tmr, souffle._id);
}
}
-
- async onConquete(actor, tmr, onRemoveToken) {
- const citeFermee = actor.data.items.find(it => this.isCase(it, tmr.coord));
- await actor.deleteOwnedItem(citeFermee._id);
- onRemoveToken(tmr, citeFermee);
- }
-
}
diff --git a/module/tmr/pelerinage.js b/module/tmr/pelerinage.js
index 76cc2fe9..ec9e4010 100644
--- a/module/tmr/pelerinage.js
+++ b/module/tmr/pelerinage.js
@@ -31,12 +31,8 @@ export class Pelerinage extends Draconique {
});
}
- async onFinPelerinage(actor, tmr, onRemoveToken) {
- const pelerinages = actor.data.items.filter(it => this.isCase(it, tmr.coord));
- for (let p of pelerinages){
- await actor.deleteOwnedItem(p.data.sourceid);
- onRemoveToken(tmr, p);
- }
+ async onActorDeleteCaseTmr(actor, casetmr) {
+ await actor.deleteOwnedItem(casetmr.data.sourceid);
}
}
diff --git a/module/tmr/periple.js b/module/tmr/periple.js
new file mode 100644
index 00000000..feba1a2b
--- /dev/null
+++ b/module/tmr/periple.js
@@ -0,0 +1,44 @@
+import { Grammar } from "../grammar.js";
+import { tmrColors, tmrConstants, tmrTokenZIndex, TMRUtility } from "../tmr-utility.js";
+import { Draconique } from "./draconique.js";
+
+export class Periple extends Draconique {
+
+ constructor() {
+ super();
+ }
+
+ type() { return 'souffle' }
+ match(item) { return Draconique.isSouffleDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('periple'); }
+ manualMessage() { return false }
+
+ async onActorCreateOwned(actor, souffle) {
+ let terrain = new Roll("1d2").evaluate().total == 1 ? 'sanctuaire' : 'necropole';
+ let tmrs = TMRUtility.getListTMR(terrain);
+ for (let tmr of tmrs) {
+ await this.createCaseTmr(actor, 'Périple: ' + tmr.label, tmr, souffle._id);
+ }
+ }
+
+
+ code() { return 'periple' }
+ tooltip(linkData) { return `Votre Périple passe par ${this.tmrDescr(linkData)}` }
+ img() { return 'icons/svg/acid.svg' }
+
+ createSprite(pixiTMR) {
+ return pixiTMR.sprite(this.code(), {
+ zIndex: tmrTokenZIndex.conquete,
+ alpha: 1,
+ color: tmrColors.souffle,
+ taille: tmrConstants.twoThird,
+ decallage: tmrConstants.right
+ });
+ }
+ getDifficulte(tmr) {
+ switch (tmr.type) {
+ case 'sanctuaire': return -3;
+ case 'necropole': return -5;
+ }
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/module/tmr/pixi-tmr.js b/module/tmr/pixi-tmr.js
index 505f1574..a2a24743 100644
--- a/module/tmr/pixi-tmr.js
+++ b/module/tmr/pixi-tmr.js
@@ -100,6 +100,7 @@ export class PixiTMR {
addTooltip(sprite, text) {
if (text) {
sprite.tooltip = new PIXI.Text(text, tooltipStyle);
+ sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false;
sprite.interactive = true;
sprite.on('pointerdown', event => this.onClickBackground(event))
diff --git a/module/tmr/urgence-draconique.js b/module/tmr/urgence-draconique.js
new file mode 100644
index 00000000..8af5c992
--- /dev/null
+++ b/module/tmr/urgence-draconique.js
@@ -0,0 +1,55 @@
+import { ChatUtility } from "../chat-utility.js";
+import { Grammar } from "../grammar.js";
+import { Misc } from "../misc.js";
+import { RdDRollTables } from "../rdd-rolltables.js";
+import { tmrColors, tmrConstants, tmrTokenZIndex, TMRUtility } from "../tmr-utility.js";
+import { Draconique } from "./draconique.js";
+
+export class UrgenceDraconique extends Draconique {
+
+ constructor() {
+ super();
+ }
+
+ type() { return 'queue' }
+ match(item) { return Draconique.isQueueDragon(item) && Grammar.toLowerCaseNoAccent(item.name).includes('urgence draconique'); }
+ manualMessage() { return false }
+ async onActorCreateOwned(actor, queue) {
+ let coordSortsReserve = (actor.data.data.reve.reserve?.list.map(it => it.coord)) ?? [];
+ if (coordSortsReserve.length == 0) {
+ // La queue se transforme en idée fixe
+ let ideeFixe = await RdDRollTables.getIdeeFixe();
+ ChatMessage.create({
+ whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
+ content: `En l'absence de sorts en réserve, l'urgence draconique de ${actor.name} se transforme en ${queue.name}`
+ });
+ await actor.createOwnedItem(ideeFixe);
+ await actor.deleteOwnedItem(queue._id);
+ return;
+ }
+ else {
+ let demiReve = actor.getDemiReve();
+ coordSortsReserve.sort((a, b) => TMRUtility.distanceTMR(a, demiReve) - TMRUtility.distanceTMR(b, demiReve));
+ let tmr = TMRUtility.getTMR(coordSortsReserve[0]);
+ await this.createCaseTmr(actor, 'Urgence draconique: ' + tmr.label, tmr, queue._id);
+ }
+ }
+
+ async onActorDeleteCaseTmr(actor, casetmr) {
+ await actor.deleteOwnedItem(casetmr.data.sourceid);
+ }
+
+ code() { return 'urgence' }
+ tooltip(linkData) { return `Urgence draconique!` }
+ img() { return 'icons/svg/hazard.svg' }
+
+ createSprite(pixiTMR) {
+ return pixiTMR.sprite(this.code(),
+ {
+ zIndex: tmrTokenZIndex.conquete,
+ color: tmrColors.queues,
+ taille: tmrConstants.full,
+ decallage: { x: 2, y: 0 }
+ });
+ }
+}
diff --git a/packs/tables-diverses.db b/packs/tables-diverses.db
index 306a2b6a..b16a39d0 100644
--- a/packs/tables-diverses.db
+++ b/packs/tables-diverses.db
@@ -7,6 +7,6 @@
{"_id":"nbH4v630P7ARaAHk","name":"Idées fixes","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"flags":{},"img":"icons/svg/d20-grey.svg","description":"Les idées fixes prennent effet immédiatement et durent jusqu’à l’extrême fin de l’heure du Château Dormant du lendemain. Si passé ce délai, l’occasion de les manifester ne s’est pas présentée, la queue prend fin néanmoins. Si elles entrent en contradiction avec une autre queue de Dragon, retirer. Les idées fixes peuvent être refoulées à tout moment au prix de 1 point de refoulement.","results":[{"_id":"xX6XGI6ujPDSbbob","flags":{},"type":2,"text":"Idée fixe : Ne s’exprimer que par des cris d’animaux (meuh ! coin-coin ! etc.)","img":"exprimer_cris.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"YCHBbRLiMzTH7IBj","weight":1,"range":[1,1],"drawn":false},{"_id":"gYN6fcQISs2H7kAn","flags":{},"type":2,"text":"Idée fixe : Garder les yeux bandés","img":"garder_yeux.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"E902EEYZHg3zFKq6","weight":1,"range":[2,2],"drawn":false},{"_id":"QIHOFfluPnEJFKHD","flags":{},"type":2,"text":"Idée fixe : Ne dire que «non» ou négation analogue","img":"dire_non.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"gMmqdJ9I7Mt8Tg3f","weight":1,"range":[3,3],"drawn":false},{"_id":"P0eaJjtQQfpNIL9I","flags":{},"type":2,"text":"Idée fixe : Garder sur soi 3d6 kilos de cailloux","img":"garder_cailloux.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"F8G3rdU1nfJzYwYR","weight":1,"range":[4,4],"drawn":false},{"_id":"PENvfmIpKFWee8rF","flags":{},"type":2,"text":"Idée fixe : Traîner son épée en laisse (ou sa meilleure arme)","img":"trainer_laisse.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"2j1q9e07ZLlIQDYl","weight":1,"range":[5,5],"drawn":false},{"_id":"6xT8v1ScJSZEente","flags":{},"type":2,"text":"Idée fixe : Garder une main sur la tête","img":"garder_main.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"iPYPgxL2uUnphStc","weight":1,"range":[6,6],"drawn":false},{"_id":"0CO9RJPygGdJT9nB","flags":{},"type":2,"text":"Idée fixe : Avoir le visage noirci à la cendre","img":"avoir_visage.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"6G0lF06jSryTduAt","weight":1,"range":[7,7],"drawn":false},{"_id":"XeCtzSCuEEm9q8TI","flags":{},"type":2,"text":"Idée fixe : Cracher dans toute nourriture ou boisson aperçue","img":"cracher_nourriture.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"22EQLBJfHVYs96iC","weight":1,"range":[8,8],"drawn":false},{"_id":"Sh5KRDvM7iXad6rK","flags":{},"type":2,"text":"Idée fixe : Anorexie. Ne rien avaler, ni solide, ni liquide, pas même une potion","img":"anorexie.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"0uc2pMIGL03Hq2Hn","weight":1,"range":[9,9],"drawn":false},{"_id":"6Q3XKURzip61EXK2","flags":{},"type":2,"text":"Idée fixe : Aller tout nu, sans porter le moindre paquet ni objet","img":"aller_nu.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"I0CtQ05xFW6ghcdP","weight":1,"range":[10,10],"drawn":false},{"_id":"7B8joMjIuhNxip6N","flags":{},"type":2,"text":"Idée fixe : Vider sur sa tête toute fiole ou flacon aperçu","img":"verser_flacon.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"afGp9CewfyJKecEE","weight":1,"range":[11,11],"drawn":false},{"_id":"Febiy30xqiNo7OqV","flags":{},"type":2,"text":"Idée fixe : Appeler les hommes «madame» et les femmes «messire»","img":"appeler_hommes_femmes.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"j2xIrFWYqhDM4TcN","weight":1,"range":[12,12],"drawn":false},{"_id":"Aa72g0fFdq2qBmCn","flags":{},"type":2,"text":"Idée fixe : Ne marcher qu’à quatre pattes","img":"marcher_quatre_pattes.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"7TKsit2Mv9mWGq3C","weight":1,"range":[13,13],"drawn":false},{"_id":"UYhT76xYv8OGXcMZ","flags":{},"type":2,"text":"Idée fixe : Boulimie. Manger au moins un point de sust. par heure","img":"boulimie.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"JcTX8qMS0z8bmdVt","weight":1,"range":[14,14],"drawn":false},{"_id":"fOnQmiXMPXrK3K6P","flags":{},"type":2,"text":"Idée fixe : Refuser de se déplacer autrement que porté","img":"refuser_deplacer.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"hghw6Cldrad1CIiJ","weight":1,"range":[15,15],"drawn":false},{"_id":"KKGlZXouFfIMKQma","flags":{},"type":2,"text":"Idée fixe : Ne pas franchir de porte. (On peut franchir une fenêtre)","img":"pas-franchir.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"UUTbsktTcxsIe5L5","weight":1,"range":[16,16],"drawn":false},{"_id":"JjWHUsMLhLuTYB2q","flags":{},"type":2,"text":"Idée fixe : Faire le mort","img":"faire_mort.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"xa4t9Lbt6uLEjap6","weight":1,"range":[17,17],"drawn":false},{"_id":"r7Dw8GD1ceypY0iV","flags":{},"type":2,"text":"Idée fixe : Éteindre tout feu rencontré (feu de camp, torche, lanterne, etc.)","img":"eteindre_feu.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"2Rtm78bMKPy8eG4q","weight":1,"range":[18,18],"drawn":false},{"_id":"i0AW1Ir1V7y2ZmEh","flags":{},"type":2,"text":"Idée fixe : Refuser de se délester du moindre objet, ni donner ni prêter","img":"refuser_delester.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"VChJbtGFtWoiFNky","weight":1,"range":[19,19],"drawn":false},{"_id":"DQBzVXbJKn9zsXUI","flags":{},"type":2,"text":"Idée fixe : Refuser de monter dans les TMR","img":"refuser_tmr.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"NpTDqICR7ZuToQrg","weight":1,"range":[20,20],"drawn":false}],"formula":"1d20","replacement":false,"displayRoll":true}
{"name":"Maladresse armé","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"flags":{},"description":"","results":[{"_id":"R18ITzDLLuGiaqXM","flags":{},"type":0,"text":"Assomé net : Endurance tombe à 0 et -1 pt de vie.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[2,2],"drawn":false},{"_id":"sfWXLz4QIkPLNJmg","flags":{},"type":0,"text":"Ami bousculé : Le compagnon bousculé doit réussir Empathie/Vigilance à -1d6 ou être en demi-surprise jusqu’à la fin du round suivant.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[3,3],"drawn":false},{"_id":"APqyDePFzBaROB6i","flags":{},"type":0,"text":"Chute : Encaissement à -1d6 sur la table des Coups non mortels.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[4,4],"drawn":false},{"_id":"8yErM3dW4F2MBFqe","flags":{},"type":0,"text":"Désarmé","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[5,5],"drawn":false},{"_id":"8dh7zUXAq8ehNag7","flags":{},"type":0,"text":"Arme choquée : L’arme utilisée joue un jet de Résistance à -2d6 et perd ce nombre de points de résistance en cas d’échec.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[6,7],"drawn":false},{"_id":"OskepgYnU7pok5jv","flags":{},"type":0,"text":"Déséquilibré : Réussir Agilité/Vigilance à -1d6 ou être en demi-surprise jusqu’à la fin du round suivant.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[8,9],"drawn":false},{"_id":"GOYmqZj1Lnc0cKO9","flags":{},"type":0,"text":"Faux mouvement : Perte de 2d6 points d’endurance.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[10,12],"drawn":false},{"_id":"NOrmphYuLWRKZxai","flags":{},"type":0,"text":"Déséquilibré : Réussir Agilité/Vigilance à -1d6 ou être en demi-surprise jusqu’à la fin du round suivant.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[13,14],"drawn":false},{"_id":"ef8tfK8JzdX1OKzf","flags":{},"type":0,"text":"Arme choquée : L’arme utilisée joue un jet de Résistance à -2d6 et perd ce nombre de points de résistance en cas d’échec.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[15,16],"drawn":false},{"_id":"9V3Gj9PEYrEY7OCc","flags":{},"type":0,"text":"Désarmé","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[17,17],"drawn":false},{"_id":"c61AFRaP9poCmr9B","flags":{},"type":0,"text":"Chute : Encaissement à -1d6 sur la table des Coups non mortels.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[18,18],"drawn":false},{"_id":"8tS1rSwYvd0cS9o4","flags":{},"type":0,"text":"Ami bousculé : Le compagnon bousculé doit réussir Empathie/Vigilance à -1d6 ou être en demi-surprise jusqu’à la fin du round suivant.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[19,19],"drawn":false},{"_id":"ma6DRHIjkgpOVc55","flags":{},"type":0,"text":"Assommé net : Endurance tombe à 0 et -1 pt de vie.","img":"icons/svg/d20-black.svg","resultId":"","weight":1,"range":[20,20],"drawn":false}],"formula":"2d10","replacement":false,"displayRoll":true,"_id":"pXYVWRlCftWdwsBP"}
{"_id":"sVWhyr4wPnieuPP8","name":"Désirs lancinants","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"flags":{},"img":"icons/svg/d20-grey.svg","description":"Les désirs lancinants durent jusqu’à ce qu’ils soient satisfaits ou refoulés. Tant que satisfaction n’est pas obtenue, aucun point d’expérience ne peut plus être gagné par l’exercice en cas de particulière et d’ajustement final négatif. Les points d’expérience dus au stress ne sont pas affectés. Les désirs lancinants peuvent être refoulés à tout moment au prix de 1 point de refoulement.","results":[{"_id":"m9BMFVAByyICiMkp","flags":{},"type":2,"text":"Désir lancinant : Danser nu sous la pluie","img":"danser_pluie.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"F6qL4d4g3qjh045R","weight":1,"range":[1,1],"drawn":false},{"_id":"EJYCgPl9BCW92LSM","flags":{},"type":2,"text":"Désir lancinant : Traire une vache","img":"traire_vache.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"QHUOwjMR6AvepGPm","weight":1,"range":[2,2],"drawn":false},{"_id":"fRgwd7VconbmXrWQ","flags":{},"type":2,"text":"Désir lancinant : Manger du poisson","img":"manger_poisson.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"SY0SsWtZdxSodMcl","weight":1,"range":[3,3],"drawn":false},{"_id":"NEWpP5vwM5KQviDD","flags":{},"type":2,"text":"Désir lancinant : Manger des champignons","img":"manger_champignons.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"gadh6aI5iCM82qpP","weight":1,"range":[4,4],"drawn":false},{"_id":"8pMq7q3evaj9fIst","flags":{},"type":2,"text":"Désir lancinant : Se soûler (minimum pas frais)","img":"se_souler.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"jShpKV8mVcqWmYvp","weight":1,"range":[5,5],"drawn":false},{"_id":"qFOyIPVjQkVjrHFI","flags":{},"type":2,"text":"Désir lancinant : Gagner de l’argent (minimum 10 deniers)","img":"gagner_argent.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"3BcC8lSsP5WIyva7","weight":1,"range":[6,6],"drawn":false},{"_id":"RrOMys2WRxUrRYev","flags":{},"type":2,"text":"Désir lancinant : Faire des bulles de savon","img":"bulles_savon.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"df5oN8Ub3dWTVxNj","weight":1,"range":[7,7],"drawn":false},{"_id":"kwOTcCx7NDRxqv8B","flags":{},"type":2,"text":"Désir lancinant : Entendre braire un âne","img":"entendre_ane.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"diCCimukdNM6bPub","weight":1,"range":[8,8],"drawn":false},{"_id":"KoGHRGGze7WXnO9R","flags":{},"type":2,"text":"Désir lancinant : Danser avec un(e) partenaire inconnu(e) (Beauté 13 minimum)","img":"danser_inconnu.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"2i3PndTKG1n3hEUU","weight":1,"range":[9,9],"drawn":false},{"_id":"JFiIv5R4gAYV1IMs","flags":{},"type":2,"text":"Désir lancinant : Construire une cabane","img":"construire_cabane.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"mN0yghXkFfj2YctJ","weight":1,"range":[10,10],"drawn":false},{"_id":"vKXdKjA4Vt7OWhOW","flags":{},"type":2,"text":"Désir lancinant : Acquérir une chèvre","img":"acqu%C3%A9rir_chevre.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"8sLXQBqo8XwjAFG0","weight":1,"range":[11,11],"drawn":false},{"_id":"wRk4nXin4UUB5c4o","flags":{},"type":2,"text":"Désir lancinant : Se rouler dans la boue","img":"se_rouler_boue.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"el4lofhhSucMv5xv","weight":1,"range":[12,12],"drawn":false},{"_id":"XUcNhyHEDjSvg8ki","flags":{},"type":2,"text":"Désir lancinant : Embrasser un cochon sur le groin","img":"embrasser_cochon.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"sjXBBr85OBk4Yg4t","weight":1,"range":[13,13],"drawn":false},{"_id":"oN3eP4peneyXENds","flags":{},"type":2,"text":"Désir lancinant : Briser un objet de verre","img":"briser_verre.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"1l59lWbtvYp74OTb","weight":1,"range":[14,14],"drawn":false},{"_id":"5AHjNXDrQL5TqLjv","flags":{},"type":2,"text":"Désir lancinant : Casser 3d6 oeufs en les jetant à terre","img":"casser_oeufs.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"OjG8XRbeYtq2jcgB","weight":1,"range":[15,15],"drawn":false},{"_id":"crQSShE6rcAnBj1k","flags":{},"type":2,"text":"Désir lancinant : Passer une nuit sur une échelle","img":"nuit_echelle.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"LlELEB0FhymLx6VM","weight":1,"range":[16,16],"drawn":false},{"_id":"dDYpP2np5BKlEMSK","flags":{},"type":2,"text":"Désir lancinant : Se faire raser la tête","img":"raser_tete.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"HSNOvBR890dsEDw2","weight":1,"range":[17,17],"drawn":false},{"_id":"CvynUiSfzZ0UnFzk","flags":{},"type":2,"text":"Désir lancinant : Masochisme. Perdre 3 points d’endurance minimum en 1 round","img":"masochisme.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"NzJJNK0YMAbobu0p","weight":1,"range":[18,18],"drawn":false},{"_id":"3pM6YTcKglgt5naZ","flags":{},"type":2,"text":"Désir lancinant : Mégalomanie. Être acclamé par un minimum de 10 personnes","img":"megalomanie.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"3ZjwYyQRatCMnBCi","weight":1,"range":[19,19],"drawn":false},{"_id":"EJYLKIrPHPPbgrlW","flags":{},"type":2,"text":"Désir lancinant : Pisser dans un violon (luth, mandoline, etc.)","img":"pisser_violon.png","collection":"foundryvtt-reve-de-dragon.queues-de-dragon","resultId":"S8PVNgxb7TcFXq9g","weight":1,"range":[20,20],"drawn":false}],"formula":"1d20","replacement":false,"displayRoll":true}
-{"name":"Têtes de Dragon pour haut-rêvants","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"flags":{},"description":"Quand une tête est dite unique, retirer si le personnage la possède déjà. Quand elle est dite cumulable, le jet de dés indiqué bénéficie d’un nouveau bonus de +1 chaque fois qu’elle est ré-obtenue. Par exemple un jet de Vue à zéro devient un jet de Vue à +1.","results":[{"_id":"SrGcS3nquZptN3Lx","flags":{},"type":2,"text":"Don de double-rêve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"VWOXA0q6GB7o8oxz","weight":1,"range":[1,10],"drawn":false},{"_id":"fGwttrng7KMYrll1","flags":{},"type":2,"text":"Don de déplacement accéléré","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"ZVh8PLlAzAJulr37","weight":1,"range":[11,20],"drawn":false},{"_id":"BASdOFjj0HJsrDI6","flags":{},"type":2,"text":"Augmentation du seuil de rêve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"Zlt01O2sFrVR9pus","weight":1,"range":[21,30],"drawn":false},{"_id":"HWgayaNNiVLlb5Lj","flags":{},"type":2,"text":"Connaissance intuitive d'un nouveau sort","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"SFxPAvWpEGYHI8mO","weight":1,"range":[31,40],"drawn":false},{"_id":"QMegfeFyxIxNNai7","flags":{},"type":2,"text":"Terre d'attache","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"YDo0a0ApM8iW9g82","weight":1,"range":[41,50],"drawn":false},{"_id":"0FYecx0AOjHJGInw","flags":{},"type":2,"text":"Présents de cités","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"ZuTV36GyOhFgTlE7","weight":1,"range":[51,60],"drawn":false},{"_id":"CtjqmAwweZVDbAjY","flags":{},"type":2,"text":"Réserve en sécurité","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"BT18LAdIqEgSG2Hh","weight":1,"range":[61,70],"drawn":false},{"_id":"WNELkA5FkYTj68Ln","flags":{},"type":2,"text":"Réserve extensible","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"bFdU6ddgj4BAlJZX","weight":1,"range":[71,80],"drawn":false},{"_id":"fn1I3FolBzOmeh0W","flags":{},"type":2,"text":"Quête des eaux","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"a3Y5W0AX5EKxZRSL","weight":1,"range":[81,90],"drawn":false},{"_id":"jr90oOnnrEH31eZj","flags":{},"type":2,"text":"Connaissance du fleuve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"5JccZSafqCXYqrwU","weight":1,"range":[91,100],"drawn":false}],"formula":"1d100","replacement":false,"displayRoll":true,"_id":"sXPDAHwb6ND5tNJG"}
+{"_id":"sXPDAHwb6ND5tNJG","name":"Têtes de Dragon pour haut-rêvants","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"flags":{},"img":"icons/svg/d20-grey.svg","description":"Quand une tête est dite unique, retirer si le personnage la possède déjà. Quand elle est dite cumulable, le jet de dés indiqué bénéficie d’un nouveau bonus de +1 chaque fois qu’elle est ré-obtenue. Par exemple un jet de Vue à zéro devient un jet de Vue à +1.","results":[{"_id":"SrGcS3nquZptN3Lx","flags":{},"type":2,"text":"Don de double-rêve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"VWOXA0q6GB7o8oxz","weight":1,"range":[1,10],"drawn":false},{"_id":"fGwttrng7KMYrll1","flags":{},"type":2,"text":"Don de déplacement accéléré","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"ZVh8PLlAzAJulr37","weight":1,"range":[11,20],"drawn":false},{"_id":"BASdOFjj0HJsrDI6","flags":{},"type":2,"text":"Augmentation du seuil de rêve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"Zlt01O2sFrVR9pus","weight":1,"range":[21,30],"drawn":false},{"_id":"HWgayaNNiVLlb5Lj","flags":{},"type":2,"text":"Connaissance intuitive d'un nouveau sort","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"SFxPAvWpEGYHI8mO","weight":1,"range":[31,40],"drawn":false},{"_id":"QMegfeFyxIxNNai7","flags":{},"type":2,"text":"Terre d'attache","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"YDo0a0ApM8iW9g82","weight":1,"range":[41,50],"drawn":false},{"_id":"0FYecx0AOjHJGInw","flags":{},"type":2,"text":"Présent des cités","img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"E4a4O1IdrgbNGpVy","weight":1,"range":[51,60],"drawn":false},{"_id":"CtjqmAwweZVDbAjY","flags":{},"type":2,"text":"Réserve en sécurité","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"BT18LAdIqEgSG2Hh","weight":1,"range":[61,70],"drawn":false},{"_id":"WNELkA5FkYTj68Ln","flags":{},"type":2,"text":"Réserve extensible","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"bFdU6ddgj4BAlJZX","weight":1,"range":[71,80],"drawn":false},{"_id":"fn1I3FolBzOmeh0W","flags":{},"type":2,"text":"Quête des eaux","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"a3Y5W0AX5EKxZRSL","weight":1,"range":[81,90],"drawn":false},{"_id":"jr90oOnnrEH31eZj","flags":{},"type":2,"text":"Connaissance du fleuve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.tetes-de-dragon-pour-haut-revants","resultId":"5JccZSafqCXYqrwU","weight":1,"range":[91,100],"drawn":false}],"formula":"1d100","replacement":true,"displayRoll":true}
{"name":"Souffles de Dragon","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"flags":{},"description":"Quand un souffle dure ddr jours, déterminer ce nombre de la même façon que la force d’un Rêve de Dragon, sur une base minimum de 7. Le souffle prend effet à l’instant même et dure jusqu’à la fin de l’heure du Château Dormant du xème jour. Aucun souffle de Dragon n’est refoulable.","results":[{"_id":"39OpB4Ly6pwHNvq3","flags":{},"type":2,"text":"Périple","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"yczTFUNwqyg0WhsQ","weight":1,"range":[1,5],"drawn":false},{"_id":"8JkuRose0umr9I9J","flags":{},"type":2,"text":"Fermeture des cités","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"aBiHdEMLWNZurX3v","weight":1,"range":[6,10],"drawn":false},{"_id":"dGKfn21m4nMJkvTj","flags":{},"type":2,"text":"Débordement","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"YiXBU7qBxR0OnkPS","weight":1,"range":[11,15],"drawn":false},{"_id":"3yosmgSu7FZ0nY3l","flags":{},"type":2,"text":"Impraticabilité des ponts","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"8B74cu17PCdqztke","weight":1,"range":[16,20],"drawn":false},{"_id":"5ujcfs6jFvDjWVOK","flags":{},"type":2,"text":"Désorientation","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"Su90PAG4Hea7qoej","weight":1,"range":[21,25],"drawn":false},{"_id":"MEunTLUJI4pVuzwv","flags":{},"type":2,"text":"Impraticabilité des ponts","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"8B74cu17PCdqztke","weight":1,"range":[26,30],"drawn":false},{"_id":"IvuyAhcW9yzbhRLF","flags":{},"type":2,"text":"Désorientation","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"Su90PAG4Hea7qoej","weight":1,"range":[31,35],"drawn":false},{"_id":"JmoyngtksVYFnNWy","flags":{},"type":2,"text":"Trou noir","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"QcfOUgWT15BCLUvl","weight":1,"range":[36,40],"drawn":false},{"_id":"pombreEUTYX2prs6","flags":{},"type":2,"text":"Péage","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"HYsVo3LM53vAm8Y4","weight":1,"range":[41,45],"drawn":false},{"_id":"PWzcBoJSsDdV8m8m","flags":{},"type":2,"text":"Montée laborieuse","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"yFZ6KkvVQVyCJLua","weight":1,"range":[46,50],"drawn":false},{"_id":"KoyO1TrUQYFxu1MM","flags":{},"type":2,"text":"Double résistance du fleuve","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"nI4sA12n6AdLNv5I","weight":1,"range":[51,55],"drawn":false},{"_id":"NGpVZ0worbKmi5bN","flags":{},"type":2,"text":"Confusion draconique","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"cuopXCY54q3nTtBl","weight":1,"range":[56,60],"drawn":false},{"_id":"wjlmxOrF5mKx5Uqt","flags":{},"type":2,"text":"Épuisement","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"GNZW6esJ2nCHjfyu","weight":1,"range":[61,65],"drawn":false},{"_id":"RYySlbmF6KtKJ8C6","flags":{},"type":2,"text":"Perte dans une caractéristique","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"ykxvi2vohrZPidLK","weight":1,"range":[66,70],"drawn":false},{"_id":"Ec0BW2vZ3QTpIZRn","flags":{},"type":2,"text":"Vieillissement instantané","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"dArTQtFwTssAyl90","weight":1,"range":[71,75],"drawn":false},{"_id":"dIBOMmIwvx2kTgTN","flags":{},"type":2,"text":"Non-combativité","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"6xvkOytCrp5yDAOs","weight":1,"range":[76,79],"drawn":false},{"_id":"3tR6RxkGiugHxSHd","flags":{},"type":2,"text":"Je-m'en-foutisme","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"2FDRnIY6Os6JJ27j","weight":1,"range":[80,83],"drawn":false},{"_id":"TZWltQcYm3F81ZqO","flags":{},"type":2,"text":"Paresse intellectuelle","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"IYfK01ZdAkDZbUS9","weight":1,"range":[84,88],"drawn":false},{"_id":"xmUhIdsW8Bgq31lJ","flags":{},"type":2,"text":"Queue de dragon perpétuelle","img":"icons/svg/mystery-man.svg","collection":"foundryvtt-reve-de-dragon.souffles-de-dragon","resultId":"CVXptnnw75fLx4LK","weight":1,"range":[89,100],"drawn":false}],"formula":"1d100","replacement":false,"displayRoll":true,"_id":"vKiKfjFVP36mYnEt"}
{"_id":"w2WBqsg1B2fPV8zT","name":"Tarot Draconique","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"flags":{"core":{"sourceId":"RollTable.wgFwo1JYrkjyy7J7"}},"img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","description":"Tirage d'une carte de tarot","results":[{"_id":"EP4xqaMgXhuFKNWe","flags":{},"type":2,"text":"L'Auberge","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"2AZ4LaHfNjewYIkO","weight":1,"range":[1,1],"drawn":false},{"_id":"hRvvPoX8nv7AonqM","flags":{},"type":2,"text":"L'Epée","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"inWYIYIuHkoZ3S4A","weight":1,"range":[2,2],"drawn":false},{"_id":"gtMrSlTlMKFV6T6i","flags":{},"type":2,"text":"L'Esprit Thanataire","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"jpNASgEPndryQwuX","weight":1,"range":[3,3],"drawn":false},{"_id":"UdJ5OXBfeg83EN8y","flags":{},"type":2,"text":"La Couronne","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"W150EcUq9vU9ryyc","weight":1,"range":[4,4],"drawn":false},{"_id":"GobVbxfElUvE1flV","flags":{},"type":2,"text":"La Déchirure","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"yIIUac5ehspmqDB2","weight":1,"range":[5,5],"drawn":false},{"_id":"JPjrJQ77YbIwTcJX","flags":{},"type":2,"text":"La Licorne","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"0tOoKLhFKqBLGdAc","weight":1,"range":[6,6],"drawn":false},{"_id":"ToLq4NpjqD2GwlZq","flags":{},"type":2,"text":"La Lune","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"J1qgDjOtf7vK4oV6","weight":1,"range":[7,7],"drawn":false},{"_id":"bTwPXktwq5t5jAAy","flags":{},"type":2,"text":"La Sebile","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"rlmOj3pu4EDd2jZi","weight":1,"range":[8,8],"drawn":false},{"_id":"tbTCmOeNaWYaG6jU","flags":{},"type":2,"text":"La Vierge","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"miO25LLlwPyOAGeR","weight":1,"range":[9,9],"drawn":false},{"_id":"ssqd2Idq1akdYakx","flags":{},"type":2,"text":"Le Chateau","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"n9il4UfR6swHQ805","weight":1,"range":[10,10],"drawn":false},{"_id":"ykbg1uUgbbySEfai","flags":{},"type":2,"text":"Le Coffre","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"vreVS8zyLuiV3qom","weight":1,"range":[11,11],"drawn":false},{"_id":"NkEN5qFZ0aGcgjne","flags":{},"type":2,"text":"Le Gibet","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"beF1v9sjQeDgmxZL","weight":1,"range":[12,12],"drawn":false},{"_id":"OjbIhV6dwaqwMLhG","flags":{},"type":2,"text":"Le Grimoire","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"G2zkE5vY7UrgzNV9","weight":1,"range":[13,13],"drawn":false},{"_id":"mzKV31aojE7DFJUz","flags":{},"type":2,"text":"Le Groin","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"yHvIWLb4TuUAbPGa","weight":1,"range":[14,14],"drawn":false},{"_id":"hD9BUGLDILRc4jJm","flags":{},"type":2,"text":"Le Haut-Rêvant","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"2dWBIwjKVCTxwrHE","weight":1,"range":[15,15],"drawn":false},{"_id":"MFjPTrGgPxUrjHeW","flags":{},"type":2,"text":"Le Luth","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"RjwPSt0pOwQ8w9Qy","weight":1,"range":[16,16],"drawn":false},{"_id":"LZ9n4jg7I1ZjB8Lg","flags":{},"type":2,"text":"Le Rabot","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"zP2OF8ZrAYEODxOn","weight":1,"range":[17,17],"drawn":false},{"_id":"nv6TDBdxJNnJyDYa","flags":{},"type":2,"text":"Le Soleil","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"zSqKPNeQTVjRuni6","weight":1,"range":[18,18],"drawn":false},{"_id":"QdM0WqpsxObvETQU","flags":{},"type":2,"text":"Le Vaisseau","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"VAhO41qD8QQLDie5","weight":1,"range":[19,19],"drawn":false},{"_id":"iDCdiYblZYjPYwYJ","flags":{},"type":2,"text":"Le Voyageur","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"BQkcM9P2xdImdNb9","weight":1,"range":[20,20],"drawn":false},{"_id":"yLioN4DMxU45TnBE","flags":{},"type":2,"text":"Les Marais","img":"systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.png","collection":"foundryvtt-reve-de-dragon.tarot-draconique","resultId":"3U9OCmUD156gwu9b","weight":1,"range":[21,21],"drawn":false}],"formula":"1d21","replacement":true,"displayRoll":true}
diff --git a/packs/tetes-de-dragon-pour-haut-revants.db b/packs/tetes-de-dragon-pour-haut-revants.db
index aeaa1281..c3793ac4 100644
--- a/packs/tetes-de-dragon-pour-haut-revants.db
+++ b/packs/tetes-de-dragon-pour-haut-revants.db
@@ -1,11 +1,10 @@
{"_id":"5JccZSafqCXYqrwU","name":"Connaissance du fleuve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Cette tête permet de téléporter instantanément son demi-rêve de n’importe quelle case de fleuve à n’importe quelle autre. Il s’agit du fleuve seul, à l’exclusion des cases de lac et marais. La téléportation remplace le mouvement normal et coûte donc un point de fatigue. Comme après un mouvement normal, une rencontre doit être tirée dans la case de fleuve d’arrivée, puis cette dernière doit être maîtrisée selon la règle usuelle. Noter enfin que la téléportation n’est possible que si le demi-rêve est libre de son mouvement, c’est-à-dire s’il n’est sous l’emprise ni d’un Reflet, ni d’un Tourbillon. Unique.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
{"_id":"BT18LAdIqEgSG2Hh","name":"Réserve en sécurité","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Un haut-rêvant possédant cette tête peut pénétrer sur une case où il a un sort en réserve sans forcément le déclencher s’il ne le souhaite pas. Même chose si un Tourbillon l’abandonne sur une case de réserve. Déclencher un sort en réserve devient un acte volontaire. Même chose pour un échec total en réserve. Cette tête s’applique à toutes les cases. Unique.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
-{"name":"Présent des cités","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"tete","data":{"description":"
Par cette tête, chaque cité offre automatiquement soit une Fleur des rêves, soit un Passeur, soit un Messager, dès qu’elle est visitée par le demi-rêve du haut-rêvant. Il n’y a pas de d7 à tirer, le haut-rêvant choisit librement ce qu’il préfère, et la rencontre est considérée automatiquement maîtrisée, Fleurs, Messagers et Passeurs ayant une force de 2d6 points. Dès qu’elle a offert son présent, la cité redevient normale en ce qui concerne le tirage des rencontres; il n’y a qu’un seul présent par cité. La tête de Dragon prend fin dès que toutes les cités (22) ont été visitées; entretemps, la tête ne peut être ré-obtenue.
"},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[],"_id":"E4a4O1IdrgbNGpVy"}
+{"_id":"E4a4O1IdrgbNGpVy","name":"Présent des cités","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"tete","data":{"description":"
Par cette tête, chaque cité offre automatiquement soit une Fleur des rêves, soit un Passeur, soit un Messager, dès qu’elle est visitée par le demi-rêve du haut-rêvant. Il n’y a pas de d7 à tirer, le haut-rêvant choisit librement ce qu’il préfère, et la rencontre est considérée automatiquement maîtrisée, Fleurs, Messagers et Passeurs ayant une force de 2d6 points. Dès qu’elle a offert son présent, la cité redevient normale en ce qui concerne le tirage des rencontres; il n’y a qu’un seul présent par cité. La tête de Dragon prend fin dès que toutes les cités (22) ont été visitées; entretemps, la tête ne peut être ré-obtenue.
"},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
{"_id":"SFxPAvWpEGYHI8mO","name":"Connaissance intuitive d'un nouveau sort","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Le haut-rêvant bénéficiaire de cette tête se retrouve en possession d’un nouveau sort ou rituel, déterminé par un procédé aléatoire quelconque.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
{"_id":"VWOXA0q6GB7o8oxz","name":"Don de double-rêve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Lors de la récupération des points de rêve, permet de jeter 1ddr toutes les demi-heures draconiques au lieu de toutes les heures. Unique.
Par cette tête, le haut-rêvant peut désigner une case des TMR de son choix à l’exception d’une case humide. Ensuite, à n’importe quel round de son périple en TMR, il peut s’y téléporter directement quel que soit le nombre de cases qui l’en sépare. Cette téléportation instantanée remplace son mouvement normal et coûte donc un point de fatigue. De même, tout comme après un mouvement normal, 1d7 de rencontre doit être tiré dès l’arrivée dans la terre d’attache. Enfin, le haut-rêvant ne peut s’y téléporter que s’il est libre de son mouvement, c’est-à-dire s’il n’est sous l’emprise ni d’un Reflet d’ancien rêve, ni d’un Tourbillon. Cumulable : on peut avoir plusieurs terres d’attache.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
{"_id":"ZVh8PLlAzAJulr37","name":"Don de déplacement accéléré","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Permet à tout moment l’option du déplacement accéléré dans les TMR sans avoir à dépenser un point de rêve supplémentaire. Unique.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
{"_id":"Zlt01O2sFrVR9pus","name":"Augmentation du seuil de rêve","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Permet d’augmenter le seuil de rêve de 2 points. Cumulable jusqu’à un maximum du double de la caractéristique Rêve.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
-{"_id":"ZuTV36GyOhFgTlE7","name":"Présents de cités","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Par cette tête, chaque cité offre automatiquement soit une Fleur des rêves, soit un Passeur, soit un Messager, dès qu’elle est visitée par le demi-rêve du haut-rêvant. Il n’y a pas de d7 à tirer, le haut-rêvant choisit librement ce qu’il préfère, et la rencontre est considérée automatiquement maîtrisée, Fleurs, Messagers et Passeurs ayant une force de 2d6 points. Dès qu’elle a offert son présent, la cité redevient normale en ce qui concerne le tirage des rencontres; il n’y a qu’un seul présent par cité. La tête de Dragon prend fin dès que toutes les cités (22) ont été visitées; entretemps, la tête ne peut être ré-obtenue.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
{"_id":"a3Y5W0AX5EKxZRSL","name":"Quête des eaux","permission":{"default":0,"jOzRscDxoXZWpGS6":3},"type":"tete","data":{"description":"
Sous l’influence de cette tête, le haut-rêvant est invité à se rendre dans un lac ou dans un marais de son choix. Dès que cette case, lac ou marais, a été maîtrisée selon les règles usuelles, la victoire devient définitive, et la case n’aura plus jamais besoin d’être maîtrisée. Une fois le lac ou le marais choisi, le haut-rêvant ne peut en changer, mais a droit à un nombre illimité d’essais pour le maîtriser. Cumulable : on peut maîtriser définitivement plusieurs lacs ou marais.
Une (seule) case spécifique librement choisie par le haut-rêvant à l’exception d’une case de fleuve, n’est plus dorénavant limitée à un seul sort en réserve. Le haut-rêvant peut y en stocker autant qu’il le désire (dans les limites du nombre permis par ses niveaux). En conséquence, tant que ce maximum n’est pas atteint, le demi-rêve peut entrer dans la case concernée sans déclencher les sorts qui s’y trouvent. Le haut-rêvant peut se contenter d’y passer, ou rajouter un sort aux précédents. Dès que le maximum permis est atteint, le fait de passer par la case déclenche automatiquement l’un des sorts (au choix). Les sorts peuvent être déclenchés l’un après l’autre (rappelons que déclencher un sort fait obligatoirement redescendre le demi-rêve), ou déclenchés ensemble (tout ou partie d’entre eux) à la condition expresse qu’ils aient la même cible (même créature, objet, ou centre de zone). Cumulable : on peut avoir plusieurs cases de réserve extensible.
","refoulement":null},"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp","effects":[]}
diff --git a/packs/vehicules.db b/packs/vehicules.db
index 9ced9deb..f458efb6 100644
--- a/packs/vehicules.db
+++ b/packs/vehicules.db
@@ -5,4 +5,5 @@
{"_id":"RFOYL8HBUxd32DXS","name":"Galère","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"vehicule","data":{"categorie":"Bateau","resistance":36,"structure":14,"vitesse":"2/2/1","bonus":"(12)/+12/+16","manoeuvrabilite":"0/-4/-6","equipage":10,"capacite_encombrement":300,"description":"Description ...","notesmj":"Notes du MJ"},"sort":100001,"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/galere.webp","token":{"flags":{},"name":"Galère","displayName":0,"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/galere_token.webp","tint":"","width":14,"height":14,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"lightAnimation":{"type":"","speed":5,"intensity":5},"actorId":"RFOYL8HBUxd32DXS","actorLink":false,"disposition":0,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[],"effects":[]}
{"_id":"TDpSn7GawJ1LCHp7","name":"Charette","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"vehicule","data":{"categorie":"Chariot","resistance":16,"structure":8,"vitesse":"","bonus":"","manoeuvrabilite":"","equipage":1,"capacite_encombrement":100,"description":"Description ...","notesmj":"Notes du MJ"},"sort":100001,"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/charette.webp","token":{"flags":{},"name":"Charette","displayName":0,"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/charette_token.webp","tint":"","width":3,"height":3,"scale":1.1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"lightAnimation":{"type":"","speed":5,"intensity":5},"actorId":"TDpSn7GawJ1LCHp7","actorLink":false,"disposition":0,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[],"effects":[]}
{"_id":"ZiyRDzz3gGzlpLIc","name":"Barque","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"vehicule","data":{"categorie":"Barque","resistance":20,"structure":10,"vitesse":"3/2/1","bonus":"(4)/+4/+6","manoeuvrabilite":"0/0/-4","equipage":4,"capacite_encombrement":100,"description":"Description ...","notesmj":"Notes du MJ"},"sort":100001,"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/barque.webp","token":{"flags":{},"name":"Barque","displayName":0,"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/barque_token.webp","tint":"","width":6,"height":6,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"lightAnimation":{"type":"","speed":5,"intensity":5},"actorId":"ZiyRDzz3gGzlpLIc","actorLink":false,"disposition":0,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[],"effects":[]}
+{"_id":"gM77co80kmpVsYg6","name":"Posé par terre","permission":{"default":0,"Q2G6GTdrotKzYGUC":3},"type":"vehicule","data":{"categorie":"Autre","resistance":0,"structure":0,"vitesse":"","bonus":"","manoeuvrabilite":"","equipage":0,"capacite_encombrement":100,"description":"
Déposer ici les objets que vous voulez échanger avec d'autres joueurs
","notesmj":""},"sort":100001,"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.png","token":{"flags":{},"name":"Posé par terre","displayName":0,"img":"systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.png","tint":"","width":1,"height":1,"scale":1,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"lightAnimation":{"type":"","speed":5,"intensity":5},"actorId":"gM77co80kmpVsYg6","actorLink":false,"disposition":0,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[],"effects":[]}
{"_id":"idyDmDWYpQ4Eppen","name":"Chariot","permission":{"default":0,"rYShh2P1DNavdoBD":3},"type":"vehicule","data":{"categorie":"Chariot","resistance":20,"structure":10,"vitesse":"","bonus":"","manoeuvrabilite":"","equipage":1,"capacite_encombrement":150,"description":"Description ...","notesmj":"Notes du MJ"},"sort":100001,"flags":{},"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/chariot.webp","token":{"flags":{},"name":"Chariot","displayName":0,"img":"systems/foundryvtt-reve-de-dragon/icons/vehicules/chariot_token.webp","tint":"","width":4,"height":4,"scale":1.4,"mirrorX":false,"mirrorY":false,"lockRotation":false,"rotation":0,"vision":false,"dimSight":0,"brightSight":0,"dimLight":0,"brightLight":0,"sightAngle":360,"lightAngle":360,"lightColor":"","lightAlpha":1,"lightAnimation":{"type":"","speed":5,"intensity":5},"actorId":"idyDmDWYpQ4Eppen","actorLink":false,"disposition":0,"displayBars":0,"bar1":{"attribute":""},"bar2":{"attribute":""},"randomImg":false},"items":[],"effects":[]}
diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html
index a86a4629..2344b651 100644
--- a/templates/actor-sheet.html
+++ b/templates/actor-sheet.html
@@ -155,7 +155,12 @@
- {{#each data.data.attributs as |attr key|}}
+
+ Beauté :
+
+
+
+ {{#each data.attributs as |attr key|}}
{{attr.label}} :
{{#if (eq key 'protection')}}
@@ -511,6 +516,13 @@
Total XP compétences{{data.competenceXPTotal}}
+ {{#if data.montrerArchetype}}
+ {{#each data.comptageArchetype as |archetype key|}}
+
+
+{{/if}}
diff --git a/templates/chat-resultat-chant.html b/templates/chat-resultat-chant.html
index 4143eb70..6af6e9d6 100644
--- a/templates/chat-resultat-chant.html
+++ b/templates/chat-resultat-chant.html
@@ -11,18 +11,4 @@
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
-{{#if oeuvre.data.description}}
-
-
- {{{oeuvre.data.description}}}
- {{#if oeuvre.data.reference}}
-
{{oeuvre.data.reference}}
- {{/if}}
-
-{{else if oeuvre.data.reference}}
-
-
-
{{oeuvre.data.reference}}
-
-{{/if}}
-
+{{> "systems/foundryvtt-reve-de-dragon/templates/chat-poesie.html" oeuvre.data}}
diff --git a/templates/chat-resultat-danse.html b/templates/chat-resultat-danse.html
index abe93ba7..9d12528b 100644
--- a/templates/chat-resultat-danse.html
+++ b/templates/chat-resultat-danse.html
@@ -11,18 +11,4 @@
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
-{{#if oeuvre.data.description}}
-
-
- {{{oeuvre.data.description}}}
- {{#if oeuvre.data.reference}}
-
{{oeuvre.data.reference}}
- {{/if}}
-
-{{else if oeuvre.data.reference}}
-
-
-
- {{/if}}
-
-{{else if oeuvre.data.reference}}
-
-
-
{{oeuvre.data.reference}}
-
-{{/if}}
-
+{{> "systems/foundryvtt-reve-de-dragon/templates/chat-poesie.html" oeuvre.data}}
diff --git a/templates/chat-resultat-musique.html b/templates/chat-resultat-musique.html
index ffea62b6..0b0a6d0b 100644
--- a/templates/chat-resultat-musique.html
+++ b/templates/chat-resultat-musique.html
@@ -11,18 +11,4 @@
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
-{{#if oeuvre.data.description}}
-
-
- {{{oeuvre.data.description}}}
- {{#if oeuvre.data.reference}}
-
{{oeuvre.data.reference}}
- {{/if}}
-
-{{else if oeuvre.data.reference}}
-
-
-
{{oeuvre.data.reference}}
-
-{{/if}}
-
+{{> "systems/foundryvtt-reve-de-dragon/templates/chat-poesie.html" oeuvre.data}}
diff --git a/templates/chat-resultat-oeuvre.html b/templates/chat-resultat-oeuvre.html
index 84141952..d7e349e5 100644
--- a/templates/chat-resultat-oeuvre.html
+++ b/templates/chat-resultat-oeuvre.html
@@ -12,18 +12,4 @@
{{alias}} est peu inspiré(e) et son interprétation a une qualité de {{qualiteFinale}}.
{{/if}}
-{{#if oeuvre.data.description}}
-
-
- {{{oeuvre.data.description}}}
- {{#if oeuvre.data.reference}}
-
{{oeuvre.data.reference}}
- {{/if}}
-
-{{else if oeuvre.data.reference}}
-
-
-
{{oeuvre.data.reference}}
-
-{{/if}}
-
+{{> "systems/foundryvtt-reve-de-dragon/templates/chat-poesie.html" oeuvre.data}}
diff --git a/templates/chat-resultat-recettecuisine.html b/templates/chat-resultat-recettecuisine.html
index fa75d72d..5eb098fd 100644
--- a/templates/chat-resultat-recettecuisine.html
+++ b/templates/chat-resultat-recettecuisine.html
@@ -6,9 +6,9 @@
{{#if rolled.isSuccess}}
- {{alias}} réussit sa recette, avec un plat de {{qualiteFinale}} pour {{oeuvre.data.sust}} Points de Sustentation.
+ {{alias}} réussit sa recette, avec un plat de qualité {{qualiteFinale}} pour {{oeuvre.data.sust}} Points de Sustentation.
{{else}}
- {{alias}} fait un pière cuisinier(e), et obtient {{#if (lt qualiteFinale 0)}}un plat à l'exotisme certain{{else}}un plat de qualité {{qualiteFinale}}{{/if}}.
+ {{alias}} fait un piètre cuisinier(e), et obtient {{#if (lt qualiteFinale 0)}}un plat à l'exotisme certain{{else}}un plat de qualité {{qualiteFinale}}{{/if}}.
Selon la décision du MJ, le plat peut fournir {{oeuvre.data.sust}} Points de Sustentation
{{/if}}
@@ -16,19 +16,4 @@
Au vu de l'exotisme du plat, les convives devront réussir un jet de Volonté / Cuisine à {{exotismeFinal}}.
En cas d'échec, ils peuvent se forcer pour faire plaisir au Maître Queux, mais devront faire un jet de moral Malheureux.
{{/if}}
-
-{{#if oeuvre.data.description}}
-
-
- {{{oeuvre.data.description}}}
- {{#if oeuvre.data.reference}}
-
{{oeuvre.data.reference}}
- {{/if}}
-
-{{else if oeuvre.data.reference}}
-
-
-