diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 7103aeff..e0101c55 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -13,6 +13,7 @@ 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"; +import { RdDItemBlessure } from "./item/blessure.js"; /* -------------------------------------------- */ /** @@ -152,13 +153,13 @@ export class RdDActorSheet extends RdDBaseActorSheet { this.createEmptyTache(); }); this.html.find('.creer-tache-blessure-legere').click(async event => { - this.actor.createTacheBlessure('legere'); + RdDItemBlessure.createTacheSoinBlessure(this.actor, 'legere'); }); this.html.find('.creer-tache-blessure-grave').click(async event => { - this.actor.createTacheBlessure('grave'); + RdDItemBlessure.createTacheSoinBlessure(this.actor, 'grave'); }); this.html.find('.creer-tache-blessure-critique').click(async event => { - this.actor.createTacheBlessure('critique'); + RdDItemBlessure.createTacheSoinBlessure(this.actor, 'critique'); }); this.html.find('.creer-une-oeuvre').click(async event => { this.selectTypeOeuvreToCreate(); @@ -189,6 +190,23 @@ export class RdDActorSheet extends RdDBaseActorSheet { await this.actor.setDataBlessureFromSheet(btype, index, psoins, pcomplets, jours, loc, psdone, scdone); }); + this.html.find('.blessure-premierssoins-done').change(async event => { + const blessure = this.getBlessure(event); + await blessure?.setSoinsBlessure({ premierssoins: { done: event.currentTarget.checked } }); + }); + this.html.find('.blessure-soinscomplets-done').change(async event => { + const blessure = this.getBlessure(event); + await blessure?.setSoinsBlessure({ soinscomplets: { done: event.currentTarget.checked } }) + }); + this.html.find('.blessure-premierssoins-bonus').change(async event => { + const blessure = this.getBlessure(event); + await blessure?.setSoinsBlessure({ premierssoins: { bonus: Number(event.currentTarget.value) } }) + }); + this.html.find('.blessure-soinscomplets-bonus').change(async event => { + const blessure = this.getBlessure(event); + await blessure?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } }) + }); + // Equip Inventory Item this.html.find('.item-equip').click(async event => { this.actor.equiperObjet(RdDSheetUtility.getItemId(event)); @@ -426,6 +444,12 @@ export class RdDActorSheet extends RdDBaseActorSheet { }); } + getBlessure(event) { + const itemId = this.html.find(event.currentTarget).parents(".item-blessure").data('item-id'); + const blessure = this.actor.getItem(itemId, 'blessure'); + return blessure; + } + isCompetenceAffichable(competence) { return !this.options.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence); } diff --git a/module/actor.js b/module/actor.js index 4295e8b5..00993a89 100644 --- a/module/actor.js +++ b/module/actor.js @@ -34,8 +34,8 @@ import { Targets } from "./targets.js"; import { DialogRepos } from "./sommeil/dialog-repos.js"; import { RdDBaseActor } from "./actor/base-actor.js"; import { RdDTimestamp } from "./rdd-timestamp.js"; -import { RdDItemTache } from "./item-tache.js"; -import { APP_ASTROLOGIE_REFRESH, AppAstrologie } from "./sommeil/app-astrologie.js"; +import { RdDItemBlessure } from "./item/blessure.js"; +import { AppAstrologie } from "./sommeil/app-astrologie.js"; const POSSESSION_SANS_DRACONIC = { img: 'systems/foundryvtt-reve-de-dragon/icons/entites/possession.webp', @@ -2374,13 +2374,16 @@ export class RdDActor extends RdDBaseActor { label: 'Jet ' + Grammar.apostrophe('de', rollData.competence.name), template: 'systems/foundryvtt-reve-de-dragon/templates/dialog-roll-competence.html', rollData: rollData, - callbackAction: r => this.$onRollCompetence(r) + callbackAction: r => this.$onRollCompetence(r, options) }); } /* -------------------------------------------- */ - async $onRollCompetence(rollData) { + async $onRollCompetence(rollData, options) { await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-competence.html') + if (options?.onRollAutomate) { + options.onRollAutomate(rollData); + } } /* -------------------------------------------- */ @@ -2408,11 +2411,21 @@ export class RdDActor extends RdDBaseActor { return tachesExistantes.length > 0 ? tachesExistantes[0] : undefined; } - async createTacheBlessure(gravite) { - const tache = RdDItemTache.prepareTacheSoin(gravite) - if (tache) { - await this.createEmbeddedDocuments('Item', [tache], { renderSheet: false }); + blessuresASoigner() { + // TODO or not TODO: filtrer les blessures poour lesquels on ne peut plus faire de premiers soins? + return this.filterItems(it => it.system.gravite > 0 && !(it.system.premierssoins.done && it.system.soinscomplets.done), 'blessure') + } + + async getTacheBlessure(blesse, blessure) { + const gravite = blessure?.system.gravite ?? 0; + if (gravite > 0) { + const tache = this.listItems('tache').find(it => it.system.itemId == blessure.id) + ?? await RdDItemBlessure.createTacheSoinBlessure(this, gravite); + await blessure?.updateTacheSoinBlessure(tache); + return tache } + return undefined; + } async rollCaracCompetence(caracName, compName, diff, options = { title: "" }) { const competence = this.getCompetence(compName); @@ -2429,7 +2442,7 @@ export class RdDActor extends RdDBaseActor { competence: competence, show: { title: options?.title ?? '' } }, - callbackAction: r => this.$onRollCompetence(r) + callbackAction: r => this.$onRollCompetence(r, options) }); } @@ -2477,6 +2490,9 @@ export class RdDActor extends RdDBaseActor { this.santeIncDec("fatigue", rollData.tache.system.fatigue); await RdDResolutionTable.displayRollData(rollData, this, 'chat-resultat-tache.html'); + if (options?.onRollAutomate) { + options.onRollAutomate(rollData); + } } /* -------------------------------------------- */ @@ -2773,7 +2789,7 @@ export class RdDActor extends RdDBaseActor { } /* -------------------------------------------- */ - async rollAppelChance(onSuccess = () => {}, onEchec = () => {}) { + async rollAppelChance(onSuccess = () => { }, onEchec = () => { }) { await this._openRollDialog({ name: 'appelChance', label: 'Appel à la chance', @@ -3094,7 +3110,77 @@ export class RdDActor extends RdDBaseActor { } RdDCombat.rddCombatTarget(target, this).attaque(competence, arme); }) + } + async rollSoins(blesse, blessureId) { + const blessure = blesse.blessuresASoigner().find(it => it.id == blessureId); + if (blessure) { + if (!blessure.system.premierssoins.done) { + const tache = await this.getTacheBlessure(blesse, blessure); + return await this.rollTache(tache.id, { + onRollAutomate: async r => blesse.onRollTachePremiersSoins(blessureId, r) + }); + } + if (!blessure.system.soinscomplets.done) { + const diff = blessure.system.difficulte + (blessure.system.premierssoins.bonus ?? 0); + return await this.rollCaracCompetence("dexterite", "Chirurgie", diff, { + title: "Soins complets", + onRollAutomate: r => blesse.onRollSoinsComplets(blessureId, r) + }) + } + } + } + + async onRollTachePremiersSoins(blessureId, rollData) { + if (!this.isOwner) { + return RdDBaseActor.remoteActorCall({ actorId: this.id, method: 'onRollTachePremiersSoins', args: [blessureId, rollData] }); + } + const blessure = this.getItem(blessureId, 'blessure') + console.log('TODO update blessure', this, blessureId, rollData, rollData.tache); + if (blessure && !blessure.system.premierssoins.done) { + const tache = rollData.tache; + if (rollData.rolled.isETotal) { + await blessure.update({ + 'system.difficulte': blessure.system.difficulte - 1, + 'system.premierssoins.tache': Math.max(0, tache.system.points_de_tache_courant) + }) + } + else { + const bonus = tache.system.points_de_tache_courant - tache.system.points_de_tache + await blessure.update({ + 'system.premierssoins': { + done: (bonus >= 0), + bonus: Math.max(0, bonus), + tache: Math.max(0, tache.system.points_de_tache_courant) + } + }) + if (bonus>=0) { + await tache.delete() + } + } + } + } + + async onRollSoinsComplets(blessureId, rollData) { + if (!this.isOwner) { + return RdDBaseActor.remoteActorCall({ actorId: this.id, method: 'onRollSoinsComplets', args: [blessureId, rollData] }); + } + const blessure = this.getItem(blessureId, 'blessure') + if (blessure && blessure.system.premierssoins.done && !blessure.system.soinscomplets.done) { + // TODO: update de la blessure: passer par le MJ! + if (rollData.rolled.isETotal) { + await blessure.setSoinsBlessure({ + difficulte: blessure.system.difficulte - 1, + premierssoins: { done: false, bonus: 0 }, soinscomplets: { done: false, bonus: 0 }, + }) + } + else { + // soins complets finis + await blessure.setSoinsBlessure({ + soinscomplets: { done: true, bonus: Math.max(0, rollData.rolled.ptTache) }, + }) + } + } } /* -------------------------------------------- */ diff --git a/module/actor/base-actor.js b/module/actor/base-actor.js index 6c838523..769bc76e 100644 --- a/module/actor/base-actor.js +++ b/module/actor/base-actor.js @@ -21,7 +21,6 @@ export class RdDBaseActor extends Actor { Hooks.on("updateActor", (actor, change, options, actorId) => actor.onUpdateActor(change, options, actorId)); } - static onSocketMessage(sockmsg) { switch (sockmsg.msg) { case "msg_remote_actor_call": diff --git a/module/item-tache.js b/module/item-tache.js deleted file mode 100644 index f17bb9af..00000000 --- a/module/item-tache.js +++ /dev/null @@ -1,18 +0,0 @@ -const BASE_TACHE_SOIN_BLESSURE = { type: "tache", img: 'systems/foundryvtt-reve-de-dragon/icons/competence_chirurgie.webp', system: { carac: "dexterite", competence: "Chirurgie", periodicite: "1 round", fatigue: 0, } } -const TACHES_SOIN_BLESSURE = { - 'critique': { name: 'Blessure critique', system: { difficulte: -6, points_de_tache: 6 } }, - 'grave': { name: 'Blessure grave', system: { difficulte: -4, points_de_tache: 4 } }, - 'legere': { name: 'Blessure légère', system: { difficulte: -2, points_de_tache: 2 } }, -} - -export class RdDItemTache extends Item { - - static prepareTacheSoin(gravite) { - const blessure = TACHES_SOIN_BLESSURE[gravite] - if (blessure) { - return mergeObject(duplicate(BASE_TACHE_SOIN_BLESSURE), blessure) - } - ui.notifications.warn(`Pas de tâche de soins pour une blessure ${gravite}`) - return undefined; - } -} \ No newline at end of file diff --git a/module/item.js b/module/item.js index 9195b29f..bc1248a5 100644 --- a/module/item.js +++ b/module/item.js @@ -31,7 +31,7 @@ const typesInventaire = { const typesObjetsOeuvres = ["oeuvre", "recettecuisine", "musique", "chant", "danse", "jeu"] const typesObjetsDraconiques = ["queue", "ombre", "souffle", "tete", "signedraconique", "sortreserve", "rencontre"] const typesObjetsConnaissance = ["meditation", "recettealchimique", "sort"] -const typesObjetsEffet = ["possession", "poison", "maladie"] +const typesObjetsEffet = ["possession", "poison", "maladie", "blessure"] const typesObjetsCompetence = ["competence", "competencecreature"] const typesObjetsTemporels = ["blessure", "poison", "maladie", "queue", "ombre", "souffle", "signedraconique", "rencontre"] const typesEnvironnement = typesInventaireMateriel; @@ -176,6 +176,7 @@ export class RdDItem extends Item { isBoisson() { return this.isNourritureBoisson() && this.system.boisson; } isAlcool() { return this.isNourritureBoisson() && this.system.boisson && this.system.alcoolise; } isHerbeAPotion() { return this.type == 'herbe' && (this.system.categorie == 'Soin' || this.system.categorie == 'Repos'); } + isBlessure() { return this.type == 'blessure' } isPresentDansMilieux(milieux) { return this.getEnvironnements(milieux).length > 0 diff --git a/module/item/blessure.js b/module/item/blessure.js index 7b445bc0..0c71df22 100644 --- a/module/item/blessure.js +++ b/module/item/blessure.js @@ -1,11 +1,56 @@ import { RdDItem } from "../item.js"; +const BASE_TACHE_SOIN_BLESSURE = { + type: "tache", + img: 'systems/foundryvtt-reve-de-dragon/icons/competence_chirurgie.webp', + system: { carac: "dexterite", competence: "Chirurgie", periodicite: "1 round", fatigue: 0, } +} +const TACHES_SOIN_BLESSURE = { + 'critique': { name: 'Blessure critique', system: { difficulte: -6, points_de_tache: 6 } }, + 'grave': { name: 'Blessure grave', system: { difficulte: -4, points_de_tache: 4 } }, + 'legere': { name: 'Blessure légère', system: { difficulte: -2, points_de_tache: 2 } }, + 6: { name: 'Blessure critique', system: { difficulte: -6, points_de_tache: 6 } }, + 4: { name: 'Blessure grave', system: { difficulte: -4, points_de_tache: 4 } }, + 2: { name: 'Blessure légère', system: { difficulte: -2, points_de_tache: 2 } }, +} + export class RdDItemBlessure extends RdDItem { static get defaultIcon() { return "systems/foundryvtt-reve-de-dragon/icons/sante/blessure.webp"; } + static prepareTacheSoin(gravite) { + const tache = TACHES_SOIN_BLESSURE[gravite] + if (!tache) { + ui.notifications.warn(`Pas de tâche de soins pour une blessure ${gravite}`) + return undefined; + } + return mergeObject(duplicate(BASE_TACHE_SOIN_BLESSURE), tache) + } + + + static async createTacheSoinBlessure(actor, gravite) { + const tache = RdDItemBlessure.prepareTacheSoin(gravite) + if (tache) { + const taches = await actor.createEmbeddedDocuments('Item', [tache], { renderSheet: false }); + return taches[0]; + } + return undefined + } + + async updateTacheSoinBlessure(tache) { + if (tache) { + await tache.update({ + system: { + itemId: this.id, + difficulte: Math.min(this.system.difficulte, tache.system.difficulte), + points_de_tache_courant: Math.max(0, this.system.premierssoins.tache) + } + }); + } + } + async calculerFinPeriodeTemporel(debut) { return await debut.nouveauJour().addJours(this.system.gravite); } @@ -16,4 +61,34 @@ export class RdDItemBlessure extends RdDItem { } } + prepareDerivedData() { + super.prepareDerivedData(); + this.system.labelGravite = this.getLabelGravite() + } + + async setSoinsBlessure(systemUpdate = {}) { + systemUpdate = mergeObject(systemUpdate, this.system, { overwrite: false }), + systemUpdate.soinscomplets.done = systemUpdate.premierssoins.done && systemUpdate.soinscomplets.done + await this.update({ + img: this.getImgSoins(systemUpdate.soinscomplets.done), + system: systemUpdate + }); + } + + getImgSoins(soins) { + return `systems/foundryvtt-reve-de-dragon/icons/sante/${soins ? 'blessure-soins' : 'blessure'}.webp` + } + + getLabelGravite() { + if (this.system.gravite >= 6) { + return 'Critique' + } + if (this.system.gravite >= 4) { + return 'Grave' + } + if (this.system.gravite >= 2) { + return 'Légère' + } + return 'Contusion/éraflure' + } } \ No newline at end of file diff --git a/module/item/sheet-blessure.js b/module/item/sheet-blessure.js index 3d5e7ebf..cbab7ad8 100644 --- a/module/item/sheet-blessure.js +++ b/module/item/sheet-blessure.js @@ -12,5 +12,18 @@ export class RdDBlessureItemSheet extends RdDItemSheet { activateListeners(html) { super.activateListeners(html); + + if (!this.options.editable) return; + + this.html.find('[name="premierssoins-done"]').change(async event => { + await this.item.setSoinsBlessure({ premierssoins: { done: event.currentTarget.checked } }); + }); + this.html.find('[name="soinscomplets-done"]').change(async event => { + await this.item.setSoinsBlessure({ soinscomplets: { done: event.currentTarget.checked } }) + }); + this.html.find('[name="system-gravite"]').change(async event => { + const gravite = Number(event.currentTarget.value) + await this.item.setSoinsBlessure({ gravite: gravite, difficulte: - gravite }) + }); } } diff --git a/module/rdd-token-hud.js b/module/rdd-token-hud.js index 8ef426ef..d96bb139 100644 --- a/module/rdd-token-hud.js +++ b/module/rdd-token-hud.js @@ -2,6 +2,7 @@ import { HtmlUtility } from "./html-utility.js"; import { Misc } from "./misc.js"; import { RdDCombatManager } from "./rdd-combat.js"; +import { Targets } from "./targets.js"; /* -------------------------------------------- */ export class RdDTokenHud { @@ -23,6 +24,9 @@ export class RdDTokenHud { let token = canvas.tokens.get(tokenId); let actor = token.actor; app.hasExtension = true; + // soins + await RdDTokenHud.addExtensionHudSoins(html, actor); + if (isCombat) { let combatant = game.combat.combatants.find(c => c.tokenId == tokenId); if (!(combatant?.actor)) { @@ -81,6 +85,27 @@ export class RdDTokenHud { }); } + static async addExtensionHudSoins(html, sourceActor) { + const target = Targets.getTarget({ warn: false }); + if (target?.actor) { + const hudSoins = { + targetActor: target.actor, + blessures: target.actor.blessuresASoigner() ?? [] + }; + if (hudSoins.blessures.length > 0) { + // soins + const controlIconTarget = html.find('.control-icon[data-action=combat]'); + await RdDTokenHud._configureSubMenu(controlIconTarget, + 'systems/foundryvtt-reve-de-dragon/templates/hud-actor-soins.hbs', + hudSoins, + (event) => { + const blessureId = event.currentTarget.attributes['data-blessure-id']?.value; + sourceActor.rollSoins(target.actor, blessureId) + }); + } + } + } + static _initiativeCommand(initCommand, combatantId) { switch (initCommand) { case 'inc': return RdDCombatManager.incDecInit(combatantId, 0.01); diff --git a/module/rdd-utility.js b/module/rdd-utility.js index 68f0f232..235d46ad 100644 --- a/module/rdd-utility.js +++ b/module/rdd-utility.js @@ -277,7 +277,8 @@ export class RdDUtility { Handlebars.registerHelper('computeResolutionChances', (row, col) => RdDResolutionTable.computeChances(row, col)); Handlebars.registerHelper('upperFirst', str => Misc.upperFirst(str ?? 'Null')); Handlebars.registerHelper('lowerFirst', str => Misc.lowerFirst(str ?? 'Null')); - Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? 'NULL'); + Handlebars.registerHelper('upper', str => str?.toUpperCase() ?? ''); + Handlebars.registerHelper('lowercase', str => str?.toLowerCase() ?? ''); Handlebars.registerHelper('le', str => Grammar.articleDetermine(str)); Handlebars.registerHelper('apostrophe', (article, str) => Grammar.apostrophe(article, str)); Handlebars.registerHelper('un', str => Grammar.articleIndetermine(str)); diff --git a/styles/simple.css b/styles/simple.css index 5d2a90dc..df341346 100644 --- a/styles/simple.css +++ b/styles/simple.css @@ -1403,6 +1403,15 @@ table.table-nombres-astraux tr:hover { top: 2.75rem; right: 4rem; } +.token-hud-ext.soins { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 13.2rem; + left: -5rem; + max-width: 8.5rem +} + .token-hud-ext.right { justify-content: flex-start; flex-direction: column; diff --git a/templates/actor/blessure.hbs b/templates/actor/blessure.hbs index a2b9666c..7fed6645 100644 --- a/templates/actor/blessure.hbs +++ b/templates/actor/blessure.hbs @@ -1,22 +1,31 @@ -