Fenêtres Roll V2

Maintenant disponibles pour:
- méditation
- tâches
- soins
This commit is contained in:
2025-10-05 02:24:34 +02:00
parent 47c4478303
commit fa6769fcd7
28 changed files with 359 additions and 102 deletions

1
assets/ui/fatigue.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 256px; width: 256px;"><g class="" transform="translate(0,-28)" style=""><path d="M249.094 18.25c-42.675 0-81.852 25.486-110.938 68.438C109.07 129.637 90.72 189.74 90.72 256.28c0 66.543 18.35 126.643 47.436 169.595s68.263 68.47 110.938 68.47 81.883-25.518 110.97-68.47c29.084-42.952 47.436-103.052 47.436-169.594 0-66.54-18.352-126.64-47.438-169.592C330.978 43.736 291.77 18.25 249.094 18.25zm-128.97 241.313c18.356 18.096 37.528 26.765 55.72 27.562 18.19.797 35.927-6.096 52.125-21.5l12.874 13.53c-19.214 18.274-42.25 27.658-65.813 26.626-23.56-1.03-47.1-12.3-68-32.905l13.095-13.313zm264.782 0 13.125 13.312C377.135 293.48 353.564 304.75 330 305.78c-23.563 1.033-46.598-8.35-65.813-26.624l12.875-13.53c16.2 15.403 33.934 22.296 52.125 21.5 18.192-.798 37.365-9.467 55.72-27.563zM251.562 371.656c36.423-.156 72.996 19.144 77.438 58.406-51.33 13.296-102.67 12.767-154 0 3.858-38.638 40.14-58.25 76.563-58.406z" fill="#fff" fill-opacity="1" transform="translate(25.6, 25.6) scale(0.9, 0.9) rotate(0, 256, 256) skewX(0) skewY(0)"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -5,6 +5,10 @@
- Ajout d'un statut "saignement" en cas de blessure grave ou critique sans premiers soins
- Nouvelle fenêtre de jets de dés
- jets de méditation
- jets de tâches
- jets de caractéristiques
- jets de compétences
- Boutons d'initiative et d'attaque V2
- fenêtre d'attaque
- choix des armes
@@ -12,7 +16,7 @@
- message au défenseur
- gestion des demi-surprises (attaquant/défenseur)
- gestion des tactiques (attaquant/défenseur)
- en cours nouvelle fenêtre de jets
- en cours nouvelle fenêtre de jets
- jets de compétence avec messages
- jets de cuisine séparés avec messages (pour gérer plus tard les spécificités: fabricatioon de plats)
- gestion des empoignades

View File

