Fix apv2, WIP
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
import { onManageActiveEffect } from "../../system/effects.mjs"
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
/**
|
||||
* Fiche de base pour tous les acteurs Vermine 2047 (ApplicationV2).
|
||||
* Remplace VermineActorSheet (AppV1).
|
||||
*/
|
||||
export class VermineBaseActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
|
||||
// ── Mode édition / jeu ──────────────────────────────────────────────
|
||||
|
||||
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||
|
||||
_sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||
|
||||
get isPlayMode() { return this._sheetMode === this.constructor.SHEET_MODES.PLAY }
|
||||
get isEditMode() { return this._sheetMode === this.constructor.SHEET_MODES.EDIT }
|
||||
|
||||
// ── Options par défaut ──────────────────────────────────────────────
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["vermine2047", "actor"],
|
||||
position: { width: 800, height: "auto" },
|
||||
form: { submitOnChange: true },
|
||||
window: { resizable: true },
|
||||
dragDrop: [{ dragSelector: ".item", dropSelector: null }],
|
||||
actions: {
|
||||
editImage: VermineBaseActorSheet.#onEditImage,
|
||||
toggleSheet: VermineBaseActorSheet.#onToggleSheet,
|
||||
edit: VermineBaseActorSheet.#onItemEdit,
|
||||
delete: VermineBaseActorSheet.#onItemDelete,
|
||||
create: VermineBaseActorSheet.#onItemCreate,
|
||||
roll: VermineBaseActorSheet.#onRollItem,
|
||||
clickRadio: VermineBaseActorSheet.#onClickRadioHexa,
|
||||
effectControl: VermineBaseActorSheet.#onEffectControl,
|
||||
chooseTotem: VermineBaseActorSheet.#onChooseTotem
|
||||
}
|
||||
}
|
||||
|
||||
// ── Drag & Drop ─────────────────────────────────────────────────────
|
||||
|
||||
#dragDrop
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
}
|
||||
|
||||
#createDragDropHandlers() {
|
||||
return this.options.dragDrop.map(d => {
|
||||
d.permissions = {
|
||||
dragstart: this._canDragStart.bind(this),
|
||||
drop: this._canDragDrop.bind(this)
|
||||
}
|
||||
d.callbacks = {
|
||||
dragover: this._onDragOver.bind(this),
|
||||
drop: this._onDrop.bind(this)
|
||||
}
|
||||
return new foundry.applications.ux.DragDrop.implementation(d)
|
||||
})
|
||||
}
|
||||
|
||||
_canDragStart() { return this.isEditable }
|
||||
_canDragDrop() { return this.isEditable }
|
||||
|
||||
// ── Contexte commun ─────────────────────────────────────────────────
|
||||
|
||||
async _prepareContext() {
|
||||
return {
|
||||
fields: this.document.schema.fields,
|
||||
systemFields: this.document.system.schema.fields,
|
||||
actor: this.document,
|
||||
system: this.document.system,
|
||||
source: this.document.toObject(),
|
||||
config: CONFIG.VERMINE,
|
||||
rollData: this.document.getRollData(),
|
||||
isGM: game.user.isGM,
|
||||
isEditMode: this.isEditMode,
|
||||
isPlayMode: this.isPlayMode,
|
||||
isEditable: this.isEditable
|
||||
}
|
||||
}
|
||||
|
||||
// ── Rendu ───────────────────────────────────────────────────────────
|
||||
|
||||
async _onRoll(event) {
|
||||
event.preventDefault()
|
||||
const el = event.currentTarget
|
||||
const type = el.dataset.type
|
||||
const label = el.dataset.label
|
||||
if (!type || !label) return
|
||||
const { default: RollDialog } = await import("../../system/dialogs/rollDialog.mjs")
|
||||
const dialog = await RollDialog.create({
|
||||
actorId: this.document.id,
|
||||
rolltype: type,
|
||||
label
|
||||
})
|
||||
if (dialog) dialog.render(true)
|
||||
}
|
||||
|
||||
// ── Actions ─────────────────────────────────────────────────────────
|
||||
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
this.#dragDrop.forEach(d => d.bind(this.element))
|
||||
this.element.querySelectorAll(".rollable").forEach(el => {
|
||||
el.addEventListener("click", this._onRoll.bind(this))
|
||||
})
|
||||
// Auto-fill empty number inputs on change to prevent validation errors
|
||||
this.element.addEventListener("change", e => {
|
||||
const input = e.target
|
||||
if (input?.type === "number" && !input.value && input.name && input !== document.activeElement) {
|
||||
input.value = "0"
|
||||
}
|
||||
}, { capture: true })
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _onDropItem(item) {
|
||||
const doc = item instanceof foundry.abstract.Document ? item : await fromUuid(item.uuid)
|
||||
if (!doc) return
|
||||
const itemData = doc.toObject()
|
||||
await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
|
||||
}
|
||||
|
||||
static #onToggleSheet() {
|
||||
const modes = this.constructor.SHEET_MODES
|
||||
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
|
||||
this.render()
|
||||
}
|
||||
|
||||
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 }),
|
||||
top: this.position.top + 40,
|
||||
left: this.position.left + 10
|
||||
})
|
||||
return fp.browse()
|
||||
}
|
||||
|
||||
static async #onItemEdit(event, target) {
|
||||
const id = target.closest("[data-item-id]")?.dataset?.itemId
|
||||
const uuid = target.closest("[data-item-uuid]")?.dataset?.itemUuid
|
||||
let item
|
||||
if (uuid) item = await fromUuid(uuid)
|
||||
if (!item) item = this.document.items.get(id)
|
||||
item?.sheet.render(true)
|
||||
}
|
||||
|
||||
static async #onItemDelete(event, target) {
|
||||
const itemUuid = target.closest("[data-item-uuid]")?.dataset?.itemUuid
|
||||
if (itemUuid) {
|
||||
const item = await fromUuid(itemUuid)
|
||||
await item?.deleteDialog()
|
||||
return
|
||||
}
|
||||
const id = target.closest("[data-item-id]")?.dataset?.itemId
|
||||
const item = this.document.items.get(id)
|
||||
await item?.deleteDialog()
|
||||
}
|
||||
|
||||
static async #onItemCreate(event, target) {
|
||||
const type = target.dataset.type
|
||||
if (!type) return
|
||||
const name = game.i18n.localize("ITEMS.new_" + type)
|
||||
await this.document.createEmbeddedDocuments("Item", [{ name, type }])
|
||||
}
|
||||
|
||||
static async #onRollItem(event, target) {
|
||||
const id = target.closest("[data-item-id]")?.dataset?.itemId
|
||||
if (!id) return
|
||||
const item = this.document.items.get(id)
|
||||
item?.roll()
|
||||
}
|
||||
|
||||
static #onClickRadioHexa(event, target) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
const input = target
|
||||
const update = {}
|
||||
let current = this.document
|
||||
const propTree = input.name.split(".")
|
||||
for (const prop of propTree) {
|
||||
current = current[prop]
|
||||
}
|
||||
if (current != input.value) {
|
||||
update[input.name] = parseInt(input.value)
|
||||
} else {
|
||||
update[input.name] = parseInt(input.value) - 1
|
||||
}
|
||||
this.document.update(update)
|
||||
}
|
||||
|
||||
static #onEffectControl(event, target) {
|
||||
onManageActiveEffect(event, this.document)
|
||||
}
|
||||
|
||||
static async #onChooseTotem(event, target) {
|
||||
const { TotemPicker } = await import("../../system/applications.mjs")
|
||||
new TotemPicker(target, this.document).render(true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user