diff --git a/css/foundryvtt-reve-de-dragon.css b/css/foundryvtt-reve-de-dragon.css index f76066ed..60bdea5a 100644 --- a/css/foundryvtt-reve-de-dragon.css +++ b/css/foundryvtt-reve-de-dragon.css @@ -4895,7 +4895,8 @@ body { margin: 0; padding: 0.2rem; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content { +.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content, +.system-foundryvtt-reve-de-dragon .application.sheet .window-content { margin: 0.2rem; padding: 0; } @@ -4952,7 +4953,8 @@ body { margin-bottom: 0px; border: 0; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-header h1.charname input[type="text"] { +.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .sheet-header h1.charname input[type="text"], +.system-foundryvtt-reve-de-dragon .application.sheet .window-content .sheet-header h1.charname input[type="text"] { width: 100%; height: 100%; margin: 0; @@ -5051,6 +5053,12 @@ body { .system-foundryvtt-reve-de-dragon nav.sheet-tabs .item { position: relative; padding: 0.1rem 0.2rem; + color: rgba(52, 52, 52, 0.95); +} +.system-foundryvtt-reve-de-dragon .sheet nav.sheet-tabs .item.active { + color: #4a0404; + text-decoration: underline; + text-shadow: none; } .system-foundryvtt-reve-de-dragon nav.sheet-tabs .item:after { content: ""; @@ -5490,7 +5498,7 @@ body { width: 100%; } .system-foundryvtt-reve-de-dragon .tab.hautreve { - height: 200px; + height: 100%; } .system-foundryvtt-reve-de-dragon .oeuvre-buttons { gap: 0.5rem; @@ -5663,8 +5671,17 @@ body { .system-foundryvtt-reve-de-dragon .rdd.sheet .window-content { overflow: hidden; } +.system-foundryvtt-reve-de-dragon .rdd.sheet .window-content > form, +.system-foundryvtt-reve-de-dragon .rdd.sheet .window-content > section { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; +} .system-foundryvtt-reve-de-dragon .rdd.sheet .window-content .sheet-body { - overflow-y: scroll; + overflow-y: auto; + scrollbar-width: auto; + scrollbar-color: auto; } .system-foundryvtt-reve-de-dragon .rdd.sheet .window-content .sheet-body .tab { padding-bottom: 30px; @@ -5687,7 +5704,7 @@ body { justify-content: stretch; } .system-foundryvtt-reve-de-dragon .rdd.sheet .window-content .sheet-body .carac-list .caracteristique .carac-value { - flex-basis: 15%; + flex-basis: 2rem; flex-grow: 0; } .system-foundryvtt-reve-de-dragon .rdd.sheet .window-content .sheet-body .carac-list .caracteristique .carac-xp { @@ -5714,20 +5731,20 @@ body { .system-foundryvtt-reve-de-dragon .prosemirror menu { background-color: var(--color-background-chat-message); } -.system-foundryvtt-reve-de-dragon .app.sheet fieldset :is(label, input) { +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet fieldset :is(label, input) { font-family: CaslonAntique; text-align: justify; font-size: 1rem; letter-spacing: 1px; } -.system-foundryvtt-reve-de-dragon .app.sheet input.attribute-value { +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet input.attribute-value { background-color: rgba(0, 0, 0, 0.05); border: 1px solid var(--color-border-light-tertiary); padding: 1px 3px; height: var(--form-field-height); border-radius: 3px; } -.system-foundryvtt-reve-de-dragon .app.sheet div.form-group { +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet div.form-group { clear: both; display: flex; flex-direction: row; @@ -5735,20 +5752,25 @@ body { margin: 0.1rem 0; align-items: center; } -.system-foundryvtt-reve-de-dragon .app.sheet .editor.prosemirror { - height: fit-content; - min-height: 6rem; +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet div.form-group.large-editor { + flex-direction: column; + align-items: stretch; } -.system-foundryvtt-reve-de-dragon .app.sheet .editor.prosemirror .editor-container { - min-height: 6rem; +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet .editor.prosemirror { + height: fit-content; + min-height: 12rem; + overflow: auto; +} +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet .editor.prosemirror .editor-container { + min-height: 12rem; height: calc(100% - 3rem); margin: 0; flex: none; } -.system-foundryvtt-reve-de-dragon .app.sheet.journal-entry-page .editor.prosemirror { +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet.journal-entry-page .editor.prosemirror { height: 100%; } -.system-foundryvtt-reve-de-dragon .app.sheet.journal-entry-page .editor.prosemirror .editor-container { +.system-foundryvtt-reve-de-dragon :is(.app, .application).sheet.journal-entry-page .editor.prosemirror .editor-container { height: calc(100% - 6rem); } .system-foundryvtt-reve-de-dragon .large-editor { @@ -5798,10 +5820,12 @@ body { .system-foundryvtt-reve-de-dragon .foundryvtt-reve-de-dragon .tabs .item { line-height: 40px; font-weight: bold; + color: rgba(52, 52, 52, 0.95); } .system-foundryvtt-reve-de-dragon .foundryvtt-reve-de-dragon .tabs .item.active { text-decoration: underline; text-shadow: none; + color: #4a0404; } .system-foundryvtt-reve-de-dragon .foundryvtt-reve-de-dragon .item-list { list-style: none; @@ -5980,31 +6004,58 @@ body { background-color: var(--background-color-button); color: var(--rdd-color-text-primary); } -.system-foundryvtt-reve-de-dragon .window-app .window-content { +.system-foundryvtt-reve-de-dragon .window-app .window-content, +.system-foundryvtt-reve-de-dragon .application .window-content { background: url(../assets/ui/bg_left.webp) no-repeat left top; color: var(--rdd-color-text-primary); } -.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header { +.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header { background: #011d33 url(../assets/ui/bg_header.webp) no-repeat left top; } .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header label, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header label, .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header .hint, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header .hint, .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header .permissions-list, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header .permissions-list, .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header nav.tabs, .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs button, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header nav.tabs button, .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header nav.tabs button .count, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header nav.tabs button .count, .system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header div, -.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header button { +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header div, +.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header button, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header button { color: rgba(255, 255, 255, 0.9); } -.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input { +.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input, +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header input { color: rgba(255, 255, 255, 0.9); border: 0 none; margin-bottom: 0.2rem; } -.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input[type="checkbox"] { +.system-foundryvtt-reve-de-dragon .window-app .window-content .sheet-header input[type="checkbox"], +.system-foundryvtt-reve-de-dragon .application .window-content .sheet-header input[type="checkbox"] { color: rgba(255, 255, 255, 0.75); } +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content :is(input[type="text"], input[type="number"], select) { + background: var(--rdd-input-background); + border: 1px solid var(--rdd-color-border-input); + color: var(--rdd-color-text-primary); +} +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .sheet-header :is(input[type="text"], input[type="number"], select) { + background: transparent; + color: rgba(255, 255, 255, 0.9); + border: 0 none; +} +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .sheet-body :is(input.carac-value, input.carac-xp, input.derivee-value, input.competence-value, input.competence-xp, input.competence-carac, input.competence-damage, input.competence-archetype) { + height: 1.6rem; + line-height: normal; + padding: 0 0.2rem; +} .system-foundryvtt-reve-de-dragon input[type="number"] { text-align: right; padding-right: 0.5rem; @@ -6239,43 +6290,43 @@ body { width: 100%; max-width: 90%; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content :is(.tooltip, .tooltip-overflow):hover .tooltiptext { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content :is(.tooltip, .tooltip-overflow):hover .tooltiptext { top: 2rem; left: 2rem; margin: 0; padding: 0.25rem; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .carac-value, -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .competence-xp { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .carac-value, +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .competence-xp { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; text-align: center; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .carac-value, -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .competence-value { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .carac-value, +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .competence-value { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; text-align: center; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .carac-value, -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .competence-archetype { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .carac-value, +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .competence-archetype { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; text-align: center; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .at-archetype { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .at-archetype { border-left: 3px solid var(--color-archetype-atteint, #b8860b); } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .archetype-check { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .archetype-check { color: var(--color-archetype-atteint, #b8860b); font-size: 0.75rem; margin-left: 0.15rem; } -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .carac-value, -.system-foundryvtt-reve-de-dragon .window-app.sheet .window-content .competence-xp-sort { +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .carac-value, +.system-foundryvtt-reve-de-dragon :is(.window-app, .application).sheet .window-content .competence-xp-sort { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; diff --git a/less/foundryvtt-reve-de-dragon.less b/less/foundryvtt-reve-de-dragon.less index 4c902fd0..492a725c 100644 --- a/less/foundryvtt-reve-de-dragon.less +++ b/less/foundryvtt-reve-de-dragon.less @@ -69,7 +69,8 @@ margin: 0; padding: 0.2rem; } - .window-app.sheet .window-content { + .window-app.sheet .window-content, + .application.sheet .window-content { margin: 0.2rem; padding: 0; } @@ -119,6 +120,11 @@ } .window-app.sheet + .window-content + .sheet-header + h1.charname + input[type="text"], + .application.sheet .window-content .sheet-header h1.charname @@ -235,6 +241,13 @@ nav.sheet-tabs .item { position: relative; padding: 0.1rem 0.2rem; + color: rgba(52, 52, 52, 0.95); + } + + .sheet nav.sheet-tabs .item.active { + color: #4a0404; + text-decoration: underline; + text-shadow: none; } nav.sheet-tabs .item:after { @@ -650,7 +663,7 @@ .btn-voir-tables-encaissement { width: 100%; } - .tab.hautreve { height: 200px; } + .tab.hautreve { height: 100%; } .oeuvre-buttons { gap: 0.5rem; flex-wrap: wrap; } @@ -811,8 +824,16 @@ .rdd.sheet { .window-content { overflow: hidden; + > form, > section { + display: flex; + flex-direction: column; + flex: 1; + overflow: hidden; + } .sheet-body { - overflow-y: scroll; + overflow-y: auto; + scrollbar-width: auto; + scrollbar-color: auto; .tab { padding-bottom: 30px; } @@ -835,7 +856,7 @@ justify-content: stretch; .carac-value { - flex-basis: 15%; + flex-basis: 2rem; flex-grow: 0; } .carac-xp { @@ -872,7 +893,7 @@ } } - .app.sheet{ + :is(.app, .application).sheet{ fieldset :is(label, input) { font-family: CaslonAntique; text-align: justify; @@ -897,19 +918,25 @@ align-items: center; } + div.form-group.large-editor { + flex-direction: column; + align-items: stretch; + } + .editor.prosemirror { height: fit-content; - min-height: 6rem; + min-height: 12rem; + overflow: auto; .editor-container { - min-height: 6rem; + min-height: 12rem; height: calc(100% - 3rem); margin: 0; flex: none; } } } - .app.sheet.journal-entry-page .editor.prosemirror{ + :is(.app, .application).sheet.journal-entry-page .editor.prosemirror{ height: 100%; .editor-container { height: calc(100% - 6rem); @@ -976,11 +1003,13 @@ .foundryvtt-reve-de-dragon .tabs .item { line-height: 40px; font-weight: bold; + color: rgba(52, 52, 52, 0.95); } .foundryvtt-reve-de-dragon .tabs .item.active { text-decoration: underline; text-shadow: none; + color: #4a0404; } .foundryvtt-reve-de-dragon .item-list { @@ -1160,7 +1189,8 @@ /* ======================================== */ /* Sheet */ - .window-app .window-content { + .window-app .window-content, + .application .window-content { background: url(../assets/ui/bg_left.webp) no-repeat left top; color: var(--rdd-color-text-primary); @@ -1190,6 +1220,24 @@ } } + :is(.window-app, .application).sheet .window-content :is(input[type="text"], input[type="number"], select) { + background: var(--rdd-input-background); + border: 1px solid var(--rdd-color-border-input); + color: var(--rdd-color-text-primary); + } + :is(.window-app, .application).sheet .window-content .sheet-header :is(input[type="text"], input[type="number"], select) { + background: transparent; + color: rgba(255, 255, 255, 0.9); + border: 0 none; + } + + :is(.window-app, .application).sheet .window-content .sheet-body + :is(input.carac-value, input.carac-xp, input.derivee-value, input.competence-value, input.competence-xp, input.competence-carac, input.competence-damage, input.competence-archetype) { + height: 1.6rem; + line-height: normal; + padding: 0 0.2rem; + } + input[type="number"] { text-align: right; padding-right: 0.5rem; @@ -1442,7 +1490,7 @@ max-width: 90%; } - .window-app.sheet + :is(.window-app, .application).sheet .window-content :is(.tooltip, .tooltip-overflow):hover .tooltiptext { @@ -1452,37 +1500,37 @@ padding: 0.25rem; } - .window-app.sheet .window-content .carac-value, - .window-app.sheet .window-content .competence-xp { + :is(.window-app, .application).sheet .window-content .carac-value, + :is(.window-app, .application).sheet .window-content .competence-xp { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; text-align: center; } - .window-app.sheet .window-content .carac-value, - .window-app.sheet .window-content .competence-value { + :is(.window-app, .application).sheet .window-content .carac-value, + :is(.window-app, .application).sheet .window-content .competence-value { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; text-align: center; } - .window-app.sheet .window-content .carac-value, - .window-app.sheet .window-content .competence-archetype { + :is(.window-app, .application).sheet .window-content .carac-value, + :is(.window-app, .application).sheet .window-content .competence-archetype { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; text-align: center; } - .window-app.sheet .window-content .at-archetype { + :is(.window-app, .application).sheet .window-content .at-archetype { border-left: 3px solid var(--color-archetype-atteint, #b8860b); } - .window-app.sheet .window-content .archetype-check { + :is(.window-app, .application).sheet .window-content .archetype-check { color: var(--color-archetype-atteint, #b8860b); font-size: 0.75rem; margin-left: 0.15rem; } - .window-app.sheet .window-content .carac-value, - .window-app.sheet .window-content .competence-xp-sort { + :is(.window-app, .application).sheet .window-content .carac-value, + :is(.window-app, .application).sheet .window-content .competence-xp-sort { flex-grow: 0; margin: 0.05rem; flex-basis: 2rem; diff --git a/module/applications/sheets/base-actor-sheet.mjs b/module/applications/sheets/base-actor-sheet.mjs new file mode 100644 index 00000000..f65b10a5 --- /dev/null +++ b/module/applications/sheets/base-actor-sheet.mjs @@ -0,0 +1,468 @@ +const { HandlebarsApplicationMixin } = foundry.applications.api + +import { RdDUtility } from "../../rdd-utility.js" +import { Misc } from "../../misc.js" +import { DialogSplitItem } from "../../dialog-split-item.js" +import { RdDSheetUtility } from "../../rdd-sheet-utility.js" +import { Monnaie } from "../../item-monnaie.js" +import { ITEM_TYPES } from "../../constants.js" +import { RdDItem } from "../../item.js" +import { RdDTextEditor } from "../../apps/rdd-text-roll-editor.js" +import { ItemAction } from "../../item/item-actions.js" +import { DialogVerifierIngredients } from "../../dialog-verifier-ingredients.js" +import { DialogLancerRecette } from "../../dialog-lancer-recette.js" +import { ChatUtility } from "../../chat-utility.js" +import { RdDItemBlessure } from "../../item/blessure.js" +import { Grammar } from "../../grammar.js" +import { SYSTEM_RDD } from "../../constants.js" + +export class RdDBaseActorSheetV2 extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) { + + get actor() { return this.document } + + // Was this.options.* — custom state + vueDetaillee = false + triEquipement = "name" + recherche = undefined + + static TYPE = undefined + + static DEFAULT_OPTIONS = { + classes: ["rdd", "sheet", "actor"], + position: { width: 640 }, + form: { submitOnChange: true, closeOnSubmit: false }, + window: { resizable: true, controls: [] }, + actions: { + postToChat: RdDBaseActorSheetV2._onPostToChat, + }, + } + + //#region Registration + + static register() { + foundry.documents.collections.Actors.registerSheet(SYSTEM_RDD, this, { + types: [this.TYPE], + makeDefault: true, + }) + } + + static async registerAll(...sheetClasses) { + const handlebars = [] + sheetClasses.forEach(sheetClass => { + if (sheetClass.PARTS) { + Object.values(sheetClass.PARTS).forEach(part => { + if (part.template) handlebars.push(part.template) + }) + } + sheetClass.register() + }) + await foundry.applications.handlebars.loadTemplates(Misc.distinct(handlebars)) + } + + //#endregion + + //#region Data + + async _prepareContext() { + Monnaie.validerMonnaies(this.actor) + this.actor.computeEtatGeneral() + + this._sheetOptions = foundry.utils.mergeObject( + RdDSheetUtility.mergeDocumentRights({ ...this.options }, this.actor, this.isEditable), + { vueDetaillee: this.vueDetaillee, triEquipement: this.triEquipement, recherche: this.recherche }, + { inplace: false } + ) + const ctx = { + title: this.title, + id: this.actor.id, + type: this.actor.type, + img: this.actor.img, + name: this.actor.name, + system: this.actor.system, + systemFields: this.actor.system.schema.fields, + description: await RdDTextEditor.enrichHTML(this.actor.system.description, this.actor), + notesmj: await RdDTextEditor.enrichHTML(this.actor.system.notesmj, this.actor), + options: this._sheetOptions, + effects: this.actor.effects, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + limited: this.actor.limited, + owner: this.actor.isOwner, + } + + RdDUtility.filterItemsPerTypeForSheet(ctx, this.actor.itemTypes, this.triEquipement) + ctx.calc = { + fortune: Monnaie.toSolsDeniers(this.actor.getFortune()), + prixTotalEquipement: this.actor.computePrixTotalEquipement(), + encTotal: this.actor.getEncTotal(), + } + + this.objetVersConteneur = RdDUtility.buildArbreDeConteneurs(ctx.conteneurs, ctx.inventaires) + this._appliquerRechercheObjets(ctx.conteneurs, ctx.inventaires) + ctx.conteneurs = RdDUtility.conteneursRacine(ctx.conteneurs) + ctx.competences.filter(it => it.type == ITEM_TYPES.competencecreature) + .forEach(it => it.isdommages = it.isDommages()) + + return ctx + } + + _appliquerRechercheObjets(conteneurs, inventaires) { + if (this.recherche?.text) { + const recherche = this.recherche + const allVisible = inventaires.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id) + let addVisible = conteneurs.filter(it => it.isNomTypeLike(recherche.text)).map(it => it.id) + do { + allVisible.push(...addVisible) + const parentsIds = conteneurs.filter(it => it.system.contenu.find(id => allVisible.includes(id))).map(it => it.id) + addVisible = parentsIds.filter(id => !allVisible.includes(id)) + } + while (addVisible.length > 0) + inventaires.forEach(it => it.system.isHidden = !allVisible.includes(it.id)) + conteneurs.forEach(it => it.system.isHidden = !allVisible.includes(it.id)) + } + else { + inventaires.forEach(it => it.system.isHidden = false) + conteneurs.forEach(it => it.system.isHidden = false) + } + } + + //#endregion + + //#region Rendering + + async _renderFrame(options) { + const frame = await super._renderFrame(options) + return frame + } + + async _onRender(context, options) { + await super._onRender(context, options) + this.html = $(this.element) + + const nav = this.element.querySelector(".sheet-tabs") + if (nav) { + const tab = this.tabGroups?.primary || nav.querySelector("[data-tab]")?.dataset.tab + if (tab) this.changeTab(tab, "primary", { force: true }) + } + + this.html.find(".actionItem").click(async event => await ItemAction.onActionItem(event, this.actor, this._sheetOptions)) + this.html.find(".item-edit").click(async event => await this._onItemEdit(event)) + this.html.find(".item-verify-ingredients").click(async event => await this._onVerifyIngredients(event)) + this.html.find(".item-lancer-recette").click(async event => await this._onLancerRecette(event)) + this.html.find(".conteneur-name a").click(async event => { + RdDUtility.toggleAfficheContenu(this._getItemId(event)) + this.render({ force: true }) + }) + + this.html.find(".actor-montrer").click(async event => await this.actor.postActorToChat()) + + this.html.find(".recherche") + .each((_index, field) => this._rechercheSelectArea(field)) + .keyup(async event => this._rechercherKeyup(event)) + .change(async event => this._rechercherKeyup(event)) + + this.html.find(".recherche").prop("disabled", false) + + this.html.find(".item-list .item").dblclick(async event => { + const item = this._getItem(event) + if (item) item.sheet.render({ force: true }) + }) + + this.html.find(".tri-equipement").change(event => { + this.triEquipement = event.currentTarget.value + this.render({ force: true }) + }) + + if (!this.isEditable) return + + this.html.find(".item-equip-armure").click(async event => await this.actor.equiperObjet(this._getItem(event))) + this.html.find(".item-delete").click(async event => await RdDUtility.confirmActorItemDelete(this._getItem(event), this.actor)) + this.html.find(".item-quantite-plus").click(async event => await this.actor.itemQuantiteIncDec(this._getItemId(event), 1)) + this.html.find(".item-quantite-moins").click(async event => await this.actor.itemQuantiteIncDec(this._getItemId(event), -1)) + this.html.find(".item-quantite").change(async event => { + const input = event.currentTarget + const item = this.actor.items.get(input.dataset.itemId) + if (item) { + const newQty = Math.max(0, parseInt(input.value) || 0) + await item.update({ "system.quantite": newQty }) + if (newQty === 0) this.render({ force: true }) + } + }) + this.html.find(".item-quantite").on("contextmenu", async event => { + event.preventDefault() + const item = this._getItem(event) + if (!item?.isInventaire()) return + new Dialog({ + title: `Quantité — ${item.name}`, + content: `

Réduire la quantité de :

+ `, + buttons: { + reduce: { + icon: '', + label: "Retirer", + callback: html => { + const amount = html.find(".qty-amount").val() + if (amount === "all") { + item.delete() + this.render({ force: true }) + } else { + item.update({ "system.quantite": Math.max(0, item.system.quantite - parseInt(amount)) }) + } + }, + }, + }, + }).render({ force: true }) + }) + + this.html.find(".creer-un-objet").click(async event => await this._selectObjetTypeToCreate()) + this.html.find(".nettoyer-conteneurs").click(async event => await this.actor.nettoyerConteneurs()) + this.html.find(".quick-add-ration").click(async event => { + const existante = this.actor.items.find(it => it.type === "nourritureboisson" && it.name === "Ration") + if (existante) { + await existante.update({ "system.quantite": Math.max(0, Number(existante.system.quantite) + 1) }) + } else { + await this.actor.createEmbeddedDocuments("Item", [{ + name: "Ration", type: "nourritureboisson", + img: "systems/foundryvtt-reve-de-dragon/icons/objets/provision_crue.webp", + system: { sust: 1, encombrement: 0.1, quantite: 1, cout: 0.1, boisson: false }, + }], { renderSheet: false }) + } + this.render({ force: true }) + }) + this.html.find(".quick-add-eau").click(async event => { + const existante = this.actor.items.find(it => it.type === "nourritureboisson" && it.name === "Eau") + if (existante) { + await existante.update({ "system.quantite": Math.max(0, Number(existante.system.quantite) + 1) }) + } else { + await this.actor.createEmbeddedDocuments("Item", [{ + name: "Eau", type: "nourritureboisson", + img: "systems/foundryvtt-reve-de-dragon/icons/objets/outre.webp", + system: { desaltere: 1, encombrement: 0.1, quantite: 1, cout: 0.05, boisson: true }, + }], { renderSheet: false }) + } + this.render({ force: true }) + }) + + this.html.find(".vue-detaillee").click(async event => { + this.vueDetaillee = !this.vueDetaillee + this.render({ force: true }) + }) + + // Revel listeners (was base-actor-reve-sheet.js) + this.html.find(".button-encaissement").click(async event => await this.actor.encaisser()) + this.html.find(".roll-carac").click(async event => { + await this.actor.rollCarac(Grammar.toLowerCaseNoAccent(event.currentTarget.attributes["data-carac-name"].value)) + }) + this.html.find(".roll-competence").click(async event => await this.actor.rollCompetence(this._getItemId(event))) + this.html.find(".endurance-plus").click(async event => await this.actor.santeIncDec("endurance", 1)) + this.html.find(".endurance-moins").click(async event => await this.actor.santeIncDec("endurance", -1)) + + if (game.user.isGM) { + this.html.find(".button-remise-a-neuf").click(async event => await this.actor.remiseANeuf()) + this.html.find(".delete-active-effect").click(async event => await this.actor.removeEffect(this.html.find(event.currentTarget).parents(".active-effect").data("effect"))) + this.html.find(".enlever-tous-effets").click(async event => await this.actor.removeEffects()) + } + this.html.find(".competence-add").click(async event => + await this.actor.createEmbeddedDocuments("Item", [{ + type: ITEM_TYPES.competencecreature, + name: "Nouvelle competence", + img: "systems/foundryvtt-reve-de-dragon/icons/compcreature-serres.webp", + system: { carac_value: this.actor.getForce() }, + }], { renderSheet: true }) + ) + this.html.find(".roll-text").click(async event => await RdDTextEditor.rollText(event, this.actor)) + this.html.find(".chat-roll-text").click(async event => await RdDTextEditor.chatRollText(event)) + + if (this.vueDetaillee) { + this.html.find(".carac-value").change(async event => { + if (event.currentTarget.name.includes("carac.")) { + const caracName = event.currentTarget.name.replace("carac.", "") + await this.actor.updateCarac(caracName, parseInt(event.currentTarget.value)) + } + }) + this.html.find(".competence-value").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCompetence(compName, parseInt(event.currentTarget.value)) + }) + } + + // Sang listeners (was base-actor-sang-sheet.js) + this.html.find(".creer-blessure-legere").click(async event => await RdDItemBlessure.createBlessure(this.actor, 2)) + this.html.find(".creer-blessure-grave").click(async event => await RdDItemBlessure.createBlessure(this.actor, 4)) + this.html.find(".creer-blessure-critique").click(async event => await RdDItemBlessure.createBlessure(this.actor, 6)) + this.html.find(".subir-blessure-contusion").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 0)) + this.html.find(".subir-blessure-legere").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 2)) + this.html.find(".subir-blessure-grave").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 4)) + this.html.find(".subir-blessure-critique").click(async event => await RdDItemBlessure.applyFullBlessure(this.actor, 6)) + this.html.find(".jet-vie").click(async event => await this.actor.jetDeVie()) + this.html.find(".jet-endurance").click(async event => await this._jetEndurance()) + this.html.find(".vie-plus").click(async event => await this.actor.santeIncDec("vie", 1)) + this.html.find(".vie-moins").click(async event => await this.actor.santeIncDec("vie", -1)) + } + + async _jetEndurance() { + const endurance = this.actor.getEnduranceActuelle() + const result = await this.actor.jetEndurance(endurance) + await ChatMessage.create({ + content: `Jet d'Endurance : ${result.jetEndurance} / ${endurance} +
${this.actor.name} a ${result.sonne ? "échoué" : "réussi"} son Jet d'Endurance ${result.sonne ? "et devient Sonné" : ""}`, + whisper: ChatUtility.getOwners(this.actor), + }) + } + + _rechercherKeyup(event) { + const currentTarget = event.currentTarget + const nouvelleRecherche = this._optionRecherche(currentTarget) + if (this.recherche?.text != nouvelleRecherche?.text) { + this.recherche = nouvelleRecherche + if (this._timerRecherche) clearTimeout(this._timerRecherche) + this._timerRecherche = setTimeout(() => { + this._timerRecherche = undefined + this.render({ force: true }) + }, 500) + } + } + + _rechercheSelectArea(field) { + if (this.recherche) { + field.focus() + field.setSelectionRange(this.recherche.start, this.recherche.end) + } + } + + _optionRecherche(target) { + if (!target.value?.length) return undefined + return { + text: target.value, + start: target.selectionStart, + end: target.selectionEnd, + } + } + + //#endregion + + //#region Drop + + async _onDropItem(event, item) { + const destItemId = this.html.find(event.target)?.closest(".item").attr("data-item-id") + const previousItemIds = new Set(this.actor.items.keys()) + const dropParams = await RdDSheetUtility.prepareItemDropParameters(destItemId, this.actor, { uuid: item.uuid }, this.objetVersConteneur) + if (dropParams) { + const callSuper = await this.actor.processDropItem(dropParams) + if (callSuper) { + await super._onDropItem(event, item) + } + if (destItemId && dropParams.sourceActorId !== this.actor.id) { + const container = this.actor.items.get(destItemId) + if (container?.isConteneur()) { + const dropped = [...this.actor.items].find(i => !previousItemIds.has(i.id)) + if (dropped && !this.actor.findConteneur(dropped)) { + await this.actor.ajouterDansConteneur(dropped, container, () => {}) + } + } + } + } + } + + async _onDropActor(event, actor) { + this.actor.addSubActeur(actor) + await super._onDropActor(event, actor) + } + + //#endregion + + //#region Helpers + + _getItemId(event) { + return RdDSheetUtility.getItemId(event) + } + + _getItem(event) { + return RdDSheetUtility.getItem(event, this.actor) + } + + async _onItemEdit(event) { + const item = this._getItem(event) + if (item) item.sheet.render({ force: true }) + } + + async _onVerifyIngredients(event) { + event.preventDefault() + const item = this._getItem(event) + if (!item || item.type !== ITEM_TYPES.recettealchimique) return + DialogVerifierIngredients.create(item, this.actor) + } + + async _onLancerRecette(event) { + event.preventDefault() + const item = this._getItem(event) + if (!item || item.type !== ITEM_TYPES.recettealchimique) return + const ingredients = Misc.safeJsonParse(item.system.ingredients, []) + if (!ingredients.length) { + ui.notifications.warn("Cette recette n'a pas d'ingrédients définis.") + return + } + DialogLancerRecette.create(item, this.actor) + } + + //#endregion + + //#region Actions + + static async _onPostToChat(event, target) { + await this.actor.postActorToChat() + } + + //#endregion + + //#region Item operations + + async _selectObjetTypeToCreate() { + const types = this._getTypesInventaire().sort(Misc.ascending(type => Misc.typeName("Item", type))) + let content = `Selectionnez le type d'équipement" + new Dialog({ + title: "Créer un équipement", + content: content, + buttons: { + create: { + icon: '', + label: "Créer l'objet", + callback: () => this.actor.createItem($(".item-type").val()), + }, + }, + }).render({ force: true }) + } + + _getTypesInventaire() { + return RdDItem.getItemTypesInventaire() + } + + async splitItem(item) { + const dialog = await DialogSplitItem.create(item, (it, split) => this._onSplitItem(it, split)) + dialog.render({ force: true }) + } + + async _onSplitItem(item, split) { + if (split >= 1 && split < item.system.quantite) { + await item.diminuerQuantite(split) + const splitItem = foundry.utils.deepClone(item) + splitItem.system.quantite = split + await this.actor.createEmbeddedDocuments("Item", [splitItem]) + } + } + + vendre(item) { + item?.proposerVente(this.actor.getQuantiteDisponible(item)) + } + + //#endregion +} diff --git a/module/applications/sheets/commerce-sheet.mjs b/module/applications/sheets/commerce-sheet.mjs new file mode 100644 index 00000000..b5e0ae4f --- /dev/null +++ b/module/applications/sheets/commerce-sheet.mjs @@ -0,0 +1,52 @@ +import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs" +import { RdDItem } from "../../item.js" +import { ACTOR_TYPES } from "../../constants.js" + +export class RdDCommerceSheetV2 extends RdDBaseActorSheetV2 { + + static TYPE = ACTOR_TYPES.commerce + + static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, { + position: { width: 600, height: 720 }, + }) + + static PARTS = { + form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-actor-sheet.hbs" }, + } + + get title() { + if (this.actor.token && this.actor.token !== this.actor.prototypeToken) { + return this.actor.token.name + } + return super.title + } + + async _prepareContext() { + const ctx = await super._prepareContext() + if (this.actor.token && this.actor.token !== this.actor.prototypeToken) { + foundry.utils.mergeObject(ctx, { + title: this.actor.token.name, + token: { img: this.actor.token.texture.src }, + }, { overwrite: true }) + } + return ctx + } + + async _onRender(context, options) { + await super._onRender(context, options) + if (!this.isEditable) return + + this.html.find("input.item-quantite").change(async event => { + const newQuantite = Math.max(0, Number.parseInt(this.html.find(event.currentTarget).val())) + await this._getItem(event)?.update({ "system.quantite": newQuantite }) + }) + this.html.find("input.item-cout").change(async event => { + const newCout = Math.max(0, Number(this.html.find(event.currentTarget).val())) + await this._getItem(event)?.update({ "system.cout": newCout }) + }) + } + + _getTypesInventaire() { + return RdDItem.getItemTypesInventaire("all") + } +} diff --git a/module/applications/sheets/creature-sheet.mjs b/module/applications/sheets/creature-sheet.mjs new file mode 100644 index 00000000..15e05b6b --- /dev/null +++ b/module/applications/sheets/creature-sheet.mjs @@ -0,0 +1,33 @@ +import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs" +import { ACTOR_TYPES } from "../../constants.js" + +export class RdDCreatureSheetV2 extends RdDBaseActorSheetV2 { + + static TYPE = ACTOR_TYPES.creature + + static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, { + position: { width: 640, height: 720 }, + }) + + static PARTS = { + form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor-creature-sheet.hbs" }, + } + + async _onRender(context, options) { + await super._onRender(context, options) + if (!this.isEditable) return + + this.html.find(".creature-carac").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value)) + }) + this.html.find(".creature-niveau").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCreatureCompetence(compName, "niveau", parseInt(event.target.value)) + }) + this.html.find(".creature-dommages").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value)) + }) + } +} diff --git a/module/applications/sheets/entite-sheet.mjs b/module/applications/sheets/entite-sheet.mjs new file mode 100644 index 00000000..b204189f --- /dev/null +++ b/module/applications/sheets/entite-sheet.mjs @@ -0,0 +1,81 @@ +import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs" +import { RdDSheetUtility } from "../../rdd-sheet-utility.js" +import { RdDUtility } from "../../rdd-utility.js" +import { DialogSelect } from "../../dialog-select.js" +import { ACTOR_TYPES } from "../../constants.js" + +export class RdDActorEntiteSheetV2 extends RdDBaseActorSheetV2 { + + static TYPE = ACTOR_TYPES.entite + + static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, { + position: { width: 640, height: 720 }, + }) + + static PARTS = { + form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor-entite-sheet.hbs" }, + } + + async _prepareContext() { + const ctx = await super._prepareContext() + ctx.niveau = this.actor.getNiveau() + const { niveau: _, ...caracRest } = ctx.system.carac + ctx.system = { ...ctx.system, carac: caracRest } + ctx.resonances = this.actor.system.sante.resonnance.actors + .map(actorId => game.actors.get(actorId)) + .filter(actor => actor != undefined) + .map(actor => ({ id: actor.id, name: actor.name, img: actor.img })) + return ctx + } + + async _onRender(context, options) { + await super._onRender(context, options) + if (!this.isEditable) return + + this.html.find(".creature-carac").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCreatureCompetence(compName, "carac_value", parseInt(event.target.value)) + }) + this.html.find(".creature-dommages").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCreatureCompetence(compName, "dommages", parseInt(event.target.value)) + }) + + this.html.find(".resonance-add").click(async event => + await DialogSelect.select({ + label: "Choisir un acteur à accorder", + list: game.actors.filter(it => true), + }, + it => this._resonanceAdd(it.id)) + ) + + this.html.find(".resonance-delete").click(async event => { + const li = RdDSheetUtility.getEventElement(event) + const actorId = li.data("actor-id") + if (actorId) { + const actorResonance = game.actors.get(actorId) + RdDUtility.confirmSubActeurDelete(this, actorResonance, li, () => { + this._resonanceDelete(actorId) + RdDUtility.slideOnDelete(this, li) + }) + } + }) + } + + async _onDropActor(event, dragData) { + const dropActor = fromUuidSync(dragData.uuid) + await this.actor.setEntiteReveAccordee(dropActor) + await super._onDropActor(event, dragData) + } + + async _resonanceAdd(actorId) { + const newResonances = [...this.actor.system.sante.resonnance.actors, actorId] + await this.actor.update({ "system.sante.resonnance.actors": newResonances }) + } + + async _resonanceDelete(actorId) { + console.log("Delete : ", actorId) + const newResonances = this.actor.system.sante.resonnance.actors.filter(id => id !== actorId) + await this.actor.update({ "system.sante.resonnance.actors": newResonances }, { render: false }) + } +} diff --git a/module/applications/sheets/export-encart-sheet.mjs b/module/applications/sheets/export-encart-sheet.mjs new file mode 100644 index 00000000..548b6d21 --- /dev/null +++ b/module/applications/sheets/export-encart-sheet.mjs @@ -0,0 +1,118 @@ +import { RdDActorSheetV2 } from "./personnage-sheet.mjs" +import { SYSTEM_RDD } from "../../constants.js" +import { Misc } from "../../misc.js" +import { EXPORT_CSV_SCRIPTARIUM, OptionsAvancees } from "../../settings/options-avancees.js" +import { ExportScriptarium } from "../../actor/export-scriptarium/export-scriptarium.js" +import { CATEGORIES_COMPETENCES_BASE, CATEGORIES_DRACONIC, Mapping } from "../../actor/export-scriptarium/mapping.js" + +export class RdDActorExportSheetV2 extends RdDActorSheetV2 { + + static TYPE = "personnage" + + static DEFAULT_OPTIONS = Object.assign({}, RdDActorSheetV2.DEFAULT_OPTIONS, { + position: { width: 550 }, + }) + + static PARTS = { + form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/actor-encart-sheet.hbs" }, + } + + showCompNiveauBase = false + vueArchetype = false + + static async init() { + await foundry.applications.handlebars.loadTemplates([ + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/arme-titre.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessure.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/blessures.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-compteur.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/carac-derivee-compteur.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/competences.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/esquive.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/fatigue.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/protection.hbs", + "systems/foundryvtt-reve-de-dragon/templates/actor/export-scriptarium/sort.hbs", + ]) + } + + static register() { + foundry.documents.collections.Actors.registerSheet(SYSTEM_RDD, this, { + types: ["personnage"], + makeDefault: false, + label: "Feuille simplifiée", + }) + } + + async _prepareContext() { + const ctx = await super._prepareContext() + ctx.context = Mapping.prepareContext(this.actor) + ctx.attaques = this.actor.listActionsAttaque() + ctx.export = this._getMappingValues(ctx.context, this.actor) + ctx.competences = this._getCompetences(CATEGORIES_COMPETENCES_BASE) + ctx.draconic = this._getCompetences(CATEGORIES_DRACONIC) + const legeres = this.actor.countBlessures(it => it.isLegere()) + const graves = this.actor.countBlessures(it => it.isGrave()) + const critiques = this.actor.countBlessures(it => it.isCritique()) + ctx.etat = { + surenc: this.actor.computeMalusSurEncombrement(), + fatigue: { + value: this.actor.getFatigueActuelle(), + max: this.actor.getFatigueMax(), + malus: this.actor.malusFatigue(), + }, + blessures: legeres + graves + critiques, + blessure: [legeres > 0, legeres > 1, legeres > 2, legeres > 3, legeres > 4, graves > 0, graves > 1, critiques > 0], + } + ctx.options.exportScriptarium = OptionsAvancees.isUsing(EXPORT_CSV_SCRIPTARIUM) + return ctx + } + + _getMappingValues(context, actor) { + return Object.fromEntries(Mapping.getMapping().map(it => [it.column, { + colName: it.colName ?? it.column, + column: it.column, + rollClass: it.rollClass, + value: String(it.getter(actor, context)), + }])) + } + + _getCompetences(categories) { + const competences = Mapping.getCompetencesCategorie(this.actor, categories) + if (competences.length == 0) return "" + const byCategories = Mapping.competencesByCategoriesByNiveau(competences, categories) + const listByCategories = Object.values(byCategories) + .map(it => it.competencesParNiveau) + .map(byNiveau => { + const niveaux = Object.keys(byNiveau).map(it => Number(it)).sort(Misc.ascending()) + if (niveaux.length == 0) return undefined + const listCategorieByNiveau = niveaux.map(niveau => { + const list = byNiveau[niveau].sort(Misc.ascending(it => it.name)) + return { niveau, list } + }) + return Misc.concat(listCategorieByNiveau) + }).filter(it => it != undefined) + return Misc.concat(listByCategories) + } + + async _onRender(context, options) { + await super._onRender(context, options) + + this.html.find(".click-blessure-remove").click(async event => + await this.actor.supprimerBlessure({ + gravite: this.html.find(event.currentTarget).data("gravite"), + }) + ) + this.html.find(".click-blessure-add").click(async event => { + const blessure = this.actor.nouvelleBlessure(this.html.find(event.currentTarget).data("gravite")) + await this.actor.createEmbeddedDocuments("Item", [blessure]) + }) + this.html.find(".button-export").click(async event => await + ExportScriptarium.INSTANCE.exportActors([this.actor], + `${this.actor.uuid}-${this.actor.name}` + ) + ) + } +} diff --git a/module/applications/sheets/personnage-sheet.mjs b/module/applications/sheets/personnage-sheet.mjs new file mode 100644 index 00000000..26c7f061 --- /dev/null +++ b/module/applications/sheets/personnage-sheet.mjs @@ -0,0 +1,403 @@ +import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs" +import { RdDUtility } from "../../rdd-utility.js" +import { HtmlUtility } from "../../html-utility.js" +import { RdDBonus } from "../../rdd-bonus.js" +import { Misc } from "../../misc.js" +import { RdDCombatManager } from "../../rdd-combat.js" +import { CARACS, RdDCarac } from "../../rdd-carac.js" +import { DialogSplitItem } from "../../dialog-split-item.js" +import { ReglesOptionnelles } from "../../settings/regles-optionnelles.js" +import { RdDSheetUtility } from "../../rdd-sheet-utility.js" +import { MAINS_DIRECTRICES } from "../../actor.js" +import { ACTOR_TYPES, ITEM_TYPES } from "../../constants.js" +import { RdDItem } from "../../item.js" +import { RdDItemCompetence } from "../../item-competence.js" +import { RdDItemBlessure } from "../../item/blessure.js" +import { RdDEmpoignade } from "../../rdd-empoignade.js" +import { RdDCoeur } from "../../coeur/rdd-coeur.js" +import { AppPersonnageAleatoire } from "../../actor/random/app-personnage-aleatoire.js" +import { RdDTextEditor } from "../../apps/rdd-text-roll-editor.js" +import { MORAL } from "../../moral/apprecier.mjs" +import { RdDItemSort } from "../../item-sort.js" + +export class RdDActorSheetV2 extends RdDBaseActorSheetV2 { + + static TYPE = ACTOR_TYPES.personnage + + static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, { + position: { width: 640 }, + }) + + static PARTS = { + form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor-sheet.hbs" }, + } + + showCompNiveauBase = false + vueArchetype = false + armesList = [] + + async _prepareContext() { + const ctx = await super._prepareContext() + ctx.options.showCompNiveauBase = this.showCompNiveauBase + ctx.options.vueArchetype = this.vueArchetype + ctx.biographie = await RdDTextEditor.enrichHTML(ctx.system.biographie, this.actor) + ctx.notes = await RdDTextEditor.enrichHTML(ctx.system.notes, this.actor) + + foundry.utils.mergeObject(ctx.calc, { + surenc: this.actor.computeMalusSurEncombrement(), + surprise: RdDBonus.find(this.actor.getSurprise(false)).label, + blessures: this.actor.computeResumeBlessure(ctx.system.blessures), + caracTotal: RdDCarac.computeTotal(ctx.system.carac, ctx.system.beaute), + caracTotalXp: RdDCarac.computeTotalXp(ctx.system.carac, ctx.system.beaute), + surEncombrementMessage: this.actor.isSurenc() ? "Sur-Encombrement!" : "", + malusArmure: this.actor.getMalusArmure(), + }) + + this._timerRecherche = undefined + + if (ctx.type == ACTOR_TYPES.personnage) { + ctx.options.mainsDirectrices = MAINS_DIRECTRICES + ctx.byCateg = Misc.classify(ctx.competences, it => it.system.categorie) + foundry.utils.mergeObject(ctx.calc, { + comptageArchetype: RdDItemCompetence.computeResumeArchetype(ctx.competences), + competenceXPTotal: RdDItemCompetence.computeTotalXP(ctx.competences), + sortsXPTotal: RdDItemSort.computeTotalXP(this.actor.itemTypes[ITEM_TYPES.sort]), + fatigue: RdDUtility.calculFatigueHtml(ctx.system.sante.fatigue.value, ctx.system.sante.endurance.max), + }) + this._amendTacheCommunes(ctx) + + ctx.competences.forEach(item => { + item.system.isHidden = this.recherche + ? !item.isNomLike(this.recherche.text) + : (this.showCompNiveauBase && RdDItemCompetence.isNiveauBase(item)) + RdDItemCompetence.levelUp(item, ctx.system.compteurs.experience.value) + }) + + Object.values(ctx.system.carac).forEach(c => RdDCarac.levelUp(c)) + + ctx.esquives = this.actor.getCompetencesEsquive() + ctx.combat = this.actor.listActionsAttaque() + ctx.empoignades = this.actor.getEmpoignades() + this.armesList = ctx.combat + + ctx.ajustementsConditions = CONFIG.RDD.ajustementsConditions + ctx.difficultesLibres = CONFIG.RDD.difficultesLibres + + ctx.hautreve = { + isDemiReve: this.actor.isDemiReve(), + cacheTMR: this.actor.isTMRCache(), + } + + ctx.livresLus = ctx.livres.filter(l => l.system.estCopieLue) + ctx.livres = ctx.livres.filter(l => !l.system.estCopieLue) + + ctx.race = this.actor.itemTypes[ITEM_TYPES.race].find(it => true) + ctx.subacteurs = { + vehicules: this.actor.listeVehicules(), + montures: this.actor.listeMontures(), + suivants: this.actor.listeSuivants(), + } + + if (this.actor.getDraconics().find(it => it.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! +
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`) + } + } + else { + console.error("Ouverture actor-sheet avec un acteur non personnage") + } + + return ctx + } + + _amendTacheCommunes(ctx) { + ctx.taches.forEach(tache => { + if (tache.isTacheCommune()) { + const tacheCommune = tache.getTacheOuCommune() + tache.system = foundry.utils.deepClone(tacheCommune.system) + tache.system.tacheCommuneId = tache.system.tacheCommuneId + } + return tache + }) + } + + async _onRender(context, options) { + await super._onRender(context, options) + if (!this.isEditable) return + + HtmlUtility.showControlWhen(this.html.find(".appliquerFatigue"), ReglesOptionnelles.isUsing("appliquer-fatigue")) + + this.html.find(".subacteur-open").click(async event => { + const subActorId = RdDSheetUtility.getEventItemData(event, "subactor-id") + this._openSubActeur(subActorId) + }) + + this.html.find(".show-hide-competences").click(async event => { + this.showCompNiveauBase = !this.showCompNiveauBase + this.render({ force: true }) + }) + + this.html.find(".button-tmr-visu").click(async event => await this.actor.displayTMR("visu")) + + this.html.find(".sheet-possession-attack").click(async event => { + const poss = RdDSheetUtility.getItem(event, this.actor) + await this.actor.conjurerPossession(poss) + }) + + this.html.find(".subacteur-coeur-toggle a").click(async event => { + const subActorId = RdDSheetUtility.getEventItemData(event, "subactor-id") + const coeurNombre = $(event.currentTarget).data("numero-coeur") + await RdDCoeur.toggleSubActeurCoeur(this.actor.id, subActorId, coeurNombre) + }) + this.html.find(".subacteur-tendre-moment").click(async event => { + const subActorId = RdDSheetUtility.getEventItemData(event, "subactor-id") + await RdDCoeur.startSubActeurTendreMoment(this.actor.id, subActorId) + }) + this.html.find(".subacteur-delete").click(async event => { + const li = RdDSheetUtility.getEventElement(event) + const subActorId = li.data("subactor-id") + this._deleteSubActeur(subActorId, li) + }) + + this.html.find("input.derivee-value[name='system.compteurs.stress.value']").change(async event => + await this.actor.updateCompteurValue("stress", parseInt(event.target.value)) + ) + this.html.find("input.derivee-value[name='system.compteurs.experience.value']").change(async event => + await this.actor.updateCompteurValue("experience", parseInt(event.target.value)) + ) + + this.html.find(".creer-tache").click(async event => await this._createEmptyTache()) + this.html.find(".creer-une-oeuvre").click(async event => await this._selectTypeOeuvreToCreate()) + this.html.find(".creer-tache-blessure-legere").click(async event => await RdDItemBlessure.createTacheSoinBlessure(this.actor, 2)) + this.html.find(".creer-tache-blessure-grave").click(async event => await RdDItemBlessure.createTacheSoinBlessure(this.actor, 4)) + this.html.find(".creer-tache-blessure-critique").click(async event => await RdDItemBlessure.createTacheSoinBlessure(this.actor, 6)) + + this.html.find(".blessure-premierssoins-done").change(async event => { + await this._getBlessure(event)?.setSoinsBlessure({ premierssoins: { done: event.currentTarget.checked } }) + }) + this.html.find(".blessure-soinscomplets-done").change(async event => { + await this._getBlessure(event)?.setSoinsBlessure({ soinscomplets: { done: event.currentTarget.checked } }) + }) + this.html.find(".blessure-premierssoins-bonus").change(async event => { + await this._getBlessure(event)?.setSoinsBlessure({ premierssoins: { bonus: Number(event.currentTarget.value) } }) + }) + this.html.find(".blessure-soinscomplets-bonus").change(async event => { + await this._getBlessure(event)?.setSoinsBlessure({ soinscomplets: { bonus: Number(event.currentTarget.value) } }) + }) + + this.html.find(".roll-chance-actuelle").click(async event => await this.actor.rollCarac(CARACS.CHANCE_ACTUELLE)) + this.html.find(".button-appel-chance").click(async event => await this.actor.rollAppelChance()) + + this.html.find('[name="jet-astrologie"]').click(async event => await this.actor.astrologieNombresAstraux()) + this.html.find(".action-tache").click(async event => await this.actor.rollTache(RdDSheetUtility.getItemId(event))) + this.html.find(".meditation-label a").click(async event => await this.actor.rollMeditation(RdDSheetUtility.getItemId(event))) + + this.html.find(".action-chant").click(async event => await this.actor.rollChant(RdDSheetUtility.getItemId(event))) + this.html.find(".action-danse").click(async event => await this.actor.rollDanse(RdDSheetUtility.getItemId(event))) + this.html.find(".action-musique").click(async event => await this.actor.rollMusique(RdDSheetUtility.getItemId(event))) + this.html.find(".action-oeuvre").click(async event => await this.actor.rollOeuvre(RdDSheetUtility.getItemId(event))) + this.html.find(".action-jeu").click(async event => await this.actor.rollJeu(RdDSheetUtility.getItemId(event))) + this.html.find(".action-recettecuisine").click(async event => await this.actor.rollRecetteCuisine(RdDSheetUtility.getItemId(event))) + + this.html.find(".description-aleatoire").click(async event => new AppPersonnageAleatoire(this.actor).render({ force: true })) + + this.html.find(".gerer-herbes").click(async event => { + const { DialogGererHerbes } = await import("../../dialog-gerer-herbes.js") + DialogGererHerbes.create(this.actor) + }) + this.html.find(".gerer-ingredients").click(async event => { + const { DialogGererIngredients } = await import("../../dialog-gerer-ingredients.js") + DialogGererIngredients.create(this.actor) + }) + this.html.find(".gerer-deniers").click(async event => { + const { DialogGererDeniers } = await import("../../dialog-gerer-deniers.js") + DialogGererDeniers.create(this.actor) + }) + this.html.find(".gerer-potions").click(async event => { + const { DialogGererPotions } = await import("../../dialog-gerer-potions.js") + DialogGererPotions.create(this.actor) + }) + + if (game.user.isGM) { + 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(".forcer-tmr-aleatoire").click(async event => await this.actor.reinsertionAleatoire("Action MJ")) + this.html.find(".don-de-haut-reve").click(async event => await this.actor.addDonDeHautReve()) + this.html.find(".afficher-tmr").click(async event => await this.actor.changeTMRVisible()) + } + + this.html.find(".roll-reve-actuel").click(async event => await this.actor.rollReveActuel({ resistance: false })) + this.html.find(".button-reve-resistance").click(async event => await this.actor.rollReveResistance()) + this.html.find(".action-empoignade").click(async event => await RdDEmpoignade.onAttaqueEmpoignadeFromItem(RdDSheetUtility.getItem(event, this.actor))) + + this.html.find(".roll-arme").click(async event => { + const action = this._getActionCombat(event) + await this.actor.rollArme(action.arme, action.main) + }) + + this.html.find(".roll-init-arme").click(async event => { + const combatant = game.combat.combatants.find(c => c.actor.id == this.actor.id) + if (combatant) { + RdDCombatManager.rollInitiativeAction(combatant.id, this._getActionCombat(event)) + } + else { + ui.notifications.info("Impossible de lancer l'initiative sans être dans un combat.") + } + }) + + this.html.find(".button-tmr").click(async event => await this.actor.displayTMR("normal")) + this.html.find(".button-tmr-rapide").click(async event => await this.actor.displayTMR("rapide")) + + this.html.find(".button-repos").click(async event => await this.actor.repos()) + + this.html.find(".carac-xp-augmenter").click(async event => + await this.actor.updateCaracXPAuto(event.currentTarget.name.replace("augmenter.", "")) + ) + this.html.find(".competence-xp-augmenter").click(async event => + await this.actor.updateCompetenceXPAuto(RdDSheetUtility.getItemId(event)) + ) + this.html.find(".competence-stress-augmenter").click(async event => { + await this.actor.updateCompetenceStress(RdDSheetUtility.getItemId(event)) + this.render({ force: true }) + }) + + if (this.vueDetaillee) { + this.html.find("input.carac-xp").change(async event => { + const caracName = event.currentTarget.name.replace(".xp", "").replace("system.carac.", "") + await this.actor.updateCaracXP(caracName, parseInt(event.target.value)) + }) + this.html.find("input.competence-xp").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCompetenceXP(compName, parseInt(event.target.value)) + }) + this.html.find("input.competence-xp-sort").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCompetenceXPSort(compName, parseInt(event.target.value)) + }) + + this.html.find(".toggle-archetype").click(async event => { + this.vueArchetype = !this.vueArchetype + this.render({ force: true }) + }) + this.html.find(".competence-archetype").change(async event => { + const compName = event.currentTarget.attributes.compname.value + await this.actor.updateCompetenceArchetype(compName, parseInt(event.target.value)) + }) + this.html.find(".nouvelle-incarnation").click(async event => await this.actor.nouvelleIncarnation()) + this.html.find(".repartition-archetype-aleatoire").click(async event => await this.actor.repartitionArchetypeAleatoire()) + } + + this.html.find(".pointsreve-value").change(async event => + await this.actor.update({ "system.reve.reve.value": event.currentTarget.value }) + ) + this.html.find(".seuil-reve-value").change(async event => + await this.actor.update({ "system.reve.seuil.value": event.currentTarget.value }) + ) + + this.html.find(".stress-test").click(async event => await this.actor.transformerStress()) + this.html.find(".moral-malheureux").click(async event => await this.actor.jetDeMoral(MORAL.MALHEUREUX)) + this.html.find(".moral-neutre").click(async event => await this.actor.jetDeMoral(MORAL.NEUTRE)) + this.html.find(".moral-heureux").click(async event => await this.actor.jetDeMoral(MORAL.HEUREUX)) + this.html.find(".button-ethylisme").click(async event => await this.actor.jetEthylisme()) + this.html.find(".btn-sust-externe").click(async event => await this.actor.mangerExterne()) + this.html.find(".btn-sust-rations").click(async event => await this.actor.mangerRations()) + + this.html.find(".ptreve-actuel-plus").click(async event => await this.actor.reveActuelIncDec(1)) + this.html.find(".ptreve-actuel-moins").click(async event => await this.actor.reveActuelIncDec(-1)) + this.html.find(".chance-actuelle-plus").click(async event => await this.actor.chanceActuelleIncDec(1)) + this.html.find(".chance-actuelle-moins").click(async event => await this.actor.chanceActuelleIncDec(-1)) + this.html.find(".fatigue-plus").click(async event => await this.actor.santeIncDec("fatigue", 1)) + this.html.find(".fatigue-moins").click(async event => await this.actor.santeIncDec("fatigue", -1)) + } + + _getBlessure(event) { + const blessureId = this.html.find(event.currentTarget).parents(".item-blessure").data("item-id") + return this.actor.getItem(blessureId, "blessure") + } + + isCompetenceAffichable(competence) { + return !this.showCompNiveauBase || !RdDItemCompetence.isNiveauBase(competence) + } + + async _onDropActor(event, dragData) { + const dropActor = fromUuidSync(dragData.uuid) + this.actor.addSubActeur(dropActor) + await super._onDrop(event, dragData) + } + + _openSubActeur(actorId) { + game.actors.get(actorId)?.sheet.render({ force: true }) + } + + _deleteSubActeur(actorId, li) { + if (actorId) { + const subActor = game.actors.get(actorId) + RdDUtility.confirmSubActeurDelete(this, subActor, li, () => { + console.log("Delete : ", subActor.id) + this.actor.deleteSubActeur(subActor.id) + RdDUtility.slideOnDelete(this, li) + }) + } + } + + async _selectTypeOeuvreToCreate() { + const types = RdDItem.getTypesOeuvres() + let content = `Selectionnez le type d'oeuvre" + new Dialog({ + title: "Créer une oeuvre", + content: content, + buttons: { + create: { + icon: '', + label: "Créer l'oeuvre", + callback: () => this.actor.createItem($(".item-type").val()), + }, + }, + }).render({ force: true }) + } + + async _createEmptyTache() { + await this.actor.createItem(ITEM_TYPES.tache, "Nouvelle tache") + } + + _getActionCombat(event) { + const li = this.html.find(event.currentTarget)?.parents(".item") + const armeName = li.data("arme-name") + const compName = li.data("competence-name") + const arme = this.armesList.find(a => a.arme.name == armeName && a.comp.name == compName) + if (!arme) { + return { + name: armeName, + arme: { name: armeName }, + comp: { name: compName }, + } + } + return arme + } + + async splitItem(item) { + const dialog = await DialogSplitItem.create(item, (it, split) => this._onSplitItem(it, split)) + dialog.render({ force: true }) + } + + async _onSplitItem(item, split) { + if (split >= 1 && split < item.system.quantite) { + await item.diminuerQuantite(split) + const splitItem = foundry.utils.deepClone(item) + splitItem.system.quantite = split + await this.actor.createEmbeddedDocuments("Item", [splitItem]) + } + } +} diff --git a/module/applications/sheets/vehicule-sheet.mjs b/module/applications/sheets/vehicule-sheet.mjs new file mode 100644 index 00000000..c8608e87 --- /dev/null +++ b/module/applications/sheets/vehicule-sheet.mjs @@ -0,0 +1,36 @@ +import { RdDBaseActorSheetV2 } from "./base-actor-sheet.mjs" +import { ACTOR_TYPES } from "../../constants.js" + +export class RdDActorVehiculeSheetV2 extends RdDBaseActorSheetV2 { + + static TYPE = ACTOR_TYPES.vehicule + + static DEFAULT_OPTIONS = Object.assign({}, RdDBaseActorSheetV2.DEFAULT_OPTIONS, { + position: { width: 640, height: 720 }, + }) + + static PARTS = { + form: { template: "systems/foundryvtt-reve-de-dragon/templates/actor-vehicule-sheet.hbs" }, + } + + async _prepareContext() { + const ctx = await super._prepareContext() + ctx.editable = this.isEditable + ctx.cssClass = this.isEditable ? "editable" : "locked" + ctx.effects = this.actor.effects + ctx.limited = this.actor.limited + ctx.owner = this.actor.isOwner + this.recherche = undefined + return ctx + } + + async _onRender(context, options) { + await super._onRender(context, options) + if (!this.isEditable) return + + this.html.find(".resistance-moins").click(async event => await this.actor.vehicleIncDec("resistance", -1)) + this.html.find(".resistance-plus").click(async event => await this.actor.vehicleIncDec("resistance", 1)) + this.html.find(".structure-moins").click(async event => await this.actor.vehicleIncDec("structure", -1)) + this.html.find(".structure-plus").click(async event => await this.actor.vehicleIncDec("structure", 1)) + } +} diff --git a/module/models/_actor-helpers.mjs b/module/models/_actor-helpers.mjs new file mode 100644 index 00000000..20808fd5 --- /dev/null +++ b/module/models/_actor-helpers.mjs @@ -0,0 +1,55 @@ +const fields = foundry.data.fields; + +export function statField(initialValue = 0, label = "", derivee = false) { + return new fields.SchemaField({ + type: new fields.StringField({ initial: "number" }), + value: new fields.NumberField({ initial: initialValue, integer: true }), + label: new fields.StringField({ initial: label }), + xp: new fields.NumberField({ initial: 0, integer: true }), + derivee: new fields.BooleanField({ initial: derivee }), + }); +} + +export function santeField(initialValue = 0, initialMax = 0, label = "", derivee = false) { + return new fields.SchemaField({ + type: new fields.StringField({ initial: "number" }), + value: new fields.NumberField({ initial: initialValue, integer: true }), + max: new fields.NumberField({ initial: initialMax, integer: true }), + label: new fields.StringField({ initial: label }), + derivee: new fields.BooleanField({ initial: derivee }), + }); +} + +export function attributField(initialValue = 0, label = "", derivee = false, isString = false) { + if (isString) { + return new fields.SchemaField({ + type: new fields.StringField({ initial: "string" }), + value: new fields.StringField({ initial: String(initialValue) }), + label: new fields.StringField({ initial: label }), + derivee: new fields.BooleanField({ initial: derivee }), + }); + } + return new fields.SchemaField({ + type: new fields.StringField({ initial: "number" }), + value: new fields.NumberField({ initial: initialValue, integer: false }), + label: new fields.StringField({ initial: label }), + derivee: new fields.BooleanField({ initial: derivee }), + }); +} + +export function compteurField(initialValue = 0, label = "", isInput = false) { + return new fields.SchemaField({ + value: new fields.NumberField({ initial: initialValue, integer: true }), + label: new fields.StringField({ initial: label }), + ...(isInput ? { isInput: new fields.BooleanField({ initial: true }) } : {}), + }); +} + +export function compteurMaxField(initialValue = 0, initialMax = 0, label = "") { + return new fields.SchemaField({ + value: new fields.NumberField({ initial: initialValue, integer: true }), + max: new fields.NumberField({ initial: initialMax, integer: true }), + label: new fields.StringField({ initial: label }), + isInput: new fields.BooleanField({ initial: true }), + }); +} diff --git a/module/models/commerce.mjs b/module/models/commerce.mjs new file mode 100644 index 00000000..eeb8c1eb --- /dev/null +++ b/module/models/commerce.mjs @@ -0,0 +1,15 @@ +import { STRING, HTMLSTRING } from "../common/field-types.mjs"; + +const fields = foundry.data.fields; + +export default class RdDModelCommerce extends foundry.abstract.TypeDataModel { + static defineSchema() { + return { + pourcentage: new fields.NumberField({ initial: 100, integer: true }), + illimite: new fields.BooleanField({ initial: false }), + description: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + race: new fields.StringField({ initial: "", ...STRING }), + notesmj: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + }; + } +} diff --git a/module/models/creature.mjs b/module/models/creature.mjs new file mode 100644 index 00000000..2e893b07 --- /dev/null +++ b/module/models/creature.mjs @@ -0,0 +1,48 @@ +import { STRING, HTMLSTRING } from "../common/field-types.mjs"; +import { statField, santeField, attributField, compteurField } from "./_actor-helpers.mjs"; + +const fields = foundry.data.fields; + +export default class RdDModelCreature extends foundry.abstract.TypeDataModel { + static defineSchema() { + return { + carac: new fields.SchemaField({ + taille: statField(10, "Taille"), + constitution: statField(10, "Constitution"), + force: statField(10, "Force"), + perception: statField(10, "Perception"), + volonte: statField(10, "Volonté"), + reve: statField(10, "Rêve"), + }), + sante: new fields.SchemaField({ + vie: santeField(10, 10, "Vie"), + endurance: santeField(10, 10, "Endurance"), + bonusPotion: new fields.NumberField({ initial: 0, integer: true }), + }), + attributs: new fields.SchemaField({ + plusdom: attributField(0, "+dom", true), + encombrement: attributField(0.0, "Encombrement", true), + vitesse: attributField(0, "Vitesse", false, true), + protection: attributField(0, "Protection naturelle", false), + }), + compteurs: new fields.SchemaField({ + etat: compteurField(0, "Etat général"), + }), + description: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + race: new fields.StringField({ initial: "", ...STRING }), + notesmj: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + }; + } + + prepareDerivedData() { + const actor = this.parent; + + this.sante.vie.max = Math.ceil((actor.getTaille() + actor.getConstitution()) / 2); + this.sante.vie.value = Math.min(this.sante.vie.value, this.sante.vie.max); + this.attributs.encombrement.value = actor.getEncombrementMax(); + + this.attributs.plusdom.value = actor.getBonusDegat(); + this.sante.endurance.max = actor.getEnduranceMax(); + this.sante.endurance.value = Math.min(this.sante.endurance.value, this.sante.endurance.max); + } +} diff --git a/module/models/entite.mjs b/module/models/entite.mjs new file mode 100644 index 00000000..e86a69ca --- /dev/null +++ b/module/models/entite.mjs @@ -0,0 +1,48 @@ +import { STRING, HTMLSTRING } from "../common/field-types.mjs"; +import { statField, santeField, attributField, compteurField } from "./_actor-helpers.mjs"; + +const fields = foundry.data.fields; + +export default class RdDModelEntite extends foundry.abstract.TypeDataModel { + static defineSchema() { + return { + definition: new fields.SchemaField({ + categorieentite: new fields.StringField({ initial: "", ...STRING }), + typeentite: new fields.StringField({ initial: "", ...STRING }), + }), + carac: new fields.SchemaField({ + taille: statField(10, "Taille"), + reve: statField(10, "Rêve"), + niveau: statField(0, "Niveau"), + }), + sante: new fields.SchemaField({ + endurance: santeField(10, 10, "Endurance"), + resonnance: new fields.SchemaField({ + actors: new fields.ArrayField( + new fields.StringField({ initial: "", ...STRING }), + { initial: [], required: true, nullable: false } + ), + }), + }), + compteurs: new fields.SchemaField({ + etat: compteurField(0, "Etat général"), + }), + attributs: new fields.SchemaField({ + plusdom: attributField(0, "+dom", true), + vitesse: attributField(0, "Vitesse", false, true), + protection: attributField(0, "Protection naturelle", false), + }), + description: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + race: new fields.StringField({ initial: "", ...STRING }), + notesmj: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + }; + } + + prepareDerivedData() { + const actor = this.parent; + + this.attributs.plusdom.value = actor.getBonusDegat(); + this.sante.endurance.max = actor.getEnduranceMax(); + this.sante.endurance.value = Math.min(this.sante.endurance.value, this.sante.endurance.max); + } +} diff --git a/module/models/personnage.mjs b/module/models/personnage.mjs new file mode 100644 index 00000000..3a460174 --- /dev/null +++ b/module/models/personnage.mjs @@ -0,0 +1,188 @@ +import { STRING, HTMLSTRING, MODEL_ARRAY } from "../common/field-types.mjs"; +import { statField, santeField, attributField, compteurField, compteurMaxField } from "./_actor-helpers.mjs"; +import { ITEM_TYPES } from "../constants.js"; +import { RdDItemRace } from "../item/race.js"; +import { RdDCarac } from "../rdd-carac.js"; +import RdDItemTete from "../documents/tete.mjs"; +import { Misc } from "../misc.js"; + +const fields = foundry.data.fields; + +export default class RdDModelPersonnage extends foundry.abstract.TypeDataModel { + static defineSchema() { + return { + carac: new fields.SchemaField({ + taille: statField(10, "Taille"), + apparence: statField(10, "Apparence"), + constitution: statField(10, "Constitution"), + force: statField(10, "Force"), + agilite: statField(10, "Agilité"), + dexterite: statField(10, "Dextérité"), + vue: statField(10, "Vue"), + ouie: statField(10, "Ouïe"), + odoratgout: statField(10, "Odorat-Goût"), + volonte: statField(10, "Volonté"), + intellect: statField(10, "Intellect"), + empathie: statField(10, "Empathie"), + reve: statField(10, "Rêve"), + chance: statField(10, "Chance"), + melee: statField(10, "Mêlée", true), + tir: statField(10, "Tir", true), + lancer: statField(10, "Lancer", true), + derobee: statField(10, "Dérobée", true), + }), + sante: new fields.SchemaField({ + vie: santeField(10, 10, "Vie", true), + endurance: santeField(10, 10, "Endurance", true), + fatigue: santeField(0, 0, "Fatigue", true), + bonusPotion: new fields.NumberField({ initial: 0, integer: true }), + }), + attributs: new fields.SchemaField({ + sconst: attributField(0, "S. Const", true), + sust: attributField(0, "Sustentation", true), + plusdom: attributField(0, "+dom", true), + encombrement: attributField(0.0, "Encombrement", true), + protection: attributField(0, "Protection naturelle", false), + hautrevant: attributField("", "Haut rêvant", true, true), + }), + reve: new fields.SchemaField({ + reve: new fields.SchemaField({ + value: new fields.NumberField({ initial: 10, integer: true }), + max: new fields.NumberField({ initial: 0, integer: true }), + label: new fields.StringField({ initial: "Points de Rêve actuels" }), + thanatosused: new fields.BooleanField({ initial: false }), + }), + seuil: new fields.SchemaField({ + value: new fields.NumberField({ initial: 10, integer: true }), + max: new fields.NumberField({ initial: 0, integer: true }), + label: new fields.StringField({ initial: "Seuil de Rêve" }), + }), + tmrpos: new fields.SchemaField({ + coord: new fields.StringField({ initial: "A1" }), + label: new fields.StringField({ initial: "Position TMR" }), + cache: new fields.BooleanField({ initial: false }), + }), + refoulement: new fields.SchemaField({ + value: new fields.NumberField({ initial: 0, integer: true }), + label: new fields.StringField({ initial: "Points de Refoulement" }), + }), + }), + compteurs: new fields.SchemaField({ + sust: compteurField(0, "Sust.", true), + eau: compteurField(0, "Eau", true), + moral: compteurField(0, "Moral", true), + exaltation: compteurField(0, "Exaltation", true), + dissolution: compteurField(0, "Dissolution", true), + chance: compteurMaxField(0, 0, "Chance actuelle"), + destinee: compteurField(0, "Destinée", true), + voyage: compteurField(0, "Voyage", true), + etat: compteurField(0, "Etat général"), + ethylisme: new fields.SchemaField({ + value: new fields.NumberField({ initial: 1, integer: true }), + label: new fields.StringField({ initial: "Ethylisme" }), + isInput: new fields.BooleanField({ initial: true }), + nb_doses: new fields.NumberField({ initial: 0, integer: true }), + jet_moral: new fields.BooleanField({ initial: false }), + }), + stress: new fields.SchemaField({ + value: new fields.NumberField({ initial: 0, integer: true }), + label: new fields.StringField({ initial: "Stress" }), + isStress: new fields.BooleanField({ initial: true }), + isInput: new fields.BooleanField({ initial: true }), + }), + experience: compteurField(0, "Experience", true), + bonmoments: new fields.ArrayField( + new fields.SchemaField({ + value: new fields.NumberField({ initial: 0, integer: true }), + label: new fields.StringField({ initial: "" }), + }), + { ...MODEL_ARRAY } + ), + }), + sommeil: new fields.SchemaField({ + nouveaujour: new fields.BooleanField({ initial: false }), + date: new fields.SchemaField({ + indexDate: new fields.NumberField({ initial: -1, integer: true }), + indexMinute: new fields.NumberField({ initial: 0, integer: true }), + }), + insomnie: new fields.BooleanField({ initial: false }), + moral: new fields.StringField({ initial: "" }), + heures: new fields.NumberField({ initial: 0, integer: true }), + }), + metier: new fields.StringField({ initial: "", ...STRING }), + biographie: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + notes: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + notesmj: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + race: new fields.StringField({ initial: "", ...STRING }), + yeux: new fields.StringField({ initial: "", ...STRING }), + cheveux: new fields.StringField({ initial: "", ...STRING }), + taille: new fields.StringField({ initial: "", ...STRING }), + poids: new fields.StringField({ initial: "", ...STRING }), + heure: new fields.StringField({ initial: "", ...STRING }), + sexe: new fields.StringField({ initial: "", ...STRING }), + age: new fields.NumberField({ initial: 0, integer: true }), + beaute: new fields.NumberField({ initial: 10, integer: true }), + main: new fields.StringField({ initial: "droitier", ...STRING }), + experiencelog: new fields.ArrayField( + new fields.SchemaField({ + date: new fields.NumberField({ initial: 0, integer: true }), + topic: new fields.StringField({ initial: "", ...STRING }), + from: new fields.NumberField({ initial: 0, integer: true }), + to: new fields.NumberField({ initial: 0, integer: true }), + description: new fields.StringField({ initial: "", ...STRING }), + }), + { ...MODEL_ARRAY } + ), + subacteurs: new fields.SchemaField({ + suivants: new fields.ArrayField( + new fields.SchemaField({ + id: new fields.StringField({ initial: "", ...STRING }), + }), + { ...MODEL_ARRAY } + ), + montures: new fields.ArrayField( + new fields.SchemaField({ + id: new fields.StringField({ initial: "", ...STRING }), + }), + { ...MODEL_ARRAY } + ), + vehicules: new fields.ArrayField( + new fields.SchemaField({ + id: new fields.StringField({ initial: "", ...STRING }), + }), + { ...MODEL_ARRAY } + ), + }), + description: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + }; + } + + prepareDerivedData() { + const actor = this.parent; + + RdDItemRace.applyRacialLimits(actor); + + this.carac.melee.value = Math.floor((actor.getForce() + Misc.toInt(this.carac.agilite.value)) / 2); + this.carac.tir.value = Math.floor((Misc.toInt(this.carac.vue.value) + Misc.toInt(this.carac.dexterite.value)) / 2); + this.carac.lancer.value = Math.floor((Misc.toInt(this.carac.tir.value) + Misc.toInt(this.carac.force.value)) / 2); + this.carac.derobee.value = Math.floor(((21 - Misc.toInt(this.carac.taille.value)) + Misc.toInt(this.carac.agilite.value)) / 2); + + this.sante.vie.max = Math.ceil((actor.getTaille() + actor.getConstitution()) / 2); + this.sante.vie.value = Math.min(this.sante.vie.value, this.sante.vie.max); + this.attributs.encombrement.value = actor.getEncombrementMax(); + + this.attributs.plusdom.value = actor.getBonusDegat(); + this.sante.endurance.max = actor.getEnduranceMax(); + this.sante.endurance.value = Math.min(this.sante.endurance.value, this.sante.endurance.max); + + this.attributs.sconst.value = RdDCarac.calculSConst(actor.getConstitution()); + this.attributs.sust.value = RdDCarac.getCaracDerivee(actor.getTaille()).sust; + this.sante.fatigue.max = actor.getFatigueMax(); + this.sante.fatigue.value = Math.min(this.sante.fatigue.value, this.sante.fatigue.max); + this.reve.reve.max = 3 * actor.getReve(); + this.compteurs.chance.max = actor.getChance(); + this.attributs.hautrevant.value = actor.itemTypes[ITEM_TYPES.tete]?.find(it => RdDItemTete.isDonDeHautReve(it)) + ? "Haut rêvant" + : ""; + } +} diff --git a/module/models/vehicule.mjs b/module/models/vehicule.mjs new file mode 100644 index 00000000..8bb99f1b --- /dev/null +++ b/module/models/vehicule.mjs @@ -0,0 +1,35 @@ +import { STRING, HTMLSTRING, INTEGER } from "../common/field-types.mjs"; + +const fields = foundry.data.fields; + +export default class RdDModelVehicule extends foundry.abstract.TypeDataModel { + static defineSchema() { + return { + categorie: new fields.StringField({ initial: "", ...STRING }), + vitesse: new fields.StringField({ initial: "", ...STRING }), + bonus: new fields.StringField({ initial: "", ...STRING }), + manoeuvrabilite: new fields.StringField({ initial: "", ...STRING }), + equipage: new fields.NumberField({ initial: 0, ...INTEGER }), + capacite_encombrement: new fields.NumberField({ initial: 0.0, integer: false }), + etat: new fields.SchemaField({ + resistance: new fields.SchemaField({ + type: new fields.StringField({ initial: "number" }), + max: new fields.NumberField({ initial: 20, integer: true }), + value: new fields.NumberField({ initial: 20, integer: true }), + label: new fields.StringField({ initial: "Résistance" }), + derivee: new fields.BooleanField({ initial: false }), + }), + structure: new fields.SchemaField({ + type: new fields.StringField({ initial: "number" }), + max: new fields.NumberField({ initial: 10, integer: true }), + value: new fields.NumberField({ initial: 10, integer: true }), + label: new fields.StringField({ initial: "Structure" }), + derivee: new fields.BooleanField({ initial: false }), + }), + }), + description: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + race: new fields.StringField({ initial: "", ...STRING }), + notesmj: new fields.HTMLField({ initial: "", ...HTMLSTRING }), + }; + } +} diff --git a/templates/actor-creature-sheet.hbs b/templates/actor-creature-sheet.hbs index 65fb5c7c..a2a223c1 100644 --- a/templates/actor-creature-sheet.hbs +++ b/templates/actor-creature-sheet.hbs @@ -1,4 +1,4 @@ -
+
{{!-- Sheet Header --}}
@@ -34,9 +34,9 @@ {{!-- Sheet Tab Navigation --}} {{!-- Sheet Body --}} @@ -70,13 +70,16 @@ {{!-- Description Tab --}}
-
- {{editor description target="system.description" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}} -
+

Description

+
+
+ {{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}} +
+
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
-
-
\ No newline at end of file + + \ No newline at end of file diff --git a/templates/actor-entite-sheet.hbs b/templates/actor-entite-sheet.hbs index 64953118..48398f91 100644 --- a/templates/actor-entite-sheet.hbs +++ b/templates/actor-entite-sheet.hbs @@ -1,4 +1,4 @@ -
+
{{!-- Sheet Header --}}
@@ -48,12 +48,15 @@ -
- {{editor description target="system.description" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}} -
+

Description

+
+
+ {{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}} +
+
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
-
\ No newline at end of file + \ No newline at end of file diff --git a/templates/actor-sheet.hbs b/templates/actor-sheet.hbs index 247b89ac..db4bd4be 100644 --- a/templates/actor-sheet.hbs +++ b/templates/actor-sheet.hbs @@ -1,4 +1,4 @@ -
+
{{>'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs'}} {{!-- Sheet Header --}} @@ -22,14 +22,14 @@ {{!-- Sheet Tab Navigation --}} {{!-- Sheet Body --}} @@ -152,9 +152,9 @@ {{> "systems/foundryvtt-reve-de-dragon/templates/actor/liens-animaux.hbs"}} {{> "systems/foundryvtt-reve-de-dragon/templates/actor/liens-vehicules.hbs"}} - + {{!-- Biography Tab --}} -
+
  • @@ -225,18 +225,21 @@

    Biographie

    -
    - {{editor biographie target="system.biographie" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}} -
    +
    +
    + {{formInput systemFields.biographie enriched=biographie value=system.biographie name="system.biographie" toggled=true}} +
    +

    Notes

    -
    - {{editor notes target="system.notes" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}} -
    +
    +
    + {{formInput systemFields.notes enriched=notes value=system.notes name="system.notes" toggled=true}} +
    +
    {{> "systems/foundryvtt-reve-de-dragon/templates/actor/xp-journal.hbs"}} {{> "systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
-
- + diff --git a/templates/actor-vehicule-sheet.hbs b/templates/actor-vehicule-sheet.hbs index e5441298..1e9b1e37 100644 --- a/templates/actor-vehicule-sheet.hbs +++ b/templates/actor-vehicule-sheet.hbs @@ -1,4 +1,4 @@ -
+
{{!-- Sheet Header --}}
@@ -36,8 +36,8 @@ {{!-- Sheet Tab Navigation --}} {{!-- Sheet Body --}} @@ -83,9 +83,12 @@ -
- {{editor description target="system.description" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}} -
+

Description

+
+
+ {{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}} +
+
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}} @@ -96,4 +99,4 @@
-
\ No newline at end of file + \ No newline at end of file diff --git a/templates/actor/commerce-actor-sheet.hbs b/templates/actor/commerce-actor-sheet.hbs index 8452057e..28339fdf 100644 --- a/templates/actor/commerce-actor-sheet.hbs +++ b/templates/actor/commerce-actor-sheet.hbs @@ -1,4 +1,4 @@ -
+
{{!-- Sheet Header --}}
@@ -36,7 +36,12 @@ {{!-- Sheet Body --}}
- {{editor description target="system.description" button=true owner=options.isOwner editable=options.isOwner engine="prosemirror"}} +
+
+ + {{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}} +
+

{{> "systems/foundryvtt-reve-de-dragon/templates/actor/commerce-inventaire.hbs"}} @@ -50,4 +55,4 @@ {{> "systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
- \ No newline at end of file +
\ No newline at end of file diff --git a/templates/actor/editor-notes-mj.hbs b/templates/actor/editor-notes-mj.hbs index 32c17a6e..1d89b9d6 100644 --- a/templates/actor/editor-notes-mj.hbs +++ b/templates/actor/editor-notes-mj.hbs @@ -1,6 +1,8 @@ {{#if options.isGM}}

Notes du MJ :

-
- {{editor notesmj target="system.notesmj" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}} -
+
+
+ {{formInput systemFields.notesmj enriched=notesmj value=system.notesmj name="system.notesmj" toggled=true}} +
+
{{/if}} diff --git a/templates/actor/export-scriptarium/actor-encart-sheet.hbs b/templates/actor/export-scriptarium/actor-encart-sheet.hbs index f5556a63..3f8cac2d 100644 --- a/templates/actor/export-scriptarium/actor-encart-sheet.hbs +++ b/templates/actor/export-scriptarium/actor-encart-sheet.hbs @@ -1,4 +1,4 @@ -
+
@@ -128,4 +128,4 @@
{{/if}}
- \ No newline at end of file +
\ No newline at end of file