Vincent Vandemeulebrouck
91717a3290
Pour permettre au MJ de rendre un personnage haut rêvant sans avoir à chercher dans les compendiums
325 lines
12 KiB
JavaScript
325 lines
12 KiB
JavaScript
import { ChatUtility } from "../chat-utility.js";
|
|
import { HIDE_DICE, SYSTEM_RDD } from "../constants.js";
|
|
import { Grammar } from "../grammar.js";
|
|
import { RdDItem } from "../item.js";
|
|
import { Misc } from "../misc.js";
|
|
import { RdDDice } from "../rdd-dice.js";
|
|
|
|
const COMPENDIUM_SETTING_PREFIX = 'compendium-';
|
|
|
|
const CONFIGURABLE_COMPENDIUMS = {
|
|
'tables-diverses': { label: "Tables aléatoires", type: "RollTable" },
|
|
'competences': { label: "Compétences", type: "Item" },
|
|
'extrait-poetique': { label: "Extraits poetiques", type: "Item" },
|
|
'queues-de-dragon': { label: "Queues de dragon", type: "Item" },
|
|
'ombres-de-thanatos': { label: "Ombres de Thanatos", type: "Item" },
|
|
'souffles-de-dragon': { label: "Souffles de Dragon", type: "Item" },
|
|
'tarot-draconique': { label: "Tarots draconiques", type: "Item" },
|
|
'rencontres': { label: "Rencontres dans les TMR", type: "Item" },
|
|
'tetes-de-dragon-pour-haut-revants': { label: "Têtes de dragons (haut-rêvant)", type: "Item" },
|
|
'tetes-de-dragon-pour-tous-personnages': { label: "Têtes de dragons (tous)", type: "Item" },
|
|
'faune-flore-mineraux': { label: "Herbes & plantes", type: "Item" },
|
|
'equipement': { label: "Equipements", type: "Item" },
|
|
}
|
|
|
|
/**
|
|
* ======= Gestion des accès aux compendiums systèmes (ou surchargés) =======
|
|
*/
|
|
export class SystemCompendiums extends FormApplication {
|
|
static initSettings() {
|
|
Object.keys(CONFIGURABLE_COMPENDIUMS).forEach(compendium => {
|
|
const definition = CONFIGURABLE_COMPENDIUMS[compendium];
|
|
foundry.utils.mergeObject(definition, {
|
|
compendium: compendium,
|
|
default: SystemCompendiums._getDefaultCompendium(compendium),
|
|
setting: SystemCompendiums._getSettingCompendium(compendium)
|
|
})
|
|
|
|
game.settings.register(SYSTEM_RDD, definition.setting, {
|
|
name: definition.label,
|
|
default: definition.default,
|
|
scope: "world",
|
|
config: false,
|
|
type: String
|
|
})
|
|
})
|
|
|
|
game.settings.registerMenu(SYSTEM_RDD, "compendium-settings", {
|
|
name: "Choisir les compendiums système",
|
|
label: "Compendiums système",
|
|
hint: "Ouvre la fenêtre de sélection des compendiums système",
|
|
icon: "fas fa-bars",
|
|
restricted: true,
|
|
type: SystemCompendiums
|
|
})
|
|
}
|
|
|
|
static getPack(compendium) {
|
|
const pack = game.packs.get(compendium);
|
|
if (pack) {
|
|
return pack;
|
|
}
|
|
return game.packs.get(SystemCompendiums.getCompendium(compendium)) ?? game.packs.get(SystemCompendiums._getDefaultCompendium(compendium));
|
|
}
|
|
|
|
static async getPackContent(compendium, docType) {
|
|
const pack = SystemCompendiums.getPack(compendium);
|
|
if (pack?.metadata.type == docType) {
|
|
return await pack.getDocuments();
|
|
}
|
|
return [];
|
|
}
|
|
|
|
static async getCompetences(actorType) {
|
|
switch (actorType ?? 'personnage') {
|
|
case 'personnage':
|
|
return await SystemCompendiums.getWorldOrCompendiumItems('competence', 'competences')
|
|
case 'entite':
|
|
case 'creature':
|
|
return await SystemCompendiums.getWorldOrCompendiumItems('competencecreature', 'competences-creatures')
|
|
case 'vehicule': return [];
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async getWorldOrCompendiumItems(itemType, compendium) {
|
|
let items = game.items.filter(it => it.type == itemType)
|
|
if (compendium) {
|
|
const ids = items.map(it => it.id)
|
|
const names = items.map(it => Grammar.toLowerCaseNoAccent(it.name))
|
|
const compendiumItems = await SystemCompendiums.getItems(compendium, itemType)
|
|
return items.concat(compendiumItems
|
|
.filter(it => !ids.includes(it.id))
|
|
.filter(it => !names.includes(Grammar.equalsInsensitive(it.name))))
|
|
}
|
|
return items
|
|
}
|
|
|
|
static async loadDocument(document) {
|
|
const pack = game.packs.get(document.pack);
|
|
return await pack.getDocument(document.id ?? document._id);
|
|
}
|
|
|
|
static async getItems(compendium, itemType = undefined) {
|
|
const items = await SystemCompendiums.getPackContent(compendium, 'Item');
|
|
return (itemType ? items.filter(it => it.type == itemType) : items);
|
|
}
|
|
|
|
static async getContent(compendium, type, filter, itemFrequence, sorting) {
|
|
let elements = await SystemCompendiums.getPackContent(compendium, type);
|
|
elements = elements.filter(filter).filter(it => itemFrequence(it) > 0);
|
|
if (sorting) {
|
|
elements = elements.sort(sorting);
|
|
}
|
|
return elements;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async loadCompendiumData(compendium) {
|
|
const pack = game.packs.get(compendium);
|
|
return await pack?.getDocuments() ?? [];
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async loadCompendium(compendium, filter = item => true) {
|
|
let compendiumData = await SystemCompendiums.loadCompendiumData(compendium);
|
|
return compendiumData.filter(filter);
|
|
}
|
|
|
|
|
|
static async getDefaultItems(compendium) {
|
|
const pack = game.packs.get(SystemCompendiums._getDefaultCompendium(compendium));
|
|
if (pack.metadata.type == 'Item') {
|
|
return await pack.getDocuments();
|
|
}
|
|
return [];
|
|
}
|
|
|
|
static getCompendium(compendium) {
|
|
const setting = CONFIGURABLE_COMPENDIUMS[compendium]?.setting;
|
|
return setting ? game.settings.get(SYSTEM_RDD, setting) : SystemCompendiums._getDefaultCompendium(compendium);
|
|
}
|
|
|
|
static _getSettingCompendium(compendium) {
|
|
return COMPENDIUM_SETTING_PREFIX + compendium;
|
|
}
|
|
|
|
static _getDefaultCompendium(compendium) {
|
|
return `${SYSTEM_RDD}.${compendium}`;
|
|
}
|
|
|
|
constructor(...args) {
|
|
super(...args);
|
|
}
|
|
|
|
static get defaultOptions() {
|
|
const options = super.defaultOptions;
|
|
foundry.utils.mergeObject(options, {
|
|
id: "system-compendiums",
|
|
template: "systems/foundryvtt-reve-de-dragon/templates/settings/system-compendiums.html",
|
|
height: 'fit-content',
|
|
width: 600,
|
|
minimizable: false,
|
|
closeOnSubmit: true,
|
|
title: "Compendiums système"
|
|
});
|
|
return options;
|
|
}
|
|
|
|
getData() {
|
|
const systemCompendiums = Object.values(CONFIGURABLE_COMPENDIUMS)
|
|
.map(it => foundry.utils.mergeObject(it, { value: SystemCompendiums.getCompendium(it.compendium) }, { inplace: false }))
|
|
const availableCompendiums = game.packs.map(pack => {
|
|
return {
|
|
name: pack.collection,
|
|
path: pack.collection.replace('.', " / "),
|
|
type: pack.metadata.type
|
|
}
|
|
});
|
|
return foundry.utils.mergeObject(super.getData(), {
|
|
systemCompendiums: systemCompendiums,
|
|
availableCompendiums: availableCompendiums
|
|
}, { inplace: false })
|
|
}
|
|
|
|
activateListeners(html) {
|
|
html.find("select.system-compendium-setting").change((event) => {
|
|
const compendium = $(event.currentTarget).data('compendium')
|
|
const value = $(event.currentTarget).val();
|
|
const systemCompendium = CONFIGURABLE_COMPENDIUMS[compendium];
|
|
|
|
game.settings.set(SYSTEM_RDD, systemCompendium.setting, value);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ======= Gestion de jets dans une table correspondant à un compendium =======
|
|
*/
|
|
export class CompendiumTable {
|
|
|
|
constructor(compendium, type, subTypes = undefined, sorting = undefined) {
|
|
this.compendium = compendium;
|
|
this.type = type;
|
|
this.subTypes = subTypes;
|
|
this.sorting = sorting ?? Misc.ascending(it => it.name);
|
|
}
|
|
|
|
async getContent(itemFrequence = it => it.system.frequence, filter = it => true) {
|
|
return await SystemCompendiums.getContent(this.compendium,
|
|
this.type,
|
|
it => (!this.subTypes || this.subTypes.includes(it.type)) && itemFrequence(it) > 0 && filter(it),
|
|
itemFrequence,
|
|
this.sorting);
|
|
}
|
|
|
|
async buildTable(itemFrequence = it => it.system.frequence, filter = it => true) {
|
|
const elements = await this.getContent(itemFrequence, filter);
|
|
return CompendiumTableHelpers.buildTable(elements, itemFrequence);
|
|
}
|
|
|
|
async getRandom(itemFrequence = it => it.system.frequence, filter = it => true, forcedRoll = undefined) {
|
|
const table = await this.buildTable(itemFrequence, filter);
|
|
return await CompendiumTableHelpers.getRandom(table, this.type, this.subTypes, forcedRoll, SystemCompendiums.getCompendium(compendium));
|
|
}
|
|
|
|
async toChatMessage(itemFrequence = it => it.system.frequence, filter = it => true, typeName = undefined) {
|
|
const table = await this.buildTable(itemFrequence, filter);
|
|
await CompendiumTableHelpers.tableToChatMessage(table, this.type, this.subTypes, typeName);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ======= Gestion de tables correspondant à un compendium =======
|
|
*/
|
|
export class CompendiumTableHelpers {
|
|
|
|
static buildTable(elements, itemFrequence) {
|
|
let max = 0;
|
|
const total = elements.map(it => itemFrequence(it)).reduce(Misc.sum(), 0);
|
|
return elements.map(it => {
|
|
const frequence = itemFrequence(it);
|
|
let row = { document: it, frequence: frequence, min: max + 1, max: max + frequence, total: total };
|
|
max += frequence;
|
|
return row;
|
|
});
|
|
}
|
|
|
|
static concatTables(...tables) {
|
|
const rows = tables.reduce((a, b) => a.concat(b));
|
|
let max = 0;
|
|
const total = rows.map(it => it.frequence).reduce(Misc.sum(), 0);
|
|
return rows.map(row => {
|
|
const frequence = row.frequence
|
|
row.min = max + 1
|
|
row.max = max + frequence
|
|
row.total = total
|
|
max += frequence
|
|
return row
|
|
})
|
|
}
|
|
static async getRandom(table, type, subTypes = ['objet'], forcedRoll = undefined, localisation = undefined) {
|
|
if (table.length == 0) {
|
|
const typeName = Misc.typeName(type, subTypes[0]);
|
|
ui.notifications.warn(`Aucun ${typeName} trouvé dans ${localisation ?? ' les compendiums'}`);
|
|
return undefined;
|
|
}
|
|
return await CompendiumTableHelpers.selectRow(table, forcedRoll);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async selectRow(table, forcedRoll = undefined) {
|
|
if (table.length == 0) {
|
|
return undefined
|
|
}
|
|
const total = table[0].total;
|
|
const formula = `1d${total}`;
|
|
if (forcedRoll != undefined && (forcedRoll > total || forcedRoll <= 0)) {
|
|
ui.notifications.warn(`Jet forcé ${forcedRoll} en dehors de la table [1..${total}], le jet est relancé`);
|
|
forcedRoll = undefined;
|
|
}
|
|
const roll = forcedRoll ? { total: forcedRoll, formula } : await RdDDice.roll(formula, { showDice: HIDE_DICE });
|
|
const row = table.find(it => it.min <= roll.total && roll.total <= it.max);
|
|
row.roll = roll;
|
|
return row;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async tableRowToChatMessage(row, type, options = { showSource: true }) {
|
|
if (!row) {
|
|
return;
|
|
}
|
|
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table-roll.hbs', {
|
|
roll: row.roll,
|
|
document: row.document,
|
|
typeName: Misc.typeName(type, row.document?.type ?? 'objet'),
|
|
isGM: game.user.isGM,
|
|
options
|
|
});
|
|
const messageData = {
|
|
user: game.user.id,
|
|
rolls: [row.roll],
|
|
sound: CONFIG.sounds.dice,
|
|
content: flavorContent
|
|
};
|
|
await ChatUtility.createChatWithRollMode(messageData)
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async tableToChatMessage(table, type, subTypes, typeName = undefined) {
|
|
const flavorContent = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-compendium-table.hbs', {
|
|
img: RdDItem.getDefaultImg(subTypes[0]),
|
|
typeName: typeName ?? Misc.typeName(type, subTypes[0]),
|
|
table,
|
|
isGM: game.user.isGM,
|
|
});
|
|
const messageData = {
|
|
user: game.user.id,
|
|
whisper: [game.user],
|
|
content: flavorContent
|
|
};
|
|
await ChatUtility.createChatWithRollMode(messageData)
|
|
}
|
|
|
|
} |