diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 5f37c9c5..66ff36a1 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -14,6 +14,7 @@ import { MAINS_DIRECTRICES } from "./actor.js"; import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js"; import { RdDItem } from "./item.js"; import { RdDItemBlessure } from "./item/blessure.js"; +import { RdDEmpoignade } from "./rdd-empoignade.js"; /* -------------------------------------------- */ /** @@ -83,6 +84,7 @@ export class RdDActorSheet extends RdDBaseActorSheet { RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac); formData.esquives = this.actor.getCompetences("Esquive"); formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac); + formData.empoignades = this.actor.getEmpoignades("Esquive"); this.armesList = formData.combat; @@ -254,6 +256,11 @@ export class RdDActorSheet extends RdDBaseActorSheet { this.actor.rollCarac('reve-actuel', true); }); + // Suite empoignade + this.html.find('.empoignade-label a').click(async event => { + let emp = RdDSheetUtility.getItem(event, this.actor) + RdDEmpoignade.onAttaqueEmpoignadeFromItem(emp) + }); // Roll Weapon1 this.html.find('.arme-label a').click(async event => { let arme = this._getEventArmeCombat(event); diff --git a/module/actor.js b/module/actor.js index 3833f413..b4c90d44 100644 --- a/module/actor.js +++ b/module/actor.js @@ -36,6 +36,7 @@ import { RdDBaseActor } from "./actor/base-actor.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDItemBlessure } from "./item/blessure.js"; import { AppAstrologie } from "./sommeil/app-astrologie.js"; +import { RdDEmpoignade } from "./rdd-empoignade.js"; const POSSESSION_SANS_DRACONIC = { img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp', @@ -231,7 +232,6 @@ export class RdDActor extends RdDBaseActor { getCompetences(name) { return RdDItemCompetence.findCompetences(this.items, name) } - /* -------------------------------------------- */ getTache(id) { return this.findItemLike(id, 'tache'); @@ -286,7 +286,9 @@ export class RdDActor extends RdDBaseActor { getPossessions() { return this.items.filter(it => it.type == 'possession'); } - + getEmpoignades() { + return this.items.filter(it => it.type == 'empoignade'); + } getDemiReve() { return this.system.reve.tmrpos.coord; } @@ -329,8 +331,21 @@ export class RdDActor extends RdDBaseActor { return ''; } + /* -------------------------------------------- */ + hasArmeeMeleeEquipee() { // Return true si l'acteur possède au moins 1 arme de mêlée équipée + let melee = this.items.filter(it => it.type == "arme" && it.system.equipe && it.system.competence != "") + return (melee.length > 0) + } + isEmpoignadeEnCours() { + return this.items.find(it => it.type == "empoignade" && it.system.pointsemp > 0) + } + /* -------------------------------------------- */ async roll() { + if (this.isEmpoignadeEnCours()) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } + const carac = mergeObject(duplicate(this.system.carac), { 'reve-actuel': this.getCaracReveActuel(), @@ -2061,6 +2076,10 @@ export class RdDActor extends RdDBaseActor { /* -------------------------------------------- */ async rollUnSort(coord) { + if (this.isEmpoignadeEnCours()) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } + if (EffetsDraconiques.isSortImpossible(this)) { ui.notifications.error("Une queue ou un souffle vous empèche de lancer de sort!"); return; @@ -2200,6 +2219,10 @@ export class RdDActor extends RdDBaseActor { /* -------------------------------------------- */ async rollCarac(caracName, jetResistance = undefined) { + if (this.isEmpoignadeEnCours()) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } + let selectedCarac = this.getCaracByName(caracName) await this._openRollDialog({ name: 'jet-' + caracName, @@ -2267,6 +2290,10 @@ export class RdDActor extends RdDBaseActor { /* -------------------------------------------- */ async rollCompetence(idOrName, options = { tryTarget: true }) { + if (this.isEmpoignadeEnCours()) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } + let rollData = { carac: this.system.carac, competence: this.getCompetence(idOrName) @@ -2347,6 +2374,10 @@ export class RdDActor extends RdDBaseActor { } async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) { + if (this.isEmpoignadeEnCours()) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } + const competence = this.getCompetence(compName); await this._openRollDialog({ name: 'jet-competence', @@ -2367,6 +2398,10 @@ export class RdDActor extends RdDBaseActor { /* -------------------------------------------- */ async rollTache(id, options = {}) { + if (this.isEmpoignadeEnCours()) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } + const tacheData = this.getTache(id) const compData = this.getCompetence(tacheData.system.competence) compData.system.defaut_carac = tacheData.system.carac; // Patch ! @@ -3754,6 +3789,9 @@ export class RdDActor extends RdDBaseActor { case 'casetmr': await this.onDeleteOwnedCaseTmr(item, options, id); break; + case 'empoignade': + await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item) + break; } } diff --git a/module/item-arme.js b/module/item-arme.js index 4f78a8e4..a564e018 100644 --- a/module/item-arme.js +++ b/module/item-arme.js @@ -165,7 +165,7 @@ export class RdDItemArme extends Item { 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 })); + armes.push(RdDItemArme.empoignade({ niveau: corpsACorps.system.niveau, initiative: init })); } static corpsACorps(mainsNuesActor) { diff --git a/module/item.js b/module/item.js index 3cd7b9c3..4fc78004 100644 --- a/module/item.js +++ b/module/item.js @@ -75,6 +75,7 @@ export const defaultItemImg = { sortreserve: "systems/foundryvtt-reve-de-dragon/icons/competence_oniros.webp", extraitpoetique: "systems/foundryvtt-reve-de-dragon/icons/competence_ecriture.webp", tarot: "systems/foundryvtt-reve-de-dragon/icons/tarots/dos-tarot.webp", + empoignade: "systems/foundryvtt-reve-de-dragon/icons/competence_corps_a_corps.webp" } /* -------------------------------------------- */ diff --git a/module/rdd-bonus.js b/module/rdd-bonus.js index 2516b0ff..2cf31e25 100644 --- a/module/rdd-bonus.js +++ b/module/rdd-bonus.js @@ -24,6 +24,9 @@ export class RdDBonus { } /* -------------------------------------------- */ static isDefenseAttaqueFinesse(rollData) { + if (rollData.isEmpoignade && rollData.rolled?.isPart) { + return true + } return rollData.attackerRoll?.particuliere == 'finesse'; } diff --git a/module/rdd-combat.js b/module/rdd-combat.js index 8689e402..0851b208 100644 --- a/module/rdd-combat.js +++ b/module/rdd-combat.js @@ -12,6 +12,7 @@ import { RdDRollTables } from "./rdd-rolltables.js"; import { ReglesOptionelles } from "./settings/regles-optionelles.js"; import { STATUSES } from "./settings/status-effects.js"; import { Targets } from "./targets.js"; +import { RdDEmpoignade } from "./rdd-empoignade.js"; /* -------------------------------------------- */ const premierRoundInit = [ @@ -57,6 +58,7 @@ export class RdDCombatManager extends Combat { ChatUtility.removeChatMessageContaining(`
`) game.messages.filter(m => ChatUtility.getMessageData(m, 'attacker-roll') != undefined && ChatUtility.getMessageData(m, 'defender-roll') != undefined) .forEach(it => it.delete()); + RdDEmpoignade.deleteAllEmpoignades() } } @@ -91,13 +93,26 @@ export class RdDCombatManager extends Combat { } } else { const armeCombat = combatant.actor.itemTypes['arme'].find(it => it.system.equipe) - const compName = (armeCombat == undefined) ? "Corps à corps" : armeCombat.system.competence; + let compName = "Corps à corps" + if (armeCombat) { + if (armeCombat.system.competence != "") { + compName = armeCombat.system.competence + } + if (armeCombat.system.lancer != "") { + compName = armeCombat.system.lancer + } + if (armeCombat.system.tir != "") { + compName = armeCombat.system.tir + } + } const competence = RdDItemCompetence.findCompetence(combatant.actor.items, compName); - if (competence) { + if (competence && competence.system.defaut_carac) { const carac = combatant.actor.system.carac[competence.system.defaut_carac].value; const niveau = competence.system.niveau; const bonusEcaille = (armeCombat?.system.magique) ? armeCombat.system.ecaille_efficacite : 0; rollFormula = RdDCombatManager.formuleInitiative(2, carac, niveau, bonusEcaille); + } else { + ui.notifications.warn(`Votre arme ${armeCombat.name} n'a pas de compétence renseignée`); } } } @@ -244,7 +259,7 @@ export class RdDCombatManager extends Combat { } else if (actor.isPersonnage()) { // Recupération des items 'arme' const armes = actor.itemTypes['arme'].filter(it => RdDItemArme.isArmeUtilisable(it)) - //.concat(RdDItemArme.empoignade()) + .concat(RdDItemArme.empoignade()) .concat(RdDItemArme.mainsNues()); const competences = actor.itemTypes['competence']; @@ -737,17 +752,13 @@ export class RdDCombat { if (!await this.attacker.accorder(this.defender, 'avant-attaque')) { return; } - if (arme.system.cac == 'empoignade' && this.attacker.isCombatTouche()) { - ChatMessage.create({ - alias: this.attacker.name, - whisper: ChatUtility.getWhisperRecipientsAndGMs(this.attacker.name), - content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-actor-perte-empoignade.html', { - attacker: this.attacker, - competence: competence - }) - }); + if (arme.system.cac == 'empoignade') { + RdDEmpoignade.onAttaqueEmpoignade(this.attacker, this.defender) return; } + if ( this.attacker.isEmpoignadeEnCours() ) { + ui.notifications.warn("Une empoignade est en cours ! Normalement, vous ne pouvez rien faire d'autre que continuer l'empoignade ou la rompre.") + } let rollData = this._prepareAttaque(competence, arme); console.log("RdDCombat.attaque >>>", rollData); diff --git a/module/rdd-empoignade.js b/module/rdd-empoignade.js new file mode 100644 index 00000000..605b6fee --- /dev/null +++ b/module/rdd-empoignade.js @@ -0,0 +1,301 @@ +/* -------------------------------------------- */ +import { RdDCombat } from "./rdd-combat.js"; +import { RdDResolutionTable } from "./rdd-resolution-table.js"; +import { RdDRoll } from "./rdd-roll.js"; +import { RdDItemCompetenceCreature } from "./item-competencecreature.js"; +import { ChatUtility } from "./chat-utility.js"; +import { STATUSES, StatusEffects } from "./settings/status-effects.js"; + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +export class RdDEmpoignade { + + /* -------------------------------------------- */ + static init() { + } + + /* -------------------------------------------- */ + static getEmpoignadeById(actor, id) { + let emp = actor.items.find(emp => emp.type == 'empoignade' && emp.system.empoignadeid == id) + return emp && duplicate(emp) || undefined; + } + + /* -------------------------------------------- */ + static getEmpoignade(attacker, defender) { + let emp = attacker.items.find(emp => emp.type == 'empoignade' && emp.system.empoigneurid == attacker.id && emp.system.empoigneid == defender.id) + if (!emp) { + emp = attacker.items.find(emp => emp.type == 'empoignade' && emp.system.empoigneurid == defender.id && emp.system.empoigneid == attacker.id) + } + if (emp) { + // TODO ? central storage ? + } + return emp && duplicate(emp) || undefined; + } + /* -------------------------------------------- */ + static getMalusTaille(emp, attacker, defender) { + // Si pas empoigné, alors 0 + if (emp.system.pointsemp == 0) { + return 0 + } + // Malus de -1 si différence de taille de 2 ou plus (p 135) + if (attacker.system.carac.taille.value < defender.system.carac.taille.value - 1) { + return attacker.system.carac.taille.value - (defender.system.carac.taille.value - 1) + } + return 0 + } + + /* -------------------------------------------- */ + static async onAttaqueEmpoignadeValidee(attacker, defender) { + let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender) + const isNouvelle = empoignade == undefined; + empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender)) + + let mode = (empoignade && empoignade.system.empoigneurid == attacker.id) ? "empoigner" : "liberer" + + let rollData = { + mode: mode, + isEmpoignade: true, + competence: attacker.getCompetence("Corps à corps"), + selectedCarac: attacker.system.carac.melee, + empoignade: empoignade, + attackerId: attacker.id, + attackerName: attacker.name, + defenderName: defender.name, + defenderId: defender.id, + malusTaille: RdDEmpoignade.getMalusTaille(empoignade, attacker, defender) + } + if (attacker.isCreatureEntite()) { + RdDItemCompetenceCreature.setRollDataCreature(rollData) + } + if (empoignade.system.pointsemp >= 2) { + let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-actions.html'); + ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) + } else { + await RdDEmpoignade.$rollAttaqueEmpoignade(attacker, rollData, isNouvelle); + } + } + + /* -------------------------------------------- */ + static async onAttaqueEmpoignade(attacker, defender) { + let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender) + const isNouvelle = empoignade == undefined; + empoignade = empoignade ?? (await RdDEmpoignade.createEmpoignade(attacker, defender)) + //console.log("W.", empoignade, defender.hasArmeeMeleeEquipee()) + if ((isNouvelle || empoignade.system.pointsemp == 0) && defender.hasArmeeMeleeEquipee()) { + ChatUtility.createChatWithRollMode(attacker.name, { + content: await renderTemplate(`systems/foundryvtt-reve-de-dragon/templates/chat-empoignade-valider.html`, { attacker: attacker, defender: defender }) + }) + } else { + await this.onAttaqueEmpoignadeValidee(attacker, defender) + } + } + + /* -------------------------------------------- */ + static async onAttaqueEmpoignadeFromItem(empoignade) { + let attacker = game.actors.get(empoignade.system.empoigneurid) + let defender = game.actors.get(empoignade.system.empoigneid) + await this.onAttaqueEmpoignadeValidee(attacker, defender) + } + + /* -------------------------------------------- */ + static async $rollAttaqueEmpoignade(attacker, rollData, isNouvelle = false) { + const dialog = await RdDRoll.create(attacker, rollData, + { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' }, + { + name: 'jet-empoignade', + label: 'Empoigner', + callbacks: [ + { condition: r => (r.rolled.isSuccess), action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, true, isNouvelle) }, + { condition: r => (r.rolled.isEchec), action: async (r) => await RdDEmpoignade.$onRollEmpoignade(r, false, isNouvelle) }, + ] + }); + dialog.render(true); + } + + /* -------------------------------------------- */ + static async $onRollEmpoignade(rollData, isSuccess, isNouvelle = false) { + let attacker = game.actors.get(rollData.attackerId) + let defender = game.actors.get(rollData.defenderId) + + let empoignade = rollData.empoignade + empoignade.isSuccess = isSuccess; + + if (isSuccess && isNouvelle) { + // Creer l'empoignade sur attaquant/defenseur + await attacker.createEmbeddedDocuments('Item', [empoignade.toObject()]) + await defender.createEmbeddedDocuments('Item', [empoignade.toObject()]) + } + let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-resultat.html'); + ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) + } + + /* -------------------------------------------- */ + static async onDefenseEmpoignade(rollData, defenseMode, competenceName = "Corps à corps", carac = "melee") { + let attacker = game.actors.get(rollData.attackerId) + let defender = game.actors.get(rollData.defenderId) + let empoignade = this.getEmpoignade(attacker, defender) + + if (!empoignade) { + ui.notifications.warn("Une erreur s'est produite : Aucune empoignade trouvée !!") + return + } + + empoignade = duplicate(empoignade) + rollData.mode = defenseMode + rollData.empoignade = empoignade + rollData.competence = defender.getCompetence(competenceName), + rollData.selectedCarac = defender.system.carac[carac], + rollData.malusTaille = RdDEmpoignade.getMalusTaille(empoignade, defender, attacker) + + await RdDEmpoignade.$rollDefenseEmpoignade(defender, rollData); + } + + /* -------------------------------------------- */ + static async $rollDefenseEmpoignade(defender, rollData) { + const dialog = await RdDRoll.create(defender, rollData, + { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-empoignade.html' }, + { + name: 'empoignade', + label: 'Contrer', + callbacks: [ + { action: async (r) => await RdDEmpoignade.$onRollContrerLiberer(r) } + ] + } + ); + dialog.render(true); + } + + /* -------------------------------------------- */ + static async $onRollContrerLiberer(rollData) { + let empoignade = rollData.empoignade + + if (rollData.mode == "contrer-empoigner" && !rollData.rolled.isSuccess) { + empoignade.system.pointsemp++ + RdDEmpoignade.$updateEtatEmpoignade(empoignade) + } + if (rollData.mode == "contrer-liberer" && !rollData.rolled.isSuccess) { + empoignade.system.pointsemp-- + RdDEmpoignade.$updateEtatEmpoignade(empoignade) + } + + if (empoignade.system.pointsemp >= 2) { + let attacker = game.actors.get(empoignade.system.empoigneurid) + let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-actions.html'); + ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) + } + await RdDResolutionTable.displayRollData(rollData, rollData.defender, 'chat-empoignade-resultat.html') + } + + /* -------------------------------------------- */ + static async $updateEtatEmpoignade(empoignade) { + console.log("UPDATE Empoignade", empoignade) + + let defender = game.actors.get(empoignade.system.empoigneid) + let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid) + let update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol } + await defender.updateEmbeddedDocuments('Item', [update]) + + let attacker = game.actors.get(empoignade.system.empoigneurid) + emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid) + update = { _id: emp._id, "system.pointsemp": empoignade.system.pointsemp, "system.ausol": empoignade.system.ausol } + await attacker.updateEmbeddedDocuments('Item', [update]) + } + + /* -------------------------------------------- */ + static async $deleteEmpoignade(empoignade) { + console.log("DELETE Empoignade", empoignade) + + let defender = game.actors.get(empoignade.system.empoigneid) + let emp = RdDEmpoignade.getEmpoignadeById(defender, empoignade.system.empoignadeid) + await defender.deleteEmbeddedDocuments('Item', [emp._id]) + + let attacker = game.actors.get(empoignade.system.empoigneurid) + emp = RdDEmpoignade.getEmpoignadeById(attacker, empoignade.system.empoignadeid) + await attacker.deleteEmbeddedDocuments('Item', [emp._id]) + } + + /* -------------------------------------------- */ + static async entrainerAuSol(rollData) { + let attacker = game.actors.get(rollData.attackerId) + let defender = game.actors.get(rollData.defenderId) + let empoignade = this.getEmpoignade(attacker, defender) + + empoignade.system.ausol = true + await this.$updateEtatEmpoignade(empoignade) + + await attacker.setEffect(STATUSES.StatusProne, true); + await defender.setEffect(STATUSES.StatusProne, true); + + let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-entrainer-sol.html'); + ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) + } + + /* -------------------------------------------- */ + static async projeterAuSol(rollData) { + let attacker = game.actors.get(rollData.attackerId) + let defender = game.actors.get(rollData.defenderId) + let empoignade = this.getEmpoignade(attacker, defender) + + await defender.setEffect(STATUSES.StatusProne, true); + await this.$deleteEmpoignade(empoignade) + + let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-projeter-sol.html'); + ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) + } + /* -------------------------------------------- */ + static async perteEndurance(rollData, perteMode) { + let attacker = game.actors.get(rollData.attackerId) + let defender = game.actors.get(rollData.defenderId) + let empoignade = this.getEmpoignade(attacker, defender) + + console.log("Perte d'endurance :!!!", perteMode) + let endValue = defender.system.sante.endurance.value + if (perteMode == "end0") { + await defender.santeIncDec("endurance", -defender.system.sante.endurance.value); + } + if (perteMode == "end1") { + await defender.santeIncDec("endurance", -(defender.system.sante.endurance.value - 1)); + } + if (perteMode == "endmoitie") { + await defender.santeIncDec("endurance", -Math.floor(defender.system.sante.endurance.value / 2)); + } + if (perteMode == "endquart") { + await defender.santeIncDec("endurance", -(3 * Math.floor(defender.system.sante.endurance.value / 4))); + } + let msg = await RdDResolutionTable.displayRollData(rollData, attacker, 'chat-empoignade-perte-endurance.html'); + ChatUtility.setMessageData(msg, "empoignade-roll-data", rollData) + } + + /* -------------------------------------------- */ + static async deleteAllEmpoignades() { + for (let actor of game.actors) { + let empList = actor.items.filter(it => it.type == "empoignade") + for (let emp of empList) { + await actor.deleteEmbeddedDocuments('Item', [emp.id]) + } + } + } + + /* -------------------------------------------- */ + static async deleteLinkedEmpoignade(actorId, empoignade) { + let actorDeleteId = (actorId == empoignade.system.empoigneurid) ? empoignade.system.empoigneid : empoignade.system.empoigneurid + let actor = game.actors.get(actorDeleteId) + let emp = this.getEmpoignadeById(actor, empoignade.system.empoignadeid) + if (emp) { + await actor.deleteEmbeddedDocuments('Item', [emp._id]) + } + } + + /* -------------------------------------------- */ + static async createEmpoignade(attacker, defender) { + return await Item.create({ + name: "Empoignade en cours de " + attacker.name + ' sur ' + defender.name, type: 'empoignade', + img: "systems/foundryvtt-reve-de-dragon/icons/entites/possession2.webp", + system: { description: "", empoignadeid: randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name } + }, + { + temporary: true + }) + } +} \ No newline at end of file diff --git a/module/rdd-main.js b/module/rdd-main.js index d8dde122..a45f2a9f 100644 --- a/module/rdd-main.js +++ b/module/rdd-main.js @@ -174,7 +174,7 @@ export class SystemReveDeDragon { "recettealchimique", "musique", "chant", "danse", "jeu", "recettecuisine", "oeuvre", "meditation", "queue", "ombre", "souffle", "tete", "casetmr", "sort", "sortreserve", "nombreastral", "tache", "maladie", "poison", "possession", - "tarot", "extraitpoetique" + "tarot", "extraitpoetique", "empoignade" ], makeDefault: true }); CONFIG.Combat.documentClass = RdDCombatManager; diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 6b2bc787..50e47113 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -15,6 +15,7 @@ import { RdDItemCompetence } from "./item-competence.js"; import { RdDResolutionTable } from "./rdd-resolution-table.js"; import { RdDTimestamp } from "./time/rdd-timestamp.js"; import { RdDRaretes } from "./item/raretes.js"; +import { RdDEmpoignade } from "./rdd-empoignade.js"; /* -------------------------------------------- */ // This table starts at 0 -> niveau -10 @@ -688,6 +689,44 @@ export class RdDUtility { RdDPossession.onDefensePossession(attackerId, defenderId, possessionId) }); + html.on("click", '.defense-empoignade-cac', event => { + const chatMessage = ChatUtility.getChatMessage(event); + const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data'); + let defenseMode = event.currentTarget.attributes['data-defense-mode'].value + RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Corps à corps", "melee") + }); + html.on("click", '.defense-empoignade-esquive', event => { + const chatMessage = ChatUtility.getChatMessage(event); + const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data'); + let defenseMode = event.currentTarget.attributes['data-defense-mode'].value + RdDEmpoignade.onDefenseEmpoignade(rollData, defenseMode, "Esquive", "derobee") + }); + html.on("click", '.empoignade-poursuivre', event => { + let attackerId = event.currentTarget.attributes['data-attackerId'].value + let defenderId = event.currentTarget.attributes['data-defenderId'].value + RdDEmpoignade.onAttaqueEmpoignadeValidee(game.actors.get(attackerId), game.actors.get(defenderId)) + }); + html.on("click", '.empoignade-entrainer-sol', event => { + const chatMessage = ChatUtility.getChatMessage(event); + const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data'); + RdDEmpoignade.entrainerAuSol(rollData) + ChatUtility.removeChatMessageId(chatMessage.id) + }); + html.on("click", '.empoignade-projeter-sol', event => { + const chatMessage = ChatUtility.getChatMessage(event); + const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data'); + RdDEmpoignade.projeterAuSol(rollData) + ChatUtility.removeChatMessageId(chatMessage.id) + }); + html.on("change", '.empoignade-perte-endurance', event => { + const chatMessage = ChatUtility.getChatMessage(event); + const rollData = ChatUtility.getMessageData(chatMessage, 'empoignade-roll-data'); + if (event.currentTarget.value && event.currentTarget.value != "none") { + RdDEmpoignade.perteEndurance(rollData, event.currentTarget.value) + ChatUtility.removeChatMessageId(chatMessage.id) + } + }); + // gestion bouton tchat Acheter html.on("click", '.button-acheter', event => { const venteData = DialogItemAchat.preparerAchat(event.currentTarget); diff --git a/module/rolldata-ajustements.js b/module/rolldata-ajustements.js index dd5d19e4..b42884f4 100644 --- a/module/rolldata-ajustements.js +++ b/module/rolldata-ajustements.js @@ -139,6 +139,12 @@ export const referenceAjustements = { isUsed: (rollData, actor) => rollData.ethylisme != undefined, getLabel: (rollData, actor) => "Ethylisme - " + RdDUtility.getNomEthylisme(rollData.ethylisme), getValue: (rollData, actor) => rollData.ethylisme, + }, + tailleempoignade: { + isVisible: (rollData, actor) => rollData.isEmpoignade, + isUsed: (rollData, actor) => rollData.isEmpoignade, + getLabel: (rollData, actor) => "Malus de taille", + getValue: (rollData, actor) => rollData.malusTaille, } } diff --git a/system.json b/system.json index ad8e69dc..a75597c7 100644 --- a/system.json +++ b/system.json @@ -1,8 +1,8 @@ { "id": "foundryvtt-reve-de-dragon", "title": "Rêve de Dragon", - "version": "10.7.7", - "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.7.7.zip", + "version": "10.7.9", + "download": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/archive/foundryvtt-reve-de-dragon-10.7.8.zip", "manifest": "https://www.uberwald.me/gitea/public/foundryvtt-reve-de-dragon/raw/v10/system.json", "compatibility": { "minimum": "10", diff --git a/template.json b/template.json index 149ba47f..785059c7 100644 --- a/template.json +++ b/template.json @@ -538,7 +538,7 @@ "service", "meditation", "rencontre", "queue", "ombre", "souffle", "tete", "casetmr", "signedraconique", "sort", "sortreserve", "nombreastral", "tache", "blessure", "maladie", "poison", "possession", - "tarot", "extraitpoetique" + "tarot", "extraitpoetique", "empoignade" ], "templates": { "description": { @@ -596,6 +596,16 @@ "ispossession": false, "dommages": 0 }, + "empoignade": { + "templates": ["description"], + "empoignadeid": "", + "empoigneurid": "", + "empoigneid": "", + "pointsemp": 0, + "empoigneurname": "", + "empoignename": "", + "ausol": false + }, "possession": { "templates": ["description"], "typepossession": "", diff --git a/templates/actor/combat.html b/templates/actor/combat.html index b7fed5fd..b8edb5ab 100644 --- a/templates/actor/combat.html +++ b/templates/actor/combat.html @@ -40,4 +40,29 @@ {{/each}} + + + \ No newline at end of file diff --git a/templates/chat-empoignade-actions.html b/templates/chat-empoignade-actions.html new file mode 100644 index 00000000..85688188 --- /dev/null +++ b/templates/chat-empoignade-actions.html @@ -0,0 +1,39 @@ + +

+ {{attackerName}} a empoigné {{defenderName}} +

+
+
+ + + Au round suivant l'acquisition des 2 points d'Emp, {{attackerName}} peut faire perdre autant de points d'Endurance qu'il souhaite à {{defenderName}} +
+ + + + + {{#if empoignade.system.ausol}} + + {{else}} +
+ Dès l'acquisition des 2 points d'Emp, {{attackerName}} peut entraîner {{defenderName}} au sol. Les deux protagonistes restent empoignés. +
+ + Entraîner au sol + +
+ A la fin du round ou les 2 points d'Emp sont acquis, {{attackerName}} peut projeter {{defenderName}} au sol. Les deux protagonistes ne sont plus empoignés. +
+ + Projeter au sol + + {{/if}} + + +
diff --git a/templates/chat-empoignade-entrainer-sol.html b/templates/chat-empoignade-entrainer-sol.html new file mode 100644 index 00000000..2185877a --- /dev/null +++ b/templates/chat-empoignade-entrainer-sol.html @@ -0,0 +1,7 @@ + +

+ {{attackerName}} a entraîné {{defenderName}} au sol. L'empoignade peut continuer. +

+
+
+
diff --git a/templates/chat-empoignade-perte-endurance.html b/templates/chat-empoignade-perte-endurance.html new file mode 100644 index 00000000..c83803e7 --- /dev/null +++ b/templates/chat-empoignade-perte-endurance.html @@ -0,0 +1,7 @@ + +

+ {{attackerName}} a fait perdre de l'endurance à {{defenderName}}, qui reste immobilisé. L'empoignade peut continuer. +

+
+
+
diff --git a/templates/chat-empoignade-projeter-sol.html b/templates/chat-empoignade-projeter-sol.html new file mode 100644 index 00000000..7d937191 --- /dev/null +++ b/templates/chat-empoignade-projeter-sol.html @@ -0,0 +1,7 @@ + +

+ {{attackerName}} a projeté {{defenderName}} au sol. L'empoignade est terminée et a été supprimée. +

+
+
+
diff --git a/templates/chat-empoignade-resultat.html b/templates/chat-empoignade-resultat.html new file mode 100644 index 00000000..cacb36c4 --- /dev/null +++ b/templates/chat-empoignade-resultat.html @@ -0,0 +1,85 @@ + +

+ {{#if (eq mode "empoigner")}} + {{attackerName}} tente d'empoigner {{defenderName}} + {{/if}} + {{#if (eq mode "contrer-empoigner")}} + {{defenderName}} tente de contrer l'empoignade de {{attackerName}} + {{/if}} + {{#if (eq mode "liberer")}} + {{attackerName}} tente de se libérer de l'empoignade de {{defenderName}} + {{/if}} + {{#if (eq mode "contrer-liberer")}} + {{defenderName}} tente de contrer la libération de {{attackerName}} + {{/if}} +

+{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.html"}} + +
+
+ + {{#if (gte empoignade.system.pointsemp 2)}} + +
{{defenderName}} est empoigné et immobilisé par {{attackerName}} ! + + {{else}} + +
+ + {{#if (eq mode "empoigner")}} + {{#if empoignade.isSuccess}} + + Contrer l'empoignade (Corps à Corps) + + {{#if (eq empoignade.system.pointsemp 0)}} + + Contrer l'empoignade (Esquive) + + {{/if}} + {{else}} + La Tentative d'empoignade a échoué ! + {{/if}} + {{/if}} + + {{#if (eq mode "liberer")}} + {{#if empoignade.isSuccess}} + + Contrer la libération (Corps à Corps) + + {{else}} + La Tentative de libération a échouée ! + {{/if}} + {{/if}} + + {{#if (eq mode "contrer-empoigner")}} + {{#if rolled.isSuccess}} + La tentative de contrer l'empoignade est un succès! + {{else}} + La tentative de contrer l'empoignade est un échec! + {{/if}} + {{/if}} + + {{#if (eq mode "contrer-liberer")}} + {{#if rolled.isSuccess}} + La tentative de contrer la libération est un succès! + {{else}} + La tentative de contrer la libération est un échec! + {{/if}} + {{/if}} + +
Points d'Emp: {{empoignade.system.pointsemp}} + + {{/if}} +
diff --git a/templates/chat-empoignade-valider.html b/templates/chat-empoignade-valider.html new file mode 100644 index 00000000..b597f976 --- /dev/null +++ b/templates/chat-empoignade-valider.html @@ -0,0 +1,20 @@ + +

+ {{attackerName}} tente d'empoigner {{defenderName}} +

+
+
+ + +
+ {{attacker.name}} tente d'empoigner {{defender.name}}, qui est équipé d'une arme de mêlée. {{defender.name}} + a automatiquement l'initiative sur {{attacker.name}}, et bénéficie d'un bonus de +4 à son attaque. Assurez vous + d'avoir effectué cette attaque avant de poursuivre l'empoignade. Ce cas s'applique également si {{defender.name}} + combat à mains nues. + + + Poursuivre l'empoignade + + +
\ No newline at end of file diff --git a/templates/dialog-roll-defense-empoignade.html b/templates/dialog-roll-defense-empoignade.html new file mode 100644 index 00000000..f810a9ea --- /dev/null +++ b/templates/dialog-roll-defense-empoignade.html @@ -0,0 +1,22 @@ +
+

+ {{defenderName}} tente de contrer l'empoignade de {{attackerName}} +

+
+
+ {{competence.name}} +
+ + +
+
+
+ {{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffFixe.html"}} + {{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-diffCondition.html"}} + {{>"systems/foundryvtt-reve-de-dragon/templates/partial-roll-forcer.html"}} +
+
+
+ +
+
diff --git a/templates/item-empoignade-sheet.html b/templates/item-empoignade-sheet.html new file mode 100644 index 00000000..aff87868 --- /dev/null +++ b/templates/item-empoignade-sheet.html @@ -0,0 +1,11 @@ +
+ {{>"systems/foundryvtt-reve-de-dragon/templates/header-item.html"}} + {{!-- Sheet Body --}} +
+
+ + +
+ {{>"systems/foundryvtt-reve-de-dragon/templates/partial-item-description.html"}} +
+