Compare commits

...

13 Commits

Author SHA1 Message Date
3821b5bea1 Merge pull request 'v11.0.9 - Les Souvenirs de Khrachtchoum le Problémeux' (#657) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#657
2023-06-21 10:40:17 +02:00
2739c650d9 Version 11.0.9 - Les Souvenirs de Khrachtchoum 2023-06-20 23:43:59 +02:00
b101d58bee Correction typo "optionnelle" 2023-06-20 23:43:58 +02:00
0e3d721fc7 Nouvelle incarnation de l'archétype 2023-06-20 23:35:38 +02:00
e1e45a83be Correction titre des fenêtres Item 2023-06-20 23:35:38 +02:00
3b14e54829 Mode de saisie d'archétype 2023-06-20 23:35:38 +02:00
615a3f1315 Organisation des règles optionelles 2023-06-20 03:05:48 +02:00
ca72d088cc Merge pull request 'v11 - report v10.7.20' (#656) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#656
2023-06-19 15:36:53 +02:00
ac89b948ca Mise à jour du système
- lien vers le change log
- organisation des compendiums par défaut
2023-06-16 23:33:44 +02:00
f22c4d2ec8 Fin d'empoignade
En cours de round en atteignant 2 points d'empoignade, on peut
uniquement entraîner au sol.

En fin de round, si la victime est immobilisée, on peut projeter, ou
faire perdre de l'endurance
2023-06-15 02:03:49 +02:00
ffd80394d3 Restreindre les actions d'empoignade
Seul le propriétaire du défenseur peut effecuer les contres
d'empoignade ou tenter de se libérer.

Seul le propriétaire de l'empoigneur peut tenter d'empêcher
la libération du défenseur, de projeter au sol, ou de faire perdre
de l'endurance.
2023-06-15 02:03:43 +02:00
0959d1b18e Correction malus de taille empoignade 2023-06-15 02:03:37 +02:00
24e58abfcc Fix empoignade
- les items d'empoignade sont ajoutés par le MJ
- les caractéristiques du défenseur sont utilisées pour la défense
- la difficulté d'attaque est imposée au défenseur
- les particulières sont en finesse (p133)
2023-06-15 02:03:30 +02:00
39 changed files with 756 additions and 425 deletions

View File

@@ -1,5 +1,23 @@
# v11.0
## v11.0.9 - Les Souvenirs de Khrachtchoum le Problémeux
- mode de saisie de l'archétype en vue détaillée
- création une nouvelle incarnation depuis l'archétype
- réorganisation de la fenêtre de sélection des règles optionnelles
- correction de l'affichage du type dans les fenêtres d'objets
## v11.0.8 - la poigne de Sémolosse
- lien vers le changelog
- organisation des compendiums du système
- correction de l'empoignade
- les items d'empoignade sont ajoutés par le MJ quand nécessaire
- seul le joueur propriétaire du personnage peut effectuer ses choix et actions d'empoignade
- les caractéristiques du défenseur sont utilisées pour la défense
- la difficulté d'attaque est imposée au défenseur
- les attaques particulières sont en finesse (p133)
- on peut entraîner au sol dès 2 points d'empoignade
- les actions liée à l'immobilisation sont proposées en fin de round
# v11.0.7
- les créatures ont maintenant le droit d'avoir des compétences de tir, lancer, mêlée, armes naturelles, parade.

View File

@@ -7,7 +7,7 @@ import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
@@ -33,7 +33,8 @@ export class RdDActorSheet extends RdDBaseActorSheet {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
vueDetaillee: false,
vueArchetype: false,
});
}
@@ -114,7 +115,7 @@ export class RdDActorSheet extends RdDBaseActorSheet {
activateListeners(html) {
super.activateListeners(html);
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
@@ -353,12 +354,19 @@ export class RdDActorSheet extends RdDBaseActorSheet {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
this.html.find('.toggle-archetype').click(async event => {
this.options.vueArchetype = !this.options.vueArchetype;
this.render(true);
});
// On competence archetype change
this.html.find('.competence-archetype').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceArchetype(compName, parseInt(event.target.value));
});
}
this.html.find('.nouvelle-incarnation').click(async event => {
this.actor.nouvelleIncarnation();
});
this.html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;

View File

