L'appréciation utilise: - un niveau de qualité (qui réutilise la qualité sur les items en ayant) - un bon moment (coeur/musique/...) - un niveau de jet de moral - une caractéristique (perception) - une compétence Les bon moments passés sont remis à zéro lors du passage de château dormant. Ajout des jets de moral très heureux. Ajout de jet d'appréciation sur les résultats des oeuvres et des jeux.
328 lines
12 KiB
JavaScript
328 lines
12 KiB
JavaScript
import { ChatUtility } from "../chat-utility.js";
|
|
import { HIDE_DICE, renderTemplate, 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" },
|
|
'races': { label: "Races", 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')
|
|
.then(list => list.sort((a, b) => a.name.localeCompare(b.name)))
|
|
case 'entite':
|
|
case 'creature':
|
|
return await SystemCompendiums.getWorldOrCompendiumItems('competencecreature', 'competences-creatures')
|
|
.then(list => list.sort((a, b) => a.name.localeCompare(b.name)))
|
|
}
|
|
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.hbs",
|
|
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)
|
|
}
|
|
|
|
} |