diff --git a/module/actor-sheet.js b/module/actor-sheet.js index a9fcc32e..29170dc2 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -62,9 +62,6 @@ export class RdDActorSheet extends ActorSheet { if (this.actor.data.type == 'creature') return formData; // Shortcut - // toujours avoir une liste d'armes (pour mettre esquive et corps à corps) - formData.itemsByType.arme = formData.itemsByType.arme ?? []; - formData.competenceByCategory = Misc.classify(formData.data.competences, item => item.data.categorie); formData.calc = { @@ -93,38 +90,15 @@ export class RdDActorSheet extends ActorSheet { }); - // Force empty arme, at least for Esquive - if (formData.itemsByType.arme == undefined) formData.itemsByType.arme = []; - for (const arme of formData.itemsByType.arme) { - arme.data.niveau = 0; // Per default, TODO to be fixed - for (const melee of formData.competenceByCategory.melee) { - if (melee.name == arme.data.competence) - arme.data.niveau = melee.data.niveau - } - for (const tir of formData.competenceByCategory.tir) { - if (tir.name == arme.data.competence) - arme.data.niveau = tir.data.niveau - } - for (const lancer of formData.competenceByCategory.lancer) { - if (lancer.name == arme.data.competence) - arme.data.niveau = lancer.data.niveau - } - } - - // To avoid armour and so on... - formData.data.combat = duplicate(RdDUtility.checkNull(formData.itemsByType['arme'])); + // toujours avoir une liste d'armes (pour mettre esquive et corps à corps) + formData.data.combat = duplicate(formData.itemsByType.arme ?? []); + RdDItemArme.computeNiveauArmes(formData.data.combat, formData.data.competences); + RdDItemArme.ajoutCorpsACorps(formData.data.combat, formData.data.competences, formData.data.carac ); + formData.esquive = RdDItemCompetence.getEsquive(formData.data.competences); formData.data.combat = RdDCombatManager.finalizeArmeList(formData.data.combat, formData.itemsByType.competence, formData.data.carac); - formData.esquive = { name: "Esquive", niveau: formData.competenceByCategory?.melee.find(it => it.name == 'Esquive')?.data.niveau ?? -6 }; - let corpsACorps = formData.competenceByCategory?.melee.find(it => it.name == 'Corps à corps'); - if (corpsACorps) { - let cc_init = RdDCombatManager.calculInitiative(corpsACorps.data.niveau, formData.data.carac['melee'].value); - formData.data.combat.push(RdDItemArme.mainsNues({ niveau: corpsACorps.data.niveau, initiative: cc_init })); - } - this.armesList = duplicate(formData.data.combat); - formData.data.carac.taille.isTaille = true; // To avoid button link; - formData.data.blessures.resume = this.actor.computeResumeBlessure(formData.data.blessures); + this.armesList = formData.data.combat; // Mise à jour de l'encombrement total et du prix de l'équipement @@ -140,14 +114,18 @@ export class RdDActorSheet extends ActorSheet { html: "" + RdDUtility.makeHTMLfatigueMatrix(formData.data.sante.fatigue.value, formData.data.sante.endurance.max).html() + "
" } - RdDUtility.filterItemsPerTypeForSheet(formData); - formData.data.sortReserve = formData.data.reve.reserve.list; - formData.data.rencontres = duplicate(formData.data.reve.rencontre.list); - formData.data.caseSpeciales = formData.itemsByType['casetmr']; + formData.hautreve = { + sortsReserve: formData.data.reve.reserve.list, + rencontres: duplicate(formData.data.reve.rencontre.list), + casesTmr: formData.itemsByType.casetmr + } + RdDUtility.buildArbreDeConteneur(this, formData); - formData.data.vehiculesList = this.actor.buildVehiculesList(); - formData.data.monturesList = this.actor.buildMonturesList(); - formData.data.suivantsList = this.actor.buildSuivantsList(); + formData.subacteurs = { + vehicules: this.actor.listeVehicules(), + montures: this.actor.listeMontures(), + suivants: this.actor.listeSuivants() + } return formData; } diff --git a/module/actor.js b/module/actor.js index 8b046d36..ee1e39af 100644 --- a/module/actor.js +++ b/module/actor.js @@ -311,6 +311,9 @@ export class RdDActor extends Actor { } return duplicate(list[0]); } + getDemiReve() { + return this.data.data.reve.tmrpos.coord; + } /* -------------------------------------------- */ async deleteSortReserve(sortReserve) { let reserve = duplicate(this.data.data.reve.reserve); @@ -1160,26 +1163,6 @@ export class RdDActor extends Actor { return tmrInnaccessibles.map(it => it.data.coord); } - /* -------------------------------------------- */ - displayTMRQueueSouffleInformation() { - let messages = []; - for (let item of this.data.items) { - if (EffetsDraconiques.isUrgenceDraconique(item)) { - messages.push("Vous souffrez d'une Urgence Draconique : " + item.data.description); - } - if (EffetsDraconiques.isPeriple(item)) { - messages.push("Vous souffrez du Souffle Périple. Vous devez gérer manuellement le détail du Périple.
" + item.data.description); - } - } - - if (messages.length > 0) { - ChatMessage.create({ - whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), - content: "RAPPEL !
" + messages.join('
') - }); - } - } - /* -------------------------------------------- */ getTMRRencontres() { return this.data.data.reve.rencontre; @@ -1193,7 +1176,7 @@ export class RdDActor extends Actor { //console.log("List", rencontres, len); let newTable = []; for (i = 0; i < len; i++) { - if (rencontres.list[i].coord != this.data.data.reve.tmrpos.coord) + if (rencontres.list[i].coord != this.getDemiReve()) newTable.push(rencontres.list[i]); } if (newTable.length != len) { @@ -1210,7 +1193,7 @@ export class RdDActor extends Actor { let i = 0; let already = false; for (i = 0; i < len; i++) { - if (rencontres.list[i].coord == this.data.data.reve.tmrpos.coord) + if (rencontres.list[i].coord == this.getDemiReve()) already = true; } if (!already) { @@ -1232,9 +1215,7 @@ export class RdDActor extends Actor { /* -------------------------------------------- */ async updateCoordTMR(coord) { - let tmrPos = duplicate(this.data.data.reve.tmrpos); - tmrPos.coord = coord; - await this.update({ "data.reve.tmrpos": tmrPos }); + await this.update({ "data.reve.tmrpos.coord": coord }); } /* -------------------------------------------- */ @@ -2546,7 +2527,7 @@ export class RdDActor extends Actor { let competence = this.getCompetence(compName); if (arme || armeName || (competence.type == 'competencecreature' && competence.data.iscombat)) { - RdDCombat.createUsingTarget(this).attaque(competence, arme); + RdDCombat.createUsingTarget(this)?.attaque(competence, arme); } else { this.rollCompetence(competence.name); } @@ -3007,7 +2988,7 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - buildVehiculesList() { + listeVehicules() { return this._buildActorLinksList( this.data.data.subacteurs?.vehicules ?? [], vehicle => { @@ -3019,12 +3000,12 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - buildSuivantsList() { + listeSuivants() { return this._buildActorLinksList(this.data.data.subacteurs?.suivants ?? []); } /* -------------------------------------------- */ - buildMonturesList() { + listeMontures() { return this._buildActorLinksList(this.data.data.subacteurs?.montures ?? []); } @@ -3205,6 +3186,9 @@ export class RdDActor extends Actor { case 'souffle': await this.onDeleteOwnedDraconique(item, options, id); break; + case 'casetmr': + await this.onDeleteOwnedCaseTmr(item, options, id); + break; } } @@ -3226,6 +3210,13 @@ export class RdDActor extends Actor { } } + async onDeleteOwnedCaseTmr(item, options, id) { + let draconique = Draconique.all().find(it => it.isCase(item)); + if (draconique) { + draconique.onActorDeleteCaseTmr(this, item) + } + } + notifyGestionTeteSouffleQueue(item, manualMessage = true) { ChatMessage.create({ whisper: ChatUtility.getWhisperRecipientsAndGMs(game.user.name), diff --git a/module/item-arme.js b/module/item-arme.js index fa3428a5..0d6ea65d 100644 --- a/module/item-arme.js +++ b/module/item-arme.js @@ -1,4 +1,5 @@ import { RdDItemCompetenceCreature } from "./item-competencecreature.js" +import { RdDCombatManager } from "./rdd-combat.js"; const nomCategorieParade = { "sans-armes": "Sans arme / armes naturelles", @@ -30,6 +31,17 @@ export class RdDItemArme extends Item { return RdDItemArme.mainsNues(); } + static computeNiveauArmes(armes, competences) { + for (const arme of armes) { + arme.data.niveau = RdDItemArme.niveauCompetenceArme(arme, competences); + } + } + + static niveauCompetenceArme(arme, competences) { + const compArme = competences.find(it => it.name == arme.data.competence); + return compArme?.data.niveau ?? -8; + } + /* -------------------------------------------- */ static getNomCategorieParade(arme) { const categorie = arme?.data ? RdDItemArme.getCategorieParade(arme) : arme; @@ -38,7 +50,7 @@ export class RdDItemArme extends Item { /* -------------------------------------------- */ static needArmeResist(armeAttaque, armeParade) { - if (!armeAttaque || !armeParade){ + if (!armeAttaque || !armeParade) { return false; } // Epées parant une arme de bois (cf. page 115 ), une résistance est nécessaire @@ -54,7 +66,7 @@ export class RdDItemArme extends Item { return arme.data.categorie_parade; } // pour compatibilité avec des personnages existants - if (arme.type == 'competencecreature' || arme.data.categorie == 'creature' ) { + if (arme.type == 'competencecreature' || arme.data.categorie == 'creature') { return arme.data.categorie_parade || (arme.data.isparade ? 'sans-armes' : ''); } if (!arme.type.match(/arme|competencecreature/)) { @@ -86,7 +98,7 @@ export class RdDItemArme extends Item { /* -------------------------------------------- */ static needParadeSignificative(armeAttaque, armeParade) { - if (!armeAttaque || !armeParade){ + if (!armeAttaque || !armeParade) { return false; } // categories d'armes à la parade (cf. page 115 ) @@ -145,10 +157,16 @@ export class RdDItemArme extends Item { } static isArmeUtilisable(item) { - return item.type == 'arme' && item.data.equipe && (item.data.resistance > 0 || item.data.portee_courte>0); + return item.type == 'arme' && item.data.equipe && (item.data.resistance > 0 || item.data.portee_courte > 0); } - static mainsNues(actorData={}) { + static ajoutCorpsACorps(armes, competences, carac) { + let corpsACorps = competences.find(it => it.name == 'Corps à corps') ?? { data: { niveau: -6 } }; + let init = RdDCombatManager.calculInitiative(corpsACorps.data.niveau, carac['melee'].value); + armes.push(RdDItemArme.mainsNues({ niveau: corpsACorps.data.niveau, initiative: init })); + } + + static mainsNues(actorData = {}) { const mainsNues = { name: 'Mains nues', data: { @@ -163,8 +181,8 @@ export class RdDItemArme extends Item { } }; if (actorData) { - mergeObject( mainsNues.data, actorData, {overwrite:false}); + mergeObject(mainsNues.data, actorData, { overwrite: false }); } return mainsNues } -} +} diff --git a/module/item-competence.js b/module/item-competence.js index 2f947f17..c7be84c7 100644 --- a/module/item-competence.js +++ b/module/item-competence.js @@ -56,11 +56,15 @@ export class RdDItemCompetence extends Item { return categorieCompetences[category].label; } + static getEsquive(competences) { + return { name: 'Esquive', niveau: RdDItemCompetence.findCompetence(competences, 'Esquive')?.data.niveau ?? -6 }; + } + /* -------------------------------------------- */ static isCompetenceArme(competence) { switch (competence.data.categorie) { case 'melee': - return competence.name.toLowerCase() != 'esquive'; + return competence.name != 'Esquive'; case 'tir': case 'lancer': return true; diff --git a/module/rdd-combat.js b/module/rdd-combat.js index db85f5c1..e668de44 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -413,9 +413,11 @@ export class RdDCombat { ? "Vous devez choisir une seule cible à attaquer!" : "Vous devez choisir une cible à attaquer!"); } - const defender = target?.actor; - const defenderTokenId = target?.data._id; - return this.create(attacker, defender, defenderTokenId, target) + else { + const defender = target?.actor; + const defenderTokenId = target?.data._id; + return this.create(attacker, defender, defenderTokenId, target) + } } /* -------------------------------------------- */ @@ -500,11 +502,13 @@ export class RdDCombat { } if ((game.user.isGM && !defenderToken.actor.hasPlayerOwner) || (defenderToken.actor.hasPlayerOwner && (game.user.character._id == defenderToken.actor.data._id))) { const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); - const defenderRoll = msg.defenderRoll; - RdDCombat._storeAttaque(msg.attackerId, defenderRoll.attackerRoll); - RdDCombat._storeDefense(defenderRoll); - rddCombat.removeChatMessageActionsPasseArme(defenderRoll.passeArme); - rddCombat._chatMessageDefense(msg.paramChatDefense); + if (rddCombat) { + const defenderRoll = msg.defenderRoll; + RdDCombat._storeAttaque(msg.attackerId, defenderRoll.attackerRoll); + RdDCombat._storeDefense(defenderRoll); + rddCombat.removeChatMessageActionsPasseArme(defenderRoll.passeArme); + rddCombat._chatMessageDefense(msg.paramChatDefense); + } } } } @@ -533,9 +537,11 @@ export class RdDCombat { const rddCombat = RdDCombat.createForAttackerAndDefender( event.currentTarget.attributes['data-attackerId']?.value, event.currentTarget.attributes['data-defenderTokenId']?.value); + if (rddCombat) { - rddCombat.onEvent(button, event); - event.preventDefault(); + rddCombat.onEvent(button, event); + event.preventDefault(); + } }); } html.on("click", '#chat-jet-vie', event => { diff --git a/module/rdd-commands.js b/module/rdd-commands.js index 317b62bf..dddfdb2c 100644 --- a/module/rdd-commands.js +++ b/module/rdd-commands.js @@ -24,6 +24,8 @@ export class RdDCommands { rddCommands.registerCommand({ path: ["/aide"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" }); rddCommands.registerCommand({ path: ["/help"], func: (content, msg, params) => rddCommands.help(msg), descr: "Affiche l'aide pour toutes les commandes" }); rddCommands.registerCommand({ path: ["/table", "queues"], func: (content, msg, params) => RdDRollTables.getQueue(true), descr: "Tire une Queue de Dragon" }); + rddCommands.registerCommand({ path: ["/table", "ideefixe"], func: (content, msg, params) => RdDRollTables.getIdeeFixe(true), descr: "Tire une Idée fixe" }); + rddCommands.registerCommand({ path: ["/table", "desir"], func: (content, msg, params) => RdDRollTables.getDesirLancinant(true), descr: "Tire un Désir Lancinant" }); rddCommands.registerCommand({ path: ["/table", "ombre"], func: (content, msg, params) => RdDRollTables.getOmbre(true), descr: "Tire une Ombre de Dragon" }); rddCommands.registerCommand({ path: ["/table", "tetehr"], func: (content, msg, params) => RdDRollTables.getTeteHR(true), descr: "Tire une Tête de Dragon pour Hauts Revants" }); rddCommands.registerCommand({ path: ["/table", "tete"], func: (content, msg, params) => RdDRollTables.getTete(true), descr: "Tire une Tête de Dragon" }); diff --git a/module/rdd-rolltables.js b/module/rdd-rolltables.js index 957c130a..74d06152 100644 --- a/module/rdd-rolltables.js +++ b/module/rdd-rolltables.js @@ -48,14 +48,22 @@ export class RdDRollTables { static async getQueue(toChat = false) { let queue = await RdDRollTables.drawItemFromRollTable("Queues de dragon", toChat); if (queue.name.toLowerCase().includes('lancinant') ) { - queue = await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat); + return await RdDRollTables.getDesirLancinant(toChat); } if (queue.name.toLowerCase().includes('fixe') ) { - queue = await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat); + return await RdDRollTables.getIdeeFixe(toChat); } return queue; } + static async getDesirLancinant(toChat = false) { + return await RdDRollTables.drawItemFromRollTable("Désirs lancinants", toChat); + } + + static async getIdeeFixe(toChat = false) { + return await RdDRollTables.drawItemFromRollTable("Idées fixes", toChat); + } + /* -------------------------------------------- */ static async getTeteHR(toChat = false) { return await RdDRollTables.drawItemFromRollTable("Têtes de Dragon pour haut-rêvants", toChat); diff --git a/module/rdd-tmr-dialog.js b/module/rdd-tmr-dialog.js index a3bcff7e..cbc1aafe 100644 --- a/module/rdd-tmr-dialog.js +++ b/module/rdd-tmr-dialog.js @@ -108,6 +108,11 @@ export class RdDTMRDialog extends Dialog { this._createTokens(); } + removeToken(tmr, casetmr) { + this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id); + this.updateTokens() + } + /* -------------------------------------------- */ _getTokensCasesTmr() { return this.casesSpeciales.map(c => this._tokenCaseSpeciale(c)).filter(token => token); @@ -142,7 +147,7 @@ export class RdDTMRDialog extends Dialog { async activateListeners(html) { super.activateListeners(html); - document.getElementById("tmrrow1").insertCell(1).append(this.pixiApp.view); + document.getElementById("tmrrow1").insertCell(0).append(this.pixiApp.view); if (this.viewOnly) { html.find('#lancer-sort').remove(); @@ -168,7 +173,6 @@ export class RdDTMRDialog extends Dialog { let tmr = TMRUtility.getTMR(this.actor.data.data.reve.tmrpos.coord); await this.manageRencontre(tmr, () => { this.postRencontre(tmr); - this.actor.displayTMRQueueSouffleInformation(); }); } @@ -498,9 +502,7 @@ export class RdDTMRDialog extends Dialog { } async _resultatMaitriseCaseHumide(rollData) { - if (rollData.rolled.isETotal) { - rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); - } + await this.souffleSiEchecTotal(rollData); this.toclose = rollData.rolled.isEchec; if (rollData.rolled.isSuccess && rollData.double) { rollData.previous = { rolled: rollData.rolled, ajustements: rollData.ajustements }; @@ -518,6 +520,12 @@ export class RdDTMRDialog extends Dialog { } } + async souffleSiEchecTotal(rollData) { + if (rollData.rolled.isETotal) { + rollData.souffle = await this.actor.ajouterSouffle({ chat: false }); + } + } + /* -------------------------------------------- */ isCaseHumide(tmr) { if (!(TMRUtility.isCaseHumide(tmr) || this.isCaseHumideAdditionelle(tmr))) { @@ -558,16 +566,29 @@ export class RdDTMRDialog extends Dialog { await this._conquerir(tmr, { difficulte: -9, action: 'Conquérir la cité', - onConqueteReussie: r => EffetsDraconiques.fermetureCites.onConquete(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), - onConqueteEchec: r => this.close(), + onConqueteReussie: r => EffetsDraconiques.fermetureCites.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => { + this.souffleSiEchecTotal(rollData); + this.close() + }, canClose: false }); } } - - removeToken(tmr, casetmr) { - this._removeTokens(t => t.coordTMR() == tmr.coord && t.caseSpeciale?._id == casetmr._id); - this.updateTokens() + /* -------------------------------------------- */ + async purifierPeriple(tmr) { + if (EffetsDraconiques.periple.find(this.casesSpeciales, tmr.coord)) { + await this._conquerir(tmr, { + difficulte: EffetsDraconiques.periple.getDifficulte(tmr), + action: 'Purifier ' + TMRUtility.getTMRDescr(tmr.coord), + onConqueteReussie: r => EffetsDraconiques.periple.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => { + this.souffleSiEchecTotal(rollData); + this.close() + }, + canClose: false + }); + } } /* -------------------------------------------- */ @@ -576,8 +597,8 @@ export class RdDTMRDialog extends Dialog { await this._conquerir(tmr, { difficulte: -7, action: 'Conquérir', - onConqueteReussie: r => EffetsDraconiques.conquete.onConquete(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), - onConqueteEchec: r => { }, + onConqueteReussie: r => EffetsDraconiques.conquete.onVisiteSupprimer(r.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)), + onConqueteEchec: r => this.close(), canClose: false }); } @@ -639,8 +660,9 @@ export class RdDTMRDialog extends Dialog { dialog.render(true); } - async validerPelerinage(tmr) { - await EffetsDraconiques.pelerinage.onFinPelerinage(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)); + async validerVisite(tmr) { + await EffetsDraconiques.pelerinage.onVisiteSupprimer(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)); + await EffetsDraconiques.urgenceDraconique.onVisiteSupprimer(this.actor, tmr, (casetmr) => this.removeToken(tmr, casetmr)); } @@ -649,11 +671,12 @@ export class RdDTMRDialog extends Dialog { let sortReserveList = TMRUtility.getSortReserveList(this.sortsReserves, coord); if (sortReserveList.length > 0) { - if (EffetsDraconiques.isSortImpossible(this.actor)) { + if (EffetsDraconiques.isSortReserveImpossible(this.actor)) { ui.notifications.error("Une queue ou un souffle vous empèche de déclencher de sort!"); return; } - if (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord)) { + if (!EffetsDraconiques.isUrgenceDraconique(this.actor) && + (EffetsDraconiques.isReserveEnSecurite(this.actor) || this.isReserveExtensible(coord))) { let msg = "Vous êtes sur une case avec un Sort en Réserve. Grâce à votre Tête Reserve en Sécurité ou Réserve Exensible, vous pouvez contrôler le déclenchement. Cliquez si vous souhaitez le déclencher :