diff --git a/assets/actions/empoignade.svg b/assets/actions/empoignade.svg
new file mode 100644
index 00000000..3385ffc2
--- /dev/null
+++ b/assets/actions/empoignade.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/ui/encaisser.svg b/assets/actions/encaisser.svg
similarity index 100%
rename from assets/ui/encaisser.svg
rename to assets/actions/encaisser.svg
diff --git a/changelog.md b/changelog.md
index c995eef6..4045f266 100644
--- a/changelog.md
+++ b/changelog.md
@@ -8,8 +8,18 @@
- l'encaissement indique une blessure dans le tchat... même si ce n'est que de l'endurance
- les blurettes suivent les règles des entités de cauchemar (p322)
- Nouvelle fenêtre de jets de dés
- - Attaque/défense des créatures
+ - attaque/défense des créatures
- les attaques/parades avec une arme trop lourde se font en demi-surprise
+ - les demandes de défense disparaîssent une fois prises en compte
+ - empoignade
+ - l'empoignade est possible avec une initiative d'empoignade, ou en cours d'empoignade
+ - seule la dague, le pugilat et la dague sont possibles en cours d'empoignade
+ - jet de Dextérité/Dague pour utiliser la dague en cours d'empoignade (p136)
+ - attaquer avec une arme un empoigneur donne un +4 si pas d'empoignade (p134)
+ - la différence de taille donne un bonus/malus en cours d'empoignade (p135)
+ - les dommages de l'empoignade ajoutent/enlèvent un point d'empoignade
+ - le statut d'empoignade est affiché sur les tokens
+ - les défenses contre une empoignade sont corrigées
## 13.0.13 - L'épanouissement d'Illysis
diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css
index a7739aef..cc54159d 100644
--- a/css/foundryvtt-reve-de-dragon.css
+++ b/css/foundryvtt-reve-de-dragon.css
@@ -648,15 +648,19 @@ select,
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="coeur"] select[name="coeur"] {
max-width: 4rem;
}
-.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="tricher"] img {
- /* image de d100 */
- max-width: 2.5rem;
- max-height: 2.5rem;
+.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section img {
+ max-width: 1rem;
+ max-height: 1rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
+.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions roll-section[name="tricher"] img {
+ /* image de d100 */
+ max-width: 2.5rem;
+ max-height: 2.5rem;
+}
.system-foundryvtt-reve-de-dragon .roll-dialog roll-buttons {
display: flex;
flex-direction: row-reverse;
diff --git a/less/roll-dialog.less b/less/roll-dialog.less
index 567c0f33..cb995f33 100644
--- a/less/roll-dialog.less
+++ b/less/roll-dialog.less
@@ -224,15 +224,19 @@
max-width: 4rem;
}
- roll-conditions roll-section[name="tricher"] img {
- /* image de d100 */
- max-width: 2.5rem;
- max-height: 2.5rem;
+ roll-conditions roll-section img {
+ max-width: 1rem;
+ max-height: 1rem;
gap: 0;
margin: 0;
padding: 0;
filter: invert(0.8);
}
+ roll-conditions roll-section[name="tricher"] img {
+ /* image de d100 */
+ max-width: 2.5rem;
+ max-height: 2.5rem;
+ }
roll-buttons {
display: flex;
diff --git a/module/actor.js b/module/actor.js
index 24368810..98b67afd 100644
--- a/module/actor.js
+++ b/module/actor.js
@@ -185,7 +185,7 @@ export class RdDActor extends RdDBaseActorSang {
const actions = []
const uniques = []
- const addAttaque = (arme, main = undefined, action = 'attaque') => {
+ const addAttaque = (arme, main = undefined) => {
const dommages = RdDItemArme.valeurMain(arme.system.dommages, main)
const forceRequise = RdDItemArme.valeurMain(arme.system.force ?? 0, main)
const ecaillesEfficacite = arme.system.magique ? arme.system.ecaille_efficacite : 0;
@@ -218,6 +218,7 @@ export class RdDActor extends RdDBaseActorSang {
})
}
+ addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS)
this.itemTypes[ITEM_TYPES.arme]
.filter(it => it.isAttaque())
.sort(Misc.ascending(it => it.name))
@@ -227,9 +228,7 @@ export class RdDActor extends RdDBaseActorSang {
if (arme.system.lancer && arme.system.resistance > 0) { addAttaque(arme, ATTAQUE_TYPE.LANCER) }
if (arme.system.tir) { addAttaque(arme, ATTAQUE_TYPE.TIR) }
})
-
- addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS)
- addAttaque(RdDItemArme.empoignade(this), ATTAQUE_TYPE.CORPS_A_CORPS, 'empoignade')
+ addAttaque(RdDItemArme.pugilat(this), ATTAQUE_TYPE.CORPS_A_CORPS)
return actions
}
@@ -3040,6 +3039,8 @@ export class RdDActor extends RdDBaseActorSang {
await this.onDeleteOwnedCaseTmr(item, options, id)
break
case ITEM_TYPES.empoignade:
+ await this.setEffect(STATUSES.StatusGrappled, false)
+ await this.setEffect(STATUSES.StatusGrappling, false)
await RdDEmpoignade.deleteLinkedEmpoignade(this.id, item)
break
}
diff --git a/module/actor/base-actor-reve.js b/module/actor/base-actor-reve.js
index 6379cfd7..92a60e9d 100644
--- a/module/actor/base-actor-reve.js
+++ b/module/actor/base-actor-reve.js
@@ -126,6 +126,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
+ computeResumeBlessure() {}
+ countBlessures(filter = it => !it.isContusion()) { return 0 }
async santeIncDec(name, inc, isCritique = false) { }
async finDeRound(options = { terminer: false }) {
@@ -204,6 +206,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
getPossession(possessionId) {
return this.itemTypes[ITEM_TYPES.possession].find(it => it.system.possessionid == possessionId);
}
+
getEmpoignades() {
return this.itemTypes[ITEM_TYPES.empoignade];
}
@@ -245,7 +248,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
async setEffect(statusId, status) {
if (this.isEffectAllowed(statusId)) {
- const effect = this.getEffectByStatus(statusId);
+ const effect = this.getEffectByStatus(statusId)
if (!status && effect) {
await this.deleteEmbeddedDocuments('ActiveEffect', [effect.id], { render: true })
}
diff --git a/module/constants.js b/module/constants.js
index 37e45b6f..fbd60dc0 100644
--- a/module/constants.js
+++ b/module/constants.js
@@ -60,7 +60,7 @@ export const RDD_CONFIG = {
icons: {
armesDisparates: 'systems/foundryvtt-reve-de-dragon/assets/actions/armes-disparates.svg',
demiReve: 'systems/foundryvtt-reve-de-dragon/assets/actions/sort.svg',
- empoignade: 'systems/foundryvtt-reve-de-dragon/icons/empoignade.webp',
+ empoignade: 'systems/foundryvtt-reve-de-dragon/assets/actions/empoignade.svg',
forceWeak: 'systems/foundryvtt-reve-de-dragon/assets/actions/weak.svg',
},
encaissement: {
diff --git a/module/initiative.mjs b/module/initiative.mjs
index 4ddf5c10..d1ceed40 100644
--- a/module/initiative.mjs
+++ b/module/initiative.mjs
@@ -58,6 +58,7 @@ export class RdDInitiative {
return {
roll: roll,
value: value,
+ rang: formule.phase.rang,
init: formule.phase.rang + value / 100,
label: formule.phase.label
}
diff --git a/module/item/arme.js b/module/item/arme.js
index bd29ead1..74ff0d35 100644
--- a/module/item/arme.js
+++ b/module/item/arme.js
@@ -27,6 +27,7 @@ export const ATTAQUE_TYPE = {
TIR: '(tir)',
LANCER: '(lancer)'
}
+export const ATTAQUE_TYPE_MELEE = [ATTAQUE_TYPE.UNE_MAIN, ATTAQUE_TYPE.DEUX_MAINS, ATTAQUE_TYPE.CORPS_A_CORPS]
export const CORPS_A_CORPS = 'Corps à corps'
export const PUGILAT = 'pugilat'
@@ -254,6 +255,14 @@ export class RdDItemArme extends RdDItem {
return this.system.resistance > 0 || (this.system.tir != '' && this.system.portee_courte > 0)
}
+ isEmpoignade() {
+ return this.system.mortalite == RDD_CONFIG.encaissement.empoignade
+ }
+
+ isUtilisableEmpoigne() {
+ return this.system.baseInit == 3 || this.system.baseInit == 4 || this.system.competence == "Dague"
+ }
+
static pugilat(actor) {
return RdDItemArme.$corpsACorps(actor, 'Pugilat', PUGILAT)
}
diff --git a/module/rdd-bonus.js b/module/rdd-bonus.js
index 42d0f812..be0efff0 100644
--- a/module/rdd-bonus.js
+++ b/module/rdd-bonus.js
@@ -74,6 +74,7 @@ export class RdDBonus {
dmgForceInsuffisante: Math.min(0, actor.getForce() - (attaque.forceRequise ?? 0)),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0
}
+ dmg.isEmpoignade = dmg.mortalite == RDD_CONFIG.encaissement.empoignade
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante + dmg.dmgDiffLibre
return dmg
}
diff --git a/module/rdd-combat.js b/module/rdd-combat.js
index 72d10848..676f6a13 100644
--- a/module/rdd-combat.js
+++ b/module/rdd-combat.js
@@ -56,6 +56,12 @@ export class RdDCombatManager extends Combat {
}
}
+
+ static getCombatant(actorId, tokenId) {
+ return game.combat.combatants.find(it => it.actor.id == actorId &&
+ it.token.id == tokenId
+ );
+ }
/* -------------------------------------------- */
async nextRound() {
await this.finDeRound();
@@ -402,7 +408,6 @@ export class RdDCombat {
/* -------------------------------------------- */
static registerChatCallbacks(html) {
for (let button of [
- '.button-defense',
'.button-parade',
'.button-esquive',
'.button-encaisser',
@@ -474,8 +479,6 @@ export class RdDCombat {
switch (button) {
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
- case '.button-defense': return this.defenseV2(attackerRoll);
-
case '.button-parade': return this.parade(attackerRoll, armeParadeId);
case '.button-esquive': return this.esquive(attackerRoll, compId, competence);
case '.button-encaisser': return this.encaisser(attackerRoll, defenderRoll);
@@ -725,24 +728,24 @@ export class RdDCombat {
async _chatMessageDefenseV2(paramDemandeDefense) {
const attackerRoll = paramDemandeDefense.attackerRoll;
RollBasicParts.loadSurprises(attackerRoll)
- attackerRoll.passeArme = attackerRoll.passeArme ?? foundry.utils.randomID(16)
attackerRoll.dmg = RdDBonus.dmgRollV2(attackerRoll, attackerRoll.current.attaque)
- const attaque = RollDialog.saveParts(attackerRoll)
- const defense = {
- attackerRoll: attaque,
- ids: RollBasicParts.reverseIds(attaque),
- passeArme: attaque.passeArme ?? foundry.utils.randomID(16)
- }
+
+ const defenseData = RollBasicParts.prepareDefense(attackerRoll)
const choixDefense = await ChatMessage.create({
// message privé: du défenseur à lui même (et aux GMs)
speaker: ChatMessage.getSpeaker(this.defender, canvas.tokens.get(this.defenderTokenId)),
alias: this.attacker?.getAlias(),
whisper: ChatUtility.getOwners(this.defender),
- content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', attackerRoll)
+ content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-demande-defense.hbs', defenseData)
});
// flag pour garder les jets d'attaque/defense
- ChatUtility.setMessageData(choixDefense, 'rollData', defense)
+ ChatUtility.setMessageData(choixDefense, 'demande-defense', true)
+ ChatUtility.setMessageData(choixDefense, 'rollData', {
+ ids: defenseData.ids,
+ attackerRoll: RollDialog.saveParts(attackerRoll),
+ passeArme: defenseData.passeArme
+ })
}
/* -------------------------------------------- */
@@ -1052,7 +1055,7 @@ export class RdDCombat {
dialog.render(true);
}
- async defenseV2(attackerRoll) {
+ async defenseV2(attackerRoll, callbacks = []) {
// this._prepareParade(attackerRoll, arme, competence);
RollDialog.loadRollData(attackerRoll)
await this.doRollDefense({
@@ -1065,7 +1068,7 @@ export class RdDCombat {
type: { allowed: [ROLL_TYPE_DEFENSE], current: ROLL_TYPE_DEFENSE },
attackerRoll: attackerRoll,
passeArme: attackerRoll.passeArme,
- })
+ }, callbacks)
}
async doRollDefense(rollData, callbacks = []) {
diff --git a/module/rdd-empoignade.js b/module/rdd-empoignade.js
index dbdf0208..4536dac6 100644
--- a/module/rdd-empoignade.js
+++ b/module/rdd-empoignade.js
@@ -4,6 +4,8 @@ import { ChatUtility } from "./chat-utility.js";
import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDRoll } from "./rdd-roll.js";
import { MappingCreatureArme } from "./item/mapping-creature-arme.mjs";
+import { MAP_PHASE } from "./initiative.mjs";
+import { RdDCombatManager } from "./rdd-combat.js";
/* -------------------------------------------- */
export class RdDEmpoignade {
@@ -12,6 +14,49 @@ export class RdDEmpoignade {
static init() {
}
+ /* -------------------------------------------- */
+ static isCombatantEmpoignade(actorId, tokenId) {
+ const combatant = RdDCombatManager.getCombatant(actorId, tokenId)
+ return MAP_PHASE.empoignade.rang == combatant?.system.init.rang
+ }
+
+ static async ajustementEmpoignade(attacker, defender, adjust = 1) {
+ const empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
+ const empId = empoignade?.system.empoignadeid ?? foundry.utils.randomID(16)
+ if (empoignade) {
+ if (empoignade.system.empoigneurid == defender.id) {
+ adjust = - adjust
+ }
+ empoignade.system.pointsemp += adjust
+ await RdDEmpoignade.$updateEtatEmpoignade(empoignade, attacker, defender)
+ }
+ else {
+ await RdDEmpoignade.$createEtatEmpoignade({
+ name: `Empoignade de ${attacker.name} sur ${defender.name}`,
+ type: ITEM_TYPES.empoignade,
+ system: {
+ description: "",
+ empoignadeid: empId,
+ empoigneurid: attacker.id,
+ empoigneid: defender.id,
+ pointsemp: adjust,
+ empoigneurname: attacker.name,
+ empoignename: defender.name
+ }
+ }, attacker, defender)
+ }
+ const result = RdDEmpoignade.getEmpoignadeById(defender, empId);
+ const defGrappled = result.system.pointsemp == (result.system.empoigneid == defender.id ? 2 : -2)
+ const attGrappled = result.system.pointsemp == (result.system.empoigneurid == attacker.id ? -2 : 2)
+ const grappling = Math.abs(result.system.pointsemp) > 0
+ await defender.setEffect(STATUSES.StatusGrappling, grappling && !defGrappled)
+ await attacker.setEffect(STATUSES.StatusGrappling, grappling && !attGrappled)
+ await defender.setEffect(STATUSES.StatusGrappled, defGrappled)
+ await attacker.setEffect(STATUSES.StatusGrappled, attGrappled)
+ return result
+ }
+
+
/* -------------------------------------------- */
static registerChatCallbacks(html) {
$(html).on("click", '.defense-empoignade-cac', event => {
@@ -237,10 +282,7 @@ export class RdDEmpoignade {
if (rollData.rolled.isSuccess && isNouvelle) {
- const objectEmpoignade = rollData.empoignade.toObject();
- // Creer l'empoignade sur attaquant/defenseur
- attacker.creerObjetParMJ(objectEmpoignade);
- defender.creerObjetParMJ(objectEmpoignade);
+ RdDEmpoignade.$createEtatEmpoignade(rollData.empoignade)
}
rollData.empoignade.isSuccess = rollData.rolled.isSuccess;
@@ -259,7 +301,7 @@ export class RdDEmpoignade {
return
}
- let empoignade = this.getEmpoignade(attacker, defender)
+ let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
if (!empoignade) {
ui.notifications.warn("Une erreur s'est produite : Aucune empoignade trouvée !!")
@@ -317,18 +359,33 @@ export class RdDEmpoignade {
}
/* -------------------------------------------- */
- static async $updateEtatEmpoignade(empoignade) {
- console.log("UPDATE Empoignade", empoignade)
+ static async $createEtatEmpoignade(empoignade) {
+ console.log("CREATE 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])
+
+ // Creer l'empoignade sur attaquant/defenseur
+ await attacker.creerObjetParMJ(empoignade)
+ await defender.creerObjetParMJ(empoignade)
+ }
+
+ /* -------------------------------------------- */
+ static async $updateEtatEmpoignade(empoignade, attacker, defender) {
+ console.log("UPDATE Empoignade", empoignade)
+ const belligerants = [
+ attacker ?? game.actors.get(empoignade.system.empoigneurid),
+ defender ?? game.actors.get(empoignade.system.empoigneid)]
+
+ await Promise.all(
+ belligerants.map(async belligerant => {
+ const emp = RdDEmpoignade.getEmpoignadeById(belligerant, empoignade.system.empoignadeid)
+ return await belligerant.updateEmbeddedDocuments('Item', [{
+ _id: emp._id,
+ "system.pointsemp": empoignade.system.pointsemp,
+ "system.ausol": empoignade.system.ausol
+ }])
+ }))
}
/* -------------------------------------------- */
@@ -347,7 +404,7 @@ export class RdDEmpoignade {
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
- let empoignade = this.getEmpoignade(attacker, defender)
+ let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
empoignade.system.ausol = true
await this.$updateEtatEmpoignade(empoignade)
@@ -366,7 +423,7 @@ export class RdDEmpoignade {
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
- let empoignade = this.getEmpoignade(attacker, defender)
+ let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
await defender.setEffect(STATUSES.StatusProne, true);
await this.$deleteEmpoignade(empoignade)
@@ -382,7 +439,7 @@ export class RdDEmpoignade {
if (!RdDEmpoignade.isActionAutorisee("immobilise", attacker, defender)) {
return
}
- let empoignade = this.getEmpoignade(attacker, defender)
+ let empoignade = RdDEmpoignade.getEmpoignade(attacker, defender)
//console.log("Perte d'endurance :!!!", perteMode)
let endValue = defender.system.sante.endurance.value
@@ -423,9 +480,17 @@ export class RdDEmpoignade {
/* -------------------------------------------- */
static async createEmpoignade(attacker, defender) {
return await Item.create({
- name: "Empoignade en cours de " + attacker.name + ' sur ' + defender.name,
- type: 'empoignade',
- system: { description: "", empoignadeid: foundry.utils.randomID(16), compteempoigne: 0, empoigneurid: attacker.id, empoigneid: defender.id, ptsemp: 0, empoigneurname: attacker.name, empoignename: defender.name }
+ name: "Empoignade de " + attacker.name + ' sur ' + defender.name,
+ type: ITEM_TYPES.empoignade,
+ system: {
+ description: "",
+ empoignadeid: foundry.utils.randomID(16),
+ empoigneurid: attacker.id,
+ empoigneid: defender.id,
+ pointsemp: 0,
+ empoigneurname: attacker.name,
+ empoignename: defender.name
+ }
},
{
temporary: true
diff --git a/module/rdd-roll.js b/module/rdd-roll.js
index 3c2bd93b..5eeda886 100644
--- a/module/rdd-roll.js
+++ b/module/rdd-roll.js
@@ -334,7 +334,7 @@ export class RdDRoll extends Dialog {
// Mise à jour valeurs
this.html.find(".dialog-roll-title").text(this._getTitle(rollData));
this.html.find("input.check-mortalite").prop('checked', rollData.dmg.mortalite == RDD_CONFIG.encaissement.nonmortel);
- this.html.find("label.dmg-arme-actor").text(rollData.dmg.mortalite == EMPOIGNADE ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total));
+ this.html.find("label.dmg-arme-actor").text(rollData.dmg.isEmpoignade ? EMPOIGNADE : Misc.toSignedString(rollData.dmg.total));
this.html.find("label.arme-mortalite").text(rollData.dmg.mortalite);
this.html.find("div.placeholder-ajustements").empty().append(adjustements);
this.html.find("div.placeholder-resolution").empty().append(resolutionTable)
diff --git a/module/roll/chat-roll-result.mjs b/module/roll/chat-roll-result.mjs
index d64212bd..6a90fb2d 100644
--- a/module/roll/chat-roll-result.mjs
+++ b/module/roll/chat-roll-result.mjs
@@ -5,13 +5,13 @@ import { RdDCombat } from "../rdd-combat.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { RdDResolutionTable } from "../rdd-resolution-table.js"
import { RDD_CONFIG, renderTemplate } from "../constants.js"
-import { EMPOIGNADE } from "../item/arme.js"
import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js"
import { RollTypeCuisine } from "./roll-type-cuisine.mjs"
import { RollTypeMeditation } from "./roll-type-meditation.mjs"
import { PART_DEFENSE } from "./roll-part-defense.mjs"
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
import { RdDRollTables } from "../rdd-rolltables.js"
+import { RdDEmpoignade } from "../rdd-empoignade.js"
export default class ChatRollResult {
static init() {
@@ -67,7 +67,7 @@ export default class ChatRollResult {
isShowEncaissement(roll) {
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
- return roll.rolled.isEchec && roll.attackerRoll?.dmg.mortalite != EMPOIGNADE
+ return roll.rolled.isEchec
}
return false
}
@@ -132,6 +132,7 @@ export default class ChatRollResult {
async chatListeners(html) {
$(html).on("click", '.appel-chance', event => this.onClickAppelChance(event))
$(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event))
+ $(html).on("click", '.button-defense', event => this.onClickDefense(event))
$(html).on("click", '.encaissement', event => this.onClickEncaissement(event))
$(html).on("click", '.resister-recul', event => this.onClickRecul(event))
$(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event))
@@ -219,18 +220,41 @@ export default class ChatRollResult {
})
}
- async onClickEncaissement(event) {
+ async onClickDefense(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = this.loadChatMessageRoll(chatMessage)
+ const attackerRoll = savedRoll.attackerRoll
+ this.getCombat(attackerRoll)?.defenseV2(attackerRoll,
+ [roll => { ChatUtility.removeChatMessageId(chatMessage.id) }]
+ )
+ }
+
+ async onClickEncaissement(event) {
+ const chatMessage = ChatUtility.getChatMessage(event)
+ const isMessageDemande = ChatUtility.getMessageData(chatMessage, 'demande-defense')
+ const savedRoll = this.loadChatMessageRoll(chatMessage)
const attaque = savedRoll.attackerRoll
- const defender = game.actors.get(savedRoll.ids.actorId)
- const attacker = game.actors.get(savedRoll.ids.opponentId)
const defenderToken = savedRoll.ids.actorTokenId ? canvas.tokens.get(savedRoll.ids.actorTokenId) : undefined
const attackerToken = savedRoll.ids.opponentTokenId ? canvas.tokens.get(savedRoll.ids.opponentTokenId) : undefined
- await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
-
- savedRoll.done.encaissement = true
- await this.updateChatMessage(chatMessage, savedRoll)
+ switch (attaque.dmg.mortalite) {
+ case RDD_CONFIG.encaissement.empoignade:
+ savedRoll.done = savedRoll.done ?? {}
+ savedRoll.done.empoignade = await RdDEmpoignade.ajustementEmpoignade(attackerToken.actor, defenderToken.actor)
+ break
+ case RDD_CONFIG.encaissement.entiteincarnee:
+ case RDD_CONFIG.encaissement.nonmortel:
+ case RDD_CONFIG.encaissement.mortel:
+ const defender = defenderToken?.actor ?? game.actors.get(savedRoll.ids.actorId)
+ const attacker = attackerToken?.actor ?? game.actors.get(savedRoll.ids.opponentId)
+ await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
+ break
+ }
+ if (isMessageDemande) {
+ ChatUtility.removeChatMessageId(chatMessage.id)
+ } else {
+ savedRoll.done.encaissement = true
+ await this.updateChatMessage(chatMessage, savedRoll)
+ }
}
async onClickRecul(event) {
diff --git a/module/roll/roll-basic-parts.mjs b/module/roll/roll-basic-parts.mjs
index 8e5365e2..d8c9c51b 100644
--- a/module/roll/roll-basic-parts.mjs
+++ b/module/roll/roll-basic-parts.mjs
@@ -54,13 +54,27 @@ export class RollBasicParts {
}
}
+ static prepareDefense(attackerRoll) {
+ if (!attackerRoll.passeArme) {
+ attackerRoll.passeArme = foundry.utils.randomID(16);
+ }
+ return {
+ ids: RollBasicParts.reverseIds(attackerRoll),
+ active: attackerRoll.opponent,
+ opponent: attackerRoll.active,
+ attackerRoll: attackerRoll,
+ passeArme: attackerRoll.passeArme,
+ show: { encaissement: true }
+ }
+ }
+
static reverseIds(rollData) {
return {
sceneId: rollData.ids.sceneId,
actorId: rollData.ids.opponentId,
actorTokenId: rollData.ids.opponentTokenId,
opponentId: rollData.ids.actorId,
- opponentTokenId: rollData.actorTokenId
+ opponentTokenId: rollData.ids.actorTokenId
}
}
diff --git a/module/roll/roll-dialog-adapter.mjs b/module/roll/roll-dialog-adapter.mjs
index 809b3258..dc653679 100644
--- a/module/roll/roll-dialog-adapter.mjs
+++ b/module/roll/roll-dialog-adapter.mjs
@@ -124,7 +124,7 @@ export class RollDialogAdapter {
const attaque = rollData.current.attaque;
const choix = []
- const isEmpoignade = attaque.dmg.mortalite == 'empoignade';
+ const isEmpoignade = attaque.dmg.isEmpoignade
const isCharge = attaque.tactique == 'charge'
/* TODO: cas de créatures faisant des lancers, Glou, Glipzouk */
const isMeleeDiffNegative = (attaque.comp.type == ITEM_TYPES.competencecreature || rollData.current.carac.key == CARACS.MELEE)
diff --git a/module/roll/roll-dialog.mjs b/module/roll/roll-dialog.mjs
index d2eefe70..0797a68a 100644
--- a/module/roll/roll-dialog.mjs
+++ b/module/roll/roll-dialog.mjs
@@ -44,6 +44,8 @@ import { RollTypeCuisine } from "./roll-type-cuisine.mjs";
import { RollPartCuisine } from "./roll-part-cuisine.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "../settings/options-avancees.js";
import { ActorImpacts } from "../technical/actor-impacts.mjs";
+import { RollPartEmpoignade } from "./roll-part-empoignade.mjs";
+import { RollPartEmpoignadeTaille } from "./roll-part-empoignade-taille.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
@@ -85,6 +87,8 @@ const ROLL_PARTS = [
new RollPartConditions(),
new RollPartEthylisme(),
new RollPartMalusArmure(),
+ new RollPartEmpoignadeTaille(),
+ new RollPartEmpoignade(),
new RollPartEncTotal(),
new RollPartSurEnc(),
new RollPartAppelMoral(),
diff --git a/module/roll/roll-part-attaque.mjs b/module/roll/roll-part-attaque.mjs
index 20b0c52b..6ca3827a 100644
--- a/module/roll/roll-part-attaque.mjs
+++ b/module/roll/roll-part-attaque.mjs
@@ -1,6 +1,10 @@
import { RDD_CONFIG } from "../constants.js"
+import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
import { RdDBonus } from "../rdd-bonus.js"
-import { DIFF, ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"
+import { CARACS } from "../rdd-carac.js"
+import { RdDEmpoignade } from "../rdd-empoignade.js"
+import { DIFF, ROLL_TYPE_ATTAQUE, ROLL_TYPE_COMP } from "./roll-constants.mjs"
+import RollDialog from "./roll-dialog.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs"
import { PART_DIFF } from "./roll-part-diff.mjs"
@@ -12,6 +16,10 @@ export const PART_ATTAQUE = 'attaque'
const TACTIQUES = RdDBonus.tactiques.filter(it => it.isTactique)
+const FILTER_ATTAQUE_EMPOIGNADE = attaque => attaque.arme.isEmpoignade()
+const FILTER_ATTAQUE_NON_EMPOIGNADE = attaque => !attaque.arme.isEmpoignade()
+const FILTER_ATTAQUE_EMPOIGNE = attaque => attaque.arme.isUtilisableEmpoigne() && ATTAQUE_TYPE_MELEE.includes(attaque.main)
+
export class RollPartAttaque extends RollPartSelect {
get code() { return PART_ATTAQUE }
@@ -22,7 +30,8 @@ export class RollPartAttaque extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const attaques = rollData.active.actor.listAttaques()
- refs.attaques = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor))
+ refs.all = attaques.map(it => RollPartAttaque.$extractAttaque(it, rollData.active.actor))
+ this.filterAttaquesEmpoignade(rollData)
refs.tactiques = TACTIQUES
if (refs.attaques.length > 0) {
const attaque = this.findAttaque(refs.attaques, this.getSaved(rollData))
@@ -30,6 +39,10 @@ export class RollPartAttaque extends RollPartSelect {
}
}
+ isAttaqueEmpoignade(it) {
+ return it.arme.isEmpoignade()
+ }
+
store(rollData, targetData) {
super.store(rollData, targetData)
this.getSaved(targetData).dmg = this.getCurrent(rollData).dmg
@@ -59,10 +72,23 @@ export class RollPartAttaque extends RollPartSelect {
}
prepareContext(rollData) {
+ this.filterAttaquesEmpoignade(rollData)
const current = this.getCurrent(rollData)
current.dmg = RdDBonus.dmgRollV2(rollData, current)
}
+ filterAttaquesEmpoignade(rollData) {
+ const refs = this.getRefs(rollData)
+ const isEmpoignade = RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
+ refs.isEmpoignadeEnCours = RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
+ const filterAttaques = isEmpoignade ?
+ FILTER_ATTAQUE_EMPOIGNADE
+ : refs.isEmpoignadeEnCours
+ ? FILTER_ATTAQUE_EMPOIGNE
+ : FILTER_ATTAQUE_NON_EMPOIGNADE
+ refs.attaques = refs.all.filter(filterAttaques)
+ }
+
getAjustements(rollData) {
const current = this.getCurrent(rollData)
const tactique = current.tactique ? [{ label: current.tactique.label, value: current.tactique.attaque }] : []
@@ -79,6 +105,7 @@ export class RollPartAttaque extends RollPartSelect {
const selectAttaque = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-attaque"]`)
const selectTactique = rollDialog.element.querySelector(`roll-section[name="${this.code}"] select[name="select-tactique"]`)
const checkMortalite = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="check-mortalite"]`)
+ const utiliserDagueEmpoignade = rollDialog.element.querySelector(`roll-section[name="${this.code}"] a.utiliser-dague-empoignade`)
const current = this.getCurrent(rollDialog.rollData)
selectAttaque.addEventListener("change", e => {
@@ -99,6 +126,23 @@ export class RollPartAttaque extends RollPartSelect {
current.dmg.mortalite = (e.currentTarget.checked ? RDD_CONFIG.encaissement.mortel : RDD_CONFIG.encaissement.nonmortel)
rollDialog.render()
})
+ utiliserDagueEmpoignade?.addEventListener("click", e => {
+ e.preventDefault()
+ const rollData = rollDialog.rollData
+ this.utiliserDagueEmpoignade(rollData)
+ })
+ }
+
+ utiliserDagueEmpoignade(rollData) {
+ RollDialog.create({
+ ids: { actorId: rollData.ids.actorId, actorTokenId: rollData.ids.actorTokenId },
+ type: { allowed: [ROLL_TYPE_COMP], current: ROLL_TYPE_COMP },
+ selected: {
+ carac: { key: CARACS.DEXTERITE, forced: true },
+ comp: { key: 'Dague', forced: true },
+ diff: { type: DIFF.IMPOSEE, value: -4 }
+ }
+ })
}
impactOtherPart(part, rollData) {
diff --git a/module/roll/roll-part-carac.mjs b/module/roll/roll-part-carac.mjs
index 065549ca..55801b36 100644
--- a/module/roll/roll-part-carac.mjs
+++ b/module/roll/roll-part-carac.mjs
@@ -1,3 +1,4 @@
+import { Grammar } from "../grammar.js"
import { RollPartSelect } from "./roll-part-select.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
@@ -12,8 +13,14 @@ export class RollPartCarac extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
+ const selected = this.getSelected(rollData)
const actor = rollData.active.actor
refs.all = [...this.$getActorCaracs(actor), ...this.$getCaracCompetenceCreature(actor)]
+ .filter(c => !selected.forced ||
+ (selected.key ?
+ Grammar.includesLowerCaseNoAccent(c.label, selected.key)
+ : c.key == '')
+ )
refs.caracs = refs.all
this.$selectCarac(rollData)
}
diff --git a/module/roll/roll-part-comp.mjs b/module/roll/roll-part-comp.mjs
index 06f3f5cc..e38832be 100644
--- a/module/roll/roll-part-comp.mjs
+++ b/module/roll/roll-part-comp.mjs
@@ -18,12 +18,16 @@ export class RollPartComp extends RollPartSelect {
loadRefs(rollData) {
const refs = this.getRefs(rollData)
const selected = this.getSelected(rollData)
- refs.all = this.$getActorComps(rollData)
- .filter(comp => !selected.forced ||
- (selected.key ?
- Grammar.includesLowerCaseNoAccent(comp.name, selected.key)
- : comp.key == '')
- )
+ const all = this.$getActorComps(rollData)
+ if (selected.forced) {
+ refs.all = all.filter(comp => Grammar.equalsInsensitive(comp.label, selected.key))
+ if (refs.all.length == 0) {
+ refs.all = all.filter(comp => Grammar.includesLowerCaseNoAccent(comp.label, selected.key))
+ }
+ }
+ else {
+ refs.all = all
+ }
refs.comps = refs.all
this.$selectComp(rollData)
}
diff --git a/module/roll/roll-part-defense.mjs b/module/roll/roll-part-defense.mjs
index 88a0c595..d89935d6 100644
--- a/module/roll/roll-part-defense.mjs
+++ b/module/roll/roll-part-defense.mjs
@@ -1,4 +1,4 @@
-import { ITEM_TYPES } from "../constants.js"
+import { ITEM_TYPES, RDD_CONFIG } from "../constants.js"
import { ATTAQUE_TYPE, RdDItemArme } from "../item/arme.js"
import { CARACS } from "../rdd-carac.js"
import { DIFF, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
@@ -15,6 +15,8 @@ export class RollPartDefense extends RollPartSelect {
get code() { return PART_DEFENSE }
get section() { return ROLLDIALOG_SECTION.CHOIX }
+
+ isValid(rollData) { return rollData.attackerRoll != undefined }
visible(rollData) { return this.isRollType(rollData, ROLL_TYPE_DEFENSE) }
static getDiffAttaque(attackerRoll) {
@@ -27,13 +29,22 @@ export class RollPartDefense extends RollPartSelect {
const attackerRoll = rollData.attackerRoll
const defenseur = rollData.active.actor
refs.isDistance = [ATTAQUE_TYPE.TIR, ATTAQUE_TYPE.LANCER].find(it => it == attackerRoll?.main)
- const esquives = refs.isDistance == ATTAQUE_TYPE.TIR ? [] : defenseur.getCompetencesEsquive()
- .map(it => RollPartDefense.$extractEsquive(it, defenseur))
+ const isEmpoignade = attackerRoll.dmg.isEmpoignade
+ const isEmpoignadeEnCours = isEmpoignade && defenseur.itemTypes[ITEM_TYPES.empoignade].find(it =>
+ [it.system.empoigneurid, it.system.empoigneid].includes(rollData.ids.opponentId) &&
+ it.system.pointsemp != 0)
- const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
- .map(it => RollPartDefense.$extractParade(it, attackerRoll?.arme, defenseur))
+ const esquives = (refs.isDistance == ATTAQUE_TYPE.TIR || isEmpoignadeEnCours)
+ ? []
+ : defenseur.getCompetencesEsquive()
+ const parades = isEmpoignade
+ ? [RdDItemArme.empoignade(defenseur)]
+ : defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
- refs.defenses = [...esquives, ...parades].filter(it => it != undefined)
+ refs.defenses = [
+ ...esquives.map(it => RollPartDefense.$extractEsquive(it, defenseur)),
+ ...parades.map(it => RollPartDefense.$extractParade(it, attackerRoll?.arme, defenseur))
+ ]
this.$selectDefense(rollData)
}
diff --git a/module/roll/roll-part-empoignade-taille.mjs b/module/roll/roll-part-empoignade-taille.mjs
new file mode 100644
index 00000000..92745a71
--- /dev/null
+++ b/module/roll/roll-part-empoignade-taille.mjs
@@ -0,0 +1,40 @@
+import { RDD_CONFIG } from "../constants.js"
+import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
+import { RdDEmpoignade } from "../rdd-empoignade.js"
+import { COMBAT_ROLL_TYPES } from "./roll-constants.mjs"
+import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
+import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
+
+const EMPOIGNADE_TAILLE = "empoignade-taille"
+
+export class RollPartEmpoignadeTaille extends RollPartCheckbox {
+
+ get code() { return EMPOIGNADE_TAILLE }
+
+ isValid(rollData) {
+ return RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
+ }
+
+ visible(rollData) {
+ return COMBAT_ROLL_TYPES.includes(rollData.type.current) &&
+ RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor) &&
+ this.getTailleDiff(rollData) != 0
+ }
+
+ getTailleDiff(rollData) {
+ const taille = rollData.active.actor.getTaille()
+ const tailleOpponent = rollData.opponent.actor.getTaille()
+ const diff = taille - tailleOpponent
+ const diffTailleAbs = Math.max(0, Math.abs(diff) - 1)
+ const signDiff = Math.sign(diff)
+ return signDiff * diffTailleAbs
+ }
+
+ getCheckboxIcon(rollData) { return `` }
+ getCheckboxLabel(rollData) {
+ return `Taille ${rollData.active.actor.getTaille()} vs ${rollData.opponent.actor.getTaille()} `
+ }
+ getCheckboxValue(rollData) {
+ return this.getTailleDiff(rollData)
+ }
+}
diff --git a/module/roll/roll-part-empoignade.mjs b/module/roll/roll-part-empoignade.mjs
new file mode 100644
index 00000000..fd7a5f0f
--- /dev/null
+++ b/module/roll/roll-part-empoignade.mjs
@@ -0,0 +1,30 @@
+import { RDD_CONFIG } from "../constants.js"
+import { ATTAQUE_TYPE_MELEE } from "../item/arme.js"
+import { RdDEmpoignade } from "../rdd-empoignade.js"
+import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"
+import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
+import { RollPartCheckbox } from "./roll-part-checkbox.mjs"
+
+const EMPOIGNADE = "empoignade"
+
+export class RollPartEmpoignade extends RollPartCheckbox {
+
+ get code() { return EMPOIGNADE }
+
+ isValid(rollData) {
+ return RdDEmpoignade.isCombatantEmpoignade(rollData.ids.opponentId, rollData.ids.opponentTokenId) &&
+ !RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId)
+ }
+
+ visible(rollData) {
+ return rollData.type.current == ROLL_TYPE_ATTAQUE &&
+ ATTAQUE_TYPE_MELEE.includes(rollData.current[PART_ATTAQUE].main) &&
+ RdDEmpoignade.isCombatantEmpoignade(rollData.ids.opponentId, rollData.ids.opponentTokenId) &&
+ !RdDEmpoignade.isCombatantEmpoignade(rollData.ids.actorId, rollData.ids.actorTokenId) &&
+ !RdDEmpoignade.isEmpoignadeEnCours(rollData.active.actor)
+ }
+
+ getCheckboxIcon(rollData) { return `
` }
+ getCheckboxLabel(rollData) { return "vs. empoigneur" }
+ getCheckboxValue(rollData) { return 4 }
+}
diff --git a/module/roll/roll-type.mjs b/module/roll/roll-type.mjs
index 4f7ed51e..bc21742c 100644
--- a/module/roll/roll-type.mjs
+++ b/module/roll/roll-type.mjs
@@ -47,6 +47,7 @@ export class RollType {
}
setDiffType(rollData, type) {
+ type = rollData.selected[PART_DIFF].type ?? type
rollData.current[PART_DIFF].type = type
this.setRollDataType(rollData)
}
diff --git a/module/settings/status-effects.js b/module/settings/status-effects.js
index 74589c8c..43ce4a20 100644
--- a/module/settings/status-effects.js
+++ b/module/settings/status-effects.js
@@ -22,8 +22,8 @@ export const demiReveStatusEffect = {
rdd: true, id: STATUSES.StatusDemiReve, name: 'EFFECT.StatusDemiReve', img: RDD_CONFIG.icons.demiReve
};
const rddStatusEffects = [
- { rdd: true, id: STATUSES.StatusGrappling, tint: '#33cc33', name: 'EFFECT.StatusGrappling', img: RDD_CONFIG.icons.empoignade },
- { rdd: true, id: STATUSES.StatusGrappled, tint: '#ff9900', name: 'EFFECT.StatusGrappled', img: RDD_CONFIG.icons.empoignade },
+ { rdd: true, id: STATUSES.StatusGrappling, name: 'EFFECT.StatusGrappling', img: RDD_CONFIG.icons.empoignade },
+ { rdd: true, id: STATUSES.StatusGrappled, tint: '#d5633d', name: 'EFFECT.StatusGrappled', img: RDD_CONFIG.icons.empoignade },
{ rdd: true, id: STATUSES.StatusRestrained, name: 'EFFECT.StatusRestrained', img: 'icons/svg/net.svg' },
{ rdd: true, id: STATUSES.StatusStunned, name: 'EFFECT.StatusStunned', img: 'icons/svg/stoned.svg', "duration.rounds": 1 },
@@ -45,8 +45,9 @@ const statusSurpriseTotale = new Set([STATUSES.StatusUnconscious, STATUSES.Statu
export class StatusEffects extends FormApplication {
static onReady() {
- const rddEffectIds = rddStatusEffects.map(it => it.id);
+ const rddEffectIds = rddStatusEffects.map(it => it.id)
rddStatusEffects.forEach(it => {
+ it.name = game.i18n.localize(it.name)
it.statuses = new Set([it.id])
})
const defaultStatusEffectIds = CONFIG.statusEffects.map(it => it.id);
diff --git a/templates/chat-demande-defense.hbs b/templates/chat-demande-defense.hbs
index 75d544be..62ae5807 100644
--- a/templates/chat-demande-defense.hbs
+++ b/templates/chat-demande-defense.hbs
@@ -1,25 +1,24 @@
-{{log this}}