DataModels + Appv2 migration : OK
This commit is contained in:
307
module/applications/sheets/base-actor-sheet.mjs
Normal file
307
module/applications/sheets/base-actor-sheet.mjs
Normal file
@@ -0,0 +1,307 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
import { BoLRoll } from "../../controllers/bol-rolls.js"
|
||||
import { BoLUtility } from "../../system/bol-utility.js"
|
||||
|
||||
/**
|
||||
* Base Actor Sheet for BoL system using AppV2
|
||||
* @extends {ActorSheetV2}
|
||||
*/
|
||||
export default class BoLBaseActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
}
|
||||
|
||||
#dragDrop
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["bol", "sheet", "actor"],
|
||||
position: {
|
||||
width: 836,
|
||||
height: 807,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
tabs: [
|
||||
{
|
||||
navSelector: "nav[data-group=\"primary\"]",
|
||||
contentSelector: "section.sheet-body",
|
||||
initial: "stats",
|
||||
},
|
||||
],
|
||||
dragDrop: [{ dragSelector: ".items-list .item", dropSelector: null }],
|
||||
actions: {},
|
||||
}
|
||||
|
||||
/** Tab groups state */
|
||||
tabGroups = { primary: "stats" }
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const actor = this.document
|
||||
return {
|
||||
actor,
|
||||
system: actor.system,
|
||||
config: game.bol.config,
|
||||
isGM: game.user.isGM,
|
||||
isEditable: this.isEditable,
|
||||
owner: this.document.isOwner,
|
||||
cssClass: this.options.classes.join(" "),
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
super._onRender(context, options)
|
||||
this.#dragDrop.forEach((d) => d.bind(this.element))
|
||||
this._activateTabs()
|
||||
this._activateListeners()
|
||||
this._applyBackgroundImage()
|
||||
this._activateImageEdit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply background image to the actor form
|
||||
* @private
|
||||
*/
|
||||
_applyBackgroundImage() {
|
||||
const logoUrl = BoLUtility.getLogoActorSheet()
|
||||
const form = this.element.querySelector(".bol-actor-form")
|
||||
if (form) form.style.backgroundImage = `url(${logoUrl})`
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate image editing via FilePicker
|
||||
* @private
|
||||
*/
|
||||
_activateImageEdit() {
|
||||
const img = this.element.querySelector('[data-edit="img"]')
|
||||
if (img && this.isEditable) {
|
||||
img.style.cursor = "pointer"
|
||||
img.addEventListener("click", () => {
|
||||
new FilePicker({
|
||||
type: "image",
|
||||
current: this.document.img,
|
||||
callback: (path) => this.document.update({ img: path }),
|
||||
}).browse()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate tab navigation
|
||||
* @private
|
||||
*/
|
||||
_activateTabs() {
|
||||
const nav = this.element.querySelector("nav[data-group]")
|
||||
if (!nav) return
|
||||
|
||||
const group = nav.dataset.group
|
||||
const activeTab = this.tabGroups[group] || "stats"
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
||||
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach((content) => {
|
||||
content.classList.toggle("active", content.dataset.tab === activeTab)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate event listeners (replaces activateListeners with vanilla DOM)
|
||||
* @private
|
||||
*/
|
||||
_activateListeners() {
|
||||
if (!this.isEditable) return
|
||||
|
||||
// Item edit
|
||||
this.element.querySelectorAll(".item-edit").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const li = ev.currentTarget.closest(".item")
|
||||
const item = this.actor.items.get(li?.dataset.itemId)
|
||||
item?.sheet.render(true)
|
||||
})
|
||||
})
|
||||
|
||||
// Item delete
|
||||
this.element.querySelectorAll(".item-delete").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const li = ev.currentTarget.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
Dialog.confirm({
|
||||
title: game.i18n.localize("BOL.ui.deletetitle"),
|
||||
content: game.i18n.localize("BOL.ui.confirmdelete"),
|
||||
yes: () => {
|
||||
this.actor.deleteEmbeddedDocuments("Item", [itemId])
|
||||
li?.remove()
|
||||
},
|
||||
no: () => {},
|
||||
defaultYes: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Item equip/unequip
|
||||
this.element.querySelectorAll(".item-equip").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const li = ev.currentTarget.closest(".item")
|
||||
const item = this.actor.items.get(li?.dataset.itemId)
|
||||
if (item) this.actor.toggleEquipItem(item)
|
||||
})
|
||||
})
|
||||
|
||||
// Toggle fight option
|
||||
this.element.querySelectorAll(".toggle-fight-option").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const li = ev.currentTarget.closest(".item")
|
||||
this.actor.toggleFightOption(li?.dataset.itemId)
|
||||
})
|
||||
})
|
||||
|
||||
// Inc/dec alchemy points
|
||||
this.element.querySelectorAll(".inc-dec-btns-alchemy").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const li = ev.currentTarget.closest(".item")
|
||||
this.actor.spendAlchemyPoint(li?.dataset.itemId, 1)
|
||||
})
|
||||
})
|
||||
|
||||
// Inc/dec resource buttons
|
||||
this.element.querySelectorAll(".inc-dec-btns-resource").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const dataset = ev.currentTarget.dataset
|
||||
this.actor.incDecResources(dataset.target, parseInt(dataset.incr))
|
||||
})
|
||||
})
|
||||
|
||||
// Generic inc/dec buttons for item fields
|
||||
this.element.querySelectorAll(".inc-dec-btns").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
const li = ev.currentTarget.closest(".item")
|
||||
if (!li) return
|
||||
const item = this.actor.items.get(li.dataset.itemId)
|
||||
if (!item) return
|
||||
const dataset = ev.currentTarget.dataset
|
||||
const operator = dataset.operator
|
||||
const target = dataset.target
|
||||
const incr = parseInt(dataset.incr)
|
||||
const min = parseInt(dataset.min)
|
||||
const max = parseInt(dataset.max) || 10000
|
||||
// eslint-disable-next-line no-eval
|
||||
let value = eval("item." + target) || 0
|
||||
if (operator === "minus") value = value >= min + incr ? value - incr : min
|
||||
if (operator === "plus") value = value <= max - incr ? value + incr : max
|
||||
item.update({ [target]: value })
|
||||
})
|
||||
})
|
||||
|
||||
// Rollable elements
|
||||
this.element.querySelectorAll(".rollable").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => this._onRoll(ev))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle clickable rolls (replaces _onRoll with vanilla DOM)
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
_onRoll(event) {
|
||||
event.preventDefault()
|
||||
const element = event.currentTarget
|
||||
const dataset = element.dataset
|
||||
const rollType = dataset.rollType
|
||||
const li = element.closest(".item")
|
||||
const itemId = li?.dataset.itemId
|
||||
|
||||
switch (rollType) {
|
||||
case "attribute":
|
||||
BoLRoll.attributeCheck(this.actor, dataset.key, event)
|
||||
break
|
||||
case "aptitude":
|
||||
BoLRoll.aptitudeCheck(this.actor, dataset.key, event)
|
||||
break
|
||||
case "weapon":
|
||||
BoLRoll.weaponCheck(this.actor, event)
|
||||
break
|
||||
case "spell":
|
||||
BoLRoll.spellCheck(this.actor, event)
|
||||
break
|
||||
case "alchemy":
|
||||
BoLRoll.alchemyCheck(this.actor, event)
|
||||
break
|
||||
case "protection":
|
||||
this.actor.rollProtection(itemId)
|
||||
break
|
||||
case "damage":
|
||||
this.actor.rollWeaponDamage(itemId)
|
||||
break
|
||||
case "aptitudexp":
|
||||
this.actor.incAptitudeXP(dataset.key)
|
||||
break
|
||||
case "attributexp":
|
||||
this.actor.incAttributeXP(dataset.key)
|
||||
break
|
||||
case "careerxp":
|
||||
this.actor.incCareerXP(itemId)
|
||||
break
|
||||
case "horoscope-minor":
|
||||
BoLRoll.horoscopeCheck(this.actor, event, "minor")
|
||||
break
|
||||
case "horoscope-major":
|
||||
BoLRoll.horoscopeCheck(this.actor, event, "major")
|
||||
break
|
||||
case "horoscope-major-group":
|
||||
BoLRoll.horoscopeCheck(this.actor, event, "majorgroup")
|
||||
break
|
||||
case "bougette":
|
||||
this.actor.rollBougette()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// #region Drag-and-Drop
|
||||
|
||||
#createDragDropHandlers() {
|
||||
return (this.options.dragDrop || []).map((dragDrop) =>
|
||||
new foundry.applications.ux.DragDrop.implementation({
|
||||
...dragDrop,
|
||||
permissions: {
|
||||
dragstart: this._canDragStart.bind(this),
|
||||
drop: this._canDragDrop.bind(this),
|
||||
},
|
||||
callbacks: {
|
||||
dragstart: this._onDragStart.bind(this),
|
||||
dragover: this._onDragOver.bind(this),
|
||||
drop: this._onDrop.bind(this),
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
_canDragStart(selector) {
|
||||
return this.isEditable
|
||||
}
|
||||
|
||||
_canDragDrop(selector) {
|
||||
return this.isEditable
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
||||
Reference in New Issue
Block a user