diff --git a/assets/icons/icon_character_path.webp b/assets/icons/icon_character_path.webp new file mode 100644 index 0000000..e0a35ba Binary files /dev/null and b/assets/icons/icon_character_path.webp differ diff --git a/css/fvtt-prism-rpg.css b/css/fvtt-prism-rpg.css index 1b28050..398ce44 100644 --- a/css/fvtt-prism-rpg.css +++ b/css/fvtt-prism-rpg.css @@ -2553,6 +2553,312 @@ i.prismrpg { .prismrpg .class-content label { flex: 10%; } +.prismrpg .character-path-content { + font-family: var(--font-primary); + font-size: calc(var(--font-size-standard) * 1); + color: var(--color-dark-1); + background-image: var(--background-image-base); + background-repeat: no-repeat; + background-size: 100% 100%; + overflow: auto; +} +.prismrpg .character-path-content nav.tabs [data-tab] { + color: #636060; +} +.prismrpg .character-path-content nav.tabs [data-tab].active { + color: #252424; +} +.prismrpg .character-path-content input:disabled, +.prismrpg .character-path-content select:disabled { + background-color: rgba(0, 0, 0, 0.2); + border-color: transparent; + color: var(--color-dark-3); +} +.prismrpg .character-path-content input, +.prismrpg .character-path-content select { + height: 1.5rem; + background-color: rgba(0, 0, 0, 0.1); + border-color: var(--color-dark-6); + color: var(--color-dark-2); +} +.prismrpg .character-path-content input[name="name"] { + height: 2.5rem; + margin-right: 4px; + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1.2); + font-weight: bold; + border: none; +} +.prismrpg .character-path-content fieldset { + margin-bottom: 4px; + border-radius: 4px; +} +.prismrpg .character-path-content .form-fields input, +.prismrpg .character-path-content .form-fields select { + text-align: center; + font-size: calc(var(--font-size-standard) * 1); +} +.prismrpg .character-path-content .form-fields select { + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1); +} +.prismrpg .character-path-content legend { + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1.2); + font-weight: bold; + letter-spacing: 1px; +} +.prismrpg .character-path-content .form-fields { + padding-top: 4px; +} +.prismrpg .character-path-content .form-group { + display: flex; + flex: 1; + flex-direction: row; +} +.prismrpg .character-path-content .form-group label { + align-content: center; + min-width: 10rem; + max-width: 10rem; +} +.prismrpg .character-path-content .form-group select, +.prismrpg .character-path-content .form-group input { + text-align: left; + min-width: 12rem; + max-width: 12rem; +} +.prismrpg .character-path-content .form-group input[type="checkbox"] { + min-width: 1.2rem; + max-width: 1.2rem; + margin-right: 0.5rem; +} +.prismrpg .character-path-content label { + font-family: var(--font-secondary); + font-size: calc(var(--font-size-standard) * 1); + flex: 50%; +} +.prismrpg .character-path-content .align-top { + align-self: flex-start; + padding: 0.1rem; + margin-right: 0.2rem; +} +.prismrpg .character-path-content .shift-right { + margin-left: 2rem; +} +.prismrpg .character-path-content .header { + display: flex; +} +.prismrpg .character-path-content .header img { + width: 50px; + height: 50px; +} +.prismrpg .character-path-content input[type="checkbox"] { + font-size: var(--font-size-14); + width: 20px; + padding-top: 0; +} +.prismrpg .character-path-content input[type="checkbox"]:checked { + background-color: rgba(0, 0, 0, 0.1); +} +.prismrpg .character-path-content input[type="checkbox"]:checked::after { + color: rgba(0, 0, 0, 0.1); +} +.prismrpg .effects-container { + padding: 0.5rem; +} +.prismrpg .effect-category { + margin-bottom: 1rem; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + overflow: hidden; + background: rgba(255, 255, 255, 0.02); +} +.prismrpg .effect-category:last-child { + margin-bottom: 0; +} +.prismrpg .stat-list { + list-style: none; + margin: 0; + padding: 0; +} +.prismrpg .stat-list.alternate-list .list-item { + padding: 0.4rem 0.6rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + align-items: center; + gap: 0.5rem; + min-height: 32px; +} +.prismrpg .stat-list.alternate-list .list-item.items-title-bg { + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.15)); + border-bottom: 2px solid rgba(0, 0, 0, 0.4); + padding: 0.5rem 0.6rem; + font-weight: bold; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); +} +.prismrpg .stat-list.alternate-list .list-item.items-title-bg h3 { + margin: 0; + font-size: 1rem; + font-family: var(--font-secondary); + color: rgba(0, 0, 0, 0.9); + text-shadow: 0 1px 2px rgba(255, 255, 255, 0.3); + font-weight: bold; +} +.prismrpg .stat-list.alternate-list .list-item.items-title-bg .short-label { + font-size: 0.7rem; + text-transform: uppercase; + font-weight: 600; + color: rgba(0, 0, 0, 0.7); + letter-spacing: 0.5px; +} +.prismrpg .stat-list.alternate-list .list-item.list-item-shadow { + transition: all 0.2s ease; + background: rgba(255, 255, 255, 0.05); +} +.prismrpg .stat-list.alternate-list .list-item.list-item-shadow:hover { + background: rgba(255, 255, 255, 0.15); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} +.prismrpg .stat-list.alternate-list .list-item:last-child { + border-bottom: none; +} +.prismrpg .sheet-competence-img { + width: 28px; + height: 28px; + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 4px; + object-fit: cover; + flex-shrink: 0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} +.prismrpg .item-name-img { + display: flex; + align-items: center; + flex-shrink: 0; + text-decoration: none; +} +.prismrpg .item-name-img:hover .sheet-competence-img { + border-color: rgba(13, 110, 253, 0.6); + box-shadow: 0 2px 4px rgba(13, 110, 253, 0.3); +} +.prismrpg .item-name-label-header-long { + flex: 2; + display: flex; + align-items: center; +} +.prismrpg .item-name-label-header-long .items-title-text { + font-weight: bold; +} +.prismrpg .item-name-label-long { + flex: 2; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(0, 0, 0, 0.9); + font-size: 0.9rem; +} +.prismrpg .item-field-label-short { + flex: 0 0 90px; + font-size: 0.8rem; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(0, 0, 0, 0.7); + font-style: italic; +} +.prismrpg .item-field-label-medium { + flex: 0 0 100px; + font-size: 0.8rem; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(0, 0, 0, 0.7); + font-style: italic; +} +.prismrpg .item-filler { + flex: 1; + min-width: 10px; +} +.prismrpg .item-controls { + display: flex; + gap: 0.3rem; + align-items: center; + flex-shrink: 0; +} +.prismrpg .item-controls.item-controls-fixed { + flex: 0 0 auto; +} +.prismrpg .item-controls.effect-controls { + gap: 0.25rem; +} +.prismrpg .effect-control { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border: 1px solid rgba(0, 0, 0, 0.2); + background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8), rgba(240, 240, 240, 0.8)); + border-radius: 4px; + cursor: pointer; + transition: all 0.15s ease; + color: rgba(0, 0, 0, 0.7); + text-decoration: none; + padding: 0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} +.prismrpg .effect-control i { + font-size: 0.85rem; + margin: 0; +} +.prismrpg .effect-control:hover { + background: linear-gradient(to bottom, #ffffff, #e6e6e6); + border-color: rgba(0, 0, 0, 0.4); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transform: translateY(-1px); +} +.prismrpg .effect-control:active { + transform: translateY(0); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} +.prismrpg .effect-control.item-edit:hover { + background: linear-gradient(to bottom, rgba(13, 110, 253, 0.2), rgba(13, 110, 253, 0.3)); + color: #0d6efd; + border-color: rgba(13, 110, 253, 0.5); +} +.prismrpg .effect-control[data-action="create-effect"] { + width: auto; + padding: 0.3rem 0.5rem; + gap: 0.3rem; + font-size: 0.7rem; + font-weight: 600; + background: linear-gradient(to bottom, rgba(40, 167, 69, 0.25), rgba(40, 167, 69, 0.35)); + border-color: rgba(40, 167, 69, 0.4); + color: #19642a; + text-transform: uppercase; + letter-spacing: 0.3px; +} +.prismrpg .effect-control[data-action="create-effect"]:hover { + background: linear-gradient(to bottom, rgba(40, 167, 69, 0.35), rgba(40, 167, 69, 0.45)); + border-color: rgba(40, 167, 69, 0.6); + color: #19642a; +} +.prismrpg .effect-control[data-action="create-effect"] i { + font-size: 0.75rem; +} +.prismrpg .effect-control[data-action="effect-delete"]:hover { + background: linear-gradient(to bottom, rgba(220, 53, 69, 0.2), rgba(220, 53, 69, 0.3)); + color: #dc3545; + border-color: rgba(220, 53, 69, 0.5); +} +.prismrpg .flexrow { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.5rem; +} .application.dialog.prismrpg { color: var(--color-dark-1); } diff --git a/lang/en.json b/lang/en.json index 98e1c9b..f41626a 100644 --- a/lang/en.json +++ b/lang/en.json @@ -403,6 +403,10 @@ "description": "Description", "details": "Details", "effects": "Effects", + "source": "Source", + "temporary": "Temporary", + "passive": "Passive", + "inactive": "Inactive", "dex": "DEX", "equipment": "Equipment", "experience": "Experience", @@ -1244,7 +1248,8 @@ "vulnerability": "Vulnerability", "weapon": "Weapon", "race": "Race", - "class": "Class" + "class": "Class", + "character-path": "Character Path" } } } \ No newline at end of file diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index dd6b4f0..b0826a4 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -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" diff --git a/module/applications/sheets/base-item-sheet.mjs b/module/applications/sheets/base-item-sheet.mjs index 5774cb0..1ca733a 100644 --- a/module/applications/sheets/base-item-sheet.mjs +++ b/module/applications/sheets/base-item-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 } diff --git a/module/applications/sheets/character-path-sheet.mjs b/module/applications/sheets/character-path-sheet.mjs new file mode 100644 index 0000000..644fe43 --- /dev/null +++ b/module/applications/sheets/character-path-sheet.mjs @@ -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>} + */ + #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 + } + +} diff --git a/module/applications/sheets/class-sheet.mjs b/module/applications/sheets/class-sheet.mjs index 8dd8c66..84faf63 100644 --- a/module/applications/sheets/class-sheet.mjs +++ b/module/applications/sheets/class-sheet.mjs @@ -20,9 +20,31 @@ export default class PrismRPGClassSheet extends PrismRPGItemSheet { }, } + /** @override */ + tabGroups = { + primary: "details", + } + + /** + * Prepare an array of form header tabs. + * @returns {Record>} + */ + #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 }) diff --git a/module/applications/sheets/race-sheet.mjs b/module/applications/sheets/race-sheet.mjs index 30e21b9..d23936c 100644 --- a/module/applications/sheets/race-sheet.mjs +++ b/module/applications/sheets/race-sheet.mjs @@ -19,9 +19,31 @@ export default class PrismRPGRaceSheet extends PrismRPGItemSheet { }, } + /** @override */ + tabGroups = { + primary: "details", + } + + /** + * Prepare an array of form header tabs. + * @returns {Record>} + */ + #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 }) diff --git a/module/applications/sheets/racial-ability-sheet.mjs b/module/applications/sheets/racial-ability-sheet.mjs index 0040f92..3c5a35f 100644 --- a/module/applications/sheets/racial-ability-sheet.mjs +++ b/module/applications/sheets/racial-ability-sheet.mjs @@ -19,4 +19,32 @@ export default class PrismRPGRacialAbilitySheet extends PrismRPGItemSheet { }, } + /** @override */ + tabGroups = { + primary: "description", + } + + /** + * Prepare an array of form header tabs. + * @returns {Record>} + */ + #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 + } + } diff --git a/module/documents/item.mjs b/module/documents/item.mjs index b296c2f..be49d58 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -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 { diff --git a/module/models/_module.mjs b/module/models/_module.mjs index bc74875..eacbb11 100644 --- a/module/models/_module.mjs +++ b/module/models/_module.mjs @@ -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" \ No newline at end of file +export { default as PrismRPGClass } from "./class.mjs" +export { default as PrismRPGCharacterPath } from "./character-path.mjs" \ No newline at end of file diff --git a/module/models/character-path.mjs b/module/models/character-path.mjs new file mode 100644 index 0000000..81ec06a --- /dev/null +++ b/module/models/character-path.mjs @@ -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"] + +} diff --git a/module/utils.mjs b/module/utils.mjs index 7a316e5..408dcd6 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -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) + } + } diff --git a/prism-rpg.mjs b/prism-rpg.mjs index 511c804..8ef8a4f 100644 --- a/prism-rpg.mjs +++ b/prism-rpg.mjs @@ -57,6 +57,7 @@ Hooks.once("init", function () { // Miracle: models.PrismRPGMiracle // Disabled - Legacy from Lethal Fantasy, PRISM uses Divine class features instead race: models.PrismRPGRace, class: models.PrismRPGClass, + "character-path": models.PrismRPGCharacterPath, } // Register sheet application classes @@ -75,6 +76,7 @@ Hooks.once("init", function () { foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGEquipmentSheet, { types: ["equipment"], makeDefault: true }) foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGRaceSheet, { types: ["race"], makeDefault: true }) foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGClassSheet, { types: ["class"], makeDefault: true }) + foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGCharacterPathSheet, { types: ["character-path"], makeDefault: true }) // Foundry.documents.collections.Items.registerSheet("prismRPG", applications.PrismRPGMiracleSheet, { types: ["miracle"], makeDefault: true }) // Disabled - Legacy from Lethal Fantasy // Other Document Configuration @@ -96,6 +98,7 @@ Hooks.once("init", function () { setupTextEnrichers() PrismRPGUtils.registerHandlebarsHelpers() + PrismRPGUtils.preloadHandlebarsTemplates() PrismRPGUtils.setHookListeners() console.info("PRISM RPG | System Initialized") diff --git a/styles/character-path.less b/styles/character-path.less new file mode 100644 index 0000000..5041ea8 --- /dev/null +++ b/styles/character-path.less @@ -0,0 +1,26 @@ +.character-path-content { + .sheet-common(); + .item-sheet-common(); + + .header { + display: flex; + img { + width: 50px; + height: 50px; + } + } + + input[type="checkbox"] { + font-size: var(--font-size-14); + width: 20px; + padding-top: 0; + } + + input[type="checkbox"]:checked { + background-color: rgba(0, 0, 0, 0.1); + } + + input[type="checkbox"]:checked::after { + color: rgba(0, 0, 0, 0.1); + } +} diff --git a/styles/effects.less b/styles/effects.less new file mode 100644 index 0000000..1be1cbb --- /dev/null +++ b/styles/effects.less @@ -0,0 +1,270 @@ +// Active Effects styling - compact and modern design + +// Effects container +.effects-container { + padding: 0.5rem; +} + +// Effect categories sections +.effect-category { + margin-bottom: 1rem; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + overflow: hidden; + background: rgba(255, 255, 255, 0.02); + + &:last-child { + margin-bottom: 0; + } +} + +// Effect list styling +.stat-list { + list-style: none; + margin: 0; + padding: 0; + + &.alternate-list { + .list-item { + padding: 0.4rem 0.6rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + align-items: center; + gap: 0.5rem; + min-height: 32px; + + &.items-title-bg { + background: linear-gradient( + to bottom, + rgba(0, 0, 0, 0.25), + rgba(0, 0, 0, 0.15) + ); + border-bottom: 2px solid rgba(0, 0, 0, 0.4); + padding: 0.5rem 0.6rem; + font-weight: bold; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + + h3 { + margin: 0; + font-size: 1rem; + font-family: var(--font-secondary); + color: rgba(0, 0, 0, 0.9); + text-shadow: 0 1px 2px rgba(255, 255, 255, 0.3); + font-weight: bold; + } + + .short-label { + font-size: 0.7rem; + text-transform: uppercase; + font-weight: 600; + color: rgba(0, 0, 0, 0.7); + letter-spacing: 0.5px; + } + } + + &.list-item-shadow { + transition: all 0.2s ease; + background: rgba(255, 255, 255, 0.05); + + &:hover { + background: rgba(255, 255, 255, 0.15); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + } + } + + &:last-child { + border-bottom: none; + } + } + } +} + +// Effect image - compact size +.sheet-competence-img { + width: 28px; + height: 28px; + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 4px; + object-fit: cover; + flex-shrink: 0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +} + +.item-name-img { + display: flex; + align-items: center; + flex-shrink: 0; + text-decoration: none; + + &:hover .sheet-competence-img { + border-color: rgba(13, 110, 253, 0.6); + box-shadow: 0 2px 4px rgba(13, 110, 253, 0.3); + } +} + +// Effect labels - compact spacing +.item-name-label-header-long { + flex: 2; + display: flex; + align-items: center; + + .items-title-text { + font-weight: bold; + } +} + +.item-name-label-long { + flex: 2; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(0, 0, 0, 0.9); + font-size: 0.9rem; +} + +.item-field-label-short { + flex: 0 0 90px; + font-size: 0.8rem; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(0, 0, 0, 0.7); + font-style: italic; +} + +.item-field-label-medium { + flex: 0 0 100px; + font-size: 0.8rem; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: rgba(0, 0, 0, 0.7); + font-style: italic; +} + +// Filler for spacing +.item-filler { + flex: 1; + min-width: 10px; +} + +// Effect controls - compact and modern +.item-controls { + display: flex; + gap: 0.3rem; + align-items: center; + flex-shrink: 0; + + &.item-controls-fixed { + flex: 0 0 auto; + } + + &.effect-controls { + gap: 0.25rem; + } +} + +.effect-control { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border: 1px solid rgba(0, 0, 0, 0.2); + background: linear-gradient( + to bottom, + rgba(255, 255, 255, 0.8), + rgba(240, 240, 240, 0.8) + ); + border-radius: 4px; + cursor: pointer; + transition: all 0.15s ease; + color: rgba(0, 0, 0, 0.7); + text-decoration: none; + padding: 0; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + + i { + font-size: 0.85rem; + margin: 0; + } + + &:hover { + background: linear-gradient( + to bottom, + rgba(255, 255, 255, 1), + rgba(230, 230, 230, 1) + ); + border-color: rgba(0, 0, 0, 0.4); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + } + + &.item-edit:hover { + background: linear-gradient( + to bottom, + rgba(13, 110, 253, 0.2), + rgba(13, 110, 253, 0.3) + ); + color: rgba(13, 110, 253, 1); + border-color: rgba(13, 110, 253, 0.5); + } + + // New effect button styling + &[data-action="create-effect"] { + width: auto; + padding: 0.3rem 0.5rem; + gap: 0.3rem; + font-size: 0.7rem; + font-weight: 600; + background: linear-gradient( + to bottom, + rgba(40, 167, 69, 0.25), + rgba(40, 167, 69, 0.35) + ); + border-color: rgba(40, 167, 69, 0.4); + color: rgba(25, 100, 42, 1); + text-transform: uppercase; + letter-spacing: 0.3px; + + &:hover { + background: linear-gradient( + to bottom, + rgba(40, 167, 69, 0.35), + rgba(40, 167, 69, 0.45) + ); + border-color: rgba(40, 167, 69, 0.6); + color: rgba(25, 100, 42, 1); + } + + i { + font-size: 0.75rem; + } + } + + // Delete button specific styling + &[data-action="effect-delete"]:hover { + background: linear-gradient( + to bottom, + rgba(220, 53, 69, 0.2), + rgba(220, 53, 69, 0.3) + ); + color: rgba(220, 53, 69, 1); + border-color: rgba(220, 53, 69, 0.5); + } +} + +// Flexrow utility +.flexrow { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.5rem; +} diff --git a/styles/fvtt-prism-rpg.less b/styles/fvtt-prism-rpg.less index d01c219..7169719 100644 --- a/styles/fvtt-prism-rpg.less +++ b/styles/fvtt-prism-rpg.less @@ -17,6 +17,8 @@ @import "miracle.less"; @import "race.less"; @import "class.less"; + @import "character-path.less"; + @import "effects.less"; } @import "roll.less"; diff --git a/system.json b/system.json index e8e6f72..3103e22 100644 --- a/system.json +++ b/system.json @@ -40,7 +40,8 @@ "spell": { "htmlFields": ["description"] }, "equipment": { "htmlFields": ["description"] }, "race": { "htmlFields": ["description", "racialPassiveDescription", "subraceAbilityDescription", "notes"] }, - "class": { "htmlFields": ["description", "attributeBonuses", "notes", "features.level1", "features.level2", "features.level3", "features.level4", "features.level5", "features.level6", "features.level7", "features.level8", "features.level9", "features.level10"] } + "class": { "htmlFields": ["description", "attributeBonuses", "notes", "features.level1", "features.level2", "features.level3", "features.level4", "features.level5", "features.level6", "features.level7", "features.level8", "features.level9", "features.level10"] }, + "character-path": { "htmlFields": ["description"] } } }, "grid": { diff --git a/templates/armor.hbs b/templates/armor.hbs index aeb6734..a87f9e8 100644 --- a/templates/armor.hbs +++ b/templates/armor.hbs @@ -108,57 +108,7 @@ data-group="primary" data-tab="effects" > - {{! Prism RPG: Armor Passive }} -
- {{localize "PRISMRPG.Label.armorPassive"}} - {{formField - systemFields.passive - value=system.passive - localize=true - label="PRISMRPG.Label.passiveName" - }} - - {{formInput - systemFields.passiveDescription - enriched=enrichedPassiveDescription - value=system.passiveDescription - name="system.passiveDescription" - toggled=true - }} -
- - {{! Prism RPG: Armor Augment }} -
- {{localize "PRISMRPG.Label.armorAugment"}} - {{formField - systemFields.augment - value=system.augment - localize=true - label="PRISMRPG.Label.augmentName" - }} - - {{formInput - systemFields.augmentDescription - enriched=enrichedAugmentDescription - value=system.augmentDescription - name="system.augmentDescription" - toggled=true - }} -
- - {{! Cost }} - {{formField - systemFields.cost - value=system.cost - localize=true - label="PRISMRPG.Label.cost" - }} - {{formField - systemFields.money - value=system.money - localize=true - label="PRISMRPG.Label.currency" - }} + {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} \ No newline at end of file diff --git a/templates/character-path.hbs b/templates/character-path.hbs new file mode 100644 index 0000000..79b90f4 --- /dev/null +++ b/templates/character-path.hbs @@ -0,0 +1,38 @@ +
+
+ + {{formInput fields.name value=source.name}} +
+ + {{! Navigation des onglets }} + + + {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
+
+ + {{! Onglet Effects }} +
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} +
+ +
\ No newline at end of file diff --git a/templates/class.hbs b/templates/class.hbs index 80c2962..cd8b932 100644 --- a/templates/class.hbs +++ b/templates/class.hbs @@ -4,6 +4,14 @@ {{formInput fields.name value=source.name}} + {{! Navigation des onglets }} + + + {{! Onglet Details }} +
@@ -63,29 +71,32 @@ }} {{/each}} +
- {{! Notes }} -
- {{localize "PRISMRPG.Label.notes"}} - {{formInput - systemFields.notes - enriched=enrichedNotes - value=system.notes - name="system.notes" - toggled=true - }} -
+ {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
- {{! Description }} -
- {{localize "PRISMRPG.Label.description"}} - {{formInput - systemFields.description - enriched=enrichedDescription - value=system.description - name="system.description" - toggled=true - }} -
+ {{! Notes }} +
+ {{localize "PRISMRPG.Label.notes"}} + {{formInput + systemFields.notes + enriched=enrichedNotes + value=system.notes + name="system.notes" + toggled=true + }} +
+
diff --git a/templates/equipment.hbs b/templates/equipment.hbs index 654aa75..9ad86c3 100644 --- a/templates/equipment.hbs +++ b/templates/equipment.hbs @@ -24,24 +24,7 @@ {{formField systemFields.money value=system.money localize=true}} {{formField systemFields.isKit value=system.isKit localize=true label="PRISMRPG.Label.isKit"}} -
- {{! Onglet Description }} -
-
- {{localize "PRISMRPG.Label.description"}} - {{formInput - systemFields.description - enriched=enrichedDescription - value=system.description - name="system.description" - toggled=true - }} -
-
- - {{! Onglet Effects }} -
{{#if system.isKit}} {{! Kit Passive }}
@@ -102,4 +85,23 @@ {{/if}}
+ {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
+
+ + {{! Onglet Effects }} +
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} +
+ \ No newline at end of file diff --git a/templates/miracle.hbs b/templates/miracle.hbs index bf547a1..041d08b 100644 --- a/templates/miracle.hbs +++ b/templates/miracle.hbs @@ -150,6 +150,27 @@ localize=true label="PRISMRPG.Label.level" }} + + {{! Prism RPG: Miracle Augment }} +
+ {{localize "PRISMRPG.Label.miracleAugment"}} + {{formField + systemFields.augment + value=system.augment + localize=true + label="PRISMRPG.Label.augmentName" + }} +
+ + {{formInput + systemFields.augmentDescription + enriched=enrichedAugmentDescription + value=system.augmentDescription + name="system.augmentDescription" + toggled=true + }} +
+
{{! Onglet Description }} @@ -176,26 +197,7 @@ data-group="primary" data-tab="effects" > - {{! Prism RPG: Miracle Augment }} -
- {{localize "PRISMRPG.Label.miracleAugment"}} - {{formField - systemFields.augment - value=system.augment - localize=true - label="PRISMRPG.Label.augmentName" - }} -
- - {{formInput - systemFields.augmentDescription - enriched=enrichedAugmentDescription - value=system.augmentDescription - name="system.augmentDescription" - toggled=true - }} -
-
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} \ No newline at end of file diff --git a/templates/partial-item-effects.hbs b/templates/partial-item-effects.hbs new file mode 100644 index 0000000..2be8797 --- /dev/null +++ b/templates/partial-item-effects.hbs @@ -0,0 +1,80 @@ +{{! Template pour l'onglet Effects des items - organisé par catégories }} +
+ + {{#each effectCategories as |section sid|}} +
+
    +
  • + +

    +
    + + + + + + +
     
    + +
  • + + {{#each section.effects as |effect|}} +
  • + + + + {{effect.name}} + {{effect.sourceName}} + {{effect.duration.label}} + +
     
    + +
  • + {{/each}} +
+
+ {{/each}} + +
\ No newline at end of file diff --git a/templates/race.hbs b/templates/race.hbs index 77c896e..fa3f072 100644 --- a/templates/race.hbs +++ b/templates/race.hbs @@ -10,82 +10,106 @@ {{formInput fields.name value=source.name}} -
-
+ {{! Navigation des onglets }} + - {{! Basic Information }} - {{formField systemFields.senses value=system.senses}} + {{! Onglet Details }} +
+
+
- {{formField systemFields.size value=system.size localize=true}} + {{! Basic Information }} + {{formField systemFields.senses value=system.senses}} - {{formField - systemFields.ageCategory - value=system.ageCategory - localize=true + {{formField systemFields.size value=system.size localize=true}} + + {{formField + systemFields.ageCategory + value=system.ageCategory + localize=true + }} + + {{formField systemFields.language value=system.language}} + +
+
+ + {{! Racial Passive }} + {{formField systemFields.racialPassive value=system.racialPassive}} + + {{! Sub-race }} + {{formField systemFields.subrace value=system.subrace}} + + {{formField systemFields.subraceAbility value=system.subraceAbility}} + +
+
+ + {{! Racial Passive Description }} +
+ {{localize "PRISMRPG.Label.racialPassive"}} + {{formInput + systemFields.racialPassiveDescription + enriched=enrichedRacialPassiveDescription + value=system.racialPassiveDescription + name="system.racialPassiveDescription" + toggled=true }} +
- {{formField systemFields.language value=system.language}} - -
-
- - {{! Racial Passive }} - {{formField systemFields.racialPassive value=system.racialPassive}} - - {{! Sub-race }} - {{formField systemFields.subrace value=system.subrace}} - - {{formField systemFields.subraceAbility value=system.subraceAbility}} - -
+ {{! Sub-race Ability Description }} +
+ {{localize "PRISMRPG.Label.subraceAbility"}} + {{formInput + systemFields.subraceAbilityDescription + enriched=enrichedSubraceAbilityDescription + value=system.subraceAbilityDescription + name="system.subraceAbilityDescription" + toggled=true + }} +
- {{! Racial Passive Description }} -
- {{localize "PRISMRPG.Label.racialPassive"}} - {{formInput - systemFields.racialPassiveDescription - enriched=enrichedRacialPassiveDescription - value=system.racialPassiveDescription - name="system.racialPassiveDescription" - toggled=true - }} -
+ {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
- {{! Sub-race Ability Description }} -
- {{localize "PRISMRPG.Label.subraceAbility"}} - {{formInput - systemFields.subraceAbilityDescription - enriched=enrichedSubraceAbilityDescription - value=system.subraceAbilityDescription - name="system.subraceAbilityDescription" - toggled=true - }} -
- - {{! Notes }} -
- {{localize "PRISMRPG.Label.notes"}} - {{formInput - systemFields.notes - enriched=enrichedNotes - value=system.notes - name="system.notes" - toggled=true - }} -
- - {{! Description }} -
- {{localize "PRISMRPG.Label.description"}} - {{formInput - systemFields.description - enriched=enrichedDescription - value=system.description - name="system.description" - toggled=true - }} -
+ {{! Notes }} +
+ {{localize "PRISMRPG.Label.notes"}} + {{formInput + systemFields.notes + enriched=enrichedNotes + value=system.notes + name="system.notes" + toggled=true + }} +
+
\ No newline at end of file diff --git a/templates/racial-ability.hbs b/templates/racial-ability.hbs index 5ea97c4..79b90f4 100644 --- a/templates/racial-ability.hbs +++ b/templates/racial-ability.hbs @@ -10,15 +10,29 @@ {{formInput fields.name value=source.name}}
-
- {{localize "PRISMRPG.Label.description"}} - {{formInput - systemFields.description - enriched=enrichedDescription - value=system.description - name="system.description" - toggled=true - }} -
+ {{! Navigation des onglets }} + + + {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
+
+ + {{! Onglet Effects }} +
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} +
\ No newline at end of file diff --git a/templates/shield.hbs b/templates/shield.hbs index 7ed8eeb..200679f 100644 --- a/templates/shield.hbs +++ b/templates/shield.hbs @@ -90,6 +90,25 @@ + + {{! Prism RPG: Block Augment }} +
+ {{localize "PRISMRPG.Label.blockAugment"}} + {{formField + systemFields.blockAugment + value=system.blockAugment + localize=true + label="PRISMRPG.Label.blockAugmentName" + }} + + {{formInput + systemFields.blockAugmentDescription + enriched=enrichedBlockAugmentDescription + value=system.blockAugmentDescription + name="system.blockAugmentDescription" + toggled=true + }} +
{{! Onglet Description }} @@ -116,24 +135,7 @@ data-group="primary" data-tab="effects" > - {{! Prism RPG: Block Augment }} -
- {{localize "PRISMRPG.Label.blockAugment"}} - {{formField - systemFields.blockAugment - value=system.blockAugment - localize=true - label="PRISMRPG.Label.blockAugmentName" - }} - - {{formInput - systemFields.blockAugmentDescription - enriched=enrichedBlockAugmentDescription - value=system.blockAugmentDescription - name="system.blockAugmentDescription" - toggled=true - }} -
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} \ No newline at end of file diff --git a/templates/spell.hbs b/templates/spell.hbs index b777241..1f412ee 100644 --- a/templates/spell.hbs +++ b/templates/spell.hbs @@ -123,32 +123,7 @@ localize=true label="PRISMRPG.Label.keywords" }} - - {{! Onglet Description }} -
-
- {{localize "PRISMRPG.Label.description"}} - {{formInput - systemFields.description - enriched=enrichedDescription - value=system.description - name="system.description" - toggled=true - }} -
-
- - {{! Onglet Effects }} -
{{! Prism RPG: Color Effect }}
{{localize "PRISMRPG.Label.colorEffect"}} @@ -186,4 +161,31 @@
+ {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
+
+ + {{! Onglet Effects }} +
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} +
+ \ No newline at end of file diff --git a/templates/weapon.hbs b/templates/weapon.hbs index b01962f..0bba688 100644 --- a/templates/weapon.hbs +++ b/templates/weapon.hbs @@ -109,24 +109,7 @@ {{formField systemFields.money value=system.money localize=true}} - - {{! Onglet Description }} -
-
- {{localize "PRISMRPG.Label.description"}} - {{formInput - systemFields.description - enriched=enrichedDescription - value=system.description - name="system.description" - toggled=true - }} -
-
- - {{! Onglet Effects }} -
{{! Prism RPG: Weapon Passives }}
@@ -204,4 +187,23 @@
+ {{! Onglet Description }} +
+
+ {{localize "PRISMRPG.Label.description"}} + {{formInput + systemFields.description + enriched=enrichedDescription + value=system.description + name="system.description" + toggled=true + }} +
+
+ + {{! Onglet Effects }} +
+ {{> systems/fvtt-prism-rpg/templates/partial-item-effects.hbs}} +
+ \ No newline at end of file