Merge branch 'queues-souffles' into 'v1.3'

Queues souffles

See merge request LeRatierBretonnien/foundryvtt-reve-de-dragon!150
This commit is contained in:
Leratier Bretonnien 2021-02-12 09:44:01 +00:00
commit 1c5d14a1fa
22 changed files with 410 additions and 211 deletions

View File

@ -1121,6 +1121,24 @@ export class RdDActor extends Actor {
return queue;
}
/* -------------------------------------------- */
async reinsertionAleatoire(raison) {
ChatMessage.create({
content: `${raison} : ré-insertion aléatoire.`,
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name)
});
const innaccessible = this.buildTMRInnaccessible();
let tmr = TMRUtility.getTMRAleatoire(tmr => !innaccessible.includes(tmr.coord) );
this.updateCoordTMR(tmr.coord);
return tmr;
}
buildTMRInnaccessible() {
const tmrInnaccessibles = this.data.items.filter(it => Draconique.isCaseTMR(it) &&
EffetsDraconiques.isInnaccessible(it));
return tmrInnaccessibles.map(it => it.data.coord);
}
/* -------------------------------------------- */
displayTMRQueueSouffleInformation() {
let messages = [];
@ -1137,9 +1155,6 @@ export class RdDActor extends Actor {
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 (EffetsDraconiques.isDesorientation(item)) {
messages.push("Vous souffrez du Souffle <strong>Désorientation</strong>. Vous devez gérer avec votre MJ les effets de ce souffle.<br>" + item.data.description);
}
}
if (messages.length > 0) {
@ -1796,6 +1811,10 @@ export class RdDActor extends Actor {
ui.notifications.info("Aucun sort disponible pour cette case !");
return;
}
if (EffetsDraconiques.isConquete(this)) {
ui.notifications.error("Vous ne pouvez pas lancer de sort sous l'effet d'une conquête!");
return;
}
if (this.currentTMR) this.currentTMR.minimize(); // Hide
let draconicList = this.filterDraconicList(sortList);
@ -2708,25 +2727,23 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async resetItemUse( ) {
await this.setFlag('foundryvtt-reve-de-dragon', 'itemUse', null );
await this.unsetFlag('foundryvtt-reve-de-dragon', 'itemUse');
await this.setFlag('foundryvtt-reve-de-dragon', 'itemUse', {} );
}
/* -------------------------------------------- */
async incItemUse( itemId ) {
let itemUse = this.getFlag('foundryvtt-reve-de-dragon', 'itemUse');
itemUse = (itemUse) ? duplicate(itemUse) : {};
itemUse[itemId] = (itemUse[itemId]) ? itemUse[itemId] + 1 : 1;
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 INC", itemUse);
}
/* -------------------------------------------- */
getItemUse( itemId ) {
let itemUse = this.getFlag('foundryvtt-reve-de-dragon', 'itemUse');
itemUse = (itemUse) ? itemUse : {};
let itemUse = this.getFlag('foundryvtt-reve-de-dragon', 'itemUse') ?? {};
console.log("ITEM USE GET", itemUse);
return itemUse[itemId] ? itemUse[itemId] : 0;
return itemUse[itemId] ?? 0;
}
/* -------------------------------------------- */

View File

@ -59,4 +59,12 @@ export class Misc {
}
}
static rollOneOf(array) {
return array[new Roll("1d" + array.length).evaluate().total - 1];
}
static distinct(array) {
return [...new Set(array)];
}
}

View File

