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: #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
24 changed files with 455 additions and 235 deletions

View File

@@ -1,4 +1,28 @@
# 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
- 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)

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');
@@ -288,13 +297,13 @@ export class RdDActor extends RdDBaseActor {
}
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();
}
/* -------------------------------------------- */
@@ -2138,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',
@@ -2159,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);
}
/* -------------------------------------------- */
@@ -2251,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;
}
}
@@ -2336,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)
@@ -2359,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)
@@ -2723,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);
@@ -2747,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',
@@ -2759,6 +2762,7 @@ export class RdDActor extends RdDBaseActor {
}
);
dialog.render(true);
this.tmrApp?.setTMRPendingAction(dialog);
}
/* -------------------------------------------- */
@@ -2777,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();
}
/* -------------------------------------------- */
@@ -3012,8 +3016,8 @@ export class RdDActor extends RdDBaseActor {
/* -------------------------------------------- */
refreshTMRView() {
if (this.currentTMR) {
this.currentTMR.externalRefresh();
if (this.tmrApp) {
this.tmrApp.externalRefresh();
}
}
@@ -3021,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)) {
@@ -3066,29 +3071,40 @@ 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);
}
/* -------------------------------------------- */
getArmeCompetence(arme, competenceName) {
let comp = arme.system[competenceName]
if (!comp || comp.name == "") {
comp = arme.system[competenceName]
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;
}
}
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()) {
RdDConfirm.confirmer({
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
</p>`,
title: 'Ne pas utiliser les automatisation de combat',

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));

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) }

View File

@@ -75,7 +75,7 @@ export class RdDCombatManager extends Combat {
}
}
}
/************************************************************************************/
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
@@ -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) {
@@ -885,8 +883,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 = {

View File

@@ -181,7 +181,7 @@ export class RdDEmpoignade {
let rollData = {
mode, empoignade, attacker, defender,
isEmpoignade: true,
competence: attacker.getCompetence("Corps à corps"),
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")
competence: attacker.getCompetenceCorpsACorps()
}
const msg = await ChatMessage.create({
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";
export class RdDHotbar {
static async createItemMacro(item, slot, armeCompetence = undefined) {
let command = `game.system.rdd.RdDHotbar.rollMacro("${item.name}", "${item.type}"` + ((armeCompetence) ? `, "${armeCompetence}");` : `);`);
let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command));
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
@@ -16,29 +20,55 @@ 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 ?? "") {
switch (item?.type ?? '') {
case TYPES.arme:
{
// Les armes peuvent avoir plusieurs usages
if (item.system.competence != "") {
await this.createItemMacro(item, slot, "competence")
slot++
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")
slot++
if (item.system.lancer != '') {
await this.createItemMacro(item, slot++, 'lancer')
}
if (item.system.tir != "") {
await this.createItemMacro(item, slot, "lancer")
slot++
if (item.system.tir != '') {
await this.createItemMacro(item, slot++, 'lancer')
}
}
return
case TYPES.competence:
case TYPES.competencecreature:
const categorie = RdDItemCompetenceCreature.getCategorieAttaque(item) ?? 'competence';
await this.createItemMacro(item, slot, categorie)
return
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
}
}
@@ -51,14 +81,13 @@ 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") {
if (documentData.type == 'Item') {
const item = fromUuidSync(documentData.uuid) ?? this.actor.items.get(documentData.uuid)
console.log("DROP", documentData, item)
switch (item?.type ?? "")
{
console.log('DROP', documentData, item)
switch (item?.type) {
case TYPES.arme:
case TYPES.competence:
case TYPES.competencecreature:
@@ -72,7 +101,7 @@ export class RdDHotbar {
}
/** Roll macro */
static rollMacro(itemName, itemType, competenceName) {
static rollMacro(itemName, itemType, categorieArme = 'competence') {
const speaker = ChatMessage.getSpeaker();
let actor;
if (speaker.token) actor = game.actors.tokens[speaker.token];
@@ -88,10 +117,22 @@ export class RdDHotbar {
// Trigger the item roll
switch (item.type) {
case TYPES.arme:
return actor.rollArme(item, competenceName);
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 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 { 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,30 @@ 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
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 +200,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 +227,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 +240,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 +265,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 +304,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 +330,7 @@ export class RdDTMRDialog extends Dialog {
switch (action) {
case 'derober':
await this.derober();
this.restoreTMRAfterAction();
return;
case 'refouler':
await this.refouler();
@@ -305,6 +343,7 @@ export class RdDTMRDialog extends Dialog {
break;
}
await this.postRencontre(tmr);
this.restoreTMRAfterAction();
}
async derober() {
@@ -360,7 +399,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 +569,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 +583,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 +614,6 @@ export class RdDTMRDialog extends Dialog {
presentCite: presentCite
};
await this._tentativeMaitrise(rencontreData);
this.maximize();
this.postRencontre(tmr);
}
@@ -587,7 +627,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 +820,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 +905,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 +939,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 +950,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 +990,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 +1019,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 +1040,9 @@ export class RdDTMRDialog extends Dialog {
/* -------------------------------------------- */
async _deplacerDemiReve(targetCoord, deplacementType) {
if (this.subdialog) {
return this.forceTMRContinueAction()
}
if (this.currentRencontre != 'normal') {
this.nettoyerRencontre();
}
@@ -1043,25 +1092,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.");
this.actor.tmrApp.forceTMRContinueAction();
}
}
else {
return await super.close();
}
ui.notifications.info("Vous devez résoudre la rencontre.");
}
}

View File

@@ -364,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');
}

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

View File

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