@@ -96,6 +96,7 @@ select,
--gradient-red: linear-gradient(150deg, rgba(255, 0, 0, 0.3), rgba(255, 200, 128, 0.05), rgba(255, 200, 128, 0.1), rgba(255, 10, 0, 0.3));
--gradient-violet: linear-gradient(150deg, rgba(100, 45, 124, 0.6), rgba(216, 157, 192, 0.3), rgba(177, 157, 216, 0.5), rgba(107, 62, 121, 0.3), rgba(100, 45, 124, 0.6));
--gradient-purple-black: linear-gradient(150deg, rgba(0, 0, 0, 0.7), rgba(100, 45, 124, 0.4), rgba(82, 17, 131, 0.3), rgba(100, 45, 124, 0.4), rgba(0, 0, 0, 0.7));
--gradient-warning: linear-gradient(150deg, hsla(32, 100%, 50%, 0.3), hsla(52, 60%, 50%, 0.1), hsla(32, 60%, 50%, 0.1), hsla(32, 100%, 50%, 0.3));
--gradient-silver-light: linear-gradient(30deg, rgba(61, 55, 93, 0.2), rgba(178, 179, 196, 0.1), rgba(59, 62, 63, 0.2), rgba(206, 204, 199, 0.1), rgba(61, 46, 49, 0.2));
--gradient-daylight: conic-gradient(from 0deg, hsla(50, 100%, 80%, 0.7), hsla(30, 30%, 40%, 0.1) 25%, hsla(250, 50%, 40%, 0.1) 25%, hsla(250, 30%, 30%, 0.7) 50%, hsla(250, 50%, 40%, 0.1) 75%, hsla(30, 30%, 40%, 0.1) 75%, hsla(50, 100%, 80%, 0.7));
--background-custom-button: linear-gradient(to bottom, hsla(208, 38%, 21%, 0.988) 5%, hsla(202, 42%, 14%, 0.671) 100%);
@@ -528,6 +529,10 @@ select,
flex-direction: row;
margin: 0.1rem 0;
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section subline .warning {
border-radius: 6px;
background: var(--gradient-warning);
}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-choix roll-section roll-part-img {
display: flex;
flex-direction: column;

View File

@@ -27,6 +27,11 @@
--gradient-red: linear-gradient(150deg, rgba(255, 0, 0, 0.3), rgba(255, 200, 128, 0.05),rgba(255, 200, 128, 0.1), rgba(255,10,0,0.3));
--gradient-violet: linear-gradient(150deg, rgba(100, 45, 124, 0.6), rgba(216, 157, 192, 0.3), rgba(177, 157, 216, 0.5), rgba(107, 62, 121, 0.3), rgba(100, 45, 124, 0.6));
--gradient-purple-black: linear-gradient(150deg, rgba(0, 0, 0, 0.7), rgba(100, 45, 124, 0.4), rgba(82, 17, 131, 0.3),rgba(100, 45, 124, 0.4), rgba(0, 0, 0, 0.7));
--gradient-warning: linear-gradient(150deg,
hsla(32, 100%, 50%, 0.3),
hsla(52, 60%, 50%, 0.1),
hsla(32, 60%, 50%, 0.1),
hsla(32, 100%, 50%, 0.3));
--gradient-silver-light: linear-gradient(30deg, rgba(61, 55, 93, 0.2), rgba(178, 179, 196, 0.1), rgba(59, 62, 63, 0.2), rgba(206, 204, 199, 0.1), rgba(61, 46, 49, 0.2));
--gradient-daylight: conic-gradient(
from 0deg,

View File

@@ -89,6 +89,10 @@
display: flex;
flex-direction: row;
margin: 0.1rem 0;
.warning {
border-radius: 6px;
background: var(--gradient-warning);
}
}
roll-part-img {

View File

@@ -3,7 +3,7 @@ import { HtmlUtility } from "./html-utility.js";
import { RdDBonus } from "./rdd-bonus.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { CARACS, RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
@@ -174,7 +174,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
await this.getBlessure(event)?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } })
});
this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac('chance-actuelle'))
this.html.find('.roll-chance-actuelle').click(async event => await this.actor.rollCarac(CARACS.CHANCE_ACTUELLE))
this.html.find('.button-appel-chance').click(async event => await this.actor.rollAppelChance())
this.html.find('[name="jet-astrologie"]').click(async event => await this.actor.astrologieNombresAstraux())
@@ -208,7 +208,7 @@ export class RdDActorSheet extends RdDBaseActorSangSheet {
}
// Points de reve actuel
this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac('reve-actuel', { resistance: true }))
this.html.find('.roll-reve-actuel').click(async event => await this.actor.rollCarac(CARACS.REVE_ACTUEL, { resistance: true }))
this.html.find('.action-empoignade').click(async event => await RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor)))
this.html.find('.roll-arme').click(async event => {

View File

@@ -47,6 +47,9 @@ import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
import { ROLL_TYPE_MEDITATION } from "./roll/roll-constants.mjs";
import { PART_TACHE } from "./roll/roll-part-tache.mjs";
import { PART_COMP } from "./roll/roll-part-comp.mjs";
export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
@@ -98,6 +101,8 @@ export class RdDActor extends RdDBaseActorSang {
}
isPersonnage() { return true }
isFeminin() { return this.system.sexe.length > 0 && this.system.sexe.charAt(0).toLowerCase() == 'f' }
isHautRevant() { return this.system.attributs.hautrevant.value != "" }
/* -------------------------------------------- */
@@ -1343,7 +1348,7 @@ export class RdDActor extends RdDBaseActorSang {
async _apprecierCuisine(item, seForcer) {
const surmonteExotisme = await this._surmonterExotisme(item, seForcer);
if (surmonteExotisme) {
await this.apprecier('gout', 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat");
await this.apprecier(CARACS.ODORATGOUT, 'cuisine', item.system.qualite, item.system.boisson ? "apprécie la boisson" : "apprécie le plat");
}
else if (seForcer) {
await this.jetDeMoral('malheureux');
@@ -1361,7 +1366,7 @@ export class RdDActor extends RdDBaseActorSang {
if (exotisme < 0 || qualite < 0) {
const competence = qualite > 0 ? 'cuisine' : undefined
const difficulte = Math.min(exotisme, qualite)
const rolled = await this.doRollCaracCompetence('volonte', competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
const rolled = await this.doRollCaracCompetence(CARACS.VOLONTE, competence, difficulte, { title: `tente de surmonter l'exotisme de ${item.name}` })
return rolled.isSuccess
}
return true;
@@ -1923,6 +1928,20 @@ export class RdDActor extends RdDBaseActorSang {
}
async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_COMP], current: PART_COMP },
selected: {
carac: {key: caracName },
comp: { key: compName, forced: options.forced }
}
}
RollDialog.create(rollData, options)
return
}
RdDEmpoignade.checkEmpoignadeEnCours(this)
const competence = this.getCompetence(compName);
await this.openRollDialog({
@@ -1945,23 +1964,34 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollTache(id, options = {}) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
const tacheData = this.getTache(id)
const compData = this.getCompetence(tacheData.system.competence)
compData.system.defaut_carac = tacheData.system.carac; // Patch !
const tache = this.getTache(id)
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { tache: { key: tache.id, forced: options.forced} },
type: { allowed: [PART_TACHE], current: PART_TACHE }
}
RollDialog.create(rollData, options)
return
}
const compData = this.getCompetence(tache.system.competence)
compData.system.defaut_carac = tache.system.carac; // Patch !
await this.openRollDialog({
name: 'jet-competence',
label: 'Jet de Tâche ' + tacheData.name,
label: 'Jet de Tâche ' + tache.name,
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.hbs',
rollData: {
competence: compData,
tache: tacheData,
diffLibre: tacheData.system.difficulte,
tache: tache,
diffLibre: tache.system.difficulte,
diffConditions: 0,
use: { libre: false, conditions: true },
carac: {
[tacheData.system.carac]: foundry.utils.duplicate(this.system.carac[tacheData.system.carac])
[tache.system.carac]: foundry.utils.duplicate(this.system.carac[tache.system.carac])
}
},
callbacks: [{ action: r => this._tacheResult(r, options) }]
@@ -2015,6 +2045,17 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */
async rollMeditation(id) {
const meditation = foundry.utils.duplicate(this.getMeditation(id));
if (meditation && OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
selected: { meditation: { key: id } },
type: { allowed: [ROLL_TYPE_MEDITATION], current: ROLL_TYPE_MEDITATION }
}
await RollDialog.create(rollData)
return
}
const competence = foundry.utils.duplicate(this.getCompetence(meditation.system.competence));
competence.system.defaut_carac = "intellect"; // Meditation = toujours avec intellect
let meditationData = {
@@ -2415,14 +2456,17 @@ export class RdDActor extends RdDBaseActorSang {
if (!blessure.system.premierssoins.done) {
const tache = await this.getTacheBlessure(blesse, blessure);
return await this.rollTache(tache.id, {
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r)
onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r),
title: 'Premiers soins',
forced: true
});
}
if (!blessure.system.soinscomplets.done) {
else if (!blessure.system.soinscomplets.done) {
const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0);
return await this.rollCaracCompetence("dexterite", "Chirurgie", diff, {
return await this.rollCaracCompetence(CARACS.DEXTERITE, "Chirurgie", diff, {
title: "Soins complets",
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r)
onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r),
forced: true
})
}
}
@@ -2437,6 +2481,7 @@ export class RdDActor extends RdDBaseActorSang {
})
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && !blessure.system.premierssoins.done) {
const tache = rollData.tache;
if (rollData.rolled.isETotal) {
@@ -3065,12 +3110,7 @@ export class RdDActor extends RdDBaseActorSang {
actorId: this.id
}
}
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
}
})
await RollDialog.create(rollData, { onRollDone: RollDialog.onRollDoneClose })
}
/* -------------------------------------------- */

