Migration vers datamodels
This commit is contained in:
2
modules/actors/sheets/_module.js
Normal file
2
modules/actors/sheets/_module.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as EcrymeActorSheet } from "./pc-npc-sheet.js"
|
||||
export { default as EcrymeAnnencySheet } from "./annency-sheet.js"
|
||||
127
modules/actors/sheets/annency-sheet.js
Normal file
127
modules/actors/sheets/annency-sheet.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import EcrymeBaseActorSheet from "./base-actor-sheet.js"
|
||||
import { EcrymeUtility } from "../../common/ecryme-utility.js"
|
||||
|
||||
/**
|
||||
* Actor sheet for the Annency type using Application V2.
|
||||
*/
|
||||
export default class EcrymeAnnencySheet extends EcrymeBaseActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["annency"],
|
||||
position: { width: 640, height: 600 },
|
||||
actions: {
|
||||
actorEdit: EcrymeAnnencySheet.#onActorEdit,
|
||||
actorDelete: EcrymeAnnencySheet.#onActorDelete,
|
||||
itemEdit: EcrymeAnnencySheet.#onItemEdit,
|
||||
itemDelete: EcrymeAnnencySheet.#onItemDelete,
|
||||
itemCreate: EcrymeAnnencySheet.#onItemCreate,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
header: { template: "systems/fvtt-ecryme/templates/actors/partials/actor-header.hbs" },
|
||||
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
||||
annency: { template: "systems/fvtt-ecryme/templates/actors/annency-annency.hbs" },
|
||||
boheme: { template: "systems/fvtt-ecryme/templates/actors/annency-boheme.hbs" },
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "annency" }
|
||||
|
||||
/** Build tabs conditionally based on active modules */
|
||||
_getTabs() {
|
||||
const tabs = {}
|
||||
if (EcrymeUtility.hasCephaly()) {
|
||||
tabs.annency = { id: "annency", group: "primary", label: "ECRY.ui.annency" }
|
||||
}
|
||||
if (EcrymeUtility.hasBoheme()) {
|
||||
tabs.boheme = { id: "boheme", group: "primary", label: "ECRY.ui.boheme" }
|
||||
}
|
||||
// Ensure initial tab is valid
|
||||
if (!tabs[this.tabGroups.primary]) {
|
||||
this.tabGroups.primary = Object.keys(tabs)[0] ?? "annency"
|
||||
}
|
||||
for (const tab of Object.values(tabs)) {
|
||||
tab.active = this.tabGroups[tab.group] === tab.id
|
||||
tab.cssClass = tab.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const actor = this.document
|
||||
return {
|
||||
actor,
|
||||
system: actor.system,
|
||||
source: actor.toObject(),
|
||||
fields: actor.schema.fields,
|
||||
systemFields: actor.system.schema.fields,
|
||||
type: actor.type,
|
||||
img: actor.img,
|
||||
name: actor.name,
|
||||
isEditable: this.isEditable,
|
||||
config: game.system.ecryme.config,
|
||||
hasCephaly: EcrymeUtility.hasCephaly(),
|
||||
hasBoheme: EcrymeUtility.hasBoheme(),
|
||||
characters: actor.buildAnnencyActorList(),
|
||||
owner: this.document.isOwner,
|
||||
isGM: game.user.isGM,
|
||||
tabs: this._getTabs(),
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _preparePartContext(partId, context) {
|
||||
context = await super._preparePartContext(partId, context)
|
||||
if (partId === "annency" || partId === "boheme") {
|
||||
context.tab = context.tabs[partId]
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _onDrop(event) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||
if (data.type === "Actor") {
|
||||
const actor = await fromUuid(data.uuid)
|
||||
if (actor) {
|
||||
this.actor.addAnnencyActor(actor.id)
|
||||
} else {
|
||||
ui.notifications.warn("Actor not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #region Static Action Handlers
|
||||
|
||||
static #onActorEdit(event, target) {
|
||||
const li = target.closest("[data-actor-id]")
|
||||
game.actors.get(li?.dataset.actorId)?.sheet.render(true)
|
||||
}
|
||||
|
||||
static async #onActorDelete(event, target) {
|
||||
const li = target.closest("[data-actor-id]")
|
||||
this.actor.removeAnnencyActor(li?.dataset.actorId)
|
||||
}
|
||||
|
||||
static #onItemEdit(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
const itemId = li?.dataset.itemId ?? target.dataset.itemId
|
||||
this.document.items.get(itemId)?.sheet.render(true)
|
||||
}
|
||||
|
||||
static async #onItemDelete(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
EcrymeUtility.confirmDelete(this, $(li)).catch(() => {})
|
||||
}
|
||||
|
||||
static #onItemCreate(event, target) {
|
||||
const dataType = target.dataset.type
|
||||
this.document.createEmbeddedDocuments("Item", [{ name: "NewItem", type: dataType }], { renderSheet: true })
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
||||
81
modules/actors/sheets/base-actor-sheet.js
Normal file
81
modules/actors/sheets/base-actor-sheet.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||
|
||||
/**
|
||||
* Base actor sheet for Ecryme using Application V2.
|
||||
* Provides common drag-drop, image editing, and shared structure.
|
||||
*/
|
||||
export default class EcrymeBaseActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||
|
||||
constructor(options = {}) {
|
||||
super(options)
|
||||
this.#dragDrop = this.#createDragDropHandlers()
|
||||
}
|
||||
|
||||
#dragDrop
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["fvtt-ecryme", "sheet", "actor"],
|
||||
position: {
|
||||
width: 860,
|
||||
height: 680,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
dragDrop: [{ dragSelector: ".item-list .item[data-item-id]", dropSelector: null }],
|
||||
actions: {
|
||||
editImage: EcrymeBaseActorSheet.#onEditImage,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
_onRender(context, options) {
|
||||
this.#dragDrop.forEach((d) => d.bind(this.element))
|
||||
}
|
||||
|
||||
// #region Drag-and-Drop
|
||||
#createDragDropHandlers() {
|
||||
return this.options.dragDrop.map((d) => {
|
||||
d.permissions = {
|
||||
dragstart: this._canDragStart.bind(this),
|
||||
drop: this._canDragDrop.bind(this),
|
||||
}
|
||||
d.callbacks = {
|
||||
dragstart: this._onDragStart.bind(this),
|
||||
dragover: this._onDragOver.bind(this),
|
||||
drop: this._onDrop.bind(this),
|
||||
}
|
||||
return new foundry.applications.ux.DragDrop.implementation(d)
|
||||
})
|
||||
}
|
||||
|
||||
_canDragStart(selector) { return this.isEditable }
|
||||
_canDragDrop(selector) { return this.isEditable && this.document.isOwner }
|
||||
_onDragStart(event) {}
|
||||
_onDragOver(event) {}
|
||||
async _onDrop(event) {}
|
||||
// #endregion
|
||||
|
||||
// #region Actions
|
||||
static async #onEditImage(event, target) {
|
||||
const attr = target.dataset.edit
|
||||
const current = foundry.utils.getProperty(this.document, attr)
|
||||
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
|
||||
const fp = new FilePicker({
|
||||
current,
|
||||
type: "image",
|
||||
redirectToRoot: img ? [img] : [],
|
||||
callback: (path) => {
|
||||
this.document.update({ [attr]: path })
|
||||
},
|
||||
top: this.position.top + 40,
|
||||
left: this.position.left + 10,
|
||||
})
|
||||
return fp.browse()
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
255
modules/actors/sheets/pc-npc-sheet.js
Normal file
255
modules/actors/sheets/pc-npc-sheet.js
Normal file
@@ -0,0 +1,255 @@
|
||||
import EcrymeBaseActorSheet from "./base-actor-sheet.js"
|
||||
import { EcrymeUtility } from "../../common/ecryme-utility.js"
|
||||
|
||||
/**
|
||||
* Actor sheet for PC and NPC types using Application V2.
|
||||
*/
|
||||
export default class EcrymeActorSheet extends EcrymeBaseActorSheet {
|
||||
|
||||
/** @override */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ["pc-npc"],
|
||||
position: { width: 860, height: 680 },
|
||||
actions: {
|
||||
openAnnency: EcrymeActorSheet.#onOpenAnnency,
|
||||
itemEdit: EcrymeActorSheet.#onItemEdit,
|
||||
itemDelete: EcrymeActorSheet.#onItemDelete,
|
||||
itemCreate: EcrymeActorSheet.#onItemCreate,
|
||||
subactorEdit: EcrymeActorSheet.#onSubactorEdit,
|
||||
subactorDelete: EcrymeActorSheet.#onSubactorDelete,
|
||||
rollSkill: EcrymeActorSheet.#onRollSkill,
|
||||
rollSpec: EcrymeActorSheet.#onRollSpec,
|
||||
rollSkillConfront: EcrymeActorSheet.#onRollSkillConfront,
|
||||
rollCephaly: EcrymeActorSheet.#onRollCephaly,
|
||||
rollWeaponConfront:EcrymeActorSheet.#onRollWeaponConfront,
|
||||
impactModify: EcrymeActorSheet.#onImpactModify,
|
||||
rollWeapon: EcrymeActorSheet.#onRollWeapon,
|
||||
lockUnlock: EcrymeActorSheet.#onLockUnlock,
|
||||
equipItem: EcrymeActorSheet.#onEquipItem,
|
||||
quantityMinus: EcrymeActorSheet.#onQuantityMinus,
|
||||
quantityPlus: EcrymeActorSheet.#onQuantityPlus,
|
||||
},
|
||||
}
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
header: { template: "systems/fvtt-ecryme/templates/actors/partials/actor-header.hbs" },
|
||||
tabs: { template: "templates/generic/tab-navigation.hbs" },
|
||||
skills: { template: "systems/fvtt-ecryme/templates/actors/actor-skills.hbs" },
|
||||
traits: { template: "systems/fvtt-ecryme/templates/actors/actor-traits.hbs" },
|
||||
combat: { template: "systems/fvtt-ecryme/templates/actors/actor-combat.hbs" },
|
||||
cephaly: { template: "systems/fvtt-ecryme/templates/actors/actor-cephaly.hbs" },
|
||||
equipements:{ template: "systems/fvtt-ecryme/templates/actors/actor-equipements.hbs" },
|
||||
biodata: { template: "systems/fvtt-ecryme/templates/actors/actor-biodata.hbs" },
|
||||
}
|
||||
|
||||
/** @override */
|
||||
tabGroups = { primary: "skills" }
|
||||
|
||||
/** Build tabs, conditionally adding cephaly if the module is active */
|
||||
_getTabs() {
|
||||
const hasCephaly = EcrymeUtility.hasCephaly()
|
||||
const tabs = {
|
||||
skills: { id: "skills", group: "primary", label: "ECRY.ui.skills" },
|
||||
traits: { id: "traits", group: "primary", label: "ECRY.ui.traits" },
|
||||
combat: { id: "combat", group: "primary", label: "ECRY.ui.healthcombat" },
|
||||
equipements:{ id: "equipements", group: "primary", label: "ECRY.ui.equipment" },
|
||||
biodata: { id: "biodata", group: "primary", label: "ECRY.ui.bionotes" },
|
||||
}
|
||||
if (hasCephaly) {
|
||||
// Insert cephaly after combat, rebuilding the object to preserve insertion order
|
||||
const ordered = {}
|
||||
for (const [k, v] of Object.entries(tabs)) {
|
||||
ordered[k] = v
|
||||
if (k === "combat") ordered.cephaly = { id: "cephaly", group: "primary", label: "ECRY.ui.cephaly" }
|
||||
}
|
||||
for (const tab of Object.values(ordered)) {
|
||||
tab.active = this.tabGroups[tab.group] === tab.id
|
||||
tab.cssClass = tab.active ? "active" : ""
|
||||
}
|
||||
return ordered
|
||||
}
|
||||
for (const tab of Object.values(tabs)) {
|
||||
tab.active = this.tabGroups[tab.group] === tab.id
|
||||
tab.cssClass = tab.active ? "active" : ""
|
||||
}
|
||||
return tabs
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _prepareContext() {
|
||||
const actor = this.document
|
||||
return {
|
||||
actor,
|
||||
system: actor.system,
|
||||
source: actor.toObject(),
|
||||
fields: actor.schema.fields,
|
||||
systemFields: actor.system.schema.fields,
|
||||
type: actor.type,
|
||||
img: actor.img,
|
||||
name: actor.name,
|
||||
isEditable: this.isEditable,
|
||||
config: game.system.ecryme.config,
|
||||
hasCephaly: EcrymeUtility.hasCephaly(),
|
||||
hasBoheme: EcrymeUtility.hasBoheme(),
|
||||
hasAmertume: EcrymeUtility.hasAmertume(),
|
||||
skills: actor.prepareSkills(),
|
||||
traits: actor.getRollTraits(),
|
||||
ideal: actor.getIdeal(),
|
||||
spleen: actor.getSpleen(),
|
||||
weapons: actor.getWeapons(),
|
||||
maneuvers: actor.getManeuvers(),
|
||||
impactsMalus: actor.getImpactsMalus(),
|
||||
equipments: actor.getEquipments(),
|
||||
cephalySkills:actor.getCephalySkills(),
|
||||
confrontations: actor.getConfrontations(),
|
||||
subActors: actor.getSubActors(),
|
||||
annency: actor.getAnnency(),
|
||||
owner: this.document.isOwner,
|
||||
isGM: game.user.isGM,
|
||||
editScore: this.options.editScore ?? true,
|
||||
tabs: this._getTabs(),
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async _preparePartContext(partId, context) {
|
||||
context = await super._preparePartContext(partId, context)
|
||||
switch (partId) {
|
||||
case "skills":
|
||||
case "traits":
|
||||
case "combat":
|
||||
case "cephaly":
|
||||
case "equipements":
|
||||
context.tab = context.tabs[partId] ?? { cssClass: "" }
|
||||
break
|
||||
case "biodata":
|
||||
context.tab = context.tabs.biodata
|
||||
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.biodata.description, { async: true }
|
||||
)
|
||||
context.enrichedGmnotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||
this.document.system.biodata.gmnotes, { async: true }
|
||||
)
|
||||
break
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
// #region Drag and Drop
|
||||
|
||||
/** Handle incoming drops: Items from sidebar/compendium, Actors as subactors */
|
||||
async _onDrop(event) {
|
||||
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||
if (!data?.type) return
|
||||
|
||||
if (data.type === "Item") {
|
||||
const item = await fromUuid(data.uuid)
|
||||
if (!item) return
|
||||
await this.document.createEmbeddedDocuments("Item", [item.toObject()])
|
||||
} else if (data.type === "Actor") {
|
||||
const actor = fromUuidSync(data.uuid)
|
||||
if (actor) await this.actor.addSubActor(actor.id)
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle outgoing drag from embedded item rows */
|
||||
_onDragStart(event) {
|
||||
const li = event.currentTarget.closest("[data-item-id]")
|
||||
if (!li) return
|
||||
const item = this.document.items.get(li.dataset.itemId)
|
||||
if (!item) return
|
||||
event.dataTransfer.setData("text/plain", JSON.stringify(item.toDragData()))
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Static Action Handlers
|
||||
|
||||
static #onOpenAnnency(event, target) {
|
||||
const actorId = target.dataset.annencyId
|
||||
game.actors.get(actorId)?.sheet.render(true)
|
||||
}
|
||||
|
||||
static #onItemEdit(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
const itemId = li?.dataset.itemId ?? target.dataset.itemId
|
||||
this.document.items.get(itemId)?.sheet.render(true)
|
||||
}
|
||||
|
||||
static async #onItemDelete(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
EcrymeUtility.confirmDelete(this, $(li)).catch(() => {})
|
||||
}
|
||||
|
||||
static #onItemCreate(event, target) {
|
||||
const dataType = target.dataset.type
|
||||
this.document.createEmbeddedDocuments("Item", [{ name: "NewItem", type: dataType }], { renderSheet: true })
|
||||
}
|
||||
|
||||
static #onSubactorEdit(event, target) {
|
||||
const li = target.closest("[data-actor-id]")
|
||||
game.actors.get(li?.dataset.actorId)?.sheet.render(true)
|
||||
}
|
||||
|
||||
static #onSubactorDelete(event, target) {
|
||||
const li = target.closest("[data-actor-id]")
|
||||
this.actor.delSubActor(li?.dataset.actorId)
|
||||
}
|
||||
|
||||
static #onRollSkill(event, target) {
|
||||
this.actor.rollSkill(target.dataset.categoryKey, target.dataset.skillKey)
|
||||
}
|
||||
|
||||
static #onRollSpec(event, target) {
|
||||
this.actor.rollSpec(target.dataset.categoryKey, target.dataset.skillKey, target.dataset.specId)
|
||||
}
|
||||
|
||||
static #onRollSkillConfront(event, target) {
|
||||
this.actor.rollSkillConfront(target.dataset.categoryKey, target.dataset.skillKey)
|
||||
}
|
||||
|
||||
static #onRollCephaly(event, target) {
|
||||
this.actor.rollCephalySkillConfront(target.dataset.skillKey)
|
||||
}
|
||||
|
||||
static #onRollWeaponConfront(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
this.actor.rollWeaponConfront(li?.dataset.itemId)
|
||||
}
|
||||
|
||||
static #onImpactModify(event, target) {
|
||||
this.actor.modifyImpact(
|
||||
target.dataset.impactType,
|
||||
target.dataset.impactLevel,
|
||||
Number(target.dataset.impactModifier)
|
||||
)
|
||||
}
|
||||
|
||||
static #onRollWeapon(event, target) {
|
||||
this.actor.rollArme(target.dataset.armeId)
|
||||
}
|
||||
|
||||
static #onLockUnlock(event, target) {
|
||||
this.options.editScore = !this.options.editScore
|
||||
this.render(true)
|
||||
}
|
||||
|
||||
static #onEquipItem(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
this.actor.equipItem(li?.dataset.itemId)
|
||||
this.render(true)
|
||||
}
|
||||
|
||||
static #onQuantityMinus(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
this.actor.incDecQuantity(li?.dataset.itemId, -1)
|
||||
}
|
||||
|
||||
static #onQuantityPlus(event, target) {
|
||||
const li = target.closest("[data-item-id]")
|
||||
this.actor.incDecQuantity(li?.dataset.itemId, +1)
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
||||
Reference in New Issue
Block a user