Autoriser le combat sans cible #578

Merged
uberwald merged 2 commits from VincentVk/foundryvtt-reve-de-dragon:v10 into v10 2022-11-22 07:38:11 +01:00
6 changed files with 232 additions and 192 deletions

View File

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

View File

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

View File

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

View File

@ -222,7 +222,7 @@ export class RdDCombatManager extends Combat {
static listActionsCreature(competences) {
return competences.filter(it => RdDItemCompetenceCreature.isCompetenceAttaque(it))
.map(it => RdDItemCompetenceCreature.toActionArme(it));
.map(it => RdDItemCompetenceCreature.armeNaturelle(it));
}
static listActionsPossessions(actor) {
@ -470,49 +470,47 @@ export class RdDCombat {
}
/* -------------------------------------------- */
static createUsingTarget(attacker) {
const target = RdDCombat.getTarget()
if (target == undefined) {
ui.notifications.warn((game.user.targets?.size ?? 0) > 1
? "Vous devez choisir <strong>une seule</strong> cible à attaquer!"
: "Vous devez choisir une cible à attaquer!");
}
else {
static rddCombatTarget(attacker, target) {
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)
}
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
static isTargetEntite(target) {
return target?.actor.type == 'entite' && target?.actor.system.definition.typeentite == ENTITE_NONINCARNE;
}
/* -------------------------------------------- */
static getTarget() {
if (game.user.targets && game.user.targets.size == 1) {
for (let target of game.user.targets) {
return target;
const targets = game.user.targets;
switch (targets?.size ?? 0) {
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;
}
return undefined;
}
/* -------------------------------------------- */
static create(attacker, defender, defenderTokenId, target = undefined) {
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
static createForAttackerAndDefender(attackerId, defenderTokenId) {
static rddCombatForAttackerAndDefender(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);
let defender = defenderTokenId ? canvas.tokens.get(defenderTokenId)?.actor : undefined;
let target = undefined
if (!defenderTokenId || !defender) {
target = RdDCombat.getTarget()
if (!target) {
return;
}
return RdDCombat.createUsingTarget(attacker)
defenderTokenId = target.id;
defender = target.actor;
if (!defenderTokenId || !defender) {
return;
}
}
return new RdDCombat(attacker, defender, defenderTokenId, target)
}
/* -------------------------------------------- */
@ -523,7 +521,7 @@ export class RdDCombat {
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
defender.encaisserDommages(attackerRoll, attacker);
const rddCombat = RdDCombat.createForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.defenderTokenId);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
}
@ -532,7 +530,7 @@ export class RdDCombat {
static onMsgDefense(msg) {
let defenderToken = canvas.tokens.get(msg.defenderTokenId);
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?._chatMessageDefense(msg.paramChatDefense, msg.defenderRoll);
}
@ -559,11 +557,10 @@ export class RdDCombat {
'#echec-total-attaque',
]) {
html.on("click", button, event => {
const rddCombat = RdDCombat.createForAttackerAndDefender(
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
event.currentTarget.attributes['data-attackerId']?.value,
event.currentTarget.attributes['data-defenderTokenId']?.value);
if (rddCombat) {
rddCombat.onEvent(button, event);
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.ptsPossession = 0
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) {
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: '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-refouler', descr: "Confirmer avant de refouler", scope: "client"},
{ group: 'Confirmations', name: 'confirmation-vider', descr: "Confirmer pour vider l'équipement", scope: "client"},