View File

@@ -11,7 +11,7 @@ import { ITEM_TYPES } from "../constants.js";
import { StatusEffects, STATUSES } from "../settings/status-effects.js";
import { Targets } from "../targets.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { RdDCarac } from "../rdd-carac.js";
import { CARACS, RdDCarac } from "../rdd-carac.js";
import { RdDRollResult } from "../rdd-roll-result.js";
import { RdDItemArme } from "../item/arme.js";
@@ -28,6 +28,7 @@ import { MappingCreatureArme } from "../item/mapping-creature-arme.mjs";
import RollDialog from "../roll/roll-dialog.mjs";
import { ATTAQUE_ROLL_TYPES, DEFAULT_ROLL_TYPES, DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP, ROLL_TYPE_JEU, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_TACHE } from "../roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "../settings/options-avancees.js";
import { PART_COMP } from "../roll/roll-part-comp.mjs";
/**
* Classe de base pour les acteurs disposant de rêve (donc, pas des objets)
@@ -380,9 +381,22 @@ export class RdDBaseActorReve extends RdDBaseActor {
}
/* -------------------------------------------- */
async rollCarac(caracName, options = {}) {
if (Grammar.equalsInsensitive(caracName, 'taille')) {
if (Grammar.equalsInsensitive(caracName, CARACS.TAILLE)) {
return
}
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
const rollData = {
ids: { actorId: this.id },
type: { allowed: [PART_COMP], current: PART_COMP },
selected: {
carac: { key: caracName },
comp: options.resistance ? { key: undefined, forced: true } : undefined
}
}
RollDialog.create(rollData, options)
return
}
foundry.utils.mergeObject(options, { resistance: false, diff: 0 }, { overwrite: false })
RdDEmpoignade.checkEmpoignadeEnCours(this)
let selectedCarac = this.getCaracByName(caracName)

View File

@@ -161,7 +161,7 @@ export class RdDBaseActorSang extends RdDBaseActorReve {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this.getFatigueMin());
}
await this.update({ "system.sante": sante })
await this.update({ "system.sante": sante }, { render: true })
if (this.isDead()) {
await this.setEffect(STATUSES.StatusComma, true);
}

