appv2: correction des éditeurs ProseMirror et scrollbar sur les fiches d'acteur

- Migration des fiches acteur V2 : racine <form> → <section>
  (DocumentSheetV2 encapsule déjà dans <form>, élimine les formulaires imbriqués)
- Ajout de submitOnChange: true sur la feuille de base acteur
  (nécessaire pour que les événements change des éditeurs déclenchent la mise à jour)
- Ajout de toggled=true + wrapper <fieldset><div><label> sur tous les formInput
  (comportement identique aux fiches d'objet)
- CSS : .window-content > section ajouté à côté de > form
  (évite la perte du flex layout et des scrollbar en passant de form à section)
- Modèles acteur : passage de StringField à HTMLField pour les champs
  description/notes/biographie/notesmj (permet le rendu ProseMirror)
- Ajout de systemFields dans le contexte _prepareContext()
This commit is contained in:
fabres
2026-07-22 00:29:56 +02:00
parent 8fddbe698c
commit 84ce2f78d7
22 changed files with 1796 additions and 98 deletions
+82 -31
View File
@@ -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;
+68 -20
View File
@@ -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;
@@ -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: `<p>Réduire la quantité de :</p>
<select class="qty-amount">
<option value="1">1</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="all">Tout jeter</option>
</select>`,
buttons: {
reduce: {
icon: '<i class="fas fa-minus"></i>',
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}
<br>${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 = `<span class="generic-label">Selectionnez le type d'équipement</span><select class="item-type">`
for (const typeName of types) {
content += `<option value="${typeName}">${Misc.typeName("Item", typeName)}</option>`
}
content += "</select>"
new Dialog({
title: "Créer un équipement",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
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
}
@@ -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")
}
}
@@ -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))
})
}
}
@@ -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 })
}
}
@@ -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}`
)
)
}
}
@@ -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!
<br>Ajoutez-lui la tête "Don de Haut-Rêve" pour lui permettre d'utiliser ses compétences et d'accéder aux terres médianes du rêve`)
}
}
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 = `<span class="generic-label">Selectionnez le type d'oeuvre</span><select class="item-type">`
for (const typeName of types) {
content += `<option value="${typeName}">${Misc.typeName("Item", typeName)}</option>`
}
content += "</select>"
new Dialog({
title: "Créer une oeuvre",
content: content,
buttons: {
create: {
icon: '<i class="fas fa-check"></i>',
label: "Créer l'oeuvre",
callback: () => this.actor.createItem($(".item-type").val()),
},
},
}).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])
}
}
}
@@ -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))
}
}
+55
View File
@@ -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 }),
});
}
+15
View File
@@ -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 }),
};
}
}
+48
View File
@@ -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);
}
}
+48
View File
@@ -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);
}
}
+188
View File
@@ -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"
: "";
}
}
+35
View File
@@ -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 }),
};
}
}
+12 -9
View File
@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<section class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
@@ -34,9 +34,9 @@
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="carac">Caractéristiques</a>
<a class="item" data-tab="items">Équipement</a>
<a class="item" data-tab="description">Description</a>
<a class="item" data-action="tab" data-group="primary" data-tab="carac">Caractéristiques</a>
<a class="item" data-action="tab" data-group="primary" data-tab="items">Équipement</a>
<a class="item" data-action="tab" data-group="primary" data-tab="description">Description</a>
</nav>
{{!-- Sheet Body --}}
@@ -70,13 +70,16 @@
{{!-- Description Tab --}}
<div class="tab description" data-group="primary" data-tab="description">
<div class="form-group large-editor">
{{editor description target="system.description" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
<h3>Description</h3>
<fieldset>
<div>
{{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}}
</div>
</fieldset>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
</div>
</section>
</form>
</section>
</section>
+8 -5
View File
@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<section class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
@@ -48,12 +48,15 @@
</div>
</div>
</div>
<div class="form-group large-editor">
{{editor description target="system.description" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
<h3>Description</h3>
<fieldset>
<div>
{{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}}
</div>
</fieldset>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
</div>
</section>
</form>
</section>
+21 -18
View File
@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<section class="{{cssClass}}" autocomplete="off">
{{>'systems/foundryvtt-reve-de-dragon/templates/scripts/autocomplete-script.hbs'}}
{{!-- Sheet Header --}}
@@ -22,14 +22,14 @@
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
{{#if options.isObserver}}
<a class="item" data-tab="carac" data-tooltip="Caractéristiques">Carac.</a>
<a class="item" data-tab="competences" data-tooltip="Caractéristiques">Compétences</a>
<a class="item" data-tab="combat" data-tooltip="Combat et santé">Combat</a>
<a class="item" data-tab="connaissances" data-tooltip="Savoirs et tâches">Savoirs&Taches</a>
<a class="item" data-tab="hautreve" data-tooltip="Haut-rêve">Haut-Rêve</a>
<a class="item" data-action="tab" data-group="primary" data-tab="carac" data-tooltip="Caractéristiques">Carac.</a>
<a class="item" data-action="tab" data-group="primary" data-tab="competences" data-tooltip="Caractéristiques">Compétences</a>
<a class="item" data-action="tab" data-group="primary" data-tab="combat" data-tooltip="Combat et santé">Combat</a>
<a class="item" data-action="tab" data-group="primary" data-tab="connaissances" data-tooltip="Savoirs et tâches">Savoirs&Taches</a>
<a class="item" data-action="tab" data-group="primary" data-tab="hautreve" data-tooltip="Haut-rêve">Haut-Rêve</a>
{{/if}}
<a class="item" data-tab="items" data-tooltip="Liste d'équipement">Équipement</a>
<a class="item" data-tab="description" data-tooltip="Description et compagnons">Description</a>
<a class="item" data-action="tab" data-group="primary" data-tab="items" data-tooltip="Liste d'équipement">Équipement</a>
<a class="item" data-action="tab" data-group="primary" data-tab="description" data-tooltip="Description et compagnons">Description</a>
</nav>
{{!-- 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"}}
</div>
{{!-- Biography Tab --}}
<div class="tab biography" data-group="primary" data-tab="description">
<div class="tab biography" data-group="primary" data-tab="description">
<article class="flexrow">
<ul class="item-list alterne-list">
<li class="flexrow list-item" data-tooltip="Glisser une Race depuis un compendium pour changer">
@@ -225,18 +225,21 @@
<article class="flexcol">
<h3>Biographie</h3>
<div class="form-group large-editor">
{{editor biographie target="system.biographie" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
<fieldset>
<div>
{{formInput systemFields.biographie enriched=biographie value=system.biographie name="system.biographie" toggled=true}}
</div>
</fieldset>
<h3>Notes</h3>
<div class="form-group large-editor">
{{editor notes target="system.notes" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
<fieldset>
<div>
{{formInput systemFields.notes enriched=notes value=system.notes name="system.notes" toggled=true}}
</div>
</fieldset>
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/xp-journal.hbs"}}
{{> "systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
</article>
</div>
</section>
</form>
</section>
+10 -7
View File
@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<section class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
@@ -36,8 +36,8 @@
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<a class="item" data-tab="description">Description</a>
<a class="item" data-tab="items">Chargement</a>
<a class="item" data-action="tab" data-group="primary" data-tab="description">Description</a>
<a class="item" data-action="tab" data-group="primary" data-tab="items">Chargement</a>
</nav>
{{!-- Sheet Body --}}
@@ -83,9 +83,12 @@
</div>
</div>
<div class="form-group large-editor">
{{editor description target="system.description" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
<h3>Description</h3>
<fieldset>
<div>
{{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}}
</div>
</fieldset>
{{>"systems/foundryvtt-reve-de-dragon/templates/actor/editor-notes-mj.hbs"}}
</div>
@@ -96,4 +99,4 @@
</div>
</section>
</form>
</section>
+8 -3
View File
@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<section class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
@@ -36,7 +36,12 @@
{{!-- Sheet Body --}}
<section class="sheet-body">
<div class="flexcol form-group medium-editor">
{{editor description target="system.description" button=true owner=options.isOwner editable=options.isOwner engine="prosemirror"}}
<fieldset>
<div>
<label>Description :</label>
{{formInput systemFields.description enriched=description value=system.description name="system.description" toggled=true}}
</div>
</fieldset>
</div>
<hr>
{{> "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"}}
<br>
</section>
</form>
</section>
+5 -3
View File
@@ -1,6 +1,8 @@
{{#if options.isGM}}
<h3>Notes du MJ : </h3>
<div class="form-group large-editor">
{{editor notesmj target="system.notesmj" button=true owner=options.isOwner editable=options.editable engine="prosemirror"}}
</div>
<fieldset>
<div>
{{formInput systemFields.notesmj enriched=notesmj value=system.notesmj name="system.notesmj" toggled=true}}
</div>
</fieldset>
{{/if}}
@@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off" >
<section class="{{cssClass}}" autocomplete="off" >
<section class="sheet-header flexrow">
<div class="flex-grow-0-5">
<img class="profile-img" src="{{img}}" data-edit="img" data-tooltip="{{name}}" />
@@ -128,4 +128,4 @@
</div>
{{/if}}
</section>
</form>
</section>