Compare commits

..

1 Commits

Author SHA1 Message Date
d795968519 v10.0.28 2022-10-14 21:52:38 +02:00
357 changed files with 8958 additions and 13412 deletions

6
.gitignore vendored
View File

@ -1,9 +1,5 @@
.vscode/settings.json
.idea
todo.txt
todo.md
/.vscode
/ignored/
/node_modules/
/jsconfig.json
/package.json
/package-lock.json

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -3,7 +3,6 @@
"TypePersonnage": "Personnage",
"TypeCreature": "Créature",
"TypeEntite": "Entité de cauchemar",
"TypeCommerce": "Commerce",
"TypeVehicule": "Véhicule"
},
"ITEM": {
@ -16,20 +15,17 @@
"TypeNombreastral": "Nombre astral",
"TypeTarot": "Carte de tarot",
"TypeCasetmr": "TMR spéciale",
"TypeRencontre": "Rencontre TMR",
"TypeRencontrestmr": "Rencontre TMR",
"TypeMunition": "Munition",
"TypeMonnaie": "Monnaie",
"TypeHerbe": "Herbe",
"TypePlante": "Plante",
"TypeHerbe": "Herbe ou plante",
"TypeIngredient": "Ingrédient",
"TypeFaune": "Faune",
"TypeLivre": "Livre",
"TypePotion": "Potion",
"TypeArme": "Arme",
"TypeArmure": "Armure",
"TypeConteneur": "Conteneur",
"TypeNourritureboisson": "Nourriture & boisson",
"TypeService": "Service",
"TypeChant": "Chant",
"TypeDanse": "Danse",
"TypeMusique": "Musique",
@ -46,8 +42,7 @@
"TypeSouffle": "Souffle de Dragon",
"TypeTete": "Tête de Dragon",
"TypePossession": "Possession",
"TypeSortreserve": "Sort en réserve",
"TypeExtraitpoetique": "Extrait poetique"
"TypeSortreserve": "Sort en réserve"
},
"EFFECT": {
"StatusStunned": "Sonné",

View File

@ -1,9 +1,14 @@
import { RdDActorSheet } from "./actor-sheet.js";
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { RdDUtility } from "./rdd-utility.js";
import { RdDActorSheet } from "./actor-sheet.js";
import { RdDCarac } from "./rdd-carac.js";
/* -------------------------------------------- */
export class RdDActorCreatureSheet extends RdDActorSheet {
/** @override */
@ -14,7 +19,7 @@ export class RdDActorCreatureSheet extends RdDActorSheet {
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }]
});
}
@ -27,17 +32,24 @@ export class RdDActorCreatureSheet extends RdDActorSheet {
if (!this.options.editable) return;
// On competence change
this.html.find('.creature-carac').change(async event => {
html.find('.creature-carac').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value));
});
this.html.find('.creature-niveau').change(async event => {
html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value));
});
this.html.find('.creature-dommages').change(async event => {
html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
});
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.object.update(formData);
}
}

View File

@ -1,40 +1,42 @@
import { RdDActorSheet } from "./actor-sheet.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDUtility } from "./rdd-utility.js";
export class RdDActorEntiteSheet extends RdDActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html",
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.html",
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac"}],
dragDrop: [{dragSelector: ".item-list .item", dropSelector: null}]
});
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// On competence change
this.html.find('.creature-carac').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value));
});
this.html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value));
});
this.html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value));
});
html.find('.creature-carac').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "carac_value", parseInt(event.target.value) );
} );
html.find('.creature-niveau').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "niveau", parseInt(event.target.value) );
} );
html.find('.creature-dommages').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCreatureCompetence( compName, "dommages", parseInt(event.target.value) );
} );
}
}

View File

