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 :
+
+ 1
+ 5
+ 10
+ Tout jeter
+ `,
+ 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 `
+ for (const typeName of types) {
+ content += `${Misc.typeName("Item", typeName)} `
+ }
+ content += " "
+ 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 `
+ for (const typeName of types) {
+ content += `${Misc.typeName("Item", typeName)} `
+ }
+ content += " "
+ 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 @@
-
\ 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 @@
-
\ 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 @@
-
\ 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 @@
-
+
\ 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 @@
-
+
\ No newline at end of file
+
\ No newline at end of file