Ajouts combat #68

- gestion de la feinte (sauf initiative)
- gestion du bonus à l'attaque de la charge
- dégats de corps à corps
- bonus dégâts si surprise, début de gestion de surprise totale
This commit is contained in:
2020-12-16 23:02:15 +01:00
parent c8a2d69e0f
commit f3453a46e7
9 changed files with 287 additions and 318 deletions

View File

@ -125,6 +125,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
getBonusDegat() {
// TODO: gérer séparation et +dom créature/entité indépendament de la compétence
return Misc.toInt(this.data.data.attributs.plusdom.value);
}
@ -169,54 +170,13 @@ export class RdDActor extends Actor {
}
}
/* -------------------------------------------- */
async performRoll(rollData, attacker = undefined) {
rollData.surprise = this.getSurprise();
// Manage weapon categories when parrying (cf. page 115 )
if (rollData.arme && rollData.attackerRoll) { // Manage parade depending on weapon type, and change roll results
let attCategory = RdDItemArme.getCategorieArme(rollData.attackerRoll.arme);
let defCategory = RdDItemArme.getCategorieArme(rollData.arme);
if (defCategory == "bouclier")
rollData.needSignificative = false;
else if (attCategory != defCategory)
rollData.needSignificative = true;
// Do we need to make resistance roll for defender ?
if (attCategory.match("epee") && (defCategory == "hache" || defCategory == "lance"))
rollData.needResist = true;
}
if (!this.isEntiteCauchemar() && rollData.particuliereAttaque == "finesse") {
rollData.needSignificative = true;
}
// garder le résultat
await RdDResolutionTable.rollData(rollData);
//console.log("performRoll", rollData)
if ( !rollData.attackerRoll) {// Store in the registry if not a defense roll
game.system.rdd.rollDataHandler[this.data._id] = rollData;
}
if (rollData.rolled.isPart && rollData.arme && !rollData.attackerRoll) { // Réussite particulière avec attaque -> choix !
let message = "<strong>Réussite particulière en attaque</strong>";
message = message + "<br><a class='chat-card-button' id='particuliere-attaque' data-mode='force' data-attackerId='" + this.data._id + "'>Attaquer en Force</a>";
// Finesse et Rapidité seulement en mêlée et si la difficulté libre est de -1 minimum
if (rollData.selectedCarac.label == "Mêlée" && rollData.diffLibre < 0 ) {
message = message + "<br><a class='chat-card-button' id='particuliere-attaque' data-mode='rapidite' data-attackerId='"+ this.data._id + "'>Attaquer en Rapidité</a>";
message = message + "<br><a class='chat-card-button' id='particuliere-attaque' data-mode='finesse' data-attackerId='"+ this.data._id + "'>Attaquer en Finesse</a>";
}
ChatMessage.create( {content : message, whisper: ChatMessage.getWhisperRecipients( this.name ) } );
} else {
this.continueRoll(rollData, attacker);
}
}
/* -------------------------------------------- */
async computeDeteriorationArme( rollData ) {
const attackerRoll = rollData.attackerRoll;
if (rollData.arme && attackerRoll) { // C'est une parade
// Est-ce que l'attaque est une particulière, en force ou charge et que l'attaque n'en est pas une ?
if ( (rollData.needResist || attackerRoll.particuliereAttaque == 'force' || attackerRoll.isCharge)
if ( (rollData.needResist || attackerRoll.particuliereAttaque == 'force' || attackerRoll.tactique == 'charge')
&& !rollData.rolled.isPart ) {
const dmg = attackerRoll.dmg.dmgArme + attackerRoll.dmg.dmgActor;
let resistance = Misc.toInt(rollData.arme.data.resistance);
@ -252,7 +212,7 @@ export class RdDActor extends Actor {
/* -------------------------------------------- */
async computeRecul( rollData, encaisser = undefined ) { // Calcul du recul (p. 132)
if ( rollData.arme || encaisser ) {
if ( (rollData.attackerRoll.particuliereAttaque && rollData.attackerRoll.particuliereAttaque == 'force') || rollData.attackerRoll.isCharge) {
if ( (rollData.attackerRoll.particuliereAttaque && rollData.attackerRoll.particuliereAttaque == 'force') || rollData.attackerRoll.tactique == 'charge') {
let reculNiveau = Misc.toInt(this.data.data.carac.taille.value) - (rollData.attackerRoll.forceValue+rollData.attackerRoll.arme.data.dommagesReels);
let recul = await RdDResolutionTable.roll( 10, reculNiveau );
let msg = "";
@ -273,103 +233,12 @@ export class RdDActor extends Actor {
}
}
/* -------------------------------------------- */
async continueRoll(rollData, attacker = undefined) {
let rolled = rollData.rolled;
let quality = rolled.quality
console.log(">>> ROLL", rollData, rolled);
this._appliquerAjoutExperience(rollData)
let resumeCompetence = (rollData.competence) ? rollData.competence.name : (rollData.diffLibre + rollData.diffConditions);
let explications = "<br>Points de taches : " + rolled.ptTache + ", ajustement qualité: " + rolled.ptQualite;
// Fight management !
let defenseMsg;
let encaisser = false;
if (rollData.arme || (rollData.competence && rollData.competence.name.toLowerCase() == 'esquive') ) {
explications = ""
// In case of fight, replace the message per dommages + localization. it indicates if result is OK or not
if (rollData.attackerRoll) { // Defense case !
if (rolled.isSign || (!rollData.needSignificative && rolled.isSuccess)) {
await this.computeDeteriorationArme( rollData );
explications += "<br><strong>Attaque parée/esquivée !</strong>";
} else {
explications += "<br><strong>Esquive/Parade échouée, encaissement !</strong>";
if (rollData.needSignificative)
explications += " Significative nécessaire!";
}
encaisser = rollData.needSignificative ? !rolled.isSign : !rolled.isSuccess;
await this.computeRecul( rollData, encaisser );
} else { // This is the attack roll!
if (rolled.isSuccess) {
let target = this._getTarget();
if (await this.targetEntiteNonAccordee(target, 'avant-defense')) {
return;
}
// Message spécial pour la rapidité, qui reste difficile à gérer automatiquement
if ( rollData.particuliereAttaque == 'rapidite') {
ChatMessage.create( { content: "Vous avez attaqué en Rapidité. Ce cas n'est pas géré autmatiquement, donc suivez les directives de votre MJ pour gérer ce cas.",
whisper: ChatMessage.getWhisperRecipients( this.name ) } );
}
rollData.dmg = RdDCombat.calculBonusDegats(rollData, this);
if (target)
{
rollData.mortalite = RdDActor._calculMortaliteEncaissement(rollData, target);
defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData);
explications += "<br><strong>Cible</strong> : " + target.actor.data.name;
}
explications += "<br>Encaissement : " + rollData.degats + "<br>Localisation : " + rollData.dmg.loc.label;
} else {
explications = "<br>Echec ! Pas de dégâts";
}
}
}
// Save it for fight in the flags area
game.system.rdd.rollDataHandler[this.data._id] = duplicate(rollData);
// Final chat message
let chatOptions = {
content: "<strong>Test : " + rollData.selectedCarac.label + " / " + resumeCompetence + "</strong>"
+ "<br>Difficultés <strong>libre : " + rollData.diffLibre + "</strong> / conditions : " + Misc.toSignedString(rollData.diffConditions) +" / état : " + rollData.etat
+ RdDResolutionTable.explain(rolled)
+ explications
}
ChatUtility.chatWithRollMode(chatOptions, this.name)
// This an attack, generate the defense message
if (defenseMsg) {
defenseMsg.rollData = duplicate(rollData);
if (defenseMsg.toSocket) {
game.socket.emit("system.foundryvtt-reve-de-dragon", {
msg: "msg_defense",
data: defenseMsg
});
if ( game.user.isGM ) { // Always push the message to the MJ
ChatMessage.create(defenseMsg);
}
} else {
defenseMsg.whisper = [game.user];
ChatMessage.create(defenseMsg);
}
}
// Get damages!
if (encaisser) {
this.encaisserDommages(rollData.attackerRoll, attacker);
}
}
/* -------------------------------------------- */
getSurprise() {
if (this.isEntiteCauchemar()) {
return '';
}
// TODO: gérer une liste de flags demi-surprise (avec icône sur le token)?
if ( this.data.data.sante.sonne.value) {
return 'demi';
}
@ -386,13 +255,6 @@ export class RdDActor extends Actor {
return this.getSurprise() == 'totale';
}
/* -------------------------------------------- */
static _calculMortaliteEncaissement(rollData, target) {
const mortalite = target.actor.isEntiteCauchemar() ? "cauchemar" : (rollData.mortalite ? rollData.mortalite : "mortel");
console.log("Mortalité : ", mortalite, target.actor.data.type);
return mortalite;
}
/* -------------------------------------------- */
async dormirChateauDormant() {
let message = {
@ -1905,7 +1767,7 @@ export class RdDActor extends Actor {
armure: armure
}).roll();
RdDDice.show(rollEncaissement);
let result = RdDUtility.computeBlessuresSante(rollEncaissement.total, attackerRoll.mortalite, attackerRoll.dmg.loc);
let result = RdDUtility.computeBlessuresSante(rollEncaissement.total, attackerRoll.dmg.mortalite, attackerRoll.dmg.loc);
result.endurance = Math.max(result.endurance, -Number(this.data.data.sante.endurance.value));
await this.santeIncDec("vie", result.vie);
await this.santeIncDec("endurance", result.endurance, (result.critiques > 0));