Merge branch 'v1.4' into 'dev_0.8'
# Conflicts: # module/actor-sheet.js # system.json # templates/actor-sheet.html
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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 <strong>Urgence Draconique</strong> : " + item.data.description);
|
||||
}
|
||||
if (EffetsDraconiques.isPeriple(item)) {
|
||||
messages.push("Vous souffrez du Souffle <strong>Périple</strong>. Vous devez gérer manuellement le détail du Périple.<br>" + item.data.description);
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.length > 0) {
|
||||
ChatMessage.create({
|
||||
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
|
||||
content: "RAPPEL !<br>" + messages.join('<hr>')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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),
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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={}) {
|
||||
|
||||
@@ -4,25 +4,25 @@ const poesieHautReve = [
|
||||
{
|
||||
reference: 'Le Ratier Bretonien',
|
||||
extrait: `Le courant du Fleuve
|
||||
<br>Te domine et te Porte
|
||||
<br>Avant que tu te moeuves
|
||||
<br>Combat le, ou il t'emporte`
|
||||
<br>Te domine et te Porte
|
||||
<br>Avant que tu te moeuves
|
||||
<br>Combat le, ou il t'emporte`
|
||||
},
|
||||
{
|
||||
reference: 'Incompatibilité, Charles Beaudelaire',
|
||||
extrait: `Et lorsque par hasard une nuée errante
|
||||
<br>Assombrit dans son vol le lac silencieux,
|
||||
<br>On croirait voir la robe ou l'ombre transparente
|
||||
<br>D'un esprit qui voyage et passe dans les cieux.`
|
||||
<br>Assombrit dans son vol le lac silencieux,
|
||||
<br>On croirait voir la robe ou l'ombre transparente
|
||||
<br>D'un esprit qui voyage et passe dans les cieux.`
|
||||
},
|
||||
{
|
||||
reference: 'Au fleuve de Loire, Joachim du Bellay',
|
||||
extrait: `Ô de qui la vive course
|
||||
<br>Prend sa bienheureuse source,
|
||||
<br>D’une argentine fontaine,
|
||||
<br>Qui d’une fuite lointaine,
|
||||
<br>Te rends au sein fluctueux
|
||||
<br>De l’Océan monstrueux`
|
||||
<br>Prend sa bienheureuse source,
|
||||
<br>D’une argentine fontaine,
|
||||
<br>Qui d’une fuite lointaine,
|
||||
<br>Te rends au sein fluctueux
|
||||
<br>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,
|
||||
<br>Se pâment longuement sous l'ardeur des climats ;
|
||||
<br>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,
|
||||
<br>Je ne me sentis plus guidé par les haleurs :
|
||||
<br>Des Peaux-Rouges criards les avaient pris pour cibles,
|
||||
<br>Les ayant cloués nus aux poteaux de couleurs.`
|
||||
},
|
||||
{
|
||||
reference: "Le bateau ivre, Arthur Rimbaud",
|
||||
extrait: `Loin des peuples vivants, errantes, condamnées,
|
||||
<br>A travers les déserts courez comme les loups ;
|
||||
<br>Faites votre destin, âmes désordonnées,
|
||||
<br>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
|
||||
<br>Trouveront dans ce sol lavé comme une grève
|
||||
<br>Le mystique aliment qui ferait leur vigueur ?`
|
||||
},
|
||||
{
|
||||
reference: "Une charogne, Charles Baudelaire",
|
||||
extrait: `Et le ciel regardait la carcasse superbe
|
||||
<br>Comme une fleur s'épanouir.
|
||||
<br>La puanteur était si forte, que sur l'herbe
|
||||
<br>Vous crûtes vous évanouir.`
|
||||
},
|
||||
{
|
||||
reference: "Conseil, Victor Hugo",
|
||||
extrait: `Rois ! la bure est souvent jalouse du velours.
|
||||
<br>Le peuple a froid l'hiver, le peuple a faim toujours.
|
||||
<br>Rendez-lui son sort plus facile.
|
||||
<br>Le peuple souvent porte un bien rude collier.
|
||||
<br>Ouvrez l'école aux fils, aux pères l'atelier,
|
||||
<br>À tous vos bras, auguste asile !`
|
||||
},
|
||||
{
|
||||
reference: "El Desdichado, Gérard de Nerval",
|
||||
extrait: `Suis-je Amour ou Phébus ?... Lusignan ou Biron ?
|
||||
<br>Mon front est rouge encor du baiser de la Reine ;
|
||||
<br>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
|
||||
<br>Chaque âme selon son désir,
|
||||
<br>Et que, close après vous, la porte
|
||||
<br>Ne se rouvre plus qu'au plaisir.`
|
||||
},
|
||||
{
|
||||
reference: "Rêve de Dragon, Denis Gerfaud",
|
||||
extrait: `Les sages ont encore coutume de dire :
|
||||
<br>« 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,
|
||||
<br>Dans la plainte du jour,
|
||||
<br>Ma mémoire devient grise
|
||||
<br>Et sombre, tour à tour,
|
||||
<br>Dans le puits du silence
|
||||
<br>Et de la solitude ;
|
||||
<br>Elle reprend son errance
|
||||
<br>Parmi la multitude.`
|
||||
}
|
||||
,
|
||||
{
|
||||
reference: "Une charogne, Charles Baudelaire",
|
||||
extrait: `Les formes s'effaçaient et n'étaient plus qu'un rêve,
|
||||
<br>Une ébauche lente à venir
|
||||
<br>Sur la toile oubliée, et que l'artiste achève
|
||||
<br>Seulement par le souvenir.`
|
||||
},
|
||||
{
|
||||
reference: "La chevelure, Charles Baudelaire",
|
||||
extrait: `Longtemps ! toujours ! ma main dans ta crinière lourde
|
||||
<br>Sèmera le rubis, la perle et le saphir,
|
||||
<br>Afin qu'à mon désir tu ne sois jamais sourde !
|
||||
<br>N'es-tu pas l'oasis où je rêve, et la gourde
|
||||
<br>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.
|
||||
<br>Le Sage se retourne et lui dit : Mon ami,
|
||||
<br>C'est fort bien fait à toi ; reçois cet écu-ci :
|
||||
<br>Tu fatigues assez pour gagner davantage.`
|
||||
},
|
||||
{
|
||||
reference: "Guitare, Victor Hugo",
|
||||
extrait: `Je la voyais passer de ma demeure,
|
||||
<br>Et c'était tout.
|
||||
<br>Mais à présent je m'ennuie à toute heure,
|
||||
<br>Plein de dégoût,
|
||||
<br>Rêveur oisif, l'âme dans la campagne,
|
||||
<br>La dague au clou ... –
|
||||
<br>Le vent qui vient à travers la montagne
|
||||
<br>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,
|
||||
<br>Et tourbillonne en sifflant !
|
||||
<br>Les ifs, que leur vol fracasse,
|
||||
<br>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
|
||||
<br>Que la couleur du désespoir
|
||||
<br>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
|
||||
<br>De fumière à flot gris, parmi l'air se jouant,
|
||||
<br>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 !
|
||||
<br>L'horrible essaim, poussé par l'aquilon,
|
||||
<br>Sans doute, ô ciel ! s'abat sur ma demeure.
|
||||
<br>Le mur fléchit sous le noir bataillon.
|
||||
<br>La maison crie et chancelle penchée,
|
||||
<br>Et l'on dirait que, du sol arrachée,
|
||||
<br>Ainsi qu'il chasse une feuille séchée,
|
||||
<br>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
|
||||
<br>ni leur apparence ni qui sont les dragons.
|
||||
<br>En dépit de l'iconographie qui les clame
|
||||
<br>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é,
|
||||
<br>Le Prince d'Aquitaine à la Tour abolie :
|
||||
<br>Ma seule Etoile est morte, – et mon luth constellé
|
||||
<br>Porte le Soleil noir de la Mélancolie.`
|
||||
},
|
||||
]
|
||||
|
||||
export class Poetique {
|
||||
static getExtrait(){
|
||||
static getExtrait() {
|
||||
return Misc.rollOneOf(poesieHautReve);
|
||||
}
|
||||
|
||||
|
||||
@@ -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})
|
||||
<br>
|
||||
`,
|
||||
},
|
||||
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 = `<h4>L'initiative de ${combatant.actor.name} a été modifiée !</h4>
|
||||
<hr>
|
||||
<div>
|
||||
Etant donné son ${arme.name}, son initative pour ce premier round est désormais de ${initData.init}.
|
||||
</div>`
|
||||
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 = '<i class="far fa-question-circle"></i>';
|
||||
option.callback = target => {
|
||||
RdDCombatManager.displayInitiativeMenu(html, target.data('combatant-id'));
|
||||
}
|
||||
}
|
||||
}
|
||||
options = [
|
||||
{ name: "Incrémenter initiative", condition: true, icon: '<i class="fas fa-plus"></i>', callback: target => { RdDCombatManager.incDecInit(target.data('combatant-id'), +0.01); } },
|
||||
{ name: "Décrémenter initiative", condition: true, icon: '<i class="fas fa-minus"></i>', 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: "<i class='fas fa-dice-d6'></i>",
|
||||
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: `<strong>Echec total à ${action}!</strong> ` + await RdDRollTables.getMaladresse({ arme: avecArme })
|
||||
content: `<strong>Maladresse à ${action}!</strong> ` + 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 });
|
||||
}
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <strong>Reserve en Sécurité</strong> ou <strong>Réserve Exensible</strong>, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher : <ul>";
|
||||
for (let sortReserve of sortReserveList) {
|
||||
msg += "<li><a class='chat-card-button' id='sort-reserve' data-actor-id='" + this.actor._id + "' data-tmr-coord='" + coord + "' data-sort-id='" + sortReserve.sort._id + "'>" + sortReserve.sort.name + "</a></li>";
|
||||
@@ -663,9 +686,9 @@ export class RdDTMRDialog extends Dialog {
|
||||
content: msg,
|
||||
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
||||
});
|
||||
} else {
|
||||
await this.processSortReserve(sortReserveList[0]);
|
||||
return;
|
||||
}
|
||||
await this.processSortReserve(sortReserveList[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,18 +858,19 @@ export class RdDTMRDialog extends Dialog {
|
||||
if (!(this.viewOnly || this.currentRencontre)) {
|
||||
await this.manageCaseHumide(tmr);
|
||||
await this.conquerirCiteFermee(tmr);
|
||||
await this.purifierPeriple(tmr);
|
||||
await this.conquerirTMR(tmr);
|
||||
await this.validerPelerinage(tmr);
|
||||
await this.validerVisite(tmr);
|
||||
await this.declencheSortEnReserve(tmr.coord);
|
||||
await this.actor.checkSoufflePeage(tmr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async forceDemiRevePositionView() {
|
||||
this._updateDemiReve();
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------- */
|
||||
async forceDemiRevePosition(coord) {
|
||||
await this.actor.updateCoordTMR(coord);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* -------------------------------------------- */
|
||||
import { HtmlUtility } from "./html-utility.js";
|
||||
import { RdDCombatManager } from "./rdd-combat.js";
|
||||
import { RdDUtility } from "./rdd-utility.js";
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -26,7 +27,7 @@ export class RdDTokenHud {
|
||||
let combatant = game.combat.data.combatants.find(c => c.tokenId == token.data._id);
|
||||
app.hasExtension = true;
|
||||
|
||||
let armesList = RdDUtility.buildListeActionsCombat(combatant) ;
|
||||
let armesList = RdDCombatManager.buildListeActionsCombat(combatant) ;
|
||||
const hudData = { combatant: combatant, armes: armesList,
|
||||
commandes: [{ name: 'Initiative +1', command: 'inc', value: 0.01}, { name: 'Initiative -1',command: 'dec', value: -0.01}] };
|
||||
|
||||
@@ -38,11 +39,11 @@ export class RdDTokenHud {
|
||||
if ( !initCommand ) {
|
||||
let armeIndex = event.currentTarget.attributes['data-arme-id'].value;
|
||||
let arme = armesList[armeIndex];
|
||||
RdDUtility.rollInitiativeCompetence(combatantId, arme);
|
||||
RdDCombatManager.rollInitiativeCompetence(combatantId, arme);
|
||||
} else if (initCommand == 'inc') {
|
||||
RdDUtility.incDecInit( combatantId, 0.01 );
|
||||
RdDCombatManager.incDecInit( combatantId, 0.01 );
|
||||
} else if ( initCommand == 'dec') {
|
||||
RdDUtility.incDecInit( combatantId, -0.01 );
|
||||
RdDCombatManager.incDecInit( combatantId, -0.01 );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { RdDRollTables } from "./rdd-rolltables.js";
|
||||
import { ChatUtility } from "./chat-utility.js";
|
||||
import { RdDCombat } from "./rdd-combat.js";
|
||||
import { RdDCombat, RdDCombatManager } from "./rdd-combat.js";
|
||||
import { RdDRollResolutionTable } from "./rdd-roll-resolution-table.js";
|
||||
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
|
||||
import { RdDItemArme } from "./item-arme.js";
|
||||
@@ -22,6 +22,22 @@ const categorieCompetences = {
|
||||
"lancer": { level: "-8", label: "Lancer" }
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
const limitesArchetypes = [
|
||||
{ "niveau": 0, "nombreMax": 100, "nombre": 0 },
|
||||
{ "niveau": 1, "nombreMax": 10, "nombre": 0 },
|
||||
{ "niveau": 2, "nombreMax": 9, "nombre": 0 },
|
||||
{ "niveau": 3, "nombreMax": 8, "nombre": 0 },
|
||||
{ "niveau": 4, "nombreMax": 7, "nombre": 0 },
|
||||
{ "niveau": 5, "nombreMax": 6, "nombre": 0 },
|
||||
{ "niveau": 6, "nombreMax": 5, "nombre": 0 },
|
||||
{ "niveau": 7, "nombreMax": 4, "nombre": 0 },
|
||||
{ "niveau": 8, "nombreMax": 3, "nombre": 0 },
|
||||
{ "niveau": 9, "nombreMax": 2, "nombre": 0 },
|
||||
{ "niveau": 10, "nombreMax": 1, "nombre": 0 },
|
||||
{ "niveau": 11, "nombreMax": 1, "nombre": 0 }
|
||||
];
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// This table starts at 0 -> niveau -10
|
||||
const carac_array = ["taille", "apparence", "constitution", "force", "agilite", "dexterite", "vue", "ouie", "odoratgout", "volonte", "intellect", "empathie", "reve", "chance", "melee", "tir", "lancer", "derobee"];
|
||||
@@ -65,27 +81,6 @@ const tableCaracDerivee = {
|
||||
32: { xp: 180, poids: "1501-2000", plusdom: +11, sconst: 10, sust: 17 }
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
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 },
|
||||
];
|
||||
|
||||
/* -------------------------------------------- */
|
||||
function _buildAllSegmentsFatigue(max) {
|
||||
const cycle = [5, 2, 4, 1, 3, 0];
|
||||
@@ -235,6 +230,7 @@ export class RdDUtility {
|
||||
'systems/foundryvtt-reve-de-dragon/templates/hud-actor-attaque.html',
|
||||
// messages tchat
|
||||
'systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/chat-poesie.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.html',
|
||||
'systems/foundryvtt-reve-de-dragon/templates/chat-demande-attaque-particuliere.html',
|
||||
@@ -258,10 +254,17 @@ export class RdDUtility {
|
||||
Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? 'NULL');
|
||||
Handlebars.registerHelper('le', str => Grammar.articleDetermine(str));
|
||||
Handlebars.registerHelper('un', str => Grammar.articleIndetermine(str));
|
||||
Handlebars.registerHelper('accord', (genre, ...args) => Grammar.accord(genre, args));
|
||||
Handlebars.registerHelper('buildConteneur', (objet) => { return RdDUtility.buildConteneur(objet); });
|
||||
|
||||
return loadTemplates(templatePaths);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getLimitesArchetypes() {
|
||||
return duplicate(limitesArchetypes);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static checkNull(items) {
|
||||
if (items && items.length) {
|
||||
@@ -437,50 +440,6 @@ export class RdDUtility {
|
||||
return tableCaracDerivee[targetValue]?.xp ?? 200;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/** 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 = RdDUtility.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 = RdDUtility.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 calculInitiative(niveau, caracValue, bonusEcaille = 0) {
|
||||
let base = niveau + Math.floor(caracValue / 2);
|
||||
base += bonusEcaille;
|
||||
return "1d6" + (base >= 0 ? "+" : "") + base;
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static computeCarac(data) {
|
||||
data.carac.force.value = Math.min(data.carac.force.value, parseInt(data.carac.taille.value) + 4);
|
||||
@@ -687,173 +646,8 @@ export class RdDUtility {
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static processPremierRoundInit() {
|
||||
// Check if we have the whole init !
|
||||
if (game.user.isGM) {
|
||||
let initDone = true;
|
||||
for (let combatant of game.combat.data.combatants) {
|
||||
if (!combatant.initiative) initDone = false;
|
||||
}
|
||||
if (initDone && game.combat.current.round == 1) { // 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 = `<h4>L'initiative de ${combatant.actor.name} a été modifiée !</h4>
|
||||
<hr>
|
||||
<div>
|
||||
Etant donné son ${arme.name}, son initative pour ce premier round est désormais de ${initData.init}.
|
||||
</div>`
|
||||
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: "<i class='fas fa-dice-d6'></i>",
|
||||
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 = '<i class="far fa-question-circle"></i>';
|
||||
option.callback = target => {
|
||||
RdDUtility.displayInitiativeMenu(html, target.data('combatant-id'));
|
||||
}
|
||||
}
|
||||
}
|
||||
options.push({
|
||||
name: "Incrémenter initiative",
|
||||
condition: true,
|
||||
icon: '<i class="fas fa-plus"></i>',
|
||||
callback: target => {
|
||||
RdDUtility.incDecInit(target.data('combatant-id'), +0.01);
|
||||
}
|
||||
});
|
||||
options.push({
|
||||
name: "Décrémenter initiative",
|
||||
condition: true,
|
||||
icon: '<i class="fas fa-minus"></i>',
|
||||
callback: target => {
|
||||
RdDUtility.incDecInit(target.data('combatant-id'), -0.01);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async chatListeners(html) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
44
module/tmr/periple.js
Normal file
44
module/tmr/periple.js
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
55
module/tmr/urgence-draconique.js
Normal file
55
module/tmr/urgence-draconique.js
Normal file
@@ -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 }
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user