View File

@@ -46,6 +46,7 @@ export class RdDBaseActor extends Actor {
}
static init() {
Handlebars.registerHelper('actor-isFeminin', actor => actor.isFeminin())
Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
Hooks.on("updateItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onUpdateItem(item, options, id))
@@ -220,6 +221,7 @@ export class RdDBaseActor extends Actor {
isHautRevant() { return false }
isVehicule() { return false }
isPersonnage() { return false }
isFeminin() { return false }
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
@@ -254,7 +256,7 @@ export class RdDBaseActor extends Actor {
}
}
async onUpdateItem(item, options, id){
async onUpdateItem(item, options, id) {
switch (item.type) {
case ITEM_TYPES.blessure:
await this.changeBleedingState()

View File

@@ -274,11 +274,10 @@ export class Mapping {
}
static getDescription(actor) {
const sexe = actor.system.sexe
const sexeFeminin = sexe.length > 0 && sexe.charAt(0).toLowerCase() == 'f' ? 'Née' : 'Né'
const naissance = actor.isFeminin() ? 'née' : 'né'
const race = ['', 'humain'].includes(Grammar.toLowerCaseNoAccent(actor.system.race)) ? '' : (actor.system.race + ' ')
const heure = actor.system.heure
const hn = `${sexeFeminin} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const hn = `${naissance} à l'heure ${RdDTimestamp.definition(heure).avecArticle}`
const age = (actor.system.age && actor.system.age >0) ? `${actor.system.age} ans` : undefined
const taille = actor.system.taille
const poids = actor.system.poids

View File

@@ -63,12 +63,12 @@ export class RdDBonus {
const dmg = {
total: 0,
dmgArme: dmgArme,
penetration: arme.penetration(),
penetration: arme?.penetration() ?? 0,
diff: attaque.diff,
dmgTactique: attaque.tactique?.dmg ?? 0,
dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme.system.mortalite, rollData.opponent?.actor?.isEntite()),
mortalite: RdDBonus.mortalite(attaque.dmg?.mortalite, arme?.system.mortalite, rollData.opponent?.actor?.isEntite()),
dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - attaque.forceRequise),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0

View File

@@ -1066,7 +1066,7 @@ export class RdDCombat {
opponentTokenId: this.attackerTokenId,
opponentId: this.attackerId,
},
type: { allowed: ['defense'], current: 'defense' },
type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE },
attackerRoll: RollDialogAdapter.mapActionAttaque(attackerRoll),
passeArme: attackerRoll.passeArme,
})
@@ -1074,10 +1074,7 @@ export class RdDCombat {
async doRollDefense(rollData, callbacks = []) {
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
},
onRollDone: RollDialog.onRollDoneClose,
customChatMessage: true,
callbacks: [
async (roll) => {

View File

@@ -301,6 +301,8 @@ export class RdDUtility {
Handlebars.registerHelper('json-stringify', object => JSON.stringify(object))
// math
Handlebars.registerHelper('math-sum', (...values) => values.slice(0, -1).reduce(Misc.sum(), 0))
Handlebars.registerHelper('math-abs', diff => Math.abs(parseInt(diff)))
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('repeat', function (n, block) {
let accum = '';

View File

@@ -42,10 +42,10 @@ import ChatRollResult from "./chat-roll-result.mjs";
import { renderTemplate } from "../constants.js";
import { RollTypeCuisine } from "./roll-type-cuisine.mjs";
import { RollPartCuisine } from "./roll-part-cuisine.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "../settings/options-avancees.js";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
const doNothing = (dialog) => { }
const ALL_ROLL_TYPES = [
new RollTypeComp(),
@@ -172,6 +172,14 @@ const ROLL_PARTS = [
/* -------------------------------------------- */
export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2)
{
static onRollDoneDoNothing(dialog) {
dialog.render()
}
static onRollDoneClose(dialog) {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
}
static init() {
}
@@ -275,9 +283,9 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
rollData.options = rollData.options ?? { rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => p.initialize(rollData))
ROLL_PARTS.forEach(p => p.restore(rollData))
ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => {
p.restore(rollData)
p.loadRefs(rollData)
p.prepareContext(rollData)
})
@@ -307,7 +315,7 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
...(rollOptions.callbacks ?? [])
],
customChatMessage: rollOptions.customChatMessage,
onRollDone: rollOptions.onRollDone ?? doNothing
onRollDone: rollOptions.onRollDone ?? RollDialog.onRollDoneDoNothing
}
this.chatRollResult = new ChatRollResult();
this.selectType()
@@ -328,16 +336,16 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
return ROLL_PARTS.filter(p => p.isActive(rollData))
}
// get title() {
// return this.rollData.title ?? `Jet de dés de ${this.rollData.active.actor.name}`
// }
rollTitle(rollData) {
return rollData.label ?? ROLL_PARTS
const title = rollData.label ?? ROLL_PARTS
.filter(it => it.section == ROLLDIALOG_SECTION.ACTION)
.filter(it => it.isActive(rollData))
.map(it => it.title(rollData))
.reduce(Misc.joining(' '))
.reduce(Misc.joining(' '));
if (this.rollOptions.title) {
return `${this.rollOptions.title} ${title}`
}
return title
}
async _onRender(context, options) {
@@ -428,15 +436,20 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
async roll() {
const roll = RollDialog.saveParts(this.rollData)
const selectedRollType = this.getSelectedType(roll);
RollDialog.loadRollData(roll)
roll.current.resultat = this.rollData.current[PART_TRICHER]?.resultat ?? -1
roll.choix = {}
roll.rolled = await RollDialogAdapter.rollDice(roll, this.rollTitle(roll))
roll.result = this.getSelectedType(roll).getResult(roll)
roll.result = selectedRollType.getResult(roll)
console.info('RollDialog.roll:', roll)
const callbacks = [
...this.rollOptions.callbacks,
...selectedRollType.callbacks(this.rollOptions),
]
await Promise.all(callbacks.map(async callback => await callback(roll)))
await this.chatRollResult.display(roll)
await Promise.all(this.rollOptions.callbacks.map(async callback => await callback(roll)))
this.rollOptions.onRollDone(this)
}

View File

@@ -28,9 +28,7 @@ export class RollPartCheckbox extends RollPart {
/* TODO: user setting? */
current.checked = true
}
if (current.value == undefined) {
current.value = this.getCheckboxValue(rollData)
}
current.icon = this.getCheckboxIcon(rollData)
}

View File

@@ -17,7 +17,14 @@ export class RollPartComp extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const selected = this.getSelected(rollData)
refs.all = this.$getActorComps(rollData)
.filter(comp => !selected.forced ||
(selected.key ?
Grammar.includesLowerCaseNoAccent(comp.name, selected.key)
: comp.key == '')
)
refs.comps = refs.all
this.$selectComp(rollData)
}
@@ -49,7 +56,6 @@ export class RollPartComp extends RollPartSelect {
allowed = allowed.filter(it => it != undefined)
const refs = this.getRefs(rollData)
refs.comps = allowed.length > 0
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.label)))
? refs.all.filter(it => allowed.includes(it.label))
: refs.all
this.$selectComp(rollData)

View File

@@ -11,26 +11,41 @@ import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_MEDITATION = "meditation"
const COMPORTEMENTS = ['isComportement', 'isHeure', 'isPurification', 'isVeture']
export class RollPartMeditation extends RollPartSelect {
get code() { return PART_MEDITATION }
get section() { return ROLLDIALOG_SECTION.CHOIX }
store(rollData, targetData) {
const current = this.getCurrent(rollData)
this.setSaved(targetData, {
key: current.key,
isComportement: current.isComportement,
isHeure: current.isHeure,
isPurification: current.isPurification,
isVeture: current.isVeture
})
}
isValid(rollData) { return rollData.active.actor.isPersonnage() && rollData.active.actor.isHautRevant() }
visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_MEDITATION) }
loadRefs(rollData) {
const refs = this.getRefs(rollData)
foundry.utils.mergeObject(refs,
{
meditations: rollData.active.actor.itemTypes[ITEM_TYPES.meditation]
refs.meditations = rollData.active.actor.itemTypes[ITEM_TYPES.meditation]
.map(it => RollPartMeditation.$extractMeditation(it, rollData.active.actor))
}
)
if (refs.meditations.length > 0) {
this.$selectMeditation(rollData)
this.$selectConditionMeditation(rollData)
const selected = this.getSelected(rollData)
const current = this.getCurrent(rollData)
COMPORTEMENTS.filter(it => selected[it]).forEach(it => current[it] = selected[it])
}
}
choices(refs) { return refs.meditations }
static $extractMeditation(meditation, actor) {
@@ -48,12 +63,7 @@ export class RollPartMeditation extends RollPartSelect {
getMalusConditions(rollData) {
const current = this.getCurrent(rollData)
const conditionsManquantes = [
current.isComportement,
current.isHeure,
current.isPurification,
current.isVeture
].filter(it => !it).length
const conditionsManquantes = COMPORTEMENTS.filter(it => !current[it]).length
return -2 * conditionsManquantes
}
@@ -71,13 +81,18 @@ export class RollPartMeditation extends RollPartSelect {
const previous = this.getCurrent(rollData)
const current = this.selectByKey(rollData, key, 0)
if (current.key != previous.key) {
const heureMonde = RdDTimestamp.getWorldTime().heure
const heureMeditation = RdDTimestamp.findHeure(current.meditation.system.heure)?.heure
current.isHeure = heureMeditation == heureMonde
current.isTMR = Grammar.equalsInsensitive(current.meditation.system.tmr, TMRUtility.getTMRType(rollData.active.actor.system.reve.tmrpos.coord))
this.$selectConditionMeditation(rollData)
}
}
$selectConditionMeditation(rollData) {
const current = this.getCurrent(rollData)
current.heureMonde = RdDTimestamp.getWorldTime().heure
current.heureMeditation = RdDTimestamp.findHeure(current.meditation.system.heure)?.heure
current.isHeure = current.heureMeditation == current.heureMonde
current.isTMR = Grammar.equalsInsensitive(current.meditation.system.tmr, TMRUtility.getTMRType(rollData.active.actor.system.reve.tmrpos.coord))
}
async _onRender(rollDialog, context, options) {
const selectMeditation = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-meditation"]`)
@@ -88,10 +103,7 @@ export class RollPartMeditation extends RollPartSelect {
rollDialog.render()
})
this.setupListenerCondition(rollDialog, 'isComportement')
this.setupListenerCondition(rollDialog, 'isHeure')
this.setupListenerCondition(rollDialog, 'isPurification')
this.setupListenerCondition(rollDialog, 'isVeture')
COMPORTEMENTS.forEach(it => this.setupListenerCondition(rollDialog, it))
}
setupListenerCondition(rollDialog, inputName) {
@@ -108,7 +120,7 @@ export class RollPartMeditation extends RollPartSelect {
const current = this.getCurrent(rollData)
switch (part.code) {
case PART_CARAC: return part.filterCaracs(rollData, [CARACS.INTELLECT])
case PART_COMP: return part.filterComps(rollData,[current.comp?.name])
case PART_COMP: return part.filterComps(rollData, [current.comp?.name])
}
}
return undefined

View File

@@ -1,5 +1,4 @@
import { ITEM_TYPES } from "../constants.js"
import { Grammar } from "../grammar.js"
import { ROLL_TYPE_TACHE } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
@@ -18,13 +17,17 @@ export class RollPartTache extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
refs.taches = rollData.active.actor.itemTypes[ITEM_TYPES.tache]
const selected = this.getSelected(rollData)
refs.all = rollData.active.actor.itemTypes[ITEM_TYPES.tache]
.filter(tache => !selected.forced || tache.id == selected.key)
.filter(tache => tache.system.points_de_tache_courant < tache.system.points_de_tache)
.map(tache => RollPartTache.$extractTache(tache, rollData.active.actor))
refs.taches = refs.all
if (refs.taches.length > 0) {
this.$selectTache(rollData)
}
}
choices(refs) { return refs.taches }
static $extractTache(tache, actor) {

View File

@@ -43,6 +43,7 @@ export class RollPart {
}
/** les informations minimales représentant la sélection dans le rollData permettant de restaurer la fenêtre */
getSelected(rollData) { return this.getSaved(rollData) }
getSaved(rollData) {
return rollData.selected[this.code] ?? {}
}
@@ -86,7 +87,7 @@ export class RollPart {
prepareContext(rollData) { }
/** permet de sauvegarder dans rollData les informations (cas des champs edit) */
validate(rollData) {}
validate(rollData) { }
/** ---- cross roll-part filtering ---- */
applyImpact(rollData, filter) { }

View File

@@ -1,3 +1,4 @@
import { RdDItemSigneDraconique } from "../item/signedraconique.js"
import { DIFF, ROLL_TYPE_MEDITATION } from "./roll-constants.mjs"
import { PART_MEDITATION } from "./roll-part-meditation.mjs"
import { RollType } from "./roll-type.mjs"
@@ -16,4 +17,20 @@ export class RollTypeMeditation extends RollType {
onSelect(rollData) {
this.setDiffType(rollData, DIFF.AUCUN)
}
callbacks(rollOptions) { return [RollTypeMeditation.$onRollMeditation] }
static async $onRollMeditation(rollData) {
const actor = rollData.active.actor
const meditation = rollData.current.meditation.meditation
const rolled = rollData.rolled
if (meditation && rolled) {
if (rolled.isSuccess) {
await actor.createEmbeddedDocuments("Item", [RdDItemSigneDraconique.prepareSigneDraconiqueMeditation(meditation, rolled)])
}
if (rolled.isEPart) {
await actor.updateEmbeddedDocuments('Item', [{ _id: meditation._id, 'system.malus': meditation.system.malus - 1 }])
}
await actor.santeIncDec("fatigue", 2)
}
}
}

View File

@@ -1,3 +1,5 @@
import { ITEM_TYPES } from "../constants.js"
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { DIFF, ROLL_TYPE_TACHE } from "./roll-constants.mjs"
import { PART_TACHE } from "./roll-part-tache.mjs"
import { RollType } from "./roll-type.mjs"
@@ -16,4 +18,26 @@ export class RollTypeTache extends RollType {
onSelect(rollData) {
this.setDiffType(rollData, DIFF.AUCUN)
}
callbacks(rollOptions) { return [ async r => await RollTypeTache.$onRollTache(r, rollOptions)] }
static async $onRollTache(rollData, rollOptions) {
const actor = rollData.active.actor
const tache = rollData.current[PART_TACHE].tache
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
await actor.santeIncDec("fatigue", tache.system.fatigue)
}
rollData.current[PART_TACHE].tache = await tache.update({
'system.points_de_tache_courant': tache.system.points_de_tache_courant + rollData.rolled.ptTache,
'system.nb_jet_succes': tache.system.nb_jet_succes + (rollData.rolled.isSuccess ? 1 : 0),
'system.nb_jet_echec': tache.system.nb_jet_echec + (rollData.rolled.isSuccess ? 0 : 1),
'system.difficulte': tache.system.difficulte - (rollData.rolled.isETotal ? 1 : 0),
}, {render:true})
if (rollOptions?.onRollAutomate) {
await rollOptions.onRollAutomate(rollData)
}
}
}

View File

@@ -18,7 +18,6 @@ export class RollType {
isAllowed(rollData) { return rollData.type.allowed == undefined || rollData.type.allowed.includes(this.code) }
visible(rollData) { return true }
title(rollData) { return this.code }
isSelected(rollData) { return rollData.type.current == this.code }
@@ -52,7 +51,7 @@ export class RollType {
this.setRollDataType(rollData)
}
getResult(rollData) {
return undefined
}
callbacks(rollOptions) { return [] }
getResult(rollData) { return undefined }
}

View File

@@ -50,7 +50,7 @@ export class OptionsAvancees extends FormApplication {
.map(it => {
it = foundry.utils.duplicate(it)
it.id = OptionsAvancees._getId(it.name)
it.active = OptionsAvancees.isSet(it.name)
it.active = OptionsAvancees.isUsing(it.name)
return it
})
formData.regles = regles
@@ -63,10 +63,6 @@ export class OptionsAvancees extends FormApplication {
}
static isUsing(name) {
return OptionsAvancees.isSet(name)
}
static isSet(name) {
return game.settings.get(SYSTEM_RDD, OptionsAvancees._getId(name))
}

View File

@@ -8,7 +8,6 @@
</li>
{{#each combat as |action key|}}
<li class="item flexrow list-item" data-item-id="{{action._id}}" data-arme-name="{{action.arme.name}}"
{{log 'action' action}}
data-competence-name="{{action.comp.name}}"
data-tooltip="{{action.label}}: niveau {{plusMoins action.comp.system.niveau}}">
<span class="list-item-label flexrow">

View File

@@ -0,0 +1,44 @@
<div class="roll-chat">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
<img src="{{current.comp.img}}" data-tooltip="{{current.comp.name}}" />
</div>
<div class="chat-header">
{{active.name}} a médité : {{current.meditation.label}}
</div>
<div class="chat-resume">
{{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}}
<br>{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}}
</div>
<div class="chat-details">
<hr>
<span class='chat-card-info'>
{{#if rolled.isSuccess}}
<span>
{{active.name}} aperçoit un signe draconique éphémère, qu'il faut aller lire en <strong>{{typeTmr-name current.meditation.meditation.system.tmr}}</strong>.
</span>
{{else}}
{{active.name}} ne distingue aucun signe Draconique, la meditation n'a pas porté de fruits.
{{/if}}
</span>
<br>
{{#if rolled.isEPart}}
<span class='chat-card-info'>
<span>L'échec {{#if rolled.isETotal}}total{{else}}particulier{{/if}} augmente la difficulté de la méditation
{{current.meditation.meditation.name}} de 1 !</span>
</span>
{{/if}}
{{#if (regle-optionnelle 'appliquer-fatigue')}}
<span class='chat-card-info'>
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/fatigue.svg"/>
{{active.name}} s'est fatigué{{~#if (actor-isFeminin active.actor)}}e{{/if}} de 2 cases.
</span>
{{/if}}
</div>
<div class="chat-buttons">
</div>
</div>

View File

@@ -0,0 +1,57 @@
<div class="roll-chat">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
<img src="{{current.tache.comp.img}}" data-tooltip="{{current.tache.comp.name}}" />
</div>
<div class="chat-header">
{{active.name}} travaille à sa tâche : {{current.tache.label}}
</div>
<div class="chat-resume">
{{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}}
<br>{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}}
</div>
<div class="chat-details">
<hr>
<p>
{{log 'tache' this }}
{{#if rolled.isSuccess}}
{{active.name}} a obtenu {{rolled.ptTache}} point{{~#unless (eq rolled.ptTache 1)}}s{{/unless}} de tâche,
{{else if (or rolled.isETotal rolled.isEPart)}}
{{active.name}} a perdu {{math-abs rolled.ptTache}} points de tâche,
{{else}}
{{active.name}} n'a pas progressé dans la tâche.
{{/if}}
{{#with current.tache.tache.system as |system|}}
Son avancement est de
<span class="rdd-roll-{{#if (gt system.points_de_tache_courant 0)}}norm{{else}}etotal{{/if}}">
{{system.points_de_tache_courant}}
{{#unless system.cacher_points_de_tache}} sur {{system.points_de_tache}}{{/unless}}
</span>
point{{~#unless (eq system.points_de_tache_courant 1)}}s{{/unless}} de tâche
{{log 'tentatives' (math-sum system.nb_jet_succes system.nb_jet_echec) }}
({{~#with (math-sum system.nb_jet_succes system.nb_jet_echec) as |tentatives|}}
{{log 'tentatives' tentatives}}
{{~#if (ne tentatives 1)}}{{tentatives}} tentatives{{else}}première tentative{{/if~}}
{{/with~}}).
{{/with}}
</p>
{{#if rolled.isETotal}}
<span class='chat-card-info'>
Son échec total augmente de 1 la difficulté de la tâche!</span>
</span>
{{/if}}
{{#if (and current.tache.tache.system.fatigue (regle-optionnelle 'appliquer-fatigue'))}}
<span class='chat-card-info'>
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/fatigue.svg"/>
{{active.name}} s'est fatigué{{~#if (actor-isFeminin active.actor)}}e{{/if}} de {{current.tache.tache.system.fatigue}} case{{~#if (gt current.tache.tache.system.fatigue 1)}}s{{/if}}.
</span>
{{/if}}
</div>
<div class="chat-buttons">
</div>
</div>

View File

@@ -19,14 +19,22 @@
<subline><strong><span name="theme">{{current.meditation.system.theme}}</span></strong></subline>
<subline>Support:&nbsp;<strong><span name="support">{{current.meditation.system.support}}</span></strong></subline>
<subline>
TMR:&nbsp;<strong><span name="tmr">{{typeTmr-name current.meditation.system.tmr}}</span></strong>
{{#unless current.isTMR}}
&nbsp;<i class="fa-solid fa-circle-exclamation"></i> actuelle: {{caseTmr-label rollData.active.actor.system.reve.tmrpos.coord}} {{rollData.active.actor.system.reve.tmrpos.coord}}
&nbsp;<div class="warning">
<i class="fa-solid fa-circle-exclamation"></i>
{{caseTmr-label rollData.active.actor.system.reve.tmrpos.coord}}
{{rollData.active.actor.system.reve.tmrpos.coord}}
</div>
{{/unless}}
</subline>
</subline>
<subline>Durée:&nbsp;60 minutes</subline>
<hr>
<br>
<subline>
<input class="conditionMeditation" type="checkbox" name="isComportement" {{#if current.isComportement}}checked{{/if}} {{#if rollData.type.retry}}disabled{{/if}}/>
<label for="isComportement">Comportement:&nbsp;
@@ -37,10 +45,17 @@
<subline>
<input class="conditionMeditation" type="checkbox" name="isHeure" {{#if current.isHeure}}checked{{/if}} {{#if rollData.type.retry}}disabled{{/if}}/>
<label for="isHeure">Heure:&nbsp;
{{#with current.meditation.system.heure as |heure|}}
<span name="heure">{{timestamp-label heure}}</span>
<span name="heure-signe">{{timestamp-imgSigneHeure heure}}</span>
{{/with}}
<span name="heure">{{timestamp-label current.heureMeditation}}</span>
<span name="heure-signe">
{{timestamp-imgSigneHeure current.heureMeditation}}
</span>
{{#if (ne current.heureMeditation current.heureMonde)}}
<span class="warning">
<i class="fa-solid fa-circle-exclamation"></i>
{{timestamp-label current.heureMonde}}
{{timestamp-imgSigneHeure current.heureMonde}}
</span>
{{/if}}
</label>
</subline>