Migration datamodels !
This commit is contained in:
27
modules/applications/sheets/_module.mjs
Normal file
27
modules/applications/sheets/_module.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Index des applications AppV2 pour Yggdrasill
|
||||
* Ce fichier centralise tous les exports des applications
|
||||
*/
|
||||
|
||||
// Applications de feuilles d'acteurs
|
||||
export { default as YggdrasillPersonnageSheet } from './yggdrasill-personnage-sheet.mjs';
|
||||
export { default as YggdrasillFigurantSheet } from './yggdrasill-figurant-sheet.mjs';
|
||||
|
||||
// Applications de feuilles d'items
|
||||
export { default as YggdrasillCompetenceSheet } from './yggdrasill-competence-sheet.mjs';
|
||||
export { default as YggdrasillDonSheet } from './yggdrasill-don-sheet.mjs';
|
||||
export { default as YggdrasillFaiblesseSheet } from './yggdrasill-faiblesse-sheet.mjs';
|
||||
export { default as YggdrasillBlessureSheet } from './yggdrasill-blessure-sheet.mjs';
|
||||
export { default as YggdrasillMaladieSheet } from './yggdrasill-maladie-sheet.mjs';
|
||||
export { default as YggdrasillPoisonSheet } from './yggdrasill-poison-sheet.mjs';
|
||||
export { default as YggdrasillProuesseSheet } from './yggdrasill-prouesse-sheet.mjs';
|
||||
export { default as YggdrasillSortsejdrSheet } from './yggdrasill-sortsejdr-sheet.mjs';
|
||||
export { default as YggdrasillSortgaldrSheet } from './yggdrasill-sortgaldr-sheet.mjs';
|
||||
export { default as YggdrasillRuneSheet } from './yggdrasill-rune-sheet.mjs';
|
||||
export { default as YggdrasillArmeccSheet } from './yggdrasill-armecc-sheet.mjs';
|
||||
export { default as YggdrasillArmedistSheet } from './yggdrasill-armedist-sheet.mjs';
|
||||
export { default as YggdrasillArmureSheet } from './yggdrasill-armure-sheet.mjs';
|
||||
export { default as YggdrasillBouclierSheet } from './yggdrasill-bouclier-sheet.mjs';
|
||||
export { default as YggdrasillEquipementSheet } from './yggdrasill-equipement-sheet.mjs';
|
||||
export { default as YggdrasillMonnaieSheet } from './yggdrasill-monnaie-sheet.mjs';
|
||||
export { default as YggdrasillEffetmagiqueSheet } from './yggdrasill-effetmagique-sheet.mjs';
|
||||
449
modules/applications/sheets/base-actor-sheet.mjs
Normal file
449
modules/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,449 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
import { YggdrasillUtility } from "../../yggdrasill-utility.js"
|
||||
|
||||
export default class YggdrasillActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
/**
|
||||
* Different sheet modes.
|
||||
* @enum {number}
|
||||
*/
|
||||
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
}
|
||||
|
||||
#dragDrop
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["fvtt-yggdrasill", "sheet", "actor"],
|
||||
position: {
|
||||
width: 750,
|
||||
height: 720,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
navSelector: 'nav[data-group="primary"]',
|
||||
contentSelector: "section.sheet-body",
|
||||
initial: "principal",
|
||||
},
|
||||
],
|
||||
dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }],
|
||||
actions: {
|
||||
editImage: YggdrasillActorSheet.#onEditImage,
|
||||
toggleSheet: YggdrasillActorSheet.#onToggleSheet,
|
||||
editItem: YggdrasillActorSheet.#onEditItem,
|
||||
deleteItem: YggdrasillActorSheet.#onDeleteItem,
|
||||
createItem: YggdrasillActorSheet.#onCreateItem,
|
||||
equipItem: YggdrasillActorSheet.#onEquipItem,
|
||||
rollCarac: YggdrasillActorSheet.#onRollCarac,
|
||||
rollCompetence: YggdrasillActorSheet.#onRollCompetence,
|
||||
rollArme: YggdrasillActorSheet.#onRollArme,
|
||||
rollSort: YggdrasillActorSheet.#onRollSort,
|
||||
rollProuesse: YggdrasillActorSheet.#onRollProuesse,
|
||||
rollDamage: YggdrasillActorSheet.#onRollDamage,
|
||||
lockUnlock: YggdrasillActorSheet.#onLockUnlock,
|
||||
incrementPV: YggdrasillActorSheet.#onIncrementPV,
|
||||
decrementPV: YggdrasillActorSheet.#onDecrementPV,
|
||||
updateCompetence: YggdrasillActorSheet.#onUpdateCompetence,
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet currently in 'Play' mode?
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isPlayMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the sheet currently in 'Edit' mode?
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isEditMode() {
|
||||
if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab groups state
|
||||
* @type {object}
|
||||
*/
|
||||
tabGroups = {
|
||||
primary: "principal",
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = {
|
||||
fields: this.document.schema.fields,
|
||||
systemFields: this.document.system.schema.fields,
|
||||
actor: this.document,
|
||||
system: this.document.system,
|
||||
source: this.document.toObject(),
|
||||
isEditMode: this.isEditMode,
|
||||
isPlayMode: this.isPlayMode,
|
||||
isEditable: this.isEditable,
|
||||
isGM: game.user.isGM,
|
||||
config: game.system.yggdrasill.config,
|
||||
editScore: this.isEditMode,
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
|
||||
// Activate tab navigation manually
|
||||
const nav = this.element.querySelector('nav.tabs[data-group], nav.sheet-tabs[data-group]')
|
||||
if (nav) {
|
||||
const group = nav.dataset.group
|
||||
// Activate the current tab
|
||||
const activeTab = this.tabGroups[group] || "principal"
|
||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||
const tab = link.dataset.tab
|
||||
link.classList.toggle('active', tab === activeTab)
|
||||
link.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
this.tabGroups[group] = tab
|
||||
this.render()
|
||||
})
|
||||
})
|
||||
|
||||
// Show/hide tab content
|
||||
this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => {
|
||||
content.classList.toggle('active', content.dataset.tab === activeTab)
|
||||
})
|
||||
}
|
||||
|
||||
// Add change listener for competence niveau selects
|
||||
this.element.querySelectorAll('select.competence-niveau').forEach(select => {
|
||||
select.addEventListener('change', async (event) => {
|
||||
const itemId = event.target.dataset.itemId
|
||||
const item = this.document.items.get(itemId)
|
||||
if (item) {
|
||||
const newNiveau = parseInt(event.target.value)
|
||||
await item.update({ "system.niveau": newNiveau })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates drag-and-drop handlers for this application
|
||||
* @returns {DragDrop[]} An array of DragDrop handlers
|
||||
* @private
|
||||
*/
|
||||
#createDragDropHandlers() {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle changing a Document's image
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onEditImage(event, target) {
|
||||
const attr = target.dataset.edit
|
||||
const current = foundry.utils.getProperty(this.document, attr)
|
||||
const fp = new FilePicker({
|
||||
current,
|
||||
type: "image",
|
||||
callback: (path) => {
|
||||
this.document.update({ [attr]: path })
|
||||
},
|
||||
})
|
||||
return fp.browse()
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle sheet mode between Edit and Play
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onToggleSheet(event, target) {
|
||||
this._sheetMode = this.isEditMode
|
||||
? this.constructor.SHEET_MODES.PLAY
|
||||
: this.constructor.SHEET_MODES.EDIT
|
||||
this.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle item editing
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onEditItem(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
const item = this.document.items.get(itemId)
|
||||
if (item) item.sheet.render(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle item deletion
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onDeleteItem(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
const item = this.document.items.get(itemId)
|
||||
if (item) {
|
||||
await item.delete()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle item creation
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onCreateItem(event, target) {
|
||||
const itemType = target.dataset.itemType
|
||||
const itemData = {
|
||||
name: `Nouveau ${itemType}`,
|
||||
type: itemType,
|
||||
}
|
||||
await this.document.createEmbeddedDocuments("Item", [itemData])
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle item equip toggle
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onEquipItem(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
const item = this.document.items.get(itemId)
|
||||
if (item && item.system.equipe !== undefined) {
|
||||
await item.update({ "system.equipe": !item.system.equipe })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle characteristic roll
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onRollCarac(event, target) {
|
||||
const caracCateg = target.dataset.caracCateg
|
||||
const caracKey = target.dataset.caracKey
|
||||
this.document.rollCarac(caracCateg, caracKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle competence roll
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onRollCompetence(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
this.document.rollCompetence(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle weapon roll
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onRollArme(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
this.document.rollArme(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle lock/unlock toggle
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onLockUnlock(event, target) {
|
||||
this._sheetMode = this.isEditMode
|
||||
? this.constructor.SHEET_MODES.PLAY
|
||||
: this.constructor.SHEET_MODES.EDIT
|
||||
this.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incrementing PV
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onIncrementPV(event, target) {
|
||||
const currentPV = this.document.system.caracsecondaire.pv.value || 0
|
||||
const maxPV = this.document.system.caracsecondaire.pv.max || 0
|
||||
const newPV = Math.min(currentPV + 1, maxPV)
|
||||
await this.document.update({ "system.caracsecondaire.pv.value": newPV })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle decrementing PV
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onDecrementPV(event, target) {
|
||||
const currentPV = this.document.system.caracsecondaire.pv.value || 0
|
||||
const newPV = Math.max(currentPV - 1, 0)
|
||||
await this.document.update({ "system.caracsecondaire.pv.value": newPV })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle competence niveau update
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {Event} event - The triggering event
|
||||
* @param {HTMLElement} target - The select element
|
||||
*/
|
||||
static async #onUpdateCompetence(event, target) {
|
||||
const itemId = target.dataset.itemId
|
||||
const item = this.document.items.get(itemId)
|
||||
if (!item) return
|
||||
const newNiveau = parseInt(target.value)
|
||||
await item.update({ "system.niveau": newNiveau })
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle sort roll
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onRollSort(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
const sortType = target.dataset.sortType || "sejdr"
|
||||
this.document.rollSort(itemId, sortType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle prouesse roll
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onRollProuesse(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
this.document.rollProuesse(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle damage roll
|
||||
* @this {YggdrasillActorSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static #onRollDamage(event, target) {
|
||||
const itemId = target.closest("[data-item-id]").dataset.itemId
|
||||
const weapon = this.document.items.get(itemId)
|
||||
if (weapon) {
|
||||
this.document.rollDamage(weapon, 'damage')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle beginning of a drag operation
|
||||
* @param {DragEvent} event - The originating drag event
|
||||
* @protected
|
||||
*/
|
||||
_onDragStart(event) {
|
||||
const li = event.currentTarget
|
||||
const itemId = li.dataset.itemId
|
||||
const item = this.document.items.get(itemId)
|
||||
if (!item) return
|
||||
|
||||
const dragData = item.toDragData()
|
||||
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a drop event
|
||||
* @param {DragEvent} event - The originating drop event
|
||||
* @protected
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event)
|
||||
const actor = this.document
|
||||
|
||||
// Handle different data types
|
||||
switch (data.type) {
|
||||
case "Item":
|
||||
return this._onDropItem(event, data)
|
||||
case "ActiveEffect":
|
||||
return this._onDropActiveEffect(event, data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropping an Item on the sheet
|
||||
* @param {DragEvent} event - The originating drop event
|
||||
* @param {object} data - The dropped data
|
||||
* @protected
|
||||
*/
|
||||
async _onDropItem(event, data) {
|
||||
if (!this.isEditable) return false
|
||||
const item = await Item.implementation.fromDropData(data)
|
||||
const itemData = item.toObject()
|
||||
|
||||
// Handle item from same actor
|
||||
if (this.document.uuid === item.parent?.uuid) return this._onSortItem(event, itemData)
|
||||
|
||||
// Create the item
|
||||
return this._onDropItemCreate(itemData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle creating an owned item from drop data
|
||||
* @param {object} itemData - The item data to create
|
||||
* @protected
|
||||
*/
|
||||
async _onDropItemCreate(itemData) {
|
||||
itemData = itemData instanceof Array ? itemData : [itemData]
|
||||
return this.document.createEmbeddedDocuments("Item", itemData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle sorting items
|
||||
* @param {DragEvent} event - The originating drop event
|
||||
* @param {object} itemData - The item data being sorted
|
||||
* @protected
|
||||
*/
|
||||
_onSortItem(event, itemData) {
|
||||
// Implement sorting logic if needed
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dropping an ActiveEffect on the sheet
|
||||
* @param {DragEvent} event - The originating drop event
|
||||
* @param {object} data - The dropped data
|
||||
* @protected
|
||||
*/
|
||||
async _onDropActiveEffect(event, data) {
|
||||
const effect = await ActiveEffect.implementation.fromDropData(data)
|
||||
if (!this.isEditable || !effect) return false
|
||||
|
||||
if (this.document.uuid === effect.parent?.uuid) return false
|
||||
return ActiveEffect.create(effect.toObject(), { parent: this.document })
|
||||
}
|
||||
}
|
||||
178
modules/applications/sheets/base-item-sheet.mjs
Normal file
178
modules/applications/sheets/base-item-sheet.mjs
Normal file
@@ -0,0 +1,178 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
export default class YggdrasillItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
}
|
||||
|
||||
#dragDrop
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["fvtt-yggdrasill", "item"],
|
||||
position: {
|
||||
width: 620,
|
||||
height: 600,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
navSelector: 'nav[data-group="primary"]',
|
||||
contentSelector: "section.sheet-body",
|
||||
initial: "description",
|
||||
},
|
||||
],
|
||||
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
|
||||
actions: {
|
||||
editImage: YggdrasillItemSheet.#onEditImage,
|
||||
postItem: YggdrasillItemSheet.#onPostItem,
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab groups state
|
||||
* @type {object}
|
||||
*/
|
||||
tabGroups = { primary: "description" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
// Import config
|
||||
const YGGDRASILL_CONFIG = game.system.yggdrasill?.config || game.system.config || {};
|
||||
|
||||
// Create options for niveau 0-5
|
||||
const optionsNiveaux4 = {};
|
||||
for (let i = 0; i <= 5; i++) {
|
||||
optionsNiveaux4[`${i}`] = `${i}`;
|
||||
}
|
||||
|
||||
// Create options for base (0-20)
|
||||
const optionsBase = {};
|
||||
for (let i = 0; i <= 20; i++) {
|
||||
optionsBase[`${i}`] = `${i}`;
|
||||
}
|
||||
|
||||
const context = {
|
||||
fields: this.document.schema.fields,
|
||||
systemFields: this.document.system.schema.fields,
|
||||
item: this.document,
|
||||
system: this.document.system,
|
||||
data: this.document.system,
|
||||
source: this.document.toObject(),
|
||||
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description || "", { async: true }),
|
||||
enrichedEffet: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.effet || "", { async: true }),
|
||||
isEditMode: true,
|
||||
isEditable: this.isEditable,
|
||||
editable: this.isEditable,
|
||||
isGM: game.user.isGM,
|
||||
config: YGGDRASILL_CONFIG,
|
||||
optionsBase: optionsBase,
|
||||
optionsNiveaux4: optionsNiveaux4,
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
|
||||
// Activate tab navigation manually
|
||||
const nav = this.element.querySelector('nav.tabs[data-group]')
|
||||
if (nav) {
|
||||
const group = nav.dataset.group
|
||||
// Activate the current tab
|
||||
const activeTab = this.tabGroups[group] || "description"
|
||||
nav.querySelectorAll('[data-tab]').forEach(link => {
|
||||
const tab = link.dataset.tab
|
||||
link.classList.toggle('active', tab === activeTab)
|
||||
link.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
this.tabGroups[group] = tab
|
||||
this.render()
|
||||
})
|
||||
})
|
||||
// Show/hide tab content
|
||||
const body = this.element.querySelector('section.sheet-body')
|
||||
if (body) {
|
||||
body.querySelectorAll('[data-tab]').forEach(content => {
|
||||
const tab = content.dataset.tab
|
||||
content.style.display = tab === activeTab ? 'block' : 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates drag-and-drop handlers for this application
|
||||
* @returns {DragDrop[]} An array of DragDrop handlers
|
||||
* @private
|
||||
*/
|
||||
#createDragDropHandlers() {
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle changing a Document's image
|
||||
* @this {YggdrasillItemSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onEditImage(event, target) {
|
||||
const attr = target.dataset.edit || "img"
|
||||
const current = foundry.utils.getProperty(this.document, attr)
|
||||
const fp = new FilePicker({
|
||||
current,
|
||||
type: "image",
|
||||
callback: (path) => {
|
||||
this.document.update({ [attr]: path })
|
||||
},
|
||||
})
|
||||
return fp.browse()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle posting item to chat
|
||||
* @this {YggdrasillItemSheet}
|
||||
* @param {PointerEvent} event - The triggering event
|
||||
* @param {HTMLElement} target - The button element
|
||||
*/
|
||||
static async #onPostItem(event, target) {
|
||||
const item = this.document
|
||||
const chatData = {
|
||||
user: game.user.id,
|
||||
speaker: ChatMessage.getSpeaker(),
|
||||
content: await foundry.applications.handlebars.renderTemplate("systems/fvtt-yggdrasill/templates/chat-item-card.hbs", {
|
||||
item: item,
|
||||
system: item.system,
|
||||
description: await TextEditor.enrichHTML(item.system.description || "", { async: true })
|
||||
})
|
||||
}
|
||||
ChatMessage.create(chatData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle beginning of a drag operation
|
||||
* @param {DragEvent} event - The originating drag event
|
||||
* @protected
|
||||
*/
|
||||
_onDragStart(event) {
|
||||
const dragData = this.document.toDragData()
|
||||
event.dataTransfer.setData("text/plain", JSON.stringify(dragData))
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a drop event
|
||||
* @param {DragEvent} event - The originating drop event
|
||||
* @protected
|
||||
*/
|
||||
async _onDrop(event) {
|
||||
// Items generally don't handle drops, but method exists for extensibility
|
||||
return false
|
||||
}
|
||||
}
|
||||
44
modules/applications/sheets/create-sheets.sh
Executable file
44
modules/applications/sheets/create-sheets.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Liste des types d'items restants à créer
|
||||
items=(
|
||||
"poison:Poison"
|
||||
"prouesse:Prouesse"
|
||||
"sortsejdr:Sortsejdr"
|
||||
"sortgaldr:Sortgaldr"
|
||||
"rune:Rune"
|
||||
"armecc:Armecc"
|
||||
"armedist:Armedist"
|
||||
"armure:Armure"
|
||||
"bouclier:Bouclier"
|
||||
"equipement:Equipement"
|
||||
"monnaie:Monnaie"
|
||||
"effetmagique:Effetmagique"
|
||||
)
|
||||
|
||||
for item in "${items[@]}"; do
|
||||
type="${item%%:*}"
|
||||
class="${item##*:}"
|
||||
|
||||
cat > "yggdrasill-${type}-sheet.mjs" << EOF
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class Yggdrasill${class}Sheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "${type}"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-${type}-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
EOF
|
||||
done
|
||||
|
||||
echo "Created all item sheets"
|
||||
ls -1 yggdrasill-*-sheet.mjs | wc -l
|
||||
16
modules/applications/sheets/yggdrasill-armecc-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-armecc-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillArmeccSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "armecc"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-armecc-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-armedist-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-armedist-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillArmedistSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "armedist"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-armedist-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-armure-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-armure-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillArmureSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "armure"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-armure-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-blessure-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-blessure-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillBlessureSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "blessure"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-blessure-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-bouclier-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-bouclier-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillBouclierSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "bouclier"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-bouclier-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
21
modules/applications/sheets/yggdrasill-competence-sheet.mjs
Normal file
21
modules/applications/sheets/yggdrasill-competence-sheet.mjs
Normal file
@@ -0,0 +1,21 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillCompetenceSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "competence"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-competence-sheet.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "details",
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-don-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-don-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillDonSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "don"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-don-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillEffetmagiqueSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "effetmagique"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-effetmagique-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-equipement-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-equipement-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillEquipementSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "equipement"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-equipement-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-faiblesse-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-faiblesse-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillFaiblesseSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "faiblesse"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-faiblesse-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
40
modules/applications/sheets/yggdrasill-figurant-sheet.mjs
Normal file
40
modules/applications/sheets/yggdrasill-figurant-sheet.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
import YggdrasillActorSheet from "./base-actor-sheet.mjs"
|
||||
|
||||
export default class YggdrasillFigurantSheet extends YggdrasillActorSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "figurant"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "Feuille de Figurant",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/fvtt-yggdrasill/templates/actor-figurant-sheet.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "principal",
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
const actor = this.document
|
||||
|
||||
// System fields for formInput helpers
|
||||
context.systemFields = actor.system.schema.fields
|
||||
|
||||
// Enrich HTML fields
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true })
|
||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.notes || "", { async: true })
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-maladie-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-maladie-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillMaladieSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "maladie"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-maladie-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-monnaie-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-monnaie-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillMonnaieSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "monnaie"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-monnaie-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
99
modules/applications/sheets/yggdrasill-personnage-sheet.mjs
Normal file
99
modules/applications/sheets/yggdrasill-personnage-sheet.mjs
Normal file
@@ -0,0 +1,99 @@
|
||||
import YggdrasillActorSheet from "./base-actor-sheet.mjs"
|
||||
|
||||
export default class YggdrasillPersonnageSheet extends YggdrasillActorSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "personnage"],
|
||||
window: {
|
||||
...super.DEFAULT_OPTIONS.window,
|
||||
title: "Feuille de Personnage",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
sheet: {
|
||||
template: "systems/fvtt-yggdrasill/templates/actor-personnage-sheet-new.hbs",
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = {
|
||||
primary: "principal",
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const context = await super._prepareContext()
|
||||
const actor = this.document
|
||||
|
||||
// Prepare items by type
|
||||
context.competencesGenerales = actor.items.filter(i => i.type === 'competence' && i.system.categorie === 'generale')
|
||||
context.competencesMartiales = actor.items.filter(i => i.type === 'competence' && i.system.categorie === 'martiale')
|
||||
context.competencesMagiques = actor.items.filter(i => i.type === 'competence' && i.system.categorie === 'magique')
|
||||
context.dons = actor.items.filter(i => i.type === 'don')
|
||||
context.faiblesses = actor.items.filter(i => i.type === 'faiblesse')
|
||||
context.blessures = actor.items.filter(i => i.type === 'blessure')
|
||||
context.maladies = actor.items.filter(i => i.type === 'maladie')
|
||||
context.poisons = actor.items.filter(i => i.type === 'poison')
|
||||
context.prouesses = actor.items.filter(i => i.type === 'prouesse')
|
||||
context.sortsSejdr = actor.items.filter(i => i.type === 'sortsejdr')
|
||||
context.sortsGaldr = actor.items.filter(i => i.type === 'sortgaldr')
|
||||
context.runes = actor.items.filter(i => i.type === 'rune')
|
||||
context.armesCC = actor.items.filter(i => i.type === 'armecc')
|
||||
context.armesDist = actor.items.filter(i => i.type === 'armedist')
|
||||
context.armures = actor.items.filter(i => i.type === 'armure')
|
||||
context.boucliers = actor.items.filter(i => i.type === 'bouclier')
|
||||
context.equipements = actor.items.filter(i => i.type === 'equipement')
|
||||
context.monnaies = actor.items.filter(i => i.type === 'monnaie')
|
||||
context.effetsMagiques = actor.items.filter(i => i.type === 'effetmagique')
|
||||
|
||||
// Prepare equipped items
|
||||
context.armesEquipees = actor.items.filter(i => (i.type === 'armecc' || i.type === 'armedist') && i.system.equipe)
|
||||
context.armureEquipee = actor.items.find(i => i.type === 'armure' && i.system.equipe)
|
||||
context.bouclierEquipe = actor.items.find(i => i.type === 'bouclier' && i.system.equipe)
|
||||
|
||||
// Calculate total protection from equipped armors
|
||||
context.protectionTotal = context.armures
|
||||
.filter(a => a.system.equipe)
|
||||
.reduce((sum, a) => sum + (parseInt(a.system.protection) || 0), 0)
|
||||
|
||||
// Calculate shield defense bonus from equipped shield
|
||||
context.dpBouclier = context.boucliers
|
||||
.filter(b => b.system.equipe)
|
||||
.reduce((sum, b) => sum + (parseInt(b.system.defensebonus) || 0), 0)
|
||||
|
||||
// Calculate total encumbrance from equipements
|
||||
context.encTotal = context.equipements
|
||||
.reduce((sum, e) => sum + ((parseInt(e.system.quantite) || 0) * (parseInt(e.system.enc) || 0)), 0)
|
||||
|
||||
// Options for selects
|
||||
context.optionsCarac = {}
|
||||
for (let i = 0; i <= 10; i++) {
|
||||
context.optionsCarac[i] = i.toString()
|
||||
}
|
||||
|
||||
context.optionsBase = {}
|
||||
for (let i = 0; i <= 5; i++) {
|
||||
context.optionsBase[i] = i.toString()
|
||||
}
|
||||
|
||||
// Options for bonus/malus (-10 to +10)
|
||||
context.optionsDMDP = []
|
||||
for (let i = -10; i <= 10; i++) {
|
||||
context.optionsDMDP.push({ value: i, text: i >= 0 ? `+${i}` : i.toString() })
|
||||
}
|
||||
|
||||
// System fields for formInput helpers
|
||||
context.systemFields = actor.system.schema.fields
|
||||
|
||||
// Enrich HTML fields
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true })
|
||||
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.notes || "", { async: true })
|
||||
context.enrichedGMNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.gmnotes || "", { async: true })
|
||||
context.enrichedTirageRunes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.tiragerunes || "", { async: true })
|
||||
|
||||
return context
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-poison-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-poison-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillPoisonSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "poison"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-poison-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-prouesse-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-prouesse-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillProuesseSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "prouesse"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-prouesse-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-rune-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-rune-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillRuneSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "rune"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-rune-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-sortgaldr-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-sortgaldr-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillSortgaldrSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "sortgaldr"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-sortgaldr-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
16
modules/applications/sheets/yggdrasill-sortsejdr-sheet.mjs
Normal file
16
modules/applications/sheets/yggdrasill-sortsejdr-sheet.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import YggdrasillItemSheet from "./base-item-sheet.mjs"
|
||||
|
||||
export default class YggdrasillSortsejdrSheet extends YggdrasillItemSheet {
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
classes: [...super.DEFAULT_OPTIONS.classes, "sortsejdr"],
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: "systems/fvtt-yggdrasill/templates/item-sortsejdr-sheet.hbs",
|
||||
},
|
||||
}
|
||||
}
|
||||
334
modules/applications/yggdrasill-roll-dialog.mjs
Normal file
334
modules/applications/yggdrasill-roll-dialog.mjs
Normal file
@@ -0,0 +1,334 @@
|
||||
import { YggdrasillUtility } from "../yggdrasill-utility.js"
|
||||
|
||||
const dureeGaldrSD = { "1d5a": 3, "1d10t": 6, "1d10m": 9, "1d10h": 12, "1d5j": 15}
|
||||
const ciblesGaldrSD = { "1": 3, "2_4": 6, "5_9": 9, "10_49": 12, "50plus": 15}
|
||||
const zonesciblesGaldrSD = { "INS10cm3": 3, "INS50cm3": 6, "INS1m3": 9, "INS5m3": 12, "INS10m3": 15}
|
||||
|
||||
/**
|
||||
* Dialogue de jet de dé pour Yggdrasill - Version DialogV2
|
||||
*/
|
||||
export class YggdrasillRollDialog {
|
||||
|
||||
/**
|
||||
* Create and display the roll dialog
|
||||
* @param {YggdrasillActor} actor - The actor making the roll
|
||||
* @param {Object} rollData - Data for the roll
|
||||
* @returns {Promise}
|
||||
*/
|
||||
static async create(actor, rollData) {
|
||||
// Initialiser attackData pour les armes si nécessaire
|
||||
if ((rollData.mode === "armecc" || rollData.mode === "armetir" || rollData.mode === "armedist") && rollData.attackDef) {
|
||||
// Les données sont déjà calculées dans attackDef par getAttaqueData/getTirData
|
||||
// On les copie simplement dans attackData pour le template
|
||||
rollData.attackData = { ...rollData.attackDef }
|
||||
}
|
||||
|
||||
// Calculer srTotal pour les sorts
|
||||
if (rollData.mode === "galdr") {
|
||||
this._updateGaldrSD(rollData)
|
||||
rollData.srTotal = 14 + (rollData.sdGaldr || 0)
|
||||
} else if (rollData.mode === "sejdr") {
|
||||
rollData.srTotal = 14 + (rollData.bonusdefense || 0)
|
||||
} else if (rollData.mode === "rune") {
|
||||
this._updateRuneData(rollData, actor)
|
||||
}
|
||||
|
||||
// Préparer le contexte pour le template
|
||||
const context = {
|
||||
...rollData,
|
||||
img: actor.img,
|
||||
name: actor.name,
|
||||
config: game.system.yggdrasill.config,
|
||||
}
|
||||
|
||||
// Titre selon le mode
|
||||
let title = "Test"
|
||||
if (rollData.mode === "competence") title = "Test de Compétence"
|
||||
else if (rollData.mode === "carac") title = "Test de Caractéristique"
|
||||
else if (rollData.mode === "attribut") title = "Test d'Attribut"
|
||||
else if (rollData.mode === "armecc") title = "Attaque au Corps à Corps"
|
||||
else if (rollData.mode === "armetir" || rollData.mode === "armedist") title = "Attaque à Distance"
|
||||
else if (rollData.mode === "sejdr") title = "Sort Sejdr"
|
||||
else if (rollData.mode === "galdr") title = "Sort Galdr"
|
||||
else if (rollData.mode === "rune") title = "Rune"
|
||||
|
||||
// Rendre le template en HTML
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
"systems/fvtt-yggdrasill/templates/roll-dialog-generic-new.hbs",
|
||||
context
|
||||
)
|
||||
|
||||
// Utiliser DialogV2.wait avec le HTML rendu
|
||||
return foundry.applications.api.DialogV2.wait({
|
||||
window: {
|
||||
title: title,
|
||||
icon: "fa-solid fa-dice-d20"
|
||||
},
|
||||
classes: ["yggdrasill-roll-dialog"],
|
||||
position: { width: 600 },
|
||||
modal: false,
|
||||
content,
|
||||
buttons: [
|
||||
{
|
||||
action: "roll",
|
||||
label: "Lancer le Test",
|
||||
icon: "fa-solid fa-dice",
|
||||
default: true,
|
||||
callback: (event, button, dialog) => {
|
||||
this._updateRollDataFromForm(rollData, button.form.elements, actor)
|
||||
this._performRoll(rollData)
|
||||
}
|
||||
},
|
||||
],
|
||||
actions: {},
|
||||
render: (event, dialog) => {
|
||||
// Pour Galdr: recalculer srTotal quand durée, cibles ou zone changent
|
||||
if (rollData.mode === "galdr") {
|
||||
$("#dureeGaldr, #nbCibles, #zoneGaldr").on("change", () => {
|
||||
rollData.dureeGaldr = $("#dureeGaldr").val()
|
||||
rollData.nbCibles = $("#nbCibles").val()
|
||||
rollData.zoneGaldr = $("#zoneGaldr").val()
|
||||
this._updateGaldrSD(rollData)
|
||||
rollData.srTotal = 14 + (rollData.sdGaldr || 0)
|
||||
$("#srTotal").text(rollData.srTotal)
|
||||
})
|
||||
}
|
||||
|
||||
// Pour Sejdr: recalculer srTotal quand DM change
|
||||
if (rollData.mode === "sejdr") {
|
||||
$("#bonusdefense").on("change", () => {
|
||||
rollData.bonusdefense = parseInt($("#bonusdefense").val()) || 0
|
||||
rollData.srTotal = 14 + rollData.bonusdefense
|
||||
$("#srTotal").text(rollData.srTotal)
|
||||
})
|
||||
}
|
||||
|
||||
// Pour Rune: recalculer srTotal quand support ou puissance changent
|
||||
if (rollData.mode === "rune") {
|
||||
$("#supportRune, #puissanceRune").on("change", () => {
|
||||
rollData.supportRune = $("#supportRune").val()
|
||||
rollData.puissanceRune = parseInt($("#puissanceRune").val()) || 1
|
||||
this._updateRuneData(rollData, actor)
|
||||
$("#runeDuree").text(rollData.runeDuree)
|
||||
$("#runeDureeVie").text(rollData.runeDureeVie)
|
||||
$("#srTotal").text(rollData.srTotal)
|
||||
})
|
||||
}
|
||||
},
|
||||
rejectClose: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the roll based on mode
|
||||
* @param {Object} rollData - The roll data
|
||||
* @private
|
||||
*/
|
||||
static _performRoll(rollData) {
|
||||
if (rollData.mode === "attribut") {
|
||||
YggdrasillUtility.rollAttribute(rollData)
|
||||
} else {
|
||||
YggdrasillUtility.rollYggdrasill(rollData)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour rollData avec les valeurs du formulaire
|
||||
* @param {Object} rollData - L'objet rollData à mettre à jour
|
||||
* @param {HTMLFormControlsCollection} formElements - Les éléments du formulaire
|
||||
* @param {YggdrasillActor} actor - L'acteur pour récupérer les données
|
||||
* @private
|
||||
*/
|
||||
static _updateRollDataFromForm(rollData, formElements, actor) {
|
||||
// Caractéristique (pour compétences)
|
||||
if (formElements.caracName) {
|
||||
const caracKey = formElements.caracName.value
|
||||
rollData.caracName = caracKey
|
||||
rollData.selectedCarac = this._findCaracByKey(actor, caracKey)
|
||||
}
|
||||
|
||||
// Type d'attaque (pour armes)
|
||||
if (formElements.typeAttack) {
|
||||
rollData.attackDef = rollData.attackDef || {}
|
||||
rollData.attackDef.typeAttack = formElements.typeAttack.value
|
||||
this._updateAttackData(rollData, actor)
|
||||
}
|
||||
|
||||
// Bonus/Malus
|
||||
if (formElements.bonusMalus) {
|
||||
rollData.bonusMalus = parseInt(formElements.bonusMalus.value) || 0
|
||||
}
|
||||
|
||||
// Furor
|
||||
if (formElements.furorUsage) {
|
||||
rollData.furorUsage = parseInt(formElements.furorUsage.value) || 0
|
||||
}
|
||||
|
||||
// Seuil de Réussite
|
||||
if (formElements.sr) {
|
||||
rollData.sr = parseInt(formElements.sr.value) || 9
|
||||
}
|
||||
|
||||
// Galdr options
|
||||
if (formElements.dureeGaldr) {
|
||||
rollData.dureeGaldr = formElements.dureeGaldr.value
|
||||
}
|
||||
if (formElements.nbCibles) {
|
||||
rollData.nbCibles = formElements.nbCibles.value
|
||||
}
|
||||
if (formElements.zoneGaldr) {
|
||||
rollData.zoneGaldr = formElements.zoneGaldr.value
|
||||
}
|
||||
if (rollData.mode === "galdr") {
|
||||
this._updateGaldrSD(rollData)
|
||||
rollData.srTotal = 14 + (rollData.sdGaldr || 0)
|
||||
}
|
||||
|
||||
// Sejdr options
|
||||
if (formElements.bonusdefense) {
|
||||
rollData.bonusdefense = parseInt(formElements.bonusdefense.value) || 0
|
||||
}
|
||||
if (rollData.mode === "sejdr") {
|
||||
rollData.srTotal = 14 + (rollData.bonusdefense || 0)
|
||||
}
|
||||
|
||||
// Rune options
|
||||
if (formElements.runeDuree) {
|
||||
rollData.runeDuree = formElements.runeDuree.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find characteristic by key in actor
|
||||
* @param {YggdrasillActor} actor
|
||||
* @param {String} caracKey
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
static _findCaracByKey(actor, caracKey) {
|
||||
for (let [categKey, categ] of Object.entries(actor.system.carac)) {
|
||||
for (let [key, carac] of Object.entries(categ.carac)) {
|
||||
if (key === caracKey) {
|
||||
return { ...carac, categName: categKey, caracName: key }
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Update attack data based on attack type
|
||||
* @param {Object} rollData
|
||||
* @param {YggdrasillActor} actor
|
||||
* @private
|
||||
*/
|
||||
static _updateAttackData(rollData, actor) {
|
||||
const config = game.system.yggdrasill.config
|
||||
const attackType = rollData.attackDef.typeAttack
|
||||
const attackMode = config.attackMode?.[attackType]
|
||||
|
||||
if (attackMode) {
|
||||
rollData.attackData = rollData.attackData || {}
|
||||
rollData.attackData.categName = attackMode.categName
|
||||
rollData.attackData.caracName = attackMode.caracName
|
||||
rollData.attackData.malus = this._computeValue(attackMode.malus, actor)
|
||||
rollData.attackData.bonusdegats = this._computeValue(attackMode.bonusdegats, actor)
|
||||
rollData.attackData.protection = this._computeValue(attackMode.protection, actor)
|
||||
rollData.attackData.label = attackMode.label
|
||||
rollData.attackData.description = attackMode.description
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute value from string formula like "puissance;3"
|
||||
* @param {String|Number} value
|
||||
* @param {YggdrasillActor} actor
|
||||
* @returns {Number}
|
||||
* @private
|
||||
*/
|
||||
static _computeValue(value, actor) {
|
||||
if (typeof value === 'number') return value
|
||||
if (!value || value === "0") return 0
|
||||
|
||||
const parts = String(value).split(';')
|
||||
if (parts.length === 2) {
|
||||
const caracName = parts[0]
|
||||
const multiplier = parseInt(parts[1]) || 1
|
||||
const caracValue = this._getCaracValue(actor, caracName)
|
||||
return caracValue * multiplier
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get characteristic value by name
|
||||
* @param {YggdrasillActor} actor
|
||||
* @param {String} caracName
|
||||
* @returns {Number}
|
||||
* @private
|
||||
*/
|
||||
static _getCaracValue(actor, caracName) {
|
||||
for (let categ of Object.values(actor.system.carac)) {
|
||||
if (categ.carac && categ.carac[caracName]) {
|
||||
return categ.carac[caracName].value || 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rune data based on support and power
|
||||
* @param {Object} rollData
|
||||
* @param {YggdrasillActor} actor
|
||||
* @private
|
||||
*/
|
||||
static _updateRuneData(rollData, actor) {
|
||||
let support = 0
|
||||
|
||||
rollData.dureeRune = 6 - (rollData.agiliteCarac?.value || 0)
|
||||
|
||||
if (rollData.supportRune === "peau") {
|
||||
support = 3
|
||||
rollData.echelleDuree = "Actions"
|
||||
rollData.echelleDureeVie = "Heures"
|
||||
} else if (rollData.supportRune === "tissu") {
|
||||
support = 6
|
||||
rollData.echelleDuree = "Tours"
|
||||
rollData.echelleDureeVie = "Jours"
|
||||
} else if (rollData.supportRune === "cuir") {
|
||||
support = 9
|
||||
rollData.echelleDuree = "Minutes"
|
||||
rollData.echelleDureeVie = "Semaines"
|
||||
} else if (rollData.supportRune === "bois") {
|
||||
support = 12
|
||||
rollData.echelleDuree = "Heures"
|
||||
rollData.echelleDureeVie = "Mois"
|
||||
} else if (rollData.supportRune === "pierremetal") {
|
||||
support = 15
|
||||
rollData.echelleDuree = "Jours"
|
||||
rollData.echelleDureeVie = "Années"
|
||||
}
|
||||
|
||||
rollData.runeDuree = `${rollData.dureeRune} ${rollData.echelleDuree}`
|
||||
rollData.runeDureeVie = `${rollData.competence?.system.niveau || 1} ${rollData.echelleDureeVie}`
|
||||
rollData.srTotal = (rollData.puissanceRune || 1) + (Number(rollData.sort?.system.niveau || 0) * 3) + support
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Galdr SD based on duration and targets
|
||||
* @param {Object} rollData
|
||||
* @private
|
||||
*/
|
||||
static _updateGaldrSD(rollData) {
|
||||
let sdDuree = Number(dureeGaldrSD[rollData.dureeGaldr]) || 0
|
||||
let sdVar = 0
|
||||
|
||||
if (rollData.sort?.system.voie === "illusion") {
|
||||
sdVar = Number(zonesciblesGaldrSD[rollData.zoneGaldr]) || 0
|
||||
} else {
|
||||
sdVar = Number(ciblesGaldrSD[rollData.nbCibles]) || 0
|
||||
}
|
||||
|
||||
rollData.sdGaldr = sdDuree + sdVar
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user