Add effects and tabs

This commit is contained in:
2025-12-13 19:34:04 +01:00
parent a0de5ce91a
commit 809a7b80c2
29 changed files with 1241 additions and 253 deletions
+1
View File
@@ -11,3 +11,4 @@ export { default as PrismRPGShieldSheet } from "./sheets/shield-sheet.mjs"
export { default as PrismRPGMiracleSheet } from "./sheets/miracle-sheet.mjs"
export { default as PrismRPGRaceSheet } from "./sheets/race-sheet.mjs"
export { default as PrismRPGClassSheet } from "./sheets/class-sheet.mjs"
export { default as PrismRPGCharacterPathSheet } from "./sheets/character-path-sheet.mjs"
@@ -31,6 +31,9 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
actions: {
toggleSheet: PrismRPGItemSheet.#onToggleSheet,
editImage: PrismRPGItemSheet.#onEditImage,
"create-effect": PrismRPGItemSheet.#onCreateActiveEffect,
"effect-edit": PrismRPGItemSheet.#onEffectEdit,
"effect-delete": PrismRPGItemSheet.#onEffectDelete,
},
}
@@ -65,6 +68,7 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
context.system = this.document.system
context.source = this.document.toObject()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.effectCategories = this.#prepareActiveEffectCategories(this.document.effects)
context.isEditMode = this.isEditMode
context.isPlayMode = this.isPlayMode
context.isEditable = this.isEditable
@@ -203,5 +207,99 @@ export default class PrismRPGItemSheet extends HandlebarsApplicationMixin(foundr
})
return fp.browse()
}
/**
* Prepare Active Effects organized by category (temporary, passive, inactive).
* @param {ActiveEffect[]} effects The raw Active Effects collection
* @returns {object} The categorized effects
* @private
*/
#prepareActiveEffectCategories(effects) {
// Define effect header categories
const categories = {
temporary: {
type: "temporary",
label: game.i18n.localize("PRISMRPG.Label.temporary"),
effects: [],
},
passive: {
type: "passive",
label: game.i18n.localize("PRISMRPG.Label.passive"),
effects: [],
},
inactive: {
type: "inactive",
label: game.i18n.localize("PRISMRPG.Label.inactive"),
effects: [],
},
}
// Iterate over active effects, classifying them into categories
for (let e of effects) {
const effect = e.toObject()
if (e.disabled) categories.inactive.effects.push(effect)
else if (e.isTemporary) categories.temporary.effects.push(effect)
else categories.passive.effects.push(effect)
}
return categories
}
/**
* Handle creating a new Active Effect on the Item.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
* @private
*/
static async #onCreateActiveEffect(event, target) {
const effectType = target.dataset.effectType
let durationValue = undefined
let disabled = false
if (effectType === "temporary") {
durationValue = 10
}
if (effectType === "inactive") {
disabled = true
}
const effectData = {
name: game.i18n.format("DOCUMENT.New", { type: game.i18n.localize("DOCUMENT.ActiveEffect") }),
img: "icons/svg/aura.svg",
origin: this.document.uuid,
disabled: disabled,
changes: [],
duration: durationValue !== undefined ? { rounds: durationValue } : {},
flags: {}
}
await this.document.createEmbeddedDocuments("ActiveEffect", [effectData])
}
/**
* Handle editing an Active Effect on the Item.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
* @private
*/
static async #onEffectEdit(event, target) {
const li = target.closest(".item")
const effectId = li.dataset.itemId
const effect = this.document.effects.get(effectId)
if (!effect) return
effect.sheet.render(true)
}
/**
* Handle deleting an Active Effect from the Item.
* @param {Event} event The initiating click event.
* @param {HTMLElement} target The current target of the event listener.
* @private
*/
static async #onEffectDelete(event, target) {
const li = target.closest(".item")
const effectId = li.dataset.itemId
const effect = this.document.effects.get(effectId)
if (!effect) return
await effect.delete()
}
// #endregion
}
@@ -0,0 +1,50 @@
import PrismRPGItemSheet from "./base-item-sheet.mjs"
export default class PrismRPGCharacterPathSheet extends PrismRPGItemSheet {
/** @override */
static DEFAULT_OPTIONS = {
classes: ["character-path"],
position: {
width: 600,
},
window: {
contentClasses: ["character-path-content"],
},
}
/** @override */
static PARTS = {
main: {
template: "systems/fvtt-prism-rpg/templates/character-path.hbs",
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
@@ -20,9 +20,31 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedAttributeBonuses = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.attributeBonuses, { async: true })
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
+22
View File
@@ -19,9 +19,31 @@ export default class PrismRPGRaceSheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "details",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
details: { id: "details", group: "primary", label: "PRISMRPG.Label.details" },
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true })
context.enrichedRacialPassiveDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.racialPassiveDescription, { async: true })
context.enrichedSubraceAbilityDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.subraceAbilityDescription, { async: true })
@@ -19,4 +19,32 @@ export default class PrismRPGRacialAbilitySheet extends PrismRPGItemSheet {
},
}
/** @override */
tabGroups = {
primary: "description",
}
/**
* Prepare an array of form header tabs.
* @returns {Record<string, Partial<ApplicationTab>>}
*/
#getTabs() {
const tabs = {
description: { id: "description", group: "primary", label: "PRISMRPG.Label.description" },
effects: { id: "effects", group: "primary", label: "PRISMRPG.Label.effects" },
}
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] === v.id
v.cssClass = v.active ? "active" : ""
}
return tabs
}
/** @override */
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.#getTabs()
return context
}
}
+2 -1
View File
@@ -7,7 +7,8 @@ export const defaultItemImg = {
shield: "systems/fvtt-prism-rpg/assets/icons/icon_shield.webp",
spell: "systems/fvtt-prism-rpg/assets/icons/icon_spell.webp",
race: "systems/fvtt-prism-rpg/assets/icons/icon_race.webp",
class: "systems/fvtt-prism-rpg/assets/icons/icon_class.webp"
class: "systems/fvtt-prism-rpg/assets/icons/icon_class.webp",
"character-path": "systems/fvtt-prism-rpg/assets/icons/icon_character_path.webp",
}
export default class PrismRPGItem extends Item {
+2 -1
View File
@@ -10,4 +10,5 @@ export { default as PrismRPGVulnerability } from "./vulnerability.mjs"
export { default as PrismRPGEquipment } from "./equipment.mjs"
export { default as PrismRPGMiracle } from "./miracle.mjs"
export { default as PrismRPGRace } from "./race.mjs"
export { default as PrismRPGClass } from "./class.mjs"
export { default as PrismRPGClass } from "./class.mjs"
export { default as PrismRPGCharacterPath } from "./character-path.mjs"
+14
View File
@@ -0,0 +1,14 @@
export default class PrismRPGCharacterPath extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields
const schema = {}
schema.description = new fields.HTMLField({ required: true, textSearch: true })
return schema
}
/** @override */
static LOCALIZATION_PREFIXES = ["PRISMRPG.CharacterPath"]
}
+11
View File
@@ -251,4 +251,15 @@ export default class PrismRPGUtils {
}
}
/**
* Preload Handlebars templates for partials
* @returns {Promise}
*/
static async preloadHandlebarsTemplates() {
const templatePaths = [
'systems/fvtt-prism-rpg/templates/partial-item-effects.hbs',
]
return foundry.applications.handlebars.loadTemplates(templatePaths)
}
}