@ -1,3 +1,8 @@
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemArme } from "./item-arme.js";
@ -7,19 +12,13 @@ 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 { ReglesOptionelles } from "./regles-optionelles.js";
import { DialogRepos } from "./dialog-repos.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js";
import { RdDItem } from "./item.js";
import { STATUSES } from "./status-effects.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDActorSheet extends RdDBaseActorSheet {
export class RdDActorSheet extends ActorSheet {
/** @override */
static get defaultOptions() {
@ -27,9 +26,9 @@ export class RdDActorSheet extends RdDBaseActorSheet {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
width: 640,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
showCompNiveauBase: false,
vueDetaillee: false
});
@ -37,38 +36,50 @@ export class RdDActorSheet extends RdDBaseActorSheet {
/* -------------------------------------------- */
async getData() {
let formData = await super.getData();
mergeObject(formData,
{
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
owner: this.actor.isOwner,
biographie: await TextEditor.enrichHTML(this.actor.system.biographie, { async: true }),
notes: await TextEditor.enrichHTML(this.actor.system.notes, { async: true }),
});
mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
})
this.timerRecherche = undefined;
let formData = {
title: this.title,
id: this.actor.id,
type: this.actor.type,
img: this.actor.img,
name: this.actor.name,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
system: foundry.utils.deepClone(this.actor.system),
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
options: this.options,
owner: this.actor.isOwner,
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}),
biographie: await TextEditor.enrichHTML(this.object.system.biographie, {async: true}),
notes: await TextEditor.enrichHTML(this.object.system.notes, {async: true}),
notesmj: await TextEditor.enrichHTML(this.object.system.notesmj, {async: true}),
calc: {
encTotal: await this.actor.computeEncombrementTotalEtMalusArmure(),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
},
}
formData.options.isGM = game.user.isGM;
RdDUtility.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
if (formData.type == 'personnage') {
formData.options.mainsDirectrices = MAINS_DIRECTRICES;
formData.byCateg = Misc.classify(formData.competences, it => it.system.categorie)
formData.calc.comptageArchetype = RdDItemCompetence.computeResumeArchetype(formData.competences);
formData.calc.competenceXPTotal = RdDItemCompetence.computeTotalXP(formData.competences);
formData.calc.fatigue = RdDUtility.calculFatigueHtml(formData.system.sante.fatigue.value, formData.system.sante.endurance.max);
formData.calc.comptageArchetype = RdDItemCompetence.computeResumeArchetype(formData.competences);
formData.calc.competenceXPTotal= RdDItemCompetence.computeTotalXP(formData.competences);
formData.calc.fatigue= RdDUtility.calculFatigueHtml(formData.system.sante.fatigue.value, formData.system.sante.endurance.max);
formData.competences.forEach(item => {
item.system.isHidden = this.options.recherche
? !item.isNomLike(this.options.recherche.text)
: (this.options.showCompNiveauBase && RdDItemCompetence.isNiveauBase(item));
item.system.isVisible = this.options.recherche
? RdDItemCompetence.nomContientTexte(item, this.options.recherche.text)
: (!this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(item));
RdDItemCompetence.levelUp(item, formData.system.compteurs.experience.value);
});
@ -91,6 +102,7 @@ export class RdDActorSheet extends RdDBaseActorSheet {
formData.hautreve = {
isDemiReve: this.actor.getEffect(STATUSES.StatusDemiReve),
rencontres: duplicate(formData.system.reve.rencontre.list),
cacheTMR: this.actor.isTMRCache()
}
@ -107,18 +119,79 @@ export class RdDActorSheet extends RdDBaseActorSheet {
return formData;
}
isCompetenceAffichable(competence) {
return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence);
}
/* -------------------------------------------- */
async _onDropActor(event, dragData) {
const dropActor = fromUuidSync(dragData.uuid);
this.actor.addSubActeur(dropActor);
super._onDropActor(event, dragData);
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = $(event.target)?.closest('.item').attr('data-item-id')
const dropParams = RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor.id, dragData, this.objetVersConteneur)
const callSuper = await this.actor.processDropItem(dropParams)
if (callSuper) {
await super._onDropItem(event, dragData)
}
}
/* -------------------------------------------- */
async createItem(name, type) {
await this.actor.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
}
/* -------------------------------------------- */
async createEmptyTache() {
await this.createItem('Nouvelle tache', 'tache');
}
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
HtmlUtility._showControlWhen($(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor));
this.html.find('.subacteur-delete').click(async event => {
html.find('.item-split').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
RdDSheetUtility.splitItem(item, this.actor);
});
html.find('.item-edit').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor)
item.sheet.render(true)
})
html.find('.display-label a').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item.sheet.render(true);
});
html.find('.rencontre-delete').click(async event => {
this.actor.deleteTMRRencontre(RdDSheetUtility.getItemId(event));
});
html.find('.item-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const item = this.actor.getObjet(li.data("item-id"));
RdDUtility.confirmerSuppressionItem(this, item, li);
});
html.find('.item-vendre').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.proposerVente();
});
html.find('.item-montrer').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.postItem();
});
html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor)
this.actor.actionItem(item);
});
html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const actorId = li.data("actor-id");
if (actorId) {
@ -126,57 +199,54 @@ export class RdDActorSheet extends RdDBaseActorSheet {
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li);
}
});
this.html.find('.experiencelog-delete').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
html.find('.experiencelog-delete').click(async event => {
const li = $(event.currentTarget)?.parents(".experiencelog");
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(key, 1);
});
this.html.find('.experiencelog-delete-previous').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
html.find('.experiencelog-delete-previous').click(async event => {
const li = $(event.currentTarget)?.parents(".experiencelog");
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(0, key + 1);
});
this.html.find('.encaisser-direct').click(async event => {
html.find('.encaisser-direct').click(async event => {
this.actor.encaisser();
})
this.html.find('.sheet-possession-attack').click(async event => {
html.find('.sheet-possession-attack').click(async event => {
const poss = RdDSheetUtility.getItem(event, this.actor)
this.actor.conjurerPossession(poss)
})
this.html.find('.remise-a-neuf').click(async event => {
html.find('.remise-a-neuf').click(async event => {
if (game.user.isGM) {
this.actor.remiseANeuf();
}
});
this.html.find('.creer-tache').click(async event => {
html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
this.html.find('.creer-tache-blessure-legere').click(async event => {
this.actor.createTacheBlessure('legere');
html.find('.creer-un-objet').click(async event => {
RdDUtility.selectObjetType(this);
});
this.html.find('.creer-tache-blessure-grave').click(async event => {
this.actor.createTacheBlessure('grave');
html.find('.creer-une-oeuvre').click(async event => {
RdDUtility.selectTypeOeuvre(this);
});
this.html.find('.creer-tache-blessure-critique').click(async event => {
this.actor.createTacheBlessure('critique');
});
this.html.find('.creer-une-oeuvre').click(async event => {
this.selectTypeOeuvreToCreate();
html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
});
// Blessure control
this.html.find('.blessure-control').click(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
html.find('.blessure-control').click(async event => {
const tr = $(event.currentTarget).parents(".item");
let btype = tr.data("blessure-type");
let index = tr.data('blessure-index');
let active = this.html.find(event.currentTarget).data('blessure-active');
let active = $(event.currentTarget).data('blessure-active');
//console.log(btype, index, active);
await this.actor.manageBlessureFromSheet(btype, index, active);
});
// Blessure data
this.html.find('.blessure-soins').change(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
html.find('.blessure-soins').change(async event => {
const tr = $(event.currentTarget).parents(".item");
let btype = tr.data('blessure-type');
let index = tr.data('blessure-index');
let psoins = tr.find('.blessure-premiers_soins').val();
@ -190,57 +260,57 @@ export class RdDActorSheet extends RdDBaseActorSheet {
});
// Equip Inventory Item
this.html.find('.item-equip').click(async event => {
html.find('.item-equip').click(async event => {
this.actor.equiperObjet(RdDSheetUtility.getItemId(event));
});
// Roll Carac
this.html.find('.carac-label a').click(async event => {
html.find('.carac-label a').click(async event => {
let caracName = event.currentTarget.attributes.name.value;
this.actor.rollCarac(caracName.toLowerCase());
});
this.html.find('.chance-actuelle').click(async event => {
html.find('.chance-actuelle').click(async event => {
this.actor.rollCarac('chance-actuelle');
});
this.html.find('.chance-appel').click(async event => {
html.find('.chance-appel').click(async event => {
this.actor.rollAppelChance();
});
this.html.find('[name="jet-astrologie"]').click(async event => {
html.find('#jet-astrologie').click(async event => {
this.actor.astrologieNombresAstraux();
});
// Roll Skill
this.html.find('a.competence-label').click(async event => {
html.find('a.competence-label').click(async event => {
this.actor.rollCompetence(RdDSheetUtility.getItemId(event));
});
this.html.find('.tache-label a').click(async event => {
html.find('.tache-label a').click(async event => {
this.actor.rollTache(RdDSheetUtility.getItemId(event));
});
this.html.find('.meditation-label a').click(async event => {
html.find('.meditation-label a').click(async event => {
this.actor.rollMeditation(RdDSheetUtility.getItemId(event));
});
this.html.find('.chant-label a').click(async event => {
html.find('.chant-label a').click(async event => {
this.actor.rollChant(RdDSheetUtility.getItemId(event));
});
this.html.find('.danse-label a').click(async event => {
html.find('.danse-label a').click(async event => {
this.actor.rollDanse(RdDSheetUtility.getItemId(event));
});
this.html.find('.musique-label a').click(async event => {
html.find('.musique-label a').click(async event => {
this.actor.rollMusique(RdDSheetUtility.getItemId(event));
});
this.html.find('.oeuvre-label a').click(async event => {
html.find('.oeuvre-label a').click(async event => {
this.actor.rollOeuvre(RdDSheetUtility.getItemId(event));
});
this.html.find('.jeu-label a').click(async event => {
html.find('.jeu-label a').click(async event => {
this.actor.rollJeu(RdDSheetUtility.getItemId(event));
});
this.html.find('.recettecuisine-label a').click(async event => {
html.find('.recettecuisine-label a').click(async event => {
this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event));
});
this.html.find('.subacteur-label a').click(async event => {
html.find('.subacteur-label a').click(async event => {
let actorId = RdDSheetUtility.getEventItemData(event, 'actor-id');
let actor = game.actors.get(actorId);
if (actor) {
@ -249,25 +319,25 @@ export class RdDActorSheet extends RdDBaseActorSheet {
});
// Boutons spéciaux MJs
this.html.find('.forcer-tmr-aleatoire').click(async event => {
html.find('.forcer-tmr-aleatoire').click(async event => {
this.actor.reinsertionAleatoire("Action MJ");
});
this.html.find('.afficher-tmr').click(async event => {
html.find('.afficher-tmr').click(async event => {
this.actor.changeTMRVisible();
});
// Points de reve actuel
this.html.find('.ptreve-actuel a').click(async event => {
this.actor.rollCarac('reve-actuel', true);
html.find('.ptreve-actuel a').click(async event => {
this.actor.rollCarac('reve-actuel');
});
// Roll Weapon1
this.html.find('.arme-label a').click(async event => {
html.find('.arme-label a').click(async event => {
let arme = this._getEventArmeCombat(event);
this.actor.rollArme(duplicate(arme));
});
// Initiative pour l'arme
this.html.find('.arme-initiative a').click(async event => {
html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
if (combatant) {
let action = this._getEventArmeCombat(event);
@ -277,195 +347,209 @@ export class RdDActorSheet extends RdDBaseActorSheet {
}
});
// Display TMR, visualisation
this.html.find('.visu-tmr').click(async event => {
html.find('.visu-tmr').click(async event => {
this.actor.displayTMR("visu");
});
// Display TMR, normal
this.html.find('.monte-tmr').click(async event => {
html.find('.monte-tmr').click(async event => {
this.actor.displayTMR("normal");
});
// Display TMR, fast
this.html.find('.monte-tmr-rapide').click(async event => {
html.find('.monte-tmr-rapide').click(async event => {
this.actor.displayTMR("rapide");
});
this.html.find('.repos').click(async event => {
await this.actor.repos();
html.find('.repos').click(async event => {
await DialogRepos.create(this.actor);
});
this.html.find('.delete-active-effect').click(async event => {
html.find('.delete-active-effect').click(async event => {
if (game.user.isGM) {
let effect = this.html.find(event.currentTarget).parents(".active-effect").data('effect');
let effect = $(event.currentTarget).parents(".active-effect").data('effect');
this.actor.removeEffect(effect);
}
});
this.html.find('.enlever-tous-effets').click(async event => {
html.find('.enlever-tous-effets').click(async event => {
if (game.user.isGM) {
await this.actor.removeEffects();
}
});
this.html.find('.carac-xp-augmenter').click(async event => {
html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
});
this.html.find('.competence-xp-augmenter').click(async event => {
html.find('.competence-xp-augmenter').click(async event => {
this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event));
});
this.html.find('.competence-stress-augmenter').click(async event => {
html.find('.competence-stress-augmenter').click(async event => {
this.actor.updateCompetenceStress(RdDSheetUtility.getItemId(event));
});
if (this.options.vueDetaillee) {
// On carac change
this.html.find('.carac-value').change(async event => {
html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
this.actor.updateCarac(caracName, parseInt(event.target.value));
});
this.html.find('input.carac-xp').change(async event => {
html.find('input.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "");
this.actor.updateCaracXP(caracName, parseInt(event.target.value));
});
// On competence change
this.html.find('.competence-value').change(async event => {
html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
//console.log("Competence changed :", compName);
this.actor.updateCompetence(compName, parseInt(event.target.value));
});
// On competence xp change
this.html.find('input.competence-xp').change(async event => {
html.find('input.competence-xp').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXP(compName, parseInt(event.target.value));
});
// On competence xp change
this.html.find('input.competence-xp-sort').change(async event => {
html.find('input.competence-xp-sort').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
// On competence archetype change
this.html.find('.competence-archetype').change(async event => {
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('.show-hide-competences').click(async event => {
html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;
this.render(true);
});
this.html.find('.vue-detaillee').click(async event => {
html.find('.recherche')
.each((index, field) => {
if (this.options.recherche) {
field.focus();
field.setSelectionRange(this.options.recherche.start, this.options.recherche.end);
}
})
.keyup(async event => {
const nouvelleRecherche = this._optionRecherche(event.currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text){
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
}
this.timerRecherche = setTimeout(() => {
this.timerRecherche = undefined;
this.render(true);
}, 500);
}
})
.change(async event =>
this.options.recherche = this._optionRecherche(event.currentTarget)
);
html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
// On pts de reve change
this.html.find('.pointsreve-value').change(async event => {
html.find('.pointsreve-value').change(async event => {
let reveValue = event.currentTarget.value;
this.actor.update({ "system.reve.reve.value": reveValue });
});
// On seuil de reve change
this.html.find('.seuil-reve-value').change(async event => {
html.find('.seuil-reve-value').change(async event => {
console.log("seuil-reve-value", event.currentTarget)
this.actor.setPointsDeSeuil(event.currentTarget.value);
});
html.find('#attribut-protection-edit').change(async event => {
this.actor.updateAttributeValue(event.currentTarget.attributes.name.value, parseInt(event.target.value));
});
// On stress change
this.html.find('.compteur-edit').change(async event => {
html.find('.compteur-edit').change(async event => {
let fieldName = event.currentTarget.attributes.name.value;
this.actor.updateCompteurValue(fieldName, parseInt(event.target.value));
});
this.html.find('.stress-test').click(async event => {
html.find('#ethylisme').change(async event => {
this.actor.setEthylisme(parseInt(event.target.value));
});
html.find('.stress-test').click(async event => {
this.actor.transformerStress();
});
this.html.find('.moral-malheureux').click(async event => {
html.find('.moral-malheureux').click(async event => {
this.actor.jetDeMoral('malheureuse');
});
this.html.find('.moral-neutre').click(async event => {
html.find('.moral-neutre').click(async event => {
this.actor.jetDeMoral('neutre');
});
this.html.find('.moral-heureux').click(async event => {
html.find('.moral-heureux').click(async event => {
this.actor.jetDeMoral('heureuse');
});
this.html.find('.ethylisme-test').click(async event => {
html.find('.ethylisme-test').click(async event => {
this.actor.jetEthylisme();
});
this.html.find('.jet-vie').click(async event => {
html.find('.jet-vie').click(async event => {
this.actor.jetVie();
});
this.html.find('.jet-endurance').click(async event => {
html.find('.jet-endurance').click(async event => {
this.actor.jetEndurance();
});
this.html.find('.vie-plus').click(async event => {
html.find('.monnaie-plus').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), 1);
});
html.find('.monnaie-moins').click(async event => {
this.actor.monnaieIncDec(RdDSheetUtility.getItemId(event), -1);
});
html.find('.vie-plus').click(async event => {
this.actor.santeIncDec("vie", 1);
});
this.html.find('.vie-moins').click(async event => {
html.find('.vie-moins').click(async event => {
this.actor.santeIncDec("vie", -1);
});
this.html.find('.endurance-plus').click(async event => {
html.find('.endurance-plus').click(async event => {
this.actor.santeIncDec("endurance", 1);
});
this.html.find('.endurance-moins').click(async event => {
html.find('.endurance-moins').click(async event => {
this.actor.santeIncDec("endurance", -1);
});
this.html.find('.ptreve-actuel-plus').click(async event => {
html.find('.ptreve-actuel-plus').click(async event => {
this.actor.reveActuelIncDec(1);
});
this.html.find('.ptreve-actuel-moins').click(async event => {
html.find('.ptreve-actuel-moins').click(async event => {
this.actor.reveActuelIncDec(-1);
});
this.html.find('.fatigue-plus').click(async event => {
html.find('.fatigue-plus').click(async event => {
this.actor.santeIncDec("fatigue", 1);
});
this.html.find('.fatigue-moins').click(async event => {
html.find('.fatigue-moins').click(async event => {
this.actor.santeIncDec("fatigue", -1);
});
}
isCompetenceAffichable(competence) {
return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence);
}
/* -------------------------------------------- */
async _onDropActor(event, dragData) {
const dropActor = fromUuidSync(dragData.uuid);
this.actor.addSubActeur(dropActor);
super._onDropActor(event, dragData);
}
/* -------------------------------------------- */
async selectTypeOeuvreToCreate() {
let typeObjets = RdDItem.getTypesOeuvres();
let content = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of typeObjets) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
_optionRecherche(target) {
if (!target.value?.length){
return undefined;
}
content += '</select>';
let dialog = new Dialog({
title: "Créer une oeuvre",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'oeuvre",
callback: () => this.actor.createItem($(".item-type").val())
}
}
});
dialog.render(true);
}
/* -------------------------------------------- */
async createEmptyTache() {
await this.actor.createItem('tache', 'Nouvelle tache');
return {
text: target.value,
start: target.selectionStart,
end: target.selectionEnd,
};
}
_getEventArmeCombat(event) {
const li = this.html.find(event.currentTarget)?.parents(".item");
const li = $(event.currentTarget)?.parents(".item");
let armeName = li.data("arme-name");
let compName = li.data('competence-name');
const arme = this.armesList.find(a => a.name == armeName && a.system.competence == compName);
@ -483,8 +567,8 @@ export class RdDActorSheet extends RdDBaseActorSheet {
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
let bodyHeight = position.height - sheetHeader[0].clientHeight;
if (sheetTabs.length > 0) {
bodyHeight -= sheetTabs[0].clientHeight;
if (sheetTabs.length>0) {
bodyHeight -= sheetTabs[0].clientHeight;
}
sheetBody.css("height", bodyHeight);
return position;

View File

@ -1,4 +1,5 @@
import { RdDUtility } from "./rdd-utility.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { RdDActorSheet } from "./actor-sheet.js";
/* -------------------------------------------- */
@ -14,26 +15,9 @@ export class RdDActorVehiculeSheet extends RdDActorSheet {
width: 640,
height: 720,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }]
});
}
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
this.html.find('.resistance-moins').click(async event => {
this.actor.vehicleIncDec("resistance", -1);
});
this.html.find('.resistance-plus').click(async event => {
this.actor.vehicleIncDec("resistance", 1);
});
this.html.find('.structure-moins').click(async event => {
this.actor.vehicleIncDec("structure", -1);
});
this.html.find('.structure-plus').click(async event => {
this.actor.vehicleIncDec("structure", 1);
});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,300 +0,0 @@
import { RdDUtility } from "../rdd-utility.js";
import { Misc } from "../misc.js";
import { DialogSplitItem } from "../dialog-split-item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { Monnaie } from "../item-monnaie.js";
import { RdDItem } from "../item.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDBaseActorSheet extends ActorSheet {
/** @override */
static get defaultOptions() {
RdDUtility.initAfficheContenu();
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
});
}
/* -------------------------------------------- */
async getData() {
Monnaie.validerMonnaies(this.actor.itemTypes['monnaie']);
this.actor.recompute();
let formData = {
title: this.title,
id: this.actor.id,
type: this.actor.type,
img: this.actor.img,
name: this.actor.name,
system: this.actor.system,
description: await TextEditor.enrichHTML(this.actor.system.description, { async: true }),
notesmj: await TextEditor.enrichHTML(this.actor.system.notesmj, { async: true }),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.actor, this.isEditable)
}
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
formData.calc = {
fortune: Monnaie.toSolsDeniers(this.actor.getFortune()),
prixTotalEquipement: this.actor.computePrixTotalEquipement(),
encTotal: await this.actor.computeEncTotal(),
}
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
this._appliquerRechercheObjets(formData.objets, formData.conteneurs);
formData.conteneurs = RdDUtility.conteneursRacine(formData.conteneurs);
return formData;
}
_appliquerRechercheObjets(objets, conteneurs) {
if (this.options.recherche?.text) {
const recherche = this.options.recherche;
const allVisible = objets.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id);
let addVisible = conteneurs.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id)
do {
allVisible.push(...addVisible)
const parentsIds = conteneurs.filter(it => it.system.contenu.find(id => allVisible.includes(id))).map(it => it.id)
addVisible = parentsIds.filter(id => !allVisible.includes(id))
}
while (addVisible.length > 0)
objets.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id))
}
else {
objets.forEach(it => it.system.isHidden = false)
conteneurs.forEach(it => it.system.isHidden = false)
}
}
/* -------------------------------------------- */
static filterItemsPerTypeForSheet(formData, itemTypes) {
formData.recettescuisine = Misc.arrayOrEmpty(itemTypes['recettecuisine']);
formData.recettesAlchimiques = Misc.arrayOrEmpty(itemTypes['recettealchimique']);
formData.maladies = Misc.arrayOrEmpty(itemTypes['maladie']);
formData.poisons = Misc.arrayOrEmpty(itemTypes['poison']);
formData.possessions = Misc.arrayOrEmpty(itemTypes['possession']);
formData.maladiesPoisons = formData.maladies.concat(formData.poisons);
formData.competences = (itemTypes['competence'] ?? []).concat(itemTypes['competencecreature'] ?? []);
formData.sortsReserve = Misc.arrayOrEmpty(itemTypes['sortreserve']);
formData.sorts = Misc.arrayOrEmpty(itemTypes['sort']);
formData.rencontres = Misc.arrayOrEmpty(itemTypes['rencontre']);
formData.casestmr = Misc.arrayOrEmpty(itemTypes['casetmr']);
formData.signesdraconiques = Misc.arrayOrEmpty(itemTypes['signedraconique']);
formData.queues = Misc.arrayOrEmpty(itemTypes['queue']);
formData.souffles = Misc.arrayOrEmpty(itemTypes['souffle']);
formData.ombres = Misc.arrayOrEmpty(itemTypes['ombre']);
formData.tetes = Misc.arrayOrEmpty(itemTypes['tete']);
formData.taches = Misc.arrayOrEmpty(itemTypes['tache']);
formData.meditations = Misc.arrayOrEmpty(itemTypes['meditation']);
formData.chants = Misc.arrayOrEmpty(itemTypes['chant']);
formData.danses = Misc.arrayOrEmpty(itemTypes['danse']);
formData.musiques = Misc.arrayOrEmpty(itemTypes['musique']);
formData.oeuvres = Misc.arrayOrEmpty(itemTypes['oeuvre']);
formData.jeux = Misc.arrayOrEmpty(itemTypes['jeu']);
formData.services = Misc.arrayOrEmpty(itemTypes['service']);
formData.conteneurs = Misc.arrayOrEmpty(itemTypes['conteneur']);
formData.materiel = Misc.arrayOrEmpty(itemTypes['objet']);
formData.armes = Misc.arrayOrEmpty(itemTypes['arme']);
formData.armures = Misc.arrayOrEmpty(itemTypes['armure']);
formData.munitions = Misc.arrayOrEmpty(itemTypes['munition']);
formData.livres = Misc.arrayOrEmpty(itemTypes['livre']);
formData.potions = Misc.arrayOrEmpty(itemTypes['potion']);
formData.plantes = Misc.arrayOrEmpty(itemTypes['plante']);
formData.ingredients = Misc.arrayOrEmpty(itemTypes['ingredient']);
formData.faunes = Misc.arrayOrEmpty(itemTypes['faune']);
formData.herbes = Misc.arrayOrEmpty(itemTypes['herbe']);
formData.nourritureboissons = Misc.arrayOrEmpty(itemTypes['nourritureboisson']);
formData.gemmes = Misc.arrayOrEmpty(itemTypes['gemme']);
formData.monnaie = Misc.arrayOrEmpty(itemTypes['monnaie']).sort(Monnaie.triValeurEntiere());
formData.objets = RdDItem.getItemTypesInventaire('all')
.map(t => Misc.arrayOrEmpty(itemTypes[t]))
.reduce((a, b) => a.concat(b), [])
.sort(Misc.ascending(it => it.name));
}
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(this.getItemId(event));
this.render(true);
});
this.html.find('.item-edit').click(async event => this.getItem(event)?.sheet.render(true))
this.html.find('.item-montrer').click(async event => this.getItem(event)?.postItemToChat());
this.html.find('.actor-montrer').click(async event => this.actor.postActorToChat());
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.item-split').click(async event => {
const item = this.getItem(event);
RdDSheetUtility.splitItem(item, this.actor);
});
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this, this.getItem(event)));
this.html.find('.item-vendre').click(async event => this.vendre(this.getItem(event)));
this.html.find('.creer-un-objet').click(async event => {
this.selectObjetTypeToCreate();
});
this.html.find('.nettoyer-conteneurs').click(async event => {
this.actor.nettoyerConteneurs();
});
this.html.find('.monnaie-plus').click(async event => {
this.actor.monnaieIncDec(this.getItemId(event), 1);
});
this.html.find('.monnaie-moins').click(async event => {
this.actor.monnaieIncDec(this.getItemId(event), -1);
});
this.html.find('.recherche')
.each((index, field) => {
this._rechercheSelectArea(field);
})
.keyup(async event => this._rechercherKeyup(event))
.change(async event => this._rechercherKeyup(event));
}
_rechercherKeyup(event) {
const currentTarget = event.currentTarget;
const nouvelleRecherche = this._optionRecherche(currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text) {
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
}
this.timerRecherche = setTimeout(() => {
this.timerRecherche = undefined;
this.render(true);
}, 500);
}
}
_rechercheSelectArea(field) {
if (this.options.recherche) {
field.focus();
field.setSelectionRange(this.options.recherche.start, this.options.recherche.end);
}
}
getItemId(event) {
return RdDSheetUtility.getItemId(event);
}
getItem(event) {
return RdDSheetUtility.getItem(event, this.actor);
}
_optionRecherche(target) {
if (!target.value?.length) {
return undefined;
}
return {
text: target.value,
start: target.selectionStart,
end: target.selectionEnd,
};
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
buttons.unshift({
class: "montrer",
icon: "fas fa-comment",
onclick: ev => this.actor.postActorToChat()
});
return buttons
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
const destItemId = this.html.find(event.target)?.closest('.item').attr('data-item-id')
const dropParams = await RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, dragData, this.objetVersConteneur)
if (dropParams) {
const callSuper = await this.actor.processDropItem(dropParams)
if (callSuper) {
await super._onDropItem(event, dragData)
}
}
}
/* -------------------------------------------- */
async selectObjetTypeToCreate() {
let typeObjets = this.getTypesInventaire().sort(Misc.ascending(type => Misc.typeName('Item', type)));
let content = `<span class="competence-label">Selectionnez le type d'équipement</span><select class="item-type">`;
for (let typeName of typeObjets) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}
content += '</select>';
let d = new Dialog({
title: "Créer un équipement",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'objet",
callback: () => this.actor.createItem($(".item-type").val())
}
}
});
d.render(true);
}
getTypesInventaire() {
return RdDItem.getItemTypesInventaire();
}
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
let bodyHeight = position.height - sheetHeader[0].clientHeight;
if (sheetTabs.length > 0) {
bodyHeight -= sheetTabs[0].clientHeight;
}
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);
}
async _onSplitItem(item, split) {
if (split >= 1 && split < item.system.quantite) {
await item.diminuerQuantite(split);
const splitItem = duplicate(item);
splitItem.system.quantite = split;
await this.actor.createEmbeddedDocuments('Item', [splitItem])
}
}
vendre(item) {
item?.proposerVente(this.actor.getQuantiteDisponible(item));
}
}

View File

@ -1,627 +0,0 @@
import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { Monnaie } from "../item-monnaie.js";
import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";
export class RdDBaseActor extends Actor {
static getDefaultImg(itemType) {
return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
}
/* -------------------------------------------- */
static init() {
Hooks.on("preUpdateItem", (item, change, options, id) => RdDBaseActor.getParentActor(item)?.onPreUpdateItem(item, change, options, id));
Hooks.on("createItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onCreateItem(item, options, id));
Hooks.on("deleteItem", (item, options, id) => RdDBaseActor.getParentActor(item)?.onDeleteItem(item, options, id));
Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId));
}
static onSocketMessage(sockmsg) {
switch (sockmsg.msg) {
case "msg_remote_actor_call":
return RdDBaseActor.onRemoteActorCall(sockmsg.data, sockmsg.userId);
case "msg_reset_nombre_astral":
console.log("RESET ASTRAL", game.user.character);
game.user.character.resetNombreAstral();
return;
}
}
static remoteActorCall(callData, userId = undefined) {
userId = userId ?? Misc.firstConnectedGMId();
if (userId == game.user.id) {
RdDBaseActor.onRemoteActorCall(callData, userId);
return false;
}
else {
game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_remote_actor_call", data: callData, userId: userId });
return true;
}
}
static onRemoteActorCall(callData, userId) {
if (userId == game.user.id) {
const actor = game.actors.get(callData?.actorId);
if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon
const args = callData.args;
console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')');
actor[callData.method](...args);
}
}
}
static getParentActor(document) {
return document?.parent instanceof Actor ? document.parent : undefined
}
/**
* Cet methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
* compétences et monnaies.
*
* @param {Object} actorData template d'acteur auquel ajouter des informations.
* @param {Object} options optionspour customiser la création
*/
static async create(actorData, options) {
// import depuis un compendium
if (actorData instanceof Array) {
return super.create(actorData, options);
}
// Création d'un acteur avec des items (uniquement en cas de duplication): pas besoin d'ajouter d'items
if (actorData.items) {
return await super.create(actorData, options);
}
actorData.items = [];
if (actorData.type == "personnage") {
const competences = await SystemCompendiums.getCompetences(actorData.type);
actorData.items = actorData.items.concat(competences.map(i => i.toObject()))
.concat(Monnaie.monnaiesStandard());
}
else if (actorData.type == "commerce") {
actorData.items = actorData.items.concat(Monnaie.monnaiesStandard());
}
return super.create(actorData, options);
}
constructor(docData, context = {}) {
if (!context.rdd?.ready) {
mergeObject(context, { rdd: { ready: true } });
const ActorConstructor = game.system.rdd.actorClasses[docData.type];
if (ActorConstructor) {
if (!docData.img) {
docData.img = ActorConstructor.defaultIcon;
}
return new ActorConstructor(docData, context);
}
}
super(docData, context);
}
isCreatureEntite() { return this.type == 'creature' || this.type == 'entite'; }
isCreature() { return this.type == 'creature'; }
isEntite() { return this.type == 'entite'; }
isPersonnage() { return this.type == 'personnage'; }
isVehicule() { return this.type == 'vehicule'; }
getItem(id, type = undefined) {
const item = this.items.get(id);
if (type == undefined || (item?.type == type)) {
return item;
}
return undefined;
}
listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
filterItems(filter, type = undefined) { return this.listItems(type)?.filter(filter) ?? []; }
findItemLike(idOrName, type) {
return this.getItem(idOrName, type)
?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
}
getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
recompute() { }
/* -------------------------------------------- */
async onPreUpdateItem(item, change, options, id) { }
async onCreateItem(item, options, id) { }
async onDeleteItem(item, options, id) { }
async onUpdateActor(update, options, actorId) { }
async onTimeChanging(oldTimestamp, newTimestamp) {
this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
/* -------------------------------------------- */
getFortune() {
return Monnaie.getFortune(this.itemTypes['monnaie']);
}
/* -------------------------------------------- */
async monnaieIncDec(id, value) {
let monnaie = this.getMonnaie(id);
if (monnaie) {
const quantite = Math.max(0, monnaie.system.quantite + value);
await this.updateEmbeddedDocuments('Item', [{ _id: monnaie.id, 'system.quantite': quantite }]);
}
}
computePrixTotalEquipement() {
return this.items.filter(it => it.isInventaire())
.filter(it => !it.isMonnaie())
.map(it => it.valeurTotale())
.reduce(Misc.sum(), 0);
}
async payerSols(depense) {
depense = Number(depense);
if (depense == 0) {
return;
}
let fortune = this.getFortune();
console.log("payer", game.user.character, depense, fortune);
let msg = "";
if (fortune >= depense) {
await Monnaie.optimiserFortune(this, fortune - depense);
msg = `Vous avez payé <strong>${depense} Sols</strong>, qui ont été soustraits de votre argent.`;
RdDAudio.PlayContextAudio("argent"); // Petit son
} else {
msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
}
let message = {
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: msg
};
ChatMessage.create(message);
}
async depenserSols(sols) {
let reste = this.getFortune() - Number(sols);
if (reste >= 0) {
await Monnaie.optimiserFortune(this, reste);
}
return reste;
}
async ajouterSols(sols, fromActorId = undefined) {
sols = Number(sols);
if (sols == 0) {
return;
}
if (sols < 0) {
ui.notifications.error(`Impossible d'ajouter un gain de ${sols} <0`);
return;
}
if (fromActorId && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(),
actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId]
});
}
else {
const fromActor = game.actors.get(fromActorId)
await Monnaie.optimiserFortune(this, sols + this.getFortune());
RdDAudio.PlayContextAudio("argent"); // Petit son
ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
});
}
}
/* -------------------------------------------- */
getQuantiteDisponible(item) {
return item?.isService() ? undefined : item?.getQuantite();
}
/* -------------------------------------------- */
async achatVente(achat) {
if (achat.vendeurId == achat.acheteurId) {
ui.notifications.info("Inutile de se vendre à soi-même");
return;
}
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
actorId: achat.vendeurId ?? achat.acheteurId,
method: 'achatVente',
args: [achat]
});
return;
}
const cout = Number(achat.prixTotal ?? 0);
const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
const vente = achat.vente;
const quantite = (achat.choix.nombreLots ?? 1) * (vente.tailleLot);
const itemVendu = vendeur?.getItem(vente.item._id);
if (!this.verifierQuantite(vendeur, itemVendu, quantite)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
return
}
if (acheteur && !acheteur.verifierFortune(cout)) {
ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
return;
}
await this.decrementerVente(vendeur, itemVendu, quantite, cout);
if (acheteur) {
await acheteur.depenserSols(cout);
const createdItemId = await acheteur.creerQuantiteItem(vente.item, quantite);
await acheteur.consommerNourritureAchetee(achat, vente, createdItemId);
}
if (cout > 0) {
RdDAudio.PlayContextAudio("argent");
}
const chatAchatItem = duplicate(vente);
chatAchatItem.quantiteTotal = quantite;
ChatMessage.create({
user: achat.userId,
speaker: { alias: (acheteur ?? vendeur).name },
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
});
if (!vente.quantiteIllimite) {
if (vente.quantiteNbLots <= achat.choix.nombreLots) {
ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
}
else if (achat.chatMessageIdVente) {
vente["properties"] = itemVendu.getProprietes();
vente.quantiteNbLots -= achat.choix.nombreLots;
vente.jsondata = JSON.stringify(vente.item);
const messageVente = game.messages.get(achat.chatMessageIdVente);
messageVente.update({ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-vente-item.html', vente) });
messageVente.render(true);
}
}
}
async decrementerVente(vendeur, itemVendu, quantite, cout) {
if (vendeur) {
await vendeur.ajouterSols(cout);
await vendeur.decrementerQuantiteItem(itemVendu, quantite);
}
}
verifierFortune(cout) {
return this.getFortune() >= cout;
}
verifierQuantite(vendeur, item, quantiteTotal) {
const disponible = vendeur?.getQuantiteDisponible(item);
return disponible == undefined || disponible >= quantiteTotal;
}
async consommerNourritureAchetee(achat, vente, createdItemId) {
if (achat.choix.consommer && vente.item.type == 'nourritureboisson' && createdItemId != undefined) {
achat.choix.doses = achat.choix.nombreLots;
await this.consommerNourritureboisson(createdItemId, achat.choix, vente.actingUserId);
}
}
async decrementerQuantiteItem(item, quantite, options = { supprimerSiZero: true }) {
if (item.isService()) {
return;
}
let resteQuantite = (item.system.quantite ?? 1) - quantite;
if (resteQuantite <= 0) {
if (options.supprimerSiZero) {
await this.deleteEmbeddedDocuments("Item", [item.id]);
}
else {
await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': 0 }]);
}
if (resteQuantite < 0) {
ui.notifications.warn(`La quantité de ${item.name} était insuffisante, l'objet a donc été supprimé`)
}
}
else if (resteQuantite > 0) {
await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': resteQuantite }]);
}
}
async creerQuantiteItem(item, quantite) {
if (this.canReceive(item)) {
const isItemEmpilable = "quantite" in item.system;
const baseItem = {
type: item.type,
img: item.img,
name: item.name,
system: mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined })
};
const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
const items = await this.createEmbeddedDocuments("Item", newItems);
return items.length > 0 ? items[0].id : undefined;
}
}
/* -------------------------------------------- */
computeMalusSurEncombrement() {
return 0;
}
getEncombrementMax() {
return 0;
}
async computeEncTotal() {
if (!this.pack) {
this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);
return this.encTotal;
}
return 0;
}
async createItem(type, name = undefined) {
if (!name) {
name = 'Nouveau ' + Misc.typeName('Item', type);
}
await this.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
}
canReceive(item) {
return false;
}
async processDropItem(params) {
const targetActorId = this.id;
const sourceActorId = params.sourceActorId;
const itemId = params.itemId;
const destId = params.destId;
const srcId = params.srcId;
if (sourceActorId && sourceActorId != targetActorId) {
console.log("Moving objects", sourceActorId, targetActorId, itemId);
this.moveItemsBetweenActors(itemId, sourceActorId);
return false;
}
let result = true;
const item = this.getItem(itemId);
if (item?.isInventaire('all') && sourceActorId == targetActorId) {
// rangement
if (srcId != destId && itemId != destId) { // déplacement de l'objet
const src = this.getItem(srcId);
const dest = this.getItem(destId);
const cible = this.getContenantOrParent(dest);
const [empilable, message] = item.isInventaireEmpilable(dest);
if (empilable) {
await dest.empiler(item)
result = false;
}
// changer de conteneur
else if (!cible || this.conteneurPeutContenir(cible, item)) {
await this.enleverDeConteneur(item, src, params.onEnleverConteneur);
await this.ajouterDansConteneur(item, cible, params.onAjouterDansConteneur);
if (message && !dest.isConteneur()) {
ui.notifications.info(cible
? `${message}<br>${item.name} a été déplacé dans: ${cible.name}`
: `${message}<br>${item.name} a été sorti du conteneur`);
}
}
}
}
await this.computeEncTotal();
return result;
}
getContenantOrParent(dest) {
if (!dest || dest.isConteneur()) {
return dest;
}
return this.getContenant(dest);
}
getContenant(item) {
return this.itemTypes['conteneur'].find(it => it.system.contenu.includes(item.id));
}
/* -------------------------------------------- */
conteneurPeutContenir(dest, item) {
if (!dest) {
return true;
}
if (!dest.isConteneur()) {
return false;
}
const destData = dest
if (this._isConteneurContenu(item, dest)) {
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${item.name}) dans un de ses contenus ${destData.name} !`);
return false; // Loop detected !
}
// Calculer le total actuel des contenus
let encContenu = this.getRecursiveEnc(dest) - Number(destData.system.encombrement);
let newEnc = this.getRecursiveEnc(item); // Calculer le total actuel du nouvel objet
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
if (Number(destData.system.capacite) < encContenu + newEnc) {
ui.notifications.warn(
`Le conteneur ${dest.name} a une capacité de ${destData.system.capacite}, et contient déjà ${encContenu}.
Impossible d'y ranger: ${item.name} d'encombrement ${newEnc}!`);
return false;
}
return true;
}
/* -------------------------------------------- */
_isConteneurContenu(item, conteneur) {
if (item?.isConteneur()) { // Si c'est un conteneur, il faut vérifier qu'on ne le déplace pas vers un sous-conteneur lui appartenant
for (let id of item.system.contenu) {
let subObjet = this.getItem(id);
if (subObjet?.id == conteneur.id) {
return true; // Loop detected !
}
if (subObjet?.isConteneur()) {
return this._isConteneurContenu(subObjet, conteneur);
}
}
}
return false;
}
/* -------------------------------------------- */
getRecursiveEnc(objet) {
if (!objet) {
return 0;
}
const tplData = objet.system;
if (objet.type != 'conteneur') {
return Number(tplData.encombrement) * Number(tplData.quantite);
}
const encContenus = tplData.contenu.map(idContenu => this.getRecursiveEnc(this.getItem(idContenu)));
return encContenus.reduce(Misc.sum(), 0)
+ Number(tplData.encombrement) /* TODO? Number(tplData.quantite) -- on pourrait avoir plusieurs conteneurs...*/
}
/* -------------------------------------------- */
/** Ajoute un item dans un conteneur, sur la base
* de leurs ID */
async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) {
if (!conteneur) {
// TODO: afficher
item.estContenu = false;
}
else if (conteneur.isConteneur()) {
item.estContenu = true;
await this.updateEmbeddedDocuments('Item', [{
_id: conteneur.id,
'system.contenu': [...conteneur.system.contenu, item.id]
}]);
onAjouterDansConteneur(item.id, conteneur.id);
}
}
/* -------------------------------------------- */
/** Fonction de remise à plat de l'équipement (ie vide les champs 'contenu') */
async nettoyerConteneurs() {
RdDConfirm.confirmer({
settingConfirmer: "confirmation-vider",
content: `<p>Etes vous certain de vouloir vider tous les conteneurs ?</p>`,
title: 'Vider les conteneurs',
buttonLabel: 'Vider',
onAction: async () => {
const corrections = [];
for (let item of this.items) {
if (item.estContenu) {
item.estContenu = undefined;
}
if (item.type == 'conteneur' && item.system.contenu.length > 0) {
corrections.push({ _id: item.id, 'system.contenu': [] });
}
}
if (corrections.length > 0) {
await this.updateEmbeddedDocuments('Item', corrections);
}
}
});
}
/* -------------------------------------------- */
buildSubConteneurObjetList(conteneurId, deleteList) {
let conteneur = this.getItem(conteneurId);
if (conteneur?.type == 'conteneur') { // Si c'est un conteneur
for (let subId of conteneur.system.contenu) {
let subObj = this.getItem(subId);
if (subObj) {
if (subObj.type == 'conteneur') {
this.buildSubConteneurObjetList(subId, deleteList);
}
deleteList.push({ id: subId, conteneurId: conteneurId });
}
}
}
}
/* -------------------------------------------- */
async deleteAllConteneur(itemId, options) {
let list = [];
list.push({ id: itemId, conteneurId: undefined }); // Init list
this.buildSubConteneurObjetList(itemId, list);
await this.deleteEmbeddedDocuments('Item', list.map(it => it.id), options);
}
/* -------------------------------------------- */
/** Supprime un item d'un conteneur, sur la base
* de leurs ID */
async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) {
if (conteneur?.isConteneur()) {
item.estContenu = false;
await this.updateEmbeddedDocuments('Item', [{
_id: conteneur.id,
'system.contenu': conteneur.system.contenu.filter(id => id != item.id)
}]);
onEnleverDeConteneur();
}
}
/* -------------------------------------------- */
async moveItemsBetweenActors(itemId, sourceActorId) {
let itemsList = []
let sourceActor = game.actors.get(sourceActorId);
itemsList.push({ id: itemId, conteneurId: undefined }); // Init list
sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list
const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
.map(it => duplicate(it))
.map(it => { it.system.contenu = []; return it; });
let newItems = await this.createEmbeddedDocuments('Item', itemsDataToCreate);
let itemMap = this._buildMapOldNewId(itemsList, newItems);
for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs
// gestion conteneur/contenu
if (item.conteneurId) { // l'Objet était dans un conteneur
let newConteneurId = itemMap[item.conteneurId]; // Get conteneur
let newConteneur = this.getItem(newConteneurId);
let newItemId = itemMap[item.id]; // Get newItem
console.log('New conteneur filling!', newConteneur, newItemId, item);
let contenu = duplicate(newConteneur.system.contenu);
contenu.push(newItemId);
await this.updateEmbeddedDocuments('Item', [{ _id: newConteneurId, 'system.contenu': contenu }]);
}
}
for (let item of itemsList) {
await sourceActor.deleteEmbeddedDocuments('Item', [item.id]);
}
}
_buildMapOldNewId(itemsList, newItems) {
let itemMap = {};
for (let i = 0; i < itemsList.length; i++) {
itemMap[itemsList[i].id] = newItems[i].id; // Pour garder le lien ancien / nouveau
}
return itemMap;
}
/* -------------------------------------------- */
async postActorToChat(modeOverride) {
let chatData = {
doctype: 'Actor',
id: this.id,
type: this.type,
img: this.img,
pack: this.pack,
name: this.name,
system: { description: this.system.description }
}
renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.html', chatData)
.then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
}
}

View File

@ -1,95 +0,0 @@
import { DialogItemAchat } from "../dialog-item-achat.js";
import { RdDItem } from "../item.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDBaseActorSheet } from "./base-actor-sheet.js";
import { RdDCommerce } from "./commerce.js";
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDCommerceSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-actor-sheet.html",
width: 600,
height: 720,
tabs: [],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }]
});
}
get title() {
if (this.actor.token && this.actor.token != this.actor.prototypeToken) {
return this.actor.token.name;
}
return super.title
}
async getData() {
const formData = await super.getData();
if (this.actor.token && this.actor.token != this.actor.prototypeToken) {
mergeObject(formData,
{
title: this.actor.token.name,
token: {
img: this.actor.token.texture.src
}
},
{ overwrite: true });
}
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
this.html.find('a.item-acheter').click(async event => await this.vente(this.getItem(event)));
if (!this.options.editable) return;
this.html.find('a.item-quantite-moins').click(async event => await this.getItem(event)?.quantiteIncDec(-1, { supprimerSiZero: false }));
this.html.find('a.item-quantite-plus').click(async event => await this.getItem(event)?.quantiteIncDec(1));
this.html.find('input.item-quantite').change(async event => {
const newQuantite = Math.max(0, Number.parseInt(this.html.find(event.currentTarget).val()));
await this.getItem(event)?.update({ "system.quantite": newQuantite });
})
this.html.find('input.item-cout').change(async event => {
const newCout = Math.max(0, Number(this.html.find(event.currentTarget).val()));
await this.getItem(event)?.update({ "system.cout": newCout });
})
}
getTypesInventaire() {
return RdDItem.getItemTypesInventaire('all');
}
async vente(item) {
const acheteur = RdDUtility.getSelectedActor();
if (!acheteur) {
ui.notifications.warn(`Pas d'acheteur sélectionné`);
return;
}
const disponible = this.actor.getQuantiteDisponible(item)
if (disponible == 0) {
ui.notifications.warn(`${this.name} n'a plus de ${item.name} en vente`);
return;
}
await DialogItemAchat.onAcheter({
item,
vendeur: this.actor,
acheteur,
quantiteIllimite: disponible == undefined,
nbLots: disponible ?? 1,
tailleLot: 1,
prixLot: item.calculerPrixCommercant()
});
}
}

View File

@ -1,53 +0,0 @@
import { Misc } from "../misc.js";
import { RdDBaseActor } from "./base-actor.js";
export class RdDCommerce extends RdDBaseActor {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/services/commerce.webp";
}
prepareData() {
super.prepareData();
}
prepareDerivedData() {
super.prepareDerivedData();
}
canReceive(item) {
if (item.isInventaire('all')) {
return true;
}
return super.canReceive(item);
}
getQuantiteDisponible(item) {
return this.system.illimite || item.isService() ? undefined : item.getQuantite();
}
verifierFortune(cout) {
return this.system.illimite || super.verifierFortune(cout);
}
async depenserSols(cout) {
if (this.system.illimite) {
return
}
await super.depenserSols(cout)
}
async consommerNourritureAchetee(achat, vente, createdItemId) {
// ne pas consommer pour un commerce
}
async decrementerQuantiteItem(item, quantite) {
if (this.system.illimite) {
return;
}
await super.decrementerQuantiteItem(item, quantite, { supprimerSiZero: false });
}
calculerPrix(item) {
const pourcentage = this.system.pourcentage ?? 100;
return Misc.keepDecimals(Math.ceil(item.system.cout * pourcentage) / 100, 2);
}
}

View File

@ -1,153 +0,0 @@
import { SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
const LATEST_USED_JOURNAL_ID = "chronologie-dernier-journal";
export class DialogChronologie extends Dialog {
static init() {
game.settings.register(SYSTEM_RDD, LATEST_USED_JOURNAL_ID, {
name: "Dernier article de journal utilisé pour enregistrer la chronologie",
scope: "client",
config: false,
default: "",
type: String
});
}
static async create() {
const dialogData = {
auteur: game.user.name,
isGM: game.user.isGM,
information: "",
journalId: game.settings.get(SYSTEM_RDD, LATEST_USED_JOURNAL_ID),
journaux: game.journal.filter(it => it.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)),
timestamp: game.system.rdd.calendrier.timestamp,
dateReel: DialogChronologie.getCurrentDateTime()
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-chronologie.html", dialogData);
const dialog = new DialogChronologie(html, dialogData);
dialog.render(true);
}
static getCurrentDateTime() {
return new Date().toLocaleString("sv-SE", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit"
}).replace(" ", "T");
}
constructor(html, dialogData) {
const options = {
classes: ["DialogChronologie"],
width: 500,
height: 'fit-content',
'z-index': 99999
};
const timeData = dialogData.timestamp.toCalendrier()
const conf = {
title: `Chronologie - ${timeData.jourDuMois} ${timeData.mois.label} - Heure ${timeData.heure.label}`,
content: html,
buttons: {
}
};
super(conf, options);
this.dialogData = dialogData;
}
activateListeners(html) {
this.html = html;
super.activateListeners(html);
const journalPrecedent = game.journal.get(this.dialogData.journalId);
this.showChronologiePreset(!(journalPrecedent?.canUserModify(game.user)))
this.html.find("a.chronologie-preset-show").click(event => this.showChronologiePreset(true));
this.html.find("a.chronologie-preset-hide").click(event => this.showChronologiePreset(false));
this.html.find("button.chronologie-ajouter").click(event => this.ajouter());
}
showChronologiePreset(showPreset) {
HtmlUtility.showControlWhen(this.html.find(".chronologie-preset-show"), !showPreset);
HtmlUtility.showControlWhen(this.html.find(".chronologie-preset-hide"), showPreset);
HtmlUtility.showControlWhen(this.html.find(".chronologie-preset"), showPreset);
}
async ajouter() {
await this.forceValidation();
const { journalId, journalEntry } = this.findJournal();
if (journalEntry?.canUserModify(game.user)) {
const journalParameters = this.extractJournalParameters();
const jour = journalParameters.dateRdD.jour;
const mois = journalParameters.dateRdD.mois.label;
const annee = journalParameters.dateRdD.annee;
const section = `${jour} ${mois} ${annee}`
const content = await this.prepareChronologieEntry(journalParameters);
// ajouter à la page ou créer une page
this.addContentToJournal(journalEntry, section, content);
this.storeLatestUsedJournalEntry(journalId);
this.close();
}
else {
const journal = this.html.find("form.rdddialogchrono select[name='journalId']").val();
ui.notifications.warn(`Le journal ${journal} n'est pas accessible`);
}
}
async forceValidation() {
await this.html.find("form.rdddialogchrono :input").change();
}
findJournal() {
const journalId = this.html.find("form.rdddialogchrono :input[name='journalId']").val();
const journalEntry = game.journal.get(journalId);
return { journalId, journalEntry };
}
async prepareChronologieEntry(journalParameters) {
return await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/chronologie-entry.html", journalParameters);
}
extractJournalParameters() {
return {
auteur: this.html.find("form.rdddialogchrono :input[name='auteur']").val(),
information: this.html.find("form.rdddialogchrono :input[name='information']").val(),
dateRdD: {
jour: this.html.find("form.rdddialogchrono :input[name='chronologie.jourDuMois']").val(),
mois: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.mois']").val()),
annee: this.html.find("form.rdddialogchrono :input[name='chronologie.annee']").val(),
heure: RdDTimestamp.definition(this.html.find("form.rdddialogchrono :input[name='chronologie.heure']").val()),
minute: this.html.find("form.rdddialogchrono :input[name='chronologie.minute']").val(),
},
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
}
}
addContentToJournal(journalEntry, section, content) {
let page = journalEntry.pages.find(p => p.type == 'text' && Grammar.equalsInsensitive(p.name, section));
if (page) {
page.update({ 'text.content': page.text.content + '\n' + content });
}
else {
journalEntry.createEmbeddedDocuments('JournalEntryPage', [this.newPageChronologie(section, content)]);
}
}
newPageChronologie(section, content) {
return new JournalEntryPage({
name: section,
type: 'text',
title: { show: true, level: 1 },
text: { content: content, format: 1 }
});
}
storeLatestUsedJournalEntry(journalId) {
game.settings.set(SYSTEM_RDD, LATEST_USED_JOURNAL_ID, journalId);
}
}

View File

@ -1,6 +1,6 @@
import { ChatUtility } from "./chat-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemSigneDraconique } from "./item/signedraconique.js";
import { RdDItemSigneDraconique } from "./item-signedraconique.js";
import { TMRUtility } from "./tmr-utility.js";
export class DialogCreateSigneDraconique extends Dialog {
@ -35,16 +35,16 @@ export class DialogCreateSigneDraconique extends Dialog {
super(conf, options);
this.dialogData = dialogData;
}
async _onCreerSigneActeurs() {
await this.html.find("[name='signe.system.ephemere']").change();
await this.html.find(".signe-xp-sort").change();
await $("[name='signe.system.ephemere']").change();
await $(".signe-xp-sort").change();
this.validerSigne();
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.forEach(actor => this._createSigneForActor(actor, this.dialogData.signe));
.map(it => game.actors.get(it.id))
.forEach(actor => this._createSigneForActor(actor, this.dialogData.signe));
}
async _createSigneForActor(actor, signe) {
actor.createEmbeddedDocuments("Item", [signe]);
ChatMessage.create({
@ -57,20 +57,19 @@ export class DialogCreateSigneDraconique extends Dialog {
}
validerSigne() {
this.dialogData.signe.name = this.html.find("[name='signe.name']").val();
this.dialogData.signe.system.valeur.norm = this.html.find("[name='signe.system.valeur.norm']").val();
this.dialogData.signe.system.valeur.sign = this.html.find("[name='signe.system.valeur.sign']").val();
this.dialogData.signe.system.valeur.part = this.html.find("[name='signe.system.valeur.part']").val();
this.dialogData.signe.system.difficulte = this.html.find("[name='signe.system.difficulte']").val();
this.dialogData.signe.system.ephemere = this.html.find("[name='signe.system.ephemere']").prop("checked");
this.dialogData.signe.system.duree = this.html.find("[name='signe.system.duree']").val();
this.dialogData.signe.name = $("[name='signe.name']").val();
this.dialogData.signe.system.valeur.norm = $("[name='signe.system.valeur.norm']").val();
this.dialogData.signe.system.valeur.sign = $("[name='signe.system.valeur.sign']").val();
this.dialogData.signe.system.valeur.part = $("[name='signe.system.valeur.part']").val();
this.dialogData.signe.system.difficulte = $("[name='signe.system.difficulte']").val();
this.dialogData.signe.system.ephemere = $("[name='signe.system.ephemere']").prop("checked");
this.dialogData.signe.system.duree = $("[name='signe.system.duree']").val();
this.dialogData.signe.system.typesTMR = TMRUtility.buildListTypesTMRSelection(this.dialogData.tmrs);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.setEphemere(this.dialogData.signe.system.ephemere);
html.find(".signe-aleatoire").click(event => this.setSigneAleatoire());
html.find("[name='signe.system.ephemere']").change((event) => this.setEphemere(event.currentTarget.checked));
@ -82,27 +81,27 @@ export class DialogCreateSigneDraconique extends Dialog {
async setSigneAleatoire() {
const newSigne = await RdDItemSigneDraconique.randomSigneDraconique({ephemere: true});
this.html.find("[name='signe.name']").val(newSigne.name);
this.html.find("[name='signe.system.valeur.norm']").val(newSigne.system.valeur.norm);
this.html.find("[name='signe.system.valeur.sign']").val(newSigne.system.valeur.sign);
this.html.find("[name='signe.system.valeur.part']").val(newSigne.system.valeur.part);
this.html.find("[name='signe.system.difficulte']").val(newSigne.system.difficulte);
this.html.find("[name='signe.system.duree']").val(newSigne.system.duree);
this.html.find("[name='signe.system.ephemere']").prop("checked", newSigne.system.ephemere);
$("[name='signe.name']").val(newSigne.name);
$("[name='signe.system.valeur.norm']").val(newSigne.system.valeur.norm);
$("[name='signe.system.valeur.sign']").val(newSigne.system.valeur.sign);
$("[name='signe.system.valeur.part']").val(newSigne.system.valeur.part);
$("[name='signe.system.difficulte']").val(newSigne.system.difficulte);
$("[name='signe.system.duree']").val(newSigne.system.duree);
$("[name='signe.system.ephemere']").prop("checked", newSigne.system.ephemere);
this.dialogData.tmrs = TMRUtility.buildSelectionTypesTMR(newSigne.system.typesTMR);
this.dialogData.tmrs.forEach(t => {
this.html.find(`[data-tmr-name='${t.name}']`).prop( "checked", t.selected);
$(`[data-tmr-name='${t.name}']`).prop( "checked", t.selected);
})
this.setEphemere(newSigne.system.ephemere);
}
async setEphemere(ephemere) {
this.dialogData.signe.system.ephemere = ephemere;
HtmlUtility.showControlWhen(this.html.find(".signe-system-duree"), ephemere);
HtmlUtility._showControlWhen($(".signe-system-duree"), ephemere);
}
async onSelectActor(event) {
const actorId = this.html.find(event.currentTarget)?.data("actor-id");
const actorId = $(event.currentTarget)?.data("actor-id");
const actor = this.dialogData.actors.find(it => it.id == actorId);
if (actor) {
actor.selected = event.currentTarget.checked;
@ -110,7 +109,7 @@ export class DialogCreateSigneDraconique extends Dialog {
}
onSelectTmr(event) {
const tmrName = this.html.find(event.currentTarget)?.data("tmr-name");
const tmrName = $(event.currentTarget)?.data("tmr-name");
const onTmr = this.tmrs.find(it => it.name == tmrName);
if (onTmr){
onTmr.selected = event.currentTarget.checked;

View File

@ -13,12 +13,18 @@ export class DialogFabriquerPotion extends Dialog {
}
let potionData = DialogFabriquerPotion.prepareData(actor, item);
const html = await renderTemplate(dialogConfig.html, potionData);
let conf = {
title: `Fabriquer une potion de ${potionData.system.categorie}`,
content: await renderTemplate(dialogConfig.html, potionData),
default: potionData.buttonName,
};
let options = { classes: ["dialogfabriquerpotion"], width: 600, height: 160, 'z-index': 99999 };
mergeObject(options, dialogConfig.options ?? {}, { overwrite: true })
new DialogFabriquerPotion(actor, potionData, html, options).render(true);
const dialog = new DialogFabriquerPotion(actor, potionData, conf, options);
dialog.render(true);
return dialog;
}
/* -------------------------------------------- */
@ -34,15 +40,10 @@ export class DialogFabriquerPotion extends Dialog {
}
/* -------------------------------------------- */
constructor(actor, potionData, html, options) {
const conf = {
title: `Fabriquer une potion de ${potionData.system.categorie}`,
content: html,
default: 'fabriquer',
buttons: {
'fabriquer': {
label: potionData.buttonName, callback: it => this.onFabriquer(html)
}
constructor(actor, potionData, conf, options) {
conf.buttons = {
[potionData.buttonName]: {
label: potionData.buttonName, callback: it => this.onFabriquer(it)
}
};
@ -52,24 +53,6 @@ export class DialogFabriquerPotion extends Dialog {
this.potionData = potionData;
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("[name='nbBrins']").change(event => {
this.potionData.nbBrins = Misc.toInt(event.currentTarget.value);
const brinsManquants = Math.max(0, DialogFabriquerPotion.nombreBrinsOptimal(this.potionData) - this.potionData.nbBrins);
this.potionData.herbebonus = Math.max(0, this.potionData.system.niveau - brinsManquants)
});
}
/* -------------------------------------------- */
async onFabriquer(html) {
await this.html.find("[name='nbBrins']").change();
this.actor.fabriquerPotion(this.potionData);
this.close();
}
static nombreBrinsMinimum(herbeData) {
switch (herbeData.system.categorie ?? '') {
case "Soin": return 1 + Math.max(0, 12 - 2 * herbeData.system.niveau);
@ -85,4 +68,22 @@ export class DialogFabriquerPotion extends Dialog {
}
return 1;
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find("#nbBrins").change(event => {
this.potionData.nbBrins = Misc.toInt(event.currentTarget.value);
const brinsManquants = Math.max(0, DialogFabriquerPotion.nombreBrinsOptimal(this.potionData) - this.potionData.nbBrins);
this.potionData.herbebonus = Math.max(0, this.potionData.system.niveau - brinsManquants)
});
}
/* -------------------------------------------- */
async onFabriquer(it) {
await $("#nbBrins").change();
this.actor.fabriquerPotion(this.potionData);
this.close();
}
}

View File

@ -1,13 +1,15 @@
import { Monnaie } from "./item-monnaie.js";
import { Misc } from "./misc.js";
import { RdDUtility } from "./rdd-utility.js";
export class DialogItemAchat extends Dialog {
static preparerAchat(chatButton) {
const vendeurId = chatButton.attributes['data-vendeurId']?.value;
static venteData(button) {
const vendeurId = button.attributes['data-vendeurId']?.value;
const vendeur = vendeurId ? game.actors.get(vendeurId) : undefined;
const acheteur = RdDUtility.getSelectedActor();
const json = chatButton.attributes['data-jsondata']?.value;
const json = button.attributes['data-jsondata']?.value;
if (!acheteur && !vendeur) {
ui.notifications.info("Pas d'acheteur ni de vendeur, aucun changement");
return undefined;
@ -17,86 +19,64 @@ export class DialogItemAchat extends Dialog {
return undefined;
}
const prixLot = Monnaie.arrondiDeniers(button.attributes['data-prixLot']?.value ?? 0);
return {
item: (json ? JSON.parse(json) : undefined),
vendeur,
acheteur,
nbLots: parseInt(chatButton.attributes['data-quantiteNbLots']?.value),
tailleLot: parseInt(chatButton.attributes['data-tailleLot']?.value ?? 1),
prixLot: Number(chatButton.attributes['data-prixLot']?.value ?? 0),
quantiteIllimite: chatButton.attributes['data-quantiteIllimite']?.value == 'true',
chatMessageIdVente: RdDUtility.findChatMessageId(chatButton),
};
}
static async onAcheter({ item, vendeur, acheteur, tailleLot, prixLot, nbLots, quantiteIllimite, chatMessageIdVente }) {
const venteData = {
item,
actingUserId: game.user.id,
vendeurId: vendeur?.id,
vendeur,
acheteur,
tailleLot,
quantiteIllimite,
quantiteNbLots: nbLots,
choix: { seForcer: false, supprimerSiZero: true },
prixLot,
item: json ? JSON.parse(json) : undefined,
vendeurId: vendeurId,
vendeur: vendeur,
acheteur: acheteur,
tailleLot: parseInt(button.attributes['data-tailleLot']?.value ?? 1),
quantiteIllimite: button.attributes['data-quantiteIllimite']?.value == 'true',
quantiteNbLots: parseInt(button.attributes['data-quantiteNbLots']?.value),
choix: {
nombreLots: 1,
seForcer: false,
supprimerSiZero: true
},
prixLot: prixLot,
prixTotal: prixLot,
isVente: prixLot > 0,
isConsommable: item.type == 'nourritureboisson' && acheteur?.isPersonnage(),
chatMessageIdVente
chatMessageIdVente: RdDUtility.findChatMessageId(button)
};
DialogItemAchat.changeNombreLots(venteData, 1);
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData);
new DialogItemAchat(html, venteData).render(true);
}
static changeNombreLots(venteData, nombreLots) {
venteData.choix.nombreLots = nombreLots;
venteData.prixTotal = (nombreLots * venteData.prixLot).toFixed(2);
if (venteData.isConsommable) {
const doses = nombreLots * venteData.tailleLot;
venteData.totalSust = Misc.keepDecimals(doses * (venteData.item.system.sust ?? 0), 2);
venteData.totalDesaltere = venteData.item.system.boisson
? Misc.keepDecimals(doses * (venteData.item.system.desaltere ?? 0), 2)
: 0;
}
static async onAcheter(venteData) {
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-achat.html`, venteData);
const dialog = new DialogItemAchat(html, venteData);
dialog.render(true);
}
constructor(html, venteData) {
let options = { classes: ["dialogachat"], width: 400, height: 'fit-content', 'z-index': 99999 };
const isConsommable = venteData.item.type == 'nourritureboisson' && venteData.acheteur?.isPersonnage();
let options = { classes: ["dialogachat"], width: 400, height: isConsommable ? 450 : 350, 'z-index': 99999 };
const actionAchat = venteData.prixLot > 0 ? "Acheter" : "Prendre";
const buttons = {};
if (venteData.isConsommable) {
buttons["consommer"] = { label: venteData.item.system.boisson ? "Boire" : "Manger", callback: it => this.onAchatConsommer() }
if (isConsommable) {
buttons["consommer"] = { label: venteData.item.system.boisson ? "Boire" : "Manger", callback: it => { this.onAchatConsommer(); } }
}
buttons[actionAchat] = { label: actionAchat, callback: it => { this.onAchat(); } };
buttons["decliner"] = { label: "Décliner", callback: it => { } };
const acheteur = venteData.acheteur?.name ?? 'Un acheteur';
const vendeur = venteData.vendeur?.name ?? 'Un vendeur';
let conf = {
title: `${acheteur} - ${actionAchat} à ${vendeur}`,
title: venteData.acheteur ? venteData.acheteur.name + " - " + actionAchat : actionAchat,
content: html,
default: actionAchat,
buttons: buttons
};
super(conf, options);
this.venteData = venteData;
}
async onAchat() {
await this.html.find(".nombreLots").change();
await $(".nombreLots").change();
(this.venteData.vendeur ?? this.venteData.acheteur).achatVente({
userId: game.user.id,
vendeurId: this.venteData.vendeur?.id,
acheteurId: this.venteData.acheteur?.id,
prixTotal: this.venteData.prixTotal,
chatMessageIdVente: this.venteData.chatMessageIdVente,
choix: this.venteData.choix,
vente: this.venteData
choix: this.venteData.choix
});
}
@ -108,9 +88,9 @@ export class DialogItemAchat extends Dialog {
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(".nombreLots").change(event => this.setNombreLots(Number(event.currentTarget.value)));
this.html.find(".se-forcer").change(event => this.setSeForcer(event));
html.find(".nombreLots").change(event => this.setNombreLots(Number(event.currentTarget.value)));
html.find(".se-forcer").change(event => this.setSeForcer(event));
}
setSeForcer(event) {
@ -118,21 +98,13 @@ export class DialogItemAchat extends Dialog {
}
setNombreLots(nombreLots) {
if (!this.venteData.quantiteIllimite) {
if (!this.venteData.quantiteIllimite && nombreLots > this.venteData.quantiteNbLots) {
ui.notifications.warn(`Seulement ${this.venteData.quantiteNbLots} lots disponibles, vous ne pouvez pas en prendre ${nombreLots}`)
}
nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
if (nombreLots > this.venteData.quantiteNbLots) {
ui.notifications.warn(`Seulement ${this.venteData.quantiteNbLots} lots disponibles, vous ne pouvez pas en prendre ${nombreLots}`)
}
DialogItemAchat.changeNombreLots(this.venteData, nombreLots);
this.html.find(".nombreLots").val(nombreLots);
this.html.find(".prixTotal").text(this.venteData.prixTotal);
this.html.find("span.total-sust").text(this.venteData.totalSust);
this.html.find("span.total-desaltere").text(this.venteData.totalDesaltere);
this.venteData.choix.nombreLots = Math.min(nombreLots, this.venteData.quantiteNbLots);
this.venteData.prixTotal = (nombreLots * this.venteData.prixLot).toFixed(2);
$(".nombreLots").val(this.venteData.choix.nombreLots);
$(".prixTotal").text(this.venteData.prixTotal);
}
}

View File

@ -2,14 +2,14 @@ import { Misc } from "./misc.js";
export class DialogConsommer extends Dialog {
static async create(actor, item, onActionItem = async () => { }) {
static async create(actor, item, onActionItem = async ()=>{}) {
const consommerData = DialogConsommer.prepareData(actor, item);
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-item-consommer.html', consommerData);
return new DialogConsommer(actor, item, consommerData, html, onActionItem)
}
constructor(actor, item, consommerData, html, onActionItem = async () => { }) {
const options = { classes: ["dialogconsommer"], width: 350, height: 'fit-content', 'z-index': 99999 };
constructor(actor, item, consommerData, html, onActionItem = async ()=>{}) {
const options = { classes: ["dialogconsommer"], width: 350, height: 450, 'z-index': 99999 };
let conf = {
title: consommerData.title,
content: html,
@ -17,9 +17,8 @@ export class DialogConsommer extends Dialog {
buttons: {
[consommerData.buttonName]: {
label: consommerData.buttonName, callback: async it => {
await this.onConsommer();
await onActionItem();
}
await this.onConsommer(it);
await onActionItem();}
}
}
};
@ -31,23 +30,17 @@ export class DialogConsommer extends Dialog {
this.consommerData = consommerData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(".se-forcer").change(event => this.setSeForcer(event));
this.html.find(".consommer-doses").change(event => this.selectDoses(event));
}
async onConsommer() {
await this.html.find(".se-forcer").change();
await this.html.find(".consommer-doses").change();
async onConsommer(event) {
await $(".se-forcer").change();
await $(".consommer-doses").change();
await this.actor.consommer(this.item, this.consommerData.choix);
}
/* -------------------------------------------- */
static prepareData(actor, item) {
item = duplicate(item);
let consommerData = {
item: duplicate(item),
item: item,
cuisine: actor.getCompetence('cuisine'),
choix: {
doses: 1,
@ -55,47 +48,33 @@ export class DialogConsommer extends Dialog {
}
}
switch (item.type) {
case 'herbe': case 'faune':
consommerData.title = 'Manger une portion crue: ';
consommerData.buttonName = "Manger";
break;
case 'nourritureboisson':
consommerData.title = item.system.boisson ? 'Boire une dose: ' : 'Manger une portion: ';
consommerData.title = item.system.boisson ? `${item.name}: boire une dose` : `${item.name}: manger une portion`;
consommerData.buttonName = item.system.boisson ? "Boire" : "Manger";
break;
case 'potion':
consommerData.title = 'Boire la potion: ';
consommerData.title = `${item.name}: boire la potion`;
consommerData.buttonName = "Boire";
break;
}
consommerData.title += item.name;
DialogConsommer.calculDoses(consommerData, item)
DialogConsommer.calculDoses(consommerData, consommerData.choix.doses)
return consommerData;
}
static calculDoses(consommer, item) {
static calculDoses(consommer) {
const doses = consommer.choix.doses;
switch (item.type) {
case 'herbe': case 'faune':
consommer.totalSust = doses;
consommer.totalDesaltere = 0;
consommer.choix.sust = 1;
consommer.choix.quantite = 0;
consommer.choix.encombrement = Misc.keepDecimals(consommer.item.system.encombrement / item.system.sust, 2);
return;
case 'nourritureboisson':
consommer.choix.sust = consommer.item.system.sust;
consommer.choix.quantite = doses;
consommer.choix.encombrement = 0
consommer.totalSust = Misc.keepDecimals(doses * (consommer.item.system.sust ?? 0), 2);
consommer.totalDesaltere = consommer.item.system.boisson
? Misc.keepDecimals(doses * (consommer.item.system.desaltere ?? 0), 2)
: 0;
break;
case 'potion':
consommer.totalSust = 0
consommer.totalDesaltere = 0
}
consommer.totalSust = Misc.keepDecimals(doses * (consommer.item.system.sust ?? 0), 2);
consommer.totalDesaltere = consommer.item.system.boisson
? Misc.keepDecimals(doses * (consommer.item.system.desaltere ?? 0), 2)
: 0;
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find(".se-forcer").change(event => this.setSeForcer(event));
html.find(".consommer-doses").change(event => this.selectDoses(event));
}
@ -105,8 +84,8 @@ export class DialogConsommer extends Dialog {
selectDoses(event) {
this.consommerData.choix.doses = Number(event.currentTarget.value);
DialogConsommer.calculDoses(this.consommerData, this.item);
this.html.find(".total-sust").text(this.consommerData.totalSust);
this.html.find(".total-desaltere").text(this.consommerData.totalDesaltere);
DialogConsommer.calculDoses(this.consommerData);
$(".total-sust").text(this.consommerData.totalSust);
$(".total-desaltere").text(this.consommerData.totalDesaltere);
}
}

View File

@ -1,30 +1,30 @@
import { HtmlUtility } from "./html-utility.js";
import { Misc } from "./misc.js";
export class DialogItemVente extends Dialog {
static async display({ item, callback, quantiteMax = undefined }) {
const quantite = quantiteMax ?? item.getQuantite() ?? 1;
const isOwned = item.parent;
static async display(item, callback) {
const quantite = item.isConteneur() ? 1 : item.system.quantite;
const venteData = {
item: item,
alias: item.actor?.name ?? game.user.name,
vendeurId: item.actor?.id,
prixOrigine: item.calculerPrixCommercant(),
prixUnitaire: item.calculerPrixCommercant(),
prixLot: item.calculerPrixCommercant(),
prixOrigine: item.system.cout,
prixUnitaire: item.system.cout,
prixLot: item.system.cout,
tailleLot: 1,
quantiteNbLots: quantite,
quantiteMaxLots: quantite,
quantiteMax: quantite,
quantiteIllimite: item.isItemCommerce() ? quantiteMax == undefined : !isOwned,
isOwned: isOwned,
quantiteMax: quantite ,
quantiteIllimite: !item.isOwned,
isOwned: item.isOwned,
};
const html = await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/dialog-item-vente.html`, venteData);
return new DialogItemVente(venteData, html, callback).render(true);
}
constructor(venteData, html, callback) {
let options = { classes: ["dialogvente"], width: 400, height: 'fit-content', 'z-index': 99999 };
let options = { classes: ["dialogvente"], width: 400, height: 300, 'z-index': 99999 };
let conf = {
title: "Proposer",
@ -38,65 +38,57 @@ export class DialogItemVente extends Dialog {
this.venteData = venteData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(".tailleLot").change(event => this.setTailleLot(Number(event.currentTarget.value)));
this.html.find(".quantiteNbLots").change(event => this.setNbLots(Number(event.currentTarget.value)));
this.html.find(".quantiteIllimite").change(event => this.setQuantiteIllimite(event.currentTarget.checked));
this.html.find(".prixLot").change(event => this.setPrixLot(Number(event.currentTarget.value)));
this.setQuantiteIllimite(this.venteData.quantiteIllimite);
}
async onProposer(it) {
this.updateVente(this.getChoixVente());
await $(".tailleLot").change();
await $(".quantiteNbLots").change();
await $(".quantiteIllimite").change();
await $(".prixLot").change();
this.callback(this.venteData);
}
updateVente(update) {
mergeObject(this.venteData, update);
}
getChoixVente() {
return {
quantiteNbLots: Number(this.html.find(".quantiteNbLots").val()),
tailleLot: Number(this.html.find(".tailleLot").val()),
quantiteIllimite: this.html.find(".quantiteIllimite").is(':checked'),
prixLot: Number(this.html.find(".prixLot").val())
};
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility._showControlWhen($(".quantiteNbLots"), !this.venteData.quantiteIllimite)
html.find(".tailleLot").change(event => this.setTailleLot(Number(event.currentTarget.value)));
html.find(".quantiteNbLots").change(event => this.setNbLots(Number(event.currentTarget.value)));
html.find(".quantiteIllimite").change(event => this.setQuantiteIllimite(event.currentTarget.checked));
html.find(".prixLot").change(event => this.setPrixLot(Number(event.currentTarget.value)));
}
setPrixLot(prixLot) {
this.venteData.prixLot = prixLot;
}
setTailleLot(tailleLot) {
const maxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
this.updateVente({
tailleLot,
quantiteNbLots: Math.min(maxLots, this.venteData.quantiteNbLots),
quantiteMaxLots: maxLots,
prixLot: (tailleLot * this.venteData.prixOrigine).toFixed(2)
});
this.html.find(".prixLot").val(this.venteData.prixLot);
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
this.html.find(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
// recalculer le prix du lot
if (tailleLot != this.venteData.tailleLot) {
this.venteData.prixLot = (tailleLot * this.venteData.prixOrigine).toFixed(2);
$(".prixLot").val(this.venteData.prixLot);
}
this.venteData.tailleLot = tailleLot;
if (this.venteData.isOwned) {
// recalculer le nombre de lots max
this.venteData.quantiteMaxLots = Math.floor(this.venteData.quantiteMax / tailleLot);
this.venteData.quantiteNbLots = Math.min(this.venteData.quantiteMaxLots, this.venteData.quantiteNbLots);
$(".quantiteNbLots").val(this.venteData.quantiteNbLots);
$(".quantiteNbLots").attr("max", this.venteData.quantiteMaxLots)
}
}
setNbLots(nbLots) {
this.updateVente({
quantiteNbLots: this.venteData.isOwned ? Math.max(0, Math.min(nbLots, this.venteData.quantiteMaxLots)) : nbLots
})
this.html.find(".quantiteNbLots").val(this.venteData.quantiteNbLots);
if (this.venteData.isOwned) {
nbLots = Math.max(0, Math.min(nbLots, this.venteData.quantiteMaxLots));
}
this.venteData.quantiteNbLots = nbLots;
$(".quantiteNbLots").val(this.venteData.quantiteNbLots);
}
setQuantiteIllimite(checked) {
this.updateVente({ quantiteIllimite: checked })
this.html.find(".label-quantiteIllimite").text(this.venteData.quantiteIllimite ? "Illimités" : "disponibles");
HtmlUtility.showControlWhen(this.html.find(".quantiteNbLots"), !this.venteData.quantiteIllimite)
this.venteData.quantiteIllimite = checked;
$(".label-quantiteIllimite").text(this.venteData.quantiteIllimite ? "Illimités" : "disponibles");
HtmlUtility._showControlWhen($(".quantiteNbLots"), !this.venteData.quantiteIllimite)
}
}

56
module/dialog-repos.js Normal file
View File

@ -0,0 +1,56 @@
import { Misc } from "./misc.js";
export class DialogRepos extends Dialog {
static async create(actor) {
let actorData = actor
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-repos.html", actorData);
new DialogRepos(html, actor).render(true);
}
constructor(html, actor) {
let options = { classes: ["DialogCreateSigneDraconiqueActorsActors"], width: 500, height: 400, 'z-index': 99999 };
let conf = {
title: "Se reposer",
content: html,
default: "repos",
buttons: {
"repos": { label: "Se reposer", callback: async it => { this.repos(); } }
}
};
super(conf, options);
this.actor = actor;
}
async repos() {
await $("[name='nb-heures']").change();
await $("[name='nb-jours']").change();
const selection = await $("[name='repos']:checked").val();
const nbHeures = Number.parseInt(await $("[name='nb-heures']").val());
const nbJours = Number.parseInt(await $("[name='nb-jours']").val());
switch (selection) {
case "sieste": {
await this.actor.dormir(nbHeures);
return;
}
case "nuit": {
let heuresDormies = await this.actor.dormir(nbHeures);
if (heuresDormies == nbHeures){
await this.actor.dormirChateauDormant();
}
return;
}
case "chateau-dormant":
await this.actor.dormirChateauDormant();
return;
case "gris-reve": {
await this.actor.grisReve(nbJours);
return;
}
}
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
}
}

View File

@ -1,37 +0,0 @@
export class DialogSelectTarget extends Dialog {
constructor(html, onSelectTarget, targets) {
const options = {
classes: ["rdd-dialog-select-target"],
width: 'fit-content',
height: 'fit-content',
'max-height': 600,
'z-index': 99999
};
const conf = {
title: "Choisir une cible",
content: html,
buttons: {}
};
super(conf, options);
this.onSelectTarget = onSelectTarget;
this.targets = targets;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("li.select-target").click((event) => {
this.targetSelected(this.html.find(event.currentTarget)?.data("token-id"));
});
}
targetSelected(tokenId) {
const target = this.targets.find(it => it.id == tokenId);
this.close();
if (target) {
this.onSelectTarget(target);
}
}
}

View File

@ -12,34 +12,40 @@ export class DialogSplitItem extends Dialog {
}
constructor(item, splitData, html, callback) {
let options = { classes: ["dialogsplit"], width: 300, height: 'fit-content', 'z-index': 99999 };
let options = { classes: ["dialogsplit"], width: 300, height: 160, 'z-index': 99999 };
let conf = {
title: "Séparer en deux",
content: html,
default: "separer",
buttons: {
"separer": { label: "Séparer", callback: it => this.onSplit() }
"separer": {
label: "Séparer", callback: it => {
this.onSplit();
}
}
}
};
super(conf, options);
this.callback = callback;
this.item = item;
this.splitData = splitData;
}
async onSplit(){
await $(".choix-quantite").change();
this.callback(this.item, this.splitData.choix.quantite);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find(".choix-quantite").change(event => {
html.find(".choix-quantite").change(event => {
this.splitData.choix.quantite = Number(event.currentTarget.value);
});
}
/* -------------------------------------------- */
async onSplit() {
await this.html.find(".choix-quantite").change();
this.callback(this.item, this.splitData.choix.quantite);
}
}

View File

@ -1,3 +1,4 @@
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
export class DialogStress extends Dialog {
@ -15,7 +16,7 @@ export class DialogStress extends Dialog {
)
};
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/sommeil/dialog-stress.html", dialogData);
const html = await renderTemplate("systems/foundryvtt-reve-de-dragon/templates/dialog-stress.html", dialogData);
new DialogStress(dialogData, html)
.render(true);
}
@ -23,7 +24,7 @@ export class DialogStress extends Dialog {
constructor(dialogData, html) {
const options = { classes: ["DialogStress"],
width: 400,
height: 'fit-content',
height: 205+dialogData.actors.length*25,
'z-index': 99999
};
const conf = {
@ -31,30 +32,31 @@ export class DialogStress extends Dialog {
content: html,
buttons: {
stress: { label: "Stress !", callback: it => { this.onStress(); } }
}
},
default: "stress"
};
super(conf, options);
this.dialogData = dialogData;
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("input.select-actor").change((event) => this.onSelectActor(event));
}
async onStress() {
const motif = this.html.find("form.rdddialogstress input[name='motif']").val();
const stress = Number(this.html.find("form.rdddialogstress input[name='stress']").val());
const compteur = (this.html.find("form.rdddialogstress input[name='immediat']").prop("checked")) ? 'experience' : 'stress';
const motif = $("form.rdddialogstress input[name='motif']").val();
const stress = Number($("form.rdddialogstress input[name='stress']").val());
const compteur = ($("form.rdddialogstress input[name='immediat']").prop("checked")) ? 'experience' : 'stress';
this.dialogData.actors.filter(it => it.selected)
.map(it => game.actors.get(it.id))
.forEach(async actor => await actor.distribuerStress(compteur, stress, motif));
.forEach(actor => actor.distribuerStress(compteur, stress, motif));
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
html.find("input.select-actor").change((event) => this.onSelectActor(event));
}
async onSelectActor(event) {
const actorId = this.html.find(event.currentTarget)?.data("actor-id");
const actorId = $(event.currentTarget)?.data("actor-id");
const actor = this.dialogData.actors.find(it => it.id == actorId);
if (actor) {
actor.selected = event.currentTarget.checked;

View File

@ -35,7 +35,7 @@ export class DialogValidationEncaissement extends Dialog {
}
let dialogOptions = {
classes: ["rdd-roll-dialog"],
classes: ["rdddialog"],
width: 350,
height: 290
}
@ -55,12 +55,11 @@ export class DialogValidationEncaissement extends Dialog {
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find('input.encaissement-roll-result').keyup(async event => {
html.find('input.encaissement-roll-result').keyup(async event => {
this.forceDiceResult.total = event.currentTarget.value;
this.encaissement = await RdDUtility.jetEncaissement(this.rollData, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult});
this.html.find('label.encaissement-total').text(this.encaissement.total);
this.html.find('label.encaissement-blessure').text(this.encaissement.blessures)
$('label.encaissement-total').text(this.encaissement.total);
$('label.encaissement-blessure').text(this.encaissement.blessures)
});
}

View File

@ -1,78 +0,0 @@
import { SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Misc } from "./misc.js";
import { CompendiumTableHelpers, CompendiumTable, SystemCompendiums } from "./settings/system-compendiums.js";
const COMPENDIUMS_RECHERCHE = 'compendiums-recherche';
export class Environnement {
static init() {
game.settings.register(SYSTEM_RDD, COMPENDIUMS_RECHERCHE, {
name: COMPENDIUMS_RECHERCHE,
default: [
SystemCompendiums.getCompendium('faune-flore-mineraux'),
SystemCompendiums.getCompendium('meditations-et-ecrits'),
SystemCompendiums.getCompendium('equipement')
],
scope: "world",
config: false,
type: Object
});
game.system.rdd.environnement = new Environnement();
Hooks.once('ready', () => game.system.rdd.environnement.onReady());
}
constructor() {
this.compendiums = [];
this.compendiumTables = [];
this.mapMilieux = {}
}
async onReady() {
await this.$prepareCompendiums()
}
async milieux() {
return Object.values(this.mapMilieux);
}
async saveCompendiums(compendiumIds) {
game.settings.set(SYSTEM_RDD, COMPENDIUMS_RECHERCHE, compendiumIds);
await this.$prepareCompendiums();
}
async $prepareCompendiums() {
this.compendiums = game.settings.get(SYSTEM_RDD, COMPENDIUMS_RECHERCHE).filter(c => SystemCompendiums.getPack(c));
this.compendiumTables = this.compendiums.map(it => new CompendiumTable(it, 'Item'));
const compendiumItems = await this.getElements(it => 1, it => it.isInventaire());
const fromCompendiums = Misc.concat(compendiumItems.map(it => it.getMilieux().filter(m => m)));
this.mapMilieux = Misc.indexLowercase(fromCompendiums);
}
async autresMilieux(item) {
const milieuxExistants = item.getMilieux().map(it => Grammar.toLowerCaseNoAccent(it));
return Object.keys(this.mapMilieux)
.filter(it => !milieuxExistants.includes(it))
.map(it => this.mapMilieux[it]);
}
async getElements(itemFrequence, filter) {
const compendiumsElement = await Promise.all(
this.compendiumTables.map(async compTable => await compTable.getContent(itemFrequence, filter))
);
const elements = compendiumsElement.reduce((a, b) => a.concat(b));
elements.sort(Misc.ascending(it => it.name))
return elements;
}
async buildTable(itemFrequence, filter = it => true) {
if (!itemFrequence) {
itemFrequence = it => it.getFrequence()
}
const elements = await this.getElements(itemFrequence, filter);;
return CompendiumTableHelpers.buildTable(elements, itemFrequence);
}
}

View File

@ -19,14 +19,10 @@ export class Grammar {
return word.match(/^[aeiouy]/i)
}
static equalsInsensitive(a, b) {
return Grammar.toLowerCaseNoAccent(a) == Grammar.toLowerCaseNoAccent(b)
}
static includesLowerCaseNoAccent(value, content) {
return Grammar.toLowerCaseNoAccent(value).includes(Grammar.toLowerCaseNoAccent(content));
}
/* -------------------------------------------- */
static toLowerCaseNoAccent(words) {
return words?.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") ?? words;

View File

@ -1,10 +1,10 @@
export class HtmlUtility{
static showControlWhen(jQuerySelector, condition) {
static _showControlWhen(control, condition) {
if (condition) {
jQuerySelector.show();
control.show();
}
else {
jQuerySelector.hide();
control.hide();
}
}
}

View File

@ -1,4 +1,5 @@
import { RdDItemCompetenceCreature } from "./item-competencecreature.js"
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
const nomCategorieParade = {
@ -27,7 +28,7 @@ export class RdDItemArme extends Item {
switch (arme ? arme.type : '') {
case 'arme': return arme;
case 'competencecreature':
return RdDItemCompetenceCreature.armeNaturelle(arme);
return RdDItemCompetenceCreature.toActionArme(arme);
}
return RdDItemArme.mainsNues();
}
@ -176,7 +177,7 @@ export class RdDItemArme extends Item {
equipe: true,
rapide: true,
force: 0,
dommages: "0",
dommages: 0,
dommagesReels: 0,
mortalite: 'non-mortel',
competence: 'Corps à corps',

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, "nombre": 0 },
{ "niveau": 1, "nombreMax": 10, "nombre": 0 },
{ "niveau": 2, "nombreMax": 9, "nombre": 0 },
{ "niveau": 3, "nombreMax": 8, "nombre": 0 },
{ "niveau": 4, "nombreMax": 7, "nombre": 0 },
{ "niveau": 5, "nombreMax": 6, "nombre": 0 },
{ "niveau": 6, "nombreMax": 5, "nombre": 0 },
{ "niveau": 7, "nombreMax": 4, "nombre": 0 },
{ "niveau": 8, "nombreMax": 3, "nombre": 0 },
{ "niveau": 9, "nombreMax": 2, "nombre": 0 },
{ "niveau": 10, "nombreMax": 1, "nombre": 0 },
{ "niveau": 11, "nombreMax": 1, "nombre": 0 }
];
/* -------------------------------------------- */
@ -34,6 +34,13 @@ const categorieCompetences = {
"lancer": { base: -8, label: "Lancer" }
}
const compendiumCompetences = {
"personnage": "foundryvtt-reve-de-dragon.competences",
"creature": "foundryvtt-reve-de-dragon.competences-creatures",
"entite": "foundryvtt-reve-de-dragon.competences-entites"
};
function _buildCumulXP() {
let cumulXP = { "-11": 0 };
let cumul = 0;
@ -48,6 +55,12 @@ function _buildCumulXP() {
const competence_xp_cumul = _buildCumulXP();
export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static actorCompendium(actorType = undefined) {
return compendiumCompetences[actorType ?? 'personnage'];
}
/* -------------------------------------------- */
static getCategorieCompetences() {
return categorieCompetences;
@ -72,9 +85,9 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static getVoieDraconic(competences, voie) {
return RdDItemCompetence.findFirstItem(competences, voie, {
preFilter: it => it.isCompetence() && RdDItemCompetence.isDraconic(it),
description: 'Draconic',
});
preFilter: it => it.isCompetence() && RdDItemCompetence.isDraconic(it),
description: 'Draconic',
});
}
/* -------------------------------------------- */
@ -186,10 +199,19 @@ export class RdDItemCompetence extends Item {
item.system.isStressLevelUp = (xpManquant > 0 && stressTransforme >= xpManquant && item.system.niveau < item.system.niveau_archetype);
item.system.stressXpMax = 0;
if (xpManquant > 0 && stressTransforme > 0 && item.system.niveau < item.system.niveau_archetype) {
item.system.stressXpMax = Math.min(xpManquant, stressTransforme);
item.system.stressXpMax = Math.min(xpManquant , stressTransforme);
}
}
/* -------------------------------------------- */
static isVisible(item) {
return Number(item.system.niveau) != RdDItemCompetence.getNiveauBase(item.system.categorie);
}
static nomContientTexte(item, texte) {
return Grammar.toLowerCaseNoAccent(item.name).includes(Grammar.toLowerCaseNoAccent(texte))
}
/* -------------------------------------------- */
static isNiveauBase(item) {
return Number(item.system.niveau) == RdDItemCompetence.getNiveauBase(item.system.categorie);
@ -197,10 +219,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static findCompetence(list, idOrName, options = {}) {
if (idOrName == undefined || idOrName == "") {
return RdDItemCompetence.sansCompetence();
if (idOrName == undefined) {
return undefined;
}
options = mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, { overwrite: false });
options = mergeObject(options, { preFilter: it => it.isCompetence(), description: 'compétence' }, {overwrite: false});
return RdDItemCompetence.findFirstItem(list, idOrName, options);
}
@ -209,23 +231,6 @@ export class RdDItemCompetence extends Item {
return Misc.findAllLike(name, list, { filter: it => it.isCompetence(), description: 'compétence' });
}
static sansCompetence() {
return {
name: "Sans compétence",
type: "competence",
img: "systems/foundryvtt-reve-de-dragon/icons/templates/icone_parchement_vierge.webp",
system: {
niveau: 0,
default_diffLibre: 0,
base: 0,
categorie: "Aucune",
description: "",
descriptionmj: "",
defaut_carac: "",
}
};
}
static findFirstItem(list, idOrName, options) {
return list.find(it => it.id == idOrName && options.preFilter(it))
?? Misc.findFirstLike(idOrName, list, options);
@ -258,47 +263,18 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static computeResumeArchetype(competences) {
const computed = duplicate(limitesArchetypes);
const archetype = RdDItemCompetence.getLimitesArchetypes();
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;
.forEach(niveau => {
archetype[niveau] = archetype[niveau] ?? { "niveau": niveau, "nombreMax": 0, "nombre": 0 };
archetype[niveau].nombre = (archetype[niveau]?.nombre ?? 0) + 1;
});
return computed.filter(it => it.reste > 0 && it.niveau > 0);
return archetype;
}
/* -------------------------------------------- */
static triVisible(competences) {
return competences.filter(it => !it.system.isHidden)
.sort((a, b) => RdDItemCompetence.compare(a, b))
}
static $positionTri(comp) {
if (comp.name.startsWith("Survie")) {
if (comp.name.includes("Cité")) return 0;
if (comp.name.includes("Extérieur")) return 1;
return 2;
}
if (comp.system.categorie.startsWith("melee")) {
if (comp.name.includes("Corps")) return 0;
if (comp.name.includes("Dague")) return 1;
if (comp.name.includes("Esquive")) return 2;
return 3;
}
if (comp.system.categorie.startsWith("draconic")) {
if (comp.name.includes("Oniros")) return 0;
if (comp.name.includes("Hypnos")) return 1;
if (comp.name.includes("Narcos")) return 2;
if (comp.name.includes("Thanatos")) return 3;
return 4;
}
return 0;
}
static compare(a, b) {
const diff = RdDItemCompetence.$positionTri(a) - RdDItemCompetence.$positionTri(b);
return diff ? diff : a.name.localeCompare(b.name);
static getLimitesArchetypes() {
return duplicate(limitesArchetypes);
}
}

View File

@ -1,4 +1,4 @@
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
/* -------------------------------------------- */
@ -12,12 +12,12 @@ export class RdDItemCompetenceCreature extends Item {
rollData.competence.system.categorie = "creature"
rollData.selectedCarac = rollData.carac.carac_creature
if (rollData.competence.system.iscombat) {
rollData.arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence);
rollData.arme = RdDItemCompetenceCreature.toActionArme(rollData.competence);
}
}
/* -------------------------------------------- */
static armeNaturelle(competencecreature) {
static toActionArme(competencecreature) {
if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) {
// si c'est un Item compétence: cloner pour ne pas modifier lma compétence
let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature;

View File

@ -2,28 +2,27 @@ import { Misc } from "./misc.js";
import { LOG_HEAD } from "./constants.js";
const MONNAIE_ETAIN = {
name: "Denier (étain)", type: 'monnaie',
name: "Etain (1 denier)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_etain_poisson.webp",
system: { quantite: 0, cout: 0.01, encombrement: 0.001, description: "" }
system: { quantite: 0, valeur_deniers: 1, encombrement: 0.001, description: "" }
};
const MONNAIE_BRONZE = {
name: "Sou (bronze)", type: 'monnaie',
name: "Bronze (10 deniers)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_bronze_epees.webp",
system: { quantite: 0, cout: 0.10, encombrement: 0.002, description: "" }
system: { quantite: 0, valeur_deniers: 10, encombrement: 0.002, description: "" }
};
const MONNAIE_ARGENT = {
name: "Sol (argent)", type: 'monnaie',
name: "Argent (1 sol)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_argent_sol.webp",
system: { quantite: 0, cout: 1, encombrement: 0.003, description: "" }
system: { quantite: 0, valeur_deniers: 100, encombrement: 0.003, description: "" }
};
const MONNAIE_OR = {
name: "Dragon (or)", type: 'monnaie',
name: "Or (10 sols)", type: 'monnaie',
img: "systems/foundryvtt-reve-de-dragon/icons/objets/piece_or_sol.webp",
system: { quantite: 0, cout: 10, encombrement: 0.004, description: "" }
system: { quantite: 0, valeur_deniers: 1000, encombrement: 0.004, description: "" }
};
const MONNAIES_STANDARD = [MONNAIE_ETAIN, MONNAIE_BRONZE, MONNAIE_ARGENT, MONNAIE_OR];
const VALEUR_DENIERS = sols => Math.max(Math.floor((sols ?? 0) * 100), 0);
export class Monnaie {
@ -33,7 +32,7 @@ export class Monnaie {
static monnaiesManquantes(actor) {
const disponibles = actor.itemTypes['monnaie'];
const manquantes = MONNAIES_STANDARD.filter(standard => !disponibles.find(disponible => Monnaie.deValeur(disponible, standard.system?.cout)));
const manquantes = MONNAIES_STANDARD.filter(standard => !disponibles.find(disponible => Monnaie.deValeur(disponible, standard.system?.valeur_deniers)));
if (manquantes.length > 0) {
console.error(`${LOG_HEAD} monnaiesManquantes pour ${actor.name}`, manquantes, ' avec monnaies', disponibles, MONNAIES_STANDARD);
}
@ -41,11 +40,15 @@ export class Monnaie {
}
static deValeur(monnaie, valeur) {
return VALEUR_DENIERS(valeur) == VALEUR_DENIERS(monnaie.system.cout)
return valeur == monnaie.system.valeur_deniers
}
static triValeurEntiere() {
return Misc.ascending(item => VALEUR_DENIERS(item.system.cout))
static arrondiDeniers(sols) {
return Number(sols).toFixed(2);
}
static triValeurDenier() {
return Misc.ascending(item => item.system.valeur_deniers)
}
static async creerMonnaiesStandard(actor) {
@ -62,52 +65,29 @@ export class Monnaie {
return deniers;
}
static toSolsDeniers(fortune) {
return {
sols: Math.floor(fortune),
deniers: Math.round(100 * (fortune - Math.floor(fortune)))
};
}
static getFortune(monnaies) {
return (monnaies??[])
.map(m => Number(m.system.cout) * Number(m.system.quantite))
.reduce(Misc.sum(), 0);
}
static async optimiserFortune(actor, fortune) {
let resteEnDeniers = Math.round(fortune * 100);
static async optimiser(actor, fortune) {
let reste = fortune;
let monnaies = actor.itemTypes['monnaie'];
let updates = [];
Monnaie.validerMonnaies(monnaies, actor);
let parValeur = Misc.classifyFirst(monnaies, it => VALEUR_DENIERS(it.system.cout));
for (let valeurDeniers of [1000, 100, 10, 1]) {
const itemPiece = parValeur[valeurDeniers];
let parValeur = Misc.classifyFirst(monnaies, it => it.system.valeur_deniers);
for (let valeur of [1000, 100, 10, 1]) {
const itemPiece = parValeur[valeur];
const piecesDeCetteValeur = Math.floor(reste / valeur);
if (itemPiece) {
const quantite = Math.floor(resteEnDeniers / valeurDeniers);
if (quantite != itemPiece.system.quantite) {
updates.push({ _id: parValeur[valeurDeniers].id, 'system.quantite': quantite });
if (piecesDeCetteValeur != itemPiece.system.quantite) {
updates.push({ _id: parValeur[valeur].id, 'system.quantite': piecesDeCetteValeur });
}
resteEnDeniers -= quantite * valeurDeniers;
reste -= piecesDeCetteValeur*valeur;
}
}
console.log('Monnaie.optimiserFortune', actor.name, 'total', fortune, 'parValeur', parValeur, 'updates', updates, 'reste', resteEnDeniers);
console.log('Monnaie.optimiser', actor.name, 'total', fortune, 'parValeur', parValeur, 'updates', updates, 'reste', reste);
if (updates.length > 0) {
await actor.updateEmbeddedDocuments('Item', updates);
}
if (resteEnDeniers > 0) {
if (reste>0){
// créer le reste en deniers fortune en deniers
await Monnaie.creerMonnaiesDeniers(actor, resteEnDeniers);
await Monnaie.creerMonnaiesDeniers(actor, reste);
}
}
static validerMonnaies(monnaies, actor = undefined) {
monnaies.filter(it => VALEUR_DENIERS(it.system.cout) == 0)
.map(it => `La monnaie ${it.name} de l'acteur ${actor?.name ?? 'sélectionné'} a une valeur de 0!`)
.forEach(message => {
ui.notifications.warn(message);
console.warn(message);
});
}
}

View File

@ -4,70 +4,45 @@ import { RdDAlchimie } from "./rdd-alchimie.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDHerbes } from "./rdd-herbes.js";
import { RdDGemme } from "./rdd-gemme.js";
import { Misc } from "./misc.js";
import { HtmlUtility } from "./html-utility.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { SYSTEM_RDD } from "./constants.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
import { Misc } from "./misc.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
/**
* Extend the basic ItemSheet for RdD specific items
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
export class RdDItemSheet extends ItemSheet {
static get ITEM_TYPE() {
return undefined
}
static defaultTemplate(type) {
return type ?
`systems/foundryvtt-reve-de-dragon/templates/item-${type}-sheet.html` :
"systems/foundryvtt-reve-de-dragon/templates/item-sheet.html";
}
static register(sheetClass) {
Items.registerSheet(SYSTEM_RDD, sheetClass, {
label: Misc.typeName('Item', sheetClass.ITEM_TYPE),
types: [sheetClass.ITEM_TYPE],
makeDefault: true
})
}
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: [SYSTEM_RDD, "sheet", "item"],
template: RdDItemSheet.defaultTemplate(RdDItemSheet.ITEM_TYPE),
template: "systems/foundryvtt-reve-de-dragon/templates/item-sheet.html",
width: 550,
height: 550
});
}
/* -------------------------------------------- */
get template() {
return RdDItemSheet.defaultTemplate(this.item.type);
}
get title() {
return `${Misc.typeName('Item', this.item.type)}: ${this.item.name}`;
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
if (this.item.isInventaire() && this.item.isVideOuNonConteneur()) {
// Add "Post to chat" button
// We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry!
if ("cout" in this.item.system && this.item.isVideOuNonConteneur()) {
buttons.unshift({
class: "vendre",
icon: "fas fa-comments-dollar",
onclick: ev => this.item.proposerVente(1)
onclick: ev => this.item.proposerVente()
});
}
buttons.unshift({
class: "montrer",
icon: "fas fa-comment",
onclick: ev => this.item.postItemToChat()
onclick: ev => this.item.postItem()
});
return buttons
}
@ -86,143 +61,175 @@ export class RdDItemSheet extends ItemSheet {
/* -------------------------------------------- */
async getData() {
let formData = {
title: this.item.name,
id: this.item.id,
title: this.item.name,
type: this.item.type,
img: this.item.img,
name: this.item.name,
system: this.item.system,
// TODO: v10 remove
data: this.item.system,
isGM: game.user.isGM,
actorId: this.actor?.id,
description: await TextEditor.enrichHTML(this.item.system.description, { async: true }),
descriptionmj: await TextEditor.enrichHTML(this.item.system.descriptionmj, { async: true }),
isComestible: this.item.getUtilisationCuisine(),
options: RdDSheetUtility.mergeDocumentRights(this.options, this.item, this.isEditable)
owner: this.item.isOwner,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
isSoins: false,
description: await TextEditor.enrichHTML(this.object.system.description, {async: true}),
descriptionmj: await TextEditor.enrichHTML(this.object.system.descriptionmj, {async: true})
}
if (this.actor) {
formData.isOwned = true;
if (this.item.type == 'conteneur') {
this.prepareConteneurData(formData);
}
}
const competences = await SystemCompendiums.getCompetences(this.actor?.type);
formData.categorieCompetences = RdDItemCompetence.getCategorieCompetences()
if (this.item.type == 'tache' || this.item.type == 'livre' || this.item.type == 'meditation' || this.item.type == 'oeuvre') {
formData.caracList = duplicate(game.system.model.Actor.personnage.carac)
formData.caracList["reve-actuel"] = duplicate(game.system.model.Actor.personnage.reve.reve)
formData.competences = competences;
formData.competences = await RdDUtility.loadItems(it => it.isCompetencePersonnage(), RdDItemCompetence.actorCompendium(this.actor?.type))
}
if (this.item.type == 'arme') {
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it));
}
if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));
formData.competences = await RdDUtility.loadItems(it => RdDItemCompetence.isCompetenceArme(it), RdDItemCompetence.actorCompendium(this.actor?.type))
}
if (this.item.type == 'recettecuisine') {
formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, { async: true })
}
if (this.item.type == 'extraitpoetique') {
formData.extrait = await TextEditor.enrichHTML(this.object.system.extrait, { async: true })
formData.texte = await TextEditor.enrichHTML(this.object.system.texte, { async: true })
formData.ingredients = await TextEditor.enrichHTML(this.object.system.ingredients, {async: true})
}
if (this.item.type == 'recettealchimique') {
RdDAlchimie.processManipulation(this.item, this.actor && this.actor.id);
formData.manipulation_update = await TextEditor.enrichHTML(this.object.system.manipulation_update, { async: true })
formData.utilisation = await TextEditor.enrichHTML(this.object.system.utilisation, { async: true })
formData.enchantement = await TextEditor.enrichHTML(this.object.system.enchantement, { async: true })
formData.sureffet = await TextEditor.enrichHTML(this.object.system.sureffet, { async: true })
formData.manipulation_update = await TextEditor.enrichHTML(this.object.system.manipulation_update, {async: true})
formData.utilisation = await TextEditor.enrichHTML(this.object.system.utilisation, {async: true})
formData.enchantement = await TextEditor.enrichHTML(this.object.system.enchantement, {async: true})
formData.sureffet = await TextEditor.enrichHTML(this.object.system.sureffet, {async: true})
}
if (this.item.type == 'gemme') {
formData.gemmeTypeList = RdDGemme.getGemmeTypeOptionList();
RdDGemme.calculDataDerivees(this.item);
}
if (this.item.type == 'potion') {
await RdDHerbes.addPotionFormData(formData);
if (this.dateUpdated) {
formData.system.prdate = this.dateUpdated;
this.dateUpdated = undefined;
}
await RdDHerbes.updatePotionData(formData);
}
if (formData.options.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
if (formData.isOwned && this.item.type == 'herbe' && (formData.system.categorie == 'Soin' || formData.system.categorie == 'Repos')) {
formData.isIngredientPotionBase = true;
}
if (this.item.type == 'sortreserve') {
const sortId = this.item.system.sortid;
formData.sort = formData.options.isOwned ? this.item.actor.items.get(sortId) : game.items.get(sortId);
formData.sort = formData.isOwned ? this.item.actor.items.get(sortId) : game.items.get(sortId);
}
formData.bonusCaseList = RdDItemSort.getBonusCaseList(formData, true);
return formData;
}
/* -------------------------------------------- */
prepareConteneurData(formData) {
RdDUtility.filterEquipementParType(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
HtmlUtility.showControlWhen(this.html.find(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs')
|| game.user.isGM
|| !this.item.isOwned);
HtmlUtility.showControlWhen(this.html.find(".item-magique"), this.item.isMagique());
if (this.item.type == 'conteneur') {
this.form.ondragstart = (event) => this._onDragStart(event);
this.form.ondrop = (event) => this._onDrop(event);
}
let itemSheetDialog = this;
HtmlUtility._showControlWhen($(".item-cout"), ReglesOptionelles.isUsing('afficher-prix-joueurs') || game.user.isGM || !this.item.isOwned);
HtmlUtility._showControlWhen($(".item-magique"), this.item.isMagique());
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.form.ondragstart = (event) => this._onDragStart(event);
this.form.ondrop = (event) => this._onDrop(event);
// Select competence categorie
this.html.find(".categorie").change(event => this._onSelectCategorie(event));
html.find(".categorie").change(event => this._onSelectCategorie(event));
this.html.find('.sheet-competence-xp').change((event) => {
html.find('.sheet-competence-xp').change((event) => {
if (this.item.isCompetencePersonnage()) {
RdDUtility.checkThanatosXP(this.item.name);
}
});
this.html.find(".item-cout input[name='system.cout']").change(event => {
if (this.item.isMonnaie()) {
const value = event.currentTarget.value;
if (Number(value) == 0) {
ui.notifications.error(`${this.actor?.name ?? 'Monnaie'}: La monnaie ${this.item.name} a maintenant une valeur de 0, et ne peut plus être utilisée pour payer!`)
}
}
})
this.html.find('.date-enchantement').change((event) => {
const jour = Number(this.html.find('input.date-enchantement[name="enchantement.jour"]').val());
const mois = RdDTimestamp.definition(this.html.find('select.date-enchantement[name="enchantement.mois"]').val());
const indexDate = game.system.rdd.calendrier.getIndexFromDate(jour, mois.heure);
this.item.update({ 'system.prdate': indexDate });
console.warn(`Date d'enchantement modifiée ${jour}/${mois.heure}: ${indexDate}`)
html.find('.enchanteDate').change((event) => {
let jour = Number($('#jourMois').val());
let mois = $('#nomMois').val();
this.dateUpdated = game.system.rdd.calendrier.getIndexFromDate(jour, mois);
});
this.html.find('.creer-tache-livre').click((event) => this._getEventActor(event).creerTacheDepuisLivre(this.item));
this.html.find('.consommer-potion').click((event) => this._getEventActor(event).consommerPotion(this.item));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).dialogFabriquerPotion(this.item));
html.find('.creer-tache-livre').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.creerTacheDepuisLivre(this.item);
});
html.find('.consommer-potion').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.consommerPotion(this.item);
});
html.find('.creer-potion-base').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
actor.dialogFabriquerPotion(this.item);
});
this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event);
html.find('.alchimie-tache a').click((event) => {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let recetteId = event.currentTarget.attributes['data-recette-id'].value;
let tacheName = event.currentTarget.attributes['data-alchimie-tache'].value;
let tacheData = event.currentTarget.attributes['data-alchimie-data'].value;
let actor = game.actors.get(actorId);
if (actor) {
let recetteId = event.currentTarget.attributes['data-recette-id'].value;
let tacheName = event.currentTarget.attributes['data-alchimie-tache'].value;
let tacheData = event.currentTarget.attributes['data-alchimie-data'].value;
actor.effectuerTacheAlchimie(recetteId, tacheName, tacheData);
} else {
ui.notifications.info("Impossible trouver un acteur pour réaliser cette tache Alchimique.");
}
});
this.html.find('.item-split').click(async event => RdDSheetUtility.splitItem(RdDSheetUtility.getItem(event, this.actor), this.actor, async () => this.render(true)));
this.html.find('.item-edit').click(async event => RdDSheetUtility.getItem(event, this.actor)?.sheet.render(true));
this.html.find('.item-delete').click(async event => RdDUtility.confirmActorItemDelete(this, RdDSheetUtility.getItem(event, this.actor)));
this.html.find('.item-vendre').click(async event => RdDSheetUtility.getItem(event, this.actor)?.proposerVente());
this.html.find('.item-montrer').click(async event => RdDSheetUtility.getItem(event, this.actor)?.postItemToChat());
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor, async () => this.render(true)));
const updateItemTimestamp = (path, timestamp) => this.item.update({ [path]: duplicate(timestamp) })
RdDTimestamp.handleTimestampEditor(this.html, 'system.temporel.debut', updateItemTimestamp);
RdDTimestamp.handleTimestampEditor(this.html, 'system.temporel.fin', updateItemTimestamp);
html.find('.item-split').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
await RdDSheetUtility.splitItem(item, this.actor, async () => itemSheetDialog.render(true));
});
html.find('.item-edit').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item.sheet.render(true);
});
html.find('.item-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const item = this.actor.getObjet(li.data("item-id"));
RdDUtility.confirmerSuppressionItem(this, item, li);
});
html.find('.item-vendre').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.proposerVente();
});
html.find('.item-montrer').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
item?.postItem();
});
html.find('.item-action').click(async event => {
const item = RdDSheetUtility.getItem(event, this.actor);
this.actor.actionItem(item, async () => itemSheetDialog.render(true));
});
html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
}
_getEventActor(event) {
let actorId = event.currentTarget.attributes['data-actor-id'].value;
let actor = game.actors.get(actorId);
return actor;
}
/* -------------------------------------------- */
async _onSelectCategorie(event) {
event.preventDefault();
@ -230,56 +237,70 @@ export class RdDItemSheet extends ItemSheet {
if (this.item.isCompetence()) {
let level = RdDItemCompetence.getNiveauBase(event.currentTarget.value);
this.item.system.base = level;
this.html.find('[name="system.base"]').val(level);
$("#base").val(level);
}
}
/* -------------------------------------------- */
get template() {
let type = this.item.type
return `systems/foundryvtt-reve-de-dragon/templates/item-${type}-sheet.html`;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
if (this.item.type == 'sort') {
// Données de bonus de cases ?
formData['system.bonuscase'] = RdDItemSort.buildBonusCaseStringFromFormData(formData.bonusValue, formData.caseValue);
}
_updateObject(event, formData) { // Deprecated en v0.8 à clarifier
// Données de bonus de cases ?
formData = RdDItemSort.buildBonusCaseStringFromFormData(formData);
return this.item.update(formData);
}
/* -------------------------------------------- */
async _onDragStart(event) {
console.log("_onDragStart", event);
if (event.target.classList.contains("entity-link")) return;
const itemId = event.srcElement?.attributes["data-item-id"].value;
const item = this.actor.items.get(itemId);
// Create drag data
const dragData = {
actorId: this.actor.id,
type: "Item",
data: item.system
};
event.dataTransfer.setData("text/plain", JSON.stringify(dragData));
}
async _onDrop(event) {
// Try to extract the dragData
let dragData = RdDItemSheet.$extractDragData(event);
if (!dragData) return false;
let dragData;
try {
dragData = JSON.parse(event.dataTransfer.getData('text/plain'));
} catch (err) {
return false;
}
const allowed = Hooks.call("dropActorSheetData", this.actor, this, dragData);
if (allowed === false) return false;
if (allowed === false) return;
// Handle different dragData types
switch (dragData.type) {
case "Item":
return this._onDropItem(event, dragData);
case "Actor":
return this._onDropActor(event, dragData);
}
return super._onDrop(event);
}
static $extractDragData(event) {
try {
const eventData = event?.dataTransfer?.getData('text/plain');
if (eventData) {
return JSON.parse(eventData);
}
} catch (err) { }
return undefined;
}
/* -------------------------------------------- */
async _onDropItem(event, dragData) {
if (this.actor) {
const dropParams = RdDSheetUtility.prepareItemDropParameters(this.item.id, this.actor.id, dragData, this.objetVersConteneur);
await this.actor.processDropItem(dropParams);
await this.render(true);
}
}
async _onDropActor(event, dragData) {
}
}

View File

@ -1,14 +1,30 @@
import { RdDItemSheet } from "../item-sheet.js";
import { RdDItemSigneDraconique } from "./signedraconique.js";
import { TMRUtility } from "../tmr-utility.js";
import { SYSTEM_RDD } from "./constants.js";
import { RdDItemSigneDraconique } from "./item-signedraconique.js";
import { Misc } from "./misc.js";
import { TMRType, TMRUtility } from "./tmr-utility.js";
/**
* Item sheet pour signes draconiques
* @extends {RdDItemSheet}
* @extends {ItemSheet}
*/
export class RdDSigneDraconiqueItemSheet extends RdDItemSheet {
export class RdDSigneDraconiqueItemSheet extends ItemSheet {
static get ITEM_TYPE() { return "signedraconique" }
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: [SYSTEM_RDD, "sheet", "item"],
template: "systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html",
width: 550,
height: 550
});
}
/* -------------------------------------------- */
_getHeaderButtons() {
let buttons = super._getHeaderButtons();
buttons.unshift({ class: "post", icon: "fas fa-comment", onclick: ev => this.item.postItem() });
return buttons;
}
/* -------------------------------------------- */
/** @override */
@ -23,9 +39,18 @@ export class RdDSigneDraconiqueItemSheet extends RdDItemSheet {
/* -------------------------------------------- */
async getData() {
const formData = await super.getData();
const formData = duplicate(this.item);
this.tmrs = TMRUtility.buildSelectionTypesTMR(this.item.system.typesTMR);
formData.tmrs = this.tmrs;
mergeObject(formData, {
tmrs: this.tmrs,
title: formData.name,
isGM: game.user.isGM,
owner: this.actor?.isOwner,
isOwned: this.actor ? true : false,
actorId: this.actor?.id,
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
});
return formData;
}
@ -37,13 +62,12 @@ export class RdDSigneDraconiqueItemSheet extends RdDItemSheet {
if (!this.options.editable) return;
html.find(".signe-aleatoire").click(event => this.setSigneAleatoire());
html.find("input.select-tmr").change(event => this.onSelectTmr(event));
html.find(".signe-xp-sort").change(event => this.onValeurXpSort(event.currentTarget.attributes['data-typereussite']?.value, Number(event.currentTarget.value)));
html.find("input.select-tmr").change((event) => this.onSelectTmr(event));
html.find(".signe-xp-sort").change((event) => this.onValeurXpSort(event.currentTarget.attributes['data-typereussite']?.value, Number(event.currentTarget.value)));
}
async setSigneAleatoire() {
const newSigne = await RdDItemSigneDraconique.randomSigneDraconique();
newSigne.name = this.item.name;
this.item.update(newSigne);
}
@ -65,4 +89,12 @@ export class RdDSigneDraconiqueItemSheet extends RdDItemSheet {
await this.item.update({ 'system.valeur': newValeur });
}
/* -------------------------------------------- */
get template() {
return `systems/foundryvtt-reve-de-dragon/templates/item-signedraconique-sheet.html`;
}
get title() {
return `Signe draconique: ${this.object.name}`;
}
}

View File

@ -1,9 +1,8 @@
import { RdDItem, defaultItemImg } from "../item.js";
import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js";
import { RdDRollTables } from "../rdd-rolltables.js";
import { RdDTimestamp } from "../rdd-timestamp.js";
import { TMRType, TMRUtility } from "../tmr-utility.js";
import { defaultItemImg } from "./item.js";
import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";
import { RdDRollTables } from "./rdd-rolltables.js";
import { TMRType, TMRUtility } from "./tmr-utility.js";
const tableSignesIndicatifs = [
{ rarete: "Très facile", difficulte: 0, xp: 6, nbCases: 14 },
@ -16,17 +15,7 @@ const tableSignesIndicatifs = [
const DIFFICULTE_LECTURE_SIGNE_MANQUE = +11;
export class RdDItemSigneDraconique extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/tmr/signe_draconique.webp";
}
async calculerFinPeriodeTemporel(debut) {
// TODO
return RdDTimestamp.formulesDuree().find(it => it.code == "").calcul(debut, this.actor);
}
export class RdDItemSigneDraconique {
static prepareSigneDraconiqueMeditation(meditation, rolled) {
return {
@ -107,6 +96,6 @@ export class RdDItemSigneDraconique extends RdDItem {
static async randomSigneDescription() {
return await RdDRollTables.drawTextFromRollTable("Signes draconiques", false);
}
}
}

View File

@ -63,21 +63,26 @@ export class RdDItemSort extends Item {
/** Met à jour les données de formulaire
* si static des bonus de cases sont présents
* */
static buildBonusCaseStringFromFormData( bonuses, cases ) {
if ( bonuses ) {
static buildBonusCaseStringFromFormData( formData ) {
if ( formData.bonusValue ) {
let list = [];
let caseCheck = {};
for (let i=0; i<bonuses.length; i++) {
let coord = cases[i]?.toUpperCase() || 'A1';
let bonus = bonuses[i] || 0;
if ( TMRUtility.verifyTMRCoord( coord ) && bonus > 0 && caseCheck[coord] == undefined ) {
caseCheck[coord] = bonus;
list.push( coord+":"+bonus );
for(let i=0; i<formData.bonusValue.length; i++) {
let coord = formData.caseValue[i] || 'A1';
coord = coord.toUpperCase();
if ( TMRUtility.verifyTMRCoord( coord ) ) { // Sanity check
let bonus = formData.bonusValue[i] || 0;
if ( bonus > 0 && caseCheck[coord] == undefined ) {
caseCheck[coord] = bonus;
list.push( coord+":"+bonus );
}
}
}
return list.toString();
formData.bonusValue = undefined;
formData.caseValue = undefined;
formData.system.bonuscase = list.toString(); // Reset
}
return undefined;
return formData;
}
/* -------------------------------------------- */

View File

@ -1,18 +1,4 @@
const BASE_TACHE_SOIN_BLESSURE = { type: "tache", img: 'systems/foundryvtt-reve-de-dragon/icons/competence_chirurgie.webp', system: { carac: "dexterite", competence: "Chirurgie", periodicite: "1 round", fatigue: 0, } }
const TACHES_SOIN_BLESSURE = {
'critique': { name: 'Blessure critique', system: { difficulte: -6, points_de_tache: 6 } },
'grave': { name: 'Blessure grave', system: { difficulte: -4, points_de_tache: 4 } },
'legere': { name: 'Blessure légère', system: { difficulte: -2, points_de_tache: 2 } },
}
export class RdDItemTache extends Item {
static prepareTacheSoin(gravite) {
const blessure = TACHES_SOIN_BLESSURE[gravite]
if (blessure) {
return mergeObject(duplicate(BASE_TACHE_SOIN_BLESSURE), blessure)
}
ui.notifications.warn(`Pas de tâche de soins pour une blessure ${gravite}`)
return undefined;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
import { RdDItem } from "../item.js";
import { Misc } from "../misc.js";
import { RdDTimestamp } from "../rdd-timestamp.js";
export class RdDItemMaladie extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/maladie.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite);
}
async onFinPeriode(oldTimestamp, newTimestamp) {
await RdDItemMaladie.notifierMaladiePoison(this, oldTimestamp, newTimestamp)
}
static async notifierMaladiePoison(mal, oldTimestamp, newTimestamp) {
if (mal.actor) {
const souffrance = mal.system.identifie
? `de ${mal.name}`
: `d'un mal inconnu`
ChatMessage.create({ content: `${mal.actor.name} souffre ${souffrance} (${Misc.typeName('Item', mal.type)}): vérifiez que les effets ne se sont pas aggravés !` });
mal.postItemToChat('gmroll');
await RdDItemMaladie.prolongerPeriode(mal,oldTimestamp, newTimestamp);
}
}
static async prolongerPeriode(mal, oldTimestamp, newTimestamp) {
if (mal.actor) {
// TODO: déterminer le nombre de périodes écoulées
console.log(`${mal.actor.name}: le mal ${mal.name} a atteint la fin de sa période et été prolongé`);
const current = newTimestamp;
const finPeriode = new RdDTimestamp(mal.system.temporel.fin)
const periodeSuivante = (finPeriode.compare(current) > 0 ? finPeriode : current);
const timestampFin = await mal.calculerFinPeriodeTemporel(periodeSuivante);
await mal.actor.updateEmbeddedDocuments('Item', [{
_id: mal.id,
'system.temporel.fin': duplicate(timestampFin),
}])
}
}
}

View File

@ -1,11 +0,0 @@
import { RdDItem } from "../item.js";
export class RdDItemOmbre extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

View File

@ -1,17 +0,0 @@
import { RdDItem } from "../item.js";
import { RdDItemMaladie } from "./maladie.js";
export class RdDItemPoison extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/maladies_venins/venin.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.addPeriode(this.system.periode.nombre, this.system.periode.unite) ;
}
async onFinPeriode(oldTimestamp, newTimestamp) {
RdDItemMaladie.notifierMaladiePoison(this, oldTimestamp, newTimestamp)
}
}

View File

@ -1,13 +0,0 @@
import { RdDItem } from "../item.js";
export class RdDItemQueue extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/queue_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

View File

@ -1,31 +0,0 @@
const RARETE_COMMUNE = { code: 'Commune', label: 'Commune', frequence: 54, min: 27, max: 108 };
const RARETE_FREQUENTE = { code: 'Frequente', label: 'Fréquente', frequence: 18, min: 9, max: 36 };
const RARETE_RARE = { code: 'Rare', label: 'Rare', frequence: 6, min: 3, max: 12 };
const RARETE_RARISSIME = { code: 'Rarissime', label: 'Rarissime', frequence: 2, min: 1, max: 4 };
const RARETE_INEXISTANT = { code: 'Inexistant', label: 'Inexistant', frequence: 0, min: 0, max: 0 };
const RARETE_EGALE = { code: 'eqal', label: 'Egal', frequence: 1, min: 1, max: 1 };
const RARETES = [
RARETE_COMMUNE,
RARETE_FREQUENTE,
RARETE_RARE,
RARETE_RARISSIME,
RARETE_INEXISTANT,
]
export class RdDRaretes {
static rareteFrequente() { return RARETE_FREQUENTE; }
static rareteEgale() { return RARETE_EGALE; }
static raretes() { return RARETES; }
static byCode(code = undefined) {
return RARETES.find(it => it.code == code) ?? RARETE_FREQUENTE;
}
static getChamp(rarete, field = undefined) {
return RdDRaretes.byCode(rarete)[field ?? 'frequence'];
}
}

View File

@ -1,80 +0,0 @@
import { EffetsRencontre } from "../tmr/effets-rencontres.js";
import { RdDItem } from "../item.js";
const tableEffets = [
{ code: "messager", resultat: "succes", description: "Envoie un message à (force) cases", method: EffetsRencontre.messager },
{ code: "passeur", resultat: "succes", description: "Déplacer le demi-rêve à (force) cases", method: EffetsRencontre.passeur},
{ code: "reve+f", resultat: "succes", description: "Gain de (force) points de rêve" , method: EffetsRencontre.reve_plus_force},
{ code: "teleport", resultat: "succes", description: "Déplacer le demi-rêve (même type)", method: EffetsRencontre.teleportation_typecase },
{ code: "part+tete", resultat: "succes", description: "Tête de dragon sur réussite particulière", method: EffetsRencontre.rdd_part_tete },
{ code: "part+xp", resultat: "succes", description: "Expérience sur réussite particulière", method: EffetsRencontre.experience_particuliere },
{ code: "seuil", resultat: "succes", description: "Récupération de seuil de rêve", method: EffetsRencontre.regain_seuil },
{ code: "reve-1", resultat: "echec", description: "Perte de 1 point de rêve", method: EffetsRencontre.reve_moins_1 },
{ code: "reve-f", resultat: "echec", description: "Perte de (force) points de rêve", method: EffetsRencontre.reve_moins_force },
{ code: "vie-1", resultat: "echec", description: "Perte de 1 point de vie", method: EffetsRencontre.vie_moins_1 },
{ code: "reinsere", resultat: "echec", description: "Réinsertion aléatoire", method: EffetsRencontre.reinsertion },
{ code: "persistant", resultat: "echec", description: "Bloque le demi-rêve", method: EffetsRencontre.rencontre_persistante },
{ code: "teleport-aleatoire", resultat: "echec", description: "Déplacement aléatoire (même type)", method: EffetsRencontre.teleportation_aleatoire_typecase },
{ code: "aleatoire", resultat: "echec", description: "Déplacement aléatoire", method: EffetsRencontre.deplacement_aleatoire },
{ code: "sort-aleatoire", resultat: "echec", description: "Déclenche un sort en réserve aléatoire", method: EffetsRencontre.sort_aleatoire },
{ code: "rompu", resultat: "echec", description: "Demi-rêve interrompu", method: EffetsRencontre.demireve_rompu },
{ code: "echec-queue", resultat: "echec", description: "Queue(s) de dragon sur échec", method: EffetsRencontre.rdd_echec_queue },
{ code: "reve+1", resultat: "succes", description: "Gain de 1 point de rêve", method: EffetsRencontre.reve_plus_1 },
{ code: "vie-f", resultat: "echec", description: "Perte de (force) points de vie", method: EffetsRencontre.vie_moins_force },
{ code: "moral+1", resultat: "succes", description: "Gain de 1 point de moral", method: EffetsRencontre.moral_plus_1 },
{ code: "moral-1", resultat: "echec", description: "Perte de 1 point de moral", method: EffetsRencontre.moral_moins_1 },
{ code: "xpsort+f", resultat: "succes", description: "Gain de (force) xp sort", method: EffetsRencontre.xp_sort_force },
{ code: "endurance-1", resultat: "echec", description: "Perte de 1 point d'endurance", method: EffetsRencontre.end_moins_1 },
{ code: "endurance-f", resultat: "echec", description: "Perte de (force) points d'endurance", method: EffetsRencontre.end_moins_force },
{ code: "fatigue+1", resultat: "echec", description: "Coup de fatigue de 1 point", method: EffetsRencontre.fatigue_plus_1},
{ code: "fatigue+f", resultat: "echec", description: "Coup de fatigue de 1 (force) points", method: EffetsRencontre.fatigue_plus_force },
{ code: "fatigue-1", resultat: "succes", description: "Récupération de 1 point de fatigue", method: EffetsRencontre.fatigue_moins_1},
{ code: "fatigue-f", resultat: "succes", description: "Récupération de 1 (force) points de fatigue", method: EffetsRencontre.fatigue_moins_force },
{ code: "perte-chance", resultat: "echec", description: "Perte de chance actuelle", method: EffetsRencontre.perte_chance },
{ code: "stress+1", resultat: "succes", description: "Gain de 1 point de stress", method: EffetsRencontre.stress_plus_1 },
// { code: "epart-souffle", resultat: "echec", description: "Souffle de dragon sur échec particulier" },
];
export class RdDRencontre extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/tete_dragon.webp";
}
static getEffetsSucces() { return RdDRencontre.getEffets("succes"); }
static getEffetsEchec() { return RdDRencontre.getEffets("echec"); }
static getEffets(resultat) {
return tableEffets.filter(e => resultat == e.resultat);
}
static mapEffets(liste) {
return liste.map(it => RdDRencontre.getEffet(it));
}
static getListeEffets(item, reussite) {
if (reussite == 'echec') {
return [...item.system.echec.effets];
}
if (reussite == 'succes') {
return [...item.system.succes.effets];
}
return [];
}
static getEffet(code) {
return tableEffets.find(it => code == it.code)
}
static async appliquer(codes, tmrDialog, rencData) {
for(const effet of RdDRencontre.mapEffets(codes)){
await effet.method(tmrDialog, rencData);
}
}
async calculerFinPeriodeTemporel(debut) {
return await debut.nouvelleHeure().addHeures(12);
}
}

View File

@ -1,17 +0,0 @@
import { RdDItem } from "../item.js";
export class RdDItemService extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/services/lit.webp";
}
isService() { return true; }
getProprietes() {
return [
RdDItem.propertyIfDefined('Qualité', this.system.qualite, this.system.qualite != 0),
RdDItem.propertyIfDefined('Moral', 'Situation heureuse', this.system.moral),
RdDItem.propertyIfDefined('Coût', `${this.calculerPrixCommercant()} sols`),
];
}
}

View File

@ -1,120 +0,0 @@
import { HtmlUtility } from "../html-utility.js";
import { RdDItemSheet } from "../item-sheet.js";
import { Misc } from "../misc.js";
import { RdDRaretes } from "./raretes.js";
const TYPE_ITEMS_NATURELS = ["faune", "herbe", "plante", "ingredient"];
export class RdDItemInventaireSheet extends RdDItemSheet {
static get defaultOptions() {
return mergeObject(RdDItemSheet.defaultOptions, {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "informations" }]
});
}
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetBody = this.element.find(".sheet-body");
sheetBody.css("height", position.height - sheetHeader[0].clientHeight)
return position;
}
async getData() {
const formData = await super.getData();
return mergeObject(formData, {
milieux: await game.system.rdd.environnement.autresMilieux(this.item)
});
}
activateListeners(html) {
super.activateListeners(html);
HtmlUtility.showControlWhen(this.html.find("div.description-milieu"), TYPE_ITEMS_NATURELS.includes(this.item.type));
if (!this.options.editable) return;
this.html.find("a.preparer-nourriture").click(event => this.preparerNourriture(event));
this.html.find("a.manger-nourriture").click(event => this.mangerNourriture(event));
this.html.find("input.input-selection-milieu").keypress(event => {
if (event.keyCode == '13') {
this.onAddMilieu(event);
}
event.stopPropagation();
})
this.html.find("a.milieu-add").click(event => this.onAddMilieu(event));
this.html.find("div.environnement-milieu a.milieu-delete").click(event => this.onDeleteMilieu(event));
this.html.find("div.environnement-milieu select.environnement-rarete").change(event => this.onChange(event,
updated => this.$changeRarete(event, updated)));
this.html.find("div.environnement-milieu input[name='environnement-frequence']").change(event => this.onChange(event,
updated => this.$changeFrequence(event, updated)));
}
async preparerNourriture(event) {
if (this.actor && this.item.getUtilisationCuisine() == 'brut') {
await this.actor.preparerNourriture(this.item);
}
}
async mangerNourriture(event) {
if (this.actor && this.item.getUtilisation() == 'cuisine') {
await this.actor.mangerNourriture(this.item);
}
}
$changeFrequence(event, updated) {
updated.frequence = Number(this.html.find(event.currentTarget).val());
}
$changeRarete(event, updated) {
const code = this.html.find(event.currentTarget).val();
const rarete = RdDRaretes.byCode(code);
updated.rarete = rarete.code;
updated.frequence = rarete.frequence;
}
async onAddMilieu(event) {
const milieu = this.html.find('input.input-selection-milieu').val();
if (!milieu) {
ui.notifications.warn(`Choisissez le milieu dans lequel se trouve le/la ${this.item.name}`);
return
}
const list = this.item.getEnvironnements();
const exists = list.find(it => it.milieu == milieu);
if (exists) {
ui.notifications.warn(`${this.item.name} a déjà une rareté ${exists.rarete} en ${milieu} (fréquence: ${exists.frequence})`);
return
}
const rarete = RdDRaretes.rareteFrequente();
const added = { milieu, rarete: rarete.code, frequence: rarete.frequence };
const newList = [added, ...list].sort(Misc.ascending(it => it.milieu))
await this.item.update({ 'system.environnement': newList })
}
async onDeleteMilieu(event) {
const milieu = this.$getEventMilieu(event);
if (milieu != undefined) {
const newList = this.item.getEnvironnements().filter(it => it.milieu != milieu)
.sort(Misc.ascending(it => it.milieu));
await this.item.update({ 'system.environnement': newList });
}
}
async onChange(event, doMutation) {
const list = this.item.system.environnement;
const milieu = this.$getEventMilieu(event);
const updated = list.find(it => it.milieu == milieu);
if (updated) {
doMutation(updated);
const newList = [...list.filter(it => it.milieu != milieu), updated]
.sort(Misc.ascending(it => it.milieu));
await this.item.update({ 'system.environnement': newList });
}
}
$getEventMilieu(event) {
return this.html.find(event.currentTarget)?.parents("div.environnement-milieu").data("milieu");
}
}

View File

@ -1,62 +0,0 @@
import { RdDBaseActorSheet } from "../actor/base-actor-sheet.js";
import { RdDSheetUtility } from "../rdd-sheet-utility.js";
import { RdDUtility } from "../rdd-utility.js";
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDConteneurItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "conteneur" };
async getData() {
const formData = await super.getData();
if (this.actor) {
this.prepareConteneurData(formData);
}
return formData;
}
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
this.html.find('.conteneur-name a').click(async event => {
RdDUtility.toggleAfficheContenu(RdDSheetUtility.getItemId(event));
this.render(true);
});
}
/* -------------------------------------------- */
prepareConteneurData(formData) {
RdDBaseActorSheet.filterItemsPerTypeForSheet(formData, this.actor.itemTypes);
this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(formData.conteneurs, formData.objets);
formData.subItems = formData.conteneurs.find(it => it._id == this.item.id)?.subItems;
}
async _onDragStart(event) {
console.log("_onDragStart", event);
if (event.target.classList.contains("entity-link")) return;
const itemId = event.srcElement?.attributes["data-item-id"].value;
const item = this.actor.items.get(itemId);
// Create drag data
const dragData = {
actorId: this.actor.id,
type: "Item",
data: item.system,
uuid: item.uuid
};
event.dataTransfer.setData("text/plain", JSON.stringify(dragData));
}
async _onDropItem(event, dragData) {
if (this.actor) {
const destItemId = this.html.find(event.target)?.closest('.item').attr('data-item-id') ?? this.item.id
const dropParams = await RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, dragData, this.objetVersConteneur);
await this.actor.processDropItem(dropParams);
await this.render(true);
}
}
}

View File

@ -1,38 +0,0 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDFauneItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "faune" };
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
html.find("a.linked-actor-delete").click(event => this.onDeleteLinkedActor());
}
async _onDropActor(event, dragData) {
console.log('faune:dropActor', event, dragData)
const linkedActor = fromUuidSync(dragData.uuid);
if (linkedActor?.pack) {
this.item.update({
'system.actor.pack': linkedActor.pack,
'system.actor.id': linkedActor._id,
'system.actor.name': linkedActor.name
});
}
else {
ui.notifications.warn(`${linkedActor.name} ne provient pas d'un compendium.
<br>Choisissez une créature du compendium pour représenter un élément de faune générique`)
}
}
async onDeleteLinkedActor() {
this.item.update({
'system.actor.pack': '',
'system.actor.id': '',
'system.actor.name': ''
});
}
}

View File

@ -1,6 +0,0 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDHerbeItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "herbe" };
}

View File

@ -1,5 +0,0 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDIngredientItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "ingredient" };
}

View File

@ -1,7 +0,0 @@
import { RdDItemInventaireSheet } from "./sheet-base-inventaire.js";
export class RdDPlanteItemSheet extends RdDItemInventaireSheet {
static get ITEM_TYPE() { return "plante" };
}

View File

@ -1,78 +0,0 @@
import { RdDRencontre } from "./rencontre.js";
import { RdDItemSheet } from "../item-sheet.js";
export class RdDRencontreItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "rencontre" };
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }]
});
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetBody = this.element.find(".sheet-body");
sheetBody.css("height", position.height - sheetHeader[0].clientHeight)
return position;
}
/* -------------------------------------------- */
async getData() {
const formData = await super.getData();
mergeObject(formData, {
effets: {
succes: {
liste: RdDRencontre.getEffetsSucces(),
select: RdDRencontre.mapEffets(this.item.system.succes.effets)
},
echec: {
liste: RdDRencontre.getEffetsEchec(),
select: RdDRencontre.mapEffets(this.item.system.echec.effets)
}
}
});
return formData;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
if (!this.options.editable) return;
this.html.find("a.effet-add").click(event => this.onAddEffet(event));
this.html.find("a.effet-delete").click(event => this.onDeleteEffet(event));
}
async onAddEffet(event) {
const resultat = this.html.find(event.currentTarget)?.data("effet-resultat");
const keyEffets = `system.${resultat}.effets`;
const code = this.html.find(event.currentTarget)?.data("effet-code");
const liste = RdDRencontre.getListeEffets(this.item, resultat);
liste.push(code);
await this._updateEffetsRencontre(keyEffets, liste);
}
async onDeleteEffet(event) {
const resultat = this.html.find(event.currentTarget)?.data("effet-resultat");
const keyEffets = `system.${resultat}.effets`;
const pos = this.html.find(event.currentTarget)?.data("effet-pos");
const liste = RdDRencontre.getListeEffets(this.item, resultat);
liste.splice(pos, 1);
await this._updateEffetsRencontre(keyEffets, liste);
}
async _updateEffetsRencontre(key, liste) {
const updates = {};
updates[key] = liste;
this.item.update(updates);
}
}

View File

@ -1,16 +0,0 @@
import { RdDItemSheet } from "../item-sheet.js";
export class RdDServiceItemSheet extends RdDItemSheet {
static get ITEM_TYPE() { return "service" };
async getData() {
const formData = await super.getData();
formData.disabled = formData.options.isGM || formData.options.isOwned ? '' : 'disabled';
return formData;
}
activateListeners(html) {
super.activateListeners(html);
}
}

View File

@ -1,13 +0,0 @@
import { RdDItem } from "../item.js";
export class RdDItemSouffle extends RdDItem {
static get defaultIcon() {
return "systems/foundryvtt-reve-de-dragon/icons/souffle_dragon.webp";
}
async calculerFinPeriodeTemporel(debut) {
return await debut.appliquerDuree(this.system.duree, this.parent);
}
}

View File

@ -1,10 +1,5 @@
import { RdDBaseActor } from "./actor/base-actor.js";
import { LOG_HEAD, SYSTEM_RDD } from "./constants.js";
import { Grammar } from "./grammar.js";
import { Monnaie } from "./item-monnaie.js";
import { RdDItem } from "./item.js";
import { RdDTimestamp } from "./rdd-timestamp.js";
import { RdDRaretes } from "./item/raretes.js";
class Migration {
get code() { return "sample"; }
@ -33,39 +28,6 @@ class Migration {
}
class _1_5_34_migrationPngWebp {
get code() { return "migrationPngWebp"; }
get version() { return "1.5.34"; }
async migrate() {
const regexOldPngJpg = /(systems\/foundryvtt-reve-de-dragon\/icons\/.*)\.(png|jpg)/;
const replaceWithWebp = '$1.webp';
function convertImgToWebp(img) {
return img.replace(regexOldPngJpg, replaceWithWebp);
}
function prepareDocumentsImgUpdate(documents) {
return documents.filter(it => it.img && it.img.match(regexOldPngJpg))
.map(it => {
return { _id: it.id, img: convertImgToWebp(it.img) }
});
}
const itemsUpdates = prepareDocumentsImgUpdate(game.items);
const actorsUpdates = prepareDocumentsImgUpdate(game.actors);
//Migrate system png to webp
await Item.updateDocuments(itemsUpdates);
await Actor.updateDocuments(actorsUpdates);
game.actors.forEach(actor => {
if (actor.token?.img && actor.token.img.match(regexOldPngJpg)) {
actor.update({ "token.img": convertImgToWebp(actor.token.img) });
}
const actorItemsToUpdate = prepareDocumentsImgUpdate(actor.items);
actor.updateEmbeddedDocuments('Item', actorItemsToUpdate);
});
}
}
class _10_0_16_MigrationSortsReserve extends Migration {
get code() { return "creation-item-sort-reserve"; }
get version() { return "10.0.16"; }
@ -80,7 +42,7 @@ class _10_0_16_MigrationSortsReserve extends Migration {
await actor.createEmbeddedDocuments("Item", sortsReserve, {
renderSheet: false,
});
await actor.update({ 'system.reve.reserve': undefined })
await actor.update({ 'system.reve.reserve.list': [] })
});
}
@ -136,301 +98,12 @@ class _10_0_21_VehiculeStructureResistanceMax extends Migration {
}
}
class _10_0_33_MigrationNomsDraconic extends Migration {
get code() { return "competences-creature-parade"; }
get version() { return "10.0.33"; }
migrationNomDraconic(ancien) {
if (typeof ancien == 'string') {
switch (ancien) {
case 'oniros': case "Voie d'Oniros": return "Voie d'Oniros";
case 'hypnos': case "Voie d'Hypnos": return "Voie d'Hypnos";
case 'narcos': case "Voie de Narcos": return "Voie de Narcos";
case 'thanatos': case "Voie de Thanatos": return "Voie de Thanatos";
}
return ancien;
}
else if (typeof ancien.name == 'string') {
return this.migrationNomDraconic(ancien.name)
}
return ancien;
}
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => ["sort", "sortreserve"].includes(it.type)
&& (typeof it.system.draconic == 'string') || (typeof it.system.draconic?.name == 'string'))
.map(it => { return { _id: it.id, "system.draconic": this.migrationNomDraconic(it.system.draconic) } }));
}
}
class _10_2_5_ArmesTirLancer extends Migration {
constructor() {
super();
this.dagues = { "system.competence": 'Dague', "system.lancer": 'Dague de jet', "system.portee_courte": 3, "system.portee_moyenne": 8, "system.portee_extreme": 15 }
this.javelot = { "system.competence": 'Lance', "system.lancer": 'Javelot', "system.portee_courte": 6, "system.portee_moyenne": 12, "system.portee_extreme": 20 }
this.fouet = { "system.competence": '', "system.lancer": 'Fouet', "system.portee_courte": 2, "system.portee_moyenne": 2, "system.portee_extreme": 3, "system.penetration": -1 }
this.arc = { "system.competence": '', "system.tir": 'Arc' }
this.arbalete = { "system.competence": '', "system.tir": 'Arbalète' }
this.fronde = { "system.competence": '', "system.tir": 'Fronde' }
this.mappings = {
'dague': { filter: it => true, updates: this.dagues },
'dague de jet': { filter: it => true, updates: this.dagues },
'javelot': { filter: it => true, updates: this.javelot },
'lance': { filter: it => it.name == 'Javeline', updates: this.javelot },
'fouet': { filter: it => true, updates: this.fouet },
'arc': { filter: it => true, updates: this.arc },
'arbalete': { filter: it => true, updates: this.arbalete },
'fronde': { filter: it => true, updates: this.fronde },
}
}
get code() { return "separation-competences-tir-lancer"; }
get version() { return "10.2.5"; }
migrateArmeTirLancer(it) {
let updates = mergeObject({ _id: it.id }, this.getMapping(it).updates);
console.log(it.name, updates);
return updates;
}
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => "arme" == it.type)
.filter(it => this.isTirLancer(it))
.filter(it => this.getMapping(it).filter(it))
.map(it => this.migrateArmeTirLancer(it)));
}
isTirLancer(it) {
return Object.keys(this.mappings).includes(this.getCompKey(it));
}
getMapping(it) {
return this.mappings[this.getCompKey(it)];
}
getCompKey(it) {
return Grammar.toLowerCaseNoAccent(it.system.competence);
}
}
class _10_2_10_DesirLancinant_IdeeFixe extends Migration {
get code() { return "desir-lancinat-idee-fixe"; }
get version() { return "10.2.10"; }
migrateQueue(it) {
let categorie = undefined
let name = it.name
if (Grammar.toLowerCaseNoAccent(name).includes('desir')) {
categorie = 'lancinant';
name = it.name.replace('Désir lancinant : ', '');
}
if (Grammar.toLowerCaseNoAccent(name).includes('idee fixe')) {
categorie = 'ideefixe';
name = it.name.replace('Idée fixe : ', '')
}
return {
_id: it.id, name: name,
'system.ideefixe': undefined,
'system.lancinant': undefined,
'system.categorie': categorie
}
}
async migrate() {
await this.applyItemsUpdates(items => items
.filter(it => ['queue', 'ombre'].includes(it.type))
.map(it => this.migrateQueue(it))
);
}
}
class _10_3_0_Inventaire extends Migration {
get code() { return "migration-equipement-inventaire"; }
get version() { return "10.3.0"; }
async migrate() {
await this.applyItemsUpdates(items => {
return this._updatesMonnaies(items)
.concat(this._updatesNonEquipe(items))
.concat(this._updatesObjets(items))
});
}
_updatesNonEquipe(items) {
return items
.filter(it => ['munition'].includes(it.type))
.map(it => { return { _id: it.id, 'system.equipe': undefined } });
}
_updatesObjets(items) {
return items
.filter(it => ['objet'].includes(it.type))
.map(it => { return { _id: it.id, 'system.resistance': undefined, 'system.equipe': undefined } });
}
_updatesMonnaies(items) {
return items
.filter(it => ['monnaie'].includes(it.type) && it.system.valeur_deniers != undefined)
.map(it => { return { _id: it.id, 'system.cout': it.system.valeur_deniers / 100, 'system.valeur_deniers': undefined } });
}
}
class _10_3_0_FrequenceEnvironnement extends Migration {
get code() { return "migration-frequence-resources"; }
get version() { return "10.3.0"; }
async migrate() {
await this.applyItemsUpdates(items => items.filter(it => ['herbe', 'ingredient'].includes(it.type))
.map(it => this._updatesFrequences(it)));
}
_updatesFrequences(it) {
const rarete = RdDRaretes.byCode(it.system.rarete);
return {
_id: it.id,
'system.rarete': undefined,
'system.environnement': [{ milieu: it.system.milieu, rarete: rarete.code, frequence: rarete.frequence }]
}
}
}
class _10_3_17_Monnaies extends Migration {
constructor() {
super();
this.mapValeur = {
"Etain (1 denier)": { name: 'Denier (étain)', system: { cout: 0.01 } },
"Bronze (10 deniers)": { name: "Sou (bronze)", system: { cout: 0.1 } },
"Argent (1 sol)": { name: "Sol (argent)", system: { cout: 1 } },
"Or (10 sols)": { name: "Dragon (or)", system: { cout: 10 } }
};
}
get code() { return "migration-monnaies"; }
get version() { return "10.3.17"; }
async migrate() {
await this.applyItemsUpdates(items => this._updatesMonnaies(items));
}
_updatesMonnaies(items) {
return items
.filter(it => 'monnaie' == it.type)
.filter(it => this.mapValeur[it.name] != undefined)
.map(it => {
const correction = this.mapValeur[it.name];
return {
_id: it.id,
'name': correction.name,
'system.cout': correction.system.cout,
'system.valeur_deniers': undefined
}
});
}
}
class _10_4_6_ServicesEnCommerces extends Migration {
get code() { return "migration-service-acteurs"; }
get version() { return "10.4.6"; }
async migrate() {
const servicesToMigrate = game.items.filter(it => it.type == 'service');
servicesToMigrate.forEach(async service => {
const commerce = await this.convertServiceToCommerce(service);
await RdDBaseActor.create(commerce, { renderSheet: false });
await service.delete();
});
}
async convertServiceToCommerce(service) {
return {
name: service.name, img: service.img, type: 'commerce',
system: {
description: service.system.description,
notesmj: service.system.descriptionmj,
illimite: service.system.illimite
},
items: await this.transformInventaireCommerce(service)
}
}
async transformInventaireCommerce(service) {
const serviceItems = (service.system.items ?? []);
const commerceItems = await Promise.all(serviceItems.map(async (it) => { return await this.transformToItemBoutique(it); }));
return commerceItems.concat(Monnaie.monnaiesStandard());
}
async transformToItemBoutique(serviceRefItem) {
const item = await RdDItem.getCorrespondingItem(serviceRefItem);
const itemToCreate = {
name: item.name, img: item.img, type: item.type,
system: mergeObject({ cout: serviceRefItem.system.cout, quantite: serviceRefItem.system.quantite }, item.system, { overwrite: false })
};
return itemToCreate;
}
}
class _10_5_0_UpdatePeriodicite extends Migration {
get code() { return "migration-periodicite-poisons-maladies"; }
get version() { return "10.5.0"; }
async migrate() {
await this.applyItemsUpdates(items => this._updatePeriodicite(items));
}
_updatePeriodicite(items) {
return items.filter(it => ['poison', 'maladie'].includes(it.type))
.filter(it => it.system.periodicite != "")
.map(it => {
let [incubation, periodicite] = this.getPeriodicite(it);
const periode = periodicite.split(' ');
let unite = periode.length == 2
? RdDTimestamp.formulesPeriode().find(it => Grammar.includesLowerCaseNoAccent(periode[1], it.code))?.code
: undefined
if (unite && Number(periode[0])) {
return {
_id: it.id,
'system.periodicite': undefined,
'system.incubation': incubation,
'system.periode.nombre': Number.parseInt(periode[0]),
'system.periode.unite': unite
};
}
else {
return {
_id: it.id,
'system.periodicite': undefined,
'system.incubation': it.system.periodicite
};
}
}).filter(it => it != undefined);
}
getPeriodicite(it) {
let p = it.system.periodicite.split(/[\/\\]/);
switch (p.length) {
case 2: return [p[0].trim(), p[1].trim()];
case 1: return ["", it.system.periodicite.trim()];
default: return [it.system.periodicite.trim(), ""];
}
}
}
export class Migrations {
static getMigrations() {
return [
new _1_5_34_migrationPngWebp(),
new _10_0_16_MigrationSortsReserve(),
new _10_0_17_MigrationCompetenceCreature(),
new _10_0_21_VehiculeStructureResistanceMax(),
new _10_0_33_MigrationNomsDraconic(),
new _10_2_5_ArmesTirLancer(),
new _10_2_10_DesirLancinant_IdeeFixe(),
new _10_3_0_Inventaire(),
new _10_3_0_FrequenceEnvironnement(),
new _10_3_17_Monnaies(),
new _10_4_6_ServicesEnCommerces(),
new _10_5_0_UpdatePeriodicite(),
];
}
@ -445,12 +118,20 @@ export class Migrations {
}
migrate() {
const currentVersion = game.settings.get(SYSTEM_RDD, "systemMigrationVersion");
const currentVersion = game.settings.get(
SYSTEM_RDD,
"systemMigrationVersion"
);
if (isNewerVersion(game.system.version, currentVersion)) {
//if (true) { /* comment previous and uncomment here to test before upgrade */
const migrations = Migrations.getMigrations().filter(m => isNewerVersion(m.version, currentVersion));
if (migrations.length > 0) {
migrations.sort((a, b) => this.compareVersions(a, b));
migrations.sort((a, b) =>
isNewerVersion(a.version, b.version)
? 1
: isNewerVersion(b.version, a.version)
? -1
: 0
);
migrations.forEach(async (m) => {
ui.notifications.info(
`Executing migration ${m.code}: version ${currentVersion} is lower than ${m.version}`
@ -476,8 +157,4 @@ export class Migrations {
console.log(LOG_HEAD + `No system version changed`);
}
}
compareVersions(a, b) {
return isNewerVersion(a.version, b.version) ? 1 : isNewerVersion(b.version, a.version) ? -1 : 0;
}
}

View File

@ -24,7 +24,7 @@ export class Misc {
}
static sum() {
return (a, b) => Number(a) + Number(b);
return (a, b) => a + b;
}
static ascending(orderFunction = x => x) {
@ -41,14 +41,6 @@ export class Misc {
return 0;
}
static typeName(type, subType) {
return subType ? game.i18n.localize(`${type.toUpperCase()}.Type${Misc.upperFirst(subType)}`)
: '';
}
static arrayOrEmpty(items) {
return items?.length ? items : [];
}
/**
* Converts the value to an integer, or to 0 if undefined/null/not representing integer
* @param {*} value value to convert to an integer using parseInt
@ -76,20 +68,6 @@ export class Misc {
}
}
static indexLowercase(list) {
const obj = {};
const addToObj = (map, val) => {
const key = Grammar.toLowerCaseNoAccent(val);
if (key && !map[key]) map[key] = val
}
list.forEach(it => addToObj(obj, it))
return obj;
}
static concat(lists) {
return lists.reduce((a, b) => a.concat(b), []);
}
static classify(items, classifier = it => it.type) {
let itemsBy = {}
Misc.classifyInto(itemsBy, items, classifier)
@ -124,11 +102,7 @@ export class Misc {
}
static join(params, separator = '') {
return params?.reduce(Misc.joining(separator)) ?? '';
}
static joining(separator = '') {
return (a, b) => a + separator + b;
return params.reduce((a, b) => a + separator + b);
}
static connectedGMOrUser(ownerId = undefined) {
@ -138,27 +112,20 @@ export class Misc {
return Misc.firstConnectedGM()?.id ?? game.user.id;
}
static isRollModeHiddenToPlayer() {
switch (game.settings.get("core", "rollMode")) {
case CONST.DICE_ROLL_MODES.BLIND:
case CONST.DICE_ROLL_MODES.SELF: return true;
}
return false
}
static getActiveUser(id) {
return game.users.find(u => u.id == id && u.active);
}
}
static firstConnectedGM() {
return game.users.filter(u => u.isGM && u.active).sort(Misc.ascending(u => u.id)).find(u => u.isGM && u.active);
}
static isOwnerPlayer(actor, user = undefined) {
static isOwnerPlayer(actor, user=undefined) {
return actor.testUserPermission(user ?? game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER)
}
static isOwnerPlayerOrUniqueConnectedGM(actor, user = undefined) {
static isOwnerPlayerOrUniqueConnectedGM(actor, user =undefined){
return Misc.isOwnerPlayer(actor, user) ?? Misc.isUniqueConnectedGM();
}
@ -166,11 +133,7 @@ export class Misc {
* @returns true pour un seul utilisateur: le premier GM connecté par ordre d'id
*/
static isUniqueConnectedGM() {
return game.user.id == Misc.firstConnectedGMId();
}
static firstConnectedGMId() {
return Misc.firstConnectedGM()?.id;
return game.user.id == Misc.firstConnectedGM()?.id;
}
/* -------------------------------------------- */
@ -228,4 +191,4 @@ export class Misc {
}
return subset;
}
}
}

View File

@ -1,13 +1,72 @@
import { Misc } from "./misc.js"
import { RdDDice } from "./rdd-dice.js";
import { SystemCompendiums } from "./settings/system-compendiums.js";
const poesieHautReve = [
{
reference: 'Le Ratier Bretonien',
extrait: `Le courant du Fleuve
<br>Te domine et te Porte
<br>Avant que tu te moeuves
<br>Combat le, ou il t'emporte`
},
{
reference: 'Incompatibilité, Charles Beaudelaire',
extrait: `Et lorsque par hasard une nuée errante
<br>Assombrit dans son vol le lac silencieux,
<br>On croirait voir la robe ou l'ombre transparente
<br>D'un esprit qui voyage et passe dans les cieux.`
},
{
reference: 'Au fleuve de Loire, Joachim du Bellay',
extrait: `Ô de qui la vive course
<br>Prend sa bienheureuse source,
<br>Dune argentine fontaine,
<br>Qui dune fuite lointaine,
<br>Te rends au sein fluctueux
<br>De lOcéan monstrueux`
},
{
reference: 'Denis Gerfaud',
extrait: `Et l'on peut savoir qui est le maître d'Oniros, c'est le Fleuve de l'Oubli.
Et l'on sait qui est le créateur du Fleuve de l'Oubli, c'est Hypnos et Narcos.
Mais l'on ne sait pas qui est le maître du Fleuve de l'Oubli,
sinon peut-être lui-même, ou peut-être Thanatos` },
{
reference: 'Denis Gerfaud',
extrait: `Narcos est la source du Fleuve de l'Oubli et Hypnos l'embouchure
Remonter le Fleuve est la Voie de la Nuit, la Voie du Souvenir.
Descendre le Fleuve est la Voie du Jour, la Voie de l'Oubli`
},
{
reference: 'Denis Gerfaud',
extrait: `Narcos engendre le fils dont il est la mère à l'heure du Vaisseau,
car Oniros s'embarque pour redescendre le Fleuve
vers son père Hypnos sur la Voie de l'Oubli`
},
{
reference: 'Denis Gerfaud',
extrait: `Hypnos engendre le fils dont il est la mère à l'heure du Serpent, car
tel les serpents, Oniros commence à remonter le Fleuve
sur le Voie du Souvenir vers son père Narcos`
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi se succèdent les Jours et les Ages.
<br>Les jours des Dragons sont les Ages des Hommes.`
},
{
reference: 'Denis Gerfaud',
extrait: `Ainsi parlent les sages:
&laquo;Les Dragons sont créateurs de leurs rêves, mais ils ne sont pas créateurs d'Oniros
Les Dragons ne sont pas les maîtres de leurs rêvezs, car ils ne sont pas maîtres d'Oniros.
Nul ne sait qui est le créateur des Dragons, ni qui est leur maître.
Mais l'on peut supposer qui est le maître du Rêve des Dragons, c'est Oniros&raquo;`
},
]
export class Poetique {
static async getExtrait() {
const items = await SystemCompendiums.getItems('extrait-poetique', 'extraitpoetique')
const selected = await RdDDice.rollOneOf(items);
return {
reference: selected?.name,
extrait: selected?.system.extrait
}
static async getExtrait(){
return await RdDDice.rollOneOf(poesieHautReve);
}
}
}

44
module/poetique.txt Normal file
View File

@ -0,0 +1,44 @@
Le courant du Fleuve
Te domine et te Porte
Avant que tu te moeuves
Combat le, ou il t'emporte
A vous qui faites ripaille
sourds aux damnés de la faim
à vous qui livrez
une inégale bataille
à ceux qui vous tendent la main
Ils sont tout près ! - Tenons fermée
<br>Cette salle, où nous les narguons.
<br>Quel bruit dehors ! Hideuse armée
<br>De vampires et de dragons !
<br>La poutre du toit descellée
<br>Ploie ainsi qu'une herbe mouillée,
<br>Et la vieille porte rouillée
<br>Tremble, à déraciner ses gonds !`),
https://www.poetica.fr/poeme-1423/guy-de-maupassant-le-sommeil-du-mandarin/
Le monde est un rêve de Dragons. Nous
ne savons pas qui sont les Dragons ni à quoi
ils ressemblent, en dépit de lantique iconographie qui les dépeint comme de gigantesques créatures ailées capables de cracher
feu et flammes.
Car parmi les humains, autre nom lui est donné,
Nom sinistre parmi tous, nom funèbre, c'est la mort!
Un ami disparu... Thanatos est passé...
Messieurs, ne crachez pas de jurons ni d'ordure
Au visage fardé de cette pauvre impure
Que déesse Famine a par un soir d'hiver,
Contrainte à relever ses jupons en plein air.

View File

@ -8,11 +8,13 @@ export class RdDAlchimie {
/* -------------------------------------------- */
static processManipulation(recette, actorId = undefined) {
//console.log("CALLED", recette, recette.isOwned, actorId );
let manip = recette.system.manipulation;
let matchArray = manip.match(matchOperations);
if (matchArray) {
for (let matchStr of matchArray) {
let result = matchStr.match(matchOperationTerms);
//console.log("RESULT ", result);
if (result[1] && result[2]) {
let commande = Misc.upperFirst(result[1]);
let replacement = this[`_alchimie${commande}`](recette, result[2], actorId);
@ -25,19 +27,20 @@ export class RdDAlchimie {
/* -------------------------------------------- */
static _alchimieCouleur(recette, couleurs, actorId) {
return RdDAlchimie._alchimieLink(recette, couleurs, actorId, 'couleur', 'Température');
if (actorId) {
return `<span class="alchimie-tache"><a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="couleur" data-alchimie-data="${couleurs}">couleur ${couleurs}</a></span>`;
} else {
return `<span class="alchimie-tache">couleur ${couleurs} </span>`;
}
}
/* -------------------------------------------- */
static _alchimieConsistance(recette, consistances, actorId) {
return RdDAlchimie._alchimieLink(recette, consistances, actorId, 'consistance', 'Consistance');
}
static _alchimieLink(recette, termes, actorId, tacheAlchimie, labelTache) {
const difficulte = RdDAlchimie.getDifficulte(termes);
const link = actorId ? ` <a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="${tacheAlchimie}" data-alchimie-data="${termes}">` : '';
const endLink = actorId ? '</a>' : '';
return `<span class="alchimie-tache">${link}${labelTache} ${termes} (${difficulte})${endLink}</span>`;
if (actorId) {
return `<span class="alchimie-tache"><a data-recette-id="${recette._id}" data-actor-id="${actorId}" data-alchimie-tache="consistance" data-alchimie-data="${consistances}">consistance ${consistances}</a></span>`;
} else {
return `<span class="alchimie-tache">consistance ${consistances} </span>`;
}
}
/* -------------------------------------------- */

View File

@ -3,37 +3,26 @@
* Extend the base Dialog entity by defining a custom window to perform roll.
* @extends {Dialog}
*/
export class RdDAstrologieEditeur extends Dialog {
export class RdDAstrologieEditeur extends Dialog {
/* -------------------------------------------- */
constructor(html, calendrier, calendrierData) {
let myButtons = {
resetButton: { label: "Re-tirer les nombres astraux", callback: html => this.resetNombreAstraux() },
saveButton: { label: "Fermer", callback: html => {} }
};
resetButton: { label: "Re-tirer les nombres astraux", callback: html => this.resetNombreAstraux() },
saveButton: { label: "Fermer", callback: html => this.fillData() }
};
// Common conf
let dialogConf = { content: html, title: "Editeur d'Astrologie", buttons: myButtons, default: "saveButton" };
let dialogOptions = {
classes: ["rdd-roll-dialog"], width: 600,
height: 'fit-content',
'max-height': 800,
'z-index': 99999
}
let dialogOptions = { classes: ["rdddialog"], width: 600, height: 300, 'z-index': 99999 }
super(dialogConf, dialogOptions)
this.calendrier = calendrier;
this.updateData(calendrierData);
this.updateData( calendrierData );
}
activateListeners(html) {
super.activateListeners(html);
this.html = html;
}
/* -------------------------------------------- */
/* -------------------------------------------- */
async resetNombreAstraux() {
game.system.rdd.calendrier.resetNombreAstral();
await game.system.rdd.calendrier.rebuildListeNombreAstral();
@ -41,9 +30,24 @@ export class RdDAstrologieEditeur extends Dialog {
game.system.rdd.calendrier.showAstrologieEditor();
}
/* -------------------------------------------- */
fillData( ) {
}
/* -------------------------------------------- */
updateData(calendrierData) {
updateData( calendrierData ) {
this.calendrierData = duplicate(calendrierData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
let astrologieData = this.astrologieData;
$(function () {
});
}
}

View File

@ -10,7 +10,7 @@ import { SYSTEM_SOCKET_ID } from "./constants.js";
export class RdDAstrologieJoueur extends Dialog {
/* -------------------------------------------- */
static async create(actor) {
static async create(actor, dialogConfig) {
let dialogData = {
nombres: this.organizeNombres(actor),
@ -20,43 +20,33 @@ export class RdDAstrologieJoueur extends Dialog {
astrologie: RdDItemCompetence.findCompetence(actor.items, 'Astrologie')
}
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-astrologie-joueur.html', dialogData);
const options = { classes: ["rdd-roll-dialog"], width: 600, height: 'fit-content', 'z-index': 99999 };
const dialog = new RdDAstrologieJoueur(html, actor, dialogData, options);
dialog.render(true);
let options = { classes: ["rdddialog"], width: 600, height: 500, 'z-index': 99999 };
if (dialogConfig.options) {
mergeObject(options, dialogConfig.options, { overwrite: true });
}
return new RdDAstrologieJoueur(html, actor, dialogData);
}
/* -------------------------------------------- */
constructor(html, actor, dialogData, dialogOptions) {
const dialogConf = {
title: "Nombres Astraux",
content: html,
default: "saveButton",
buttons: {
saveButton: { label: "Fermer", callback: html => {} }
},
constructor(html, actor, dialogData) {
let myButtons = {
saveButton: { label: "Fermer", callback: html => this.quitDialog() }
};
// Get all n
// Common conf
let dialogConf = { content: html, title: "Nombres Astraux", buttons: myButtons, default: "saveButton" };
let dialogOptions = { classes: ["rdddialog"], width: 600, height: 300, 'z-index': 99999 };
super(dialogConf, dialogOptions);
this.actor = actor;
this.dataNombreAstral = duplicate(dialogData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.html.find("[name='diffConditions']").val(0);
this.html.find('[name="jet-astrologie"]').click((event) => {
this.requestJetAstrologie();
});
}
/* -------------------------------------------- */
static organizeNombres(actor) {
let itemNombres = actor.listItems('nombreastral');
let itemNombres = actor.listItemsData('nombreastral');
let itemFiltered = {};
for (let item of itemNombres) {
if (itemFiltered[item.system.jourindex]) {
@ -78,8 +68,8 @@ export class RdDAstrologieJoueur extends Dialog {
carac_vue: this.actor.system.carac['vue'].value,
etat: this.dataNombreAstral.etat,
astrologie: this.dataNombreAstral.astrologie,
conditions: this.html.find('[name="diffConditions"]').val(),
date: this.html.find('[name="joursAstrologie"]').val(),
conditions: $("#diffConditions").val(),
date: $("#joursAstrologie").val(),
userId: game.user.id
}
if (Misc.isUniqueConnectedGM()) {
@ -93,4 +83,21 @@ export class RdDAstrologieJoueur extends Dialog {
this.close();
}
/* -------------------------------------------- */
quitDialog() {
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
$(function () {
$("#diffConditions").val(0);
});
html.find('#jet-astrologie').click((event) => {
this.requestJetAstrologie();
});
}
}

View File

@ -1,4 +1,4 @@
import { RdDTimestamp } from "./rdd-timestamp.js";
import { Misc } from "./misc.js";
/**
* Extend the base Dialog entity by defining a custom window to perform roll.
@ -8,60 +8,50 @@ export class RdDCalendrierEditeur extends Dialog {
/* -------------------------------------------- */
constructor(html, calendrier, calendrierData) {
let dialogConf = {
content: html,
title: "Editeur de date/heure",
buttons: {
save: { label: "Enregistrer", callback: html => this.saveCalendrier() }
},
default: "save"
};
let dialogOptions = { classes: ["rdd-dialog-calendar-editor"], width: 400, height: 'fit-content', 'z-index': 99999 }
super(dialogConf, dialogOptions)
let myButtons = {
saveButton: { label: "Enregistrer", callback: html => this.fillData() }
};
// Common conf
let dialogConf = { content: html, title: "Editeur de date/heure", buttons: myButtons, default: "saveButton" };
let dialogOptions = { classes: ["rdddialog"], width: 400, height: 300, 'z-index': 99999 }
super(dialogConf, dialogOptions)
this.calendrier = calendrier;
this.calendrierData = calendrierData;
this.calendrierData = calendrierData; //duplicate(calendrierData);
}
/* -------------------------------------------- */
fillData( ) {
this.calendrierData.moisKey = $("#nomMois").val();
this.calendrierData.heureKey = $("#nomHeure").val();
this.calendrierData.jourMois = $("#jourMois").val();
this.calendrierData.minutesRelative = $("#minutesRelative").val();
console.log("UPDATE ", this.calendrierData);
this.calendrier.saveEditeur( this.calendrierData )
}
/* -------------------------------------------- */
updateData( calendrierData ) {
this.calendrierData = duplicate(calendrierData);
}
/* -------------------------------------------- */
activateListeners(html) {
super.activateListeners(html);
this.html = html;
let calendrierData = this.calendrierData;
this.html.find("input[name='calendar.annee']").val(this.calendrierData.annee);
this.html.find("select[name='calendar.mois']").val(this.calendrierData.mois.key);
this.html.find("select[name='calendar.heure']").val(this.calendrierData.heure.key);
RdDCalendrierEditeur.setLimited(this.html.find("input[name='calendar.jourDuMois']"), this.calendrierData.jourDuMois, 1, 28);
RdDCalendrierEditeur.setLimited(this.html.find("input[name='calendar.minute']"), this.calendrierData.minute, 0, 119);
}
static setLimited(input, init, min, max) {
input.val(init);
input.change(event => {
const val = Number.parseInt(input.val());
if (val < min) {
input.val(min);
}
if (val > max) {
input.val(max);
}
$(function () {
console.log(calendrierData);
$("#nomMois").val(calendrierData.moisKey);
$("#nomHeure").val(calendrierData.heureKey);
$("#jourMois").val(calendrierData.jourMois);
$("#minutesRelative").val(calendrierData.minutesRelative);
});
}
/* -------------------------------------------- */
saveCalendrier() {
const annee = Number.parseInt(this.html.find("input[name='calendar.annee']").val());
const mois = this.html.find("select[name='calendar.mois']").val();
const jour = Number.parseInt(this.html.find("input[name='calendar.jourDuMois']").val());
const heure = this.html.find("select[name='calendar.heure']").val();
const minute = Number.parseInt(this.html.find("input[name='calendar.minute']").val());
this.calendrier.setNewTimestamp(RdDTimestamp.timestamp(annee, mois, jour, heure, minute))
}
/* -------------------------------------------- */
updateData(calendrierData) {
this.calendrierData = duplicate(calendrierData);
}
}

View File

@ -1,35 +1,73 @@
/* -------------------------------------------- */
import { RdDCalendrierEditeur } from "./rdd-calendrier-editeur.js";
import { RdDAstrologieEditeur } from "./rdd-astrologie-editeur.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDUtility } from "./rdd-utility.js";
import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js";
import { Misc } from "./misc.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { DialogChronologie } from "./dialog-chronologie.js";
import { RdDTimestamp, WORLD_TIMESTAMP_SETTING } from "./rdd-timestamp.js";
import { DialogChateauDormant } from "./sommeil/dialog-chateau-dormant.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
const RDD_JOUR_PAR_MOIS = 28;
/* -------------------------------------------- */
const dossierIconesHeures = 'systems/foundryvtt-reve-de-dragon/icons/heures/'
const heuresList = ["vaisseau", "sirene", "faucon", "couronne", "dragon", "epees", "lyre", "serpent", "poissonacrobate", "araignee", "roseau", "chateaudormant"];
const heuresDef = {
"vaisseau": { label: "Vaisseau", lettreFont: 'v', saison: "printemps", heure: 0, icon: 'hd01.svg' },
"sirene": { label: "Sirène", lettreFont: 'i', saison: "printemps", heure: 1, icon: 'hd02.svg' },
"faucon": { label: "Faucon", lettreFont: 'f', saison: "printemps", heure: 2, icon: 'hd03.svg' },
"couronne": { label: "Couronne", lettreFont: '', saison: "ete", heure: 3, icon: 'hd04.svg' },
"dragon": { label: "Dragon", lettreFont: 'd', saison: "ete", heure: 4, icon: 'hd05.svg' },
"epees": { label: "Epées", lettreFont: 'e', saison: "ete", heure: 5, icon: 'hd06.svg' },
"lyre": { label: "Lyre", lettreFont: 'l', saison: "automne", heure: 6, icon: 'hd07.svg' },
"serpent": { label: "Serpent", lettreFont: 's', saison: "automne", heure: 7, icon: 'hd08.svg' },
"poissonacrobate": { label: "Poisson Acrobate", lettreFont: 'p', saison: "automne", heure: 8, icon: 'hd09.svg' },
"araignee": { label: "Araignée", lettreFont: 'a', saison: "hiver", heure: 9, icon: 'hd10.svg' },
"roseau": { label: "Roseau", lettreFont: 'r', saison: "hiver", heure: 10, icon: 'hd11.svg' },
"chateaudormant": { label: "Château Dormant", lettreFont: 'c', saison: "hiver", heure: 11, icon: 'hd12.svg' }
};
const saisonsDef = {
"printemps": { label: "Printemps" },
"ete": { label: "Eté" },
"automne": { label: "Automne" },
"hiver": { label: "Hiver" }
};
const RDD_MOIS_PAR_AN = 12;
export const RDD_JOUR_PAR_MOIS = 28;
const RDD_HEURES_PAR_JOUR = 12;
const RDD_MINUTES_PAR_HEURES = 120;
const MAX_NOMBRE_ASTRAL = 12;
const JOURS_DU_MOIS = Array(RDD_JOUR_PAR_MOIS).fill().map((item, index) => 1 + index);
/* -------------------------------------------- */
export class RdDCalendrier extends Application {
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html",
popOut: false,
resizable: false
});
}
static createCalendrierPos() {
return { top: 200, left: 200 };
}
static getDefSigne(chiffre) {
chiffre = chiffre % RDD_MOIS_PAR_AN;
return Object.values(heuresDef).find(h => h.heure == chiffre);
}
static getChiffreFromSigne(signe) {
return heuresList.indexOf(signe);
}
static getCalendrier(index) {
let calendrier = {
heureRdD: 0, // Index dans heuresList / heuresDef[x].heure
minutesRelative: 0,
indexJour: index,
annee: Math.floor(index / (RDD_JOUR_PAR_MOIS * RDD_MOIS_PAR_AN)),
moisRdD: Math.floor(index / RDD_JOUR_PAR_MOIS) % RDD_MOIS_PAR_AN,
jour: (index % RDD_JOUR_PAR_MOIS) // Le calendrier stocke le jour en 0-27, mais en 1-28 à l'affichage
}
calendrier.moisLabel = RdDCalendrier.getDefSigne(calendrier.moisRdD).label;
return calendrier;
}
constructor() {
super();
// position
@ -38,48 +76,479 @@ export class RdDCalendrier extends Application {
this.calendrierPos = RdDCalendrier.createCalendrierPos();
game.settings.set(SYSTEM_RDD, "calendrier-pos", this.calendrierPos);
}
// Calendrier
this.timestamp = RdDTimestamp.getWorldTime();
this.calendrier = duplicate(game.settings.get(SYSTEM_RDD, "calendrier") ?? RdDCalendrier.getCalendrier(0));
this.calendrier.annee = this.calendrier.annee ?? Math.floor((this.calendrier.moisRdD ?? 0) / RDD_MOIS_PAR_AN);
this.calendrier.moisRdD = (this.calendrier.moisRdD ?? 0) % RDD_MOIS_PAR_AN;
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp);
game.settings.set(SYSTEM_RDD, "calendrier", this.calendrier);
this.listeNombreAstral = this.getListeNombreAstral();
this.rebuildListeNombreAstral(HIDE_DICE); // Ensure always up-to-date
this.rebuildListeNombreAstral(false); // Ensure always up-to-date
}
console.log('RdDCalendrier.constructor()', this.timestamp, this.timestamp.toCalendrier(), this.calendrierPos, this.listeNombreAstral);
Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
console.log(this.calendrier, this.calendrierPos, this.listeNombreAstral);
}
async onUpdateSetting(setting, update, options, id) {
if (setting.key == SYSTEM_RDD + '.' + WORLD_TIMESTAMP_SETTING) {
this.timestamp = RdDTimestamp.getWorldTime();
this.updateDisplay();
/* -------------------------------------------- */
getListeNombreAstral() {
return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? [];
}
/* -------------------------------------------- */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
template: "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html",
popOut: false,
resizable: false
});
}
/* -------------------------------------------- */
getDateFromIndex(index) {
const date = RdDCalendrier.getCalendrier(index ?? this.getCurrentDayIndex());
return (date.jour + 1) + ' ' + RdDCalendrier.getDefSigne(date.moisRdD).label;
}
/* -------------------------------------------- */
getNumericDateFromIndex(index = undefined) {
const dateRdD = RdDCalendrier.getCalendrier(index ?? this.getCurrentDayIndex());
return {
day: dateRdD.jour + 1,
month: heuresList[dateRdD.moisRdD]
}
}
/* -------------------------------------------- */
getCurrentHeure() {
return heuresList[this.calendrier.heureRdD];
}
/* -------------------------------------------- */
getCurrentDayIndex() {
return (((this.calendrier.annee ?? 0) * RDD_MOIS_PAR_AN + (this.calendrier.moisRdD ?? 0)) * RDD_JOUR_PAR_MOIS) + (this.calendrier.jour ?? 0);
}
/* -------------------------------------------- */
getIndexFromDate(jour, mois) {
return (heuresDef[mois].heure * RDD_JOUR_PAR_MOIS) + jour - 1;
}
/* -------------------------------------------- */
getJoursSuivants(num) {
let jours = [];
let index = this.getCurrentDayIndex();
for (let i = 0; i < num; i++) {
jours[i] = { label: this.getDateFromIndex(index + i), index: index + i };
}
return jours;
}
/* -------------------------------------------- */
async ajouterNombreAstral(index, showDice = SHOW_DICE) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" });
const dateFuture = this.getDateFromIndex(index);
if (showDice != HIDE_DICE) {
ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients("GM"),
content: `Le chiffre astrologique du ${dateFuture} sera le ${nombreAstral}`
});
}
return {
nombreAstral: nombreAstral,
valeursFausses: [],
index: index
}
}
/* -------------------------------------------- */
getCurrentNombreAstral() {
let indexDate = this.getCurrentDayIndex();
return this.getNombreAstral(indexDate);
}
/* -------------------------------------------- */
resetNombreAstral() {
this.listeNombreAstral = [];
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_reset_nombre_astral",
data: {}
});
}
/* -------------------------------------------- */
getNombreAstral(indexDate) {
let astralData = this.getListeNombreAstral().find((nombreAstral, i) => nombreAstral.index == indexDate);
return astralData?.nombreAstral;
}
/* -------------------------------------------- */
async rebuildListeNombreAstral(showDice = SHOW_DICE) {
if (Misc.isUniqueConnectedGM()) {
console.log("rebuildListeNombreAstral", showDice);
let jourCourant = this.getCurrentDayIndex();
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
let dayIndex = jourCourant + i;
let na = this.listeNombreAstral.find(n => n.index == dayIndex);
if (na) {
newList[i] = na;
} else {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
}
}
//console.log("SAVE list", newList, jourCourant);
this.listeNombreAstral = newList;
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
}
}
/* -------------------------------------------- */
async onCalendarButton(ev) {
ev.preventDefault();
const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
if (calendarAvance) {
await this.incrementTime(Number(calendarAvance.value));
}
else if (calendarSet) {
this.positionnerHeure(Number(calendarSet.value));
}
this.updateDisplay();
}
/* -------------------------------------------- */
checkMaladie( periode) {
for (let actor of game.actors) {
if (actor.type == 'personnage') {
let maladies = actor.items.filter( item => (item.type == 'maladie' || (item.type == 'poison' && item.system.active) ) && item.system.periodicite.toLowerCase().includes(periode) );
for (let maladie of maladies) {
if ( maladie.system.identifie) {
ChatMessage.create({ content: `${actor.name} souffre de ${maladie.name} (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
} else {
ChatMessage.create({ content: `${actor.name} souffre d'un mal inconnu (${maladie.type}): vérifiez que les effets ne se sont pas aggravés !` });
}
let itemMaladie = actor.getObjet(maladie.id)
itemMaladie.postItem( 'gmroll');
}
}
}
}
/* -------------------------------------------- */
async incrementTime(minutes = 0) {
this.calendrier.minutesRelative += minutes;
this.checkMaladie("round");
this.checkMaladie("minute");
if (this.calendrier.minutesRelative >= RDD_MINUTES_PAR_HEURES) {
this.calendrier.minutesRelative -= RDD_MINUTES_PAR_HEURES;
this.calendrier.heureRdD += 1;
this.checkMaladie("heure");
}
if (this.calendrier.heureRdD >= RDD_HEURES_PAR_JOUR) {
this.calendrier.heureRdD -= RDD_HEURES_PAR_JOUR;
await this.incrementerJour();
this.checkMaladie("heure");
this.checkMaladie("jour");
}
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
// Notification aux joueurs // TODO: replace with Hook on game settings update
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_sync_time",
data: duplicate(this.calendrier)
});
}
/* -------------------------------------------- */
async incrementerJour() {
const index = this.getCurrentDayIndex() + 1;
this.calendrier = RdDCalendrier.getCalendrier(index);
await this.rebuildListeNombreAstral();
}
/* -------------------------------------------- */
syncPlayerTime(calendrier) {
this.calendrier = duplicate(calendrier); // Local copy update
this.updateDisplay();
}
/* -------------------------------------------- */
async positionnerHeure(indexHeure) {
if (indexHeure <= this.calendrier.heureRdD) {
await this.incrementerJour();
}
this.calendrier.heureRdD = indexHeure;
this.calendrier.minutesRelative = 0;
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
}
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
console.log(this.calendrier);
let moisKey = heuresList[this.calendrier.moisRdD];
let heureKey = heuresList[this.calendrier.heureRdD];
console.log(moisKey, heureKey);
const mois = heuresDef[moisKey];
const heure = heuresDef[heureKey];
formData.heureKey = heureKey;
formData.moisKey = moisKey;
formData.jourMois = this.calendrier.jour + 1;
formData.nomMois = mois.label; // heures et mois nommés identiques
formData.iconMois = dossierIconesHeures + mois.icon;
formData.nomHeure = heure.label;
formData.iconHeure = dossierIconesHeures + heure.icon;
formData.nomSaison = saisonsDef[mois.saison].label;
formData.heureRdD = this.calendrier.heureRdD;
formData.minutesRelative = this.calendrier.minutesRelative;
formData.isGM = game.user.isGM;
return formData;
}
/* -------------------------------------------- */
getLectureAstrologieDifficulte(dateIndex) {
let indexNow = this.getCurrentDayIndex();
let diffDay = dateIndex - indexNow;
return - Math.floor(diffDay / 2);
}
/* -------------------------------------------- */
async requestNombreAstral(request) {
if (Misc.isUniqueConnectedGM()) { // Only once
console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
let rollData = {
caracValue: request.carac_vue,
finalLevel: niveau,
showDice: HIDE_DICE,
rollMode: "blindroll"
};
await RdDResolutionTable.rollData(rollData);
let nbAstral = this.getNombreAstral(request.date);
request.rolled = rollData.rolled;
request.isValid = true;
if (!request.rolled.isSuccess) {
request.isValid = false;
nbAstral = await RdDDice.rollTotal("1dhr" + nbAstral, { rollMode: "selfroll" });
// Mise à jour des nombres astraux du joueur
let astralData = this.listeNombreAstral.find((nombreAstral, i) => nombreAstral.index == request.date);
astralData.valeursFausses.push({ actorId: request.id, nombreAstral: nbAstral });
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
}
request.nbAstral = nbAstral;
if (Misc.getActiveUser(request.userId)?.isGM) {
RdDUtility.responseNombreAstral(request);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_response_nombre_astral",
data: request
});
}
}
}
/* -------------------------------------------- */
findHeure(heure) {
heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
let parHeureOuLabel = Object.values(heuresDef).filter(it => (it.heure + 1) == parseInt(heure) || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);
if (parHeureOuLabel.length == 1) {
return parHeureOuLabel[0];
}
let parLabelPartiel = Object.values(heuresDef).filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
if (parLabelPartiel.length > 0) {
parLabelPartiel.sort(Misc.ascending(h => h.label.length));
return parLabelPartiel[0];
}
return undefined;
}
/* -------------------------------------------- */
getHeureNumber( hNum) {
let heure = Object.values(heuresDef).find(it => (it.heure) == hNum);
return heure
}
/* -------------------------------------------- */
getHeuresChanceMalchance(heureNaissance) {
let heuresChancesMalchances = [];
let defHeure = this.findHeure(heureNaissance);
if (defHeure) {
let hn = defHeure.heure;
let chiffreAstral = this.getCurrentNombreAstral() ?? 0;
heuresChancesMalchances[0] = { value : "+4", heures: [this.getHeureNumber((hn + chiffreAstral) % RDD_HEURES_PAR_JOUR).label]};
heuresChancesMalchances[1] = { value : "+2", heures: [this.getHeureNumber((hn + chiffreAstral+4) % RDD_HEURES_PAR_JOUR).label,
this.getHeureNumber((hn + chiffreAstral + 8) % RDD_HEURES_PAR_JOUR).label ] };
heuresChancesMalchances[2] = { value : "-4", heures: [this.getHeureNumber((hn + chiffreAstral+6) % RDD_HEURES_PAR_JOUR).label]};
heuresChancesMalchances[3] = { value : "-2", heures: [this.getHeureNumber((hn + chiffreAstral+3) % RDD_HEURES_PAR_JOUR).label,
this.getHeureNumber((hn + chiffreAstral + 9) % RDD_HEURES_PAR_JOUR).label ]};
}
return heuresChancesMalchances;
}
/* -------------------------------------------- */
getAjustementAstrologique(heureNaissance, name = undefined) {
let defHeure = this.findHeure(heureNaissance);
if (defHeure) {
let hn = defHeure.heure;
let chiffreAstral = this.getCurrentNombreAstral() ?? 0;
let heureCourante = this.calendrier.heureRdD;
let ecartChance = (hn + chiffreAstral - heureCourante) % RDD_HEURES_PAR_JOUR;
switch (ecartChance) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
case 3: case 9: return -2;
}
}
else if (name) {
ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
}
else {
ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
}
return 0;
}
/* -------------------------------------------- */
getData() {
let formData = super.getData();
this.fillCalendrierData(formData);
this.setPos(this.calendrierPos);
return formData;
}
/* -------------------------------------------- */
setPos(pos) {
return new Promise(resolve => {
function check() {
let elmnt = document.getElementById("calendar-time-container");
if (elmnt) {
elmnt.style.bottom = null;
let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
resolve();
} else {
setTimeout(check, 30);
}
}
check();
});
}
/* -------------------------------------------- */
updateDisplay() {
let calendrier = this.fillCalendrierData();
// Rebuild text du calendrier
let dateHTML = `Jour ${calendrier.jourMois} de ${calendrier.nomMois} (${calendrier.nomSaison})`
if (game.user.isGM) {
dateHTML = dateHTML + " - NA: " + (this.getCurrentNombreAstral() ?? "indéterminé");
}
for (let handle of document.getElementsByClassName("calendar-date-rdd")) {
handle.innerHTML = dateHTML;
}
for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
heure.innerHTML = calendrier.nomHeure;
}
for (const minute of document.getElementsByClassName("calendar-time-disp")) {
minute.innerHTML = `${calendrier.minutesRelative} minutes`;
}
for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
heureImg.src = calendrier.iconHeure;
}
}
/* -------------------------------------------- */
async saveEditeur(calendrierData) {
this.calendrier.minutesRelative = Number(calendrierData.minutesRelative);
this.calendrier.jour = Number(calendrierData.jourMois) - 1;
this.calendrier.moisRdD = RdDCalendrier.getChiffreFromSigne(calendrierData.moisKey);
this.calendrier.heureRdD = RdDCalendrier.getChiffreFromSigne(calendrierData.heureKey);
game.settings.set(SYSTEM_RDD, "calendrier", duplicate(this.calendrier));
await this.rebuildListeNombreAstral();
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_sync_time",
data: duplicate(this.calendrier)
});
this.updateDisplay();
}
/* -------------------------------------------- */
async showCalendarEditor() {
let calendrierData = duplicate(this.fillCalendrierData());
if (this.editeur == undefined) {
calendrierData.jourMoisOptions = RdDCalendrier.buildJoursMois();
calendrierData.heuresOptions = [0, 1];
calendrierData.minutesOptions = Array(RDD_MINUTES_PAR_HEURES).fill().map((item, index) => 0 + index);
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
}
this.editeur.updateData(calendrierData);
this.editeur.render(true);
}
static buildJoursMois() {
return Array(RDD_JOUR_PAR_MOIS).fill().map((item, index) => 1 + index);
}
/* -------------------------------------------- */
async showAstrologieEditor() {
let calendrierData = duplicate(this.fillCalendrierData());
let astrologieArray = [];
this.listeNombreAstral = this.listeNombreAstral || [];
for (let astralData of this.listeNombreAstral) {
astralData.humanDate = this.getDateFromIndex(astralData.index);
for (let vf of astralData.valeursFausses) {
let actor = game.actors.get(vf.actorId);
vf.actorName = (actor) ? actor.name : "Inconnu";
}
astrologieArray.push(duplicate(astralData));
}
let heuresParActeur = {};
for (let actor of game.actors) {
let heureNaissance = actor.getHeureNaissance();
if ( heureNaissance) {
heuresParActeur[actor.name] = this.getHeuresChanceMalchance(heureNaissance);
}
}
//console.log("ASTRO", astrologieArray);
calendrierData.astrologieData = astrologieArray;
calendrierData.heuresParActeur = heuresParActeur;
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-astrologie-template.html', calendrierData);
let astrologieEditeur = new RdDAstrologieEditeur(html, this, calendrierData)
astrologieEditeur.updateData(calendrierData);
astrologieEditeur.render(true);
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
this.html = html;
this.updateDisplay();
this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.calendar-set-datetime').click(ev => {
html.find('.calendar-btn-edit').click(ev => {
ev.preventDefault();
this.showCalendarEditor();
});
this.html.find('.calendar-astrologie').click(ev => {
html.find('.astrologie-btn-edit').click(ev => {
ev.preventDefault();
this.showAstrologieEditor();
});
this.html.find('.calendar-title').mousedown(ev => {
html.find('#calendar-move-handle').mousedown(ev => {
ev.preventDefault();
ev = ev || window.event;
let isRightMB = false;
@ -114,16 +583,16 @@ export class RdDCalendrier extends Application {
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.bottom = undefined
elmnt.style.bottom = null
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
elmnt.onmousedown = undefined;
document.onmouseup = undefined;
document.onmousemove = undefined;
elmnt.onmousedown = null;
document.onmouseup = null;
document.onmousemove = null;
let xPos = (elmnt.offsetLeft - pos1) > window.innerWidth ? window.innerWidth - 200 : (elmnt.offsetLeft - pos1);
let yPos = (elmnt.offsetTop - pos2) > window.innerHeight - 20 ? window.innerHeight - 100 : (elmnt.offsetTop - pos2)
xPos = xPos < 0 ? 0 : xPos;
@ -149,364 +618,5 @@ export class RdDCalendrier extends Application {
}
});
}
/* -------------------------------------------- */
getListeNombreAstral() {
return game.settings.get(SYSTEM_RDD, "liste-nombre-astral") ?? [];
}
/* -------------------------------------------- */
dateCourante() {
return this.timestamp.formatDate();
}
isAfterIndexDate(indexDate) {
// TODO: standardize
return indexDate < this.timestamp.indexDate;
}
/* -------------------------------------------- */
heureCourante() { return RdDTimestamp.definition(this.timestamp.heure); }
/* -------------------------------------------- */
getCurrentMinute() { return this.timestamp.indexMinute; }
getTimestamp() {
return this.timestamp;
}
getTimestampFinChateauDormant(nbJours = 0) {
return this.timestamp.nouveauJour().addJours(nbJours);
}
getTimestampFinHeure(nbHeures = 0) {
return this.timestamp.nouvelleHeure().addHeures(nbHeures);
}
/* -------------------------------------------- */
getIndexFromDate(jour, mois) {
const addYear = mois < this.timestamp.mois || (mois == this.timestamp.mois && jour < this.timestamp.jour)
const time = RdDTimestamp.timestamp(this.timestamp.annee + (addYear ? 1 : 0), mois, jour);
return time.indexDate;
}
/* -------------------------------------------- */
getJoursSuivants(count) {
let jours = [];
let indexDate = this.timestamp.indexDate;
for (let i = 0; i < count; i++, indexDate++) {
jours[i] = { label: RdDTimestamp.formatIndexDate(indexDate), index: indexDate };
}
return jours;
}
/* -------------------------------------------- */
async ajouterNombreAstral(indexDate, showDice = SHOW_DICE) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, rollMode: "selfroll" });
const dateFuture = RdDTimestamp.formatIndexDate(indexDate);
if (showDice != HIDE_DICE) {
ChatMessage.create({
whisper: ChatMessage.getWhisperRecipients("GM"),
content: `Le chiffre astrologique du ${dateFuture} sera le ${nombreAstral}`
});
}
return {
nombreAstral: nombreAstral,
valeursFausses: [],
index: indexDate
}
}
/* -------------------------------------------- */
resetNombreAstral() {
this.listeNombreAstral = [];
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", []);
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_reset_nombre_astral",
data: {}
});
}
/* -------------------------------------------- */
/**
*
* @param {*} indexDate la date pour laquelle obtenir le nombre astral. Si undefined, on prend la date du jour
* @returns le nombre astral pour la date, ou pour la date du jour si la date n'est pas fournie.
* Si aucun nombre astral n'est trouvé, retourne 0 (cas où l'on demanderait un nombre astral en dehors des 12 jours courant et à venir)
*/
getNombreAstral(indexDate = undefined) {
if (indexDate == undefined) {
indexDate = this.timestamp.indexDate;
}
const listNombreAstral = this.getListeNombreAstral();
let astralData = listNombreAstral.find((nombreAstral, i) => nombreAstral.index == indexDate);
return astralData?.nombreAstral ?? 0;
}
/* -------------------------------------------- */
async rebuildListeNombreAstral(showDice = HIDE_DICE) {
if (Misc.isUniqueConnectedGM()) {
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
let dayIndex = this.timestamp.indexDate + i;
let na = this.listeNombreAstral.find(n => n.index == dayIndex);
if (na) {
newList[i] = na;
} else {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
}
}
this.listeNombreAstral = newList;
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", newList);
}
}
/* -------------------------------------------- */
async setNewTimestamp(newTimestamp) {
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")) {
await DialogChateauDormant.create();
}
this.timestamp = newTimestamp;
await this.rebuildListeNombreAstral();
this.updateDisplay();
}
/* -------------------------------------------- */
async onCalendarButton(ev) {
ev.preventDefault();
const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
if (calendarAvance) {
await this.incrementTime(Number(calendarAvance.value));
}
else if (calendarSet) {
this.positionnerHeure(Number(calendarSet.value));
}
this.updateDisplay();
}
/* -------------------------------------------- */
async incrementTime(minutes = 0) {
await this.setNewTimestamp(this.timestamp.addMinutes(minutes));
}
/* -------------------------------------------- */
async incrementerJour() {
await this.setNewTimestamp(this.timestamp.nouveauJour());
}
/* -------------------------------------------- */
async positionnerHeure(heure) {
const indexDate = this.timestamp.indexDate;
const addDay = this.timestamp.heure < heure ? 0 : 1;
const newTimestamp = new RdDTimestamp({ indexDate: indexDate + addDay}).addHeures(heure);
await this.setNewTimestamp(newTimestamp)
}
/* -------------------------------------------- */
fillCalendrierData(formData = {}) {
mergeObject(formData, this.timestamp.toCalendrier());
formData.isGM = game.user.isGM;
return formData;
}
/* -------------------------------------------- */
getLectureAstrologieDifficulte(dateIndex) {
let indexNow = this.timestamp.indexDate;
let diffDay = dateIndex - indexNow;
return - Math.floor(diffDay / 2);
}
/* -------------------------------------------- */
async requestNombreAstral(request) {
const actor = game.actors.get(request.id);
if (Misc.isUniqueConnectedGM()) { // Only once
console.log(request);
let jourDiff = this.getLectureAstrologieDifficulte(request.date);
let niveau = Number(request.astrologie.system.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
let rollData = {
caracValue: request.carac_vue,
finalLevel: niveau,
showDice: HIDE_DICE,
rollMode: "blindroll"
};
await RdDResolutionTable.rollData(rollData);
request.rolled = rollData.rolled;
request.isValid = request.rolled.isSuccess;
request.nbAstral = this.getNombreAstral(request.date);
if (request.rolled.isSuccess) {
if (request.rolled.isPart) {
// Gestion expérience (si existante)
request.competence = actor.getCompetence("astrologie")
request.selectedCarac = actor.system.carac["vue"];
actor.appliquerAjoutExperience(request, 'hide');
}
}
else {
request.nbAstral = await RdDDice.rollTotal("1dhr" + request.nbAstral, {
rollMode: "selfroll", showDice: HIDE_DICE
});
// Mise à jour des nombres astraux du joueur
this.addNbAstralIncorect(request.id, request.date, request.nbAstral);
}
if (Misc.getActiveUser(request.userId)?.isGM) {
RdDUtility.responseNombreAstral(request);
} else {
game.socket.emit(SYSTEM_SOCKET_ID, {
msg: "msg_response_nombre_astral",
data: request
});
}
}
}
addNbAstralIncorect(actorId, date, nbAstral) {
let astralData = this.listeNombreAstral.find((nombreAstral, i) => nombreAstral.index == date);
astralData.valeursFausses.push({ actorId: actorId, nombreAstral: nbAstral });
game.settings.set(SYSTEM_RDD, "liste-nombre-astral", this.listeNombreAstral);
}
static ecartHeureChance(heureNaissance, nombreAstral, heure) {
return (heureNaissance + nombreAstral - heure) % RDD_HEURES_PAR_JOUR;
}
/* -------------------------------------------- */
getAjustementAstrologique(heureNaissance, name = undefined) {
let defHeure = RdDTimestamp.findHeure(heureNaissance);
if (defHeure) {
return RdDCalendrier.ajustementAstrologiqueHeure(defHeure.heure, this.getNombreAstral(), this.timestamp.heure);
}
else if (name) {
ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
}
else {
ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
}
return 0;
}
static ajustementAstrologiqueHeure(hn, nbAstral, heure) {
switch (RdDCalendrier.ecartHeureChance(hn, nbAstral, heure)) {
case 0: return 4;
case 4: case 8: return 2;
case 6: return -4;
case 3: case 9: return -2;
}
return 0;
}
/* -------------------------------------------- */
getData() {
let formData = super.getData();
this.fillCalendrierData(formData);
this.setPos(this.calendrierPos);
return formData;
}
/* -------------------------------------------- */
setPos(pos) {
return new Promise(resolve => {
function check() {
let elmnt = document.getElementById("calendar-time-container");
if (elmnt) {
elmnt.style.bottom = undefined;
let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
elmnt.style.top = (yPos) + "px";
elmnt.style.left = (xPos) + "px";
resolve();
} else {
setTimeout(check, 30);
}
}
check();
});
}
/* -------------------------------------------- */
updateDisplay() {
let calendrier = this.fillCalendrierData();
// Rebuild text du calendrier
let dateHTML = `${calendrier.jourDuMois} ${calendrier.mois.label} (${calendrier.mois.saison}) de l'année ${calendrier.annee}`
if (game.user.isGM) {
dateHTML = dateHTML + "<br>Nombre Astral: " + (this.getNombreAstral() ?? "?");
}
for (let handle of document.getElementsByClassName("calendar-title")) {
handle.innerHTML = dateHTML;
}
for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
heure.innerHTML = calendrier.heure.label;
}
for (const minute of document.getElementsByClassName("calendar-minute-texte")) {
minute.innerHTML = `${calendrier.minute} minutes`;
}
for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
heureImg.src = calendrier.heure.icon;
}
}
/* -------------------------------------------- */
async saveEditeur(calendrierData) {
const newTimestamp = RdDTimestamp.timestamp(
Number.parseInt(calendrierData.annee),
calendrierData.mois.heure,
Number.parseInt(calendrierData.jourMois),
calendrierData.heure.heure,
Number.parseInt(calendrierData.minutes)
);
await this.setNewTimestamp(newTimestamp);
}
/* -------------------------------------------- */
async showCalendarEditor() {
let calendrierData = this.fillCalendrierData();
if (this.editeur == undefined) {
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
}
this.editeur.updateData(calendrierData);
this.editeur.render(true);
}
static buildJoursMois() { return JOURS_DU_MOIS; }
/* -------------------------------------------- */
async showAstrologieEditor() {
const calendrierData = duplicate(this.fillCalendrierData());
this.listeNombreAstral = this.listeNombreAstral || [];
calendrierData.astrologieData = this.listeNombreAstral.map(astro => {
const timestamp = new RdDTimestamp({ indexDate: astro.index });
astro.date = { mois: timestamp.mois, jour: timestamp.jour + 1 }
for (let vf of astro.valeursFausses) {
let actor = game.actors.get(vf.actorId);
vf.actorName = (actor) ? actor.name : "Inconnu";
}
return astro;
});
const nbAstral = this.getNombreAstral()
calendrierData.heures = Array.from(Array(RDD_HEURES_PAR_JOUR).keys());
calendrierData.ajustementsActeur = game.actors.filter(it => it.isPersonnage() && it.hasPlayerOwner).map(actor => {
return {
actor,
ajustements: calendrierData.heures.map(heure => {
const hn = RdDTimestamp.findHeure(actor.getHeureNaissance())?.heure;
return {
heure,
ajustement: RdDCalendrier.ajustementAstrologiqueHeure(hn, nbAstral, heure)
}
})
}
});
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-astrologie-template.html', calendrierData);
let astrologieEditeur = new RdDAstrologieEditeur(html, this, calendrierData)
astrologieEditeur.updateData(calendrierData);
astrologieEditeur.render(true);
}
}

View File

@ -39,10 +39,9 @@ const tableCaracDerivee = {
export class RdDCarac {
static isAgiliteOuDerobee(selectedCarac) {
static isAgiliteOuDerivee(selectedCarac) {
return selectedCarac?.label.match(/(Agilité|Dérobée)/);
}
static isVolonte(selectedCarac) {
return selectedCarac?.label == 'Volonté';
}
@ -53,11 +52,6 @@ export class RdDCarac {
return selectedCarac?.label?.toLowerCase()?.match(/r(e|ê)ve(( |-)actuel)?/);
}
static isActionPhysique(selectedCarac) {
return !selectedCarac ||
selectedCarac?.label.match(/(Apparence|Force|Agilité|Dextérité|Vue|Ouïe|Odorat-Goût|Empathie|Dérobée|Mêlée|Tir|Lancer)/);
}
static isIgnoreEtatGeneral(rollData) {
const selectedCarac = rollData.selectedCarac;
return !selectedCarac ||

View File

@ -1,5 +1,5 @@
import { ChatUtility } from "./chat-utility.js";
import { ENTITE_BLURETTE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { ENTITE_BLURETTE, ENTITE_INCARNE, ENTITE_NONINCARNE, HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "./constants.js";
import { Grammar } from "./grammar.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
@ -9,9 +9,8 @@ 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 { STATUSES } from "./settings/status-effects.js";
import { Targets } from "./targets.js";
import { ReglesOptionelles } from "./regles-optionelles.js";
import { STATUSES } from "./status-effects.js";
/* -------------------------------------------- */
const premierRoundInit = [
@ -83,6 +82,7 @@ export class RdDCombatManager extends Combat {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
ids = typeof ids === "string" ? [ids] : ids;
const currentId = this.combatant._id;
// calculate initiative
for (let cId = 0; cId < ids.length; cId++) {
const combatant = this.combatants.get(ids[cId]);
@ -101,19 +101,19 @@ export class RdDCombatManager extends Combat {
const carac = combatant.actor.system.carac[competence.system.defaut_carac].value;
const niveau = competence.system.niveau;
const bonusEcaille = (armeCombat?.system.magique) ? armeCombat.system.ecaille_efficacite : 0;
rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille);
rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille);
}
}
}
//console.log("Combatat", c);
const roll = combatant.getInitiativeRoll(rollFormula);
if (!roll.total) {
roll.evaluate({ async: false });
if ( !roll.total) {
roll.evaluate( {async: false});
}
const total = Math.max(roll.total, 0.00);
console.log("Compute init for", rollFormula, roll, total, combatant);
if (roll.total <= 0) roll.total = 0.00;
console.log("Compute init for", rollFormula, roll.total, combatant);
let id = combatant._id || combatant.id;
await this.updateEmbeddedDocuments("Combatant", [{ _id: id, initiative: total }]);
await this.updateEmbeddedDocuments("Combatant", [{ _id: id, initiative: roll.total }]);
// Send a chat message
let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode");
@ -121,7 +121,7 @@ export class RdDCombatManager extends Combat {
{
speaker: {
scene: canvas.scene._id,
actor: combatant.actor?._id,
actor: combatant.actor ? combatant.actor._id : null,
token: combatant.token._id,
alias: combatant.token.name,
sound: CONFIG.sounds.dice,
@ -151,102 +151,81 @@ export class RdDCombatManager extends Combat {
}
/* -------------------------------------------- */
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main / lancer */
/** Retourne une liste triée d'actions d'armes avec le split arme1 main / arme 2 main */
static listActionsArmes(armes, competences, carac) {
let actions = [];
// Gestion des armes 1/2 mains
let actionsArme = [];
for (const arme of armes) {
if (arme.system.equipe) {
const dommages = arme.system.dommages.toString();
const tableauDommages = dommages.includes("/") ? dommages.split("/") : [dommages, dommages];
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) {
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + arme.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
if ((arme.system.unemain && arme.system.competence) ||
(arme.system.competence.toLowerCase().includes("corps à corps"))) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(1 main)",
dommagesReel: Number(tableauDommages[0]),
competence: arme.system.competence,
carac: carac,
competences: competences
}));
}
if (arme.system.deuxmains && arme.system.competence) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(2 mains)",
dommagesReel: Number(tableauDommages[1]),
competence: arme.system.competence.replace(" 1 main", " 2 mains"),
carac: carac,
competences: competences
}));
}
if (arme.system.lancer) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(lancer)",
dommagesReel: Number(tableauDommages[0]),
competence: arme.system.lancer,
carac: carac,
competences: competences
}));
}
if (arme.system.tir) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(tir)",
dommagesReel: Number(tableauDommages[0]),
competence: arme.system.tir,
carac: carac,
competences: competences
}));
let action = duplicate(arme)
if (action.system.equipe) {
let compData = competences.find(c => c.name == action.system.competence)
actionsArme.push(action);
action.action = 'attaque';
action.system.dommagesReels = Number(action.system.dommages);
action.system.niveau = compData.system.niveau;
action.system.initiative = RdDCombatManager.calculInitiative(compData.system.niveau, carac[compData.system.defaut_carac].value);
// Dupliquer les armes pouvant être à 1 main et 2 mains en patchant la compétence
if (action.system.unemain && !action.system.deuxmains) {
action.system.mainInfo = "(1m)";
} else if (!action.system.unemain && action.system.deuxmains) {
action.system.mainInfo = "(2m)";
} else if (action.system.unemain && action.system.deuxmains) {
action.system.mainInfo = "(1m)";
const comp2m = action.system.competence.replace(" 1 main", " 2 mains"); // Replace !
const comp = competences.find(c => c.name == comp2m)
const arme2main = duplicate(action);
arme2main.system.mainInfo = "(2m)";
arme2main.system.niveau = comp.system.niveau;
arme2main.system.competence = comp2m;
arme2main.system.initiative = RdDCombatManager.calculInitiative(arme2main.system.niveau, carac[comp.system.defaut_carac].value);
actionsArme.push(arme2main);
const containsSlash = action.system.dommages.includes("/");
if (containsSlash) {
const tableauDegats = action.system.dommages.split("/");
action.system.dommagesReels = Number(tableauDegats[0]);
arme2main.system.dommagesReels = Number(tableauDegats[1]);
}
else{
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + action.name + " ne sont pas corrects (ie sous la forme X/Y)");
}
}
}
}
return actions.sort(Misc.ascending(action => action.name + (action.system.infoMain ?? '')));
}
static $prepareAttaqueArme(infoAttaque) {
const comp = infoAttaque.competences.find(c => c.name == infoAttaque.competence);
const attaque = duplicate(infoAttaque.arme);
attaque.action = 'attaque';
attaque.system.competence = infoAttaque.competence;
attaque.system.dommagesReels = infoAttaque.dommagesReel;
attaque.system.infoMain = infoAttaque.infoMain;
attaque.system.niveau = comp.system.niveau;
attaque.system.initiative = RdDCombatManager.calculInitiative(comp.system.niveau, infoAttaque.carac[comp.system.defaut_carac].value);
return attaque;
return actionsArme.sort(Misc.ascending(armeData => armeData.name + (armeData.system.mainInfo ?? '')));
}
static listActionsCreature(competences) {
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.armeNaturelle(it));
.map(it => RdDItemCompetenceCreature.toActionArme(it));
}
static listActionsPossessions(actor) {
return RdDCombatManager._indexActions(actor.getPossessions().map(p => {
return {
name: p.name,
action: 'conjurer',
system: {
competence: p.name,
possessionid: p.system.possessionid,
return RdDCombatManager._indexActions(actor.getPossessions().map(p =>
{
return {
name: p.name,
action: 'conjurer',
system: {
competence: p.name,
possessionid: p.system.possessionid,
}
}
}
}));
}));
}
/* -------------------------------------------- */
static listActionsCombat(combatant) {
const actor = combatant.actor;
let actions = RdDCombatManager.listActionsPossessions(actor);
if (actions.length > 0) {
if (actions.length>0) {
return actions;
}
if (actor.isCreatureEntite()) {
if (actor.isCreature()) {
actions = actions.concat(RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']));
} else if (actor.isPersonnage()) {
} else {
// Recupération des items 'arme'
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
//.concat(RdDItemArme.empoignade())
@ -355,7 +334,7 @@ export class RdDCombatManager extends Combat {
compData = RdDItemCompetence.findCompetence(combatant.actor.items, action.system.competence);
compNiveau = compData.system.niveau;
initInfo = action.name + " / " + action.system.competence;
if (combatant.actor.type == 'creature' || combatant.actor.type == 'entite') {
caracForInit = compData.system.carac_value;
} else {
@ -383,7 +362,7 @@ export class RdDCombatManager extends Combat {
switch (arme.system.cac) {
case "empoignade":
return 3;
case "pugilat":
case "pugilat":
case "naturelle":
return 4;
}
@ -394,7 +373,7 @@ export class RdDCombatManager extends Combat {
static displayInitiativeMenu(html, combatantId) {
console.log("Combatant ; ", combatantId);
const combatant = game.combat.combatants.get(combatantId);
if (!(combatant?.actor)) {
if (! (combatant?.actor) ) {
ui.notifications.warn(`Le combatant ${combatant.name ?? combatantId} n'est pas associé à un acteur, impossible de déterminer ses actions de combat!`)
return;
}
@ -469,30 +448,49 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static rddCombatTarget(target, attacker) {
const defender = target?.actor;
const defenderTokenId = target?.id;
static createUsingTarget(attacker) {
const target = RdDCombat.getTarget()
if (target == undefined) {
ui.notifications.warn((game.user.targets?.size ?? 0) > 1
? "Vous devez choisir <strong>une seule</strong> cible à attaquer!"
: "Vous devez choisir une cible à attaquer!");
}
else {
const defender = target?.actor;
const defenderTokenId = target?.id;
if ( defender.type == 'entite' && defender.system.definition.typeentite == ENTITE_NONINCARNE) {
ui.notifications.warn("Vous ne pouvez pas cibler une entité non incarnée !!!!");
} else {
return this.create(attacker, defender, defenderTokenId, target)
}
}
}
/* -------------------------------------------- */
static getTarget() {
if (game.user.targets && game.user.targets.size == 1) {
for (let target of game.user.targets) {
return target;
}
}
return undefined;
}
/* -------------------------------------------- */
static create(attacker, defender, defenderTokenId, target = undefined) {
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) {
static createForAttackerAndDefender(attackerId, defenderTokenId) {
const attacker = game.actors.get(attackerId);
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
let target = undefined
if (!defenderTokenId || !defender) {
console.warn(`RdDCombat.rddCombatForAttackerAndDefender: appel avec defenderTokenId ${defenderTokenId} incorrect, ou pas de defender correspondant`);
target = Targets.getTarget()
if (!target) {
return;
}
defenderTokenId = target.id;
defender = target.actor;
if (!defenderTokenId || !defender) {
return;
}
if (defenderTokenId) {
const defenderToken = canvas.tokens.get(defenderTokenId);
const defender = defenderToken.actor;
return RdDCombat.create(attacker, defender, defenderTokenId);
}
return new RdDCombat(attacker, defender, defenderTokenId, target)
return RdDCombat.createUsingTarget(attacker)
}
/* -------------------------------------------- */
@ -500,10 +498,10 @@ export class RdDCombat {
let defender = canvas.tokens.get(msg.defenderTokenId).actor;
if (Misc.isOwnerPlayerOrUniqueConnectedGM()) {
let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : null;
defender.encaisserDommages(attackerRoll, attacker);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
}
@ -512,7 +510,7 @@ export class RdDCombat {
static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId);
if (defenderToken && Misc.isUniqueConnectedGM()) {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme);
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
}
@ -539,10 +537,11 @@ export class RdDCombat {
'#echec-total-attaque',
]) {
html.on("click", button, event => {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
const rddCombat = RdDCombat.createForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) {
rddCombat.onEvent(button, event);
event.preventDefault();
}
@ -569,7 +568,7 @@ export class RdDCombat {
async onEvent(button, event) {
const chatMessage = ChatUtility.getChatMessage(event);
const defenderRoll = ChatUtility.getMessageData(chatMessage, 'defender-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll');
const attackerRoll = defenderRoll?.attackerRoll ?? ChatUtility.getMessageData(chatMessage, 'attacker-roll') ;
console.log('RdDCombat', attackerRoll, defenderRoll);
const defenderTokenId = event.currentTarget.attributes['data-defenderTokenId']?.value;
@ -689,13 +688,12 @@ export class RdDCombat {
}
/* -------------------------------------------- */
async proposerAjustementTirLancer(rollData) {
async proposerAjustementTirLancer( rollData ) {
if (['tir', 'lancer'].includes(rollData.competence.system.categorie)) {
if (this.defender.isEntite([ENTITE_BLURETTE])) {
ChatMessage.create({
if (this.defender.isEntite([ENTITE_BLURETTE])){
ChatMessage.create( {
content: `<strong>La cible est une blurette, l'arme à distance sera perdue dans le blurêve`,
whisper: ChatMessage.getWhisperRecipients("GM")
})
whisper: ChatMessage.getWhisperRecipients("GM")})
}
else {
const defenderToken = canvas.tokens.get(this.defenderTokenId);
@ -704,7 +702,7 @@ export class RdDCombat {
const portee = this._ajustementPortee(dist, rollData.arme)
const taille = this._ajustementTaille(this.defender)
const activite = this._ajustementMouvement(this.defender)
const total = [portee, taille, activite].map(it => it.diff).filter(d => !Number.isNaN(d)).reduce(Misc.sum(), 0)
const total = [portee, taille, activite].map(it=>it.diff).filter(d => !Number.isNaN(d)).reduce(Misc.sum(), 0)
ChatMessage.create({
content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html', {
rollData: rollData,
@ -726,32 +724,32 @@ export class RdDCombat {
isVisible(token, defenderToken) {
return canvas.effects.visibility.testVisibility(defenderToken.center, { object: token })
}
distance(token, defenderToken) {
return Number(canvas.grid.measureDistances([{ ray: new Ray(token.center, defenderToken.center) }], { gridSpaces: false })).toFixed(1);
}
_ajustementPortee(dist, arme) {
if (dist <= arme.system.portee_courte) return { msg: "courte", diff: 0 };
if (dist <= arme.system.portee_moyenne) return { msg: "moyenne", diff: -3 };
if (dist <= arme.system.portee_extreme) return { msg: "extrême", diff: -5 };
return { msg: "inatteignable", diff: -10 };
if (dist <= arme.system.portee_courte) return {msg:"courte", diff:0};
if (dist <= arme.system.portee_moyenne) return {msg: "moyenne" , diff: -3};
if (dist <= arme.system.portee_extreme) return {msg: "extrême", diff:-5};
return {msg: "inatteignable", diff: -10};
}
_ajustementTaille(actor) {
if (actor.isVehicule()) return { msg: "véhicule", diff: 0 }
if (actor.isVehicule()) return {msg: "véhicule", diff: 0}
const taille = actor.getCaracByName('TAILLE')?.value ?? 1;
if (taille <= 1) return { msg: "souris", diff: -8 };
if (taille <= 3) return { msg: "chat", diff: -4 };
if (taille <= 5) return { msg: "chien", diff: -2 };
if (taille <= 15) return { msg: "humanoïde", diff: 0 };
if (taille <= 20) return { msg: "ogre", diff: 2 };
return { msg: "gigantesque", diff: 4 };
if (taille <= 1) return {msg: "souris", diff: -8};
if (taille <= 3) return {msg: "chat", diff: -4};
if (taille <= 5) return {msg: "chien", diff: -2};
if (taille <= 15) return {msg: "humanoïde", diff: 0};
if (taille <= 20) return {msg: "ogre", diff: 2};
return {msg: "gigantesque", diff: 4};
}
_ajustementMouvement(defender) {
if (defender.getSurprise(true)) return { msg: "immobile (surprise)", diff: 0 };
if (game.combat?.combatants.find(it => it.actorId == defender.id)) return { msg: "en mouvement (combat)", diff: -4 };
return { msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3 };
if (defender.getSurprise(true)) return {msg: "immobile (surprise)", diff: 0};
if (game.combat?.combatants.find(it => it.actorId == defender.id)) return {msg: "en mouvement (combat)", diff: -4};
return {msg: "à déterminer (0 immobile, -3 actif, -4 en mouvement, -5 en zig-zag)", diff: -3};
}
/* -------------------------------------------- */
@ -765,7 +763,7 @@ export class RdDCombat {
// whisper: ChatMessage.getWhisperRecipients("GM")})
// }
if (!await this.attacker.accorder(this.defender, 'avant-attaque')) {
if (!await this.accorderEntite('avant-attaque')) {
return;
}
if (arme.system.cac == 'empoignade' && this.attacker.isCombatTouche()) {
@ -788,21 +786,23 @@ export class RdDCombat {
await this.proposerAjustementTirLancer(rollData)
const dialog = await RdDRoll.create(this.attacker, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
name: 'jet-attaque',
label: 'Attaque: ' + (arme?.name ?? competence.name),
callbacks: [
this.attacker.createCallbackExperience(),
this.attacker.createCallbackAppelAuMoral(),
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: r => arme && !RdDCombat.isParticuliere(r), action: r => this.attacker.incDecItemUse(arme._id) },
{ condition: r => (RdDCombat.isReussite(r) && !RdDCombat.isParticuliere(r)), action: r => this._onAttaqueNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onAttaqueParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onAttaqueEchec(r) },
{ condition: RdDCombat.isEchecTotal, action: r => this._onAttaqueEchecTotal(r) },
]
});
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
options: { height: 540 }
}, {
name: 'jet-attaque',
label: 'Attaque: ' + (arme?.name ?? competence.name),
callbacks: [
this.attacker.createCallbackExperience(),
this.attacker.createCallbackAppelAuMoral(),
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: r => arme && !RdDCombat.isParticuliere(r), action: r => this.attacker.incDecItemUse(arme._id) },
{ condition: r => (RdDCombat.isReussite(r) && !RdDCombat.isParticuliere(r)), action: r => this._onAttaqueNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onAttaqueParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onAttaqueEchec(r) },
{ condition: RdDCombat.isEchecTotal, action: r => this._onAttaqueEchecTotal(r) },
]
});
dialog.render(true);
}
@ -815,11 +815,10 @@ export class RdDCombat {
competence: competence,
surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
targetToken: Targets.extractTokenData(this.target),
essais: {}
};
if (this.attacker.isCreatureEntite()) {
if (this.attacker.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
}
else if (arme) {
@ -837,7 +836,7 @@ export class RdDCombat {
/* -------------------------------------------- */
async _onAttaqueParticuliere(rollData) {
const isMeleeDiffNegative = (rollData.competence.type == 'competencecreature' || rollData.selectedCarac.label == "Mêlée") && rollData.diffLibre < 0;
// force toujours, sauf empoignade
// finesse seulement en mélée, pour l'empoignade, ou si la difficulté libre est de -1 minimum
@ -855,7 +854,7 @@ export class RdDCombat {
else if (!isForce && !isFinesse && isRapide) {
return await this.choixParticuliere(rollData, "rapidite");
}
const choixParticuliere = await ChatMessage.create({
alias: this.attacker.name,
whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name),
@ -884,7 +883,7 @@ export class RdDCombat {
}
await RdDResolutionTable.displayRollData(attackerRoll, this.attacker, 'chat-resultat-attaque.html');
if (!await this.attacker.accorder(this.defender, 'avant-defense')) {
if (!await this.accorderEntite('avant-defense')) {
return;
}
@ -894,7 +893,7 @@ export class RdDCombat {
}
/* -------------------------------------------- */
isPossession(attackerRoll) {
isPossession( attackerRoll) {
return attackerRoll.selectedCarac.label.toLowerCase() == 'possession';
}
@ -1037,28 +1036,31 @@ export class RdDCombat {
const arme = this.defender.getArmeParade(armeParadeId);
console.log("RdDCombat.parade >>>", attackerRoll, armeParadeId, arme);
const competence = arme?.system?.competence;
if (competence == undefined) {
console.error("Pas de compétence de parade associée à ", arme?.name, armeParadeId);
if (competence == undefined)
{
console.error("Pas de compétence de parade associée à ", arme?.name, armeParadeId) ;
return;
}
let rollData = this._prepareParade(attackerRoll, arme, competence);
const dialog = await RdDRoll.create(this.defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
name: 'jet-parade',
label: 'Parade: ' + (arme ? arme.name : rollData.competence.name),
callbacks: [
this.defender.createCallbackExperience(),
this.defender.createCallbackAppelAuMoral(),
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: r => !RdDCombat.isParticuliere(r), action: r => this.defender.incDecItemUse(armeParadeId) },
{ condition: RdDCombat.isReussite, action: r => this._onParadeNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onParadeParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onParadeEchec(r) },
]
});
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
options: { height: 540 }
}, {
name: 'jet-parade',
label: 'Parade: ' + (arme ? arme.name : rollData.competence.name),
callbacks: [
this.defender.createCallbackExperience(),
this.defender.createCallbackAppelAuMoral(),
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: r => !RdDCombat.isParticuliere(r), action: r => this.defender.incDecItemUse(armeParadeId) },
{ condition: RdDCombat.isReussite, action: r => this._onParadeNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onParadeParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onParadeEchec(r) },
]
});
dialog.render(true);
}
@ -1077,7 +1079,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(defenderRoll);
}
@ -1126,20 +1128,19 @@ export class RdDCombat {
let rollData = this._prepareEsquive(attackerRoll, esquive);
const dialog = await RdDRoll.create(this.defender, rollData,
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' },
{
name: 'jet-esquive',
label: 'Esquiver',
callbacks: [
this.defender.createCallbackExperience(),
this.defender.createCallbackAppelAuMoral(),
{ condition: r => !RdDCombat.isParticuliere(r), action: r => this.defender.incDecItemUse(esquive._id) },
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: RdDCombat.isReussite, action: r => this._onEsquiveNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onEsquiveParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onEsquiveEchec(r) },
]
});
{ html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' }, {
name: 'jet-esquive',
label: 'Esquiver',
callbacks: [
this.defender.createCallbackExperience(),
this.defender.createCallbackAppelAuMoral(),
{ condition: r => !RdDCombat.isParticuliere(r), action: r => this.defender.incDecItemUse(esquive._id) },
{ action: r => this.removeChatMessageActionsPasseArme(r.passeArme) },
{ condition: RdDCombat.isReussite, action: r => this._onEsquiveNormale(r) },
{ condition: RdDCombat.isParticuliere, action: r => this._onEsquiveParticuliere(r) },
{ condition: RdDCombat.isEchec, action: r => this._onEsquiveEchec(r) },
]
});
dialog.render(true);
}
@ -1156,7 +1157,7 @@ export class RdDCombat {
show: {}
};
if (this.defender.isCreatureEntite()) {
if (this.defender.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData);
}
return rollData;
@ -1317,6 +1318,35 @@ export class RdDCombat {
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
/* -------------------------------------------- */
/* retourne true si on peut continuer, false si on ne peut pas continuer */
async accorderEntite(when = 'avant-encaissement') {
if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
|| this.defender == undefined
|| !this.defender.isEntite([ENTITE_INCARNE])
|| this.defender.isEntiteAccordee(this.attacker)) {
return true;
}
let rolled = await RdDResolutionTable.roll(this.attacker.getReveActuel(), - Number(this.defender.system.carac.niveau.value));
let message = {
content: "Jet de points actuels de rêve à " + rolled.finalLevel + RdDResolutionTable.explain(rolled) + "<br>",
whisper: ChatMessage.getWhisperRecipients(this.attacker.name)
};
if (rolled.isSuccess) {
await this.defender.setEntiteReveAccordee(this.attacker);
message.content += this.attacker.name + " s'est accordé avec " + this.defender.name;
}
else {
message.content += this.attacker.name + " n'est pas accordé avec " + this.defender.name;
}
ChatMessage.create(message);
return rolled.isSuccess;
}
/* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor) {
let formData = {

Some files were not shown because too many files have changed in this diff Show More