@ -1,3 +1,4 @@
import { Misc } from "./misc.js"
const poesieHautReve = [
{
@ -64,7 +65,7 @@ const poesieHautReve = [
export class Poetique {
static getExtrait(){
return poesieHautReve[new Roll("1d" + poesieHautReve.length).evaluate().total - 1]
return Misc.rollOneOf(poesieHautReve);
}
}

View File

@ -1,3 +1,4 @@
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
const words = [ 'pore', 'pre', 'flor', 'lane', 'turlu', 'pin', 'a', 'alph', 'i', 'onse', 'iane', 'ane', 'zach', 'arri', 'ba', 'bo', 'bi',
@ -7,9 +8,7 @@ const words = [ 'pore', 'pre', 'flor', 'lane', 'turlu', 'pin', 'a', 'alph', 'i',
export class RdDNameGen {
static getName( msg, params ) {
let max = words.length;
let name = words[new Roll("1d"+max+" -1").roll().total];
name += words[new Roll("1d"+max+" -1").roll().total];
let name = Misc.upperFirst( Misc.rollOneOf(words) + Misc.rollOneOf(words) )
//console.log(name);
ChatMessage.create( { content: `Nom : ${name}`, whisper: ChatMessage.getWhisperRecipients("GM") } );
}

View File

@ -10,6 +10,7 @@ import { Poetique } from "./poetique.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
import { Grammar } from "./grammar.js";
/* -------------------------------------------- */
export class RdDTMRDialog extends Dialog {
@ -50,7 +51,7 @@ export class RdDTMRDialog extends Dialog {
this.cumulFatigue = 0;
this.loadRencontres();
this.loadSortsReserve();
this.casesSpeciales = this.actor.data.items.filter(item => Draconique.isCaseTMR(item));
this.loadCasesSpeciales();
this.allTokens = [];
this.rencontreState = 'aucune';
this.pixiApp = new PIXI.Application({ width: 720, height: 860 });
@ -66,6 +67,10 @@ export class RdDTMRDialog extends Dialog {
this.pixiTMR.load((loader, resources) => this.createPixiSprites());
}
loadCasesSpeciales() {
this.casesSpeciales = this.actor.data.items.filter(item => Draconique.isCaseTMR(item));
}
loadSortsReserve() {
this.sortsReserves = duplicate(this.actor.data.data.reve.reserve.list);
}
@ -99,6 +104,7 @@ export class RdDTMRDialog extends Dialog {
this._removeTokens(t => true);
this.loadRencontres();
this.loadSortsReserve();
this.loadCasesSpeciales();
this._createTokens();
}
@ -396,10 +402,13 @@ export class RdDTMRDialog extends Dialog {
if (rencontre) {
return rencontre;
}
// TODO: dialog pour remplacer la rencontre par un présent
//if (this.casesSpeciales.find(c => EffetsDraconiques.isPresentCite(c, tmr.coord))) {
if (this.casesSpeciales.find(c => EffetsDraconiques.isPresentCite(c, tmr.coord))) {
// TODO: dialog pour remplacer la rencontre par un présent
}
let myRoll = new Roll("1d7").evaluate().total;
if (TMRUtility.isForceRencontre() || myRoll == 7) {
return await this.rencontreTMRRoll(tmr, this.actor.isRencontreSpeciale());
@ -415,30 +424,22 @@ export class RdDTMRDialog extends Dialog {
? await TMRRencontres.getMauvaiseRencontre()
: await TMRRencontres.getRencontreAleatoire(tmr.type));
rencontre.coord = tmr.coord;
rencontre.date = game.system.rdd.calendrier.getDateFromIndex();
rencontre.date = game.system.rdd.calendrier.getDateFromIndex();
rencontre.heure = game.system.rdd.calendrier.getCurrentHeure();
return rencontre;
}
/* -------------------------------------------- */
async manageCaseSpeciale(tmr) {
if (this.casesSpeciales.find(c => EffetsDraconiques.isCaseTrouNoir(c, tmr.coord))) {
let newTMR = TMRUtility.getTMRAleatoire();
let tmrPos = duplicate(this.actor.data.data.reve.tmrpos);
tmrPos.coord = newTMR.coord;
await this.actor.update({ "data.reve.tmrpos": tmrPos });
ChatMessage.create({
content: "Vous êtes rentré sur un Trou Noir : ré-insertion aléatoire.",
whisper: ChatMessage.getWhisperRecipients(game.user.name)
});
async manageTmrInnaccessible(tmr) {
const caseTmrInnaccessible = this.casesSpeciales.find(c => EffetsDraconiques.isInnaccessible(c, tmr.coord));
if (caseTmrInnaccessible) {
return await this.actor.reinsertionAleatoire(caseTmrInnaccessible.name);
}
return tmr;
}
/* -------------------------------------------- */
async manageCaseHumide(tmr) {
if (this.viewOnly || this.currentRencontre) {
return;
}
if (this.isCaseHumide(tmr)) {
let rollData = {
actor: this.actor,
@ -449,8 +450,8 @@ export class RdDTMRDialog extends Dialog {
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } },
maitrise: { verbe: 'maîtriser', action: 'Maîtriser le fleuve' }
}
rollData.double = EffetsDraconiques.isDoubleResistanceFleuve(this.actor) ? true: undefined,
rollData.competence.data.defaut_carac = 'reve-actuel';
await this._rollMaitriseCaseHumide(rollData);
}
}
@ -465,8 +466,9 @@ export class RdDTMRDialog extends Dialog {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
this.toclose = rollData.rolled.isEchec;
if (rollData.rolled.isSuccess && !rollData.previous && EffetsDraconiques.isDoubleResistanceFleuve(this.actor)) {
if (rollData.rolled.isSuccess && rollData.double) {
rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements };
rollData.double = undefined;
await this._rollMaitriseCaseHumide(rollData);
return;
}
@ -513,29 +515,55 @@ export class RdDTMRDialog extends Dialog {
}
return false;
}
/* -------------------------------------------- */
async conquerirCiteFermee(tmr) {
if (this.viewOnly || this.currentRencontre) {
return;
}
const citeFermee = this.isCiteFermee(tmr.coord);
if (citeFermee) {
let rollData = {
actor: this.actor,
competence: duplicate(this.actor.getBestDraconic()),
tmr: tmr,
canClose: false,
diffLibre: -9,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } },
maitrise: { verbe: 'conquérir', action: 'Conquérir la cité' }
}
rollData.competence.data.defaut_carac = 'reve-actuel';
await this._maitriserTMR(rollData, r => this._resultatConqueteCiteFermee(r));
if (EffetsDraconiques.fermetureCites.find(this.casesSpeciales, tmr.coord)) {
await this._conquerir(tmr, {
difficulte: -9,
action: 'Conquérir la cité',
onConqueteReussie: r => EffetsDraconiques.fermetureCites.onConquete(r.actor, tmr.coord),
onConqueteEchec: r => this.close(),
canClose: false
});
}
}
async _resultatConqueteCiteFermee(rollData) {
removeToken(tmr, casetmr) {
this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id);
this.updateTokens()
}
/* -------------------------------------------- */
async conquerirTMR(tmr) {
if (EffetsDraconiques.conquete.find(this.casesSpeciales, tmr.coord)) {
await this._conquerir(tmr, {
difficulte: -7,
action: 'Conquérir',
onConqueteReussie: r => EffetsDraconiques.conquete.onConquete(r.actor, tmr.coord, (casetmr) => this.removeToken(tmr, casetmr)),
onConqueteEchec: r => {},
canClose: false
});
}
}
/* -------------------------------------------- */
async _conquerir(tmr, options) {
let rollData = {
actor: this.actor,
competence: duplicate(this.actor.getBestDraconic()),
tmr: tmr,
canClose: options.canClose ?? false,
diffLibre: options.difficulte ?? -7,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: this.actor.getReveActuel() } },
maitrise: { verbe: 'conquérir', action: options.action }
};
rollData.competence.data.defaut_carac = 'reve-actuel';
await this._maitriserTMR(rollData, r => this._onResultatConquerir(r, options));
}
async _onResultatConquerir(rollData, options) {
if (rollData.rolled.isETotal) {
rollData.souffle = await this.actor.ajouterSouffle({ chat: false });
}
@ -547,12 +575,11 @@ export class RdDTMRDialog extends Dialog {
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-resultat-maitrise-tmr.html`, rollData)
});
if (rollData.rolled.isEchec) {
this.close();
options.onConqueteEchec(rollData, options.effetDraconique);
}
else {
const citeFermee = this.actor.data.items.find(it => EffetsDraconiques.isCiteFermee(it, rollData.tmr.coord));
this.actor.deleteOwnedItem(citeFermee._id);
this._removeTokens(t => t.coordTMR() == citeFermee.data.coord && t.caseSpeciale?._id == citeFermee._id);
await options.onConqueteReussie(rollData, options.effetDraconique);
this.updateTokens();
}
}
@ -577,17 +604,18 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
async declencheSortEnReserve(coordTMR) {
if (this.viewOnly) {
return;
}
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coordTMR);
async declencheSortEnReserve(coord) {
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord);
if (sortReserveList.length > 0) {
if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coordTMR)) {
if (EffetsDraconiques.isConquete(this.actor)) {
ui.notifications.error("Vous ne pouvez pas déclencher de sort sous l'effet d'une conquête!");
return;
}
if (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='" + coordTMR + "' data-sort-id='" + sortReserve.sort._id + "'>" + sortReserve.sort.name + "</a></li>";
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>";
}
msg += "</ol>";
ChatMessage.create({
@ -601,8 +629,8 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
lancerSortEnReserve(coordTMR, sortId) {
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coordTMR);
lancerSortEnReserve(coord, sortId) {
let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord);
let sortReserve = sortReserveList.find(sortReserve => sortReserve.sort._id == sortId);
//console.log("SORT RESA", sortReserveList, coordTMR, sortId, sortReserve);
if (sortReserve) {
@ -620,10 +648,10 @@ export class RdDTMRDialog extends Dialog {
await this.actor.deleteSortReserve(sortReserve);
this.updateSortReserve();
console.log("declencheSortEnReserve", sortReserve)
const declenchementSort = "Vous avez déclenché le sort <strong>" + sortReserve.sort.name
+ "</strong> en réserve en " + sortReserve.coord + " (" + TMRUtility.getTMR(sortReserve.coord).label
+ ") avec " + sortReserve.sort.data.ptreve_reel + " points de Rêve";
this._tellToGM(declenchementSort);
this._tellToGM(`Vous avez déclenché le sort en réserve <strong> ${sortReserve.sort.name}</strong>
avec ${sortReserve.sort.data.ptreve_reel} points de Rêve
en ${sortReserve.coord} (${TMRUtility.getTMRLabel(sortReserve.coord)})
`);
this.close();
}
@ -639,40 +667,28 @@ export class RdDTMRDialog extends Dialog {
this.rencontreState = 'aucune'; // Et de l'état
}
/* -------------------------------------------- */
processClickPostRencontre(coord) {
if (this.rencontreState == 'passeur' || this.rencontreState == 'messager' || this.rencontreState == 'changeur') {
console.log("Searching", this.currentRencontre.locList, coord);
let isInArea = this.currentRencontre.locList.find(locCoord => locCoord == coord);
if (isInArea) { // OK !
return (this.rencontreState == 'messager') ? 'messager' : 'saut';
}
}
return "erreur";
}
/* -------------------------------------------- */
isCaseInondee(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isCaseInondee(c, coord));
return EffetsDraconiques.debordement.find(this.casesSpeciales, coord);
}
isCiteFermee(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isCiteFermee(c, coord));
return EffetsDraconiques.fermetureCites.find(this.casesSpeciales, coord);
}
/* -------------------------------------------- */
isTerreAttache(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isTerreAttache(c, coord));
return EffetsDraconiques.terreAttache.find(this.casesSpeciales, coord);
}
/* -------------------------------------------- */
isCaseMaitrisee(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isCaseMaitrisee(c, coord));
return EffetsDraconiques.queteEaux.find(this.casesSpeciales, coord);
}
/* -------------------------------------------- */
isReserveExtensible(coord) {
return this.casesSpeciales.find(c => EffetsDraconiques.isReserveExtensible(c, coord));
return EffetsDraconiques.reserveExtensible.find(this.casesSpeciales, coord);
}
/* -------------------------------------------- */
@ -700,40 +716,47 @@ export class RdDTMRDialog extends Dialog {
console.log("deplacerDemiReve >>>>", currentPos, eventPos);
let targetCoordTMR = TMRUtility.convertToTMRCoord(eventPos);
let currentCoordTMR = TMRUtility.convertToTMRCoord(currentPos);
let targetCoord = TMRUtility.convertToTMRCoord(eventPos);
let currentCoord = TMRUtility.convertToTMRCoord(currentPos);
// Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter)
let deplacementType = 'erreur';
if (this.rencontreState == 'aucune') { // Pas de recontre en post-processing, donc deplacement normal
if (this.isTerreAttache(targetCoordTMR) || this.isConnaissanceFleuve(currentCoordTMR, targetCoordTMR) || !RdDTMRDialog._horsDePortee(currentPos, eventPos)) {
deplacementType = 'normal';
}
} else {
deplacementType = this.processClickPostRencontre(targetCoordTMR);
}
let deplacementType = this._calculDeplacement(targetCoord, currentCoord, currentPos, eventPos);
// Si le deplacement est valide
if (deplacementType == 'normal' || deplacementType == 'saut') {
await this._deplacerDemiReve(targetCoordTMR, deplacementType);
await this._deplacerDemiReve(targetCoord, deplacementType);
} else if (deplacementType == 'messager') { // Dans ce cas, ouverture du lancement de sort sur la case visée
/*
TODO: si la case a un sort en réserve, lancer ce sort.
Si la case est le demi-rêve, ne pas lancer de sort.
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre si on essaie de lancer un sort (ou bloquer le lancer de sort)
*/
await this._messagerDemiReve(targetCoordTMR);
await this._messagerDemiReve(targetCoord);
} else {
ui.notifications.error("Vous ne pouvez vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
console.log("STATUS :", this.rencontreState, this.currentRencontre);
}
this.checkQuitterTMR();
}
async _messagerDemiReve(targetCoordTMR) {
await this.actor.rollUnSort(targetCoordTMR);
/* -------------------------------------------- */
_calculDeplacement(targetCoord, currentCoord, currentPos, eventPos) {
let isInArea = this.rencontreState == 'aucune'
? this.isTerreAttache(targetCoord) || this.isConnaissanceFleuve(currentCoord, targetCoord) || !RdDTMRDialog._horsDePortee(currentPos, eventPos)
: this.currentRencontre?.locList.find(coord => coord == targetCoord) ?? false
if (isInArea) {
switch (this.rencontreState) {
case 'aucune': return 'normal';
case 'messager': return 'messager';
case 'passeur': case 'changeur': return 'saut';
}
}
return 'erreur'
}
/* -------------------------------------------- */
async _messagerDemiReve(targetCoord) {
/*
TODO: si la case a un sort en réserve, lancer ce sort.
Si la case est le demi-rêve, ne pas lancer de sort.
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre si on essaie de lancer un sort (ou bloquer le lancer de sort)
*/
await this.actor.rollUnSort(targetCoord);
this.nettoyerRencontre();
}
@ -742,14 +765,12 @@ export class RdDTMRDialog extends Dialog {
this.nettoyerRencontre();
}
let tmr = TMRUtility.getTMR(targetCoord);
console.log("deplacerDemiReve", tmr, this);
// Gestion cases spéciales type Trou noir, etc
tmr = await this.manageTmrInnaccessible(tmr);
await this.manageCaseSpeciale(tmr); // Gestion cases spéciales type Trou noir, etc
console.log("deplacerDemiReve: TMR is", tmr, this);
let tmrPos = duplicate(this.actor.data.data.reve.tmrpos);
tmrPos.coord = targetCoord;
await this.actor.update({ "data.reve.tmrpos": tmrPos });
this.actor.updateCoordTMR(tmr.coord);
await this.actor.updateCoordTMR(tmr.coord);
this._updateDemiReve();
this.cumulFatigue += this.fatigueParCase;
@ -757,7 +778,7 @@ export class RdDTMRDialog extends Dialog {
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_tmr_move", data: {
actorId: this.actor.data._id,
tmrPos: tmrPos
tmrPos: this.actor.data.data.reve.tmrpos
}
});
@ -770,25 +791,31 @@ export class RdDTMRDialog extends Dialog {
}
async postRencontre(tmr) {
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.declencheSortEnReserve(tmr.coord);
await this.actor.checkSoufflePeage(tmr);
if (!(this.viewOnly || this.currentRencontre)) {
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.conquerirTMR(tmr);
await this.declencheSortEnReserve(tmr.coord);
await this.actor.checkSoufflePeage(tmr);
}
}
/* -------------------------------------------- */
async forceDemiRevePositionView(coordTMR) {
async forceDemiRevePositionView() {
this._updateDemiReve();
}
/* -------------------------------------------- */
async forceDemiRevePosition(coordTMR) {
await this.actor.updateCoordTMR(coordTMR);
async forceDemiRevePosition(coord) {
await this.actor.updateCoordTMR(coord);
this._updateDemiReve();
let tmr = TMRUtility.getTMR(coordTMR);
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.declencheSortEnReserve(tmr.coord);
let tmr = TMRUtility.getTMR(coord);
if (!(this.viewOnly || this.currentRencontre)) {
await this.manageCaseHumide(tmr);
await this.conquerirCiteFermee(tmr);
await this.declencheSortEnReserve(tmr.coord);
}
return tmr;
}
/* -------------------------------------------- */

View File

@ -1,5 +1,6 @@
import { DeDraconique } from "./de-draconique.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { TMRUtility } from "./tmr-utility.js";
import { TMRType } from "./tmr-utility.js";
@ -271,6 +272,12 @@ const rencontresStandard = [
{ code: "rdd", name: "Rêve de Dragon", type: "rdd", genre: "m", force: "1ddr + 7", refoulement: 2, quitterTMR: true }
];
const presentsCite = [
{ code: "messager2d6", name: "Messager des Rêves", type: "messager", genre: "m", force: "2d6", ignorer: true },
{ code: "passeur2d6", name: "Passeur des Rêves", type: "passeur", genre: "m", force: "2d6", ignorer: true },
{ code: "fleur2d6", name: "Fleur des Rêves", type: "fleur", genre: "f", force: "2d6", ignorer: true }
]
const tableRencontres = {
cite: [{ code: 'messager', range: [1, 25] }, { code: 'passeur', range: [26, 50] }, { code: 'fleur', range: [51, 65] }, { code: 'mangeur', range: [66, 70] }, { code: 'changeur', range: [71, 80] }, { code: 'briseur', range: [81, 85] }, { code: 'reflet', range: [86, 90] }, { code: 'tbblanc', range: [91, 94] }, { code: 'tbnoir', range: [95, 97] }, { code: 'rdd', range: [98, 100] }],
sanctuaire: [{ code: 'messager', range: [1, 25] }, { code: 'passeur', range: [26, 50] }, { code: 'fleur', range: [51, 65] }, { code: 'mangeur', range: [66, 70] }, { code: 'changeur', range: [71, 80] }, { code: 'briseur', range: [81, 85] }, { code: 'reflet', range: [86, 90] }, { code: 'tbblanc', range: [91, 94] }, { code: 'tbnoir', range: [95, 97] }, { code: 'rdd', range: [98, 100] }],
@ -367,10 +374,10 @@ export class TMRRencontres {
/* -------------------------------------------- */
static async getMauvaiseRencontre(index = undefined) {
if (index == undefined || index >= mauvaisesRencontres.length) {
index = new Roll("1d" + mauvaisesRencontres.length).roll().total - 1;
}
const rencontre = duplicate(mauvaisesRencontres[index]);
const rencontre = duplicate(
(index && index>=0 && index < mauvaisesRencontres.length)
? mauvaisesRencontres[index]
: Misc.rollOneOf(mauvaisesRencontres));
await TMRRencontres.evaluerForceRencontre(rencontre);
return rencontre;
}
@ -408,15 +415,15 @@ export class TMRRencontres {
data.message = gestion.msgSucces(data);
if (data.nbRounds > 1) {
data.message += ` Au total, vous avez passé ${data.nbRounds} rounds à vous battre!`;
}
}
data.poesie = gestion.poesieSucces;
return gestion.postSucces;
}
data.message = gestion.msgEchec(data);
if (data.nbRounds > 1) {
data.message += ` Vous avez passé ${data.nbRounds} rounds à lutter!`;
}
}
data.poesie = gestion.poesieEchec;
return gestion.postEchec;
}
@ -428,8 +435,7 @@ export class TMRRencontres {
data.newTMR = TMRUtility.getTMR(data.sortReserve.coord);
} else {
// Déplacement aléatoire de la force du Passeur Fou
const locList = TMRUtility.getTMRPortee(data.tmr.coord, data.rencontre.force);
const newCoord = locList[new Roll("1d" + locList.length).evaluate().total - 1];
const newCoord = Misc.rollOneOf(TMRUtility.getTMRPortee(data.tmr.coord, data.rencontre.force));
data.newTMR = TMRUtility.getTMR(newCoord);
}
if (data.sortReserve) {
@ -466,7 +472,7 @@ export class TMRRencontres {
static async _toubillonner(tmrDialog, actor, cases) {
let coord = actor.data.data.reve.tmrpos.coord;
for (let i = 0; i < cases; i++) {
coord = TMRUtility.deplaceTMRAleatoire(coord);
coord = TMRUtility.deplaceTMRAleatoire(actor, coord).coord;
}
await tmrDialog.forceDemiRevePosition(coord)
}
@ -477,7 +483,7 @@ export class TMRRencontres {
}
await data.actor.resultCombatReveDeDragon(data);
}
static async onPostEchecReveDeDragon(tmrDialog, data) {
await data.actor.resultCombatReveDeDragon(data);
tmrDialog.close();

View File

@ -1,6 +1,4 @@
import { DeDraconique } from "./de-draconique.js";
import { TMRRencontres } from "./tmr-rencontres.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
/* -------------------------------------------- */
@ -92,7 +90,7 @@ const TMRMapping = {
A7: { type: "plaines", label: "Plaines de lArc" },
B7: { type: "marais", label: "Marais Bluants" },
C7: { type: "fleuve", label: "Fleuve de l'Oubli" },
D7: { type: "plaines", label: "Plaines dA!a" },
D7: { type: "plaines", label: "Plaines dAffa" },
E7: { type: "foret", label: "Forêt de Glusks" },
F7: { type: "fleuve", label: "Fleuve de l'Oubli" },
G7: { type: "cite", label: "Cité de Terwa" },
@ -270,6 +268,7 @@ export const tmrColors = {
sort: 0xFF8800,
tetes: 0xA000FF,
souffle: 0x804040,
queues: 0xAA4040,
trounoir: 0x401060,
demireve: 0x00FFEE,
rencontre: 0xFF0000,
@ -283,7 +282,9 @@ export const tmrColors = {
export class TMRUtility {
static init() {
for (let coord in TMRMapping) {
TMRMapping[coord].coord = coord;
const tmr = TMRMapping[coord];
tmr.coord = coord;
tmr.genre = TMRType[tmr.type].genre;
}
let tmrByType = Misc.classify(Object.values(TMRMapping));
for (const [type, list] of Object.entries(tmrByType)) {
@ -317,9 +318,14 @@ export class TMRUtility {
}
/* -------------------------------------------- */
static getTMR(coordTMR) {
return TMRMapping[coordTMR];
static getTMR(coord) {
return TMRMapping[coord];
}
static getTMRLabel(coord) {
return TMRMapping[coord]?.label ?? (coord+": case inconnue");
}
static isCaseHumide(tmr) {
return tmr.type == 'fleuve' || tmr.type == 'lac' || tmr.type == 'marais';
}
@ -355,24 +361,23 @@ export class TMRUtility {
/* -------------------------------------------- */
static getDirectionPattern() {
let roll = new Roll("1d" + tmrRandomMovePatten.length).evaluate().total;
return tmrRandomMovePatten[roll - 1];
return Misc.rollOneOf(tmrRandomMovePatten);
}
static deplaceTMRAleatoire(coord) {
return TMRUtility.deplaceTMRSelonPattern(coord, TMRUtility.getDirectionPattern(), 1);
static deplaceTMRAleatoire(actor, coord) {
return TMRUtility.deplaceTMRSelonPattern(actor, coord, TMRUtility.getDirectionPattern(), 1);
}
/* -------------------------------------------- */
static deplaceTMRSelonPattern(coord, direction, nTime) {
static async deplaceTMRSelonPattern(actor, coord, direction, nTime) {
for (let i = 0; i < nTime; i++) {
let currentPosXY = TMRUtility.convertToCellPos(coord);
currentPosXY.x = currentPosXY.x + direction.x;
currentPosXY.y = currentPosXY.y + direction.y;
if (this._checkTMRCoord(currentPosXY.x, currentPosXY.y)) { // Sortie de carte ! Ré-insertion aléatoire
coord = TMRUtility.convertToTMRCoord(currentPosXY);
let currentPos = TMRUtility.convertToCellPos(coord);
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));
} else {
coord = this.getTMRAleatoire().coord;
coord = await actor.reinsertionAleatoire('Sortie de carte');
}
console.log("Nouvelle case iteration !!!", i, coord);
}
@ -392,10 +397,8 @@ export class TMRUtility {
return TMRUtility.filterTMR(filter).map(it => it.coord);
}
static getTMRAleatoire(filter = undefined) {
let list = TMRUtility.filterTMR(filter);
let index = new Roll("1d" + list.length).evaluate().total - 1;
return list[index];
static getTMRAleatoire(filter = it => true) {
return Misc.rollOneOf(TMRUtility.filterTMR(filter))
}
/* -------------------------------------------- */
@ -415,39 +418,32 @@ export class TMRUtility {
}
/* -------------------------------------------- */
static getSortReserveList(reserveList, coordTMR) {
static getSortReserveList(reserveList, coord) {
// TODO : Gérer les têtes spéciales réserve!
let sortReserveList
let tmrDescr = this.getTMR(coordTMR);
let tmrDescr = this.getTMR(coord);
//console.log("Sort réserve : ", tmrDescr);
if (tmrDescr.type == 'fleuve') { // Gestion de la reserve en Fleuve
sortReserveList = reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve');
} else { // Reserve sur un case "normale"
sortReserveList = reserveList.filter(it => it.coord == coordTMR);
return reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve');
}
//console.log("Sort réserve : ", tmrDescr, sortReserve, reserveList);
return sortReserveList;
// Reserve sur un case "normale"
return reserveList.filter(it => it.coord == coord);
}
/* -------------------------------------------- */
/** Returns a list of case inside a given distance
*
*/
static getTMRPortee(centerCoord, portee) {
return TMRUtility.getTMRArea(centerCoord, portee, tmrConstants);
}
static getTMRArea(centerCoord, distance, tmrConstants) {
let centerPos = this.convertToCellPos(centerCoord);
static getTMRPortee(coord, portee) {
let centerPos = this.convertToCellPos(coord);
let posPic = this.computeRealPictureCoordinates(centerPos, tmrConstants);
let caseList = [];
for (let dx = -distance; dx <= distance; dx++) { // Loop thru lines
for (let dy = -distance; dy <= distance; dy++) { // Loop thru lines
for (let dx = -portee; dx <= portee; dx++) { // Loop thru lines
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 < distance + 0.5) {
if (dist < portee + 0.5) {
caseList.push(this.convertToTMRCoord(currentPos)); // Inside the area
}
}

41
module/tmr/conquete.js Normal file
View File

@ -0,0 +1,41 @@
import { Misc } from "../misc.js";
import { tmrColors, tmrConstants, TMRUtility } from "../tmr-utility.js";
import { Draconique } from "./draconique.js";
export class Conquete extends Draconique {
constructor() {
super();
}
type() { return 'queue' }
match(item) { return Draconique.isQueueDragon(item) && item.name.toLowerCase() == 'conquête'; }
manualMessage() { return false }
async onActorCreateOwned(actor, item) { await this._creerConquete(actor, item); }
code() { return 'conquete' }
tooltip(linkData) { return `La ${this.tmrLabel(linkData)} doit être conquise` }
img() { return 'icons/svg/combat.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.queues, taille: tmrConstants.full, decallage: { x: 2, y: 0 }
});
}
async _creerConquete(actor, queue) {
let existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
let possibles = TMRUtility.filterTMR(tmr => !TMRUtility.isCaseHumide(tmr) && !existants.includes(tmr.coord));
let conquete = Misc.rollOneOf(possibles);
await this.createCaseTmr(actor, 'Conquête: ' + conquete.label, conquete, queue._id);
}
async onConquete(actor, coord) {
let existants = actor.data.items.filter(it => this.isCase(it, coord));
for (let casetmr of existants) {
await actor.deleteOwnedItem(casetmr._id);
await actor.deleteOwnedItem(casetmr.data.sourceid);
}
}
}

View File

@ -13,7 +13,7 @@ export class Debordement extends Draconique {
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'debordement' }
tooltip(linkData) { return `Débordement en ${TMRUtility.getTMR(linkData.data.coord).label}` }
tooltip(linkData) { return `Débordement en ${this.tmrLabel(linkData)}` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/svg/wave.svg' }
_createSprite(pixiTMR) {

View File

@ -0,0 +1,58 @@
import { Misc } from "../misc.js";
import { RollDataAjustements } from "../rolldata-ajustements.js";
import { tmrColors, tmrConstants, TMRType, TMRUtility } from "../tmr-utility.js";
import { Debordement } from "./debordement.js";
import { Draconique } from "./draconique.js";
export class Desorientation extends Draconique {
constructor() {
super();
}
type() { return 'souffle' }
match(item) { return Draconique.isSouffleDragon(item) && item.name.toLowerCase().includes('désorientation'); }
manualMessage() { return false }
async onActorCreateOwned(actor, souffle) {
const type = Misc.rollOneOf(this._typesPossibles(actor));
console.log("désorientation", type);
souffle.name += ": " + TMRType[type].name;
await this._creerCasesTmr(actor, type, souffle);
}
_typesPossibles(actor) {
const dejaDesorientes = Misc.distinct(actor.data.items.filter(it => this.isCase(it)).map(it => it.type));
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' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.trounoir, alpha: 1, taille: tmrConstants.full, decallage: { x: 2, y: 2 },
});
}
async _creerCasesTmr(actor, type, souffle) {
const existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
let tmrs = TMRUtility.filterTMR(it => it.type == type && !existants.includes(it.coord));
for (let tmr of tmrs) {
await this.createCaseTmr(actor, 'Désorientation: ' + tmr.label, tmr, souffle._id);
}
}
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

@ -1,3 +1,4 @@
import { TMRUtility } from "../tmr-utility.js";
import { PixiTMR } from "./pixi-tmr.js";
const registeredEffects = [
@ -14,6 +15,8 @@ export class Draconique
static isTeteDragon(element) { return element.type == 'tete'; }
static isQueueSouffle(it) { return Draconique.isQueueDragon(it) || Draconique.isSouffleDragon(it); }
tmrLabel(linkData) { return TMRUtility.getTMRLabel(linkData.data.coord); }
static register(draconique) {
registeredEffects[draconique.code()] = draconique;
if (draconique.img()) {
@ -48,10 +51,10 @@ export class Draconique
* Méthode responsable de gérer une draconique (par exemple, ajouter des casetmr pour la fermeture des cités).
* @param actor auquel la draconique est ajoutée
*/
async onActorCreateOwned(actor) {
async onActorCreateOwned(actor, item) {
return false;
}
async onActorDeleteOwned(actor) {
async onActorDeleteOwned(actor, item) {
return false;
}
/**
@ -108,10 +111,15 @@ export class Draconique
return Draconique.isCaseTMR(it) && it.data.specific == this.code() && (coord ? it.data.coord == coord : true);
}
async createCaseTmr(actor, label, tmr) {
find(list, coord = undefined) {
return list.find(c => this.isCase(c, coord));
}
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() }
data: { coord: tmr.coord, specific: this.code(), sourceid:sourceId }
});
}

View File

@ -11,6 +11,8 @@ import { CarteTmr } from "./carte-tmr.js";
import { PontImpraticable } from "./pont-impraticable.js";
import { Draconique } from "./draconique.js";
import { PresentCites } from "./present-cites.js";
import { Desorientation } from "./desorientation.js";
import { Conquete } from "./conquete.js";
@ -27,6 +29,8 @@ export class EffetsDraconiques {
static terreAttache = new TerreAttache();
static trouNoir = new TrouNoir();
static pontImpraticable = new PontImpraticable();
static desorientation = new Desorientation();
static conquete = new Conquete();
static init() {
Draconique.register(EffetsDraconiques.carteTmr);
@ -41,25 +45,32 @@ export class EffetsDraconiques {
Draconique.register(EffetsDraconiques.trouNoir);
Draconique.register(EffetsDraconiques.pontImpraticable);
Draconique.register(EffetsDraconiques.presentCites);
Draconique.register(EffetsDraconiques.desorientation);
Draconique.register(EffetsDraconiques.conquete);
}
/* -------------------------------------------- */
static isCaseInondee(caseTMR, coord) {
return EffetsDraconiques.debordement.isCase(caseTMR, coord) || EffetsDraconiques.pontImpraticable.isCase(caseTMR, coord);
return EffetsDraconiques.debordement.isCase(caseTMR, coord) ||
EffetsDraconiques.pontImpraticable.isCase(caseTMR, coord);
}
static isInnaccessible(caseTMR, coord) {
return EffetsDraconiques.trouNoir.isCase(caseTMR, coord) ||
EffetsDraconiques.desorientation.isCase(caseTMR, coord);
}
static isCaseTrouNoir(caseTMR, coord) {
return EffetsDraconiques.trouNoir.isCase(caseTMR, coord);
}
static isReserveExtensible(caseTMR, coord) {
return EffetsDraconiques.reserveExtensible.isCase(caseTMR, coord);
}
static isTerreAttache(caseTMR, coord) {
return EffetsDraconiques.terreAttache.isCase(caseTMR, coord);
}
static isCiteFermee(caseTMR, coord) {
return EffetsDraconiques.fermetureCites.isCase(caseTMR, coord);
}
@ -76,8 +87,8 @@ export class EffetsDraconiques {
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueSouffle(it) && it.name.toLowerCase().includes('montée laborieuse'));
}
static isFermetureCite(element) {
/* -------------------------------------------- */
static isFermetureCite(element) {
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.fermetureCites.match(it));
}
@ -99,14 +110,12 @@ export class EffetsDraconiques {
}
static isDesorientation(element) {
// TODO
return EffetsDraconiques.isMatching(element, it => Draconique.isSouffleDragon(it) && it.name.toLowerCase() == 'désorientation');
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.desorientation.match(it)); // TODO
}
/* -------------------------------------------- */
static isConquete(element) {
// TODO
return EffetsDraconiques.isMatching(element, it => Draconique.isQueueDragon(it) && it.name.toLowerCase() == 'conquête');
return EffetsDraconiques.isMatching(element, it => EffetsDraconiques.conquete.match(it));
}
static isPelerinage(element) {

View File

@ -13,7 +13,7 @@ export class FermetureCites extends Draconique {
async onActorCreateOwned(actor, item) { await this._fermerLesCites(actor); }
code() { return 'fermeture' }
tooltip(linkData) { return `La ${TMRUtility.getTMR(linkData.data.coord).label} est fermée` }
tooltip(linkData) { return `La ${this.tmrLabel(linkData)} est fermée` }
img() { return 'icons/svg/door-closed.svg' }
_createSprite(pixiTMR) {
@ -30,4 +30,10 @@ export class FermetureCites extends Draconique {
await this.createCaseTmr(actor, 'Fermeture: ' + tmr.label, tmr);
}
}
async onConquete(actor, coord) {
const citeFermee = actor.data.items.find(it => this.isCase(it, coord));
await this.actor.deleteOwnedItem(citeFermee._id);
}
}

View File

@ -14,7 +14,7 @@ export class PontImpraticable extends Draconique {
async onActorDeleteOwned(actor, item) { await this._supprimerCaseTmr(actor); }
code() { return 'pont-impraticable' }
tooltip(linkData) { return `${TMRUtility.getTMR(linkData.data.coord).label} impraticable` }
tooltip(linkData) { return `${this.tmrLabel(linkData)} impraticable` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/svg/wave.svg' }
_createSprite(pixiTMR) {

View File

@ -14,29 +14,44 @@ export class PresentCites extends Draconique {
async onActorCreateOwned(actor, item) { await this._ajouterPresents(actor); }
code() { return 'present-cites' }
tooltip(linkData) { return `La ${TMRUtility.getTMR(linkData.data.coord).label} a un présent` }
tooltip(linkData) { return `La ${this.tmrLabel(linkData)} a un présent` }
img() { return 'systems/foundryvtt-reve-de-dragon/icons/svg/gift.svg' }
_createSprite(pixiTMR) {
return pixiTMR.sprite(this.code(),
{
color: tmrColors.tetes, alpha: 0.7, taille: tmrConstants.third, decallage:tmrConstants.topRight
color: tmrColors.tetes, alpha: 0.7, taille: tmrConstants.third, decallage: tmrConstants.topRight
});
}
async _ajouterPresents(actor) {
let existants = actor.data.items.filter(it => this.isCase(it)).map(it => it.data.coord);
if (existants.length >0 ) {
if (existants.length > 0) {
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user),
whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name),
content: "Vous avez encore des présents dans des cités, vous devrez tirer une autre tête pour remplacer celle ci!"
})
}
else{
else {
let cites = TMRUtility.filterTMR(it => it.type == 'cite');
for (let tmr of cites) {
await this.createCaseTmr(actor, 'Présent: ' + tmr.label, tmr);
}
}
}
async choisirUnPresent(casetmr) {
let d = new Dialog({
title: "Présent des cités",
content: `La ${casetmr.data.coord} vous offre un présent`,
buttons: {
fleur: {
icon: '<i class="fas fa-check"></i>',
label: "Fleur des rêves 2d6",
callback: () => this.creerObjet()
}
}
});
d.render(true);
}
}

View File

@ -12,7 +12,7 @@ export class QueteEaux extends Draconique {
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'maitrisee' }
tooltip(linkData) { return `Quête des eaux, le ${TMRUtility.getTMR(linkData.data.coord).label} est maîtrisé` }
tooltip(linkData) { return `Quête des eaux, le ${this.tmrLabel(linkData)} est maîtrisé` }
img() { return 'icons/svg/bridge.svg' }
_createSprite(pixiTMR) {

View File

@ -12,7 +12,7 @@ export class ReserveExtensible extends Draconique {
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'reserve_extensible' }
tooltip(linkData) { return `Réserve extensible en ${TMRUtility.getTMR(linkData.data.coord).label} !` }
tooltip(linkData) { return `Réserve extensible en ${this.tmrLabel(linkData)} !` }
img() { return 'icons/svg/chest.svg' }
_createSprite(pixiTMR) {

View File

@ -12,7 +12,7 @@ export class TerreAttache extends Draconique {
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'attache' }
tooltip(linkData) { return `Terre d'attache en ${TMRUtility.getTMR(linkData.data.coord).label} !` }
tooltip(linkData) { return `Terre d'attache en ${this.tmrLabel(linkData)} !` }
img() { return 'icons/svg/anchor.svg' }
_createSprite(pixiTMR) {

View File

@ -12,7 +12,7 @@ export class TrouNoir extends Draconique {
async onActorCreateOwned(actor, item) { await this._creerCaseTmr(actor); }
code() { return 'trounoir' }
tooltip(linkData) { return `Trou noir en ${TMRUtility.getTMR(linkData.data.coord).label} !` }
tooltip(linkData) { return `Trou noir en ${this.tmrLabel(linkData)} !` }
img() { return 'icons/svg/explosion.svg' }
_createSprite(pixiTMR) {

View File

@ -6,7 +6,7 @@
"manifestPlusVersion": "1.0.0",
"minimumCoreVersion": "0.7.5",
"compatibleCoreVersion": "0.7.9",
"templateVersion": 93,
"templateVersion": 94,
"author": "LeRatierBretonnien",
"authors": [
{

View File

@ -718,14 +718,20 @@
},
"queue": {
"description": "",
"refoulement": 0
},
"refoulement": 0,
"duree": "",
"restant" : 0
},
"ombre": {
"description": "",
"refoulement": 2
"refoulement": 2,
"duree": "",
"restant" : 0
},
"souffle": {
"description": ""
"description": "",
"duree": "",
"restant" : 0
},
"tete": {
"description": ""
@ -764,7 +770,8 @@
"type": "",
"label": "",
"specific": "",
"description": ""
"description": "",
"sourceid":""
},
"recettealchimique": {
"but": "",

View File

@ -7,5 +7,6 @@
<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>