Ajouter de "jet de dés" dans les descriptions,

On peut maintenant ajouter des liens dans les descriptions
(acteurs, items) et autres champs similaires.
This commit is contained in:
Vincent Vandemeulebrouck 2025-01-11 00:44:59 +01:00
parent 785bd4b9ce
commit b87f406093
15 changed files with 169 additions and 68 deletions

View File

@ -1,4 +1,9 @@
# 12.0
## 12.0.34 - la tête d'Astrobazzarh
- on peut ajouter des liens "jet de dés" dans les descriptions, notes, ...
- les liens "jet de dés" peuvent être utilisés pour un acteur, ou les items de l'acteurs
- les liens "jet de dés" d'"un item non lié à un acteur agit sur les tokens sélectionnés
## 12.0.33 - la vieillesse d'Astrobazzarh
- retour de l'expérience pour les joueurs
- suppression du message "Pas de caractéristique" sur les jets d'odorat-goût

View File

@ -19,6 +19,7 @@ import { RdDBaseActorSangSheet } from "./actor/base-actor-sang-sheet.js";
import { RdDCoeur } from "./coeur/rdd-coeur.js";
import { AppPersonnageAleatoire } from "./actor/random/app-personnage-aleatoire.js";
import { RdDItemRace } from "./item/race.js";
import { RdDTextEditor } from "./apps/rdd-text-roll.js";
/* -------------------------------------------- */
/**
@ -44,8 +45,8 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
cssClass: this.isEditable ? "editable" : "locked",
limited: this.actor.limited,
owner: this.actor.isOwner,
biographie: await TextEditor.enrichHTML(this.actor.system.biographie, { async: true }),
notes: await TextEditor.enrichHTML(this.actor.system.notes, { async: true }),
biographie: await RdDTextEditor.enrichHTML(this.actor.system.biographie),
notes: await RdDTextEditor.enrichHTML(this.actor.system.notes),
});
foundry.utils.mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(),

View File

@ -17,7 +17,7 @@ import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { Draconique } from "./tmr/draconique.js";
import { LIST_CARAC, RdDCarac } from "./rdd-carac.js";
import { LIST_CARAC_PERSONNAGE, RdDCarac } from "./rdd-carac.js";
import { DialogConsommer } from "./dialog-item-consommer.js";
import { DialogFabriquerPotion } from "./dialog-fabriquer-potion.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
@ -662,15 +662,15 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async updateCarac(caracName, to) {
to = Number(to)
if (!RdDItemRace.checkRacialMax(this, caracName, to)){
if (!RdDItemRace.checkRacialMax(this, caracName, to)) {
return
}
if (caracName == LIST_CARAC.reve.code) {
if (caracName == LIST_CARAC_PERSONNAGE.reve.code) {
if (to > Misc.toInt(this.system.reve.seuil.value)) {
this.setPointsDeSeuil(to);
}
}
if (caracName == LIST_CARAC.chance.code) {
if (caracName == LIST_CARAC_PERSONNAGE.chance.code) {
if (to > Misc.toInt(this.system.compteurs.chance.value)) {
this.setPointsDeChance(to);
}
@ -1387,7 +1387,7 @@ export class RdDActor extends RdDBaseActorSang {
}
await RdDResolutionTable.rollData(ethylismeData.jetVie);
this._gererExperience(ethylismeData.jetVie);
this.gererExperience(ethylismeData.jetVie);
RollDataAjustements.calcul(ethylismeData.jetVie, this);
if (ethylismeData.jetVie.rolled.isSuccess) {
ethylisme.nb_doses++;
@ -1419,7 +1419,7 @@ export class RdDActor extends RdDBaseActorSang {
finalLevel: Number(ethylisme.value) + Number(this.system.compteurs.moral.value)
}
await RdDResolutionTable.rollData(ethylismeData.jetVolonte);
this._gererExperience(ethylismeData.jetVolonte);
this.gererExperience(ethylismeData.jetVolonte);
RollDataAjustements.calcul(ethylismeData.jetVolonte, this);
}
}
@ -1799,38 +1799,8 @@ export class RdDActor extends RdDBaseActorSang {
}
}
/**
* Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
* @param {*} caracName
* @param {*} compName
* @param {*} diff
* @param {*} options
* @returns
*/
async doRollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
const carac = this.getCaracByName(caracName);
if (!carac) {
ui.notifications.warn(`${this.name} n'a pas de caractéristique correspondant à ${caracName}`)
return;
}
const competence = this.getCompetence(compName);
let rollData = {
alias: this.getAlias(),
caracValue: Number(carac.value),
selectedCarac: carac,
competence: competence,
diffLibre: diff,
show: { title: options?.title ?? '' }
};
RollDataAjustements.calcul(rollData, this);
await RdDResolutionTable.rollData(rollData);
this._gererExperience(rollData);
await RdDResolutionTable.displayRollData(rollData, this)
return rollData.rolled;
}
/* -------------------------------------------- */
_gererExperience(rollData) {
gererExperience(rollData) {
const callback = this.createCallbackExperience();
if (callback.condition(rollData)) {
callback.action(rollData);

View File

@ -1,3 +1,4 @@
import { RdDTextEditor } from "../apps/rdd-text-roll.js";
import { Grammar } from "../grammar.js";
import { ITEM_TYPES } from "../item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
@ -47,6 +48,7 @@ export class RdDBaseActorReveSheet extends RdDBaseActorSheet {
}
}], { renderSheet: true })
)
this.html.find('.roll-carac-competence').click(async event => await RdDTextEditor.rollText(event, this.actor))
if (this.options.vueDetaillee) {
// On carac change

View File

@ -22,6 +22,7 @@ import { RdDCombat } from "../rdd-combat.js";
import { RdDEmpoignade } from "../rdd-empoignade.js";
import { RdDPossession } from "../rdd-possession.js";
import { BASE_CORPS_A_CORPS, BASE_ESQUIVE, POSSESSION_SANS_DRACONIC } from "../item/base-items.js";
import { RollDataAjustements } from "../rolldata-ajustements.js";
/**
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
@ -293,6 +294,38 @@ export class RdDBaseActorReve extends RdDBaseActor {
createCallbackAppelAuMoral() { return this.createEmptyCallback(); }
async _onCloseRollDialog(html) { }
/**
* Méthode pour faire un jet prédéterminer sans ouvrir la fenêtre de dialogue
* @param {*} caracName code ou label de la caractéristique. On peut utiliser 'intel' pour Intellect.
* @param {*} compName nom de compétence ou nom abrégé.
* @param {*} diff difficulté (0 si undefined)
* @param {*} options
* @returns le jet effectué
*/
async doRollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
const carac = this.getCaracByName(caracName);
if (!carac) {
ui.notifications.warn(`${this.name} n'a pas de caractéristique correspondant à ${caracName}`)
return
}
const competence = this.getCompetence(compName);
let rollData = {
alias: this.getAlias(),
caracValue: Number(carac.value),
selectedCarac: carac,
competence: competence,
diffLibre: diff ?? 0,
show: { title: options?.title ?? '' }
}
RollDataAjustements.calcul(rollData, this);
await RdDResolutionTable.rollData(rollData);
this.gererExperience(rollData);
await RdDResolutionTable.displayRollData(rollData, this)
return rollData.rolled;
}
gererExperience(rollData) { }
/* -------------------------------------------- */
async roll() {
RdDEmpoignade.checkEmpoignadeEnCours(this)

View File

@ -5,6 +5,7 @@ import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { Monnaie } from "../item-monnaie.js";
import { RdDItem, ITEM_TYPES } from "../item.js";
import { RdDItemCompetenceCreature } from "../item-competencecreature.js";
import { RdDTextEditor } from "../apps/rdd-text-roll.js";
/* -------------------------------------------- */
/**
@ -35,8 +36,8 @@ export class RdDBaseActorSheet extends ActorSheet {
img: this.actor.img,
name: this.actor.name,
system: this.actor.system,
description: await TextEditor.enrichHTML(this.actor.system.description, { async: true }),
notesmj: await TextEditor.enrichHTML(this.actor.system.notesmj, { async: true }),
description: await RdDTextEditor.enrichHTML(this.actor.system.description),
notesmj: await RdDTextEditor.enrichHTML(this.actor.system.notesmj),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.actor, this.isEditable),
effects: this.actor.effects
}

View File

@ -0,0 +1,54 @@
import "./xregexp-all.js";
import { RdDCarac } from "../rdd-carac.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
import { RdDItemCompetence } from "../item-competence.js";
import { ACTOR_TYPES } from "../item.js";
const XREGEXP_ROLL = XRegExp("@roll\\[(?<carac>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+)(\\/(?<competence>[A-Za-zÀ-ÖØ-öø-ÿ\\s\\-]+))?(/(?<diff>[\\+\\-]?\\d+))?\\]", 'giu')
export class RdDTextEditor {
static async enrichHTML(text) {
const rddTextEditor = new RdDTextEditor(text)
const replacedRolls = await rddTextEditor.replaceRolls()
return await TextEditor.enrichHTML(replacedRolls, { async: true })
}
constructor(text) {
this.original = text
}
async replaceRolls() {
if (!this.updated) {
this.updated = this.original
await XRegExp.forEach(this.original, XREGEXP_ROLL, async (rollMatch, i) => await this._replaceOneRoll(rollMatch))
}
return this.updated
}
async _replaceOneRoll(rollMatch) {
const carac = RdDCarac.caracDetails(rollMatch.carac);
const competence = rollMatch.competence ? RdDItemCompetence.findCompetence(await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage),
rollMatch.competence) : undefined
if (carac) {
const replacement = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/apps/link-text-roll.hbs`, {
carac: carac,
competence: competence?.name,
diff: rollMatch.diff
});
this.updated = this.updated.replace(rollMatch[0], replacement);
}
}
static async rollText(event, actor) {
const caracCode = event.currentTarget.attributes['data-carac-code'].value;
const competence = event.currentTarget.attributes['data-competence']?.value;
const diff = event.currentTarget.attributes['data-diff']?.value
const path = RdDCarac.caracDetails(caracCode)?.path
const actors = actor ? [actor] : canvas.tokens.controlled.map(it => it.actor).filter(it => it)
actors.filter(it => foundry.utils.getProperty(it, path) != undefined)
.forEach(it => it.doRollCaracCompetence(caracCode, competence, diff))
}
}

View File

@ -2002,7 +2002,7 @@ XRegExp.exec = function (str, regex, pos, sticky) {
*/
XRegExp.forEach = function (str, regex, callback) {
XRegExp.forEach = async function (str, regex, callback) {
var pos = 0;
var i = -1;
var match;
@ -2014,7 +2014,7 @@ XRegExp.forEach = function (str, regex, callback) {
// at least. Actually, because of the way `XRegExp.exec` caches globalized versions of
// regexes, mutating the regex will not have any effect on the iteration or matched strings,
// which is a nice side effect that brings extra safety.
callback(match, ++i, str, regex);
await callback(match, ++i, str, regex);
pos = match.index + (match[0].length || 1);
}
};

View File

@ -12,8 +12,9 @@ import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js";
import { RdDTimestamp } from "./time/rdd-timestamp.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ITEM_TYPES, RdDItem } from "./item.js";
import { ACTOR_TYPES, ITEM_TYPES, RdDItem } from "./item.js";
import { FLEUVE_COORD, TMRUtility } from "./tmr-utility.js";
import { RdDTextEditor } from "./apps/rdd-text-roll.js";
/**
* Extend the basic ItemSheet for RdD specific items
@ -97,11 +98,11 @@ export class RdDItemSheet extends ItemSheet {
name: this.item.name,
system: this.item.system,
actorId: this.actor?.id,
description: await TextEditor.enrichHTML(this.item.system.description, { async: true }),
descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }),
description: await RdDTextEditor.enrichHTML(this.item.system.description),
descriptionmj: await RdDTextEditor.enrichHTML(this.item.system.descriptionmj),
isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable),
competences: await SystemCompendiums.getCompetences('personnage'),
competences: await SystemCompendiums.getCompetences(ACTOR_TYPES.personnage),
categories: RdDItem.getCategories(this.item.type),
}
@ -120,18 +121,18 @@ export class RdDItemSheet extends ItemSheet {
formData.competences = formData.competences.filter(it => it.isCompetenceArme())
}
if (this.item.type == ITEM_TYPES.recettecuisine) {
formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, { async: true })
formData.ingredients = await RdDTextEditor.enrichHTML(this.object.system.ingredients)
}
if (this.item.type == ITEM_TYPES.extraitpoetique) {
formData.extrait = await TextEditor.enrichHTML(this.object.system.extrait, { async: true })
formData.texte = await TextEditor.enrichHTML(this.object.system.texte, { async: true })
formData.extrait = await RdDTextEditor.enrichHTML(this.object.system.extrait)
formData.texte = await RdDTextEditor.enrichHTML(this.object.system.texte)
}
if (this.item.type == ITEM_TYPES.recettealchimique) {
RdDAlchimie.processManipulation(this.item, this.actor?.id);
formData.manipulation_update = await TextEditor.enrichHTML(this.object.system.manipulation_update, { async: true })
formData.utilisation = await TextEditor.enrichHTML(this.object.system.utilisation, { async: true })
formData.enchantement = await TextEditor.enrichHTML(this.object.system.enchantement, { async: true })
formData.sureffet = await TextEditor.enrichHTML(this.object.system.sureffet, { async: true })
const manipulation = RdDAlchimie.processManipulation(this.item, this.actor?.id);
formData.manipulation = await RdDTextEditor.enrichHTML(manipulation)
formData.utilisation = await RdDTextEditor.enrichHTML(this.object.system.utilisation)
formData.enchantement = await RdDTextEditor.enrichHTML(this.object.system.enchantement)
formData.sureffet = await RdDTextEditor.enrichHTML(this.object.system.sureffet)
}
if (this.item.type == ITEM_TYPES.gemme) {
formData.gemmeTypeList = RdDGemme.getGemmeTypeOptionList();
@ -207,6 +208,7 @@ export class RdDItemSheet extends ItemSheet {
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).actionHerbe(this.item));
this.html.find('input[name="system.cacher_points_de_tache"]').change(async event => await this.item.update({ 'system.cacher_points_de_tache': event.currentTarget.checked }));
this.html.find('.roll-carac-competence').click(async event => await RdDTextEditor.rollText(event, this.actor))
this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event);
if (actor) {
@ -272,7 +274,7 @@ export class RdDItemSheet extends ItemSheet {
}
}
async supprimerBonusCase(deleteCoord){
async supprimerBonusCase(deleteCoord) {
if (this.item.type == ITEM_TYPES.sort) {
const oldList = RdDItemSort.getBonusCaseList(this.item)
const newList = oldList.filter(it => it.case != deleteCoord);

View File

@ -1,6 +1,6 @@
import { ITEM_TYPES, RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { LIST_CARAC, RdDCarac } from "../rdd-carac.js";
import { LIST_CARAC_PERSONNAGE, RdDCarac } from "../rdd-carac.js";
export class RdDItemRace extends RdDItem {
@ -12,7 +12,7 @@ export class RdDItemRace extends RdDItem {
static checkRacialMax(actor, code, value) {
const race = RdDItemRace.getRace(actor)
if (code == LIST_CARAC.force.code) {
if (code == LIST_CARAC_PERSONNAGE.force.code) {
if (!race.isForceValid(actor, value)) {
ui.notifications.warn(race.system.carac.force.limitmessage)
return false
@ -55,7 +55,7 @@ export class RdDItemRace extends RdDItem {
if (value == undefined) {
value = path ? foundry.utils.getProperty(actor, path) : 0
}
if (code == LIST_CARAC.force.code) {
if (code == LIST_CARAC_PERSONNAGE.force.code) {
return value >= this.getForceMax(actor)
}
const max = foundry.utils.getProperty(this, path) ?? -1

View File

@ -20,7 +20,7 @@ export class RdDAlchimie {
}
}
}
recette.system.manipulation_update = manip;
return manip;
}
/* -------------------------------------------- */

View File

@ -37,7 +37,7 @@ const TABLE_CARACTERISTIQUES_DERIVEES = {
32: { xp: 180, niveau: 11, poids: "1501-2000", poidsMin: 1501, poidsMax: 2000, plusdom: +11, sconst: 10, sust: 17 }
};
export const LIST_CARAC = {
export const LIST_CARAC_PERSONNAGE = {
'taille': { code: 'taille', label: 'Taille', isCarac: true, path: 'system.carac.taille.value' },
'apparence': { code: 'apparence', label: 'Apparence', isCarac: true, path: 'system.carac.apparence.value' },
'constitution': { code: 'constitution', label: 'Constitution', isCarac: true, path: 'system.carac.constitution.value' },
@ -56,18 +56,43 @@ export const LIST_CARAC = {
'beaute': { code: 'beaute', label: 'Beauté', isCarac: false, path: 'system.background.beaute.value' }
}
export const LIST_CARAC_AUTRES = {
'perception': { code: 'perception', label: 'Perception', path: 'system.carac.perception.value' },
}
const LIST_CARAC_DERIVEE = {
'melee': { code: "melee", label: 'Mêlée', path: 'system.carac.melee.value' },
'tir': { code: "tir", label: 'Tir', path: 'system.carac.tir.value' },
'lancer': { code: "lancer", label: 'Lancer', path: 'system.carac.lancer.value' },
'derobee': { code: "derobee", label: 'Dérobée', path: 'system.carac.derobee.value' },
'chance-actuelle': { code: "chance-actuelle", label: 'Chance actuelle', path: 'system.carac.lancer.value' },
'reve-actuel': { code: "reve-actuel", label: 'Rêve actuel', path: 'system.reve.reve.value' },
}
const LIST_CARAC_ROLL = Object.values(LIST_CARAC_PERSONNAGE).filter(it => it.isCarac && it.code != 'taille')
.concat(Object.values(LIST_CARAC_AUTRES))
.concat(Object.values(LIST_CARAC_DERIVEE))
export class RdDCarac {
static caracDetails(name) {
let entry = Misc.findFirstLike(name, LIST_CARAC_ROLL, { mapper: it => it.code, description: 'caractéristique', onMessage: m => { } })
if (entry && entry.length > 0) {
return entry
}
return Misc.findFirstLike(name, LIST_CARAC_ROLL, { mapper: it => it.label, description: 'caractéristique' })
}
static carac(code) {
return LIST_CARAC[code]
return LIST_CARAC_PERSONNAGE[code]
}
static label(code) {
return RdDCarac.carac(code)?.label ?? '---'
}
return RdDCarac.carac(code)?.label ?? '---'
}
static caracs(filter = it => it.isCarac) {
return Object.values(LIST_CARAC).filter(filter)
return Object.values(LIST_CARAC_PERSONNAGE).filter(filter)
}
static isAgiliteOuDerobee(selectedCarac) {

View File

@ -3,9 +3,9 @@
"types": ["personnage", "creature", "entite", "commerce", "vehicule"],
"templates": {
"description": {
"description": "Description ...",
"description": "",
"race": "",
"notesmj": "Notes du MJ"
"notesmj": ""
},
"subacteurs": {
"subacteurs": {

View File

@ -0,0 +1,8 @@
<span><a class="roll-carac-competence content-link"
{{#if competence}}data-competence="{{competence}}"{{/if~}}
{{#if diff}}data-diff="{{diff}}"{{/if~}}
data-carac-code="{{carac.code}}">
{{~uppercase carac.label~}}
{{#if competence}} / {{upperFirst competence}}{{/if~}}
{{#if diff}} à {{diff}}{{/if~}}
</a></span>

View File

@ -9,7 +9,7 @@
<div class="flexcol">
<span><label class="item-label">Manipulation : </label></span>
<div class="form-group medium-editor">
{{editor manipulation_update target="system.manipulation" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
{{editor manipulation target="system.manipulation" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
</div>
<div class="flexcol">