Merge branch 'v1.4-draconique' into 'v1.4'

V1.4 draconique

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!160
This commit is contained in:
Leratier Bretonnien 2021-02-28 07:34:54 +00:00
commit f3528e62a4
16 changed files with 469 additions and 141 deletions

View File

@ -310,18 +310,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 });
}
}
@ -1156,26 +1157,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;
@ -1189,7 +1170,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) {
@ -1206,7 +1187,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) {
@ -1228,9 +1209,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 });
}
/* -------------------------------------------- */
@ -3199,6 +3178,9 @@ export class RdDActor extends Actor {
case 'souffle':
await this.onDeleteOwnedDraconique(item, options, id);
break;
case 'casetmr':
await this.onDeleteOwnedCaseTmr(item, options, id);
break;
}
}
@ -3220,6 +3202,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),

View File

@ -26,7 +26,7 @@ export class Grammar {
/* -------------------------------------------- */
static articleDetermine(genre) {
switch (toLowerCaseNoAccent(genre)) {
switch (Grammar.toLowerCaseNoAccent(genre)) {
case 'f': case 'feminin': return 'la';
case 'p': case 'mp': case 'fp': case 'pluriel': return 'les';
default:
@ -35,8 +35,8 @@ export class Grammar {
}
/* -------------------------------------------- */
static articleIndétermine(genre) {
switch (toLowerCaseNoAccent(genre)) {
static articleIndetermine(genre) {
switch (Grammar.toLowerCaseNoAccent(genre)) {
case 'f': case 'feminin': return 'une';
case 'p': case 'fp': case 'mp': case 'pluriel': return 'des';
case 'n': case 'neutre': return 'du'
@ -58,7 +58,7 @@ export class Grammar {
* @param {...any} mots
*/
static accord(genre, ...mots) {
switch (toLowerCaseNoAccent(genre)) {
switch (Grammar.toLowerCaseNoAccent(genre)) {
default:
case 'n': case 'neutre':
case 'm': case 'masculin': return mots[0];

View File

@ -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>Dune argentine fontaine,
<br>Qui dune fuite lointaine,
<br>Te rends au sein fluctueux
<br>De lOcéan monstrueux`
<br>Prend sa bienheureuse source,
<br>Dune argentine fontaine,
<br>Qui dune fuite lointaine,
<br>Te rends au sein fluctueux
<br>De lOcé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&raquo;`
},
{
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 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>&laquo; 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 ? &raquo;`
},
{
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 je rêve, et la gourde
<br> 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>-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);
}

View File

@ -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" });

View File

@ -48,14 +48,22 @@ 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 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);

View File

@ -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);

View File

@ -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 },
@ -332,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) {
@ -384,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');
}
@ -435,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);
}
/* -------------------------------------------- */
@ -450,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
}
}
@ -460,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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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));
}
/* -------------------------------------------- */

View File

@ -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);
}
}

View File

@ -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
View 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;
}
}

View 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 }
});
}
}

View File

@ -1,12 +1,13 @@
<option value="maitrisee">Case humide maitrisée (Quête des Eaux)</option>
<option value="conquete">Conquête</option>
<option value="debordement">Débordement</option>
<option value="desorientation">Désorientation</option>
<option value="fermeture">Fermeture cité</option>
<option value="pelerinage">Pèlerinage</option>
<option value="periple">Périple</option>
<option value="pont-impraticable">Pont impraticable</option>
<option value="present-cites">Présents des cités</option>
<option value="reserve_extensible">Réserve extensible</option>
<option value="attache">Terre d'Attache</option>
<option value="trounoir">Trou Noir</option>
<option value="debordement">Débordement</option>
<option value="reserve_extensible">Réserve extensible</option>
<option value="maitrisee">Case humide maitrisée (Quête des Eaux)</option>
<option value="fermeture">Fermeture cité</option>
<option value="pont-impraticable">Pont impraticable</option>
<option value="desorientation">Désoriantation</option>
<option value="periple">Periple</option>
<option value="conquete">Conquête</option>
<option value="pelerinage">Pèlerinage</option>
<option value="present-cites">Présents des cités</option>
<option value="urgence">Urgence draconique</option>