diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 64f48d02..f34555dd 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -25,6 +25,10 @@ export default class AdversarySheet extends DHBaseActorSheet { }; static PARTS = { + limited: { + template: 'systems/daggerheart/templates/sheets/actors/adversary/limited.hbs', + scrollable: ['.limited-container'] + }, sidebar: { template: 'systems/daggerheart/templates/sheets/actors/adversary/sidebar.hbs', scrollable: ['.shortcut-items-section'] @@ -52,6 +56,18 @@ export default class AdversarySheet extends DHBaseActorSheet { } }; + /** @inheritdoc */ + _initializeApplicationOptions(options) { + const applicationOptions = super._initializeApplicationOptions(options); + + if (applicationOptions.document.testUserPermission(game.user, 'LIMITED', { exact: true })) { + applicationOptions.position.width = 360; + applicationOptions.position.height = 'auto'; + } + + return applicationOptions; + } + /**@inheritdoc */ async _prepareContext(options) { const context = await super._prepareContext(options); @@ -65,6 +81,7 @@ export default class AdversarySheet extends DHBaseActorSheet { context = await super._preparePartContext(partId, context, options); switch (partId) { case 'header': + case 'limited': await this._prepareHeaderContext(context, options); const adversaryTypes = CONFIG.DH.ACTOR.allAdversaryTypes(); diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 82c258e1..b9859a01 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -76,6 +76,11 @@ export default class CharacterSheet extends DHBaseActorSheet { /**@override */ static PARTS = { + limited: { + id: 'limited', + scrollable: ['.limited-container'], + template: 'systems/daggerheart/templates/sheets/actors/character/limited.hbs' + }, sidebar: { id: 'sidebar', scrollable: ['.shortcut-items-section'], @@ -141,23 +146,37 @@ export default class CharacterSheet extends DHBaseActorSheet { }); } + /** @inheritdoc */ + _initializeApplicationOptions(options) { + const applicationOptions = super._initializeApplicationOptions(options); + + if (applicationOptions.document.testUserPermission(game.user, 'LIMITED', { exact: true })) { + applicationOptions.position.width = 360; + applicationOptions.position.height = 'auto'; + } + + return applicationOptions; + } + /** @inheritDoc */ async _onRender(context, options) { await super._onRender(context, options); - this.element - .querySelector('.level-value') - ?.addEventListener('change', event => this.document.updateLevel(Number(event.currentTarget.value))); + if (!this.document.testUserPermission(game.user, 'LIMITED', { exact: true })) { + this.element + .querySelector('.level-value') + ?.addEventListener('change', event => this.document.updateLevel(Number(event.currentTarget.value))); - const observer = this.document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER, { - exact: true - }); - if (observer) { - this.element.querySelector('.window-content').classList.add('viewMode'); + const observer = this.document.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER, { + exact: true + }); + if (observer) { + this.element.querySelector('.window-content').classList.add('viewMode'); + } + + this._createFilterMenus(); + this._createSearchFilter(); } - - this._createFilterMenus(); - this._createSearchFilter(); } /* -------------------------------------------- */ diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index b353adbb..fd8cddbf 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -14,6 +14,10 @@ export default class DhCompanionSheet extends DHBaseActorSheet { }; static PARTS = { + limited: { + template: 'systems/daggerheart/templates/sheets/actors/companion/limited.hbs', + scrollable: ['.limited-container'] + }, header: { template: 'systems/daggerheart/templates/sheets/actors/companion/header.hbs' }, details: { template: 'systems/daggerheart/templates/sheets/actors/companion/details.hbs' }, effects: { diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index 0389d2c9..abf5dec0 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -26,6 +26,10 @@ export default class DhpEnvironment extends DHBaseActorSheet { /**@override */ static PARTS = { + limited: { + template: 'systems/daggerheart/templates/sheets/actors/environment/limited.hbs', + scrollable: ['.limited-container'] + }, header: { template: 'systems/daggerheart/templates/sheets/actors/environment/header.hbs' }, features: { template: 'systems/daggerheart/templates/sheets/actors/environment/features.hbs', @@ -47,6 +51,18 @@ export default class DhpEnvironment extends DHBaseActorSheet { } }; + /** @inheritdoc */ + _initializeApplicationOptions(options) { + const applicationOptions = super._initializeApplicationOptions(options); + + if (applicationOptions.document.testUserPermission(game.user, 'LIMITED', { exact: true })) { + applicationOptions.position.width = 360; + applicationOptions.position.height = 'auto'; + } + + return applicationOptions; + } + /**@inheritdoc */ async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 930edf86..273a3c67 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -47,6 +47,12 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { return (this.#settingSheet ??= SheetClass ? new SheetClass({ document: this.document }) : null); } + get isVisible() { + const viewPermission = this.document.testUserPermission(game.user, this.options.viewPermission); + const limitedOnly = this.document.testUserPermission(game.user, this.options.viewPermission, { exact: true }); + return limitedOnly ? this.document.system.metadata.hasLimitedView : viewPermission; + } + /* -------------------------------------------- */ /* Prepare Context */ /* -------------------------------------------- */ @@ -72,6 +78,31 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { return context; } + _configureRenderParts(options) { + const parts = super._configureRenderParts(options); + if (!this.document.system.metadata.hasLimitedView) return parts; + + if (this.document.testUserPermission(game.user, 'LIMITED', { exact: true })) return { limited: parts.limited }; + + return Object.keys(parts).reduce((acc, key) => { + if (key !== 'limited') acc[key] = parts[key]; + + return acc; + }, {}); + } + + /** @inheritDoc */ + async _onRender(context, options) { + await super._onRender(context, options); + + if ( + this.document.system.metadata.hasLimitedView && + this.document.testUserPermission(game.user, 'LIMITED', { exact: true }) + ) { + this.element.classList = `${this.element.classList} limited`; + } + } + /**@inheritdoc */ _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 9653cd6d..772a5af3 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -40,7 +40,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { isNPC: true, settingSheet: null, hasResistances: true, - hasAttribution: false + hasAttribution: false, + hasLimitedView: true }; } diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 4a7d6404..f3cf74b0 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -1,3 +1,22 @@ +@import '../../utils/fonts.less'; +@import '../../utils/colors.less'; +@import '../../utils/mixin.less'; + +.appTheme({ + .limited-container { + .domains-section img { + filter: @golden-filter; + } + } +}, { + .limited-container { + + .domains-section img { + filter: brightness(0) saturate(100%); + } + } +}); + .application.sheet.daggerheart.actor.dh-style { .portrait img, .profile { @@ -17,4 +36,192 @@ font-family: @font-body; color: light-dark(@chat-blue-bg, @beige-50); } + + &.limited { + &.character, + &.adversary, + &.environment, + &.companion { + .window-content { + display: unset; + padding-bottom: 20px; + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + + .limited-container { + width: 100%; + padding-top: var(--header-height); + display: flex; + flex-direction: column; + gap: 8px; + + header { + position: relative; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + top: -36px; + margin-bottom: -30px; + + .profile { + width: 100%; + max-height: 275px; + max-width: fit-content; + mask-image: linear-gradient(0deg, transparent 0%, black 10%); + } + + .title-name { + text-align: start; + font-size: var(--font-size-28); + color: light-dark(@dark-blue, @golden); + text-align: center; + } + } + + .character-details { + display: flex; + flex-direction: column; + gap: 5px; + + .basic-info, + .multiclass { + text-align: center; + padding: 0 10px; + } + } + + .domain-details, + .bio-details, + .partner-details { + margin-top: 10px; + + .domain-header, + .bio-header, + .partner-header { + display: flex; + align-items: center; + padding: 0 10px; + gap: 10px; + + h3 { + font-size: var(--font-size-20); + } + } + + .items-list { + padding: 0 20px; + } + + .partner-placeholder { + display: flex; + opacity: 0.6; + text-align: center; + font-style: italic; + justify-content: center; + padding: 10px 0; + } + + .domains-section { + position: relative; + display: flex; + gap: 10px; + background-color: light-dark(transparent, @dark-blue); + color: light-dark(@dark-blue, @golden); + padding: 5px 10px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + align-items: center; + width: fit-content; + height: 30px; + place-self: center; + margin-top: 10px; + + h4 { + font-size: var(--font-size-14); + font-weight: bold; + text-transform: uppercase; + color: light-dark(@dark-blue, @golden); + } + + .domain { + display: flex; + align-items: center; + gap: 5px; + + .label { + font-size: var(--font-size-14); + font-weight: bold; + text-transform: uppercase; + color: light-dark(@dark-blue, @golden); + } + + img { + height: 20px; + width: 20px; + } + } + } + + .bio-list { + display: flex; + gap: 20px; + flex-wrap: wrap; + justify-content: center; + margin-top: 10px; + + .bio-info { + display: flex; + flex-direction: column; + align-items: center; + padding: 10px; + border-radius: 5px; + min-width: 90px; + color: light-dark(@dark-blue, @golden); + background-color: light-dark(@dark-blue-10, @golden-40); + } + } + } + + .level-details { + align-self: center; + } + + .description { + font-style: italic; + text-align: center; + padding: 0 20px; + } + + .tags { + display: flex; + gap: 10px; + padding-bottom: 10px; + justify-content: center; + + .tag { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + padding: 3px 5px; + font-size: var(--font-size-12); + font: @font-body; + + background: light-dark(@dark-15, @beige-15); + border: 1px solid light-dark(@dark, @beige); + border-radius: 3px; + } + + .label { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + font-size: var(--font-size-12); + } + } + } + } + } } diff --git a/templates/sheets/actors/adversary/limited.hbs b/templates/sheets/actors/adversary/limited.hbs new file mode 100644 index 00000000..a9c53185 --- /dev/null +++ b/templates/sheets/actors/adversary/limited.hbs @@ -0,0 +1,23 @@ +