Compare commits

..

14 Commits

Author SHA1 Message Date
85f8a716d4 Merge pull request '## v11.0.26 - le crépuscule de Khrachtchoum' (#673) from VincentVk/foundryvtt-reve-de-dragon:v11 into v11
Reviewed-on: public/foundryvtt-reve-de-dragon#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: public/foundryvtt-reve-de-dragon#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: public/foundryvtt-reve-de-dragon#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
24 changed files with 455 additions and 235 deletions

View File

@@ -1,4 +1,28 @@
# v11.0 # v11.0
## 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 ## v11.0.23 - la lumière de Khrachtchoum
- ajustement automatique de la luminosité selon l'heure pour les scènes: - 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 une vision des tokens (sinon: ce n'est pas une scène de carte pour tokens)

View File

@@ -81,9 +81,12 @@ export class RdDActorSheet extends RdDBaseActorSheet {
}); });
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps) // 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.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.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac); formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
formData.empoignades = this.actor.getEmpoignades(); formData.empoignades = this.actor.getEmpoignades();

View File

@@ -232,12 +232,21 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
getCompetence(idOrName, options = {}) { getCompetence(idOrName, options = {}) {
if (idOrName instanceof Item) {
return idOrName.isCompetence() ? idOrName : undefined
}
return RdDItemCompetence.findCompetence(this.items, idOrName, options) return RdDItemCompetence.findCompetence(this.items, idOrName, options)
} }
getCompetences(name) { getCompetences(name) {
return RdDItemCompetence.findCompetences(this.items, name) return RdDItemCompetence.findCompetences(this.items, name)
} }
getCompetenceCorpsACorps(options = {}) {
return this.getCompetence("Corps à corps", options)
}
getCompetencesEsquive() {
return this.getCompetences("esquive")
}
/* -------------------------------------------- */ /* -------------------------------------------- */
getTache(id) { getTache(id) {
return this.findItemLike(id, 'tache'); return this.findItemLike(id, 'tache');
@@ -288,13 +297,13 @@ export class RdDActor extends RdDBaseActor {
} }
getPossession(possessionId) { 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() { getPossessions() {
return this.items.filter(it => it.type == TYPES.possession); return this.itemTypes[TYPES.possession];
} }
getEmpoignades() { getEmpoignades() {
return this.items.filter(it => it.type == TYPES.empoignade); return this.itemTypes[TYPES.empoignade];
} }
getDemiReve() { getDemiReve() {
return this.system.reve.tmrpos.coord; return this.system.reve.tmrpos.coord;
@@ -369,7 +378,7 @@ export class RdDActor extends RdDBaseActor {
async _openRollDialog({ name, label, template, rollData, callbackAction }) { async _openRollDialog({ name, label, template, rollData, callbackAction }) {
const dialog = await RdDRoll.create(this, rollData, const dialog = await RdDRoll.create(this, rollData,
{ html: template }, { html: template, close: html => { this.tmrApp?.restoreTMRAfterAction() } },
{ {
name: name, name: name,
label: label, label: label,
@@ -380,6 +389,7 @@ export class RdDActor extends RdDBaseActor {
] ]
}); });
dialog.render(true); dialog.render(true);
return dialog
} }
@@ -545,7 +555,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _recuperationBlessures(message, isMaladeEmpoisonne) { async _recuperationBlessures(message, isMaladeEmpoisonne) {
const timestamp = game.system.rdd.calendrier.getTimestamp() 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({ await Promise.all(blessures.map(b => b.recuperationBlessure({
actor: this, actor: this,
@@ -559,7 +569,7 @@ export class RdDActor extends RdDBaseActor {
} }
async supprimerBlessures(filterToDelete) { async supprimerBlessures(filterToDelete) {
const toDelete = this.filterItems(filterToDelete, 'blessure') const toDelete = this.filterItems(filterToDelete, TYPES.blessure)
.map(it => it.id); .map(it => it.id);
await this.deleteEmbeddedDocuments('Item', toDelete); await this.deleteEmbeddedDocuments('Item', toDelete);
} }
@@ -567,7 +577,7 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _recupererVie(message, isMaladeEmpoisonne) { async _recupererVie(message, isMaladeEmpoisonne) {
const tData = this.system 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) { if (blessures.length > 0) {
return 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' } system: { sortid: sort._id, draconic: (draconic?.name ?? sort.system.draconic), ptreve: ptreve, coord: coord, heurecible: 'Vaisseau' }
}], }],
{ renderSheet: false }); { renderSheet: false });
this.currentTMR.updateTokens(); this.tmrApp.updateTokens();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -2138,12 +2148,11 @@ export class RdDActor extends RdDBaseActor {
ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`); ui.notifications.info(`Aucun sort disponible en ${TMRUtility.getTMR(coord).label} !`);
return; return;
} }
if (this.currentTMR) this.currentTMR.minimize(); // Hide
const draconicList = this.computeDraconicAndSortIndex(sorts); const draconicList = this.computeDraconicAndSortIndex(sorts);
const reve = duplicate(this.system.carac.reve); const reve = duplicate(this.system.carac.reve);
await this._openRollDialog({ const dialog = await this._openRollDialog({
name: 'lancer-un-sort', name: 'lancer-un-sort',
label: 'Lancer un sort', label: 'Lancer un sort',
template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html', template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html',
@@ -2159,8 +2168,12 @@ export class RdDActor extends RdDBaseActor {
diffLibre: RdDItemSort.getDifficulte(sorts[0], -7), // Per default at startup diffLibre: RdDItemSort.getDifficulte(sorts[0], -7), // Per default at startup
coutreve: Array(30).fill().map((item, index) => 1 + index), 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);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -2251,17 +2264,11 @@ export class RdDActor extends RdDBaseActor {
reveActuel = Math.max(reveActuel - rollData.depenseReve, 0); reveActuel = Math.max(reveActuel - rollData.depenseReve, 0);
await this.update({ "system.reve.reve.value": reveActuel }); 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 // Final chat message
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-sort.html'); await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-sort.html');
if (reveActuel == 0) { // 0 points de reve if (reveActuel == 0) { // 0 points de reve
ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" }); ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" });
closeTMR = true;
} }
} }
@@ -2336,16 +2343,14 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollCompetence(idOrName, options = { tryTarget: true }) { async rollCompetence(idOrName, options = { tryTarget: true }) {
RdDEmpoignade.checkEmpoignadeEnCours(this) RdDEmpoignade.checkEmpoignadeEnCours(this)
let rollData = { const competence = this.getCompetence(idOrName);
carac: this.system.carac, let rollData = { carac: this.system.carac, competence: competence }
competence: this.getCompetence(idOrName) if (competence.type == TYPES.competencecreature) {
} const arme = RdDItemCompetenceCreature.armeCreature(competence)
if (rollData.competence.type == TYPES.competencecreature) {
const arme = RdDItemCompetenceCreature.armeCreature(rollData.competence)
if (arme && options.tryTarget && Targets.hasTargets()) { if (arme && options.tryTarget && Targets.hasTargets()) {
Targets.selectOneToken(target => { Targets.selectOneToken(target => {
if (arme.action == "possession") { if (arme.action == "possession") {
RdDPossession.onAttaquePossession(target, this, rollData.competence) RdDPossession.onAttaquePossession(target, this, competence)
} }
else { else {
RdDCombat.rddCombatTarget(target, this).attaque(competence, arme) RdDCombat.rddCombatTarget(target, this).attaque(competence, arme)
@@ -2359,7 +2364,7 @@ export class RdDActor extends RdDBaseActor {
await this._openRollDialog({ await this._openRollDialog({
name: 'jet-competence', 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', template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html',
rollData: rollData, rollData: rollData,
callbackAction: r => this.$onRollCompetence(r, options) callbackAction: r => this.$onRollCompetence(r, options)
@@ -2723,8 +2728,6 @@ export class RdDActor extends RdDBaseActor {
ui.notifications.info(`Aucun signe draconiques en ${coord} !`); ui.notifications.info(`Aucun signe draconiques en ${coord} !`);
return; return;
} }
if (this.currentTMR) this.currentTMR.minimize(); // Hide
let draconicList = this.getDraconicList() let draconicList = this.getDraconicList()
.map(draconic => { .map(draconic => {
let draconicLecture = duplicate(draconic); let draconicLecture = duplicate(draconic);
@@ -2747,7 +2750,7 @@ export class RdDActor extends RdDBaseActor {
const dialog = await RdDRoll.create(this, rollData, const dialog = await RdDRoll.create(this, rollData,
{ {
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-signedraconique.html', 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', name: 'lire-signe-draconique',
@@ -2759,6 +2762,7 @@ export class RdDActor extends RdDBaseActor {
} }
); );
dialog.render(true); dialog.render(true);
this.tmrApp?.setTMRPendingAction(dialog);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -2777,7 +2781,7 @@ export class RdDActor extends RdDBaseActor {
} }
await this.deleteEmbeddedDocuments("Item", [rollData.signe._id]); await this.deleteEmbeddedDocuments("Item", [rollData.signe._id]);
await RdDResolutionTable.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.html'); await RdDResolutionTable.displayRollData(rollData, this.name, 'chat-resultat-lecture-signedraconique.html');
this.currentTMR.close(); this.tmrApp.close();
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -3012,8 +3016,8 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
refreshTMRView() { refreshTMRView() {
if (this.currentTMR) { if (this.tmrApp) {
this.currentTMR.externalRefresh(); this.tmrApp.externalRefresh();
} }
} }
@@ -3021,6 +3025,7 @@ export class RdDActor extends RdDBaseActor {
async displayTMR(mode = "normal") { async displayTMR(mode = "normal") {
if (this.tmrApp) { if (this.tmrApp) {
ui.notifications.warn("Vous êtes déja dans les TMR...."); ui.notifications.warn("Vous êtes déja dans les TMR....");
this.tmrApp.forceTMRDisplay();
return return
} }
if (mode != 'visu' && this.getEffect(STATUSES.StatusDemiReve)) { if (mode != 'visu' && this.getEffect(STATUSES.StatusDemiReve)) {
@@ -3066,29 +3071,40 @@ export class RdDActor extends RdDBaseActor {
hasPlayerOwner: this.hasPlayerOwner hasPlayerOwner: this.hasPlayerOwner
} }
this.currentTMR = await RdDTMRDialog.create(this, tmrFormData); this.tmrApp = await RdDTMRDialog.create(this, tmrFormData);
this.currentTMR.render(true); this.tmrApp.render(true);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getArmeCompetence(arme, competenceName) { getCompetenceArme(arme, competenceName) {
let comp = arme.system[competenceName] switch (arme.type) {
if (!comp || comp.name == "") { case TYPES.competencecreature:
comp = arme.system[competenceName] 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;
} }
if ( !comp || comp.name == "") {
comp = arme.system[competenceName]
} }
return comp return undefined
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
rollArme(arme, competenceName = "competence") { /**
let compToUse = this.getArmeCompetence(arme, competenceName) *
* @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()) { if (!Targets.hasTargets()) {
RdDConfirm.confirmer({ RdDConfirm.confirmer({
settingConfirmer: "confirmer-combat-sans-cible", settingConfirmer: "confirmer-combat-sans-cible",
content: `<p>Voulez vous faire un jet de compétence ${competenceName} 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 <br>Tous les jets de combats devront être gérés à la main
</p>`, </p>`,
title: 'Ne pas utiliser les automatisation de combat', title: 'Ne pas utiliser les automatisation de combat',

View File

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

View File

@@ -79,10 +79,9 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isCompetenceArme(competence) { static isCompetenceArme(competence) {
if (competence.isCompetence()) { if (competence.isCompetence() && !competence.isCorpsACorps() && !competence.isEsquive()) {
switch (competence.system.categorie) { switch (competence.system.categorie) {
case 'melee': case 'melee':
return !Grammar.toLowerCaseNoAccent(competence.name).includes('esquive');
case 'tir': case 'tir':
case 'lancer': case 'lancer':
return true; return true;
@@ -93,10 +92,10 @@ export class RdDItemCompetence extends Item {
/* -------------------------------------------- */ /* -------------------------------------------- */
static isArmeUneMain(competence) { 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) { 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) { 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"; import { RdDCombatManager } from "./rdd-combat.js";
const categories = { const categories = {

View File

@@ -113,7 +113,7 @@ export class RdDItemSheet extends ItemSheet {
formData.competences = competences; formData.competences = competences;
} }
if (this.item.type == 'arme') { 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)) { if (['sort', 'sortreserve'].includes(this.item.type)) {
formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it)); formData.competences = competences.filter(it => RdDItemCompetence.isDraconic(it));

View File

@@ -220,6 +220,32 @@ export class RdDItem extends Item {
isService() { return this.type == TYPES.service; } isService() { return this.type == TYPES.service; }
isCompetence() { return typesObjetsCompetence.includes(this.type) } 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" } isCompetencePossession() { return TYPES.competencecreature == this.type && this.system.categorie == "possession" }
isTemporel() { return typesObjetsTemporels.includes(this.type) } isTemporel() { return typesObjetsTemporels.includes(this.type) }
isOeuvre() { return typesObjetsOeuvres.includes(this.type) } isOeuvre() { return typesObjetsOeuvres.includes(this.type) }

View File

@@ -171,8 +171,7 @@ export class RdDCombatManager extends Combat {
if (arme.system.unemain && arme.system.deuxmains && !dommages.includes("/")) { 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)"); 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) || if (arme.system.unemain && arme.system.competence) {
(arme.system.competence.toLowerCase().includes("corps à corps"))) {
actions.push(RdDCombatManager.$prepareAttaqueArme({ actions.push(RdDCombatManager.$prepareAttaqueArme({
arme: arme, arme: arme,
infoMain: "(1 main)", infoMain: "(1 main)",
@@ -187,7 +186,7 @@ export class RdDCombatManager extends Combat {
arme: arme, arme: arme,
infoMain: "(2 mains)", infoMain: "(2 mains)",
dommagesReel: Number(tableauDommages[1]), dommagesReel: Number(tableauDommages[1]),
competence: arme.system.competence.replace(" 1 main", " 2 mains"), competence: RdDItemArme.competence2Mains(arme),
carac: carac, carac: carac,
competences: competences competences: competences
})); }));
@@ -258,11 +257,10 @@ export class RdDCombatManager extends Combat {
actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']); actions = RdDCombatManager.listActionsCreature(actor.itemTypes['competencecreature']);
} else if (actor.isPersonnage()) { } else if (actor.isPersonnage()) {
// Recupération des items 'arme' // 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 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); actions = RdDCombatManager.listActionsArmes(armes, competences, actor.system.carac);
if (actor.system.attributs.hautrevant.value) { if (actor.system.attributs.hautrevant.value) {
@@ -885,8 +883,8 @@ export class RdDCombat {
} }
// # utilisation esquive // # utilisation esquive
const corpsACorps = this.defender.getCompetence("Corps à corps", { onMessage: it => console.info(it, this.defender) }); const corpsACorps = this.defender.getCompetenceCorpsACorps({ onMessage: it => console.info(it, this.defender) });
const esquives = duplicate(this.defender.getCompetences("esquive", { 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); esquives.forEach(e => e.system.nbUsage = e?._id ? this.defender.getItemUse(e._id) : 0);
const paramChatDefense = { const paramChatDefense = {

View File

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

View File

@@ -1,13 +1,17 @@
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { TYPES } from "./item.js"; import { TYPES } from "./item.js";
export class RdDHotbar { export class RdDHotbar {
static async createItemMacro(item, slot, armeCompetence = undefined) { static async createItemMacro(item, slot, armeCompetence = undefined) {
let command = `game.system.rdd.RdDHotbar.rollMacro("${item.name}", "${item.type}"` + ((armeCompetence) ? `, "${armeCompetence}");` : `);`); const itemName = item.name;
let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command)); 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) { if (!macro) {
macro = await Macro.create({ macro = await Macro.create({
name: item.name, name: macroName,
type: "script", type: "script",
img: item.img, img: item.img,
command: command command: command
@@ -16,29 +20,55 @@ export class RdDHotbar {
await game.user.assignHotbarMacro(macro, slot); 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) { static async addToHotbar(item, slot) {
switch (item?.type ?? "") { switch (item?.type ?? '') {
case TYPES.arme: case TYPES.arme:
{ {
// Les armes peuvent avoir plusieurs usages // Les armes peuvent avoir plusieurs usages
if (item.system.competence != "") { if (item.system.competence != '') {
await this.createItemMacro(item, slot, "competence") if (item.system.unemain) {
slot++ await this.createItemMacro(item, slot++, 'unemain')
} }
if (item.system.lancer != "") { if (item.system.deuxmains) {
await this.createItemMacro(item, slot, "lancer") await this.createItemMacro(item, slot++, 'deuxmains')
slot++
} }
if (item.system.tir != "") { }
await this.createItemMacro(item, slot, "lancer") if (item.system.lancer != '') {
slot++ await this.createItemMacro(item, slot++, 'lancer')
}
if (item.system.tir != '') {
await this.createItemMacro(item, slot++, 'lancer')
} }
} }
return return
case TYPES.competence:
case TYPES.competencecreature: case TYPES.competencecreature:
const categorie = RdDItemCompetenceCreature.getCategorieAttaque(item) ?? 'competence';
await this.createItemMacro(item, slot, categorie)
return
default: default:
await this.createItemMacro(item, slot) 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 return
} }
} }
@@ -51,14 +81,13 @@ export class RdDHotbar {
*/ */
static initDropbar() { 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 // Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (documentData.type == "Item") { if (documentData.type == 'Item') {
const item = fromUuidSync(documentData.uuid) ?? this.actor.items.get(documentData.uuid) const item = fromUuidSync(documentData.uuid) ?? this.actor.items.get(documentData.uuid)
console.log("DROP", documentData, item) console.log('DROP', documentData, item)
switch (item?.type ?? "") switch (item?.type) {
{
case TYPES.arme: case TYPES.arme:
case TYPES.competence: case TYPES.competence:
case TYPES.competencecreature: case TYPES.competencecreature:
@@ -72,7 +101,7 @@ export class RdDHotbar {
} }
/** Roll macro */ /** Roll macro */
static rollMacro(itemName, itemType, competenceName) { static rollMacro(itemName, itemType, categorieArme = 'competence') {
const speaker = ChatMessage.getSpeaker(); const speaker = ChatMessage.getSpeaker();
let actor; let actor;
if (speaker.token) actor = game.actors.tokens[speaker.token]; if (speaker.token) actor = game.actors.tokens[speaker.token];
@@ -88,10 +117,22 @@ export class RdDHotbar {
// Trigger the item roll // Trigger the item roll
switch (item.type) { switch (item.type) {
case TYPES.arme: case TYPES.arme:
return actor.rollArme(item, competenceName); return actor.rollArme(item, categorieArme);
case TYPES.competence: 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: case TYPES.competencecreature:
return actor.rollCompetence(itemName); return item.system.iscombat && !item.system.isparade
? actor.rollArme(item, categorieArme)
: actor.rollCompetence(item);
} }
} }

View File

@@ -2,7 +2,6 @@ import { SHOW_DICE } from "./constants.js";
import { RollDataAjustements } from "./rolldata-ajustements.js"; import { RollDataAjustements } from "./rolldata-ajustements.js";
import { RdDUtility } from "./rdd-utility.js"; import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js"; import { TMRUtility } from "./tmr-utility.js";
import { tmrConstants } from "./tmr-constants.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js"; import { RdDTMRRencontreDialog } from "./rdd-tmr-rencontre-dialog.js";
import { ChatUtility } from "./chat-utility.js"; import { ChatUtility } from "./chat-utility.js";
@@ -39,14 +38,16 @@ export class RdDTMRDialog extends Dialog {
title: "Terres Médianes de Rêve", title: "Terres Médianes de Rêve",
content: html, content: html,
buttons: { buttons: {
closeButton: { label: "Fermer", callback: html => this.close(html) } closeButton: {
label: "Fermer", callback: html => this.close()
}
}, },
default: "closeButton" default: "closeButton"
} }
const dialogOptions = { const dialogOptions = {
classes: ["tmrdialog"], classes: ["tmrdialog"],
width: 920, height: 980, width: 920, maxheight: 1024, height: 'fit-content',
'z-index': 40 'z-index': 40
} }
super(dialogConf, dialogOptions); super(dialogConf, dialogOptions);
@@ -62,8 +63,8 @@ export class RdDTMRDialog extends Dialog {
this.allTokens = []; this.allTokens = [];
this.rencontreState = 'aucune'; this.rencontreState = 'aucune';
this.pixiApp = new PIXI.Application({ width: 720, height: 860 }); this.pixiApp = new PIXI.Application({ width: 720, height: 860 });
this.pixiTMR = new PixiTMR(this, this.pixiApp); this.pixiTMR = new PixiTMR(this, this.pixiApp);
this.subdialog = undefined
this.callbacksOnAnimate = []; this.callbacksOnAnimate = [];
if (!this.viewOnly) { if (!this.viewOnly) {
@@ -74,6 +75,30 @@ export class RdDTMRDialog extends Dialog {
this.pixiTMR.load((loader, resources) => this.createPixiSprites()); this.pixiTMR.load((loader, resources) => this.createPixiSprites());
} }
async forceTMRDisplay() {
this.bringToTop();
if (this.subdialog) {
this.subdialog.bringToTop();
}
}
async restoreTMRAfterAction() {
this.subdialog = undefined
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() { isDemiReveCache() {
return !game.user.isGM && this.actor.isTMRCache(); return !game.user.isGM && this.actor.isTMRCache();
} }
@@ -175,6 +200,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async moveFromKey(move) { async moveFromKey(move) {
if (this.subdialog) {
return this.forceTMRContinueAction();
}
let oddq = TMRUtility.coordTMRToOddq(this._getActorCoord()); let oddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
if (move == 'top') oddq.row -= 1; if (move == 'top') oddq.row -= 1;
@@ -199,7 +227,9 @@ export class RdDTMRDialog extends Dialog {
super.activateListeners(html); super.activateListeners(html);
this.html = 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) { if (this.viewOnly) {
this.html.find('.lancer-sort').remove(); this.html.find('.lancer-sort').remove();
@@ -210,6 +240,10 @@ export class RdDTMRDialog extends Dialog {
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue")); HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue"));
HtmlUtility.showControlWhen(this.html.find(".lire-signe-draconique"), this.actor.isResonanceSigneDraconique(this._getActorCoord())); 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 // Roll Sort
this.html.find('.lancer-sort').click((event) => { this.html.find('.lancer-sort').click((event) => {
this.actor.rollUnSort(this._getActorCoord()); this.actor.rollUnSort(this._getActorCoord());
@@ -231,7 +265,6 @@ export class RdDTMRDialog extends Dialog {
this.cumulFatigue += this.fatigueParCase; this.cumulFatigue += this.fatigueParCase;
} }
await this.actor.reveActuelIncDec(reveCout); await this.actor.reveActuelIncDec(reveCout);
// Le reste... // Le reste...
this.updateValuesDisplay(); this.updateValuesDisplay();
let tmr = TMRUtility.getTMR(this._getActorCoord()); let tmr = TMRUtility.getTMR(this._getActorCoord());
@@ -271,6 +304,10 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async close() { async close() {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
this.descenteTMR = true; this.descenteTMR = true;
if (this.actor.tmrApp) { if (this.actor.tmrApp) {
this.actor.tmrApp = undefined; // Cleanup reference this.actor.tmrApp = undefined; // Cleanup reference
@@ -293,6 +330,7 @@ export class RdDTMRDialog extends Dialog {
switch (action) { switch (action) {
case 'derober': case 'derober':
await this.derober(); await this.derober();
this.restoreTMRAfterAction();
return; return;
case 'refouler': case 'refouler':
await this.refouler(); await this.refouler();
@@ -305,6 +343,7 @@ export class RdDTMRDialog extends Dialog {
break; break;
} }
await this.postRencontre(tmr); await this.postRencontre(tmr);
this.restoreTMRAfterAction();
} }
async derober() { async derober() {
@@ -360,7 +399,6 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
checkQuitterTMR() { checkQuitterTMR() {
if (this.actor.isDead()) { if (this.actor.isDead()) {
this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !"); this._tellToGM("Vous êtes mort : vous quittez les Terres médianes !");
this.close(); this.close();
@@ -531,8 +569,9 @@ export class RdDTMRDialog extends Dialog {
await this.maitriserRencontre(); await this.maitriserRencontre();
} }
else { else {
let dialog = new RdDTMRRencontreDialog(this, this.currentRencontre, tmr); const dialog = new RdDTMRRencontreDialog(this.actor, this.currentRencontre, tmr);
dialog.render(true); dialog.render(true);
this.setTMRPendingAction(dialog);
} }
} }
else { else {
@@ -544,9 +583,12 @@ export class RdDTMRDialog extends Dialog {
_presentCite(tmr) { _presentCite(tmr) {
const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord)); const presentCite = this.casesSpeciales.find(c => EffetsDraconiques.presentCites.isCase(c, tmr.coord));
if (presentCite) { if (presentCite) {
this.minimize();
const caseData = presentCite; 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; return presentCite;
} }
@@ -572,8 +614,6 @@ export class RdDTMRDialog extends Dialog {
presentCite: presentCite presentCite: presentCite
}; };
await this._tentativeMaitrise(rencontreData); await this._tentativeMaitrise(rencontreData);
this.maximize();
this.postRencontre(tmr); this.postRencontre(tmr);
} }
@@ -587,7 +627,10 @@ export class RdDTMRDialog extends Dialog {
? TMRUtility.getTMRType(tmr.coord) + " ??" ? TMRUtility.getTMRType(tmr.coord) + " ??"
: tmr.label + " (" + tmr.coord + ")"); : tmr.label + " (" + tmr.coord + ")");
const fakeDialogRencontre = { bringToTop: () => { } };
this.setTMRPendingAction(fakeDialogRencontre)
let myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE }); let myRoll = await RdDDice.rollTotal("1dt", { showDice: SHOW_DICE });
this.restoreTMRAfterAction()
if (myRoll == 7) { if (myRoll == 7) {
this._tellToUser(myRoll + ": Rencontre en " + locTMR); this._tellToUser(myRoll + ": Rencontre en " + locTMR);
return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre()) return await game.system.rdd.rencontresTMR.getRencontreAleatoire(tmr, this.actor.isMauvaiseRencontre())
@@ -777,22 +820,22 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _maitriserTMR(rollData, callbackMaitrise) { async _maitriserTMR(rollData, callbackMaitrise) {
this.minimize(); // Hide
rollData.isTMRCache = rollData.actor.isTMRCache(); rollData.isTMRCache = rollData.actor.isTMRCache();
const dialog = await RdDRoll.create(this.actor, rollData, const dialog = await RdDRoll.create(this.actor, rollData,
{ {
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-maitrise-tmr.html', 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, name: rollData.maitrise.verbe, label: rollData.maitrise.action,
callbacks: [ callbacks: [
this.actor.createCallbackExperience(), this.actor.createCallbackExperience(),
{ action: r => { this.restoreTMRAfterAction() } },
{ action: callbackMaitrise } { action: callbackMaitrise }
] ]
} }
); );
dialog.render(true); dialog.render(true);
this.setTMRPendingAction(dialog);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -862,7 +905,8 @@ export class RdDTMRDialog extends Dialog {
nettoyerRencontre() { nettoyerRencontre() {
if (!this.currentRencontre) return; // Sanity check if (!this.currentRencontre) return; // Sanity check
if (this.currentRencontre.graphics) { 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); this.pixiApp.stage.removeChild(drawRect);
} }
} }
@@ -895,8 +939,8 @@ export class RdDTMRDialog extends Dialog {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
isConnaissanceFleuve(currentTMR, nextTMR) { isConnaissanceFleuve(tmrApp, nextTMR) {
return TMRUtility.getTMR(currentTMR).type == 'fleuve' && return TMRUtility.getTMR(tmrApp).type == 'fleuve' &&
TMRUtility.getTMR(nextTMR).type == 'fleuve' && TMRUtility.getTMR(nextTMR).type == 'fleuve' &&
EffetsDraconiques.isConnaissanceFleuve(this.actor); EffetsDraconiques.isConnaissanceFleuve(this.actor);
} }
@@ -906,15 +950,15 @@ export class RdDTMRDialog extends Dialog {
if (this.viewOnly) { if (this.viewOnly) {
return; return;
} }
let clickOddq = RdDTMRDialog._computeEventOddq(event.nativeEvent); if (this.subdialog) {
await this._onClickTMRPos(clickOddq); // Vérifier l'état des compteurs reve/fatigue/vie return this.forceTMRContinueAction()
} }
let clickOddq = TMRUtility.computeEventOddq(event);
/* -------------------------------------------- */
async _onClickTMRPos(clickOddq) {
let currentOddq = TMRUtility.coordTMRToOddq(this._getActorCoord()); let currentOddq = TMRUtility.coordTMRToOddq(this._getActorCoord());
let targetCoord = TMRUtility.oddqToCoordTMR(clickOddq); let targetCoord = TMRUtility.oddqToCoordTMR(clickOddq);
let currentCoord = TMRUtility.oddqToCoordTMR(currentOddq); let currentCoord = TMRUtility.oddqToCoordTMR(currentOddq);
// Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter) // Validation de la case de destination (gestion du cas des rencontres qui peuvent téléporter)
let deplacementType = this._calculDeplacement(targetCoord, currentCoord, currentOddq, clickOddq); let deplacementType = this._calculDeplacement(targetCoord, currentCoord, currentOddq, clickOddq);
@@ -946,7 +990,7 @@ export class RdDTMRDialog extends Dialog {
await this._messagerDemiReve(targetCoord); await this._messagerDemiReve(targetCoord);
break; break;
default: 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); console.log("STATUS :", this.rencontreState, this.currentRencontre);
} }
@@ -975,9 +1019,11 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _messagerDemiReve(targetCoord) { 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 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); this.notifierResonanceSigneDraconique(targetCoord);
await this.actor.rollUnSort(targetCoord); await this.actor.rollUnSort(targetCoord);
@@ -994,6 +1040,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _deplacerDemiReve(targetCoord, deplacementType) { async _deplacerDemiReve(targetCoord, deplacementType) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
if (this.currentRencontre != 'normal') { if (this.currentRencontre != 'normal') {
this.nettoyerRencontre(); this.nettoyerRencontre();
} }
@@ -1043,25 +1092,16 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
async positionnerDemiReve(coord) { async positionnerDemiReve(coord) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
await this.actor.updateCoordTMR(coord); await this.actor.updateCoordTMR(coord);
this.forceDemiRevePositionView(); this.forceDemiRevePositionView();
let tmr = TMRUtility.getTMR(coord); let tmr = TMRUtility.getTMR(coord);
await this.postRencontre(tmr); await this.postRencontre(tmr);
return 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 */ /** Retourne les coordonnées x, h, w, h du rectangle d'une case donnée */
_getCaseRectangleCoord(coord) { _getCaseRectangleCoord(coord) {

View File

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

View File

@@ -364,7 +364,7 @@ export class RdDCalendrier extends Application {
if (request.rolled.isSuccess) { if (request.rolled.isSuccess) {
if (request.rolled.isPart) { if (request.rolled.isPart) {
// Gestion expérience (si existante) // Gestion expérience (si existante)
request.competence = actor.getCompetence("astrologie") request.competence = actor.getCompetence('Astrologie')
request.selectedCarac = actor.system.carac["vue"]; request.selectedCarac = actor.system.carac["vue"];
actor.appliquerAjoutExperience(request, 'hide'); actor.appliquerAjoutExperience(request, 'hide');
} }

View File

@@ -1,6 +1,7 @@
import { Misc } from "./misc.js"; import { Misc } from "./misc.js";
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js"; import { RdDDice } from "./rdd-dice.js";
import { tmrConstants } from "./tmr-constants.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
const TMRMapping = { const TMRMapping = {
@@ -163,7 +164,7 @@ const TMRMapping = {
C12: { type: "lac", label: "Lac de Fricassa" }, C12: { type: "lac", label: "Lac de Fricassa" },
D12: { type: "collines", label: "Collines dHuaï" }, D12: { type: "collines", label: "Collines dHuaï" },
E12: { type: "monts", label: "Monts Ajourés" }, 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" }, G12: { type: "plaines", label: "Plaines de Lufmil" },
H12: { type: "collines", label: "Collines de Tooth" }, H12: { type: "collines", label: "Collines de Tooth" },
I12: { type: "gouffre", label: "Gouffre Abimeux" }, I12: { type: "gouffre", label: "Gouffre Abimeux" },
@@ -375,6 +376,36 @@ export class TMRUtility {
return caseList; 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 // https://www.redblobgames.com/grids/hexagons/#distances
// TMR Letter-row correspond to "odd-q" grid (letter => col, numeric => row ) // TMR Letter-row correspond to "odd-q" grid (letter => col, numeric => row )

View File

@@ -4,7 +4,6 @@ import { PixiTMR } from "./pixi-tmr.js";
export class CarteTmr extends Draconique { export class CarteTmr extends Draconique {
constructor() { constructor() {
console.log("Sprite create 1!!!!")
super(); super();
} }
@@ -14,9 +13,25 @@ export class CarteTmr extends Draconique {
async onActorCreateOwned(actor, item) { } async onActorCreateOwned(actor, item) { }
code() { return 'tmr' } 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) { 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

@@ -82,7 +82,7 @@ export class Draconique {
img() { return undefined } 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. * @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) { token(pixiTMR, linkData, coordTMR, type = undefined) {
@@ -91,12 +91,32 @@ export class Draconique {
coordTMR: coordTMR coordTMR: coordTMR
}; };
token[type ?? this.code()] = linkData; token[type ?? this.code()] = linkData;
console.log("SPRITE: ", token.sprite) this.linkData = linkData;
//PixiTMR.getImgFromCode() if (this.tooltip(linkData)) {
pixiTMR.addTooltip(token.sprite, this.tooltip(linkData)); pixiTMR.addTooltip(token.sprite, (e, s) => this.computeTooltip(e, s));
}
return token; 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 * 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. * @param {*} pixiTMR instance de PixiTMR qui gère les tooltips, les méthodes de création de sprite standard, les clicks.

View File

@@ -1,6 +1,8 @@
import { RdDTMRDialog } from "../rdd-tmr-dialog.js";
import { tmrConstants, tmrTokenZIndex } from "../tmr-constants.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', fontFamily: 'CaslonAntique',
fontSize: 18, fontSize: 18,
fill: '#FFFFFF', fill: '#FFFFFF',
@@ -40,33 +42,10 @@ export class PixiTMR {
PixiTMR.textures[name] = img; PixiTMR.textures[name] = img;
} }
animate(animation = pixiApp=>{}) animate(animation = pixiApp => { }) {
{
this.callbacksOnAnimate.push(() => animation(this.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 = {}) { sprite(code, options = {}) {
let img = PixiTMR.getImgFromCode(code) let img = PixiTMR.getImgFromCode(code)
const texture = PIXI.utils.TextureCache[img]; const texture = PIXI.utils.TextureCache[img];
@@ -98,27 +77,37 @@ export class PixiTMR {
return sprite; return sprite;
} }
addTooltip(sprite, text) { addTooltip(sprite, computeTooltip) {
if (text) { sprite.tooltip = new PIXI.Text('', tooltipStyle);
sprite.tooltip = new PIXI.Text(text, tooltipStyle);
sprite.tooltip.zIndex = tmrTokenZIndex.tooltip; sprite.tooltip.zIndex = tmrTokenZIndex.tooltip;
sprite.isOver = false; sprite.isOver = false;
// Deprecated : sprite.interactive = true;
sprite.eventMode = 'dynamic'; // PIXI 7 To be checked sprite.eventMode = 'dynamic'; // PIXI 7 To be checked
sprite.on('pointerdown', event => this.onClickBackground(event)) sprite
.on('pointerover', () => this.onShowTooltip(sprite)) .on('pointermove', event => this.onPointerMove(event, sprite, computeTooltip))
.on('pointerout', () => this.onHideTooltip(sprite)); .on('pointerdown', event => this.onClickBackground(event))
.on('pointerover', event => this.onShowTooltip(event, sprite))
.on('pointerout', event => this.onHideTooltip(event, sprite));
} }
}
onClickBackground(event) { onClickBackground(event) {
if (!this.viewOnly) {
this.tmrObject.onClickTMR(event) this.tmrObject.onClickTMR(event)
} }
}
onShowTooltip(sprite) { 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.tooltip) {
if (!sprite.isOver) { if (!sprite.isOver) {
sprite.tooltip.x = sprite.x; sprite.tooltip.x = sprite.x;
sprite.tooltip.y = sprite.y; sprite.tooltip.y = sprite.y;
@@ -128,7 +117,7 @@ export class PixiTMR {
} }
} }
onHideTooltip(sprite) { onHideTooltip(event, sprite) {
if (sprite.tooltip) { if (sprite.tooltip) {
if (sprite.isOver) { if (sprite.isOver) {
this.pixiApp.stage.removeChild(sprite.tooltip); this.pixiApp.stage.removeChild(sprite.tooltip);

View File

@@ -49,12 +49,13 @@ export class PresentCites extends Draconique {
const presents = await game.system.rdd.rencontresTMR.getPresentsCite() const presents = await game.system.rdd.rencontresTMR.getPresentsCite()
const buttons = {}; const buttons = {};
presents.forEach(r => buttons['present'+r.id] = { icon: '<i class="fas fa-check"></i>', label: r.name, callback: async () => onChoixPresent(r) }); 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", title: "Présent des cités",
content: `La ${this.tmrLabel(casetmr)} vous offre un présent, faites votre choix`, content: `La ${this.tmrLabel(casetmr)} vous offre un présent, faites votre choix`,
buttons: buttons buttons: buttons
}); });
d.render(true); dialog.render(true);
return dialog
} }
async ouvrirLePresent(actor, casetmr) { 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; width: fit-content;
} }
.tmr-dialog table {
border: none;
}
.system-foundryvtt-reve-de-dragon .sheet-header div.tmr-buttons { .system-foundryvtt-reve-de-dragon .sheet-header div.tmr-buttons {
padding: 0; padding: 0;
margin: 0; margin: 0;

View File

@@ -1,8 +1,8 @@
{ {
"id": "foundryvtt-reve-de-dragon", "id": "foundryvtt-reve-de-dragon",
"title": "Rêve de Dragon", "title": "Rêve de Dragon",
"version": "11.0.23", "version": "11.0.26",
"download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.0.23.zip", "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-11.0.26.zip",
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v11/system.json", "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", "changelog": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/branch/v11/changelog.md",
"compatibility": { "compatibility": {

View File

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