foundryvtt-reve-de-dragon/module/actor-sheet.js
Vincent Vandemeulebrouck d816490839 Expérience sur jets de résistance
Une réussite particulière à un JR de difficulté négative fait
gagner un point d’expérience. On ne gagne qu’un seul point
quelle que soit la difficulté du JR, et cet unique point va
toujours en caractéristique RÊVE, jamais en Draconic.

Pour l'éthylisme, le jet de vie n'est pas un jet d'action, mais de
résistance, limitation à 1 point d'expérience pour lutter contre
l'alcoolisme optimisateur.
2023-01-19 01:48:41 +01:00

541 lines
20 KiB
JavaScript

import { RdDUtility } from "./rdd-utility.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDItemArme } from "./item-arme.js";
import { RdDItemCompetence } from "./item-competence.js";
import { RdDBonus } from "./rdd-bonus.js";
import { Misc } from "./misc.js";
import { RdDCombatManager } from "./rdd-combat.js";
import { RdDCarac } from "./rdd-carac.js";
import { DialogSplitItem } from "./dialog-split-item.js";
import { ReglesOptionelles } from "./settings/regles-optionelles.js";
import { RdDSheetUtility } from "./rdd-sheet-utility.js";
import { STATUSES } from "./settings/status-effects.js";
import { MAINS_DIRECTRICES } from "./actor.js";
import { RdDBaseActorSheet } from "./actor/base-actor-sheet.js";
import { RdDItem } from "./item.js";
/* -------------------------------------------- */
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class RdDActorSheet extends RdDBaseActorSheet {
/** @override */
static get defaultOptions() {
RdDUtility.initAfficheContenu();
return mergeObject(super.defaultOptions, {
classes: ["rdd", "sheet", "actor"],
template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.html",
width: 550,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "carac" }],
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: undefined }],
showCompNiveauBase: false,
vueDetaillee: false
});
}
/* -------------------------------------------- */
async getData() {
let formData = await super.getData();
mergeObject(formData,
{
editable: this.isEditable,
cssClass: this.isEditable ? "editable" : "locked",
effects: this.actor.effects.map(e => foundry.utils.deepClone(e)),
limited: this.actor.limited,
owner: this.actor.isOwner,
biographie: await TextEditor.enrichHTML(this.actor.system.biographie, { async: true }),
notes: await TextEditor.enrichHTML(this.actor.system.notes, { async: true }),
});
mergeObject(formData.calc, {
surenc: this.actor.computeMalusSurEncombrement(),
surprise: RdDBonus.find(this.actor.getSurprise(false)).descr,
resumeBlessures: this.actor.computeResumeBlessure(this.actor.system.blessures),
caracTotal: RdDCarac.computeTotal(this.actor.system.carac, this.actor.system.beaute),
surEncombrementMessage: this.actor.getMessageSurEncombrement(),
})
this.timerRecherche = undefined;
if (formData.type == 'personnage') {
formData.options.mainsDirectrices = MAINS_DIRECTRICES;
formData.byCateg = Misc.classify(formData.competences, it => it.system.categorie)
formData.calc.comptageArchetype = RdDItemCompetence.computeResumeArchetype(formData.competences);
formData.calc.competenceXPTotal = RdDItemCompetence.computeTotalXP(formData.competences);
formData.calc.fatigue = RdDUtility.calculFatigueHtml(formData.system.sante.fatigue.value, formData.system.sante.endurance.max);
formData.competences.forEach(item => {
item.system.isVisible = this.options.recherche
? RdDItemCompetence.nomContientTexte(item, this.options.recherche.text)
: (!this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(item));
RdDItemCompetence.levelUp(item, formData.system.compteurs.experience.value);
});
Object.values(formData.system.carac).forEach(c => {
RdDCarac.levelUp(c);
});
// toujours avoir une liste d'armes (pour mettre esquive et corps à corps)
formData.combat = duplicate(formData.armes ?? []);
RdDItemArme.computeNiveauArmes(formData.combat, formData.competences);
RdDItemArme.ajoutCorpsACorps(formData.combat, formData.competences, formData.system.carac);
formData.esquives = this.actor.getCompetences("Esquive");
formData.combat = RdDCombatManager.listActionsArmes(formData.combat, formData.competences, formData.system.carac);
this.armesList = formData.combat;
// Common data
formData.ajustementsConditions = CONFIG.RDD.ajustementsConditions;
formData.difficultesLibres = CONFIG.RDD.difficultesLibres;
formData.hautreve = {
isDemiReve: this.actor.getEffect(STATUSES.StatusDemiReve),
cacheTMR: this.actor.isTMRCache()
}
formData.subacteurs = {
vehicules: this.actor.listeVehicules(),
montures: this.actor.listeMontures(),
suivants: this.actor.listeSuivants()
}
if (this.actor.getBestDraconic().system.niveau > -11 && !this.actor.isHautRevant()) {
ui.notifications.error(`${this.actor.name} a des compétences draconiques, mais pas le don de Haut-Rêve!
<br>Ajoutez-lui la tête "Don de Haut-Rêve" pour lui permettre d'utiliser ses compétences et d'accéder aux terres médianes du rêve`);
}
}
return formData;
}
/* -------------------------------------------- */ /** @override */
activateListeners(html) {
super.activateListeners(html);
HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionelles.isUsing("appliquer-fatigue"));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
this.html.find('.item-action').click(async event => RdDSheetUtility.getItem(event, this.actor)?.actionPrincipale(this.actor));
this.html.find('.subacteur-delete').click(async event => {
const li = RdDSheetUtility.getEventElement(event);
const actorId = li.data("actor-id");
if (actorId) {
const subActor = game.actors.get(actorId);
RdDUtility.confirmerSuppressionSubacteur(this, subActor, li);
}
});
this.html.find('.experiencelog-delete').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(key, 1);
});
this.html.find('.experiencelog-delete-previous').click(async event => {
const li = this.html.find(event.currentTarget)?.parents(".experiencelog");
const key = Number(li.data("key") ?? -1);
await this.actor.deleteExperienceLog(0, key + 1);
});
this.html.find('.encaisser-direct').click(async event => {
this.actor.encaisser();
})
this.html.find('.sheet-possession-attack').click(async event => {
const poss = RdDSheetUtility.getItem(event, this.actor)
this.actor.conjurerPossession(poss)
})
this.html.find('.remise-a-neuf').click(async event => {
if (game.user.isGM) {
this.actor.remiseANeuf();
}
});
this.html.find('.creer-tache').click(async event => {
this.createEmptyTache();
});
this.html.find('.creer-une-oeuvre').click(async event => {
this.selectTypeOeuvreToCreate();
});
// Blessure control
this.html.find('.blessure-control').click(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
let btype = tr.data("blessure-type");
let index = tr.data('blessure-index');
let active = this.html.find(event.currentTarget).data('blessure-active');
//console.log(btype, index, active);
await this.actor.manageBlessureFromSheet(btype, index, active);
});
// Blessure data
this.html.find('.blessure-soins').change(async event => {
const tr = this.html.find(event.currentTarget).parents(".item");
let btype = tr.data('blessure-type');
let index = tr.data('blessure-index');
let psoins = tr.find('.blessure-premiers_soins').val();
let pcomplets = tr.find('.blessure-soins_complets').val();
let jours = tr.find('.blessure-jours').val();
let loc = tr.find('.blessure-localisation').val();
let psdone = tr.find('.blessure-psdone:checked').val();
let scdone = tr.find('.blessure-scdone:checked').val();
console.log(btype, index, psoins, pcomplets, jours, loc, psdone, scdone);
await this.actor.setDataBlessureFromSheet(btype, index, psoins, pcomplets, jours, loc, psdone, scdone);
});
// Equip Inventory Item
this.html.find('.item-equip').click(async event => {
this.actor.equiperObjet(RdDSheetUtility.getItemId(event));
});
// Roll Carac
this.html.find('.carac-label a').click(async event => {
let caracName = event.currentTarget.attributes.name.value;
this.actor.rollCarac(caracName.toLowerCase());
});
this.html.find('.chance-actuelle').click(async event => {
this.actor.rollCarac('chance-actuelle');
});
this.html.find('.chance-appel').click(async event => {
this.actor.rollAppelChance();
});
this.html.find('[name="jet-astrologie"]').click(async event => {
this.actor.astrologieNombresAstraux();
});
// Roll Skill
this.html.find('a.competence-label').click(async event => {
this.actor.rollCompetence(RdDSheetUtility.getItemId(event));
});
this.html.find('.tache-label a').click(async event => {
this.actor.rollTache(RdDSheetUtility.getItemId(event));
});
this.html.find('.meditation-label a').click(async event => {
this.actor.rollMeditation(RdDSheetUtility.getItemId(event));
});
this.html.find('.chant-label a').click(async event => {
this.actor.rollChant(RdDSheetUtility.getItemId(event));
});
this.html.find('.danse-label a').click(async event => {
this.actor.rollDanse(RdDSheetUtility.getItemId(event));
});
this.html.find('.musique-label a').click(async event => {
this.actor.rollMusique(RdDSheetUtility.getItemId(event));
});
this.html.find('.oeuvre-label a').click(async event => {
this.actor.rollOeuvre(RdDSheetUtility.getItemId(event));
});
this.html.find('.jeu-label a').click(async event => {
this.actor.rollJeu(RdDSheetUtility.getItemId(event));
});
this.html.find('.recettecuisine-label a').click(async event => {
this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event));
});
this.html.find('.subacteur-label a').click(async event => {
let actorId = RdDSheetUtility.getEventItemData(event, 'actor-id');
let actor = game.actors.get(actorId);
if (actor) {
actor.sheet.render(true);
}
});
// Boutons spéciaux MJs
this.html.find('.forcer-tmr-aleatoire').click(async event => {
this.actor.reinsertionAleatoire("Action MJ");
});
this.html.find('.afficher-tmr').click(async event => {
this.actor.changeTMRVisible();
});
// Points de reve actuel
this.html.find('.ptreve-actuel a').click(async event => {
this.actor.rollCarac('reve-actuel', true);
});
// Roll Weapon1
this.html.find('.arme-label a').click(async event => {
let arme = this._getEventArmeCombat(event);
this.actor.rollArme(duplicate(arme));
});
// Initiative pour l'arme
this.html.find('.arme-initiative a').click(async event => {
let combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id);
if (combatant) {
let action = this._getEventArmeCombat(event);
RdDCombatManager.rollInitiativeAction(combatant._id, action);
} else {
ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.");
}
});
// Display TMR, visualisation
this.html.find('.visu-tmr').click(async event => {
this.actor.displayTMR("visu");
});
// Display TMR, normal
this.html.find('.monte-tmr').click(async event => {
this.actor.displayTMR("normal");
});
// Display TMR, fast
this.html.find('.monte-tmr-rapide').click(async event => {
this.actor.displayTMR("rapide");
});
this.html.find('.repos').click(async event => {
await this.actor.repos();
});
this.html.find('.delete-active-effect').click(async event => {
if (game.user.isGM) {
let effect = this.html.find(event.currentTarget).parents(".active-effect").data('effect');
this.actor.removeEffect(effect);
}
});
this.html.find('.enlever-tous-effets').click(async event => {
if (game.user.isGM) {
await this.actor.removeEffects();
}
});
this.html.find('.carac-xp-augmenter').click(async event => {
let caracName = event.currentTarget.name.replace("augmenter.", "");
this.actor.updateCaracXPAuto(caracName);
});
this.html.find('.competence-xp-augmenter').click(async event => {
this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event));
});
this.html.find('.competence-stress-augmenter').click(async event => {
this.actor.updateCompetenceStress(RdDSheetUtility.getItemId(event));
});
if (this.options.vueDetaillee) {
// On carac change
this.html.find('.carac-value').change(async event => {
let caracName = event.currentTarget.name.replace(".value", "").replace("system.carac.", "");
this.actor.updateCarac(caracName, parseInt(event.target.value));
});
this.html.find('input.carac-xp').change(async event => {
let caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "");
this.actor.updateCaracXP(caracName, parseInt(event.target.value));
});
// On competence change
this.html.find('.competence-value').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
//console.log("Competence changed :", compName);
this.actor.updateCompetence(compName, parseInt(event.target.value));
});
// On competence xp change
this.html.find('input.competence-xp').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXP(compName, parseInt(event.target.value));
});
// On competence xp change
this.html.find('input.competence-xp-sort').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value));
});
// On competence archetype change
this.html.find('.competence-archetype').change(async event => {
let compName = event.currentTarget.attributes.compname.value;
this.actor.updateCompetenceArchetype(compName, parseInt(event.target.value));
});
}
this.html.find('.show-hide-competences').click(async event => {
this.options.showCompNiveauBase = !this.options.showCompNiveauBase;
this.render(true);
});
this.html.find('.recherche')
.each((index, field) => {
if (this.options.recherche) {
field.focus();
field.setSelectionRange(this.options.recherche.start, this.options.recherche.end);
}
})
.keyup(async event => {
const nouvelleRecherche = this._optionRecherche(event.currentTarget);
if (this.options.recherche?.text != nouvelleRecherche?.text) {
this.options.recherche = nouvelleRecherche;
if (this.timerRecherche) {
clearTimeout(this.timerRecherche);
}
this.timerRecherche = setTimeout(() => {
this.timerRecherche = undefined;
this.render(true);
}, 500);
}
})
.change(async event =>
this.options.recherche = this._optionRecherche(event.currentTarget)
);
this.html.find('.vue-detaillee').click(async event => {
this.options.vueDetaillee = !this.options.vueDetaillee;
this.render(true);
});
// On pts de reve change
this.html.find('.pointsreve-value').change(async event => {
let reveValue = event.currentTarget.value;
this.actor.update({ "system.reve.reve.value": reveValue });
});
// On seuil de reve change
this.html.find('.seuil-reve-value').change(async event => {
console.log("seuil-reve-value", event.currentTarget)
this.actor.setPointsDeSeuil(event.currentTarget.value);
});
// On stress change
this.html.find('.compteur-edit').change(async event => {
let fieldName = event.currentTarget.attributes.name.value;
this.actor.updateCompteurValue(fieldName, parseInt(event.target.value));
});
this.html.find('.stress-test').click(async event => {
this.actor.transformerStress();
});
this.html.find('.moral-malheureux').click(async event => {
this.actor.jetDeMoral('malheureuse');
});
this.html.find('.moral-neutre').click(async event => {
this.actor.jetDeMoral('neutre');
});
this.html.find('.moral-heureux').click(async event => {
this.actor.jetDeMoral('heureuse');
});
this.html.find('.ethylisme-test').click(async event => {
this.actor.jetEthylisme();
});
this.html.find('.jet-vie').click(async event => {
this.actor.jetVie();
});
this.html.find('.jet-endurance').click(async event => {
this.actor.jetEndurance();
});
this.html.find('.vie-plus').click(async event => {
this.actor.santeIncDec("vie", 1);
});
this.html.find('.vie-moins').click(async event => {
this.actor.santeIncDec("vie", -1);
});
this.html.find('.endurance-plus').click(async event => {
this.actor.santeIncDec("endurance", 1);
});
this.html.find('.endurance-moins').click(async event => {
this.actor.santeIncDec("endurance", -1);
});
this.html.find('.ptreve-actuel-plus').click(async event => {
this.actor.reveActuelIncDec(1);
});
this.html.find('.ptreve-actuel-moins').click(async event => {
this.actor.reveActuelIncDec(-1);
});
this.html.find('.fatigue-plus').click(async event => {
this.actor.santeIncDec("fatigue", 1);
});
this.html.find('.fatigue-moins').click(async event => {
this.actor.santeIncDec("fatigue", -1);
});
}
isCompetenceAffichable(competence) {
return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence);
}
/* -------------------------------------------- */
async _onDropActor(event, dragData) {
const dropActor = fromUuidSync(dragData.uuid);
this.actor.addSubActeur(dropActor);
super._onDropActor(event, dragData);
}
/* -------------------------------------------- */
async selectTypeOeuvreToCreate() {
let typeObjets = RdDItem.getTypesOeuvres();
let content = `<span class="competence-label">Selectionnez le type d'oeuvre</span><select class="item-type">`;
for (let typeName of typeObjets) {
content += `<option value="${typeName}">${Misc.typeName('Item', typeName)}</option>`
}
content += '</select>';
let dialog = new Dialog({
title: "Créer une oeuvre",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'oeuvre",
callback: () => this.actor.createItem($(".item-type").val())
}
}
});
dialog.render(true);
}
/* -------------------------------------------- */
async createEmptyTache() {
await this.actor.createItem('tache', 'Nouvelle tache');
}
_optionRecherche(target) {
if (!target.value?.length) {
return undefined;
}
return {
text: target.value,
start: target.selectionStart,
end: target.selectionEnd,
};
}
_getEventArmeCombat(event) {
const li = this.html.find(event.currentTarget)?.parents(".item");
let armeName = li.data("arme-name");
let compName = li.data('competence-name');
const arme = this.armesList.find(a => a.name == armeName && a.system.competence == compName);
if (!arme) {
return { name: armeName, system: { competence: compName } };
}
return arme;
}
/* -------------------------------------------- */
/** @override */
setPosition(options = {}) {
const position = super.setPosition(options);
const sheetHeader = this.element.find(".sheet-header");
const sheetTabs = this.element.find(".sheet-tabs");
const sheetBody = this.element.find(".sheet-body");
let bodyHeight = position.height - sheetHeader[0].clientHeight;
if (sheetTabs.length > 0) {
bodyHeight -= sheetTabs[0].clientHeight;
}
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Update the Actor
return this.actor.update(formData);
}
async splitItem(item) {
const dialog = await DialogSplitItem.create(item, (item, split) => this._onSplitItem(item, split));
dialog.render(true);
}
async _onSplitItem(item, split) {
if (split >= 1 && split < item.system.quantite) {
await item.diminuerQuantite(split);
const splitItem = duplicate(item);
splitItem.system.quantite = split;
await this.actor.createEmbeddedDocuments('Item', [splitItem])
}
}
}