diff --git a/module/actor.js b/module/actor.js index 4b918d10..97a831a6 100644 --- a/module/actor.js +++ b/module/actor.js @@ -25,7 +25,9 @@ import { RdDAlchimie } from "./rdd-alchimie.js"; * @extends {Actor} */ export class RdDActor extends Actor { - + /* -------------------------------------------- */ + static init() { + } /* -------------------------------------------- */ /** * Override the create() function to provide additional RdD functionality. @@ -2196,15 +2198,15 @@ export class RdDActor extends Actor { } /* -------------------------------------------- */ - async encaisserDommages(attackerRoll, attacker = undefined) { + async encaisserDommages(rollData, attacker = undefined) { if (attacker && !await attacker.accorder(this, 'avant-encaissement')) { return; } - console.log("encaisserDommages", attackerRoll) + console.log("encaisserDommages", rollData) let santeOrig = duplicate(this.data.data.sante); - let encaissement = this.jetEncaissement(attackerRoll); + let encaissement = this.jetEncaissement(rollData); this.ajouterBlessure(encaissement); // Will upate the result table const perteVie = await this.santeIncDec("vie", - encaissement.vie); @@ -2513,7 +2515,6 @@ export class RdDActor extends Actor { } } - /* -------------------------------------------- */ _alchimieResult(rollData) { RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-alchimie.html'); diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 14f3966b..ae860ea9 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -9,6 +9,67 @@ import { RdDRollTables } from "./rdd-rolltables.js"; export class RdDCombat { + static init() { + this.initStorePasseArmes(); + Hooks.on("updateCombat", (combat, data) => { RdDCombat.onUpdateCombat(combat, data) }); + Hooks.on("preDeleteCombat", (combat, options) => { RdDCombat.onPreDeleteCombat(combat, options); }); + } + + /* -------------------------------------------- */ + static initStorePasseArmes() { + game.system.rdd.combatStore = { + attaques: {}, + defenses: {} + }; + } + + /* -------------------------------------------- */ + static onSocketMessage(sockmsg) { + switch (sockmsg.msg) { + case "msg_encaisser": + return RdDCombat.terminerPasseArmes(data); + case "msg_defense": + return RdDCombat.handleMsgDefense(sockmsg.data); + } + } + + /* -------------------------------------------- */ + static onUpdateCombat(combat, data) { + if (combat.data.round != 0 && combat.turns && combat.data.active) { + RdDCombat.combatNouveauRound(combat); + } + } + + /* -------------------------------------------- */ + static onPreDeleteCombat(combat, options) { + if (game.user.isGM) { + ChatUtility.removeMyChatMessageContaining(`
`) + /* + * TODO: support de plusieurs combats parallèles + * il faudrait avoir un id de combat en plus de celui de passe d'armes + */ + for (const key in game.system.rdd.combatStore.attaques) { + const attackerRoll = game.system.rdd.combatStore.attaques[key]; + ChatUtility.removeChatMessageActionsPasseArme(`
`); + } + for (const key in game.system.rdd.combatStore.defenses) { + const defenderRoll = game.system.rdd.combatStore.defenses[key]; + ChatUtility.removeMyChatMessageContaining(`
`); + } + RdDCombat.initStorePasseArmes(); + } + } + + /* -------------------------------------------- */ + static combatNouveauRound(combat) { + let turn = combat.turns.find(t => t.tokenId == combat.current.tokenId); + if (game.user.isGM) { + // seul le GM notifie le status + this.displayActorCombatStatus(combat, turn.actor); + // TODO Playaudio for player?? + } + } + /* -------------------------------------------- */ static isActive() { return true; @@ -58,6 +119,33 @@ export class RdDCombat { return RdDCombat.createUsingTarget(attacker) } + /* -------------------------------------------- */ + static handleMsgDefense(data) { + let defenderToken = canvas.tokens.get(data.defenderTokenId); + if (defenderToken) { + if (!game.user.isGM && game.user.character == undefined) { // vérification / sanity check + ui.notifications.error("Le joueur " + game.user.name + " n'est connecté à aucun personnage. Impossible de continuer."); + return; + } + if ((game.user.isGM && !defenderToken.actor.hasPlayerOwner) || (defenderToken.actor.hasPlayerOwner && (game.user.character.id == defenderToken.actor.data._id))) { + //console.log("User is pushing message...", game.user.name); + game.system.rdd.combatStore.attaques[data.attackerId] = duplicate(data.rollData); + data.whisper = [game.user]; + data.blind = true; + data.rollMode = "blindroll"; + ChatMessage.create(data); + } + } + } + + static terminerPasseArmes(data) { + if (game.user.isGM) { // Seul le GM nettoie le stockage des données de combat + let attackerRoll = game.system.rdd.combatStore.attaques[data.attackerId]; // Retrieve the rolldata from the store + game.system.rdd.combatStore.attaques[data.attackerId] = undefined; + game.system.rdd.combatStore.defenses[attackerRoll.passeArme] = undefined; + } + } + /* -------------------------------------------- */ static _sendRollMessage(sender, recipient, defenderTokenId, topic, message, rollData) { let chatMessage = { @@ -129,7 +217,7 @@ export class RdDCombat { /* -------------------------------------------- */ async onEvent(button, event) { - let attackerRoll = game.system.rdd.rollDataHandler.attaques[this.attackerId]; + let attackerRoll = game.system.rdd.combatStore.attaques[this.attackerId]; if (!attackerRoll) { ui.notifications.warn("Action automatisée impossible, le jet de l'attaquant a été perdu (suite à un raffraichissement?)") return; @@ -163,18 +251,18 @@ export class RdDCombat { /* -------------------------------------------- */ _consumeDefense(passeArme) { let defenderRoll = this._getDefense(passeArme); - game.system.rdd.rollDataHandler.defenses[passeArme] = undefined; + game.system.rdd.combatStore.defenses[passeArme] = undefined; return defenderRoll; } /* -------------------------------------------- */ _getDefense(passeArme) { - return game.system.rdd.rollDataHandler.defenses[passeArme]; + return game.system.rdd.combatStore.defenses[passeArme]; } /* -------------------------------------------- */ _storeDefense(defenderRoll) { - game.system.rdd.rollDataHandler.defenses[defenderRoll.passeArme] = defenderRoll; + game.system.rdd.combatStore.defenses[defenderRoll.passeArme] = defenderRoll; } /* -------------------------------------------- */ @@ -320,7 +408,7 @@ export class RdDCombat { /* -------------------------------------------- */ async _onAttaqueParticuliere(rollData) { - game.system.rdd.rollDataHandler.attaques[this.attackerId] = duplicate(rollData); + game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(rollData); // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum const isMeleeDiffNegative = rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0; @@ -342,7 +430,7 @@ export class RdDCombat { attackerRoll.dmg = RdDBonus.dmg(attackerRoll, this.attacker.getBonusDegat(), this.defender.isEntiteCauchemar()); // Save rollData for defender - game.system.rdd.rollDataHandler.attaques[this.attackerId] = duplicate(attackerRoll); + game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(attackerRoll); attackerRoll.show = { cible: this.target ? this.defender.data.name : 'la cible', @@ -401,7 +489,7 @@ export class RdDCombat { /* -------------------------------------------- */ async _onAttaqueEchecTotal(attackerRoll) { - game.system.rdd.rollDataHandler.attaques[this.attackerId] = duplicate(attackerRoll); + game.system.rdd.combatStore.attaques[this.attackerId] = duplicate(attackerRoll); // Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum ChatMessage.create({ @@ -755,8 +843,9 @@ export class RdDCombat { } /* -------------------------------------------- */ - static async displayActorCombatStatus(actor) { - let rollData = { + static async displayActorCombatStatus(combat, actor) { + let data = { + combatId: combat._id, alias: actor.name, etatGeneral: actor.getEtatGeneral(), isSonne: actor.getSonne(), @@ -767,22 +856,14 @@ export class RdDCombat { isCritique: false } if (actor.countBlessuresByName("critiques") > 0) { // Pour éviter le cumul grave + critique - rollData.isCritique = true; + data.isCritique = true; } else if (actor.countBlessuresByName("graves") > 0) { - rollData.isGrave = true; + data.isGrave = true; } + ChatUtility.createChatWithRollMode(actor.name, { - content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, rollData) + content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-actor-turn-summary.html`, data) }); } - /* -------------------------------------------- */ - static updateCombatRound(combat, data) { - if (combat.data.round != 0 && combat.turns && combat.data.active) { - let turn = combat.turns.find(t => t.tokenId == combat.current.tokenId); - this.displayActorCombatStatus(turn.actor); - // TODO Playaudio ?? - } - } - } \ No newline at end of file diff --git a/module/rdd-main.js b/module/rdd-main.js index 18be45a1..80ed4442 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -45,16 +45,16 @@ const _patch_initiative = () => { ids = typeof ids === "string" ? [ids] : ids; const currentId = this.combatant._id; // calculate initiative - for ( let cId = 0; cId < ids.length; cId++) { - const c = this.getCombatant( ids[cId] ); + for (let cId = 0; cId < ids.length; cId++) { + const c = this.getCombatant(ids[cId]); //if (!c) return results; let rollFormula = formula; // Init per default - if ( !rollFormula ) { + if (!rollFormula) { let armeCombat, competence; - if ( c.actor.data.type == 'creature' || c.actor.data.type == 'entite') { + if (c.actor.data.type == 'creature' || c.actor.data.type == 'entite') { for (const competenceItem of c.actor.data.items) { - if ( competenceItem.data.iscombat) { + if (competenceItem.data.iscombat) { competence = duplicate(competenceItem); } } @@ -65,17 +65,17 @@ const _patch_initiative = () => { armeCombat = duplicate(item); } } - let compName = ( armeCombat == undefined ) ? "Corps à corps" : armeCombat.data.competence; - competence = RdDItemCompetence.findCompetence( c.actor.data.items, compName ); - rollFormula = RdDUtility.calculInitiative(competence.data.niveau, c.actor.data.data.carac[competence.data.defaut_carac].value); + let compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.data.competence; + competence = RdDItemCompetence.findCompetence(c.actor.data.items, compName); + rollFormula = RdDUtility.calculInitiative(competence.data.niveau, c.actor.data.data.carac[competence.data.defaut_carac].value); } } //console.log("Combatat", c); const roll = this._getInitiativeRoll(c, rollFormula); - if ( roll.total <= 0 ) roll.total = 1; + if (roll.total <= 0) roll.total = 1; //console.log("Compute init for", armeCombat, competence, rollFormula, roll.total); - await this.updateEmbeddedEntity("Combatant", { _id: c._id, initiative: roll.total }); - + await this.updateEmbeddedEntity("Combatant", { _id: c._id, initiative: roll.total }); + // Send a chat message let rollMode = messageOptions.rollMode || game.settings.get("core", "rollMode"); let messageData = mergeObject( @@ -98,22 +98,17 @@ const _patch_initiative = () => { } /************************************************************************************/ -Hooks.once("init", async function() { +Hooks.once("init", async function () { console.log(`Initializing Reve de Dragon System`); - + // preload handlebars templates RdDUtility.preloadHandlebarsTemplates(); - // Create useful storage space game.system.rdd = { - rollDataHandler: { - attaques: {}, - defenses: {} - }, TMRUtility: TMRUtility } - /* -------------------------------------------- */ + /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "accorder-entite-cauchemar", { name: "Accorder le rêve aux entités", hint: "A quel moment les personnages doivent accorder leur rêve aux entités de cauchemar", @@ -130,7 +125,7 @@ Hooks.once("init", async function() { /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "calendrier", { - name: "calendrier", + name: "calendrier", scope: "world", config: false, type: Object @@ -138,7 +133,7 @@ Hooks.once("init", async function() { /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "liste-nombre-astral", { - name: "liste-nombre-astral", + name: "liste-nombre-astral", scope: "world", config: false, type: Object @@ -146,7 +141,7 @@ Hooks.once("init", async function() { /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "calendrier-pos", { - name: "calendrierPos", + name: "calendrierPos", scope: "world", config: false, type: Object @@ -160,7 +155,7 @@ Hooks.once("init", async function() { config: true, default: false, type: Boolean - }); + }); /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "supprimer-dialogues-combat-chat", { @@ -170,7 +165,7 @@ Hooks.once("init", async function() { config: true, default: true, type: Boolean - }); + }); /* -------------------------------------------- */ game.settings.register("foundryvtt-reve-de-dragon", "activer-sons-audio", { name: "Activer les bruitages intégrés", @@ -180,37 +175,39 @@ Hooks.once("init", async function() { default: true, type: Boolean }); - + /* -------------------------------------------- */ - // Set an initiative formula for the system - CONFIG.Combat.initiative = { - formula: "1d20", + // Set an initiative formula for the system + CONFIG.Combat.initiative = { + formula: "1d20", decimals: 2 }; - + /* -------------------------------------------- */ game.socket.on("system.foundryvtt-reve-de-dragon", data => { - RdDUtility.performSocketMesssage( data ); + RdDUtility.onSocketMesssage(data); + RdDCombat.onSocketMessage(data); }); /* -------------------------------------------- */ - // Define custom Entity classes + // Define custom Entity classes CONFIG.Actor.entityClass = RdDActor; - CONFIG.RDD = { - resolutionTable : RdDResolutionTable.resolutionTable, - carac_array : RdDUtility.getCaracArray(), - ajustementsConditions : RdDUtility.getAjustementsConditions(), - difficultesLibres : RdDUtility.getDifficultesLibres() + CONFIG.RDD = { + resolutionTable: RdDResolutionTable.resolutionTable, + carac_array: RdDUtility.getCaracArray(), + ajustementsConditions: RdDUtility.getAjustementsConditions(), + difficultesLibres: RdDUtility.getDifficultesLibres() } - + /* -------------------------------------------- */ // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorSheet, { - types: ["personnage"], - makeDefault: true } + types: ["personnage"], + makeDefault: true + } ); - Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorCreatureSheet, { + Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorCreatureSheet, { types: ["creature"], makeDefault: true }); @@ -218,44 +215,41 @@ Hooks.once("init", async function() { types: ["vehicule"], makeDefault: true }); - Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorEntiteSheet, { + Actors.registerSheet("foundryvtt-reve-de-dragon", RdDActorEntiteSheet, { types: ["entite"], makeDefault: true }); Items.unregisterSheet("core", ItemSheet); - Items.registerSheet("foundryvtt-reve-de-dragon", RdDItemSheet, {makeDefault: true}); + Items.registerSheet("foundryvtt-reve-de-dragon", RdDItemSheet, { makeDefault: true }); // Handlebar function pour container Handlebars.registerHelper('buildConteneur', (objet) => { return RdDUtility.buildConteneur(objet); }); // Patch the initiative formula _patch_initiative(); + + // préparation des différents modules + RdDCommands.init(); + RdDCombat.init(); + RdDTokenHud.init(); }); /* -------------------------------------------- */ -function messageDeBienvenue(){ +function messageDeBienvenue() { ChatUtility.removeMyChatMessageContaining('
'); - ChatMessage.create( { + ChatMessage.create({ user: game.user._id, - whisper: [game.user._id], - content : `
Bienvenue dans le Rêve des Dragons ! + whisper: [game.user._id], + content: `
Bienvenue dans le Rêve des Dragons !
Vous trouverez quelques informations pour démarrer dans ce document : @Compendium[foundryvtt-reve-de-dragon.rappel-des-regles.7uGrUHGdPu0EmIu2]{Documentation MJ/Joueurs}
La commande /aide dans le chat permet de voir les commandes spécifiques à Rêve de Dragon.
` }); } -/* -------------------------------------------- */ -Hooks.once("renderApplication", () => { - messageDeBienvenue(); -}); - /* -------------------------------------------- */ /* Foundry VTT Initialization */ /* -------------------------------------------- */ -Hooks.once("ready", function() { - - // préparation des lignes de commandes - RdDCommands.init(); +Hooks.once("ready", function () { /* -------------------------------------------- */ /* Affiche/Init le calendrier */ let calendrier = new RdDCalendrier(); @@ -264,20 +258,20 @@ Hooks.once("ready", function() { let templateData = {}; renderTemplate(templatePath, templateData).then(html => { calendrier.render(true); - } ); + }); game.system.rdd.calendrier = calendrier; // Reference; // Avertissement si joueur sans personnage - if ( !game.user.isGM && game.user.character == undefined) { - ui.notifications.info( "Attention ! Vous n'êtes connecté à aucun personnage !" ); - ChatMessage.create( { content:"ATTENTION Le joueur " + game.user.name + " n'est connecté à aucun personnage !", - user: game.user._id } ); - //whisper: [ ChatMessage.getWhisperRecipients("GM") ] } ); + if (!game.user.isGM && game.user.character == undefined) { + ui.notifications.info("Attention ! Vous n'êtes connecté à aucun personnage !"); + ChatMessage.create({ + content: "ATTENTION Le joueur " + game.user.name + " n'est connecté à aucun personnage !", + user: game.user._id + }); + //whisper: [ ChatMessage.getWhisperRecipients("GM") ] } ); } - // Integration du TokenHUD - Hooks.on('renderTokenHUD', (app, html, data) => { RdDTokenHud.addTokenHudExtensions(app, html, data._id) }); - Hooks.on("updateCombat", (combat, data) => { RdDCombat.updateCombatRound(combat, data) } ); + messageDeBienvenue(); }); /* -------------------------------------------- */ @@ -287,7 +281,7 @@ Hooks.on("chatMessage", (html, content, msg) => { if (content[0] == '/') { let regExp = /(\S+)/g; let commands = content.toLowerCase().match(regExp); - if (game.system.rdd.commands.processChatCommand(commands, content, msg)){ + if (game.system.rdd.commands.processChatCommand(commands, content, msg)) { return false; } } @@ -296,5 +290,5 @@ Hooks.on("chatMessage", (html, content, msg) => { /* -------------------------------------------- */ Hooks.on("getCombatTrackerEntryContext", (html, options) => { - RdDUtility.pushInitiativeOptions( html, options ); + RdDUtility.pushInitiativeOptions(html, options); }) diff --git a/module/rdd-token-hud.js b/module/rdd-token-hud.js index c056c61d..501f4928 100644 --- a/module/rdd-token-hud.js +++ b/module/rdd-token-hud.js @@ -5,6 +5,11 @@ import { RdDUtility } from "./rdd-utility.js"; /* -------------------------------------------- */ export class RdDTokenHud { + static init(){ + // Integration du TokenHUD + Hooks.on('renderTokenHUD', (app, html, data) => { RdDTokenHud.addTokenHudExtensions(app, html, data._id) }); + } + /* -------------------------------------------- */ static async removeExtensionHud( app, html, tokenId) { let combat = html.find('.control-icon.rdd-combat'); diff --git a/module/rdd-utility.js b/module/rdd-utility.js index f3dcea1d..94323548 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -629,13 +629,11 @@ export class RdDUtility { } /* -------------------------------------------- */ - static performSocketMesssage(sockmsg) { + static onSocketMesssage(sockmsg) { console.log(">>>>> MSG RECV", sockmsg); switch (sockmsg.msg) { case "msg_encaisser": return RdDUtility._handleMsgEncaisser(sockmsg.data); - case "msg_defense": - return RdDUtility._handleMsgDefense(sockmsg.data); case "msg_gm_chat_message": return ChatUtility.handleGMChatMessage(sockmsg.data); case "msg_sync_time": @@ -647,25 +645,6 @@ export class RdDUtility { } } - /* -------------------------------------------- */ - static _handleMsgDefense(data) { - let defenderToken = canvas.tokens.get(data.defenderTokenId); - if (defenderToken) { - if (!game.user.isGM && game.user.character == undefined) { // vérification / sanity check - ui.notifications.error("Le joueur " + game.user.name + " n'est connecté à aucun personnage. Impossible de continuer."); - return; - } - if ((game.user.isGM && !defenderToken.actor.hasPlayerOwner) || (defenderToken.actor.hasPlayerOwner && (game.user.character.id == defenderToken.actor.data._id))) { - //console.log("User is pushing message...", game.user.name); - game.system.rdd.rollDataHandler.attaques[data.attackerId] = duplicate(data.rollData); - data.whisper = [game.user]; - data.blind = true; - data.rollMode = "blindroll"; - ChatMessage.create(data); - } - } - } - /* -------------------------------------------- */ static rollInitiativeCompetence(combatantId, arme) { const combatant = game.combat.getCombatant(combatantId); @@ -760,12 +739,9 @@ export class RdDUtility { /* -------------------------------------------- */ static _handleMsgEncaisser(data) { if (game.user.isGM) { // Seul le GM effectue l'encaissement sur la fiche - let attackerRoll = game.system.rdd.rollDataHandler.attaques[data.attackerId]; // Retrieve the rolldata from the store - game.system.rdd.rollDataHandler.attaques[data.attackerId] = undefined; - game.system.rdd.rollDataHandler.defenses[attackerRoll.passeArme] = undefined; - let defender = canvas.tokens.get(data.defenderTokenId).actor; - defender.encaisserDommages(attackerRoll); + let attacker = data.attackerId ? game.actors.get(data.attackerId) : null; + defender.encaisserDommages(attackerRoll, attacker); } } diff --git a/styles/simple.css b/styles/simple.css index f1d91e6f..256ccf9c 100644 --- a/styles/simple.css +++ b/styles/simple.css @@ -241,8 +241,18 @@ table {border: 1px solid #7a7971;} } .button-img { + vertical-align: baseline; width: 8%; height: 8%; + max-height: 48px; + border-width: 0; +} + +.button-effect-img { + vertical-align: baseline; + width: 16px; + max-height: 16px; + height: 16; border-width: 0; } @@ -253,12 +263,6 @@ table {border: 1px solid #7a7971;} vertical-align: bottom; } -.img-sonne { - vertical-align: baseline; - max-height: 16px; - border: 0; -} - .foundryvtt-reve-de-dragon .sheet-header .header-fields { -webkit-box-flex: 1; -ms-flex: 1; diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index 35d3cac3..27eab69d 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -41,7 +41,7 @@
  • diff --git a/templates/chat-actor-turn-summary.html b/templates/chat-actor-turn-summary.html index c87f041e..f0f89a1e 100644 --- a/templates/chat-actor-turn-summary.html +++ b/templates/chat-actor-turn-summary.html @@ -1,5 +1,5 @@ -

    C'est au tour de {{alias}} !

    -
    {{blessuresStatus}}
    +

    C'est au tour de {{alias}} !

    +
    {{blessuresStatus}}
    Son état général est de : {{etatGeneral}} {{#if isSonne}} et est sonné{{/if}}
    {{#if isGrave}}
    {{alias}} souffre de Blessure(s) Grave(s) : n'oubliez pas de faire un Je de Vie toutes les SC ({{SConst}}) minutes.