Merge pull request 'Autoriser le combat sans cible' (#578) from VincentVk/foundryvtt-reve-de-dragon:v10 into v10

Reviewed-on: #578
This commit is contained in:
uberwald 2022-11-22 07:38:11 +01:00
commit da9158e718
6 changed files with 232 additions and 192 deletions

View File

@ -223,8 +223,18 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getReveActuel() { getReveActuel() {
return Misc.toInt(this.system.reve?.reve?.value ?? this.carac.reve.value); switch(this.type) {
case 'personnage':
return Misc.toInt(this.system.reve?.reve?.value ?? this.carac.reve.value);
case 'creature':
case 'entite':
return Misc.toInt(this.system.carac.reve?.value)
case 'vehicule':
default:
return 0;
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getChanceActuel() { getChanceActuel() {
return Misc.toInt(this.system.compteurs.chance?.value ?? 10); return Misc.toInt(this.system.compteurs.chance?.value ?? 10);
@ -2535,48 +2545,43 @@ export class RdDActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollCompetence(idOrName) { async rollCompetence(idOrName, options = {tryTarget: true}) {
let rollData = { competence: this.getCompetence(idOrName) } let rollData = {
carac: this.system.carac,
competence: this.getCompetence(idOrName)
}
if (rollData.competence.type == 'competencecreature') { if (rollData.competence.type == 'competencecreature') {
if (rollData.competence.system.iscombat) { if (rollData.competence.system.iscombat && options.tryTarget) {
if (rollData.competence.system.ispossession) { const target = RdDCombat.getTarget();
RdDPossession.onAttaquePossession(this, rollData.competence) if (target) {
return if (rollData.competence.system.ispossession) {
} RdDPossession.onAttaquePossession(target, this, rollData.competence)
else if (RdDCombat.getTarget()) { }
const arme = RdDItemCompetenceCreature.toActionArme(rollData.competence) else {
RdDCombat.createUsingTarget(this)?.attaque(competence, arme) const arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence)
return RdDCombat.rddCombatTarget(this, target).attaque(competence, arme)
}
return;
} }
} }
// Fake competence pour créature // Transformer la competence de créature
RdDItemCompetenceCreature.setRollDataCreature(rollData) RdDItemCompetenceCreature.setRollDataCreature(rollData)
} else {
rollData.carac = this.system.carac
} }
console.log("rollCompetence !!!", rollData); console.log("rollCompetence !!!", rollData);
const dialog = await RdDRoll.create(this, rollData, { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' }, { const dialog = await RdDRoll.create(this, rollData, { html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html' }, {
name: 'jet-competence', name: 'jet-competence',
label: 'Jet ' + Grammar.apostrophe('de', rollData.competence.name), label: 'Jet ' + Grammar.apostrophe('de', rollData.competence.name),
callbacks: [ callbacks: [
this.createCallbackExperience(), this.createCallbackExperience(),
this.createCallbackAppelAuMoral(), this.createCallbackAppelAuMoral(),
{ action: r => this._competenceResult(r) } { action: r => this.$onRollCompetence(r) }
] ]
}); });
dialog.render(true); dialog.render(true);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
conjurerPossession(possession) { async $onRollCompetence(rollData) {
let draconic = this.getDraconicOuPossession();
RdDPossession.onAttaquePossession(this, draconic, possession)
}
/* -------------------------------------------- */
async _competenceResult(rollData) {
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html') await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html')
} }
@ -3217,26 +3222,42 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
rollArme(arme) { rollArme(arme) {
let competence = this.getCompetence(arme.system.competence) const target = RdDCombat.getTarget();
if (arme || (competence.type == 'competencecreature' && competence.system.iscombat)) { if (!target) {
if (competence.system.ispossession) { RdDConfirm.confirmer({
RdDPossession.onAttaquePossession(this, competence); settingConfirmer: "confirmer-combat-sans-cible",
} else { content: `<p>Voulez vous faire un jet de compétence ${arme.system.competence} sans choisir de cible valide?
RdDCombat.createUsingTarget(this)?.attaque(competence, arme); <br>Tous les jets de combats devront être gérés à la main
} </p>`,
} else { title: 'Ne pas utiliser les automatisation de combat',
this.rollCompetence(competence.name); buttonLabel: "Pas d'automatisation",
onAction: async () => {
this.rollCompetence(arme.system.competence, {tryTarget: false})
}
});
return;
} }
if (RdDCombat.isTargetEntite(target)){
ui.notifications.warn(`Vous ne pouvez pas attaquer une entité non incarnée avec votre ${arme.name}!!!!`);
return;
}
// if (RdDCombat.isTargetEntite(targets[0])) {
// ui.notifications.warn("Vous ne pouvez pas cibler une entité non incarnée!");
// return;
// }
const competence = this.getCompetence(arme.system.competence)
if (competence.system.ispossession) {
return RdDPossession.onAttaquePossession(target, this, competence);
}
RdDCombat.rddCombatTarget(this, target).attaque(competence, arme);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
_getTarget() { conjurerPossession(possession) {
if (game.user.targets && game.user.targets.size == 1) { // TODO: choix de la compétence de draconic ou de possession
for (let target of game.user.targets) { let draconic = this.getDraconicOuPossession();
return target; RdDPossession.onConjurerPossession(this, draconic, possession)
}
}
return undefined;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@ -28,7 +28,7 @@ export class RdDItemArme extends Item {
switch (arme ? arme.type : '') { switch (arme ? arme.type : '') {
case 'arme': return arme; case 'arme': return arme;
case 'competencecreature': case 'competencecreature':
return RdDItemCompetenceCreature.toActionArme(arme); return RdDItemCompetenceCreature.armeNaturelle(arme);
} }
return RdDItemArme.mainsNues(); return RdDItemArme.mainsNues();
} }

View File

@ -1,4 +1,4 @@
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js"; import { RdDCombatManager } from "./rdd-combat.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -12,12 +12,12 @@ export class RdDItemCompetenceCreature extends Item {
rollData.competence.system.categorie = "creature" rollData.competence.system.categorie = "creature"
rollData.selectedCarac = rollData.carac.carac_creature rollData.selectedCarac = rollData.carac.carac_creature
if (rollData.competence.system.iscombat) { if (rollData.competence.system.iscombat) {
rollData.arme = RdDItemCompetenceCreature.toActionArme(rollData.competence); rollData.arme = RdDItemCompetenceCreature.armeNaturelle(rollData.competence);
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static toActionArme(competencecreature) { static armeNaturelle(competencecreature) {
if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) { if (RdDItemCompetenceCreature.isCompetenceAttaque(competencecreature)) {
// si c'est un Item compétence: cloner pour ne pas modifier lma compétence // si c'est un Item compétence: cloner pour ne pas modifier lma compétence
let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature; let arme = (competencecreature instanceof Item) ? competencecreature.clone(): competencecreature;

View File

@ -222,7 +222,7 @@ export class RdDCombatManager extends Combat {
static listActionsCreature(competences) { static listActionsCreature(competences) {
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it)) return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.toActionArme(it)); .map(it => RdDItemCompetenceCreature.armeNaturelle(it));
} }
static listActionsPossessions(actor) { static listActionsPossessions(actor) {
@ -470,51 +470,49 @@ export class RdDCombat {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static createUsingTarget(attacker) { static rddCombatTarget(attacker, target) {
const target = RdDCombat.getTarget() const defender = target?.actor;
if (target == undefined) { const defenderTokenId = target?.id;
ui.notifications.warn((game.user.targets?.size ?? 0) > 1 return new RdDCombat(attacker, defender, defenderTokenId, target)
? "Vous devez choisir <strong>une seule</strong> cible à attaquer!" }
: "Vous devez choisir une cible à attaquer!");
} static isTargetEntite(target) {
else { return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE;
const defender = target?.actor;
const defenderTokenId = target?.id;
if (defender.type == 'entite' && defender.system.definition.typeentite == ENTITE_NONINCARNE) {
ui.notifications.warn("Vous ne pouvez pas cibler une entité non incarnée !!!!");
} else {
return this.create(attacker, defender, defenderTokenId, target)
}
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static getTarget() { static getTarget() {
if (game.user.targets && game.user.targets.size == 1) { const targets = game.user.targets;
for (let target of game.user.targets) { switch (targets?.size ?? 0) {
return target; case 1:
for (let t of targets) {
return t;
}
default:
ui.notifications.warn("Vous devez choisir une cible (et <strong>une seule</strong>) à attaquer!");
return;
}
}
/* -------------------------------------------- */
static rddCombatForAttackerAndDefender(attackerId, defenderTokenId) {
const attacker = game.actors.get(attackerId);
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
let target = undefined
if (!defenderTokenId || !defender) {
target = RdDCombat.getTarget()
if (!target) {
return;
}
defenderTokenId = target.id;
defender = target.actor;
if (!defenderTokenId || !defender) {
return;
} }
} }
return undefined;
}
/* -------------------------------------------- */
static create(attacker, defender, defenderTokenId, target = undefined) {
return new RdDCombat(attacker, defender, defenderTokenId, target) return new RdDCombat(attacker, defender, defenderTokenId, target)
} }
/* -------------------------------------------- */
static createForAttackerAndDefender(attackerId, defenderTokenId) {
const attacker = game.actors.get(attackerId);
if (defenderTokenId) {
const defenderToken = canvas.tokens.get(defenderTokenId);
const defender = defenderToken.actor;
return RdDCombat.create(attacker, defender, defenderTokenId);
}
return RdDCombat.createUsingTarget(attacker)
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static onMsgEncaisser(msg) { static onMsgEncaisser(msg) {
let defender = canvas.tokens.get(msg.defenderTokenId).actor; let defender = canvas.tokens.get(msg.defenderTokenId).actor;
@ -523,7 +521,7 @@ export class RdDCombat {
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined; let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
defender.encaisserDommages(attackerRoll, attacker); defender.encaisserDommages(attackerRoll, attacker);
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme); rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
} }
} }
@ -532,7 +530,7 @@ export class RdDCombat {
static onMsgDefense(msg) { static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId); let defenderToken = canvas.tokens.get(msg.defenderTokenId);
if (defenderToken && Misc.isUniqueConnectedGM()) { if (defenderToken && Misc.isUniqueConnectedGM()) {
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId); const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme); rddCombat?.removeChatMessageActionsPasseArme(msg.defenderRoll.passeArme);
rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll); rddCombat?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
} }
@ -559,11 +557,10 @@ export class RdDCombat {
'#echec-total-attaque', '#echec-total-attaque',
]) { ]) {
html.on("click", button, event => { html.on("click", button, event => {
const rddCombat = RdDCombat.createForAttackerAndDefender( const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value, event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value); event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) { if (rddCombat) {
rddCombat.onEvent(button, event); rddCombat.onEvent(button, event);
event.preventDefault(); event.preventDefault();
} }

View File

@ -27,7 +27,133 @@ export class RdDPossession {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static updateEtatPossession(possession) { static async onAttaquePossession(target, attacker, competence, possession = undefined) {
const defender = target.actor;
possession = duplicate(possession ?? this.searchPossessionFromEntite(attacker, defender) ?? (await this.createPossession(attacker, defender)));
this.$updateEtatPossession(possession)
let rollData = {
mode: "possession",
isECNIDefender: false,
competence: competence,
possession: possession,
attacker: attacker,
defender: defender
};
if (attacker.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
await RdDPossession.$rollAttaquePossession(attacker, rollData);
}
/* -------------------------------------------- */
static async onConjurerPossession(attacker, competence, possession) {
possession = duplicate(possession);
this.$updateEtatPossession(possession)
let rollData = {
mode: "possession",
isECNIDefender: true,
competence: competence,
possession: possession,
attacker: attacker,
defender: game.actors.get(possession.system.possesseurid)
};
await RdDPossession.$rollAttaquePossession(attacker, rollData);
}
/* -------------------------------------------- */
static async onDefensePossession(attackerId, defenderId, possessionId) {
let attacker = game.actors.get(attackerId)
let possession = attacker?.getPossession(possessionId)
defenderId = defenderId ?? possession?.system.possesseurid ?? undefined
let defender = game.actors.get(defenderId)
possession = possession ?? defender?.getPossession(possessionId) ?? undefined;
if (!possession) {
ui.notifications.warn("Une erreur s'est produite : Aucune possession trouvée !!")
return
}
// Update for draconic roll
let rollData = {
mode: "conjuration",
isECNIDefender: defender.type == "entite",
possession: duplicate(possession),
attacker: attacker,
defender: defender,
competence: defender.getDraconicOuPossession(),
selectedCarac: defender.system.carac.reve,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } }
}
rollData.competence.system.defaut_carac = 'reve-actuel'
await RdDPossession.$rollDefensePossesion(defender, rollData);
}
/* -------------------------------------------- */
static async $rollAttaquePossession(attacker, rollData) {
const dialog = await RdDRoll.create(attacker, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html'
}, {
name: 'jet-possession',
label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession',
callbacks: [
{ condition: r => (r.rolled.isSuccess), action: async (r) => await this.$onRollPossession(r, true) },
{ condition: r => (r.rolled.isEchec), action: async (r) => await this.$onRollPossession(r, false) },
]
});
dialog.render(true);
}
/* -------------------------------------------- */
static async $rollDefensePossesion(defender, rollData) {
const dialog = await RdDRoll.create(defender, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html'
},
{
name: 'conjurer',
label: 'Conjurer une Possession',
callbacks: [
{ action: async (r) => await this.$onRollConjuration(r) }
]
}
);
dialog.render(true);
}
/* -------------------------------------------- */
static async $onRollPossession(rollData, isSuccess) {
rollData.possession.isSuccess = isSuccess;
this.$updateEtatPossession(rollData.possession);
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html');
}
/* -------------------------------------------- */
static async $onRollConjuration(rollData) {
let actor = game.actors.get(rollData.possession.system.possedeid)
if (!rollData.rolled.isSuccess) {
if (rollData.isECNIDefender) {
rollData.possession.system.compteur--
} else {
rollData.possession.system.compteur++
}
let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur }
await actor.updateEmbeddedDocuments('Item', [update])
}
this.$updateEtatPossession(rollData.possession)
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
// conjuration
actor.deleteEmbeddedDocuments("Item", [rollData.possession._id])
}
}
/* -------------------------------------------- */
static $updateEtatPossession(possession) {
possession.ptsConjuration = 0 possession.ptsConjuration = 0
possession.ptsPossession = 0 possession.ptsPossession = 0
console.log("Possession", possession) console.log("Possession", possession)
@ -47,111 +173,6 @@ export class RdDPossession {
} }
} }
/* -------------------------------------------- */
static async resultConjuration(rollData) {
let actor = game.actors.get(rollData.possession.system.possedeid)
if (!rollData.rolled.isSuccess) {
if (rollData.isECNIDefender) {
rollData.possession.system.compteur--
} else {
rollData.possession.system.compteur++
}
let update = { _id: rollData.possession._id, "system.compteur": rollData.possession.system.compteur }
await actor.updateEmbeddedDocuments('Item', [update])
}
this.updateEtatPossession(rollData.possession)
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html')
if (rollData.possession.isPosseder || rollData.possession.isConjurer) {
actor.deleteEmbeddedDocuments("Item", [rollData.possession._id])
}
}
/* -------------------------------------------- */
static async onDefensePossession(attackerId, defenderId, possessionId) {
let attacker = game.actors.get(attackerId)
let defender = game.actors.get(defenderId)
let possession = attacker.getPossession(possessionId) ?? defender.getPossession(possessionId) ;
if (!possession) {
ui.notifications.warn("Une erreur s'est produite : Aucune possession trouvée !!")
return
}
// Update for draconic roll
let rollData = {
mode: "conjuration",
isECNIDefender: defender.type == "entite",
possession: duplicate(possession),
attacker: attacker,
defender: defender,
competence: defender.getDraconicOuPossession(),
selectedCarac: defender.system.carac.reve,
forceCarac: { 'reve-actuel': { label: "Rêve Actuel", value: defender.getReveActuel() } }
}
rollData.competence.system.defaut_carac = 'reve-actuel'
const dialog = await RdDRoll.create(defender, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-defense-possession.html'
},
{
name: 'conjurer',
label: 'Conjurer une Possession',
callbacks: [
{ action: async r => await this.resultConjuration(r) }
]
}
);
dialog.render(true)
}
/* -------------------------------------------- */
static async onAttaquePossession(attacker, competence, possession = undefined) {
const target = RdDCombat.getTarget()
if (target == undefined) {
ui.notifications.warn((game.user.targets?.size ?? 0) > 1
? "Vous devez choisir <strong>une seule</strong> cible à posséder!"
: "Vous devez choisir une cible à posséder!");
return;
}
const defender = target.actor;
possession = duplicate(possession ?? this.searchPossessionFromEntite(attacker, defender) ??(await this.createPossession(attacker, defender)));
this.updateEtatPossession(possession)
let rollData = {
mode: "possession",
isECNIDefender: defender.type == "entite",
competence: competence,
possession: possession,
attacker: attacker,
defender: defender
};
if (attacker.isCreature()) {
RdDItemCompetenceCreature.setRollDataCreature(rollData)
}
const dialog = await RdDRoll.create(attacker, rollData,
{
html: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html'
}, {
name: 'jet-possession',
label: rollData.isECNIDefender ? 'Conjurer la possession' : 'Possession',
callbacks: [
{ condition: r => (r.rolled.isSuccess), action: async r => await this._onRollPossession(r, true) },
{ condition: r => (r.rolled.isEchec), action: async r => await this._onRollPossession(r, false) },
]
});
dialog.render(true)
}
/* -------------------------------------------- */
static async _onRollPossession(rollData, isSuccess) {
rollData.possession.isSuccess = isSuccess;
this.updateEtatPossession(rollData.possession);
await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-possession.html');
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static async createPossession(attacker, defender) { static async createPossession(attacker, defender) {
let possessionData = { let possessionData = {

View File

@ -18,6 +18,7 @@ const listeReglesOptionelles = [
{ group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"}, { group: 'Règles générales', name: 'appliquer-fatigue', descr: "Appliquer les règles de fatigue"},
{ group: 'Règles générales', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false }, { group: 'Règles générales', name: 'afficher-colonnes-reussite', descr: "Afficher le nombre de colonnes de réussite ou d'échec", default: false },
{ group: 'Confirmations', name: 'confirmer-combat-sans-cible', descr: "Confirmer avant une attaque sans cible", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"}, { group: 'Confirmations', name: 'confirmation-tmr', descr: "Confirmer pour monter dans les TMR", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"}, { group: 'Confirmations', name: 'confirmation-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"}, { group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},