From 307eb657f9890615a57e7a557f9ff4e5650e49fb Mon Sep 17 00:00:00 2001 From: Kevin Lee Garner Date: Thu, 25 Jul 2024 15:54:03 -0500 Subject: [PATCH] [#687] [#672] NPC Exhaustion and Conditions (#688) * Added NPC Effects tab. Generalized CharacterConditions to be ActorConditions. Added conditions and effects adjustments to NPC context. Replaced NPC flag-based exhaustion to system-schema-based exhaustion. * Implemented NPC exhaustion migration. * Removed the Tidy 5e sheets alpha migration and its localization keys. * Made conditions and effects data preparation code shared. Reused in PC and NPC sheet data prep. --- public/lang/en.json | 11 +- src/constants.ts | 3 +- .../ConditionsAndEffects.ts | 73 +++++++ src/migrations/BulkMigrations.svelte | 14 +- src/migrations/MigrationTally.ts | 2 +- .../notification/MigrationNotification.svelte | 10 +- .../v1/V1OnboardingMigration.svelte | 29 --- src/migrations/v1/alpha-flags-to-v1.ts | 59 ------ src/migrations/v1/alpha-settings-to-v1.ts | 33 ---- .../v5/NpcExhaustionToSystemMigration.svelte | 165 ++++++++++++++++ src/migrations/v5/npc-exhaustion-to-system.ts | 31 +++ src/runtime/NpcSheetRuntime.ts | 4 +- src/sheets/Tidy5eCharacterSheet.ts | 60 +----- src/sheets/Tidy5eNpcSheet.ts | 13 +- .../ActorConditions.svelte} | 4 +- .../character/tabs/CharacterEffectsTab.svelte | 4 +- src/sheets/npc/parts/NpcProfile.svelte | 15 +- src/sheets/npc/tabs/NpcEffectsTab.svelte | 185 ++++++++++++++++++ src/types/types.ts | 3 + 19 files changed, 520 insertions(+), 198 deletions(-) create mode 100644 src/features/conditions-and-effects/ConditionsAndEffects.ts delete mode 100644 src/migrations/v1/alpha-flags-to-v1.ts delete mode 100644 src/migrations/v1/alpha-settings-to-v1.ts create mode 100644 src/migrations/v5/NpcExhaustionToSystemMigration.svelte create mode 100644 src/migrations/v5/npc-exhaustion-to-system.ts rename src/sheets/{character/parts/CharacterConditions.svelte => actor/ActorConditions.svelte} (90%) create mode 100644 src/sheets/npc/tabs/NpcEffectsTab.svelte diff --git a/public/lang/en.json b/public/lang/en.json index bbb595369..23fdf439d 100644 --- a/public/lang/en.json +++ b/public/lang/en.json @@ -641,7 +641,7 @@ "name": "Migrations", "buttonLabel": "Migrate Tidy Data", "dialogTitle": "Migrate Tidy Data", - "hint": "If you were a user of the original Tidy5e Sheet module or the Alpha sheets, optionally use this Migrations menu to migrate your GM settings and Tidy sheet data.", + "hint": "If you were a user of the original Tidy5e Sheet module, optionally use this Migrations menu to migrate your GM settings and Tidy sheet data.", "migrationBeginningMessage": "Migrating data. Please do not refresh the page until done.", "migrationErrorMessage": "An error occurred. More details are in the dev console.", "migrationCompleteMessage": "Migration complete.", @@ -657,10 +657,7 @@ "mainExplanation1": "Migrating data to the new Tidy 5e Sheets module is not required in order to use it. Worst-case, if you are an original Tidy5e Sheet user and skip migrations, you will need to re-apply your settings to your liking.", "mainExplanation2": "{boldStart}Note{boldEnd}: Client Settings are not a part of this migration. Enough has changed about how client settings work in Tidy, so it is recommended to take a fresh look at client settings.", "originalHeader": "Original Tidy5e Sheet", - "originalExplanation": "Migrate GM settings and Tidy sheet data flags from the original Tidy5e Sheet. Most of the differences in data are from GM settings. If you migrate your settings and find a setting to be not to your liking, simply change the settings from the config settings menu.", - "alphaHeader": "Alpha Tidy 5e Sheets", - "alphaExplanation1": "Migrate GM settings and Tidy sheet data flags from the Alpha Tidy 5e Sheets. Most of the differences in data are from the sheet data flags. The Alpha flags migration targets top-level actors, top-level actors' items, and top-level items. This will bring in biographical data, favorites, and other sheet-specific Alpha data flags.", - "alphaExplanation2": "{boldStart}Note{boldEnd}: this migration does not affect compendium content." + "originalExplanation": "Migrate GM settings and Tidy sheet data flags from the original Tidy5e Sheet. Most of the differences in data are from GM settings. If you migrate your settings and find a setting to be not to your liking, simply change the settings from the config settings menu." }, "Notification": { "Title": "Tidy 5e Sheets: Migrations Available", @@ -716,6 +713,10 @@ "sectionTitle": "Spell Class to Source Class", "selectionDialogTitle": "Migration: Spell Class to Source Class" }, + "NpcExhaustion": { + "sectionTitle": "NPC Exhaustion", + "selectionDialogTitle": "Migration: NPC Exhaustion to System" + }, "Parent": "Parent" }, "TIDY5E.Settings.SheetPreferences": { diff --git a/src/constants.ts b/src/constants.ts index a58ec09de..3ebc1e1f5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -85,7 +85,8 @@ export const CONSTANTS = { TAB_MIGRATIONS_CCSS_TO_TIDY: 'ccss-to-tidy', TAB_MIGRATIONS_FAVORITES_TO_SYSTEM: 'favorites-to-system', TAB_MIGRATIONS_BONDS_IDEALS_FLAWS_TO_SYSTEM: 'bonds-ideals-flaws-to-system', - TAB_SPELL_CLASS_TO_SOURCE_CLASS: 'spell-class-to-source-class', + TAB_MIGRATIONS_SPELL_CLASS_TO_SOURCE_CLASS: 'spell-class-to-source-class', + TAB_MIGRATIONS_NPC_EXHAUSTION: 'npc-exhaustion', TAB_NPC_ABILITIES: 'attributes', TAB_NPC_INVENTORY: 'inventory', TAB_NPC_SPELLBOOK: 'spellbook', diff --git a/src/features/conditions-and-effects/ConditionsAndEffects.ts b/src/features/conditions-and-effects/ConditionsAndEffects.ts new file mode 100644 index 000000000..d302a5d46 --- /dev/null +++ b/src/features/conditions-and-effects/ConditionsAndEffects.ts @@ -0,0 +1,73 @@ +import type { Dnd5eActorCondition } from 'src/foundry/foundry-and-system'; +import type { Actor5e } from 'src/types/types'; + +// TODO: In AppV2, consider a mixin for common data preparation between Actors, like in dnd5e. +export class ConditionsAndEffects { + static async getConditionsAndEffects( + actor: Actor5e, + object: any, + effectSections: any[] + ): Promise<{ + conditions: Dnd5eActorCondition[]; + effects: any; + }> { + const conditionIds = new Set(); + const conditions = Object.entries(CONFIG.DND5E.conditionTypes).reduce< + Dnd5eActorCondition[] + >((arr, [k, c]) => { + if (c.pseudo) return arr; // Filter out pseudo-conditions. + const { label: name, icon, reference } = c; + const id = dnd5e.utils.staticID(`dnd5e${k}`); + conditionIds.add(id); + const existing = actor.effects.get(id); + const { disabled, img } = existing ?? {}; + arr.push({ + name, + reference, + id: k, + icon: img ?? icon, + disabled: existing ? disabled : true, + }); + return arr; + }, []); + + for (const category of Object.values(effectSections)) { + category.effects = await category.effects.reduce( + async (arr: any[], effect: any) => { + effect.updateDuration(); + if (conditionIds.has(effect.id) && !effect.duration.remaining) + return arr; + const { id, name, img, disabled, duration } = effect; + let source = (await effect.getSource()) ?? actor; + // If the source is an ActiveEffect from another Actor, note the source as that Actor instead. + if ( + source instanceof dnd5e.documents.ActiveEffect5e && + source.target !== object + ) { + source = source.target; + } + + arr = await arr; + arr.push({ + id, + name, + img, + disabled, + duration, + source, + parentId: effect.target === effect.parent ? null : effect.parent.id, + durationParts: duration.remaining ? duration.label.split(', ') : [], + hasTooltip: source instanceof dnd5e.documents.Item5e, + }); + return arr; + }, + [] + ); + } + + return { + conditions, + effects: effectSections, + }; + } +} diff --git a/src/migrations/BulkMigrations.svelte b/src/migrations/BulkMigrations.svelte index 458ebc4e3..17007ec95 100644 --- a/src/migrations/BulkMigrations.svelte +++ b/src/migrations/BulkMigrations.svelte @@ -12,14 +12,24 @@ import FavoritesToSystemMigration from './v4/FavoritesToSystemMigration.svelte'; import BondsIdealsFlawsToSystemMigration from './v5/BondsIdealsFlawsToSystemMigration.svelte'; import SpellClassToSourceClassMigration from './v5/SpellClassToSourceClassMigration.svelte'; + import NpcExhaustionToSystemMigration from './v5/NpcExhaustionToSystemMigration.svelte'; - export let selectedTabId: string = CONSTANTS.TAB_SPELL_CLASS_TO_SOURCE_CLASS; + export let selectedTabId: string = + CONSTANTS.TAB_MIGRATIONS_SPELL_CLASS_TO_SOURCE_CLASS; const localize = FoundryAdapter.localize; const tabs: Tab[] = [ { - id: CONSTANTS.TAB_SPELL_CLASS_TO_SOURCE_CLASS, + id: CONSTANTS.TAB_MIGRATIONS_NPC_EXHAUSTION, + title: 'TIDY5E.Settings.Migrations.NpcExhaustion.sectionTitle', + content: { + component: NpcExhaustionToSystemMigration, + type: 'svelte', + }, + }, + { + id: CONSTANTS.TAB_MIGRATIONS_SPELL_CLASS_TO_SOURCE_CLASS, title: 'TIDY5E.Settings.Migrations.SpellClassToSourceClass.sectionTitle', content: { component: SpellClassToSourceClassMigration, diff --git a/src/migrations/MigrationTally.ts b/src/migrations/MigrationTally.ts index 15458b325..0fe99470f 100644 --- a/src/migrations/MigrationTally.ts +++ b/src/migrations/MigrationTally.ts @@ -6,4 +6,4 @@ * The number of times this tally advances is not as important as the fact that it advances at least once for any release that contains at least one new migration. */ -export const MigrationTally = 5 as const; +export const MigrationTally = 6 as const; diff --git a/src/migrations/notification/MigrationNotification.svelte b/src/migrations/notification/MigrationNotification.svelte index 500af69b7..b4b47b556 100644 --- a/src/migrations/notification/MigrationNotification.svelte +++ b/src/migrations/notification/MigrationNotification.svelte @@ -15,6 +15,14 @@ SettingsProvider.settings.migrationsConfirmationTally.get(); const migrations = [ + { + label: localize('TIDY5E.Settings.Migrations.NpcExhaustion.sectionTitle'), + migrationTallyVersion: 6, + onClick: () => + new BulkMigrationsApplication( + CONSTANTS.TAB_MIGRATIONS_NPC_EXHAUSTION, + ).render(true), + }, { label: localize( 'TIDY5E.Settings.Migrations.SpellClassToSourceClass.sectionTitle', @@ -22,7 +30,7 @@ migrationTallyVersion: 5, onClick: () => new BulkMigrationsApplication( - CONSTANTS.TAB_SPELL_CLASS_TO_SOURCE_CLASS, + CONSTANTS.TAB_MIGRATIONS_SPELL_CLASS_TO_SOURCE_CLASS, ).render(true), }, { diff --git a/src/migrations/v1/V1OnboardingMigration.svelte b/src/migrations/v1/V1OnboardingMigration.svelte index 60f81cbfb..cfbba720e 100644 --- a/src/migrations/v1/V1OnboardingMigration.svelte +++ b/src/migrations/v1/V1OnboardingMigration.svelte @@ -1,6 +1,4 @@ + +
+

+ {localize('TIDY5E.Settings.Migrations.NpcExhaustion.sectionTitle')} +

+

{localize('TIDY5E.Settings.Migrations.UnlinkedExplanation')}

+

{localize('TIDY5E.Settings.Migrations.OptionsHeader')}

+
+ +
+ + +
+ + diff --git a/src/migrations/v5/npc-exhaustion-to-system.ts b/src/migrations/v5/npc-exhaustion-to-system.ts new file mode 100644 index 000000000..ab90f9d2b --- /dev/null +++ b/src/migrations/v5/npc-exhaustion-to-system.ts @@ -0,0 +1,31 @@ +import { TidyFlags } from 'src/foundry/TidyFlags'; +import type { Actor5e } from 'src/types/types'; + +type NpcExhaustionToSystemMigrationParams = { + npc: Actor5e; + clearFlagData: boolean; +}; + +const exhaustionFlag = 'exhaustion'; + +export async function migrateNpcExhaustionToSystem({ + npc, + clearFlagData, +}: NpcExhaustionToSystemMigrationParams) { + const tidyExhaustion = TidyFlags.tryGetFlag( + npc, + exhaustionFlag + ); + + if (tidyExhaustion === null || tidyExhaustion === undefined) { + return; + } + + await npc.update({ + 'system.attributes.exhaustion': tidyExhaustion, + }); + + if (clearFlagData) { + TidyFlags.unsetFlag(npc, exhaustionFlag); + } +} diff --git a/src/runtime/NpcSheetRuntime.ts b/src/runtime/NpcSheetRuntime.ts index 034b617b3..f06558009 100644 --- a/src/runtime/NpcSheetRuntime.ts +++ b/src/runtime/NpcSheetRuntime.ts @@ -8,7 +8,7 @@ import { CONSTANTS } from 'src/constants'; import NpcAbilitiesTab from 'src/sheets/npc/tabs/NpcAbilitiesTab.svelte'; import NpcSpellbookTab from 'src/sheets/npc/tabs/NpcSpellbookTab.svelte'; import NpcBiographyTab from 'src/sheets/npc/tabs/NpcBiographyTab.svelte'; -import ActorEffectsTab from 'src/sheets/actor/ActorEffectsTab.svelte'; +import NpcEffectsTab from 'src/sheets/npc/tabs/NpcEffectsTab.svelte'; import ActorJournalTab from 'src/sheets/actor/tabs/ActorJournalTab.svelte'; import ActorActionsTab from 'src/sheets/actor/tabs/ActorActionsTab.svelte'; import type { RegisteredContent, RegisteredTab } from './types'; @@ -55,7 +55,7 @@ export class NpcSheetRuntime { id: 'effects', title: 'DND5E.Effects', content: { - component: ActorEffectsTab, + component: NpcEffectsTab, type: 'svelte', }, layout: 'classic', diff --git a/src/sheets/Tidy5eCharacterSheet.ts b/src/sheets/Tidy5eCharacterSheet.ts index 4e7a18699..ad409f1a7 100644 --- a/src/sheets/Tidy5eCharacterSheet.ts +++ b/src/sheets/Tidy5eCharacterSheet.ts @@ -71,6 +71,7 @@ import { TidyHooks } from 'src/foundry/TidyHooks'; import { TidyFlags } from 'src/foundry/TidyFlags'; import { Container } from 'src/features/containers/Container'; import { InlineContainerToggleService } from 'src/features/containers/InlineContainerToggleService'; +import { ConditionsAndEffects } from 'src/features/conditions-and-effects/ConditionsAndEffects'; export class Tidy5eCharacterSheet extends ActorSheetCustomSectionMixin( @@ -708,60 +709,12 @@ export class Tidy5eCharacterSheet }; // Effects & Conditions - const conditionIds = new Set(); - const conditions = Object.entries(CONFIG.DND5E.conditionTypes).reduce< - Dnd5eActorCondition[] - >((arr, [k, c]) => { - if (c.pseudo) return arr; // Filter out pseudo-conditions. - const { label: name, icon, reference } = c; - const id = dnd5e.utils.staticID(`dnd5e${k}`); - conditionIds.add(id); - const existing = this.actor.effects.get(id); - const { disabled, img } = existing ?? {}; - arr.push({ - name, - reference, - id: k, - icon: img ?? icon, - disabled: existing ? disabled : true, - }); - return arr; - }, []); - - for (const category of Object.values( - defaultDocumentContext.effects as any[] - )) { - category.effects = await category.effects.reduce( - async (arr: any[], effect: any) => { - effect.updateDuration(); - if (conditionIds.has(effect.id) && !effect.duration.remaining) - return arr; - const { id, name, img, disabled, duration } = effect; - let source = (await effect.getSource()) ?? this.actor; - // If the source is an ActiveEffect from another Actor, note the source as that Actor instead. - if ( - source instanceof dnd5e.documents.ActiveEffect5e && - source.target !== this.object - ) { - source = source.target; - } - arr = await arr; - arr.push({ - id, - name, - img, - disabled, - duration, - source, - parentId: effect.target === effect.parent ? null : effect.parent.id, - durationParts: duration.remaining ? duration.label.split(', ') : [], - hasTooltip: source instanceof dnd5e.documents.Item5e, - }); - return arr; - }, - [] + let { conditions, effects: enhancedEffectSections } = + await ConditionsAndEffects.getConditionsAndEffects( + this.actor, + this.object, + defaultDocumentContext.effects ); - } const context: CharacterSheetContext = { ...defaultDocumentContext, @@ -817,6 +770,7 @@ export class Tidy5eCharacterSheet defaultDocumentContext ), editable: defaultDocumentContext.editable, + effects: enhancedEffectSections, filterData: this.itemFilterService.getDocumentItemFilterData(), filterPins: ItemFilterRuntime.defaultFilterPins[this.actor.type], flawEnrichedHtml: await FoundryAdapter.enrichHtml( diff --git a/src/sheets/Tidy5eNpcSheet.ts b/src/sheets/Tidy5eNpcSheet.ts index cb9ed2d25..7ab49f645 100644 --- a/src/sheets/Tidy5eNpcSheet.ts +++ b/src/sheets/Tidy5eNpcSheet.ts @@ -59,6 +59,7 @@ import { TidyHooks } from 'src/foundry/TidyHooks'; import { Inventory } from 'src/features/sections/Inventory'; import { Container } from 'src/features/containers/Container'; import { InlineContainerToggleService } from 'src/features/containers/InlineContainerToggleService'; +import { ConditionsAndEffects } from 'src/features/conditions-and-effects/ConditionsAndEffects'; export class Tidy5eNpcSheet extends ActorSheetCustomSectionMixin(dnd5e.applications.actor.ActorSheet5eNPC) @@ -659,6 +660,14 @@ export class Tidy5eNpcSheet }, }; + // Effects & Conditions + let { conditions, effects: enhancedEffectSections } = + await ConditionsAndEffects.getConditionsAndEffects( + this.actor, + this.object, + defaultDocumentContext.effects + ); + const context: NpcSheetContext = { ...defaultDocumentContext, actions: await getActorActionSections(this.actor), @@ -698,6 +707,7 @@ export class Tidy5eNpcSheet relativeTo: this.actor, } ), + conditions: conditions, containerPanelItems: await Inventory.getContainerPanelItems( defaultDocumentContext.items ), @@ -707,8 +717,9 @@ export class Tidy5eNpcSheet customContent: await NpcSheetRuntime.getContent(defaultDocumentContext), useClassicControls: SettingsProvider.settings.useClassicControlsForNpc.get(), - encumbrance: this.actor.system.attributes.encumbrance, + effects: enhancedEffectSections, editable: defaultDocumentContext.editable, + encumbrance: this.actor.system.attributes.encumbrance, filterData: this.itemFilterService.getDocumentItemFilterData(), filterPins: ItemFilterRuntime.defaultFilterPins[this.actor.type], flawEnrichedHtml: await FoundryAdapter.enrichHtml( diff --git a/src/sheets/character/parts/CharacterConditions.svelte b/src/sheets/actor/ActorConditions.svelte similarity index 90% rename from src/sheets/character/parts/CharacterConditions.svelte rename to src/sheets/actor/ActorConditions.svelte index f269e846e..6da7a9d1e 100644 --- a/src/sheets/character/parts/CharacterConditions.svelte +++ b/src/sheets/actor/ActorConditions.svelte @@ -5,11 +5,11 @@ import ConditionToggle from 'src/components/toggle/ConditionToggle.svelte'; import { CONSTANTS } from 'src/constants'; import { FoundryAdapter } from 'src/foundry/foundry-adapter'; - import type { CharacterSheetContext } from 'src/types/types'; + import type { CharacterSheetContext, NpcSheetContext } from 'src/types/types'; import { getContext } from 'svelte'; import type { Readable } from 'svelte/store'; - let context = getContext>( + let context = getContext>( CONSTANTS.SVELTE_CONTEXT.CONTEXT, ); diff --git a/src/sheets/character/tabs/CharacterEffectsTab.svelte b/src/sheets/character/tabs/CharacterEffectsTab.svelte index f99f0a450..6f4db274f 100644 --- a/src/sheets/character/tabs/CharacterEffectsTab.svelte +++ b/src/sheets/character/tabs/CharacterEffectsTab.svelte @@ -17,7 +17,7 @@ import type { Readable } from 'svelte/store'; import Notice from 'src/components/notice/Notice.svelte'; import { declareLocation } from 'src/types/location-awareness.types'; - import CharacterConditions from '../parts/CharacterConditions.svelte'; + import ActorConditions from '../../actor/ActorConditions.svelte'; import ClassicControls from 'src/sheets/shared/ClassicControls.svelte'; import ActorEffectToggleControl from 'src/components/item-list/controls/ActorEffectToggleControl.svelte'; import EffectFavoriteControl from 'src/components/item-list/controls/EffectFavoriteControl.svelte'; @@ -193,6 +193,6 @@ {/each} {/if} {#if $context.conditions} - + {/if} diff --git a/src/sheets/npc/parts/NpcProfile.svelte b/src/sheets/npc/parts/NpcProfile.svelte index 4b5261b53..b750ea1fc 100644 --- a/src/sheets/npc/parts/NpcProfile.svelte +++ b/src/sheets/npc/parts/NpcProfile.svelte @@ -13,7 +13,6 @@ import { settingStore } from 'src/settings/settings'; import ExhaustionInput from 'src/sheets/actor/ExhaustionInput.svelte'; import { ActiveEffectsHelper } from 'src/utils/active-effect'; - import { TidyFlags } from 'src/foundry/TidyFlags'; import { CONSTANTS } from 'src/constants'; let context = getContext>( @@ -24,8 +23,10 @@ ($context.actor?.system?.attributes?.hp?.value ?? 0) <= 0 && $context.actor?.system?.attributes?.hp?.max !== 0; - function onLevelSelected(event: CustomEvent<{ level: number }>) { - TidyFlags.setFlag($context.actor, 'exhaustion', event.detail.level); + async function onLevelSelected(event: CustomEvent<{ level: number }>) { + await $context.actor.update({ + 'system.attributes.exhaustion': event.detail.level, + }); } @@ -43,23 +44,23 @@ {/if} {#if $settingStore.useExhaustion && $settingStore.exhaustionConfig.type === 'specific'} {:else if $settingStore.useExhaustion && $settingStore.exhaustionConfig.type === 'open'} {/if} diff --git a/src/sheets/npc/tabs/NpcEffectsTab.svelte b/src/sheets/npc/tabs/NpcEffectsTab.svelte new file mode 100644 index 000000000..4ae78b920 --- /dev/null +++ b/src/sheets/npc/tabs/NpcEffectsTab.svelte @@ -0,0 +1,185 @@ + + +
+ {#if !$context.allowEffectsManagement && $context.unlocked} + {localize('TIDY5E.GMOnlyEdit')} + {/if} + + {#if noEffects && !$context.unlocked && $context.allowEffectsManagement} + {localize('TIDY5E.EmptySection')} + {:else} + {#each effectSections as section} + {#if !section.hidden} + {#if ($context.unlocked && $context.allowEffectsManagement) || section.effects.length > 0} + + + + + {localize(section.label)} + + + {localize('DND5E.Source')} + + + {localize('DND5E.Duration')} + + {#if $context.editable && $context.useClassicControls && $context.allowEffectsManagement} + + {/if} + + + + {#each section.effects as effectContext} + + FoundryAdapter.editOnMiddleClick( + event.detail, + FoundryAdapter.getEffect({ + document: $context.actor, + effectId: effectContext.id, + parentId: effectContext.parentId, + }), + )} + contextMenu={{ + type: CONSTANTS.CONTEXT_MENU_TYPE_EFFECTS, + uuid: effectContext.uuid, + }} + getDragData={() => + FoundryAdapter.getEffect({ + document: $context.actor, + effectId: effectContext.id, + parentId: effectContext.parentId, + })?.toDragData()} + effect={effectContext} + > + + + {effectContext.name} + + {effectContext.source?.name ?? ''} + {effectContext.duration?.label ?? ''} + {#if $context.editable && $context.useClassicControls && $context.allowEffectsManagement} + + + + {/if} + + {/each} + {#if $context.unlocked && $context.allowEffectsManagement} + + FoundryAdapter.addEffect(section.type, $context.actor)} + isItem={false} + /> + {/if} + + + {/if} + {/if} + {/each} + {/if} + {#if $context.conditions} + + {/if} +
diff --git a/src/types/types.ts b/src/types/types.ts index 782fdd83e..9b71b7256 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -18,6 +18,7 @@ import type { DocumentFilters } from 'src/runtime/item/item.types'; import type { Writable } from 'svelte/store'; import type { UtilityToolbarCommandParams } from 'src/components/utility-bar/types'; import type { CONSTANTS } from 'src/constants'; +import type { Dnd5eActorCondition } from 'src/foundry/foundry-and-system'; export type Actor5e = any; @@ -247,6 +248,7 @@ export type CharacterSheetContext = { appearanceEnrichedHtml: string; biographyEnrichedHtml: string; bondEnrichedHtml: string; + conditions: Dnd5eActorCondition[]; containerPanelItems: ContainerPanelItemContext[]; favorites: FavoriteSection[]; features: CharacterFeatureSection[]; @@ -295,6 +297,7 @@ export type NpcSheetContext = { appearanceEnrichedHtml: string; biographyEnrichedHtml: string; bondEnrichedHtml: string; + conditions: Dnd5eActorCondition[]; containerPanelItems: ContainerPanelItemContext[]; encumbrance: any; features: NpcAbilitySection[];