Fix regressions: - renommage updatePointsDeReve pas fait partout - coût de rêve des sort fixes introduction de RdDItemSort pour grouper les fonctions liées aux sorts (et tester les sorts variables partout pareil)
1324 lines
51 KiB
JavaScript
1324 lines
51 KiB
JavaScript
/**
|
|
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
|
|
* @extends {Actor}
|
|
*/
|
|
|
|
import { RdDUtility } from "./rdd-utility.js";
|
|
import { TMRUtility } from "./tmr-utility.js";
|
|
import { RdDRollDialog } from "./rdd-roll-dialog.js";
|
|
import { RdDTMRDialog } from "./rdd-tmr-dialog.js";
|
|
import { Misc } from "./misc.js";
|
|
|
|
import { RdDResolutionTable } from "./rdd-resolution-table.js";
|
|
import { RdDDice } from "./rdd-dice.js";
|
|
import { RdDRollTables } from "./rdd-rolltables.js";
|
|
import { ChatUtility } from "./chat-utility.js";
|
|
|
|
export class RdDActor extends Actor {
|
|
|
|
/* -------------------------------------------- */
|
|
/**
|
|
* Override the create() function to provide additional RdD functionality.
|
|
*
|
|
* This overrided create() function adds initial items
|
|
* Namely: Basic skills, money,
|
|
*
|
|
* @param {Object} data Barebones actor data which this function adds onto.
|
|
* @param {Object} options (Unused) Additional options which customize the creation workflow.
|
|
*
|
|
*/
|
|
|
|
static async create(data, options) {
|
|
|
|
// Case of compendium global import
|
|
if (data instanceof Array)
|
|
return super.create(data, options);
|
|
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
|
|
if (data.items) {
|
|
return super.create(data, options);
|
|
}
|
|
|
|
data.items = [];
|
|
let compendiumName = "";
|
|
if (data.type == "personnage") {
|
|
compendiumName = "foundryvtt-reve-de-dragon.competences";
|
|
} else if (data.type == "humanoide") {
|
|
compendiumName = "foundryvtt-reve-de-dragon.competences-humanoides";
|
|
} else if (data.type == "creature") {
|
|
compendiumName = "foundryvtt-reve-de-dragon.competences-creatures";
|
|
} else if (data.type == "entite") {
|
|
compendiumName = "foundryvtt-reve-de-dragon.competences-entites";
|
|
}
|
|
let competences = [];
|
|
const pack = game.packs.get(compendiumName);
|
|
await pack.getIndex().then(index => competences = index);
|
|
for (let comp of competences)
|
|
{
|
|
let compItem = undefined;
|
|
await pack.getEntity(comp._id).then(skill => compItem = skill);
|
|
data.items.push(compItem);
|
|
}
|
|
|
|
return super.create(data, options);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
|
|
/* -------------------------------------------- */
|
|
prepareData() {
|
|
super.prepareData();
|
|
|
|
const actorData = this.data;
|
|
const data = actorData.data;
|
|
const flags = actorData.flags;
|
|
|
|
// Dynamic computing fields
|
|
this.encombrementTotal = 0;
|
|
|
|
// Make separate methods for each Actor type (character, npc, etc.) to keep
|
|
// things organized.
|
|
if (actorData.type === 'personnage') this._prepareCharacterData(actorData);
|
|
if (actorData.type === 'creature') this.computeEtatGeneral(actorData);
|
|
if (actorData.type === 'humanoide') this.computeEtatGeneral(actorData);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/**
|
|
* Prepare Character type specific data
|
|
*/
|
|
_prepareCharacterData(actorData) {
|
|
// Initialize empty items
|
|
RdDUtility.computeCarac(actorData.data);
|
|
this.computeEncombrementTotal();
|
|
this.computeEtatGeneral();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getReveActuel() {
|
|
return this.data.data.reve.reve.value;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getBestDraconic() {
|
|
const list = this.getDraconicList().sort((a, b) => b.data.niveau - a.data.niveau);
|
|
if (list.length==0)
|
|
{
|
|
return { name: "none", niveau: -11 };
|
|
}
|
|
return duplicate(list[0]);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async deleteSortReserve(coordTMR) {
|
|
let reserve = duplicate(this.data.data.reve.reserve);
|
|
let len = reserve.list.length;
|
|
let i = 0;
|
|
let newTable = [];
|
|
for( i=0; i < len; i++) {
|
|
if (reserve.list[i].coord != coordTMR )
|
|
newTable.push(reserve.list[i]);
|
|
}
|
|
if ( newTable.length != len ) {
|
|
reserve.list = newTable;
|
|
await this.update( {"data.reve.reserve": reserve } );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async performRoll(rollData) {
|
|
let rolled = await RdDResolutionTable.roll(rollData.caracValue, rollData.finalLevel);
|
|
//rolled.isPart = true; // Pour tester le particulières
|
|
rollData.rolled = rolled; // garder le résultat
|
|
console.log("performRoll", rollData, rolled)
|
|
if ( !rollData.attackerRoll) // Store in the registry if not a defense roll
|
|
game.system.rdd.rollDataHandler[this.data._id] = rollData;
|
|
|
|
if (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);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async continueRoll(rollData) {
|
|
|
|
let rolled = rollData.rolled;
|
|
let result = rolled.roll;
|
|
let quality = rolled.quality
|
|
|
|
// Manage weapon categories when parrying (cf. page 115 )
|
|
let need_resist = false; // Do we need to make resistance roll for defender ?
|
|
if (rollData.arme && rollData.attackerRoll) { // Manage parade depeding on weapon type, and change roll results
|
|
let attCategory = RdDUtility.getArmeCategory(rollData.attackerRoll.arme);
|
|
let defCategory = RdDUtility.getArmeCategory(rollData.arme);
|
|
if (defCategory == "bouclier")
|
|
rollData.needSignificative = false;
|
|
else if (attCategory != defCategory)
|
|
rollData.needSignificative = true;
|
|
if (attCategory.match("epee") && (defCategory == "hache" || defCategory == "lance"))
|
|
need_resist = true;
|
|
}
|
|
if (this.data.type != 'entite' && (this.data.data.sante.sonne.value || rollData.particuliereAttaque == "finesse")) {
|
|
rollData.needSignificative = true;
|
|
}
|
|
|
|
console.log(">>> ROLL", rollData, rolled);
|
|
let xpmsg = RdDResolutionTable.buildXpMessage(rolled, rollData.finalLevel);
|
|
|
|
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 (rollData.needSignificative && rolled.isSign ) {
|
|
explications += "<br><strong>Attaque parée/esquivée !</strong>";
|
|
} else if ( !rollData.needSignificative && rolled.isSuccess) {
|
|
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 = true;
|
|
}
|
|
} else { // This is the attack roll!
|
|
if (rolled.isSuccess) {
|
|
// 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.domArmePlusDom = this._calculBonusDommages(rollData.selectedCarac, rollData.arme, rollData.particuliereAttaque == 'force' );
|
|
rollData.degats = new Roll("2d10").roll().total + rollData.domArmePlusDom;
|
|
rollData.loc = RdDUtility.getLocalisation();
|
|
for (let target of game.user.targets) {
|
|
rollData.mortalite = (rollData.mortalite) ? rollData.mortalite : "mortel";// Force default
|
|
rollData.mortalite = (target.actor.data.type == 'entite') ? "cauchemar" : rollData.mortalite;
|
|
console.log("Mortalité : ", rollData.mortalite, target.actor.data.type);
|
|
defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData);
|
|
explications += "<br><strong>Cible</strong> : " + target.actor.data.name;
|
|
}
|
|
explications += "<br>Dégâts : " + rollData.degats + "<br>Localisation : " + rollData.loc.label;
|
|
} else {
|
|
explications = "<br>Echec ! Pas de dégâts";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort management
|
|
if (rollData.selectedSort) { // Lancement de sort !
|
|
resumeCompetence = rollData.selectedDraconic.name + "/" + rollData.selectedSort.name;
|
|
explications = await this._rollLancementDeSort(rollData, rolled);
|
|
}
|
|
|
|
// 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)
|
|
+ "<br><strong>" + quality + "</strong>"
|
|
+ explications + xpmsg
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_calculBonusDommages(carac, arme, isForce=false) {
|
|
if ( arme.name.toLowerCase() == "esquive") return 0; // Specific case management
|
|
let dmgArme = 0;
|
|
const dmgPerso = parseInt(this.data.data.attributs.plusdom.value);
|
|
if ( arme.data.dommages ) {
|
|
dmgArme = parseInt(arme.data.dommages) + (isForce)? 5 : 0;
|
|
if (carac.label == "Tir") {
|
|
return dmgArme;
|
|
}
|
|
if (carac.label == "Lancer") {
|
|
return dmgArme + Math.min(dmgArme, dmgPerso);
|
|
}
|
|
}
|
|
return dmgArme + dmgPerso;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async _rollLancementDeSort(rollData, rolled) {
|
|
|
|
let sort = duplicate(rollData.selectedSort);
|
|
|
|
let closeTMR = true;
|
|
let coutReve = sort.data.ptreve_reel; // toujours positionné par cas de sort à ptreve variables
|
|
|
|
let explications = "<br>Lancement du sort <strong>" + sort.name + "</strong> : " + Misc.upperFirst(sort.data.draconic)
|
|
+ " pour " + coutReve + " points de Rêve"
|
|
+ "<br>Depuis la case " + rollData.coord + " (" + TMRUtility.getTMRDescription(rollData.coord).label + ")";
|
|
|
|
let myReve = duplicate(this.data.data.reve.reve);
|
|
if (rolled.isSuccess) { // Réussite du sort !
|
|
sort.ptreve_reel = coutReve;
|
|
if (rolled.isPart) {
|
|
coutReve = Math.max(Math.ceil(coutReve / 2), 1);
|
|
}
|
|
if (myReve.value > coutReve){
|
|
explications += "<br>Réussite du sort: " + coutReve + " points de Rêve sont dépensés";
|
|
|
|
if (rollData.isSortReserve) {
|
|
// Mise en réserve
|
|
myReve.value--;
|
|
await this.sortMisEnReserve(rollData, sort);
|
|
closeTMR = false;
|
|
}
|
|
}
|
|
else {
|
|
// Todo 0 pts de reve !!!!
|
|
explications += "<br>Pas assez de rêve";
|
|
mergeObject(rollData, RdDResolutionTable.getResultat("echec"));
|
|
}
|
|
} else {
|
|
if (rolled.isETotal) { // Echec total !
|
|
coutReve *= 2;
|
|
myReve.value = myReve.value - coutReve;
|
|
explications += "<br><strong>Echec TOTAL</strong> du sort : " + coutReve + " Points de Rêve";
|
|
} else {
|
|
coutReve = 0
|
|
explications += "<br>Echec du sort !";
|
|
}
|
|
}
|
|
|
|
myReve.value = Math.max(myReve.value - coutReve, 0);
|
|
await this.update({ "data.reve.reve": myReve });
|
|
|
|
if (myReve.value == 0) { // 0 points de reve
|
|
ChatMessage.create({ content: this.name + " est réduit à 0 Points de Rêve, et tombe endormi !" });
|
|
closeTMR = true;
|
|
}
|
|
if (closeTMR) {
|
|
this.currentTMR.close(); // Close TMR !
|
|
}
|
|
return explications
|
|
}
|
|
|
|
async dormirChateauDormant() {
|
|
let message = {
|
|
whisper: ChatUtility.getWhisperRecipientsAndGMs( this.name ),
|
|
content : ""
|
|
|
|
};
|
|
const blessures = duplicate(this.data.data.blessures);
|
|
console.log("dormirChateauDormant", blessures)
|
|
await this._recupererBlessures(message, "legere", blessures.legeres.liste.filter(b => b.active), []);
|
|
await this._recupererBlessures(message, "grave", blessures.graves.liste.filter(b => b.active), blessures.legeres.liste);
|
|
await this._recupererBlessures(message,"legere", blessures.critiques.liste.filter(b => b.active), blessures.graves.liste);
|
|
await this.update( {"data.blessures": blessures } );
|
|
await this._recupererVie(message);
|
|
await this.transformerStress(message);
|
|
await this.retourSeuilDeReve(message);
|
|
message.content = "A la fin Chateau Dormant, " + message.content +"<br>Un nouveau jour se lève";
|
|
ChatMessage.create( message );
|
|
}
|
|
|
|
async _recupererBlessures(message, type, liste, moindres) {
|
|
let count = 0;
|
|
const definitions = RdDUtility.getDefinitionsBlessures();
|
|
let definition = definitions.find( d => d.type == type);
|
|
for (let blessure of liste) {
|
|
if (blessure.jours >= definition.facteur) {
|
|
let rolled = await this._jetRecuperationConstitution(Misc.toInt(blessure.soins_complets), message);
|
|
blessure.soins_complets = 0;
|
|
if (rolled.isSuccess && this._retrograderBlessure(type, blessure, moindres)) {
|
|
message.content += " -- une blessure " + type + " cicatrise";
|
|
count++;
|
|
}
|
|
else if (rolled.isETotal) {
|
|
message.content += " -- une blessure " + type + " s'infecte (temps de guérison augmenté de " + definition.facteur + " jours, perte de vie)";
|
|
blessure.jours = 0;
|
|
await this.santeIncDec("vie", -1);
|
|
}
|
|
else {
|
|
message.content += " -- une blessure " + type + " reste stable";
|
|
}
|
|
}
|
|
else {
|
|
blessure.jours++;
|
|
}
|
|
}
|
|
}
|
|
|
|
_retrograderBlessure(type, blessure, blessuresMoindres)
|
|
{
|
|
if (type != "legere") {
|
|
let retrograde = blessuresMoindres.find(b => !b.active);
|
|
if (!retrograde) {
|
|
return false;
|
|
}
|
|
mergeObject(retrograde, { "active": true, "premiers_soins": 0, "soins_complets": 0, "jours": 0, "localisation": blessure.localisation });
|
|
}
|
|
mergeObject(blessure, { "active": false, "premiers_soins": 0, "soins_complets": 0, "jours": 0, "localisation": "" });
|
|
return true;
|
|
}
|
|
|
|
async _recupererVie(message) {
|
|
let blessures = [].concat(this.data.data.blessures.legeres.liste).concat(this.data.data.blessures.graves.liste).concat(this.data.data.blessures.critiques.liste);
|
|
let nbBlessures = blessures.filter(b => b.active);
|
|
let vieManquante = this.data.data.sante.vie.max - this.data.data.sante.vie.value;
|
|
if (nbBlessures == 0 && vieManquante>0) {
|
|
let bonusSoins = 0;
|
|
for (let b of blessures)
|
|
{
|
|
bonusSoins = Math.max(bonusSoins, Misc.toInt(b.soins_complets));
|
|
}
|
|
let rolled = await this._jetRecuperationConstitution(bonusSoins, message)
|
|
if (rolled.isSuccess) {
|
|
const gain = Math.min(rolled.isPart ? 2 : 1, vieManquante);
|
|
message.content += " -- récupération de vie: " + gain;
|
|
await this.santeIncDec("vie", gain);
|
|
}
|
|
else if (rolled.isETotal) {
|
|
message.content += " -- perte de vie: 1";
|
|
await this.santeIncDec("vie", -1);
|
|
}
|
|
else{
|
|
message.content +=" -- vie stationnaire ";
|
|
}
|
|
}
|
|
}
|
|
|
|
async _jetRecuperationConstitution(bonusSoins, message = undefined) {
|
|
let difficulte = Misc.toInt(bonusSoins) + Math.min(0, this.data.data.sante.vie.value - this.data.data.sante.vie.max);
|
|
let rolled = await RdDResolutionTable.roll(this.data.data.carac.constitution.value, difficulte);
|
|
if (message) {
|
|
message.content += RdDResolutionTable.explain(rolled).replace(/Jet :/, "Constitution :");
|
|
}
|
|
return rolled;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async dormir(heures=1) {
|
|
let message = {
|
|
whisper: ChatUtility.getWhisperRecipientsAndGMs( this.name ),
|
|
content : "Vous dormez " + heures + " heure" + (heures > 1 ? "s": "")
|
|
};
|
|
await this.recupereEndurance(message);
|
|
for (let i=0; i<heures; i++) {
|
|
await this.recupererFatigue(message);
|
|
await this.recuperationReve(message);
|
|
}
|
|
ChatMessage.create( message );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async recupereEndurance(message) {
|
|
const manquant = this._computeEnduranceMax() - this.data.data.sante.endurance.value;
|
|
if (manquant > 0) {
|
|
await this.santeIncDec("endurance", manquant);
|
|
message.content += "<br>Vous récuperez " + manquant + " points d'endurance";
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async recupererFatigue(message) {
|
|
let fatigue = duplicate(this.data.data.sante.fatigue)
|
|
const fatigueMin = this._computeFatigueMin();
|
|
if (fatigue.value <= fatigueMin) {
|
|
message.content += "<br>Vous êtes déjà reposé";
|
|
return;
|
|
}
|
|
fatigue.value = Math.max(fatigueMin, this._calculRecuperationSegment(fatigue.value));
|
|
console.log("recupererFatigue", fatigue)
|
|
await this.update( {"data.sante.fatigue": fatigue } );
|
|
if (fatigue.value == 0)
|
|
{
|
|
message.content += "<br>Vous êtes bien reposé";
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_calculRecuperationSegment(actuel)
|
|
{
|
|
const segments = RdDUtility.getSegmentsFatigue(this.data.data.sante.endurance.max);
|
|
let cumul = 0;
|
|
let i;
|
|
for (i=0; i <11; i++) {
|
|
cumul += segments[i];
|
|
let diff = cumul - actuel;
|
|
if (diff >= 0)
|
|
{
|
|
const limit2Segments = Math.floor(segments[i] / 2);
|
|
if (diff > limit2Segments && i > 0) {
|
|
cumul -= segments[i-1]; // le segment est à moins de la moitié, il est récupéré
|
|
}
|
|
cumul -= segments[i];
|
|
break;
|
|
}
|
|
};
|
|
return cumul;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async recuperationReve(message) {
|
|
const seuil = this.data.data.reve.seuil.value;
|
|
const reveActuel = this.getReveActuel();
|
|
if (reveActuel >= seuil) {
|
|
message.content += "<br>Vous avez suffisament rêvé (seuil " + seuil + ", rêve actuel "+reveActuel+")";
|
|
}
|
|
else {
|
|
let deRecuperation = await RdDDice.deDraconique();
|
|
console.log("recuperationReve", deRecuperation);
|
|
if (deRecuperation>=7)
|
|
{
|
|
// Rêve de Dragon !
|
|
message.content += "<br>Vous faites un <strong>Rêve de Dragon</strong> de " + deRecuperation + " Points de rêve";
|
|
message.content += await this.combattreReveDeDragon(deRecuperation);
|
|
}
|
|
else{
|
|
message.content += "<br>Vous récupérez " + deRecuperation + " Points de rêve";
|
|
await this.reveActuelIncDec(deRecuperation);
|
|
}
|
|
}
|
|
}
|
|
async retourSeuilDeReve(message) {
|
|
const seuil = this.data.data.reve.seuil.value;
|
|
const reveActuel = this.getReveActuel();
|
|
if (reveActuel > seuil) {
|
|
message.content += "<br>Votre rêve redescend vers son seuil naturel (seuil " + seuil + ", nouveau rêve actuel "+(reveActuel-1)+")";
|
|
await this.reveActuelIncDec(-1);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async combattreReveDeDragon(force){
|
|
let draconic = this.getBestDraconic();
|
|
let niveau = Math.max(0, draconic.data.niveau);
|
|
let etat = this.data.data.compteurs.etat.value;
|
|
let difficulte = niveau - etat - force;
|
|
let reveActuel = this.getReveActuel();
|
|
let rolled = await RdDResolutionTable.roll(reveActuel, difficulte);
|
|
// TODO: xp particulière
|
|
console.log("combattreReveDeDragon", rolled );
|
|
return await this.appliquerReveDeDragon(rolled, force);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async appliquerReveDeDragon(roll, force) {
|
|
let message = "";
|
|
if (roll.isSuccess) {
|
|
message += "<br>Vous gagnez " + force + " points de Rêve";
|
|
await this.updatePointDeSeuil();
|
|
await this.reveActuelIncDec(force);
|
|
}
|
|
if (roll.isPart) {
|
|
// TODO: Dialog pour choix entre HR opu général?
|
|
let tete = "à déterminer";
|
|
message += "<br>Vous gagnez une Tête de dragon: " + tete;
|
|
}
|
|
if (roll.isEchec) {
|
|
message += "<br>Vous subissez une Queue de Dragon";
|
|
this.ajouterQueue();
|
|
}
|
|
if (roll.isETotal) {
|
|
message += "<br>A cause de votre échec total, vous subissez une deuxième Queue de Dragon !"
|
|
this.ajouterQueue();
|
|
}
|
|
return message;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async sortMisEnReserve(rollData, sort) {
|
|
let reserve = duplicate(this.data.data.reve.reserve);
|
|
reserve.list.push({ coord: rollData.coord, sort: sort, draconic: duplicate(rollData.selectedDraconic) });
|
|
await this.update({ "data.reve.reserve": reserve });
|
|
this.currentTMR.updateSortReserve();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
updateCarac( caracName, caracValue )
|
|
{
|
|
let caracpath = "data.carac." + caracName + ".value"
|
|
if (caracName == "reve") {
|
|
if (caracValue > Misc.toInt(this.data.data.reve.seuil.value)) {
|
|
this.setPointsDeSeuil(caracValue);
|
|
}
|
|
}
|
|
this.update( { caracpath: caracValue } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async updateCreatureCompetence( compName, fieldName, compValue )
|
|
{
|
|
let comp = RdDUtility.findCompetence( this.data.items, compName);
|
|
console.log( comp );
|
|
if ( comp ) {
|
|
const update = {_id: comp._id }
|
|
if (fieldName == "niveau")
|
|
update['data.niveau'] = compValue;
|
|
else if (fieldName == "dommages")
|
|
update['data.dommages'] = compValue;
|
|
else
|
|
update['data.carac_value'] = compValue;
|
|
console.log(update);
|
|
const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async updateCompetence( compName, compValue )
|
|
{
|
|
let comp = RdDUtility.findCompetence( this.data.items, compName);
|
|
if ( comp ) {
|
|
let troncList = RdDUtility.isTronc( compName );
|
|
let maxNiveau = compValue;
|
|
if ( troncList ) {
|
|
let message = "Vous avez modifié une compétence 'tronc'. Vérifiez que les compétences suivantes évoluent ensemble jusqu'au niveau 0 : ";
|
|
for(let troncName of troncList) {
|
|
message += "<br>" + troncName;
|
|
}
|
|
ChatMessage.create( { title : "Compétence Tronc",
|
|
content: message } );
|
|
}
|
|
const update = {_id: comp._id, 'data.niveau': maxNiveau };
|
|
const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity
|
|
} else {
|
|
console.log("Competence not found", compName);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async updateCompetenceXP( compName, compValue )
|
|
{
|
|
let comp = RdDUtility.findCompetence( this.data.items, compName);
|
|
if ( comp ) {
|
|
const update = {_id: comp._id, 'data.xp': compValue };
|
|
const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity
|
|
} else {
|
|
console.log("Competence not found", compName);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async updateCompteurValue( fieldName, fieldValue )
|
|
{
|
|
//console.log("Update", fieldName, fieldValue);
|
|
let content;
|
|
let compteurs = duplicate(this.data.data.compteurs);
|
|
compteurs[fieldName].value = fieldValue;
|
|
await this.update( {"data.compteurs": compteurs } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Teste si le conteneur de destination a suffisament de capacité
|
|
* pour recevoir le nouvel objet
|
|
*/
|
|
testConteneurCapacite( itemId, conteneurId ) {
|
|
if ( !conteneurId ) return true; // pas de conteneur (porté sur soi), donc toujours OK.
|
|
let conteneur = this.items.find( conteneur => conteneurId == conteneur._id); // recup conteneur
|
|
//console.log("Conteneur trouvé : ", conteneur);
|
|
if ( conteneur && conteneur.type == "conteneur" ) {
|
|
let currentEnc = 0; // Calculer le total actuel des contenus
|
|
for (let id of conteneur.data.data.contenu) {
|
|
let objet = this.items.find( objet => (id == objet._id) );
|
|
currentEnc += (objet) ? objet.data.data.encombrement : 0;
|
|
}
|
|
// Et gérer le nouvel objet
|
|
let nouvelObjet = this.items.find( objet => (itemId == objet._id) );
|
|
if ( currentEnc + nouvelObjet.data.data.encombrement > Number(conteneur.data.data.capacite) )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Supprime un item d'un conteneur, sur la base
|
|
* de leurs ID */
|
|
async enleverDeConteneur( itemId, conteneurId ) {
|
|
if ( !conteneurId ) return; // pas de conteneur (porté sur soi)
|
|
let conteneur = this.items.find( conteneur => conteneurId == conteneur._id); // recup conteneur
|
|
if ( conteneur ) { // Si présent
|
|
//console.log("Suppression du conteneur1", conteneurId, itemId, conteneur.data.data.contenu);
|
|
let contenu = conteneur.data.data.contenu;
|
|
contenu.splice(contenu.indexOf('itemId'), 1);
|
|
//let newContenu = conteneur.data.data.contenu.filter( function(value, index, arr) { return value != itemId } );
|
|
//console.log("Suppression du conteneur2", conteneurId, itemId, newContenu);
|
|
//let update = {_id: conteneurId, "data.contenu": newContenu };
|
|
await this.updateEmbeddedEntity("OwnedItem", conteneur.data);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** Ajoute un item dans un conteneur, sur la base
|
|
* de leurs ID */
|
|
async ajouterAConteneur( itemId, conteneurId ) {
|
|
if ( !conteneurId ) return; // pas de conteneur (porté sur soi)
|
|
let conteneur = this.items.find( conteneur => conteneurId == conteneur._id);
|
|
if ( conteneur && conteneur.type == 'conteneur' ) {
|
|
conteneur.data.data.contenu.push( itemId );
|
|
await this.updateEmbeddedEntity("OwnedItem", conteneur.data );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
detectSurEncombrement( ) {
|
|
let diffEnc = Number(this.encombrementTotal) - Number(this.data.data.attributs.encombrement.value);
|
|
if ( diffEnc > 0 ) { // Sur-encombrement
|
|
let malus = Math.round( diffEnc);
|
|
malus = (malus == 0) ? 1 : malus; // Always 1 at least
|
|
//console.log("Sur enc malus", malus);
|
|
return malus;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
computeEncombrementTotal( ) {
|
|
let totalEnc = 0;
|
|
for (const item of this.data.items) {
|
|
if ( item.data && item.data.encombrement != undefined ) {
|
|
if ( !Number(item.data.encombrement) ) item.data.encombrement = 0; // Auto-fix
|
|
if (!item.data.quantite) item.data.quantite = 1; // Auto-fix
|
|
item.data.encTotal = Number(item.data.encombrement) * Number(item.data.quantite);
|
|
//console.log("Enc:", item.name, item.data.encombrement, item.data.quantite, item.data.encTotal);
|
|
totalEnc += item.data.encTotal;
|
|
} else {
|
|
item.data.encTotal = 0; // Force default enc
|
|
}
|
|
}
|
|
this.encombrementTotal = totalEnc;
|
|
this.detectSurEncombrement();
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
computeEtatGeneral( )
|
|
{
|
|
let data = this.data.data;
|
|
// Pas d'état général pour les entités forçage à 0
|
|
if ( this.data.type == 'entite') {
|
|
data.compteurs.etat.value = 0;
|
|
return;
|
|
}
|
|
// Pour les autres
|
|
let state = 0;
|
|
state = state - (data.sante.vie.max - data.sante.vie.value);
|
|
if (data.sante.fatigue) // Creatures n'ont pas de fatigue
|
|
state = state + RdDUtility.currentFatigueMalus(data.sante.fatigue.value, data.sante.endurance.max);
|
|
state = state - this.detectSurEncombrement();
|
|
data.compteurs.etat.value = state;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async ajouterRefoulement( value=1) {
|
|
let ret = "none";
|
|
|
|
let refoulement = duplicate(this.data.data.reve.refoulement);
|
|
refoulement.value = refoulement.value + value;
|
|
|
|
let total = new Roll("d20").roll().total;
|
|
if ( total <= refoulement.value ) {
|
|
refoulement.value = 0;
|
|
this.ajouterSouffle();
|
|
ret = "souffle";
|
|
}
|
|
|
|
await this.update( {"data.reve.refoulement": refoulement } );
|
|
return ret;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
ajouterSouffle() {
|
|
let souffle = RdDRollTables.getSouffle();
|
|
// ChatMessage.create({
|
|
// title: "Souffle de Dragon",
|
|
// content: this.name + " subit un Souffle de Dragon : " + souffle.name
|
|
// });
|
|
// this.actor.createOwnedItem(souffle);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async ajouterQueue() {
|
|
// TODO: Déterminer si Thanatos a été utilisé? => laisser le joueur ne pas choisir Thanatos => choisir sa voie?
|
|
let utiliseThanatos = false;
|
|
let queue;
|
|
if (utiliseThanatos) {
|
|
queue = await RdDRollTables.getOmbre();
|
|
// mettre à jour: plus d'ombre en vue
|
|
}
|
|
else {
|
|
queue = await RdDRollTables.getQueue();
|
|
}
|
|
/*
|
|
// TODO: convertir la queue obtenue en nouvel item ...
|
|
// ou bien l'ajouter à la liste spécifique => this.data.data.reve.queues
|
|
this.createOwnedItem(queue);
|
|
|
|
ChatMessage.create({
|
|
content: this.name + " subit un Queue de Dragon : " + queue.name
|
|
});
|
|
|
|
return queue.name;
|
|
*/
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async deleteTMRRencontreAtPosition( ) {
|
|
let rencontres = duplicate(this.data.data.reve.rencontre);
|
|
let len = rencontres.list.length;
|
|
let i = 0;
|
|
//console.log("List", rencontres, len);
|
|
let newTable = [];
|
|
for( i=0; i < len; i++) {
|
|
if (rencontres.list[i].coord != this.data.data.reve.tmrpos.coord )
|
|
newTable.push(rencontres.list[i]);
|
|
}
|
|
if ( newTable.length != len ) {
|
|
rencontres.list = newTable;
|
|
//console.log("Result: ", rencontres);
|
|
await this.update( {"data.reve.rencontre": rencontres } );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async addTMRRencontre( currentRencontre ) {
|
|
let rencontres = duplicate(this.data.data.reve.rencontre);
|
|
let len = rencontres.list.length;
|
|
let i = 0;
|
|
let already = false;
|
|
for( i=0; i < len; i++) {
|
|
if (rencontres.list[i].coord == this.data.data.reve.tmrpos.coord )
|
|
already = true;
|
|
}
|
|
if ( !already ) {
|
|
rencontres.list.push( {coord: this.data.data.reve.tmrpos.coord, rencontre: currentRencontre} );
|
|
await this.update( {"data.reve.rencontre": rencontres } );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async updateCoordTMR( coord ) {
|
|
let tmrPos = duplicate(this.data.data.reve.tmrpos );
|
|
tmrPos.coord = coord;
|
|
await this.update( {"data.reve.tmrpos": tmrPos } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async reveActuelIncDec( value ) {
|
|
let reve = duplicate(this.data.data.reve.reve);
|
|
reve.value = Math.max(reve.value + value, 0);
|
|
await this.update( {"data.reve.reve": reve } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async updatePointDeSeuil(value=1) {
|
|
const seuil = Misc.toInt(this.data.data.reve.seuil.value);
|
|
const reve = Misc.toInt(this.data.data.carac.reve.value);
|
|
if (seuil < reve) {
|
|
await this.setPointsDeSeuil(Math.min(seuil+value, reve));
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async setPointsDeSeuil( value ) {
|
|
let seuil = duplicate(this.data.data.reve.seuil);
|
|
seuil.value = value;
|
|
await this.update( {"data.reve.seuil": seuil } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
testSiSonne( sante, endurance )
|
|
{
|
|
let result = new Roll("d20").roll().total;
|
|
if ( result <= endurance)
|
|
sante.sonne.value = false;
|
|
if ( result > endurance || result == 20) // 20 is always a failure
|
|
sante.sonne.value = true;
|
|
if (result == 1) {
|
|
sante.sonne.value = false;
|
|
let xp = Misc.toInt(this.data.data.carac.constitution.xp) + 1;
|
|
this.update( {"data.carac.constitution.xp": xp } ); // +1 XP !
|
|
// TODO : Output to chat
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
countBlessures( blessuresListe )
|
|
{
|
|
return blessuresListe.filter(b => b.active).length
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async santeIncDec(name, inc ) {
|
|
const sante = duplicate(this.data.data.sante);
|
|
let data = sante[name];
|
|
let minValue = 0;
|
|
if (this.type == 'personnage')
|
|
minValue = name == "vie" ? Number(-this.data.data.attributs.sconst.value) : 0;
|
|
let newValue = Math.max(minValue, Math.min(data.value + inc, data.max));
|
|
|
|
if (name == "endurance" && this.data.type != 'entite' ) {
|
|
if ( sante.fatigue && inc < 0 ) { // Each endurance lost -> fatigue lost
|
|
sante.fatigue.value = sante.fatigue.value - inc
|
|
}
|
|
if ( newValue == 0 && inc < 0) { // perte endurance et endurance devient 0 -> -1 vie
|
|
sante.vie.value = sante.vie.value - 1;
|
|
}
|
|
newValue = Math.max(0, newValue);
|
|
if (inc>0) { // le max d'endurance s'applique seulement à la récupération
|
|
newValue = Math.max(newValue, this._computeEnduranceMax())
|
|
}
|
|
if (data.value - newValue > 1) {
|
|
this.testSiSonne(sante, newValue); // Peut-être sonné si 2 points d'endurance perdus d'un coup
|
|
} else if (inc>0) {
|
|
sante.sonne.value = false;
|
|
}
|
|
}
|
|
data.value = newValue;
|
|
//console.log(name, inc, data.value, newValue, minValue, data.max);
|
|
if ( sante.fatigue) { // If endurance lost, then the same amount of fatigue cannot be recovered
|
|
sante.fatigue.value = Math.max(sante.fatigue.value, this._computeFatigueMin());
|
|
}
|
|
//console.log("SANTE::::", sante);
|
|
|
|
await this.update( {"data.sante": sante } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_computeFatigueMin() {
|
|
return this.data.data.sante.endurance.max - this.data.data.sante.endurance.value;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
_computeEnduranceMax() {
|
|
let blessures = this.data.data.blessures;
|
|
let diffVie = this.data.data.sante.vie.max - this.data.data.sante.vie.value;
|
|
let maxEndVie = this.data.data.sante.endurance.max - (diffVie * 2);
|
|
let nbGraves = this.countBlessures(blessures.graves.liste);
|
|
let nbCritiques = this.countBlessures(blessures.critiques.liste);
|
|
let maxEndGraves = Math.floor(this.data.data.sante.endurance.max / (2 * nbGraves));
|
|
let maxEndCritiques = nbCritiques > 0 ? 1 : this.data.data.sante.endurance.max;
|
|
return Math.max(0, Math.min(maxEndVie, maxEndGraves, maxEndCritiques));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async manageBlessureFromSheet( bType, index, active ) {
|
|
let bList = duplicate(this.data.data.blessures);
|
|
let blessure = bList[bType+"s"].liste[index];
|
|
blessure.active = !blessure.active;
|
|
if ( !blessure.active ) {
|
|
blessure.premiers_soins = 0;
|
|
blessure.soins_complets = 0;
|
|
blessure.jours = 0;
|
|
blessure.localisation = "";
|
|
}
|
|
//console.log("Blessure update", bType, index, blessure, bList );
|
|
await this.update( { 'data.blessures': bList } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async setDataBlessureFromSheet( bType, index, psoins, pcomplets, jours, loc) {
|
|
let bList = duplicate(this.data.data.blessures);
|
|
let blessure = bList[bType+"s"].liste[index];
|
|
blessure.premiers_soins = psoins;
|
|
blessure.soins_complets = pcomplets;
|
|
blessure.jours = jours;
|
|
blessure.localisation = loc;
|
|
await this.update( { 'data.blessures': bList } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
manageBlessures( blessuresData )
|
|
{
|
|
// Fast exit
|
|
if ( this.data.type == 'entite') return; // Une entité n'a pas de blessures
|
|
if ( blessuresData.legeres + blessuresData.graves + blessuresData.critiques == 0 ) return;
|
|
|
|
let workData = duplicate(blessuresData);
|
|
let blessures = duplicate(this.data.data.blessures);
|
|
// Manage blessures
|
|
if ( workData.legeres > 0 ) {
|
|
for (let k=0; k<blessures.legeres.liste.length; k++) {
|
|
let bless = blessures.legeres.liste[k];
|
|
if ( !bless.active ){
|
|
bless.active = true;
|
|
bless.loc = workData.locName;
|
|
workData.legeres--;
|
|
}
|
|
if (workData.legeres == 0) break;
|
|
}
|
|
}
|
|
|
|
if ( workData.legeres > 0 ) {
|
|
workData.graves += 1;
|
|
blessuresData.graves += 1;
|
|
}
|
|
|
|
if ( workData.graves > 0) {
|
|
for (let k=0; k<blessures.graves.liste.length; k++) {
|
|
let bless = blessures.graves.liste[k];
|
|
if ( !bless.active ) {
|
|
bless.active = true;
|
|
bless.loc = workData.locName;
|
|
workData.graves--;
|
|
}
|
|
if ( workData.graves == 0) break;
|
|
}
|
|
}
|
|
|
|
if ( workData.graves > 0 ) {
|
|
workData.critiques = 1;
|
|
blessuresData.critiques = 1;
|
|
}
|
|
|
|
if ( workData.critiques > 0 ) {
|
|
workData.endurance = this.data.data.sante.endurance.value; // Patch with real endurance current value (ie end -> 0 when critique)
|
|
blessures.critiques.liste[0].active = true;
|
|
blessures.critiques.liste[0].loc = workData.locName;
|
|
}
|
|
|
|
this.update( { "data.blessures": blessures } );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async stressTest() {
|
|
const message = {
|
|
content: "",
|
|
whisper: ChatMessage.getWhisperRecipients(game.user.name)
|
|
};
|
|
await this.transformerStress(message);
|
|
|
|
ChatMessage.create(message);
|
|
}
|
|
|
|
async transformerStress(message) {
|
|
const stress = Misc.toInt(this.data.data.compteurs.stress.value);
|
|
if (stress<=0) {
|
|
return;
|
|
}
|
|
let stressRoll = await this._stressRoll();
|
|
let convertis = Math.floor(stress * stressRoll.factor);
|
|
|
|
let compteurs = duplicate(this.data.data.compteurs);
|
|
compteurs.experience.value += convertis;
|
|
compteurs.stress.value = Math.max(stress - convertis - 1, 0);
|
|
message.content += "<br>Vous transformez " + convertis + " points de Stress en Expérience" + stressRoll.comment;
|
|
await this.update({ "data.compteurs": compteurs });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async _stressRoll() {
|
|
let reveActuel = this.getReveActuel();
|
|
let result = await RdDResolutionTable.roll(reveActuel, 0);
|
|
console.log("_stressRoll", result);
|
|
switch (result.code) {
|
|
case "sign": return { factor: 0.75, comment: " (75%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
|
|
case "norm": return { factor: 0.5, comment: " (50%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
|
|
case "echec": return { factor: 0.2, comment: " (20%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
|
|
case "epart": return { factor: 0.1, comment: " (10%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
|
|
case "etotal": return { factor: 0, comment: " (0%): " + result.quality + " - " + result.roll + " sur " + result.score + "%" }
|
|
case "part":
|
|
{
|
|
let second = await RdDResolutionTable.roll(reveActuel, 0);
|
|
console.log("_stressRoll", second);
|
|
switch (second.code) {
|
|
case "part": case "sign":
|
|
return { factor: 1.5, comment: " (150%): Double Particulière - " + result.roll + " puis " + second.roll + " sur " + result.score + "%" }
|
|
default:
|
|
return { factor: 1, comment: " (150%): " + result.quality + " - " + result.roll + " puis " + second.roll + " sur " + result.score + "%" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async rollUnSort(coord) {
|
|
let draconicList = this.getDraconicList();
|
|
let sortList = duplicate(this.getSortList()); // Duplication car les pts de reve sont modifiés dans le sort
|
|
|
|
let rollData = {
|
|
selectedCarac: this.data.data.carac.reve,
|
|
etat: this.data.data.compteurs.etat.value,
|
|
draconicList: draconicList,
|
|
sortList: sortList,
|
|
selectedDraconic: this.getBestDraconic(),
|
|
selectedSort: sortList[0],
|
|
coord: coord,
|
|
coordLabel: TMRUtility.getTMRDescription( coord).label,
|
|
finalLevel: 0,
|
|
diffConditions: 0,
|
|
diffLibre: sortList[0].data.difficulte, // Per default at startup
|
|
coutreve: Array(20).fill().map((item, index) => 1 + index),
|
|
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
|
|
difficultesLibres: CONFIG.RDD.difficultesLibres
|
|
}
|
|
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html', rollData);
|
|
new RdDRollDialog("sort", html, rollData, this ).render(true);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async rollCarac( caracName )
|
|
{
|
|
let carac;
|
|
if ( caracName == "reveActuel") { // Fake carac for Reve Actuel
|
|
carac = {type: "number",
|
|
value: this.getReveActuel(),
|
|
label: "Rêve Actuel"
|
|
}
|
|
} else {
|
|
carac = this.data.data.carac[caracName];// Per default
|
|
}
|
|
let rollData = {
|
|
selectedCarac: carac,
|
|
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
|
|
difficultesLibres: CONFIG.RDD.difficultesLibres,
|
|
etat: this.data.data.compteurs.etat.value,
|
|
finalLevel: 0,
|
|
diffConditions: 0,
|
|
diffLibre: 0
|
|
}
|
|
console.log(caracName, rollData);
|
|
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html', rollData);
|
|
new RdDRollDialog("carac", html, rollData, this ).render(true);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getSortList() {
|
|
return this.data.items.filter(item => item.type == "sort");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
getDraconicList() {
|
|
return this.data.items.filter(item => item.data.categorie == 'draconic')
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async displayTMR(mode="normal" )
|
|
{
|
|
let isRapide= mode == "rapide"
|
|
if (mode != "visu")
|
|
{
|
|
let minReveValue = (isRapide) ? 3 : 2;
|
|
if (this.getReveActuel() < minReveValue ) {
|
|
ChatMessage.create( {
|
|
content: "Vous n'avez plus assez de Points de Reve pour monter dans les Terres Médianes",
|
|
whisper: ChatMessage.getWhisperRecipients(game.user.name) } );
|
|
return;
|
|
}
|
|
}
|
|
|
|
let data = {
|
|
fatigue: {
|
|
malus: RdDUtility.calculMalusFatigue(this.data.data.sante.fatigue.value, this.data.data.sante.endurance.max),
|
|
html: "<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix( this.data.data.sante.fatigue.value, this.data.data.sante.endurance.max ).html() + "</table>"
|
|
},
|
|
draconic: this.getDraconicList(),
|
|
sort: this.getSortList(),
|
|
caracReve: this.data.data.carac.reve.value,
|
|
pointsReve: this.getReveActuel(),
|
|
isRapide: isRapide
|
|
}
|
|
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', data );
|
|
this.currentTMR = new RdDTMRDialog(html, this, data, mode == "visu");
|
|
this.currentTMR.render(true);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
rollArme( armeName, competenceName=undefined )
|
|
{
|
|
let armeItem = this.data.items.find(item=>item.type==="arme" && (item.name === armeName));
|
|
if (armeItem ) {
|
|
if ( competenceName == undefined) competenceName = armeItem.data.competence;
|
|
this.rollCompetence( competenceName, armeItem );
|
|
} else {
|
|
this.rollCompetence( armeName ); //Bypass mode!
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async rollCompetence( name, armeItem=undefined, attackerRoll=undefined ) {
|
|
let competence = RdDUtility.findCompetence( this.data.items, name);
|
|
console.log("rollCompetence !!!", competence, armeItem, attackerRoll);
|
|
// Common rollData values
|
|
let rollData = {
|
|
ajustementsConditions: CONFIG.RDD.ajustementsConditions,
|
|
difficultesLibres: CONFIG.RDD.difficultesLibres,
|
|
etat: this.data.data.compteurs.etat.value,
|
|
diffConditions: 0,
|
|
diffLibre: (attackerRoll) ? attackerRoll.diffLibre : 0,
|
|
attackerRoll: attackerRoll,
|
|
finalLevel: 0,
|
|
coupsNonMortels: false
|
|
}
|
|
|
|
if ( competence.type == 'competencecreature') { // Specific case for Creatures
|
|
if ( competence.data.iscombat ) {
|
|
armeItem = { name: name, data: { dommages: competence.data.dommages, dommagesReels: competence.data.dommages} };
|
|
}
|
|
competence.data.defaut_carac = "carac_creature"; // Fake default competence
|
|
competence.data.categorie = "creature"; // Fake default competence
|
|
rollData.competence = competence;
|
|
rollData.arme = armeItem;
|
|
rollData.carac = { carac_creature: { label: name, value: competence.data.carac_value } };
|
|
} else { // Usual competence
|
|
rollData.competence = competence;
|
|
if (armeItem ) {
|
|
armeItem.data.dommagesReels = armeItem.data.dommages; // Per default
|
|
if ( !armeItem.data.unemain && !armeItem.data.deuxmains) // Force default
|
|
armeItem.data.unemain = true;
|
|
if (armeItem.data.unemain && armeItem.data.deuxmains) { // manage 1/2 main
|
|
//console.log("Weapon", armeItem.data.dommages);
|
|
if ( armeItem.data.dommages.includes("/") ) { // Sanity check
|
|
if ( name.toLowerCase().includes("1 main") )
|
|
armeItem.data.dommagesReels = Number(armeItem.data.dommages.split("/")[0]);
|
|
else // 2 mains
|
|
armeItem.data.dommagesReels = Number(armeItem.data.dommages.split("/")[1]);
|
|
} else {
|
|
ui.notifications.info("Les dommages de l'arme à 1/2 mains " + name + " ne sont pas corrects (ie sous la forme X/Y)");
|
|
}
|
|
}
|
|
}
|
|
rollData.arme = armeItem;
|
|
rollData.carac = this.data.data.carac;
|
|
}
|
|
|
|
let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html', rollData);
|
|
if (rollData.arme) {
|
|
new RdDRollDialog("arme", html, rollData, this ).render(true);
|
|
} else {
|
|
new RdDRollDialog("competence", html, rollData, this ).render(true);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
async equiperObjet( itemID )
|
|
{
|
|
let item = this.getOwnedItem(itemID);
|
|
if ( item && item.data.data ) {
|
|
let update = {_id: item._id, "data.equipe": !item.data.data.equipe };
|
|
await this.updateEmbeddedEntity("OwnedItem", update);
|
|
this.computeEncombrementTotal(); // Mise à jour encombrement
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
computeArmure( locData, domArmePlusDom )
|
|
{
|
|
let protection = 0;
|
|
for (const item of this.data.items) {
|
|
if (item.type == "armure" && item.data.equipe) {
|
|
let update = duplicate(item);
|
|
protection += new Roll(update.data.protection.toString()).roll().total;
|
|
update.data.deterioration += domArmePlusDom;
|
|
domArmePlusDom = 0; // Reset it
|
|
if ( update.data.deterioration >= 10) {
|
|
update.data.deterioration = 0;
|
|
if ( update.data.protection.toString().length == 1 )
|
|
update.data.protection = "d"+update.data.protection+"-0";
|
|
else {
|
|
let regex = /d\(d+)\-(\d+)/g;
|
|
let res = regex.exec( update.data.protection );
|
|
update.data.protection = "d"+res[1]+"-"+(parseInt(res[2])+1);
|
|
}
|
|
/* TODO - POST chat message */
|
|
}
|
|
this.updateEmbeddedEntity("OwnedItem", update);
|
|
}
|
|
}
|
|
console.log("Final protect", protection);
|
|
return protection;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
encaisserDommages( attackerRoll ) {
|
|
console.log("encaisserDommages", attackerRoll )
|
|
const armure = this.computeArmure( attackerRoll.loc, attackerRoll.domArmePlusDom);
|
|
let degatsReel = attackerRoll.degats - armure;
|
|
|
|
let result = RdDUtility.computeBlessuresSante(degatsReel, attackerRoll.mortalite);
|
|
if ( this.data.type != 'entite') // Pas de PV chez les entités
|
|
this.santeIncDec("vie", result.vie);
|
|
this.santeIncDec("endurance", result.endurance);
|
|
result.locName = (attackerRoll.loc) ? attackerRoll.loc.label : "Corps";
|
|
|
|
this.manageBlessures(result); // Will upate the result table
|
|
const blessureLegere = (result.legeres > 0 ? "une blessure légère" : "");
|
|
const blessureGrave = (result.graves > 0 ? "une blessure grave" : "");
|
|
const blessureCritique = (result.critiques > 0 ? "une blessure critique" : "");
|
|
let commonMsg = { title: "Blessures !", content: this.data.name + " a encaissé : " +
|
|
"<br>Encaissement final : " + degatsReel +
|
|
"<br>" + blessureLegere + blessureGrave + blessureCritique }
|
|
let addonMsg = "<br>Et a perdu : <br>" + result.endurance + " Endurance et " + result.vie + " Points de Vie";
|
|
if ( this.hasPlayerOwner ) {
|
|
commonMsg.content += addonMsg; // Message pour tout le monde
|
|
ChatMessage.create( commonMsg );
|
|
} else { // Le defenseur n'est pas un PJ, donc message complet uniquement pour le MJ
|
|
ChatMessage.create( commonMsg ); // Message pour tout le monde
|
|
let gmMsg = duplicate(commonMsg);
|
|
gmMsg.content = addonMsg; // Et message complémentaire uniquement pour le MJ
|
|
gmMsg.whisper = ChatMessage.getWhisperRecipients( "GM" );
|
|
ChatMessage.create( gmMsg );
|
|
}
|
|
|
|
this.computeEtatGeneral();
|
|
this.sheet.render(true);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------- */
|
|
parerAttaque( attackerRoll, armeId )
|
|
{
|
|
let armeItem = this.getOwnedItem(armeId); // Item.data.data !
|
|
console.log("Going to PARY !!!!!!!!!", armeItem, attackerRoll.diffLibre);
|
|
this.rollCompetence( armeItem.data.data.competence, armeItem.data, attackerRoll );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
esquiverAttaque( attackerRoll )
|
|
{
|
|
this.rollCompetence( "esquive", undefined, attackerRoll );
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/** @override */
|
|
getRollData() {
|
|
const data = super.getRollData();
|
|
|
|
return data;
|
|
}
|
|
|
|
}
|
|
|
|
|