@@ -18,7 +18,7 @@ import { RdDAlchimie } from "./rdd-alchimie.js";
import { STATUSES, StatusEffects } from "./settings/status-effects.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { Draconique } from "./tmr/draconique.js";
import { RdDCarac } from "./rdd-carac.js";
@@ -134,7 +134,7 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
getFatigueActuelle() {
if (ReglesOptionelles.isUsing("appliquer-fatigue") && this.isPersonnage()) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && this.isPersonnage()) {
return this.system.sante.fatigue?.value;
}
return 0;
@@ -273,24 +273,24 @@ export class RdDActor extends RdDBaseActor {
getDraconicOuPossession() {
const possession = this.items.filter(it => it.type == TYPES.competencecreature && it.system.categorie == 'possession')
.sort(Misc.descending(it => it.system.niveau))
.find(it=>true);
.find(it => true);
if (possession) {
return possession.clone();
}
const draconics = [...this.getDraconicList().filter(it => it.system.niveau >= 0).map(it =>it.clone()),
const draconics = [...this.getDraconicList().filter(it => it.system.niveau >= 0).map(it => it.clone()),
POSSESSION_SANS_DRACONIC]
.sort(Misc.descending(it => it.system.niveau));
return draconics[0];
}
getPossession(possessionId) {
return this.items.find(it => it.type == 'possession' && it.system.possessionid == possessionId);
return this.items.find(it => it.type == TYPES.possession && it.system.possessionid == possessionId);
}
getPossessions() {
return this.items.filter(it => it.type == 'possession');
return this.items.filter(it => it.type == TYPES.possession);
}
getEmpoignades() {
return this.items.filter(it => it.type == 'empoignade');
return this.items.filter(it => it.type == TYPES.empoignade);
}
getDemiReve() {
return this.system.reve.tmrpos.coord;
@@ -457,7 +457,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async dormirChateauDormant() {
if (!ReglesOptionelles.isUsing("chateau-dormant-gardien") || !this.system.sommeil || this.system.sommeil.nouveaujour) {
if (!ReglesOptionnelles.isUsing("chateau-dormant-gardien") || !this.system.sommeil || this.system.sommeil.nouveaujour) {
const message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: ""
@@ -712,7 +712,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async recupererFatigue(message) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
let fatigue = this.system.sante.fatigue.value;
const fatigueMin = this._computeFatigueMin();
if (fatigue <= fatigueMin) {
@@ -961,7 +961,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async updateCompetence(idOrName, compValue) {
const competence = this.getCompetence(idOrName);
const competence = this.getCompetence(idOrName);
if (competence) {
const toNiveau = compValue ?? RdDItemCompetence.getNiveauBase(competence.system.categorie, competence.getCategories());
this.notifyCompetencesTronc(competence, toNiveau);
@@ -1178,7 +1178,7 @@ export class RdDActor extends RdDBaseActor {
}
// Pour les autres
let state = Math.min(this.system.sante.vie.value - this.system.sante.vie.max, 0);
if (ReglesOptionelles.isUsing("appliquer-fatigue") && this.system.sante.fatigue) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && this.system.sante.fatigue) {
state += RdDUtility.currentFatigueMalus(this.system.sante.fatigue.value, this.system.sante.endurance.max);
}
// Ajout de l'éthylisme
@@ -1362,21 +1362,46 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async finDeRound(options = { terminer: false }) {
await this.$finDeRoundSuppressionEffetsTermines(options);
await this.$finDeRoundBlessuresGraves();
await this.$finDeRoundSupprimerObsoletes();
await this.$finDeRoundEmpoignade();
}
async $finDeRoundSuppressionEffetsTermines(options) {
for (let effect of this.getEffects()) {
if (effect.duration.type !== 'none' && (effect.duration.remaining <= 0 || options.terminer)) {
await effect.delete();
ChatMessage.create({ content: `${this.name} n'est plus ${Misc.lowerFirst(game.i18n.localize(effect.system.label))} !` });
}
}
}
async $finDeRoundBlessuresGraves() {
if (this.isPersonnage() || this.isCreature()) {
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length
const nbGraves = this.filterItems(it => it.isGrave(), 'blessure').length;
if (nbGraves > 0) {
// Gestion blessure graves : -1 pt endurance par blessure grave
await this.santeIncDec("endurance", - nbGraves);
await this.santeIncDec("endurance", -nbGraves);
}
}
}
async $finDeRoundSupprimerObsoletes() {
const obsoletes = []
.concat(this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp <= 0))
.concat(this.itemTypes[TYPES.possession].filter(it => it.system.compteur < -2 || it.system.compteur > 2))
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', obsoletes);
}
async $finDeRoundEmpoignade() {
const immobilisations = this.itemTypes[TYPES.empoignade].filter(it => it.system.pointsemp >= 2 && it.system.empoigneurid == this.id);
immobilisations.forEach(emp => RdDEmpoignade.onImmobilisation(this,
game.actors.get(emp.system.empoigneid),
emp
))
}
/* -------------------------------------------- */
async setSonne(sonne = true) {
if (this.isEntite()) {
@@ -1480,7 +1505,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async santeIncDec(name, inc, isCritique = false) {
if (name == 'fatigue' && !ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (name == 'fatigue' && !ReglesOptionnelles.isUsing("appliquer-fatigue")) {
return;
}
const sante = duplicate(this.system.sante)
@@ -1522,7 +1547,7 @@ export class RdDActor extends RdDBaseActor {
}
compteur.value = result.newValue;
// If endurance lost, then the same amount of fatigue cannot be recovered
if (ReglesOptionelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && sante.fatigue && fatigue > 0) {
sante.fatigue.value = Math.max(sante.fatigue.value + fatigue, this._computeFatigueMin());
}
await this.update({ "system.sante": sante })
@@ -2424,7 +2449,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async _tacheResult(rollData, options) {
// Mise à jour de la tache
rollData.appliquerFatigue = ReglesOptionelles.isUsing("appliquer-fatigue");
rollData.appliquerFatigue = ReglesOptionnelles.isUsing("appliquer-fatigue");
rollData.tache = duplicate(rollData.tache);
rollData.tache.system.points_de_tache_courant += rollData.rolled.ptTache;
if (rollData.rolled.isETotal) {
@@ -3205,7 +3230,7 @@ export class RdDActor extends RdDBaseActor {
return;
}
const attackerId = attacker?.id;
if (ReglesOptionelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
if (ReglesOptionnelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
actorId: this.id,
method: 'appliquerEncaissement',
@@ -3218,7 +3243,7 @@ export class RdDActor extends RdDBaseActor {
async appliquerEncaissement(rollData, show, attackerId) {
const armure = await this.computeArmure(rollData);
if (ReglesOptionelles.isUsing('validation-encaissement-gr')) {
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
DialogValidationEncaissement.validerEncaissement(this, rollData, armure, show, attackerId, (encaissement, show, attackerId) => this._appliquerEncaissement(encaissement, show, attackerId));
}
else {
@@ -3805,5 +3830,39 @@ export class RdDActor extends RdDBaseActor {
content: `${this.name} a reçu un/une ${item.type}: ${item.name}, qui ${manualMessage ? "n'est pas" : "est"} géré(e) automatiquement. ${manualMessage ? manualMessage : ''}`
});
}
async nouvelleIncarnation() {
let incarnation = this.toObject();
incarnation.items = Array.from(this.items.filter(it => it.type == TYPES.competence),
it => {
it = it.toObject();
it.id = undefined;
it.system.niveau = it.system.base;
it.system.niveau_archetype = Math.max(it.system.niveau + (it.system.xp > 0 ? 1 : 0), it.system.niveau_archetype);
it.system.xp = 0;
it.system.xp_sort = 0;
it.system.default_diffLibre = 0;
return it;
});
incarnation.name = 'Réincarnation de ' + incarnation.name
incarnation.system = {
carac: duplicate(this.system.carac),
heure: RdDTimestamp.defHeure(await RdDDice.rollTotal("1dh", { rollMode: "selfroll", showDice: SHOW_DICE })).key,
age: 18,
biographie: '',
notes: '',
experiencelog: [],
'compteurs.experience.value': 3000,
'reve.seuil.value': this.system.carac.reve.value,
'reve.reve.value': this.system.carac.reve.value,
subacteurs: { suivants: [], montures: [], vehicules: [] },
}
incarnation = await RdDBaseActor.create(incarnation);
await incarnation.deleteEmbeddedDocuments('ActiveEffect', incarnation.getEmbeddedCollection("ActiveEffect").map(it => it.id));
await incarnation.remiseANeuf();
incarnation.sheet.render(true);
}
}

View File

@@ -144,6 +144,18 @@ export class RdDBaseActor extends Actor {
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
async creerObjetParMJ(object){
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
actorId: this.id,
method: 'creerObjetParMJ',
args: [object]
});
return;
}
await this.createEmbeddedDocuments('Item', [object])
}
/* -------------------------------------------- */
getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']);

View File

@@ -78,11 +78,7 @@ export class ChatUtility {
/* -------------------------------------------- */
static async createChatWithRollMode(name, chatOptions) {
return await ChatUtility.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions);
}
/* -------------------------------------------- */
static async createChatMessage(name, rollMode, chatOptions) {
let rollMode = game.settings.get("core", "rollMode")
switch (rollMode) {
case "blindroll": // GM only
if (!game.user.isGM) {

View File

@@ -8,18 +8,18 @@ const xp_par_niveau = [5, 5, 5, 10, 10, 10, 10, 15, 15, 15, 15, 20, 20, 20, 20,
const niveau_max = xp_par_niveau.length - 10;
/* -------------------------------------------- */
const limitesArchetypes = [
{ "niveau": 0, "nombreMax": 100, "reste": 100 },
{ "niveau": 1, "nombreMax": 10, "reste": 10 },
{ "niveau": 2, "nombreMax": 9, "reste": 9 },
{ "niveau": 3, "nombreMax": 8, "reste": 8 },
{ "niveau": 4, "nombreMax": 7, "reste": 7 },
{ "niveau": 5, "nombreMax": 6, "reste": 6 },
{ "niveau": 6, "nombreMax": 5, "reste": 5 },
{ "niveau": 7, "nombreMax": 4, "reste": 4 },
{ "niveau": 8, "nombreMax": 3, "reste": 3 },
{ "niveau": 9, "nombreMax": 2, "reste": 2 },
{ "niveau": 10, "nombreMax": 1, "reste": 1 },
{ "niveau": 11, "nombreMax": 1, "reste": 1 }
{ niveau: 0, nombreMax: 100 },
{ niveau: 1, nombreMax: 10 },
{ niveau: 2, nombreMax: 9 },
{ niveau: 3, nombreMax: 8 },
{ niveau: 4, nombreMax: 7 },
{ niveau: 5, nombreMax: 6 },
{ niveau: 6, nombreMax: 5 },
{ niveau: 7, nombreMax: 4 },
{ niveau: 8, nombreMax: 3 },
{ niveau: 9, nombreMax: 2 },
{ niveau: 10, nombreMax: 1 },
{ niveau: 11, nombreMax: 1 },
];
/* -------------------------------------------- */
@@ -259,13 +259,17 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const computed = duplicate(limitesArchetypes);
computed.forEach(it => { it.nombre = 0; it.reste = it.nombreMax; });
competences.map(it => Math.max(0, it.system.niveau_archetype))
.filter(n => n > 0)
.forEach(n => {
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0 };
computed[n].reste = computed[n].reste - 1;
computed[n] = computed[n] ?? { niveau: n, nombreMax: 0, reste: 0, nombre: 0 };
computed[n].reste--;
computed[n].nombre++;
});
return computed.filter(it => it.reste > 0 && it.niveau > 0);
return computed.filter(it => it.niveau > 0);
}
/* -------------------------------------------- */

View File

@@ -5,7 +5,7 @@ import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { SYSTEM_RDD } from "./constants.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
@@ -157,7 +157,7 @@ export class RdDItemSheet extends ItemSheet {
super.activateListeners(html);
this.html = html;
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs')
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionnelles.isUsing('afficher-prix-joueurs')
|| game.user.isGM
|| !this.item.isOwned);
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique());

View File

@@ -1,6 +1,6 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { ReglesOptionelles } from "../settings/regles-optionelles.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
export class RdDItemArmure extends RdDItem {
@@ -9,7 +9,7 @@ export class RdDItemArmure extends RdDItem {
}
deteriorerArmure(dmg) {
if (!ReglesOptionelles.isUsing('deteriorationArmure') || this.system.protection == '0') {
if (!ReglesOptionnelles.isUsing('deteriorationArmure') || this.system.protection == '0') {
return;
}
let deterioration = (this.system.deterioration ?? 0) + dmg;

View File

@@ -42,7 +42,7 @@ export class Misc {
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`${type.toUpperCase()}.Type${Misc.upperFirst(subType)}`)
return subType ? game.i18n.localize(`TYPES.${type}.${Misc.upperFirst(subType)}`)
: '';
}

View File

@@ -9,7 +9,7 @@ import { RdDBonus } from "./rdd-bonus.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDRoll } from "./rdd-roll.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { STATUSES } from "./settings/status-effects.js";
import { Targets } from "./targets.js";
import { RdDEmpoignade } from "./rdd-empoignade.js";
@@ -1048,7 +1048,7 @@ export class RdDCombat {
competence: this.defender.getCompetence(competenceParade).clone(),
arme: armeParade,
surprise: this.defender.getSurprise(true),
needParadeSignificative: ReglesOptionelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
needParadeSignificative: ReglesOptionnelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
needResist: RdDItemArme.needArmeResist(attackerRoll.arme, armeParade),
carac: this.defender.system.carac,
show: {}
@@ -1166,7 +1166,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async computeDeteriorationArme(defenderRoll) {
if (!ReglesOptionelles.isUsing('resistanceArmeParade')) {
if (!ReglesOptionnelles.isUsing('resistanceArmeParade')) {
return;
}
const attackerRoll = defenderRoll.attackerRoll;
@@ -1215,7 +1215,7 @@ export class RdDCombat {
}
}
// Si l'arme de parade n'est pas un bouclier, jet de désarmement (p.132)
if (ReglesOptionelles.isUsing('defenseurDesarme') && resistance > 0 && RdDItemArme.getCategorieParade(defenderRoll.arme) != 'boucliers') {
if (ReglesOptionnelles.isUsing('defenseurDesarme') && resistance > 0 && RdDItemArme.getCategorieParade(defenderRoll.arme) != 'boucliers') {
let desarme = await RdDResolutionTable.rollData({
caracValue: this.defender.getForce(),
finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg,
@@ -1230,7 +1230,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async computeRecul(defenderRoll) { // Calcul du recul (p. 132)
const attackerRoll = defenderRoll.attackerRoll;
if (ReglesOptionelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) {
if (ReglesOptionnelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) {
const impact = this._computeImpactRecul(attackerRoll);
const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact });
if (rollRecul.rolled.isSuccess) {

View File

@@ -1,10 +1,10 @@
import { Grammar } from "./grammar.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
export class RdDConfirm {
/* -------------------------------------------- */
static confirmer(options, autresActions) {
options.bypass = options.bypass || !(options.settingConfirmer == undefined || ReglesOptionelles.isUsing(options.settingConfirmer));
options.bypass = options.bypass || !(options.settingConfirmer == undefined || ReglesOptionnelles.isUsing(options.settingConfirmer));
if (options.bypass) {
options.onAction();
}
@@ -47,7 +47,7 @@ export class RdDConfirm {
icon: '<i class="fas fa-user-check"></i>',
label: options.buttonLabel + "<br>et ne plus demander",
callback: () => {
ReglesOptionelles.set(options.settingConfirmer, false);
ReglesOptionnelles.set(options.settingConfirmer, false);
options.onAction();
}
}

View File

@@ -4,6 +4,8 @@ import { RdDRoll } from "./rdd-roll.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { ChatUtility } from "./chat-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { TYPES } from "./item.js";
/* -------------------------------------------- */
@@ -14,6 +16,47 @@ export class RdDEmpoignade {
static init() {
}
/* -------------------------------------------- */
static registerChatCallbacks(html) {
html.on("click", '.defense-empoignade-cac', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee")
});
html.on("click", '.defense-empoignade-esquive', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee")
});
html.on("click", '.empoignade-poursuivre', event => {
let attackerId = event.currentTarget.attributes['data-attackerId'].value
let defenderId = event.currentTarget.attributes['data-defenderId'].value
RdDEmpoignade.onAttaqueEmpoignadeValidee(game.actors.get(attackerId), game.actors.get(defenderId))
});
html.on("click", '.empoignade-entrainer-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.entrainerAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("click", '.empoignade-projeter-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
RdDEmpoignade.projeterAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("change", '.empoignade-perte-endurance', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = RdDEmpoignade.$readRollEmpoignade(chatMessage);
if (event.currentTarget.value && event.currentTarget.value != "none") {
RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
ChatUtility.removeChatMessageId(chatMessage.id)
}
});
}
/* -------------------------------------------- */
static checkEmpoignadeEnCours(actor) {
// TODO: autoriser la perception? la comédie/séduction?
@@ -24,74 +67,92 @@ export class RdDEmpoignade {
return false;
}
/* -------------------------------------------- */
static $storeRollEmpoignade(msg, rollData) {
RdDEmpoignade.$reduceActorToIds(rollData);
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData);
}
static $reduceActorToIds(rollData) {
rollData.attacker = { id: rollData.attacker.id };
rollData.defender = { id: rollData.defender.id };
}
/* -------------------------------------------- */
static $readRollEmpoignade(msg) {
const rollData = ChatUtility.getMessageData(msg, 'empoignade-roll-data');
RdDEmpoignade.$replaceIdsWithActors(rollData);
return rollData
}
static $replaceIdsWithActors(rollData) {
rollData.attacker = game.actors.get(rollData.attacker.id);
rollData.defender = game.actors.get(rollData.defender.id);
}
/* -------------------------------------------- */
static isEmpoignadeEnCours(actor) {
return actor.itemTypes['empoignade'].find(it => it.system.pointsemp > 0)
return actor.itemTypes[TYPES.empoignade].find(it => it.system.pointsemp > 0)
}
/* -------------------------------------------- */
static getEmpoignadeById(actor, id) {
let emp = actor.itemTypes['empoignade'].find(it => it.system.empoignadeid == id)
let emp = actor.itemTypes[TYPES.empoignade].find(it => it.system.empoignadeid == id)
return emp && duplicate(emp) || undefined;
}
/* -------------------------------------------- */
static getEmpoignade(attacker, defender) {
let emp = attacker.itemTypes['empoignade'].find(it => it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id)
if (!emp) {
emp = attacker.itemTypes['empoignade'].find(it => it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
}
let emp = attacker.itemTypes[TYPES.empoignade].find(it =>
(it.system.empoigneurid == attacker.id && it.system.empoigneid == defender.id) ||
(it.system.empoigneurid == defender.id && it.system.empoigneid == attacker.id)
)
if (emp) {
return duplicate(emp);
}
return undefined;
}
/* -------------------------------------------- */
static getMalusTaille(emp, attacker, defender) {
// Si pas empoigné, alors 0
if (emp.system.pointsemp == 0) {
return 0
}
// Malus de -1 si différence de taille de 2 ou plus (p 135)
if (attacker.system.carac.taille.value < defender.system.carac.taille.value - 1) {
return attacker.system.carac.taille.value - (defender.system.carac.taille.value - 1)
// p135: Malus de -1 par point de taille de différence de taille au delà de 1 (donc -2 pour une différence de 3, ...)
const diffTaille = attacker.system.carac.taille.value - defender.system.carac.taille.value;
const diffTailleAbs = Math.abs(diffTaille)
const signDiff = diffTaille > 0 ? 1 : -1
if (diffTailleAbs > 2) {
return signDiff * (diffTailleAbs - 1)
}
return 0
}
/* -------------------------------------------- */
static async onAttaqueEmpoignadeValidee(attacker, defender) {
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
static isActionAutorisee(mode, attacker, defender) {
const acting = RdDEmpoignade.isActionDefenseur(mode) ? defender : attacker;
if (acting.getUserLevel(game.user) < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) {
ui.notifications.warn(`Vous n'êtes pas autorisé à choisir l'action de ${acting.name}`)
return false;
}
return true
}
let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"
let rollData = {
mode: mode,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone(),
selectedCarac: attacker.system.carac.melee,
empoignade: empoignade,
attackerId: attacker.id,
attackerName: attacker.name,
defenderName: defender.name,
defenderId: defender.id,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
if (empoignade.system.pointsemp >= 2) {
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-actions.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
} else {
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle);
static isActionDefenseur(mode) {
switch (mode) {
case "liberer":
case "contrer-empoigner":
return true;
}
return false;
}
/* -------------------------------------------- */
static async onAttaqueEmpoignade(attacker, defender) {
if (!RdDEmpoignade.isActionAutorisee("empoigner", attacker, defender)) {
return
}
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
@@ -105,6 +166,38 @@ export class RdDEmpoignade {
}
}
/* -------------------------------------------- */
static async onAttaqueEmpoignadeValidee(attacker, defender) {
let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
const isNouvelle = empoignade == undefined;
empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender))
let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer"
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
return
}
let rollData = {
mode, empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone(),
selectedCarac: attacker.system.carac.melee,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
if (attacker.isCreatureEntite()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
if (empoignade.system.pointsemp >= 2) {
if (!empoignade.system.ausol) {
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
} else {
await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle);
}
}
/* -------------------------------------------- */
static async onAttaqueEmpoignadeFromItem(empoignade) {
let attacker = game.actors.get(empoignade.system.empoigneurid)
@@ -112,6 +205,20 @@ export class RdDEmpoignade {
await this.onAttaqueEmpoignadeValidee(attacker, defender)
}
static async onImmobilisation(attacker, defender, empoignade) {
const rollData = {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone()
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name),
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-immobilise.html`, rollData)
})
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async $rollAttaqueEmpoignade(attacker, rollData, isNouvelle = false) {
const dialog = await RdDRoll.create(attacker, rollData,
@@ -119,35 +226,40 @@ export class RdDEmpoignade {
{
name: 'jet-empoignade',
label: 'Empoigner',
callbacks: [
{ condition: r => (r.rolled.isSuccess), action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, true, isNouvelle) },
{ condition: r => (r.rolled.isEchec), action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, false, isNouvelle) },
]
callbacks: [{ action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, isNouvelle) },]
});
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollEmpoignade(rollData, isSuccess, isNouvelle = false) {
let attacker = game.actors.get(rollData.attackerId)
let defender = game.actors.get(rollData.defenderId)
static async $onRollEmpoignade(rollData, isNouvelle = false) {
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
let empoignade = rollData.empoignade
empoignade.isSuccess = isSuccess;
if (isSuccess && isNouvelle) {
if (rollData.rolled.isSuccess && isNouvelle) {
const objectEmpoignade = rollData.empoignade.toObject();
// Creer l'empoignade sur attaquant/defenseur
await attacker.createEmbeddedDocuments('Item', [empoignade.toObject()])
await defender.createEmbeddedDocuments('Item', [empoignade.toObject()])
attacker.creerObjetParMJ(objectEmpoignade);
defender.creerObjetParMJ(objectEmpoignade);
}
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
if (rollData.rolled.isPart) {
rollData.particuliere = "finesse";
}
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-resultat.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async onDefenseEmpoignade(rollData, defenseMode, competenceName = "Corps à corps", carac = "melee") {
let attacker = game.actors.get(rollData.attackerId)
let defender = game.actors.get(rollData.defenderId)
static async onDefenseEmpoignade(attackerRoll, mode, competenceName = "Corps à corps", carac = "melee") {
let attacker = game.actors.get(attackerRoll.attacker.id)
let defender = game.actors.get(attackerRoll.defender.id)
if (!RdDEmpoignade.isActionAutorisee(mode, attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
if (!empoignade) {
@@ -156,18 +268,23 @@ export class RdDEmpoignade {
}
empoignade = duplicate(empoignade)
rollData.mode = defenseMode
rollData.empoignade = empoignade
rollData.competence = defender.getCompetence(competenceName),
rollData.selectedCarac = defender.system.carac[carac],
rollData.malusTaille = RdDEmpoignade.getMalusTaille(empoignade, defender, attacker)
await RdDEmpoignade.$rollDefenseEmpoignade(defender, rollData);
let defenderRoll = {
mode, attacker, defender, empoignade, attackerRoll,
diffLibre: attackerRoll.diffLibre,
attaqueParticuliere: attackerRoll.particuliere,
competence: defender.getCompetence(competenceName).clone(),
surprise: defender.getSurprise(true),
carac: defender.system.carac,
selectedCarac: defender.system.carac[carac],
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, defender, attacker),
show: {}
};
await RdDEmpoignade.$rollDefenseEmpoignade(defender, defenderRoll);
}
/* -------------------------------------------- */
static async $rollDefenseEmpoignade(defender, rollData) {
const dialog = await RdDRoll.create(defender, rollData,
static async $rollDefenseEmpoignade(defender, defenderRoll) {
const dialog = await RdDRoll.create(defender, defenderRoll,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.html' },
{
name: 'empoignade',
@@ -183,7 +300,7 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async $onRollContrerLiberer(rollData) {
let empoignade = rollData.empoignade
if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) {
empoignade.system.pointsemp++
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
@@ -192,13 +309,12 @@ export class RdDEmpoignade {
empoignade.system.pointsemp--
RdDEmpoignade.$updateEtatEmpoignade(empoignade)
}
if (empoignade.system.pointsemp >= 2) {
let attacker = game.actors.get(empoignade.system.empoigneurid)
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-actions.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
}
await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html')
if (empoignade.system.pointsemp >= 2) {
let msg = await RdDResolutionTable.displayRollData(rollData, rollData.attacker, 'chat-empoignade-entrainer.html');
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
}
/* -------------------------------------------- */
@@ -223,16 +339,15 @@ export class RdDEmpoignade {
let defender = game.actors.get(empoignade.system.empoigneid)
let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid)
await defender.deleteEmbeddedDocuments('Item', [emp._id])
//let attacker = game.actors.get(empoignade.system.empoigneurid)
//emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid)
//await attacker.deleteEmbeddedDocuments('Item', [emp._id])
}
/* -------------------------------------------- */
static async entrainerAuSol(rollData) {
let attacker = game.actors.get(rollData.attackerId)
let defender = game.actors.get(rollData.defenderId)
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
empoignade.system.ausol = true
@@ -242,25 +357,32 @@ export class RdDEmpoignade {
await defender.setEffect(STATUSES.StatusProne, true);
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async projeterAuSol(rollData) {
let attacker = game.actors.get(rollData.attackerId)
let defender = game.actors.get(rollData.defenderId)
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
await defender.setEffect(STATUSES.StatusProne, true);
await this.$deleteEmpoignade(empoignade)
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */
static async perteEndurance(rollData, perteMode) {
let attacker = game.actors.get(rollData.attackerId)
let defender = game.actors.get(rollData.defenderId)
let attacker = game.actors.get(rollData.attacker.id)
let defender = game.actors.get(rollData.defender.id)
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
let empoignade = this.getEmpoignade(attacker, defender)
//console.log("Perte d'endurance :!!!", perteMode)
@@ -278,7 +400,7 @@ export class RdDEmpoignade {
await defender.santeIncDec("endurance", -(3 * Math.floor(endValue / 4)));
}
let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html');
ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData)
RdDEmpoignade.$storeRollEmpoignade(msg, rollData);
}
/* -------------------------------------------- */

View File

@@ -15,7 +15,7 @@ import { RdDCombatManager, RdDCombat } from "./rdd-combat.js";
import { ChatUtility } from "./chat-utility.js";
import { StatusEffects } from "./settings/status-effects.js";
import { RdDCompendiumOrganiser } from "./rdd-compendium-organiser.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDHotbar } from "./rdd-hotbar-drop.js"
import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { RdDHerbes } from "./rdd-herbes.js";
@@ -186,7 +186,7 @@ export class SystemReveDeDragon {
RdDCalendrier.init();
SystemCompendiums.init();
DialogChronologie.init();
ReglesOptionelles.init();
ReglesOptionnelles.init();
RdDUtility.init();
RdDDice.init();
RdDCommands.init();

View File

@@ -1,7 +1,7 @@
import { ChatUtility } from "./chat-utility.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
/**
* difficultés au delà de -10
@@ -91,13 +91,17 @@ export class RdDResolutionTable {
/* -------------------------------------------- */
static async displayRollData(rollData, actor = undefined, template = 'chat-resultat-general.html') {
return await ChatUtility.createChatWithRollMode(actor?.userName ?? game.user.name, {
content: await RdDResolutionTable.buildRollDataHtml(rollData, actor, template)
return await ChatUtility.createChatWithRollMode(RdDResolutionTable.actorChatName(actor), {
content: await RdDResolutionTable.buildRollDataHtml(rollData, template)
});
}
static actorChatName(actor) {
return actor?.userName ?? game.user.name;
}
/* -------------------------------------------- */
static async buildRollDataHtml(rollData, actor, template = 'chat-resultat-general.html') {
static async buildRollDataHtml(rollData, template = 'chat-resultat-general.html') {
rollData.show = rollData.show || {};
return await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/${template}`, rollData);
}
@@ -122,7 +126,7 @@ export class RdDResolutionTable {
rolled.bonus = rollData.bonus;
rolled.factorHtml = Misc.getFractionHtml(rollData.diviseurSignificative);
if (ReglesOptionelles.isUsing("afficher-colonnes-reussite")) {
if (ReglesOptionnelles.isUsing("afficher-colonnes-reussite")) {
rolled.niveauNecessaire = this.findNiveauNecessaire(caracValue, rolled.roll);
rolled.ajustementNecessaire = rolled.niveauNecessaire - finalLevel;
}

View File

@@ -6,7 +6,7 @@ import { Misc } from "./misc.js";
import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
/**
* Extend the base Dialog entity to select roll parameters
@@ -85,7 +85,7 @@ export class RdDRoll extends Dialog {
if (RdDBonus.isDefenseAttaqueFinesse(rollData)) {
facteurSign *= 2;
}
if (!ReglesOptionelles.isUsing('tripleSignificative')) {
if (!ReglesOptionnelles.isUsing('tripleSignificative')) {
facteurSign = Math.min(facteurSign, 4);
}
return facteurSign;

View File

@@ -12,7 +12,7 @@ import { EffetsDraconiques } from "./tmr/effets-draconiques.js";
import { PixiTMR } from "./tmr/pixi-tmr.js";
import { Draconique } from "./tmr/draconique.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDDice } from "./rdd-dice.js";
import { STATUSES } from "./settings/status-effects.js";
import { RdDRencontre } from "./item/rencontre.js";
@@ -55,7 +55,7 @@ export class RdDTMRDialog extends Dialog {
this.actor = actor;
this.actor.tmrApp = this; // reference this app in the actor structure
this.viewOnly = tmrData.mode == "visu"
this.fatigueParCase = this.viewOnly || !ReglesOptionelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue();
this.fatigueParCase = this.viewOnly || !ReglesOptionnelles.isUsing("appliquer-fatigue") ? 0 : this.actor.getTMRFatigue();
this.cumulFatigue = 0;
this.loadRencontres();
this.loadCasesSpeciales();
@@ -207,7 +207,7 @@ export class RdDTMRDialog extends Dialog {
return;
}
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
// Roll Sort
@@ -227,7 +227,7 @@ export class RdDTMRDialog extends Dialog {
// Gestion du cout de montée en points de rêve
let reveCout = ((this.tmrdata.isRapide && !EffetsDraconiques.isDeplacementAccelere(this.actor)) ? -2 : -1) - this.actor.countMonteeLaborieuse();
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
await this.actor.reveActuelIncDec(reveCout);
@@ -263,7 +263,7 @@ export class RdDTMRDialog extends Dialog {
let refoulement = document.getElementById("tmr-refoulement-value");
refoulement.innerHTML = this.actor.system.reve.refoulement.value;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
let fatigueItem = document.getElementById("tmr-fatigue-table");
fatigueItem.innerHTML = "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(this.actor.system.sante.fatigue.value, this.actor.system.sante.endurance.max).html() + "</table>";
}
@@ -367,7 +367,7 @@ export class RdDTMRDialog extends Dialog {
return true;
}
const resteAvantInconscience = this.actor.getFatigueMax() - this.actor.getFatigueActuelle() - this.cumulFatigue;
if (ReglesOptionelles.isUsing("appliquer-fatigue") && resteAvantInconscience <= 0) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue") && resteAvantInconscience <= 0) {
this._tellToGM("Vous vous écroulez de fatigue : vous quittez les Terres médianes !");
this.quitterLesTMRInconscient();
return true;
@@ -451,7 +451,7 @@ export class RdDTMRDialog extends Dialog {
setTimeout(() => {
// TODO: remplacer par une boucle while(this.currentRencontre) ?
rencData.nbRounds++;
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
this._tentativeMaitrise(rencData);
@@ -1004,7 +1004,7 @@ export class RdDTMRDialog extends Dialog {
await this.actor.updateCoordTMR(tmr.coord);
this.forceDemiRevePositionView();
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
this.cumulFatigue += this.fatigueParCase;
}
this.updateValuesDisplay();

View File

@@ -5,7 +5,7 @@ import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js";
import { TMRUtility } from "./tmr-utility.js";
import { DialogItemAchat } from "./dialog-item-achat.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDItem } from "./item.js";
import { RdDPossession } from "./rdd-possession.js";
@@ -121,6 +121,7 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/actor/header-compteurs-entitee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-effects.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/header-hautreve.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/archetype.hbs',
'systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-main.html',
'systems/foundryvtt-reve-de-dragon/templates/actor/carac-derivee.html',
@@ -278,7 +279,7 @@ export class RdDUtility {
Handlebars.registerHelper('timestamp-formulesPeriode', () => RdDTimestamp.formulesPeriode());
Handlebars.registerHelper('min', (...args) => Math.min(...args.slice(0, -1)));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionelles.isUsing(option));
Handlebars.registerHelper('regle-optionnelle', (option) => ReglesOptionnelles.isUsing(option));
Handlebars.registerHelper('trier', list => list.sort((a, b) => a.name.localeCompare(b.name)));
Handlebars.registerHelper('filtreTriCompetences', competences => RdDItemCompetence.triVisible(competences));
Handlebars.registerHelper('linkCompendium', (pack, id, name) => RdDUtility.linkCompendium(pack, id, name));
@@ -478,7 +479,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static calculFatigueHtml(fatigue, endurance) {
return ReglesOptionelles.isUsing("appliquer-fatigue") ? {
return ReglesOptionnelles.isUsing("appliquer-fatigue") ? {
malus: RdDUtility.calculMalusFatigue(fatigue, endurance),
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix(fatigue, endurance).html() + "</table>"
} : { malus: 0, html: '' };
@@ -554,14 +555,14 @@ export class RdDUtility {
let formula = "2d10";
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre')) {
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "min" + valeurMin;
}
}
// Chaque dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-ajout-malus-libre')) {
if (ReglesOptionnelles.isUsing('degat-ajout-malus-libre')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
formula += "+" + valeurMin;
@@ -571,7 +572,7 @@ export class RdDUtility {
let roll = await RdDDice.roll(formula, options);
// 1 dé fait au minmum la difficulté libre
if (ReglesOptionelles.isUsing('degat-minimum-malus-libre-simple')) {
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre-simple')) {
if (rollData.diffLibre < 0) {
let valeurMin = Math.abs(rollData.diffLibre);
if (roll.terms[0].results[0].result < valeurMin) {
@@ -624,7 +625,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static currentFatigueMalus(value, max) {
if (ReglesOptionelles.isUsing("appliquer-fatigue")) {
if (ReglesOptionnelles.isUsing("appliquer-fatigue")) {
max = Math.max(1, Math.min(max, 60));
value = Math.min(max * 2, Math.max(0, value));
@@ -668,6 +669,7 @@ export class RdDUtility {
/* -------------------------------------------- */
static async chatListeners(html) {
RdDCombat.registerChatCallbacks(html);
RdDEmpoignade.registerChatCallbacks(html);
// Gestion spécifique message passeurs
html.on("click", '.tmr-passeur-coord a', event => {
@@ -694,44 +696,6 @@ export class RdDUtility {
RdDPossession.onDefensePossession(attackerId, defenderId, possessionId)
});
html.on("click", '.defense-empoignade-cac', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee")
});
html.on("click", '.defense-empoignade-esquive', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee")
});
html.on("click", '.empoignade-poursuivre', event => {
let attackerId = event.currentTarget.attributes['data-attackerId'].value
let defenderId = event.currentTarget.attributes['data-defenderId'].value
RdDEmpoignade.onAttaqueEmpoignadeValidee(game.actors.get(attackerId), game.actors.get(defenderId))
});
html.on("click", '.empoignade-entrainer-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
RdDEmpoignade.entrainerAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("click", '.empoignade-projeter-sol', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
RdDEmpoignade.projeterAuSol(rollData)
ChatUtility.removeChatMessageId(chatMessage.id)
});
html.on("change", '.empoignade-perte-endurance', event => {
const chatMessage = ChatUtility.getChatMessage(event);
const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data');
if (event.currentTarget.value && event.currentTarget.value != "none") {
RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value)
ChatUtility.removeChatMessageId(chatMessage.id)
}
});
// gestion bouton tchat Acheter
html.on("click", '.button-acheter', event => {
const venteData = DialogItemAchat.preparerAchat(event.currentTarget);

View File

@@ -7,7 +7,7 @@ import { RdDBonus } from "./rdd-bonus.js";
import { RdDCarac } from "./rdd-carac.js";
import { RdDPossession } from "./rdd-possession.js";
import { RdDUtility } from "./rdd-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
/**
* tous les ajustements pouvant s'appliquer.
@@ -86,7 +86,7 @@ export const referenceAjustements = {
getValue: (rollData, actor) => actor.getMoralTotal()
},
astrologique: {
isUsed: (rollData, actor) => ReglesOptionelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
isUsed: (rollData, actor) => ReglesOptionnelles.isUsing("astrologie") && RdDBonus.isAjustementAstrologique(rollData),
getLabel: (rollData, actor) => 'Astrologique',
getValue: (rollData, actor) => actor.ajustementAstrologique()
},

View File

@@ -1,119 +0,0 @@
import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
const listeReglesOptionelles = [
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },
{ group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" },
{ group: 'Règles de combat', name: 'categorieParade', descr: "Le défenseur doit obtenir une significative en cas de parade avec des armes de catégories différentes" },
{ group: 'Règles de combat', name: 'tripleSignificative', descr: "En cas de demi-surprise, d'attaque particulière en finesse, et de catégories d'armes différentes, le défenseur doit obtenir 1/8 des chances de succès" },
{ group: 'Règles de combat', name: 'degat-minimum-malus-libre-simple', descr: "Le malus libre d'attaque remplace une des valeurs de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, si le plus petit dé est inférieur à 4, alors il devient 4.", default: false },
{ group: 'Règles de combat', name: 'degat-minimum-malus-libre', descr: "Le malus libre d'attaque remplace une valeur de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, tout résultat inférieur à 4 devient 4.", default: false },
{ group: 'Règles de combat', name: 'degat-ajout-malus-libre', descr: "Le malus libre d'attaque s'ajoute au jet d'encaissement et aux autres bonus. Exemple : la difficulté libre de l'attaquant est de -4. Le jet d'encaissement est effectué à 2d10+4, plus les bonus de situation et d'armes.", default: false },
{ group: 'Règles de combat', name: 'validation-encaissement-gr', descr: "Le Gardien des Rêves doit valider les jets d'encaissement et peut les changer.", default: false },
{ group: 'Règles générales', name: 'astrologie', descr: "Appliquer les ajustements astrologiques aux jets de chance et aux rituels"},
{ group: 'Règles générales', name: 'afficher-prix-joueurs', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"},
{ group: 'Règles générales', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ group: 'Règles générales', name: 'chateau-dormant-gardien', descr: "Saisie des heures de sommeil/jets de moral par le gardien des rêves", default: true },
{ group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-lien-acteur', descr: "Confirmer pour détacher un animal/suivant/véhicule", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-equipement', descr: "Confirmer la suppression des équipements", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-oeuvre', descr: "Confirmer la suppression des oeuvres", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-connaissance', descr: "Confirmer la suppression des connaissances", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-draconique', descr: "Confirmer la suppression des queues, souffles, têtes", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-effet', descr: "Confirmer la suppression des effets", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-competence', descr: "Confirmer la suppression des compétences", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-autres', descr: "Confirmer la suppression des autres types d'Objets", scope: "client"},
];
const uniquementJoueur = listeReglesOptionelles.filter(it => it.uniquementJoueur).map(it=>it.name);
export class ReglesOptionelles extends FormApplication {
static init() {
for (const regle of listeReglesOptionelles) {
const name = regle.name;
const id = ReglesOptionelles._getIdRegle(name);
game.settings.register(SYSTEM_RDD, id, { name: id, scope: regle.scope ?? "world", config: false, default: regle.default == undefined ? true : regle.default, type: Boolean });
}
game.settings.registerMenu(SYSTEM_RDD, "rdd-options-regles", {
name: "Choisir les règles optionelles",
label: "Règles optionelles",
hint: "Ouvre la fenêtre de sélection des règles optionelles",
icon: "fas fa-bars",
type: ReglesOptionelles
});
}
constructor(...args) {
super(...args);
}
static _getIdRegle(name) {
return `rdd-option-${name}`;
}
static get defaultOptions() {
const options = super.defaultOptions;
mergeObject(options, {
id: "regles-optionelles",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/regles-optionelles.html",
height: 600,
width: 450,
minimizable: false,
closeOnSubmit: true,
title: "Règles optionnelles"
});
return options;
}
getData() {
let formData = super.getData();
const regles = listeReglesOptionelles.filter(it => game.user.isGM || it.scope == "client").map(it => {
it = duplicate(it);
it.id = ReglesOptionelles._getIdRegle(it.name);
it.active = ReglesOptionelles.isSet(it.name);
return it;
});
formData.regles = regles;
formData.groups = Misc.classify(regles, it => it.group);
return formData;
}
static isUsing(name) {
if (game.user.isGM && uniquementJoueur.includes(name)) {
return true;
}
return ReglesOptionelles.isSet(name);
}
static isSet(name) {
return game.settings.get(SYSTEM_RDD, ReglesOptionelles._getIdRegle(name));
}
static set(name, value) {
return game.settings.set(SYSTEM_RDD, ReglesOptionelles._getIdRegle(name), value ? true: false);
}
activateListeners(html) {
html.find(".select-option").click((event) => {
if (event.currentTarget.attributes.name) {
let id = event.currentTarget.attributes.name.value;
let isChecked = event.currentTarget.checked;
game.settings.set(SYSTEM_RDD, id, isChecked);
}
});
}
async _updateObject(event, formData) {
this.close();
}
}

View File

@@ -0,0 +1,122 @@
import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
const listeReglesOptionnelles = [
{ group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"},
{ group: 'Règles générales', name: 'astrologie', descr: "Appliquer les ajustements astrologiques aux jets de chance et aux rituels"},
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },
{ group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" },
{ group: 'Règles de combat', name: 'categorieParade', descr: "Le défenseur doit obtenir une significative en cas de parade avec des armes de catégories différentes" },
{ group: 'Règles de combat', name: 'tripleSignificative', descr: "En cas de demi-surprise, d'attaque particulière en finesse, et de catégories d'armes différentes, le défenseur doit obtenir 1/8 des chances de succès" },
{ group: 'Règles de combat', name: 'validation-encaissement-gr', descr: "Le Gardien des Rêves doit valider les jets d'encaissement et peut les changer.", default: false },
{ group: 'Automatisation', name: 'chateau-dormant-gardien', descr: "Saisie des heures de sommeil/jets de moral par le gardien des rêves", default: true },
{ group: 'Affichage', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ group: 'Affichage', name: 'afficher-prix-joueurs', descr: "Afficher le prix de l'équipement des joueurs", uniquementJoueur: true},
{ group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-lien-acteur', descr: "Confirmer pour détacher un animal/suivant/véhicule", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-equipement', descr: "Confirmer la suppression des équipements", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-oeuvre', descr: "Confirmer la suppression des oeuvres", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-connaissance', descr: "Confirmer la suppression des connaissances", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-draconique', descr: "Confirmer la suppression des queues, souffles, têtes", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-effet', descr: "Confirmer la suppression des effets", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-competence', descr: "Confirmer la suppression des compétences", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-supprimer-autres', descr: "Confirmer la suppression des autres types d'Objets", scope: "client"},
{ group: 'Options alternatives', name: 'degat-minimum-malus-libre-simple', descr: "Le malus libre d'attaque remplace une des valeurs de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, si le plus petit dé est inférieur à 4, alors il devient 4.", default: false },
{ group: 'Options alternatives', name: 'degat-minimum-malus-libre', descr: "Le malus libre d'attaque remplace une valeur de dés d'encaissement si elle est plus petite. Exemple : la difficulté libre de l'attaquant est de -4. Sur le jet d'encaissement, tout résultat inférieur à 4 devient 4.", default: false },
{ group: 'Options alternatives', name: 'degat-ajout-malus-libre', descr: "Le malus libre d'attaque s'ajoute au jet d'encaissement et aux autres bonus. Exemple : la difficulté libre de l'attaquant est de -4. Le jet d'encaissement est effectué à 2d10+4, plus les bonus de situation et d'armes.", default: false },
];
const uniquementJoueur = listeReglesOptionnelles.filter(it => it.uniquementJoueur).map(it=>it.name);
export class ReglesOptionnelles extends FormApplication {
static init() {
for (const regle of listeReglesOptionnelles) {
const name = regle.name;
const id = ReglesOptionnelles._getIdRegle(name);
game.settings.register(SYSTEM_RDD, id, { name: id, scope: regle.scope ?? "world", config: false, default: regle.default == undefined ? true : regle.default, type: Boolean });
}
game.settings.registerMenu(SYSTEM_RDD, "rdd-options-regles", {
name: "Choisir les règles optionnelles",
label: "Règles optionnelles",
hint: "Ouvre la fenêtre de sélection des règles optionnelles",
icon: "fas fa-bars",
type: ReglesOptionnelles
});
}
constructor(...args) {
super(...args);
}
static _getIdRegle(name) {
return `rdd-option-${name}`;
}
static get defaultOptions() {
const options = super.defaultOptions;
mergeObject(options, {
id: "regles-optionnelles",
template: "systems/foundryvtt-reve-de-dragon/templates/settings/regles-optionnelles.html",
height: 600,
width: 450,
minimizable: false,
closeOnSubmit: true,
title: "Règles optionnelles"
});
return options;
}
getData() {
let formData = super.getData();
const regles = listeReglesOptionnelles.filter(it => game.user.isGM || it.scope == "client").map(it => {
it = duplicate(it);
it.id = ReglesOptionnelles._getIdRegle(it.name);
it.active = ReglesOptionnelles.isSet(it.name);
return it;
});
formData.regles = regles;
formData.groups = Misc.classify(regles, it => it.group);
return formData;
}
static isUsing(name) {
if (game.user.isGM && uniquementJoueur.includes(name)) {
return true;
}
return ReglesOptionnelles.isSet(name);
}
static isSet(name) {
return game.settings.get(SYSTEM_RDD, ReglesOptionnelles._getIdRegle(name));
}
static set(name, value) {
return game.settings.set(SYSTEM_RDD, ReglesOptionnelles._getIdRegle(name), value ? true: false);
}
activateListeners(html) {
html.find(".select-option").click((event) => {
if (event.currentTarget.attributes.name) {
let id = event.currentTarget.attributes.name.value;
let isChecked = event.currentTarget.checked;
game.settings.set(SYSTEM_RDD, id, isChecked);
}
});
}
async _updateObject(event, formData) {
this.close();
}
}

View File

@@ -1,4 +1,4 @@
import { ReglesOptionelles } from "../settings/regles-optionelles.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { EffetsDraconiques } from "../tmr/effets-draconiques.js";
export class DialogRepos extends Dialog {
@@ -7,7 +7,7 @@ export class DialogRepos extends Dialog {
if (!actor.isPersonnage()) {
return
}
if (!ReglesOptionelles.isUsing("chateau-dormant-gardien") || !actor.hasPlayerOwner) {
if (!ReglesOptionnelles.isUsing("chateau-dormant-gardien") || !actor.hasPlayerOwner) {
actor.system.sommeil = {
"nouveaujour": true,
"insomnie": EffetsDraconiques.isSujetInsomnie(actor),

View File

@@ -6,7 +6,7 @@ import { RdDDice } from "../rdd-dice.js";
import { Misc } from "../misc.js";
import { DialogChronologie } from "../dialog-chronologie.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { ReglesOptionelles } from "../settings/regles-optionelles.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js";
import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js";
@@ -305,7 +305,7 @@ export class RdDCalendrier extends Application {
const oldTimestamp = this.timestamp;
await Promise.all(game.actors.map(async actor => await actor.onTimeChanging(oldTimestamp, newTimestamp)));
RdDTimestamp.setWorldTime(newTimestamp);
if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionelles.isUsing("chateau-dormant-gardien")) {
if (oldTimestamp.indexDate + 1 == newTimestamp.indexDate && ReglesOptionnelles.isUsing("chateau-dormant-gardien")) {
await DialogChateauDormant.create();
}
this.timestamp = newTimestamp;

View File

@@ -156,7 +156,9 @@ export class RdDTimestamp {
fields.minute.change(async (event) => await onChangeTimestamp(fields, path));
}
static defHeure(heure) {
return DEFINITION_HEURES.find(it => (it.heure) == heure % RDD_HEURES_PAR_JOUR);
}
static findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = DEFINITION_HEURES.filter(it => (it.heure) == parseInt(heure) % RDD_HEURES_PAR_JOUR || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);

View File

@@ -287,6 +287,16 @@ table {border: 1px solid #7a7971;}
padding: 0;
}
.grid-competence-archetype {
display: grid;
grid-column: span 3 / span 3;
grid-template-columns: 2fr 2fr 1fr;
gap: 0.5rem;
margin: 0.5rem 0;
padding: 0;
}
.grid-3col {
grid-column: span 3 / span 3;
grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -1898,34 +1908,51 @@ div.calendar-timestamp-edit select.calendar-signe-heure {
opacity: 1;
}
.chat-card-button {
box-shadow: inset 0px 1px 0px 0px #a6827e;
background: var(--background-custom-button);
background-color: #7d5d3b00;
border-radius: 3px;
border: 2px ridge #846109;
display: inline-block;
.chat-card-button, .chat-card-button-pushed {
border-radius: 0.2rem;
cursor: pointer;
color: #ffffff;
font-family: CaslonPro;
font-size: 0.9rem;
padding: 4px 12px 0px 12px;
padding: 0.2rem 0.4rem 0rem 0.4rem;
text-decoration: none;
text-shadow: 0px 1px 0px #4d3534;
position: relative;
margin:5px;
margin: 0.3rem;
border: 2px ridge #846109;
display: inline-block;
}
.chat-card-button{
text-shadow: 1px 1px #4d3534;
box-shadow: inset 1x 1px #a6827e;
color: var(--major-button-color);
background: var(--background-custom-button);
background-color: #7d5d3b00;
}
.chat-card-button-pushed {
text-shadow: 1px 1px hsla(202, 30%, 70%, 0.5);
box-shadow: inset -1px -1px #a6827e;
color: hsla(202, 42%, 14%, 0.7);
background: var(--major-button-color);
background-color: #7d5d3b00;
}
.chat-card-button:hover {
background: var(--background-custom-button-hover);
background-color: red;
}
.chat-card-button:active {
.chat-card-button-pushed:hover {
background: var(--background-custom-button-hover);
background-color: red;
}
.chat-card-button:active, .chat-card-button-pushed:active {
position:relative;
top:1px;
}
/* Dropdown Content (Hidden by Default) */
.button-dropdown-content {
display: none;

View File

@@ -1,9 +1,10 @@
{
"id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon",
"version": "11.0.7",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.0.7.zip",
"version": "11.0.9",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.0.9.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json",
"changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": {
"minimum": "11",
"verified": "11",
@@ -82,7 +83,8 @@
"path": "packs/arts-et-divertissements.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -117,7 +119,8 @@
"path": "packs/sorts-oniros.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -128,7 +131,8 @@
"path": "packs/sorts-hypnos.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -139,7 +143,8 @@
"path": "packs/sorts-narcos.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -150,7 +155,8 @@
"path": "packs/sorts-thanatos.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -161,7 +167,8 @@
"path": "packs/equipement.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -184,7 +191,8 @@
"path": "packs/rappel-des-regles.db",
"type": "JournalEntry",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OBSERVER"
},
"flags": {}
},
@@ -195,7 +203,8 @@
"path": "packs/macros.db",
"type": "Macro",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -242,7 +251,8 @@
"path": "packs/tarot-draconique.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -253,7 +263,8 @@
"path": "packs/extrait-poetique.db",
"type": "Item",
"ownership": {
"PLAYER": "OBSERVER"
"PLAYER": "OBSERVER",
"ASSISTANT": "OWNER"
},
"flags": {}
},
@@ -438,6 +449,72 @@
"flags": {}
}
],
"packFolders": [
{
"name": "Rêve de Dragon",
"sorting": "m",
"packs": [
"rappel-des-regles"
],
"folders": [
{
"name": "Personnages",
"sorting": "m",
"packs": [
"voyageurs",
"archetypes",
"humanoides",
"equipement",
"competences",
"arts-et-divertissements",
"meditations-et-ecrits",
"recettes-alchimiques"
]
},
{
"name": "Le Haut-rêve",
"sorting": "m",
"packs": [
"tetes-de-dragon-pour-tous-personnages",
"sorts-oniros",
"sorts-hypnos",
"sorts-narcos",
"sorts-thanatos",
"invocations",
"rencontres",
"queues-de-dragon",
"ombres-de-thanatos",
"souffles-de-dragon",
"tetes-de-dragon-pour-haut-revants"
]
},
{
"name": "Découverte du monde",
"sorting": "m",
"packs": [
"animaux",
"vehicules",
"entites",
"faune-flore-mineraux",
"competences-creatures",
"competences-entites",
"maladies-et-poisons"
]
},
{
"name": "Outils du Gardien",
"sorting": "m",
"packs": [
"scenes-rdd",
"tables-diverses",
"macros",
"tarot-draconique",
"extrait-poetique"
]
}
]
}
],
"socket": true,
"gridDistance": 1,
"gridUnits": "m",

View File

@@ -53,7 +53,9 @@
{{#if options.isObserver}}{{!-- Compétences Tab --}}
<div class="tab competences" data-group="primary" data-tab="competences">
<div class="flexrow">
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html"}}
<span>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/vue-detaillee.html"}}
</span>
<span class="flexrow"><a class="show-hide-competences">
{{#if options.showCompNiveauBase}}
<i class="fa-regular fa-filter-slash"></i> Montrer tout
@@ -65,9 +67,17 @@
<input class="recherche flex-grow" type="text" value="{{options.recherche.text}}" name="recherche" size="8" data-dtype="String" placeholder=""/>
</span>
<span>
{{#if options.vueDetaillee}}
&nbsp;&nbsp;
{{#if @root.options.vueArchetype}}
<a class="competence-archetype toggle-archetype chat-card-button-pushed"><i class="fa-solid fa-up-right-from-square"></i>Incarnation</a>
{{else}}
<a class="competence-archetype toggle-archetype chat-card-button"><i class="fa-solid fa-people-line"></i>Archétype</a>
{{/if}}
{{/if}}
</span>
</div>
<div class="grid grid-2col">
<div class="grid {{#if (and options.vueDetaillee options.vueArchetype)}}grid-competence-archetype{{else}}grid-2col{{/if}}">
<div class="competence-column">
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html" competences=(filtreTriCompetences byCateg.generale) categorie="Compétences générales"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html" competences=(filtreTriCompetences byCateg.particuliere) categorie="Compétences Particulières"}}
@@ -83,6 +93,11 @@
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/competence-categorie.html" competences=(filtreTriCompetences byCateg.draconic) categorie="Draconic"}}
{{/if}}
</div>
{{#if (and options.vueDetaillee options.vueArchetype)}}
<div>
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/archetype.hbs"}}
</div>
{{/if}}
</div>
</div>
{{/if}}

View File

@@ -0,0 +1,21 @@
<div>
<ul class="item-list">
{{#if @root.options.isGM}}
<li>
<a class="nouvelle-incarnation chat-card-button"><i class="fa-solid fa-person-circle-plus"></i> Nouvelle incarnation</a>
</li>
{{/if}}
<li><hr></li>
<li>Niveaux d'archétype</li>
{{#if calc.comptageArchetype}}
{{#each calc.comptageArchetype as |archetype|}}
<li class="item flexrow">
<label class="generic-label">
Niveaux {{numberFormat archetype.niveau decimals=0 sign=true}} : {{archetype.nombre}} / {{archetype.nombreMax}}
</label>
</li>
{{/each}}
{{/if}}
</ul>
</div>

View File

@@ -14,12 +14,13 @@
<input class="competence-value" type="text" compname="{{name}}" name="comp-value-{{name}}"
value="{{numberFormat system.niveau decimals=0 sign=true}}" data-dtype="number"
{{#unless @root.options.vueDetaillee}}disabled{{/unless}} />
{{#if (or (not @root.options.vueDetaillee) @root.options.vueArchetype)}}disabled{{/if}} />
{{#if @root.options.vueDetaillee}}
<span class="competence-xp {{#unless system.isLevelUp}}tooltip{{/unless}}">
<input class="competence-xp " type="text" compname="{{name}}" name="comp-xp-{{name}}"
value="{{numberFormat system.xp decimals=0 sign=false}}" data-dtype="number"/>
value="{{numberFormat system.xp decimals=0 sign=false}}" data-dtype="number"
{{#if (or (not @root.options.vueDetaillee) @root.options.vueArchetype)}}disabled{{/if}} />
{{#unless system.isLevelUp}}
<span class="tooltiptext left-competence ttt-xp">Vous devez acquérir {{system.xpNext}} points d'Experience pour augmenter de 1 votre compétence {{name}}</span>
{{/unless}}
@@ -28,7 +29,7 @@
{{#if (eq system.categorie 'draconic')}}
<input class="competence-xp-sort" type="text" compname="{{name}}" name="comp-xp-sort-{{name}}"
value="{{numberFormat system.xp_sort decimals=0 sign=false}}" data-dtype="number"
{{#unless @root.options.vueDetaillee}}disabled{{/unless}}/>
{{#if (or (not @root.options.vueDetaillee) @root.options.vueArchetype)}}disabled{{/if}} />
{{/if}}
{{#if @root.options.vueDetaillee}}
<div class="item-controls">
@@ -41,7 +42,8 @@
<i class="far fa-circle"></i>
{{/if}}
<input class="competence-archetype niveau-archetype" type="text" compname="{{name}}" name="comp-archetype-{{name}}"
value="{{numberFormat system.niveau_archetype decimals=0 sign=true}}" data-dtype="number"/>
value="{{numberFormat system.niveau_archetype decimals=0 sign=true}}" data-dtype="number"
{{#if (not @root.options.vueArchetype)}}disabled{{/if}} />
<a class="item-edit" title="Modifier"><i class="fas fa-edit"></i></a>
{{#if @root.options.isGM}}
<a class="item-delete" title="Supprimer"><i class="fas fa-trash"></i></a>

View File

@@ -13,18 +13,7 @@
<span class="generic-label">Total XP compétences</span>
<span class="competence-value">{{calc.competenceXPTotal}}</span>
</li>
{{#if calc.comptageArchetype}}
<li><hr></li>
<li>Niveaux d'archétype à répartir</li>
{{#each calc.comptageArchetype as |archetype key|}}
{{#if (gt archetype.reste 0)}}
<li class="item flexrow">
<label class="generic-label">Reste {{archetype.reste}} niveaux {{numberFormat archetype.niveau decimals=0 sign=true}} sur {{archetype.nombreMax}}</label>
</li>
{{/if}}
{{/each}}
{{/if}}
{{/if}}
{{/if}}
<li>&nbsp;</li>
</ul>
</div>

View File

@@ -1,39 +0,0 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attackerName}} a empoigné {{defenderName}}
</h4>
<hr>
<div>
<span class='chat-card-button-area'>
Au round suivant l'acquisition des 2 points d'Emp, {{attackerName}} peut faire perdre autant de points d'Endurance qu'il souhaite à {{defenderName}}
<br>
<a class='empoignade-perte-endurance chat-card-button'>
<select class='empoignade-perte-endurance'>
<option value="none">Faire perdre de l'endurance (selectionnez)</option>
<option value="end0">Endurance à 0</option>
<option value="end1">Endurance à 1</option>
<option value="endmoitie">La moitié de l'endurance</option>
<option value="endquart">Le quart de l'endurance</option>
</select>
</a>
{{#if empoignade.system.ausol}}
{{else}}
<br>
Dès l'acquisition des 2 points d'Emp, {{attackerName}} peut entraîner {{defenderName}} au sol. Les deux protagonistes restent empoignés.
<br>
<a class='empoignade-entrainer-sol chat-card-button'>
Entraîner au sol
</a>
<br>
A la fin du round ou les 2 points d'Emp sont acquis, {{attackerName}} peut projeter {{defenderName}} au sol. Les deux protagonistes ne sont plus empoignés.
<br>
<a class='empoignade-projeter-sol chat-card-button'>
Projeter au sol
</a>
{{/if}}
</div>

View File

@@ -1,7 +1,8 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attackerName}} a entraîné {{defenderName}} au sol. L'empoignade peut continuer.
{{attacker.name}} a entraîné {{defender.name}} au sol
</h4>
<hr>
<div>
L'empoignade peut continuer.
</div>

View File

@@ -0,0 +1,16 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attacker.name}} a empoigné {{defender.name}}
</h4>
<hr>
<div>
<span class='chat-card-button-area'>
{{attacker.name}} vient d'obtenir 2 points d'Emp, et peut
entraîner {{defender.name}} au sol. Les deux protagonistes
restent empoignés.
<br>
<a class='empoignade-entrainer-sol chat-card-button'>
Entraîner au sol
</a>
</span>
</div>

View File

@@ -0,0 +1,35 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attacker.name}} a empoigné {{defender.name}}
</h4>
<hr>
<div>
<span class='chat-card-button-area'>
<p>
{{attacker.name}} a obtenu 2 points d'Emp à la fin du round précédent, et peut:
<ul><li>
faire perdre des points d'Endurance à {{defender.name}}
<br>
<a class='empoignade-perte-endurance chat-card-button'>
<select class='empoignade-perte-endurance'>
<option value="none">Faire perdre de l'endurance (selectionnez)</option>
<option value="end0">Endurance à 0</option>
<option value="end1">Endurance à 1</option>
<option value="endmoitie">La moitié de l'endurance</option>
<option value="endquart">Le quart de l'endurance</option>
</select>
</a>
{{#if empoignade.system.ausol}}
{{else}}
</li><li>
projeter {{defender.name}} au sol. Les deux protagonistes ne sont plus empoignés.
<br>
<a class='empoignade-projeter-sol chat-card-button'>
Projeter au sol
</a>
{{/if}}
</li></ul>
</p>
</span>
</div>

View File

@@ -1,7 +1,8 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attackerName}} a fait perdre de l'endurance à {{defenderName}}, qui reste immobilisé. L'empoignade peut continuer.
{{attacker.name}} a fait perdre de l'endurance à {{defender.name}}
</h4>
<hr>
<div>
{{defender.name}} reste immobilisé. L'empoignade peut continuer.
</div>

View File

@@ -1,7 +1,8 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attackerName}} a projeté {{defenderName}} au sol. L'empoignade est terminée et a été supprimée.
{{attacker.name}} a projeté {{defender.name}} au sol
</h4>
<hr>
<div>
L'empoignade est terminée et a été supprimée.
</div>

View File

@@ -1,16 +1,17 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{log 'rollData' this}}
{{#if (eq mode "empoigner")}}
{{attackerName}} tente d'empoigner {{defenderName}}
{{attacker.name}} tente d'empoigner {{defender.name}}
{{/if}}
{{#if (eq mode "contrer-empoigner")}}
{{defenderName}} tente de contrer l'empoignade de {{attackerName}}
{{defender.name}} tente de contrer l'empoignade de {{attacker.name}}
{{/if}}
{{#if (eq mode "liberer")}}
{{attackerName}} tente de se libérer de l'empoignade de {{defenderName}}
{{attacker.name}} tente de se libérer de l'empoignade de {{defender.name}}
{{/if}}
{{#if (eq mode "contrer-liberer")}}
{{defenderName}} tente de contrer la libération de {{attackerName}}
{{defender.name}} tente de contrer la libération de {{attacker.name}}
{{/if}}
</h4>
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}}
@@ -20,7 +21,7 @@
{{#if (gte empoignade.system.pointsemp 2)}}
<br><strong>{{defenderName}} est empoigné et immobilisé par {{attackerName}} !</strong>
<br><strong>{{defender.name}} est empoigné et immobilisé par {{attacker.name}} !</strong>
{{else}}
<span class='chat-card-button-area'>
@@ -29,16 +30,12 @@
{{#if (eq mode "empoigner")}}
{{#if empoignade.isSuccess}}
<a class='defense-empoignade-cac chat-card-button'
data-attackerId='{{attacker.id}}'
data-defenderId='{{defender.id}}'
data-diff-libre='{{diffLibre}}'
data-defense-mode="contrer-empoigner">
Contrer l'empoignade (Corps à Corps)
</a>
{{#if (eq empoignade.system.pointsemp 0)}}
<a class='defense-empoignade-esquive chat-card-button'
data-attackerId='{{attacker.id}}'
data-defenderId='{{defender.id}}'
data-diff-libre='{{diffLibre}}'
data-defense-mode="contrer-empoigner">
Contrer l'empoignade (Esquive)
@@ -52,8 +49,6 @@
{{#if (eq mode "liberer")}}
{{#if empoignade.isSuccess}}
<a class='defense-empoignade-cac chat-card-button'
data-attackerId='{{attacker.id}}'
data-defenderId='{{defender.id}}'
data-diff-libre='{{diffLibre}}'
data-defense-mode="contrer-liberer">
Contrer la libération (Corps à Corps)
@@ -78,8 +73,7 @@
La tentative de contrer la libération est un échec!
{{/if}}
{{/if}}
<br>Points d'Emp: {{empoignade.system.pointsemp}}
</span>
<br>Points d'Emp: {{empoignade.system.pointsemp}}
{{/if}}
</div>

View File

@@ -1,10 +1,9 @@
<img class="chat-icon" src="{{competence.img}}" />
<h4>
{{attackerName}} tente d'empoigner {{defenderName}}
{{attacker.name}} tente d'empoigner {{defender.name}}
</h4>
<hr>
<div>
<span class='chat-card-button-area'>
<br>
<strong>{{attacker.name}} tente d'empoigner {{defender.name}}, qui est équipé d'une arme de mêlée. {{defender.name}}
@@ -16,5 +15,5 @@
data-defenderId='{{defender.id}}'>
Poursuivre l'empoignade
</a>
</span>
</div>

View File

@@ -1,6 +1,6 @@
<form class="skill-roll-dialog">
<h2>
{{defenderName}} tente de contrer l'empoignade de {{attackerName}}
{{defender.name}} tente de contrer l'empoignade de {{attacker.name}}
</h2>
<div class="grid grid-2col">
<div class="flex-group-left">