Compare commits

...

49 Commits

Author SHA1 Message Date
e6592f8333 Merge pull request 'v11.0.28 - les fractures de Khrachtchoum' (#675) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #675
2023-10-30 22:34:39 +01:00
42567e7ca0 Amélioration de fenêtre de jets
- indication du type de dégâts
- affichage du moral avant l'icone d'appel au moral
- meilleur alignement des différentes options
2023-10-30 21:33:38 +01:00
9d1fe6d8fd Version 11.0.28 2023-10-30 19:47:08 +01:00
f5bd0f32f4 Fix: affichage de la gravité des blessures
Le type de blessure est indiqué dans le résumé d'encaissement
2023-10-30 19:42:56 +01:00
fad29f9652 Jet de vie par les propriétaires
Les messages de tour sont séparés en deux:
- un message pour tout le monde disant de qui c'est le tour
- un message pour les propriétaires du token pour les informations
  de santé (jets de vie à faire, ...)

Seul les propriétaires peuvent déclencher les jets de vie
2023-10-30 19:40:21 +01:00
9d51631d5c Merge pull request 'v11.0.27' (#674) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #674
2023-10-25 00:02:40 +02:00
d1b73e7658 Version 11.0.27 2023-10-24 22:51:45 +02:00
54158ee10d Fix disparition du tooltip dans les TMR
Passage den eventMode static permet de ne pas faire disparaître le
tooltip.
2023-10-24 22:44:51 +02:00
e089bdf9c8 Surencombrement seulement sur actions physiques
CF p70
2023-10-24 22:32:49 +02:00
41ab593059 Fix: fabrication de potion depuis l'herbe
La fabrication ne marchait plus
2023-10-24 22:24:54 +02:00
42ad4c5b26 Fix Restauration TMR si minimisée
Lors d'une action dans les TMRs, si la fenêtre est minimisée,
on la réaffiche
2023-10-24 22:16:49 +02:00
85f8a716d4 Merge pull request '## v11.0.26 - le crépuscule de Khrachtchoum' (#673) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #673
2023-10-24 07:07:01 +02:00
47f305d865 Les attaques de créatures fonctionnent de nouveau 2023-10-24 00:50:16 +02:00
407b4f82d9 Version 11.0.26 2023-10-24 00:43:16 +02:00
c950f568fd Fix: les TMRs sont en arrière plan 2023-10-24 00:30:00 +02:00
0ed90f6177 Macros pour le corps à corps
Cas particulier car 3 utilisations possibles, et pas d'armes
2023-10-23 23:48:07 +02:00
b74fc27079 Minor cleanup 2023-10-23 23:38:34 +02:00
a65d4511c5 Fix Macros de combat et corps à corps
- gestion des armes à 1/2 mains depuis les armes / le combat
- gestion correcte des compétences d'attaques de créatures
- message pour les macros de compétence d'arme
2023-10-23 23:29:02 +02:00
e61417c44e Merge pull request '## v11.0.25 - la vision du rêve de Khrachtchoum' (#672) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #672
2023-10-22 21:40:42 +02:00
8ca725bd38 Version 11.0.25 2023-10-22 16:17:53 +02:00
441a5965c7 Fix TMR minimize/maximize
Au lieu de minimiser les TMRs, les actions dans les TMRs sont bloquées
tant qu'une action liée au TMRs est en cours.
2023-10-22 16:17:37 +02:00
f08c8f93da Merge pull request '## v11.0.24 - les couleurs de Khrachtchoum' (#671) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #671
2023-10-22 09:40:26 +02:00
d28362bf14 Version 11.0.24 2023-10-22 00:36:14 +02:00
e506382d18 typo: Nécropole de Throat 2023-10-22 00:36:10 +02:00
c1cecc76b3 Nouvelles TMRs 2023-10-22 00:19:45 +02:00
f2a3e1db45 Merge pull request 'v11.0.23 - la lumière de Khrachtchoum' (#669) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #669
2023-10-20 07:12:09 +02:00
b69359a47d Version 11.0.23 2023-10-20 02:36:49 +02:00
fe6c2e2ff2 Ajustement de luminosité automatique
Activable par le MJ avec le bouton lumière du calendrier
2023-10-20 02:36:00 +02:00
9bd13a6021 Comptence de creatures 2023-10-18 21:18:29 +02:00
a29630f9a2 Merge pull request 'v11.0.21 - les automatismes de Khrachtchoum le Problémeux' (#668) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #668
2023-10-18 21:17:03 +02:00
c7fd9f7596 Version 11.0.22
les automatismes de Khrachtchoum le Problémeux
2023-10-18 21:13:50 +02:00
7b18fd25c3 Macro pour compétences de créature 2023-10-18 21:08:01 +02:00
5c256e2c49 F>ix version 2023-10-04 21:44:41 +02:00
1c475348d5 Gestion des armes dans hotbar 2023-10-04 21:24:51 +02:00
de5d32f88f Gestion des armes dans hotbar 2023-10-04 09:33:24 +02:00
76a02d60ca Gestion des armes dans hotbar 2023-10-04 09:07:49 +02:00
724c556b9e Merge pull request 'v11' (#667) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #667
2023-10-04 09:07:10 +02:00
7dfba94a11 Nettoyage de date
Pas de "T" entre date et heure
2023-10-03 21:03:19 +02:00
928c7bbcfe Désespoir 2023-08-28 01:46:43 +02:00
222a06a978 Fix remoteCall 2023-08-15 17:24:01 +02:00
b4edaf8584 Fix remoteActorCall 2023-08-13 20:43:32 +02:00
3c062afd56 Fix remoteActorCall 2023-08-13 20:42:54 +02:00
6de4fff403 Merge pull request 'v11.0.15 - L'apprentissage de Khrachtchoum' (#666) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #666
2023-08-03 09:10:32 +02:00
3543f081b2 Version 11.0.15 2023-08-03 00:41:16 +02:00
56f14e12a2 Fix: regression sur les particulières
L'utilisation de la méthode clone() pôur cloner la compétence
supprime l'id de la compétence, du coup l'update pour y
ajouter de l'expérience ne marchait plus et bloquait la suite de la
résolution (pas de messages dans le tchat, ...)
2023-08-03 00:41:16 +02:00
e226af5ac5 Merge pull request 'v11.0.14' (#665) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: #665
2023-07-30 09:25:10 +02:00
d8be37a2ec v11.0.14 2023-07-29 23:47:08 +02:00
ba2d9de7b6 Version 11.0.13
Les pincettes de Khrachtchoum le Problémeux
- Correction du calcul de la place restante lors de l'ajout dans un conteneur
2023-07-29 23:44:56 +02:00
6b8fb3267a Fix: calcul de la place dans un conteneur
Correction de l'ajout dans un conteneur, en faisant un calcul arrondi
pour éviter les problèmes de virgule flotante
2023-07-29 23:42:22 +02:00
40 changed files with 758 additions and 394 deletions

View File

@ -1,4 +1,65 @@
# v11.0
## v11.0.28 - les fractures de Khrachtchoum
- La gravité de la blessure est affichée dans le résumé de l'encaissement
- Lors du changement d'acteur pendant le round
- le message annonçant le joueur dont c'est le tour ne contient plus d'informations de santé
- un message avec les informations de santé est envoyé au Gardienn et au propriétaire du token.acteur
- le jet de vie est bien fait par le token si besoin
- seul les propriétaires peuvent faire les jets de vie
- Amélioration de la fenêtre de jets
- le type de dégâts pour les attaques est toujours affiché
- le moral est indiqué avant l'icone d'appel au moral
## v11.0.27 - Khrachtchoum le méticuleux
- le tooltip dans les TMR reste visible si on ne bouge pas la souris
- le surencombrement n'affecte QUE les actions physiques
- on peut de nouveau fabriquer une potion depuis la fenêtre d'édition de l'herbe
- si les TMR sont minimisées alors qu'une action est requise, elles sont bien réaffichées lorsque l'action est faite
## v11.0.26 - le crépuscule de Khrachtchoum
- gestion correcte des TMRs
- les TMRs ne sont jamais minimisées (par le système) quand le haut-rêvant est en demi-rêve
- lorsqu'une fenêtre liée aux demi-rêve est affichée, cliquer sur les TMRs n'a pas d'effet
- les lancers de sorts et lectures de signes sont affichées en premier plan
- Les effets qui ouvrent une fenêtre sont bien affichés en premier plan
- en cas de rencontre suivie de maîtrises/conquêtes, les fenêtres s'enchaînent
- Le drag&drop vers la barre de macro est corrigé
- pour les créatures, possibilités d'avoir les attaques ou autres compétences
- pour les personnages, les macros sont créées:
- pour les compétences
- pour le corps à corps, trois macros sont créées: compétence, pugilat, empoignade
- pour les armes
- deux macros sont créées pour les armes à 1/2 mains
- deux macros sont créées pour les armes de mélée et lancer
- 4 macros si votre arbalête se lance, tire, et se manie à 1 ou 2 mains...
- les jets de compétences d'attaque des créatures fonctionnent de nouveau
## v11.0.25 - la vision du rêve de Khrachtchoum
- Les TMRs restent affichées tant que le Haut-rêvant est en demi-rêve
## v11.0.24 - les couleurs de Khrachtchoum
- nouvelle carte des TMRs
## v11.0.23 - la lumière de Khrachtchoum
- ajustement automatique de la luminosité selon l'heure pour les scènes:
- avec une vision des tokens (sinon: ce n'est pas une scène de carte pour tokens)
- avec illumination globale (correspondant à une illumination extérieure)
- quand lampe "allumée" dans la fenêtre du calendrier
## v11.0.22 - les automatismes de Khrachtchoum le Problémeux
- Macro pour attaquer avec les compétences de créatures
## v11.0.20
- Macro pour attaquer avec les armes des personnages
## v11.0.17
- Fix: les actions de commerce ne s'appliquait pas bien aux personnages des tokens non liés
## v11.0.15 - L'apprentissage de Khrachtchoum
- Fix: l'expérience ne s'appliquait plus sur certaines réussites particulières (régression depuis la 11.0.7)
## v11.0.14 - Les pincettes de Khrachtchoum le Problémeux
- Correction du calcul de la place restante lors de l'ajout dans un conteneur
## v11.0.13 - La multiplication de l'eau de Khrachtchoum le Problémeux
- Correction de la vente depuis un commerce ayant des quantités illimitées

View File

@ -81,9 +81,12 @@ export class RdDActorSheet extends RdDBaseActorSheet {
});
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps)
formData.combat = duplicate(formData.armes ?? []);
const actor = this.actor;
formData.combat = duplicate(formData.armes);
RdDItemArme.computeNiveauArmes(formData.combat, formData.competences);
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac);
formData.combat.push(RdDItemArme.mainsNues(actor));
formData.combat.push(RdDItemArme.empoignade(actor));
formData.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
formData.empoignades = this.actor.getEmpoignades();

View File

@ -232,12 +232,21 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
getCompetence(idOrName, options = {}) {
if (idOrName instanceof Item) {
return idOrName.isCompetence() ? idOrName : undefined
}
return RdDItemCompetence.findCompetence(this.items, idOrName, options)
}
getCompetences(name) {
return RdDItemCompetence.findCompetences(this.items, name)
}
getCompetenceCorpsACorps(options = {}) {
return this.getCompetence("Corps à corps", options)
}
getCompetencesEsquive() {
return this.getCompetences("esquive")
}
/* -------------------------------------------- */
getTache(id) {
return this.findItemLike(id, 'tache');
@ -265,7 +274,7 @@ export class RdDActor extends RdDBaseActor {
}
/* -------------------------------------------- */
getDraconicList() {
return this.items.filter(it => it.isCompetencePersonnage() && it.system.categorie == 'draconic')
return this.itemTypes[TYPES.competence].filter(it => it.system.categorie == 'draconic')
}
/* -------------------------------------------- */
getBestDraconic() {
@ -275,26 +284,26 @@ export class RdDActor extends RdDBaseActor {
}
getDraconicOuPossession() {
const possession = this.items.filter(it => it.type == TYPES.competencecreature && it.system.categorie == 'possession')
const possession = this.itemTypes[TYPES.competencecreature].filter(it => it.system.categorie == 'possession')
.sort(Misc.descending(it => it.system.niveau))
.find(it => true);
if (possession) {
return possession.clone();
return possession;
}
const draconics = [...this.getDraconicList().filter(it => it.system.niveau >= 0).map(it => it.clone()),
const draconics = [...this.getDraconicList().filter(it => it.system.niveau >= 0),
POSSESSION_SANS_DRACONIC]
.sort(Misc.descending(it => it.system.niveau));
return draconics[0];
}
getPossession(possessionId) {
return this.items.find(it => it.type == TYPES.possession && it.system.possessionid == possessionId);
return this.itemTypes[TYPES.possession].find(it => it.system.possessionid == possessionId);
}
getPossessions() {
return this.items.filter(it => it.type == TYPES.possession);
return this.itemTypes[TYPES.possession];
}
getEmpoignades() {
return this.items.filter(it => it.type == TYPES.empoignade);
return this.itemTypes[TYPES.empoignade];
}
getDemiReve() {
return this.system.reve.tmrpos.coord;
@ -369,7 +378,7 @@ export class RdDActor extends RdDBaseActor {
async _openRollDialog({ name, label, template, rollData, callbackAction }) {
const dialog = await RdDRoll.create(this, rollData,
{ html: template },
{ html: template, close: html => { this.tmrApp?.restoreTMRAfterAction() } },
{
name: name,
label: label,
@ -380,6 +389,7 @@ export class RdDActor extends RdDBaseActor {
]
});
dialog.render(true);
return dialog
}
@ -545,7 +555,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async _recuperationBlessures(message, isMaladeEmpoisonne) {
const timestamp = game.system.rdd.calendrier.getTimestamp()
const blessures = this.filterItems(it => it.system.gravite > 0, 'blessure').sort(Misc.ascending(it => it.system.gravite))
const blessures = this.filterItems(it => it.system.gravite > 0, TYPES.blessure).sort(Misc.ascending(it => it.system.gravite))
await Promise.all(blessures.map(b => b.recuperationBlessure({
actor: this,
@ -559,7 +569,7 @@ export class RdDActor extends RdDBaseActor {
}
async supprimerBlessures(filterToDelete) {
const toDelete = this.filterItems(filterToDelete, 'blessure')
const toDelete = this.filterItems(filterToDelete, TYPES.blessure)
.map(it => it.id);
await this.deleteEmbeddedDocuments('Item', toDelete);
}
@ -567,7 +577,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async _recupererVie(message, isMaladeEmpoisonne) {
const tData = this.system
let blessures = this.filterItems(it => it.system.gravite > 0, 'blessure');
let blessures = this.filterItems(it => it.system.gravite > 0, TYPES.blessure);
if (blessures.length > 0) {
return
}
@ -846,7 +856,7 @@ export class RdDActor extends RdDBaseActor {
system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
}],
{ renderSheet: false });
this.currentTMR.updateTokens();
this.tmrApp.updateTokens();
}
/* -------------------------------------------- */
@ -1741,6 +1751,7 @@ export class RdDActor extends RdDBaseActor {
async consommerNourritureboisson(itemId, choix = { doses: 1, seForcer: false, supprimerSiZero: false }, userId = undefined) {
if (userId != undefined && userId != game.user.id) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'consommerNourritureboisson',
args: [itemId, choix, userId]
@ -2137,12 +2148,11 @@ export class RdDActor extends RdDBaseActor {
ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`);
return;
}
if (this.currentTMR) this.currentTMR.minimize(); // Hide
const draconicList = this.computeDraconicAndSortIndex(sorts);
const reve = duplicate(this.system.carac.reve);
await this._openRollDialog({
const dialog = await this._openRollDialog({
name: 'lancer-un-sort',
label: 'Lancer un sort',
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
@ -2158,8 +2168,12 @@ export class RdDActor extends RdDBaseActor {
diffLibre: RdDItemSort.getDifficulte(sorts[0], -7), // Per default at startup
coutreve: Array(30).fill().map((item, index) => 1 + index),
},
callbackAction: r => this._rollUnSortResult(r)
callbackAction: async r => {
await this._rollUnSortResult(r);
if (!r.isSortReserve) this.tmrApp?.close();
}
});
this.tmrApp?.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
@ -2250,17 +2264,11 @@ export class RdDActor extends RdDBaseActor {
reveActuel = Math.max(reveActuel - rollData.depenseReve, 0);
await this.update({ "system.reve.reve.value": reveActuel });
if (rollData.isSortReserve) {
this.currentTMR.maximize(); // Re-display TMR
} else {
this.currentTMR.close(); // Close TMR !
}
// Final chat message
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-sort.html');
if (reveActuel == 0) { // 0 points de reve
ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" });
closeTMR = true;
}
}
@ -2335,16 +2343,14 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
async rollCompetence(idOrName, options = { tryTarget: true }) {
RdDEmpoignade.checkEmpoignadeEnCours(this)
let rollData = {
carac: this.system.carac,
competence: this.getCompetence(idOrName)
}
if (rollData.competence.type == TYPES.competencecreature) {
const arme = RdDItemCompetenceCreature.armeCreature(rollData.competence)
const competence = this.getCompetence(idOrName);
let rollData = { carac: this.system.carac, competence: competence }
if (competence.type == TYPES.competencecreature) {
const arme = RdDItemCompetenceCreature.armeCreature(competence)
if (arme && options.tryTarget && Targets.hasTargets()) {
Targets.selectOneToken(target => {
if (arme.action == "possession") {
RdDPossession.onAttaquePossession(target, this, rollData.competence)
RdDPossession.onAttaquePossession(target, this, competence)
}
else {
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
@ -2358,7 +2364,7 @@ export class RdDActor extends RdDBaseActor {
await this._openRollDialog({
name: 'jet-competence',
label: 'Jet ' + Grammar.apostrophe('de', rollData.competence.name),
label: 'Jet ' + Grammar.apostrophe('de', competence.name),
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
rollData: rollData,
callbackAction: r => this.$onRollCompetence(r, options)
@ -2722,8 +2728,6 @@ export class RdDActor extends RdDBaseActor {
ui.notifications.info(`Aucun signe draconiques en ${coord} !`);
return;
}
if (this.currentTMR) this.currentTMR.minimize(); // Hide
let draconicList = this.getDraconicList()
.map(draconic => {
let draconicLecture = duplicate(draconic);
@ -2746,7 +2750,7 @@ export class RdDActor extends RdDBaseActor {
const dialog = await RdDRoll.create(this, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-signedraconique.html',
close: html => { this.currentTMR.maximize() } // Re-display TMR
close: html => { this.tmrApp?.restoreTMRAfterAction() }
},
{
name: 'lire-signe-draconique',
@ -2758,6 +2762,7 @@ export class RdDActor extends RdDBaseActor {
}
);
dialog.render(true);
this.tmrApp?.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
@ -2776,7 +2781,7 @@ export class RdDActor extends RdDBaseActor {
}
await this.deleteEmbeddedDocuments("Item", [rollData.signe._id]);
await RdDResolutionTable.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.html');
this.currentTMR.close();
this.tmrApp.close();
}
/* -------------------------------------------- */
@ -3011,8 +3016,8 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
refreshTMRView() {
if (this.currentTMR) {
this.currentTMR.externalRefresh();
if (this.tmrApp) {
this.tmrApp.externalRefresh();
}
}
@ -3020,6 +3025,7 @@ export class RdDActor extends RdDBaseActor {
async displayTMR(mode = "normal") {
if (this.tmrApp) {
ui.notifications.warn("Vous êtes déja dans les TMR....");
this.tmrApp.forceTMRDisplay();
return
}
if (mode != 'visu' && this.getEffect(STATUSES.StatusDemiReve)) {
@ -3065,34 +3071,59 @@ export class RdDActor extends RdDBaseActor {
hasPlayerOwner: this.hasPlayerOwner
}
this.currentTMR = await RdDTMRDialog.create(this, tmrFormData);
this.currentTMR.render(true);
this.tmrApp = await RdDTMRDialog.create(this, tmrFormData);
this.tmrApp.render(true);
}
/* -------------------------------------------- */
rollArme(arme) {
getCompetenceArme(arme, competenceName) {
switch (arme.type) {
case TYPES.competencecreature:
return arme.name
case TYPES.arme:
switch (competenceName) {
case 'competence': return arme.system.competence;
case 'unemain': return RdDItemArme.competence1Mains(arme);
case 'deuxmains': return RdDItemArme.competence2Mains(arme);
case 'tir': return arme.system.tir;
case 'lancer': return arme.system.lancer;
}
}
return undefined
}
/* -------------------------------------------- */
/**
*
* @param {*} arme item d'arme/compétence de créature
* @param {*} categorieArme catégorie d'attaque à utiliser: competence (== melee), lancer, tir; naturelle, possession
* @returns
*/
rollArme(arme, categorieArme = "competence") {
let compToUse = this.getCompetenceArme(arme, categorieArme)
if (!Targets.hasTargets()) {
RdDConfirm.confirmer({
settingConfirmer: "confirmer-combat-sans-cible",
content: `<p>Voulez vous faire un jet de compétence ${arme.system.competence} sans choisir de cible valide?
content: `<p>Voulez vous faire un jet de ${compToUse} sans choisir de cible valide?
<br>Tous les jets de combats devront être gérés à la main
</p>`,
title: 'Ne pas utiliser les automatisation de combat',
buttonLabel: "Pas d'automatisation",
onAction: async () => {
this.rollCompetence(arme.system.competence, { tryTarget: false })
this.rollCompetence(compToUse, { tryTarget: false })
}
});
return;
}
Targets.selectOneToken(target => {
if (Targets.isTargetEntite(target)) {
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
return;
}
const competence = this.getCompetence(arme.system.competence)
const competence = this.getCompetence(compToUse)
//console.log("RollArme", competence, arme)
if (competence.isCompetencePossession()) {
return RdDPossession.onAttaquePossession(target, this, competence);
}
@ -3121,7 +3152,7 @@ export class RdDActor extends RdDBaseActor {
async onRollTachePremiersSoins(blessureId, rollData) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({ actorId: this.id, method: 'onRollTachePremiersSoins', args: [blessureId, rollData] });
return RdDBaseActor.remoteActorCall({ tokenId: this.token?.id, actorId: this.id, method: 'onRollTachePremiersSoins', args: [blessureId, rollData] });
}
const blessure = this.getItem(blessureId, 'blessure')
console.log('TODO update blessure', this, blessureId, rollData, rollData.tache);
@ -3151,7 +3182,7 @@ export class RdDActor extends RdDBaseActor {
async onRollSoinsComplets(blessureId, rollData) {
if (!this.isOwner) {
return RdDBaseActor.remoteActorCall({ actorId: this.id, method: 'onRollSoinsComplets', args: [blessureId, rollData] });
return RdDBaseActor.remoteActorCall({ tokenId: this.token?.id, actorId: this.id, method: 'onRollSoinsComplets', args: [blessureId, rollData] });
}
const blessure = this.getItem(blessureId, 'blessure')
if (blessure && blessure.system.premierssoins.done && !blessure.system.soinscomplets.done) {
@ -3245,6 +3276,7 @@ export class RdDActor extends RdDBaseActor {
const attackerId = attacker?.id;
if (ReglesOptionnelles.isUsing('validation-encaissement-gr') && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'appliquerEncaissement',
args: [rollData, show, attackerId]

View File

@ -50,7 +50,13 @@ export class RdDBaseActor extends Actor {
static onRemoteActorCall(callData, userId) {
if (userId == game.user.id) {
const actor = game.actors.get(callData?.actorId);
let actor = game.actors.get(callData?.actorId);
if ( callData.tokenId) {
let token = canvas.tokens.placeables.find(t => t.id == callData.tokenId)
if (token) {
actor = token.actor
}
}
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, ')');
@ -144,9 +150,10 @@ export class RdDBaseActor extends Actor {
.forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
}
async creerObjetParMJ(object){
async creerObjetParMJ(object) {
if (!Misc.isUniqueConnectedGM()) {
RdDBaseActor.remoteActorCall({
tokenId: this.token?.id,
actorId: this.id,
method: 'creerObjetParMJ',
args: [object]
@ -220,6 +227,7 @@ export class RdDBaseActor extends Actor {
if (fromActorId && !game.user.isGM) {
RdDBaseActor.remoteActorCall({
userId: Misc.connectedGMOrUser(),
tokenId: this.token?.id,
actorId: this.id,
method: 'ajouterSols', args: [sols, fromActorId]
});
@ -262,7 +270,7 @@ export class RdDBaseActor extends Actor {
const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
if (!itemVendu) {
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !`: `Impossible de retrouver: ${achat.vente.item.name} !`);
ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`);
return;
}
if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) {
@ -447,62 +455,33 @@ export class RdDBaseActor extends Actor {
/* -------------------------------------------- */
conteneurPeutContenir(dest, item) {
conteneurPeutContenir(dest, moved) {
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 !
if (moved.isConteneurContenu(dest)) {
ui.notifications.warn(`Impossible de déplacer un conteneur parent (${moved.name}) dans un de ses contenus ${dest.name} !`);
return false;
}
// 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
const encContenu = dest.getEncContenu();
const newEnc = moved.getEncTotal(); // Calculer le total actuel du nouvel objet
const placeDisponible = Math.roundDecimals(dest.system.capacite - encContenu - newEnc, 4)
// Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
if (Number(destData.system.capacite) < encContenu + newEnc) {
if (placeDisponible < 0) {
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}!`);
`Le conteneur ${dest.name} a une capacité de ${dest.system.capacite}, et contient déjà ${encContenu}.
Impossible d'y ranger: ${moved.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 */

View File

@ -114,7 +114,7 @@ export class DialogChronologie extends Dialog {
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()
dateReel: this.html.find("form.rdddialogchrono :input[name='dateReel']").val().replace('T', ' ')
}
}

View File

@ -20,14 +20,14 @@ const nomCategorieParade = {
export class RdDItemArme extends Item {
static isArme(item) {
return RdDItemCompetenceCreature.getCategorieAttaque(item) || item.type == 'arme';
return RdDItemCompetenceCreature.getCategorieAttaque(item) || item.type == TYPES.arme;
}
/* -------------------------------------------- */
static getArme(arme) {
switch (arme ? arme.type : '') {
case 'arme': return arme;
case 'competencecreature':
case TYPES.arme: return arme;
case TYPES.competencecreature:
return RdDItemCompetenceCreature.armeCreature(arme);
}
return RdDItemArme.mainsNues();
@ -68,14 +68,14 @@ export class RdDItemArme extends Item {
return armeData.system.categorie_parade;
}
// pour compatibilité avec des personnages existants
if (armeData.type == 'competencecreature' || armeData.system.categorie == 'creature') {
if (armeData.type == TYPES.competencecreature || armeData.system.categorie == 'creature') {
return armeData.system.categorie_parade || (armeData.system.isparade ? 'armes-naturelles' : '');
}
if (!armeData.type.match(/arme|competencecreature/)) {
return '';
}
if (armeData.system.competence == undefined) {
return 'competencecreature';
return TYPES.competencecreature;
}
let compname = armeData.system.competence.toLowerCase();
if (compname.match(/^(dague de jet|javelot|fouet|arc|arbalête|fronde|hache de jet|fléau)$/)) return '';
@ -157,23 +157,33 @@ export class RdDItemArme extends Item {
}
return armeData;
}
static competence2Mains(arme) {
return arme.system.competence.replace(" 1 main", " 2 mains");
}
static competence1Mains(arme) {
return arme.system.competence.replace(" 2 mains", " 1 main");
}
static isArmeUtilisable(arme) {
return arme.type == 'arme' && arme.system.equipe && (arme.system.resistance > 0 || arme.system.portee_courte > 0);
}
static ajoutCorpsACorps(armes, competences, carac) {
let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { system: { niveau: -6 } };
let init = RdDCombatManager.calculInitiative(corpsACorps.system.niveau, carac['melee'].value);
armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.system.niveau, initiative: init }));
armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init }));
static ajoutCorpsACorps(armes, actor) {
armes.push(RdDItemArme.mainsNues(actor));
armes.push(RdDItemArme.empoignade(actor));
}
static corpsACorps(mainsNuesActor) {
const corpsACorps = {
static corpsACorps(actor) {
let competence = actor?.getCompetenceCorpsACorps() ?? { system: { niveau: -6 } };
let melee = actor? actor.system.carac['melee'].value : 0
return {
_id: competence?.id,
name: 'Corps à corps',
type: TYPES.arme,
img: 'systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp',
system: {
initiative: RdDCombatManager.calculInitiative(competence.system.niveau, melee),
equipe: true,
rapide: true,
force: 0,
@ -181,23 +191,22 @@ export class RdDItemArme extends Item {
dommagesReels: 0,
mortalite: 'non-mortel',
competence: 'Corps à corps',
deuxmains: true,
categorie_parade: 'sans-armes'
}
};
mergeObject(corpsACorps.system, mainsNuesActor ?? {}, { overwrite: false });
return corpsACorps;
}
}
static mainsNues(mainsNuesActor) {
const mainsNues = RdDItemArme.corpsACorps(mainsNuesActor)
static mainsNues(actor) {
const mainsNues = RdDItemArme.corpsACorps(actor)
mainsNues.name = 'Mains nues'
mainsNues.system.cac = 'pugilat'
mainsNues.system.baseInit = 4
return mainsNues;
}
static empoignade(mainsNuesActor) {
const empoignade = RdDItemArme.corpsACorps(mainsNuesActor)
static empoignade(actor) {
const empoignade = RdDItemArme.corpsACorps(actor)
empoignade.name = 'Empoignade'
empoignade.system.cac = 'empoignade'
empoignade.system.baseInit = 3

View File

@ -79,10 +79,9 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static isCompetenceArme(competence) {
if (competence.isCompetence()) {
if (competence.isCompetence() && !competence.isCorpsACorps() && !competence.isEsquive()) {
switch (competence.system.categorie) {
case 'melee':
return !Grammar.toLowerCaseNoAccent(competence.name).includes('esquive');
case 'tir':
case 'lancer':
return true;
@ -93,10 +92,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */
static isArmeUneMain(competence) {
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("1 main");
return competence.isCompetenceArme() && competence.name.toLowerCase().includes("1 main");
}
static isArme2Main(competence) {
return RdDItemCompetence.isCompetenceArme(competence) && competence.name.toLowerCase().includes("2 main");
return competence.isCompetenceArme() && competence.name.toLowerCase().includes("2 main");
}
static isThanatos(competence) {

View File

@ -1,5 +1,5 @@
import { RdDItem, TYPES } from "./item.js";
import { TYPES } from "./item.js";
import { RdDCombatManager } from "./rdd-combat.js";
const categories = {

View File

@ -113,7 +113,7 @@ export class RdDItemSheet extends ItemSheet {
formData.competences = competences;
}
if (this.item.type == 'arme') {
formData.competences = competences.filter(it => RdDItemCompetence.isCompetenceArme(it))
formData.competences = competences.filter(it => it.isCompetenceArme())
}
if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));
@ -195,7 +195,7 @@ export class RdDItemSheet extends ItemSheet {
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.getActionRenderItem()));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).dialogFabriquerPotion(this.item));
this.html.find('.creer-potion-base').click((event) => this._getEventActor(event).actionHerbe(this.item));
this.html.find('.alchimie-tache a').click((event) => {
let actor = this._getEventActor(event);

View File

@ -220,6 +220,32 @@ export class RdDItem extends Item {
isService() { return this.type == TYPES.service; }
isCompetence() { return typesObjetsCompetence.includes(this.type) }
isEsquive() {
return (this.isCompetence()
&& this.system.categorie == 'melee'
&& Grammar.includesLowerCaseNoAccent(this.name, 'Esquive'));
}
isCorpsACorps() {
return (this.isCompetence()
&& this.system.categorie == 'melee'
&& Grammar.includesLowerCaseNoAccent(this.name, 'Corps à Corps'));
}
isCompetenceArme() {
if (this.isCompetence()) {
switch (this.system.categorie) {
case 'melee':
return !this.isCorpsACorps() && !this.isEsquive()
case 'tir':
case 'lancer':
return true;
}
}
return false;
}
isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) }
@ -395,6 +421,16 @@ export class RdDItem extends Item {
return Math.max(this.system.encombrement ?? 0, 0);
}
getEncContenu() {
return this.getContenu()
.map(it => it.getRecursiveEnc())
.reduce(Misc.sum(), 0);
}
getRecursiveEnc() {
return this.getEncTotal() + this.getEncContenu()
}
getEncHerbe() {
switch (this.system.categorie) {
case 'Repos': case 'Soin': case 'Alchimie':
@ -404,6 +440,18 @@ export class RdDItem extends Item {
}
getContenu() {
if (this.isConteneur()) {
return this.system.contenu.map(idContenu => this.actor.getItem(idContenu));
}
return []
}
isConteneurContenu(conteneur) {
return this.getContenu()
.find(it => it.id == conteneur.id || it.isConteneurContenu(conteneur))
}
valeurTotale() {
return (this.isService() ? 1 : this.getQuantite()) * this.valeur()
}
@ -675,7 +723,7 @@ export class RdDItem extends Item {
_armeChatData() {
return [
`<b>Compétence</b>: ${this.system.competence}`,
`<b>Dommages</b>: ${this.system.dommages} ${this.system.mortalite=='non-mortel'? '(Non mortel)':''}`,
`<b>Dommages</b>: ${this.system.dommages} ${this.system.mortalite == 'non-mortel' ? '(Non mortel)' : ''}`,
`<b>Force minimum</b>: ${this.system.force}`,
`<b>Resistance</b>: ${this.system.resistance}`,
...this._inventaireTemplateChatData()

View File

@ -171,8 +171,7 @@ export class RdDCombatManager extends Combat {
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"))) {
if (arme.system.unemain && arme.system.competence) {
actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme,
infoMain: "(1 main)",
@ -187,7 +186,7 @@ export class RdDCombatManager extends Combat {
arme: arme,
infoMain: "(2 mains)",
dommagesReel: Number(tableauDommages[1]),
competence: arme.system.competence.replace(" 1 main", " 2 mains"),
competence: RdDItemArme.competence2Mains(arme),
carac: carac,
competences: competences
}));
@ -258,11 +257,10 @@ export class RdDCombatManager extends Combat {
actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']);
} else if (actor.isPersonnage()) {
// Recupération des items 'arme'
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
.concat(RdDItemArme.empoignade())
.concat(RdDItemArme.mainsNues());
const competences = actor.itemTypes['competence'];
const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it))
.concat(RdDItemArme.empoignade(actor))
.concat(RdDItemArme.mainsNues(actor));
actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac);
if (actor.system.attributs.hautrevant.value) {
@ -450,7 +448,7 @@ export class RdDCombat {
if (Misc.isUniqueConnectedGM()) {
let turn = combat.turns.find(t => t.token?.id == combat.current.tokenId);
if (turn?.actor) {
RdDCombat.displayActorCombatStatus(combat, turn.actor);
RdDCombat.displayActorCombatStatus(combat, turn.actor, turn.token.id);
// TODO Playaudio for player??
}
}
@ -514,8 +512,12 @@ export class RdDCombat {
/* -------------------------------------------- */
static _callJetDeVie(event) {
let actorId = event.currentTarget.attributes['data-actorId'].value;
let actor = game.actors.get(actorId);
actor.jetVie();
let tokenId = event.currentTarget.attributes['data-tokenId'].value;
let token = canvas.tokens.placeables.find(t => t.id == tokenId)
const actor = token?.actor ?? game.actors.get(actorId);
if (actor?.isOwner) {
actor.jetVie();
}
}
/* -------------------------------------------- */
@ -541,7 +543,7 @@ export class RdDCombat {
}
});
}
html.on("click", '#chat-jet-vie', event => {
html.on("click", 'a.chat-jet-vie', event => {
event.preventDefault();
RdDCombat._callJetDeVie(event);
});
@ -789,7 +791,7 @@ export class RdDCombat {
let rollData = {
passeArme: randomID(16),
mortalite: arme?.system.mortalite,
competence: competence.clone(),
competence: competence,
surprise: this.attacker.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
targetToken: Targets.extractTokenData(this.target),
@ -885,8 +887,8 @@ export class RdDCombat {
}
// # utilisation esquive
const corpsACorps = this.defender.getCompetence("Corps à corps", { onMessage: it => console.info(it, this.defender) });
const esquives = duplicate(this.defender.getCompetences("esquive", { onMessage: it => console.info(it, this.defender) }))
const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) });
const esquives = duplicate(this.defender.getCompetencesEsquive())
esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
const paramChatDefense = {
@ -1044,7 +1046,7 @@ export class RdDCombat {
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: this.defender.getCompetence(competenceParade).clone(),
competence: this.defender.getCompetence(competenceParade),
arme: armeParade,
surprise: this.defender.getSurprise(true),
needParadeSignificative: ReglesOptionnelles.isUsing('categorieParade') && RdDItemArme.needParadeSignificative(attackerRoll.arme, armeParade),
@ -1125,7 +1127,7 @@ export class RdDCombat {
passeArme: attackerRoll.passeArme,
diffLibre: attackerRoll.diffLibre,
attackerRoll: attackerRoll,
competence: competence.clone(),
competence: competence,
surprise: this.defender.getSurprise(true),
surpriseDefenseur: this.defender.getSurprise(true),
carac: this.defender.system.carac,
@ -1294,7 +1296,7 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static async displayActorCombatStatus(combat, actor) {
static async displayActorCombatStatus(combat, actor, tokenId) {
let formData = {
combatId: combat._id,
alias: actor.name,
@ -1303,12 +1305,18 @@ export class RdDCombat {
blessuresStatus: actor.computeResumeBlessure(),
SConst: actor.getSConst(),
actorId: actor.id,
tokenId: tokenId,
isGrave: actor.countBlessures(it => it.isGrave()) > 0,
isCritique: actor.countBlessures(it => it.isCritique()) > 0
}
ChatUtility.createChatWithRollMode(actor.name, {
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, formData)
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs`, formData),
alias: actor.name
});
await ChatMessage.create({
content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs`, formData),
whisper: ChatUtility.getWhisperRecipientsAndGMs(actor.name),
alias: actor.name
});
}
}
}

View File

@ -181,7 +181,7 @@ export class RdDEmpoignade {
let rollData = {
mode, empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone(),
competence: attacker.getCompetenceCorpsACorps(),
selectedCarac: attacker.system.carac.melee,
malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender)
}
@ -210,7 +210,7 @@ export class RdDEmpoignade {
mode: "immobilise",
empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps").clone()
competence: attacker.getCompetenceCorpsACorps()
}
const msg = await ChatMessage.create({
whisper: ChatUtility.getWhisperRecipientsAndGMs(attacker.name),
@ -272,7 +272,7 @@ export class RdDEmpoignade {
mode, attacker, defender, empoignade, attackerRoll,
diffLibre: attackerRoll.diffLibre,
attaqueParticuliere: attackerRoll.particuliere,
competence: defender.getCompetence(competenceName).clone(),
competence: defender.getCompetence(competenceName),
surprise: defender.getSurprise(true),
carac: defender.system.carac,
selectedCarac: defender.system.carac[carac],

View File

@ -1,12 +1,17 @@
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { TYPES } from "./item.js";
export class RdDHotbar {
static async addToHotbar(item, slot) {
let command = `game.system.rdd.RdDHotbar.rollMacro("${item.name}", "${item.type}");`;
let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command));
static async createItemMacro(item, slot, armeCompetence = undefined) {
const itemName = item.name;
let macroName = itemName + RdDHotbar.$macroNameSuffix(armeCompetence);
let command = `game.system.rdd.RdDHotbar.rollMacro("${itemName}", "${item.type}", "${armeCompetence}");`
let macro = game.macros.contents.find(m => (m.name === itemName) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: item.name,
name: macroName,
type: "script",
img: item.img,
command: command
@ -15,6 +20,59 @@ export class RdDHotbar {
await game.user.assignHotbarMacro(macro, slot);
}
static $macroNameSuffix(armeCompetence) {
switch (armeCompetence) {
case 'unemain': return ' (1 main)';
case 'deuxmains': return ' (2 main)';
case 'tir': return ' (tir)';
case 'lancer': return ' (lancer)';
case 'pugilat': return ' (pugilat)';
case 'empoignade': return ' (empoignade)';
}
return ''
}
static async addToHotbar(item, slot) {
switch (item?.type ?? '') {
case TYPES.arme:
{
// Les armes peuvent avoir plusieurs usages
if (item.system.competence != '') {
if (item.system.unemain) {
await this.createItemMacro(item, slot++, 'unemain')
}
if (item.system.deuxmains) {
await this.createItemMacro(item, slot++, 'deuxmains')
}
}
if (item.system.lancer != '') {
await this.createItemMacro(item, slot++, 'lancer')
}
if (item.system.tir != '') {
await this.createItemMacro(item, slot++, 'lancer')
}
}
return
case TYPES.competencecreature:
const categorie = RdDItemCompetenceCreature.getCategorieAttaque(item) ?? 'competence';
await this.createItemMacro(item, slot, categorie)
return
default:
case TYPES.competence:
await this.createItemMacro(item, slot++, 'competence')
if (item.isCorpsACorps()) {
await this.createItemMacro(item, slot++, 'pugilat')
await this.createItemMacro(item, slot++, 'empoignade')
}
if (item.isCompetenceArme()) {
ui.notifications.info(`${item.name} est une compétence d'arme, la macro n'est pas liée à un arme.<br>
Créez la macro depuis l'arme ou l'onglet combat pour garder les automatisations de combat.`);
}
return
}
}
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog for item
@ -23,20 +81,19 @@ export class RdDHotbar {
*/
static initDropbar() {
Hooks.on("hotbarDrop", (bar, documentData, slot) => {
Hooks.on('hotbarDrop', (bar, documentData, slot) => {
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (documentData.type == "Item") {
let item = fromUuidSync(documentData.uuid)
if (item == undefined) {
item = this.actor.items.get(documentData.uuid)
if (documentData.type == 'Item') {
const item = fromUuidSync(documentData.uuid) ?? this.actor.items.get(documentData.uuid)
console.log('DROP', documentData, item)
switch (item?.type) {
case TYPES.arme:
case TYPES.competence:
case TYPES.competencecreature:
this.addToHotbar(item, slot)
return false
}
console.log("DROP", documentData, item)
if (!item || (item.type != "arme" && item.type != "competence")) {
return true
}
this.addToHotbar(item, slot)
return false
}
return true
@ -44,12 +101,14 @@ export class RdDHotbar {
}
/** Roll macro */
static rollMacro(itemName, itemType, bypassData) {
static rollMacro(itemName, itemType, categorieArme = 'competence') {
const speaker = ChatMessage.getSpeaker();
let actor;
if (speaker.token) actor = game.actors.tokens[speaker.token];
if (!actor) actor = game.actors.get(speaker.actor);
if (!actor) {
return ui.notifications.warn(`Impossible de trouver le personnage concerné`);
}
let item = actor?.items.find(it => it.name === itemName && it.type == itemType) ?? undefined;
if (!item) {
return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`);
@ -57,10 +116,23 @@ export class RdDHotbar {
// Trigger the item roll
switch (item.type) {
case "arme":
return actor.rollArme(item);
case "competence":
return actor.rollCompetence(itemName);
case TYPES.arme:
return actor.rollArme(item, categorieArme);
case TYPES.competence:
if (item.isCorpsACorps()) {
switch (categorieArme) {
case 'pugilat':
return actor.rollArme(RdDItemArme.mainsNues(actor), 'competence');
case 'empoignade':
return actor.rollArme(RdDItemArme.empoignade(actor), 'competence');
}
}
return actor.rollCompetence(item);
case TYPES.competencecreature:
return item.system.iscombat && !item.system.isparade
? actor.rollArme(item, categorieArme)
: actor.rollCompetence(item);
}
}

View File

@ -59,6 +59,7 @@ import { RdDSigneDraconiqueItemSheet } from "./item/sheet-signedraconique.js";
import { RdDItemInventaireSheet } from "./item/sheet-base-inventaire.js";
import { AppAstrologie } from "./sommeil/app-astrologie.js";
import { RdDItemArmure } from "./item/armure.js";
import { AutoAdjustDarkness as AutoAdjustDarkness } from "./time/auto-adjust-darkness.js";
/**
* RdD system
@ -182,6 +183,7 @@ export class SystemReveDeDragon {
CONFIG.Combat.documentClass = RdDCombatManager;
// préparation des différents modules
AutoAdjustDarkness.init();
RdDTimestamp.init();
RdDCalendrier.init();
SystemCompendiums.init();

View File

@ -39,7 +39,7 @@ export class RdDPossession {
let rollData = {
mode: "attaque",
isECNIDefender: false,
competence: competence.clone(),
competence: competence,
possession: possession,
attacker: attacker,
defender: defender,

View File

@ -22,7 +22,7 @@ export class RdDRoll extends Dialog {
const html = await renderTemplate(dialogConfig.html, rollData);
let options = { classes: ["rdd-roll-dialog"], width: 600, height: 'fit-content', 'z-index': 99999, close: html => {} };
let options = { classes: ["rdd-roll-dialog"], width: 650, height: 'fit-content', 'z-index': 99999, close: html => {} };
if (dialogConfig.close) {
options.close = dialogConfig.close;
}
@ -50,8 +50,6 @@ export class RdDRoll extends Dialog {
encTotal: true
},
isMalusEncombrementTotal: RdDItemCompetence.isMalusEncombrementTotal(rollData.competence),
malusArmureValue: actor.getMalusArmure(),
surencMalusValue: actor.computeMalusSurEncombrement(),
encTotal: actor.getEncTotal(),
ajustementAstrologique: actor.ajustementAstrologique(),
surprise: actor.getSurprise(false),
@ -187,7 +185,7 @@ export class RdDRoll extends Dialog {
console.log("RdDRollSelectDialog - Cout reve", ptreve);
this.updateRollResult(html);
});
this.html.find("[name='mortalite']").change((event) => {
this.html.find("input.check-mortalite").change((event) => {
this.rollData.dmg.mortalite = event.currentTarget.checked ? "non-mortel" : "mortel";
this.updateRollResult(html);
});
@ -291,34 +289,31 @@ export class RdDRoll extends Dialog {
/* -------------------------------------------- */
async updateRollResult(html) {
let rollData = this.rollData;
const rollData = this.rollData;
rollData.dmg = rollData.attackerRoll?.dmg ?? RdDBonus.dmg(rollData, this.actor.getBonusDegat())
rollData.caracValue = parseInt(rollData.selectedCarac.value)
rollData.mortalite = rollData.attackerRoll?.dmg.mortalite ?? rollData.dmg.mortalite ?? rollData.mortalite ?? 'mortel';
rollData.dmg.mortalite = rollData.dmg.mortalite ?? 'mortel';
rollData.use.appelAuMoral = this.actor.isPersonnage() && RdDCarac.isActionPhysique(rollData.selectedCarac);
let dmgText = Misc.toSignedString(rollData.dmg.total);
switch (rollData.mortalite) {
case 'non-mortel': dmgText = `(${dmgText}) non-mortel`; break;
case 'empoignade': dmgText = `empoignade`; break;
}
RollDataAjustements.calcul(rollData, this.actor);
const resolutionTable = await RdDResolutionTable.buildHTMLTable(RdDResolutionTable.subTable(rollData.caracValue, rollData.finalLevel))
const adjustements = await this.buildAjustements(rollData);
HtmlUtility.showControlWhen(this.html.find(".use-encTotal"), rollData.ajustements.encTotal.visible && RdDCarac.isAgiliteOuDerobee(rollData.selectedCarac));
HtmlUtility.showControlWhen(this.html.find(".use-surenc"), rollData.ajustements.surenc.visible && RdDCarac.isActionPhysique(rollData.selectedCarac));
HtmlUtility.showControlWhen(this.html.find(".utilisation-moral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".divAppelAuMoral"), rollData.use.appelAuMoral);
HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moralTotal.used);
// HtmlUtility.showControlWhen(this.html.find(".diffMoral"), rollData.ajustements.moral.used);
// Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
this.html.find("[name='mortalite']").prop('checked', rollData.mortalite == 'non-mortel');
this.html.find(".dmg-arme-actor").text(dmgText);
this.html.find("input.check-mortalite").prop('checked', rollData.dmg.mortalite == 'non-mortel');
this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) );
this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite);
// this.html.find("[name='dmg-arme-actor']").text(rollData.dmg.mortalite == 'empoignade'? 'empoignade': Misc.toSignedString(rollData.dmg.total) );
// this.html.find("[name='arme-mortalite']").text(rollData.dmg.mortalite);
this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)
}
@ -336,16 +331,19 @@ export class RdDRoll extends Dialog {
return carac;
}
const compName = rollData.competence.name;
if (rollData.draconicList && rollData.selectedSort) {
return compName + " - " + rollData.selectedSort.name;
}
// If a weapon is there, add it in the title
const niveau = Misc.toSignedString(rollData.competence.system.niveau)
if (compName == carac) {
// cas des créatures
return carac + " Niveau " + niveau
return `${carac} Niveau ${niveau}`
}
const armeTitle = (rollData.arme) ? " (" + rollData.arme.name + ") " : "";
return carac + "/" + compName + armeTitle + " Niveau " + niveau
if (rollData.draconicList && rollData.selectedSort) {
// cas de lancer de sort
return `${rollData.competence.name} Niveau ${niveau} ${rollData.selectedSort.name}`
}
if (rollData.arme && rollData.arme.name != compName) {
// ajouter l'arme au titre si son nom n'est pas la compétence
return `${carac} / ${compName} (${rollData.arme.name}) Niveau ${niveau}`
}
return `${carac} / ${compName} Niveau ${niveau}`
}
}

View File

@ -2,7 +2,6 @@ import { SHOW_DICE } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { tmrConstants } from "./tmr-constants.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { ChatUtility } from "./chat-utility.js";
@ -39,14 +38,16 @@ export class RdDTMRDialog extends Dialog {
title: "Terres Médianes de Rêve",
content: html,
buttons: {
closeButton: { label: "Fermer", callback: html => this.close(html) }
closeButton: {
label: "Fermer", callback: html => this.close()
}
},
default: "closeButton"
}
const dialogOptions = {
classes: ["tmrdialog"],
width: 920, height: 980,
width: 920, maxheight: 1024, height: 'fit-content',
'z-index': 40
}
super(dialogConf, dialogOptions);
@ -62,8 +63,8 @@ export class RdDTMRDialog extends Dialog {
this.allTokens = [];
this.rencontreState = 'aucune';
this.pixiApp = new PIXI.Application({ width: 720, height: 860 });
this.pixiTMR = new PixiTMR(this, this.pixiApp);
this.subdialog = undefined
this.callbacksOnAnimate = [];
if (!this.viewOnly) {
@ -74,6 +75,31 @@ export class RdDTMRDialog extends Dialog {
this.pixiTMR.load((loader, resources) => this.createPixiSprites());
}
async forceTMRDisplay() {
this.bringToTop();
if (this.subdialog) {
this.subdialog.bringToTop();
}
}
async restoreTMRAfterAction() {
this.subdialog = undefined
await this.maximize();
this.bringToTop();
}
forceTMRContinueAction() {
ui.notifications.warn('Vous devez finir votre action avant de continuer dans les TMR');
this.subdialog.bringToTop();
return;
}
setTMRPendingAction(dialog) {
this.subdialog = dialog
if (dialog instanceof Application) {
dialog.bringToTop();
}
}
isDemiReveCache() {
return !game.user.isGM && this.actor.isTMRCache();
}
@ -175,6 +201,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async moveFromKey(move) {
if (this.subdialog) {
return this.forceTMRContinueAction();
}
let oddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
if (move == 'top') oddq.row -= 1;
@ -199,7 +228,9 @@ export class RdDTMRDialog extends Dialog {
super.activateListeners(html);
this.html = html;
document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view);
document.getElementsByClassName("tmr-row")
.item(0)
.insertCell(0).append(this.pixiApp.view);
if (this.viewOnly) {
this.html.find('.lancer-sort').remove();
@ -210,6 +241,10 @@ export class RdDTMRDialog extends Dialog {
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord()));
this.html.find('tr.tmr-row *').click((event) => {
this.subdialog?.bringToTop();
});
// Roll Sort
this.html.find('.lancer-sort').click((event) => {
this.actor.rollUnSort(this._getActorCoord());
@ -231,7 +266,6 @@ export class RdDTMRDialog extends Dialog {
this.cumulFatigue += this.fatigueParCase;
}
await this.actor.reveActuelIncDec(reveCout);
// Le reste...
this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this._getActorCoord());
@ -271,6 +305,10 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async close() {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
this.descenteTMR = true;
if (this.actor.tmrApp) {
this.actor.tmrApp = undefined; // Cleanup reference
@ -293,6 +331,7 @@ export class RdDTMRDialog extends Dialog {
switch (action) {
case 'derober':
await this.derober();
this.restoreTMRAfterAction();
return;
case 'refouler':
await this.refouler();
@ -305,6 +344,7 @@ export class RdDTMRDialog extends Dialog {
break;
}
await this.postRencontre(tmr);
this.restoreTMRAfterAction();
}
async derober() {
@ -360,7 +400,6 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
checkQuitterTMR() {
if (this.actor.isDead()) {
this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !");
this.close();
@ -531,8 +570,9 @@ export class RdDTMRDialog extends Dialog {
await this.maitriserRencontre();
}
else {
let dialog = new RdDTMRRencontreDialog(this, this.currentRencontre, tmr);
const dialog = new RdDTMRRencontreDialog(this.actor, this.currentRencontre, tmr);
dialog.render(true);
this.setTMRPendingAction(dialog);
}
}
else {
@ -544,9 +584,12 @@ export class RdDTMRDialog extends Dialog {
_presentCite(tmr) {
const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord));
if (presentCite) {
this.minimize();
const caseData = presentCite;
EffetsDraconiques.presentCites.choisirUnPresent(caseData, (present => this._utiliserPresentCite(presentCite, present, tmr)));
const dialog = EffetsDraconiques.presentCites.choisirUnPresent(caseData, present => {
this._utiliserPresentCite(presentCite, present, tmr)
this.restoreTMRAfterAction();
});
this.setTMRPendingAction(dialog);
}
return presentCite;
}
@ -572,8 +615,6 @@ export class RdDTMRDialog extends Dialog {
presentCite: presentCite
};
await this._tentativeMaitrise(rencontreData);
this.maximize();
this.postRencontre(tmr);
}
@ -587,7 +628,10 @@ export class RdDTMRDialog extends Dialog {
? TMRUtility.getTMRType(tmr.coord) + " ??"
: tmr.label + " (" + tmr.coord + ")");
const fakeDialogRencontre = { bringToTop: () => { } };
this.setTMRPendingAction(fakeDialogRencontre)
let myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE });
this.restoreTMRAfterAction()
if (myRoll == 7) {
this._tellToUser(myRoll + ": Rencontre en " + locTMR);
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
@ -777,22 +821,22 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _maitriserTMR(rollData, callbackMaitrise) {
this.minimize(); // Hide
rollData.isTMRCache = rollData.actor.isTMRCache();
const dialog = await RdDRoll.create(this.actor, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html',
close: html => { this.maximize(); } // Re-display TMR
},
{
name: rollData.maitrise.verbe, label: rollData.maitrise.action,
callbacks: [
this.actor.createCallbackExperience(),
{ action: r => { this.restoreTMRAfterAction() } },
{ action: callbackMaitrise }
]
}
);
dialog.render(true);
this.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
@ -862,7 +906,8 @@ export class RdDTMRDialog extends Dialog {
nettoyerRencontre() {
if (!this.currentRencontre) return; // Sanity check
if (this.currentRencontre.graphics) {
for (let drawRect of this.currentRencontre.graphics) { // Suppression des dessins des zones possibles
for (let drawRect of this.currentRencontre.graphics) {
// Suppression des dessins des zones possibles
this.pixiApp.stage.removeChild(drawRect);
}
}
@ -895,8 +940,8 @@ export class RdDTMRDialog extends Dialog {
}
/* -------------------------------------------- */
isConnaissanceFleuve(currentTMR, nextTMR) {
return TMRUtility.getTMR(currentTMR).type == 'fleuve' &&
isConnaissanceFleuve(tmrApp, nextTMR) {
return TMRUtility.getTMR(tmrApp).type == 'fleuve' &&
TMRUtility.getTMR(nextTMR).type == 'fleuve' &&
EffetsDraconiques.isConnaissanceFleuve(this.actor);
}
@ -906,15 +951,15 @@ export class RdDTMRDialog extends Dialog {
if (this.viewOnly) {
return;
}
let clickOddq = RdDTMRDialog._computeEventOddq(event.nativeEvent);
await this._onClickTMRPos(clickOddq); // Vérifier l'état des compteurs reve/fatigue/vie
}
/* -------------------------------------------- */
async _onClickTMRPos(clickOddq) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
let clickOddq = TMRUtility.computeEventOddq(event);
let currentOddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
let targetCoord = TMRUtility.oddqToCoordTMR(clickOddq);
let currentCoord = TMRUtility.oddqToCoordTMR(currentOddq);
// Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter)
let deplacementType = this._calculDeplacement(targetCoord, currentCoord, currentOddq, clickOddq);
@ -946,7 +991,7 @@ export class RdDTMRDialog extends Dialog {
await this._messagerDemiReve(targetCoord);
break;
default:
ui.notifications.error("Vous ne pouvez pas vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
ui.notifications.error("Vous ne pouvez vous déplacer que sur des cases adjacentes à votre position ou valides dans le cas d'une rencontre");
console.log("STATUS :", this.rencontreState, this.currentRencontre);
}
@ -975,9 +1020,11 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _messagerDemiReve(targetCoord) {
/*
TODO: si la case a un sort en réserve, lancer ce sort.
TODO:
Si la case a un sort en réserve, lancer ce sort.
Si la case est le demi-rêve, ne pas lancer de sort.
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre si on essaie de lancer un sort (ou bloquer le lancer de sort)
Si un lancement de sort est en cours, trouver un moyen de réafficher cette fenêtre
si on essaie de lancer un sort (ou bloquer le lancer de sort)
*/
this.notifierResonanceSigneDraconique(targetCoord);
await this.actor.rollUnSort(targetCoord);
@ -994,6 +1041,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _deplacerDemiReve(targetCoord, deplacementType) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
if (this.currentRencontre != 'normal') {
this.nettoyerRencontre();
}
@ -1043,25 +1093,16 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async positionnerDemiReve(coord) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
await this.actor.updateCoordTMR(coord);
this.forceDemiRevePositionView();
let tmr = TMRUtility.getTMR(coord);
await this.postRencontre(tmr);
return tmr;
}
/* -------------------------------------------- */
static _computeEventOddq(origEvent) {
console.log("EVENT", origEvent)
let canvasRect = origEvent.target.getBoundingClientRect();
let x = origEvent.clientX - canvasRect.left;
let y = origEvent.clientY - canvasRect.top;
let col = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12]
y -= col % 2 == 0 ? tmrConstants.col1_y : tmrConstants.col2_y;
let row = Math.floor(y / tmrConstants.cellh); // [From 0 -> 14]
return { col: col, row: row };
}
/* -------------------------------------------- */
/** Retourne les coordonnées x, h, w, h du rectangle d'une case donnée */
_getCaseRectangleCoord(coord) {

View File

@ -2,7 +2,7 @@
export class RdDTMRRencontreDialog extends Dialog {
/* -------------------------------------------- */
constructor(tmrApp, rencontre, tmr) {
constructor(actor, rencontre, tmr) {
const dialogConf = {
title: "Rencontre en TMR!",
content: "Vous rencontrez un " + rencontre.name + " de force " + rencontre.system.force + "<br>",
@ -28,23 +28,30 @@ export class RdDTMRRencontreDialog extends Dialog {
this.toClose = false;
this.tmr = tmr;
this.tmrApp = tmrApp;
this.actor = actor;
this.rencontre = rencontre;
this.tmrApp.minimize();
}
async onButtonAction(action) {
this.toClose = true;
this.tmrApp.onActionRencontre(action, this.tmr, this.rencontre)
await this.actor.tmrApp?.restoreTMRAfterAction();
this.actor.tmrApp?.onActionRencontre(action, this.tmr, this.rencontre)
}
/* -------------------------------------------- */
close() {
if (this.toClose) {
this.tmrApp.maximize();
return super.close();
async close() {
if (this.actor.tmrApp){
if (this.toClose) {
return await super.close();
}
else {
ui.notifications.info("Vous devez résoudre la rencontre.");
return this.actor.tmrApp.forceTMRContinueAction();
}
}
else {
return await super.close();
}
ui.notifications.info("Vous devez résoudre la rencontre.");
}
}

View File

@ -245,7 +245,8 @@ export class RdDUtility {
'systems/foundryvtt-reve-de-dragon/templates/chat-description.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-info-distance.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-acteur.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-sante.hbs',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-competence-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-actor-carac-xp.html',
'systems/foundryvtt-reve-de-dragon/templates/chat-potionenchantee-chateaudormant.html',

View File

@ -69,8 +69,8 @@ export const referenceAjustements = {
getValue: (rollData, actor) => -actor.getEncTotal()
},
surenc: {
isVisible: (rollData, actor) => actor.isSurenc(),
isUsed: (rollData, actor) => rollData.use?.surenc,
isVisible: (rollData, actor) => RdDCarac.isActionPhysique(rollData.selectedCarac) && actor.isSurenc(),
isUsed: (rollData, actor) => rollData.use?.surenc && RdDCarac.isActionPhysique(rollData.selectedCarac),
getLabel: (rollData, actor) => 'Sur-encombrement',
getValue: (rollData, actor) => actor.computeMalusSurEncombrement()
},

View File

@ -128,7 +128,7 @@ export class AppAstrologie extends Application {
this.selectHeureNaissance(event.currentTarget.attributes['data-heure-naissance'].value);
})
this.html.find('[name="jet-astrologie"]').click(event => this.requestJetAstrologie());
this.html.find('[name="rebuild-nombres-astraux"]').click(event => this.rebuildNombresAstraux());
this.html.find('[name="rebuild-nombres-astraux"]').click(event => this.onRebuild());
this.onCalculThemeAstral();
}
@ -144,7 +144,7 @@ export class AppAstrologie extends Application {
}
/* -------------------------------------------- */
async rebuildNombresAstraux() {
async onRebuild() {
game.system.rdd.calendrier.resetNombresAstraux();
await game.system.rdd.calendrier.rebuildNombresAstraux();

View File

@ -0,0 +1,34 @@
import { SYSTEM_RDD } from "../constants.js";
export const AUTO_ADJUST_DARKNESS = "auto-adjust-darkness";
export class AutoAdjustDarkness {
static init() {
game.settings.register(SYSTEM_RDD, AUTO_ADJUST_DARKNESS, {
name: AUTO_ADJUST_DARKNESS,
scope: "world",
config: false,
default: true,
type: Boolean
});
}
static async adjust(darkness) {
if (AutoAdjustDarkness.isAuto()) {
const scene = game.scenes.viewed;
if (scene.globalLight && scene.tokenVision) {
await scene.update({ darkness });
}
}
}
static isAuto() {
return game.settings.get(SYSTEM_RDD, AUTO_ADJUST_DARKNESS);
}
static async toggle() {
const previous = AutoAdjustDarkness.isAuto();
await game.settings.set(SYSTEM_RDD, AUTO_ADJUST_DARKNESS, !previous)
}
}

View File

@ -5,10 +5,11 @@ import { RdDUtility } from "../rdd-utility.js";
import { RdDDice } from "../rdd-dice.js";
import { Misc } from "../misc.js";
import { DialogChronologie } from "../dialog-chronologie.js";
import { HIDE_DICE, SHOW_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { HIDE_DICE, SYSTEM_RDD, SYSTEM_SOCKET_ID } from "../constants.js";
import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { DialogChateauDormant } from "../sommeil/dialog-chateau-dormant.js";
import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "../sommeil/app-astrologie.js";
import { AutoAdjustDarkness } from "./auto-adjust-darkness.js";
const TEMPLATE_CALENDRIER = "systems/foundryvtt-reve-de-dragon/templates/time/calendar.hbs";
@ -51,7 +52,7 @@ export class RdDCalendrier extends Application {
if (Misc.isUniqueConnectedGM()) { // Uniquement si GM
RdDTimestamp.setWorldTime(this.timestamp);
this.nombresAstraux = this.getNombresAstraux();
this.rebuildNombresAstraux(HIDE_DICE); // Ensure always up-to-date
this.rebuildNombresAstraux(); // Ensure always up-to-date
}
Hooks.on('updateSetting', async (setting, update, options, id) => this.onUpdateSetting(setting, update, options, id));
}
@ -84,33 +85,22 @@ export class RdDCalendrier extends Application {
}
display() {
AutoAdjustDarkness.adjust(RdDTimestamp.getWorldTime().darkness);
const pos = this.getSavePosition()
this.render(true, { left: pos.left, top: pos.top });
return this;
}
_getHeaderButtons() {
const buttons = [];
if (game.user.isGM) {
buttons.unshift({
class: "calendar-astrologie",
icon: "fa-solid fa-moon-over-sun",
onclick: ev => this.showAstrologieEditor()
},
{
class: "calendar-set-datetime",
icon: "fa-solid fa-calendar-pen",
onclick: ev => this.showCalendarEditor()
});
return [
{ class: "calendar-astrologie", icon: "fa-solid fa-moon-over-sun", onclick: ev => this.showAstrologieEditor() },
{ class: "calendar-set-datetime", icon: "fa-solid fa-calendar-pen", onclick: ev => this.showCalendarEditor() },
]
}
return buttons
return []
}
/*async maximize() {
await super.maximize()
this.render(true)
}*/
async close() { }
async onUpdateSetting(setting, update, options, id) {
@ -134,6 +124,7 @@ export class RdDCalendrier extends Application {
formData.isGM = game.user.isGM;
formData.heures = RdDTimestamp.definitions()
formData.horlogeAnalogique = this.horlogeAnalogique;
formData.autoDarkness = AutoAdjustDarkness.isAuto()
return formData;
}
@ -144,6 +135,7 @@ export class RdDCalendrier extends Application {
this.html = html;
this.html.find('.ajout-chronologie').click(ev => DialogChronologie.create());
this.html.find('.toggle-horloge-analogique').click(ev => this.onToggleHorlogeAnalogique())
this.html.find('.toggle-auto-darkness').click(ev => this.onToggleAutoDarkness())
this.html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));
this.html.find('.horloge-roue .horloge-heure').click(event => {
const h = this.html.find(event.currentTarget)?.data('heure');
@ -229,15 +221,8 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
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}`
});
}
async ajouterNombreAstral(indexDate) {
const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: HIDE_DICE, rollMode: "selfroll" });
return {
nombreAstral: nombreAstral,
valeursFausses: [],
@ -272,9 +257,8 @@ export class RdDCalendrier extends Application {
}
/* -------------------------------------------- */
async rebuildNombresAstraux(showDice = HIDE_DICE) {
async rebuildNombresAstraux() {
if (Misc.isUniqueConnectedGM()) {
console.log("Astral rebuild")
let newList = [];
for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
let dayIndex = this.timestamp.indexDate + i;
@ -282,7 +266,7 @@ export class RdDCalendrier extends Application {
if (na) {
newList[i] = na;
} else {
newList[i] = await this.ajouterNombreAstral(dayIndex, showDice);
newList[i] = await this.ajouterNombreAstral(dayIndex);
}
}
this.nombresAstraux = newList;
@ -380,7 +364,7 @@ export class RdDCalendrier extends Application {
if (request.rolled.isSuccess) {
if (request.rolled.isPart) {
// Gestion expérience (si existante)
request.competence = actor.getCompetence("astrologie")
request.competence = actor.getCompetence('Astrologie')
request.selectedCarac = actor.system.carac["vue"];
actor.appliquerAjoutExperience(request, 'hide');
}
@ -452,4 +436,9 @@ export class RdDCalendrier extends Application {
async showAstrologieEditor() {
await AppAstrologie.create();
}
async onToggleAutoDarkness() {
await AutoAdjustDarkness.toggle()
this.display()
}
}

View File

@ -2,6 +2,7 @@ import { SHOW_DICE, SYSTEM_RDD } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Misc } from "../misc.js";
import { RdDDice } from "../rdd-dice.js";
import { AutoAdjustDarkness } from "./auto-adjust-darkness.js";
export const WORLD_TIMESTAMP_SETTING = "calendrier";
@ -15,18 +16,18 @@ export const RDD_MINUTES_PAR_JOUR = 1440; //RDD_HEURES_PAR_JOUR * RDD_MINUTES_PA
const ROUNDS_PAR_MINUTE = 10;
const DEFINITION_HEURES = [
{ key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps" },
{ key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps" },
{ key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps" },
{ key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté" },
{ key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté" },
{ key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté" },
{ key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne" },
{ key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne" },
{ key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne" },
{ key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver" },
{ key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver" },
{ key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver" },
{ key: "vaisseau", label: "Vaisseau", lettreFont: 'v', saison: "Printemps" , darkness: 0.7},
{ key: "sirene", label: "Sirène", lettreFont: 'i', saison: "Printemps" , darkness: 0.4},
{ key: "faucon", label: "Faucon", lettreFont: 'f', saison: "Printemps" , darkness: 0},
{ key: "couronne", label: "Couronne", lettreFont: '', saison: "Eté" , darkness: 0},
{ key: "dragon", label: "Dragon", lettreFont: 'd', saison: "Eté", darkness: 0 },
{ key: "epees", label: "Epées", lettreFont: 'e', saison: "Eté", darkness: 0},
{ key: "lyre", label: "Lyre", lettreFont: 'l', saison: "Automne", darkness: 0.4 },
{ key: "serpent", label: "Serpent", lettreFont: 's', saison: "Automne", darkness: 0.7 },
{ key: "poissonacrobate", label: "Poisson Acrobate", lettreFont: 'p', saison: "Automne", darkness: 1 },
{ key: "araignee", label: "Araignée", lettreFont: 'a', saison: "Hiver", darkness: 1 },
{ key: "roseau", label: "Roseau", lettreFont: 'r', saison: "Hiver", darkness: 1 },
{ key: "chateaudormant", label: "Château Dormant", lettreFont: 'c', saison: "Hiver", darkness: 1 },
]
const FORMULES_DUREE = [
@ -58,6 +59,7 @@ export class RdDTimestamp {
type: Object
});
for (let i = 0; i < DEFINITION_HEURES.length; i++) {
DEFINITION_HEURES[i].heure = i;
DEFINITION_HEURES[i].hh = RdDTimestamp.hh(i);
@ -66,7 +68,6 @@ export class RdDTimestamp {
}
}
static hh(heure) {
return heure < 9 ? `0${heure + 1}` : `${heure + 1}`;
}
@ -239,6 +240,13 @@ export class RdDTimestamp {
get angleHeure() { return this.indexMinute / RDD_MINUTES_PAR_JOUR * 360 - 45 }
get angleMinute() { return this.indexMinute / RDD_MINUTES_PAR_HEURES * 360 + 45 }
get darkness() {
const darknessDebut = RdDTimestamp.definition(this.heure).darkness *100
const darknessFin = RdDTimestamp.definition(this.heure + 1).darkness *100
const darknessMinute = Math.round((darknessFin - darknessDebut) * this.minute / RDD_MINUTES_PAR_HEURES);
return (darknessDebut + darknessMinute)/100
}
/**
* Convertit le timestamp en une structure avec les informations utiles
* pour afficher la date et l'heure

View File

@ -1,6 +1,7 @@
import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js";
import { tmrConstants } from "./tmr-constants.js";
/* -------------------------------------------- */
const TMRMapping = {
@ -163,7 +164,7 @@ const TMRMapping = {
C12: { type: "lac", label: "Lac de Fricassa" },
D12: { type: "collines", label: "Collines dHuaï" },
E12: { type: "monts", label: "Monts Ajourés" },
F12: { type: "necropole", label: "Nécropole de Troat" },
F12: { type: "necropole", label: "Nécropole de Throat" },
G12: { type: "plaines", label: "Plaines de Lufmil" },
H12: { type: "collines", label: "Collines de Tooth" },
I12: { type: "gouffre", label: "Gouffre Abimeux" },
@ -199,7 +200,7 @@ const TMRMapping = {
K14: { type: "necropole", label: "Nécropole dAntinéar" },
L14: { type: "plaines", label: "Plaines de Jislith" },
M14: { type: "desolation", label: "Désolation dAprès" },
A15: { type: "cite", label: "Cité de Mielh" },
C15: { type: "plaines", label: "Plaines de Toué" },
E15: { type: "foret", label: "Forêt des Furies" },
@ -274,11 +275,11 @@ export class TMRUtility {
const tmr = TMRUtility.getTMR(coord);
return Grammar.articleDetermine(tmr.type) + ' ' + tmr.label;
}
static findTMRLike(type, options = {inclusMauvaise:true}) {
static findTMRLike(type, options = { inclusMauvaise: true }) {
const choix = [...Object.values(TMRType)]
if (options.inclusMauvaise){
choix.push({name: 'Mauvaise'});
if (options.inclusMauvaise) {
choix.push({ name: 'Mauvaise' });
}
const selection = Misc.findAllLike(type, choix).map(it => it.name);
if (selection.length == 0) {
@ -297,7 +298,7 @@ export class TMRUtility {
}
static buildSelectionTypesTMR(typesTMR) {
typesTMR = typesTMR?? [];
typesTMR = typesTMR ?? [];
return Object.values(TMRType).map(value => Misc.upperFirst(value.name))
.sort()
.map(name => { return { name: name, selected: typesTMR.includes(name) } });
@ -375,6 +376,36 @@ export class TMRUtility {
return caseList;
}
// /* -------------------------------------------- */
static computeEventPosition(event) {
if (!event.nativeEvent.target.getBoundingClientRect) {
return { x: 0, y: 0 }
}
const canvasRect = event.nativeEvent.target.getBoundingClientRect();
return {
x: event.nativeEvent.clientX - canvasRect.left,
y: event.nativeEvent.clientY - canvasRect.top
};
}
/* -------------------------------------------- */
static computeEventOddq(event) {
var { x, y } = TMRUtility.computeEventPosition(event);
return TMRUtility.computeOddq(x, y);
}
static computeOddq(x, y) {
const col = Math.floor(x / tmrConstants.cellw); // [From 0 -> 12]
const decallageColonne = col % 2 == 0 ? tmrConstants.col1_y : tmrConstants.col2_y;
const row = Math.floor((y - decallageColonne) / tmrConstants.cellh); // [From 0 -> 14]
return { col, row };
}
static computeEventCoord(event) {
const oddq = TMRUtility.computeEventOddq(event);
return TMRUtility.oddqToCoordTMR(oddq);
}
/* -------------------------------------------- */
// https://www.redblobgames.com/grids/hexagons/#distances
// TMR Letter-row correspond to "odd-q" grid (letter => col, numeric => row )
@ -400,7 +431,7 @@ export class TMRUtility {
col >= 0 && col < 13 &&
row >= 0 &&
(row + col % 2 <= 14)
);
);
// if (x >= 0 && x < 13 && y >= 0 && y < 14) return true;
// if (x >= 0 && x < 13 && x % 2 == 0 && y == 14) return true;
// return false;
@ -444,7 +475,7 @@ export class TMRUtility {
static axial_subtract(a, b) {
return {
q: a.q- b.q,
q: a.q - b.q,
r: a.r - b.r
};
}
@ -456,7 +487,7 @@ export class TMRUtility {
// return Cube(q, r, s)
// }
// /* -------------------------------------------- */
// static computeRealPictureCoordinates(coordOddq) {
// let decallagePairImpair = (coordOddq.col % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;

View File

@ -4,7 +4,6 @@ import { PixiTMR } from "./pixi-tmr.js";
export class CarteTmr extends Draconique {
constructor() {
console.log("Sprite create 1!!!!")
super();
}
@ -14,9 +13,25 @@ export class CarteTmr extends Draconique {
async onActorCreateOwned(actor, item) { }
code() { return 'tmr' }
img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmp_main_r1.webp' }
img() { return 'systems/foundryvtt-reve-de-dragon/styles/img/ui/tmr.webp' }
createSprite(pixiTMR) {
return pixiTMR.carteTmr(this.code());
const img = PixiTMR.getImgFromCode(this.code())
const sprite = new PIXI.Sprite(PIXI.utils.TextureCache[img]);
// Setup the position of the TMR
sprite.x = 0;
sprite.y = 0;
sprite.width = 722;
sprite.height = 860;
// Rotate around the center
sprite.anchor.set(0);
sprite.buttonMode = true;
sprite.tmrObject = pixiTMR;
pixiTMR.addTooltip(sprite, (e,s) => this.computeTooltip(e,s));
pixiTMR.pixiApp.stage.addChild(sprite);
return sprite;
}
}

View File

@ -11,7 +11,7 @@ const registeredEffects = [
export class Draconique {
static isCaseTMR(item) { return item.type == TYPES.casetmr; }
static isQueueDragon(item) { return item.isQueueDragon(); }
static isSouffleDragon(item) {return item.type == TYPES.souffle; }
static isSouffleDragon(item) { return item.type == TYPES.souffle; }
static isTeteDragon(item) { return item.type == TYPES.tete; }
static isQueueSouffle(item) { return Draconique.isQueueDragon(item) || Draconique.isSouffleDragon(item); }
@ -78,25 +78,45 @@ export class Draconique {
/**
* @param {*} img l'url du fichier image à utiliser pour le token. Si indéfini (et si createSprite n'est pas surchargé),
* un disque est utilisé.
*/
*/
img() { return undefined }
/**
* factory d'élément graphique PIXI correpsondant à l'objet draconique
* factory d'élément graphique PIXI correspondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
*/
*/
token(pixiTMR, linkData, coordTMR, type = undefined) {
const token = {
sprite: this.createSprite(pixiTMR),
coordTMR: coordTMR
};
token[type ?? this.code()] = linkData;
console.log("SPRITE: ", token.sprite)
//PixiTMR.getImgFromCode()
pixiTMR.addTooltip(token.sprite, this.tooltip(linkData));
this.linkData = linkData;
if (this.tooltip(linkData)) {
pixiTMR.addTooltip(token.sprite, (e, s) => this.computeTooltip(e, s));
}
return token;
}
/**
* methode en charge de recalculer le tooltip lorsque la souris bouge
* @param {*} event evenement contenant les coordonnées
* @param {*} sprite sprite pour laquelle calculer le tooltip
*/
computeTooltip(event, sprite) {
if (sprite.isOver) {
const oddq = TMRUtility.computeEventOddq(event);
const coord = TMRUtility.oddqToCoordTMR(oddq);
const tmr = TMRUtility.getTMR(coord)
if (tmr){
const label = TMRUtility.getTMRLabel(coord);
const text = this.tooltip(this.linkData);
return text ? `${coord}: ${label}\n${text}` : `${coord}: ${label}`
}
}
return '';
}
/**
* factory d'élément graphique PIXI correpsondant à l'objet draconique
* @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.
@ -118,11 +138,11 @@ export class Draconique {
isCase(item, coord = undefined) {
return Draconique.isCaseTMR(item) && item.system.specific == this.code() && (coord ? item.system.coord == coord : true);
}
find(list, coord = undefined) {
return list.find(c => this.isCase(c, coord));
}
async createCaseTmr(actor, label, tmr, sourceId = undefined) {
const casetmrData = {
name: label, type: 'casetmr', img: this.img(),
@ -130,12 +150,12 @@ export class Draconique {
};
await actor.createEmbeddedDocuments('Item', [casetmrData]);
}
async deleteCasesTmr(actor, draconique) {
let caseTmrs = actor.items.filter(it => this.isCaseForSource(it, draconique));
await actor.deleteEmbeddedDocuments('Item', caseTmrs.map(it => it.id));
}
isCaseForSource(item, draconique) {
return Draconique.isCaseTMR(item) && item.system.specific == this.code() && item.system.sourceid == draconique.id;
}

View File

@ -1,6 +1,8 @@
import { RdDTMRDialog } from "../rdd-tmr-dialog.js";
import { tmrConstants, tmrTokenZIndex } from "../tmr-constants.js";
import { TMRUtility } from "../tmr-utility.js";
const tooltipStyle = new PIXI.TextStyle({
export const tooltipStyle = new PIXI.TextStyle({
fontFamily: 'CaslonAntique',
fontSize: 18,
fill: '#FFFFFF',
@ -20,7 +22,7 @@ export class PixiTMR {
this.callbacksOnAnimate = [];
}
async load( onLoad = (loader, resources) => {} ) {
async load(onLoad = (loader, resources) => { }) {
// WIP - Deprecated since v7 : let loader = new PIXI.Loader();
for (const [name, img] of Object.entries(PixiTMR.textures)) {
const texture = await PIXI.Assets.load(img);
@ -40,40 +42,17 @@ export class PixiTMR {
PixiTMR.textures[name] = img;
}
animate(animation = pixiApp=>{})
{
animate(animation = pixiApp => { }) {
this.callbacksOnAnimate.push(() => animation(this.pixiApp));
}
carteTmr(code) {
let img = PixiTMR.getImgFromCode(code)
const carteTmr = new PIXI.Sprite(PIXI.utils.TextureCache[img]);
console.log(code, carteTmr)
// Setup the position of the TMR
carteTmr.x = 0;
carteTmr.y = 0;
carteTmr.width = 720;
carteTmr.height = 860;
// Rotate around the center
carteTmr.anchor.set(0);
carteTmr.eventMode = 'dynamic'; // PIXI 7 : Not sure ..
// This one is deprecated ; carteTmr.interactive = true;
carteTmr.buttonMode = true;
carteTmr.tmrObject = this;
if (!this.tmrObject.viewOnly) {
carteTmr.on('pointerdown', event => this.onClickBackground(event));
}
this.pixiApp.stage.addChild(carteTmr);
return carteTmr;
}
sprite(code, options = {}) {
let img = PixiTMR.getImgFromCode(code)
const texture = PIXI.utils.TextureCache[img];
if (!texture) {
console.error("Texture manquante", code, PIXI.utils.TextureCache)
return;
}
}
let sprite = new PIXI.Sprite(texture);
sprite.width = options.taille ?? tmrConstants.half;
sprite.height = options.taille ?? tmrConstants.half;
@ -81,13 +60,13 @@ export class PixiTMR {
if (options.color) {
sprite.tint = options.color;
}
sprite.zIndex = options.zIndex ?? tmrTokenZIndex.casehumide+1;
sprite.zIndex = options.zIndex ?? tmrTokenZIndex.casehumide + 1;
sprite.alpha = options.alpha ?? 0.75;
sprite.decallage = options.decallage ?? tmrConstants.center;
this.pixiApp.stage.addChild(sprite);
return sprite;
}
}
circle(name, options = {}) {
let sprite = new PIXI.Graphics();
sprite.beginFill(options.color, options.opacity);
@ -98,27 +77,37 @@ export class PixiTMR {
return sprite;
}
addTooltip(sprite, text) {
if (text) {
sprite.tooltip = new PIXI.Text(text, tooltipStyle);
sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false;
// Deprecated : sprite.interactive = true;
sprite.eventMode = 'dynamic'; // PIXI 7 To be checked
sprite.on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', () => this.onShowTooltip(sprite))
.on('pointerout', () => this.onHideTooltip(sprite));
}
addTooltip(sprite, computeTooltip) {
sprite.tooltip = new PIXI.Text('', tooltipStyle);
sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false;
sprite.eventMode = 'static';
sprite
.on('pointermove', event => this.onPointerMove(event, sprite, computeTooltip))
.on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', event => this.onShowTooltip(event, sprite))
.on('pointerout', event => this.onHideTooltip(event, sprite));
}
onClickBackground(event) {
this.tmrObject.onClickTMR(event)
if (!this.viewOnly) {
this.tmrObject.onClickTMR(event)
}
}
onShowTooltip(sprite) {
if (sprite.tooltip) {
onPointerMove(event, sprite, computeTooltip) {
if (sprite.isOver && sprite.tooltip) {
var { x, y } = TMRUtility.computeEventPosition(event);
const oddq = TMRUtility.computeOddq(x, y);
sprite.tooltip.x = x + (oddq.col > 8 ? - 3 * tmrConstants.full : tmrConstants.half)
sprite.tooltip.y = y + (oddq.row > 10 ? - tmrConstants.half : tmrConstants.half)
sprite.tooltip.text = computeTooltip(event, sprite);
}
}
onShowTooltip(event, sprite) {
if (sprite.tooltip) {
if (!sprite.isOver) {
sprite.tooltip.x = sprite.x;
sprite.tooltip.y = sprite.y;
@ -128,7 +117,7 @@ export class PixiTMR {
}
}
onHideTooltip(sprite) {
onHideTooltip(event, sprite) {
if (sprite.tooltip) {
if (sprite.isOver) {
this.pixiApp.stage.removeChild(sprite.tooltip);
@ -137,7 +126,7 @@ export class PixiTMR {
}
}
setPosition( sprite, oddq) {
setPosition(sprite, oddq) {
let decallagePairImpair = (oddq.col % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
let dx = (sprite.decallage == undefined) ? 0 : sprite.decallage.x;
let dy = (sprite.decallage == undefined) ? 0 : sprite.decallage.y;

View File

@ -49,12 +49,13 @@ export class PresentCites extends Draconique {
const presents = await game.system.rdd.rencontresTMR.getPresentsCite()
const buttons = {};
presents.forEach(r => buttons['present'+r.id] = { icon: '<i class="fas fa-check"></i>', label: r.name, callback: async () => onChoixPresent(r) });
let d = new Dialog({
let dialog = new Dialog({
title: "Présent des cités",
content: `La ${this.tmrLabel(casetmr)} vous offre un présent, faites votre choix`,
buttons: buttons
});
d.render(true);
dialog.render(true);
return dialog
}
async ouvrirLePresent(actor, casetmr) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

BIN
styles/img/ui/tmr.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 KiB

View File

@ -171,6 +171,9 @@ i:is(.fas, .far) {
width: fit-content;
}
.tmr-dialog table {
border: none;
}
.system-foundryvtt-reve-de-dragon .sheet-header div.tmr-buttons {
padding: 0;
margin: 0;

View File

@ -1,14 +1,13 @@
{
"id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon",
"version": "11.0.13",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.0.13.zip",
"version": "11.0.28",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.0.28.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json",
"changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": {
"minimum": "11",
"verified": "11",
"maximum": "11"
"verified": "11"
},
"description": "Rêve de Dragon RPG for FoundryVTT",
"authors": [

View File

@ -0,0 +1 @@
<h4>C'est au tour de {{alias}} !</h4>

View File

@ -1,4 +1,4 @@
<h4>C'est au tour de {{alias}} !</h4>
<h4>Résumé de santé pour {{alias}}</h4>
<div data-combatid="{{combatId}}" data-combatmessage="actor-turn-summary">{{blessuresStatus}}</div>
<div>Son état général est de : {{etatGeneral}} {{#if isSonne}} et est <strong>sonné</strong>{{/if}}</div>
{{#if isGrave}}
@ -6,5 +6,7 @@
{{/if}}
{{#if isCritique}}
<div>{{alias}} souffre d'une <strong>Blessure Critique</strong> : faites un
<a id="chat-jet-vie" class="chat-card-button" data-actorId="{{actorId}}">Jet de Vie.<a></div>
<a class="chat-card-button chat-jet-vie"
data-tokenId="{{tokenId}}"
data-actorId="{{actorId}}">Jet de Vie.<a></div>
{{/if}}

View File

@ -1,6 +1,6 @@
{{#if use.moral}}
<span>
Vous avez fait appel {{#if (gt moral 0)}}au moral{{else}}à l'énergie du déspoir{{/if}}
Vous avez fait appel {{#if (gt moral 0)}}au moral{{else}}à l'énergie du désespoir{{/if}}
{{#if (eq perteMoralEchec 'dissolution')}}et échoué, cous marquez un point de dissolution!.
{{else if (eq perteMoralEchec 'perte')}}et échoué, votre moral baisse à {{moral}}.
{{else}}et réussi, votre moral reste de {{moral}}.

View File

@ -26,7 +26,7 @@
{{#if (eq dmg.mortalite 'entiteincarnee')}}subit le coup
{{else if mort}}vient de mourir
{{else if blessure}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.labelGravite}}
{{#if (gt blessure.system.gravite 0)}}subit une blessure {{blessure.system.label}}
{{else}}subit une contusion
{{~/if~}}
{{else}}s'en sort sans une égratignure

View File

@ -1,13 +1,29 @@
<form class="skill-roll-dialog">
<img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/>
<h2 class="dialog-roll-title"></h2>
<div class="grid grid-2col">
<div class="flex-group-left">
<img class="chat-icon" src="{{competence.img}}" alt="{{competence.name}}"/>
<div class="flexrow">
<label>Caractéristique</label>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html"}}
<span>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-select-carac.html"}}
</span>
</div>
{{#if targetToken}}
<div class="flexrow">
<label>Cible:</label>
<label>
<img class="sheet-competence-img" src="{{targetToken.img}}" title="{{targetToken.name}}" />
{{targetToken.name}}
</label>
</div>
{{/if}}
{{#if ajustements.attaqueDefenseurSurpris.used}}
<div class="flexrow">
<label>{{ajustements.attaqueDefenseurSurpris.label}}</label>
</div>
{{/if}}
{{#if arme}}
{{#if attackerRoll}}
{{#if attackerRoll.tactique}}
@ -27,41 +43,28 @@
<div class="tooltiptext ttt-ajustements">
<div>
<strong>Charge</strong> : Les longueurs d'armes n'interviennent pas dans la charge, il faut gérer une initiative aléatoire dans ce cas.
<br><strong>Feinte</strong> : Vous devez avoir l'initative sur votre adversaire et y renoncer.
<br>
<strong>Feinte</strong> : Vous devez avoir l'initative sur votre adversaire et y renoncer.
</div>
</div>
</span>
</div>
{{/if}}
{{#if targetToken}}
<div class="flexrow">
Cible: {{targetToken.name}}
<img class="sheet-competence-img" src="{{targetToken.img}}" title="{{targetToken.name}}" />
</div>
{{/if}}
{{#if ajustements.attaqueDefenseurSurpris.used}}
<div class="flexrow">
<label>{{ajustements.attaqueDefenseurSurpris.label}}</label>
</div>
{{/if}}
{{#unless attackerRoll}}
<div class="flexrow">
<label>D&eacute;gats:</label>
{{#if (eq arme.system.mortalite 'non-mortel')}}
<label class="dmg-arme-actor"></label>
{{else if (eq arme.system.mortalite 'empoignade')}}
{{#if (eq arme.system.mortalite 'empoignade')}}
<label>Empoignade</label>
{{else}}
<span>
<input class="attribute-value" type="checkbox" name="motalite" {{#unless (eq mortalite 'mortel')}}checked{{/unless}} />
<label class="dmg-arme-actor"></label>
{{#unless (eq arme.system.mortalite 'non-mortel')}}
<input class="attribute-value check-mortalite" type="checkbox" name="mortalite" {{#unless (eq mortalite 'mortel')}}checked{{/unless}} />
{{/unless}}
<label class="dmg-arme-actor" name="dmg-arme-actor"></label> (<label class="arme-mortalite" name="arme-mortalite"></label>)
</span>
{{/if}}
</div>
{{/unless}}
{{/if}}
{{/if}}
<div class="flexrow"></div>
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-surenc.html"}}
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-enctotal.html"}}
</div>
@ -75,6 +78,7 @@
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffCondition.html"}}
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-forcer.html"}}
{{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-moral.html"}}
<div class="flexrow"></div>
<div class="placeholder-ajustements" class="flexrow"></div>
</div>
</div>

View File

@ -1,8 +1,7 @@
<form class="tmr-dialog">
<h2 class="comptmrdialog" id="tmrDialogTitle"></h2>
<table id="tmrsheet">
<tr id="tmrrow1">
<h2 class="comptmrdialog" id="tmrDialogTitle" style="visibility: hidden;"></h2>
<table>
<tr class="tmr-row">
<td>
{{#if (eq mode "visu")}}
<div class="flex-group-center">
@ -54,6 +53,5 @@
</td>
</tr>
</table>
</form>

View File

@ -16,6 +16,7 @@
</div>
{{/if}}
<div class="horloge-digitale">
<span>
<a class="toggle-horloge-analogique">
{{#if horlogeAnalogique}}
@ -33,6 +34,15 @@
{{#if isGM}}
<span class="calendar-minute-texte">{{minute}} minutes</span>
{{/if}}
{{#if isGM}}
<span class="toggle-auto-darkness">
{{#if autoDarkness}}
<i class="fa-solid fa-lightbulb"></i>
{{else}}
<i class="fa-regular fa-lightbulb"></i>
{{/if}}
</span>
{{/if}}
</div>
<div class="horloge-analogique {{#unless horlogeAnalogique}}horloge-analogique-hidden{{/unless}}">
{{> 'systems/foundryvtt-reve-de-dragon/templates/time/horloge.hbs' }}