Skip to content

Commit

Permalink
Merge pull request #829 from kgar/dnd5e-4.1.x-compat-first-pass
Browse files Browse the repository at this point in the history
First pass at compatibility
  • Loading branch information
kgar authored Nov 10, 2024
2 parents f5a3258 + 320e502 commit 8dd3358
Show file tree
Hide file tree
Showing 31 changed files with 324 additions and 212 deletions.
4 changes: 2 additions & 2 deletions public/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@
{
"id": "dnd5e",
"compatibility": {
"minimum": "4.0.0",
"verified": "4.0.0",
"minimum": "4.1.0",
"verified": "4.1.0",
"maximum": "4.1.x"
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/components/item-list/controls/DeleteOrOpenActivity.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import type { Item5e } from 'src/types/item.types';
import SpellOpenCastActivityControl from './SpellOpenCastActivityControl.svelte';
import ItemDeleteControl from './ItemDeleteControl.svelte';
export let item: Item5e;
</script>

{#if item.canDelete || !item.system.linkedActivity}
<ItemDeleteControl {item} />
{:else}
<SpellOpenCastActivityControl {item} />
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { FoundryAdapter } from 'src/foundry/foundry-adapter';
import ItemControl from './ItemControl.svelte';
import type { Item5e } from 'src/types/item.types';
export let item: Item5e;
const localize = FoundryAdapter.localize;
</script>

<ItemControl
iconCssClass="fas fa-gear"
class={$$restProps.class ?? ''}
onclick={() => item.system.linkedActivity.sheet.render(true)}
title={localize('DOCUMENT.DND5E.Activity')}
/>
9 changes: 7 additions & 2 deletions src/components/spellbook/SpellbookList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import type { Item5e } from 'src/types/item.types';
import ClassicControls from 'src/sheets/shared/ClassicControls.svelte';
import ConcentrationOverlayIcon from './ConcentrationOverlayIcon.svelte';
import DeleteOrOpenActivity from '../item-list/controls/DeleteOrOpenActivity.svelte';
let context = getContext<Readable<CharacterSheetContext | NpcSheetContext>>(
CONSTANTS.SVELTE_CONTEXT.CONTEXT,
Expand Down Expand Up @@ -86,7 +87,8 @@
if ($context.unlocked) {
controls.push({
component: ItemDeleteControl,
// svelte 5 - snippet?
component: DeleteOrOpenActivity,
props: ({ item }) => ({
item,
}),
Expand Down Expand Up @@ -209,7 +211,10 @@
<InlineFavoriteIcon />
{/if}
<ItemTableCell baseWidth={spellComponentsBaseWidth} cssClass="no-gap">
<SpellComponents {spell} spellComponentLabels={$context.spellComponentLabels} />
<SpellComponents
{spell}
spellComponentLabels={$context.spellComponentLabels}
/>
</ItemTableCell>
{#if includeSchool}
{@const icon = SpellSchool.getIcon(spell.system.school)}
Expand Down
58 changes: 39 additions & 19 deletions src/context-menu/tidy5e-context-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,21 +286,23 @@ function getItemContextOptions(item: Item5e) {
}

// Toggle Prepared State
if ('preparation' in item.system) {
if (FoundryAdapter.canPrepareSpell(item)) {
const isPrepared = item.system?.preparation?.prepared === true;
options.push({
name: isActive
? 'TIDY5E.ContextMenuActionUnprepare'
: 'TIDY5E.ContextMenuActionPrepare',
icon: isActive
? "<i class='fas fa-book fa-fw'></i>"
: "<i class='fas fa-book fa-fw'></i>",
callback: () =>
item.update({ 'system.preparation.prepared': !isPrepared }),
condition: () => item.isOwner && !item.compendium?.locked,
});
}
if (
'preparation' in item.system &&
FoundryAdapter.canPrepareSpell(item) &&
!item.getFlag('dnd5e', 'cachedFor')
) {
const isPrepared = item.system?.preparation?.prepared === true;
options.push({
name: isActive
? 'TIDY5E.ContextMenuActionUnprepare'
: 'TIDY5E.ContextMenuActionPrepare',
icon: isActive
? "<i class='fas fa-book fa-fw'></i>"
: "<i class='fas fa-book fa-fw'></i>",
callback: () =>
item.update({ 'system.preparation.prepared': !isPrepared }),
condition: () => item.isOwner && !item.compendium?.locked,
});
}

options.push({
Expand Down Expand Up @@ -349,7 +351,7 @@ function getItemContextOptions(item: Item5e) {
icon: "<i class='fas fa-copy fa-fw'></i>",
condition: () =>
isUnlocked &&
!['race', 'background', 'class', 'subclass'].includes(item.type) &&
item.canDuplicate &&
item.isOwner &&
!item.compendium?.locked,

Expand All @@ -369,7 +371,21 @@ function getItemContextOptions(item: Item5e) {
name: 'TIDY5E.ContextMenuActionDelete',
icon: "<i class='fas fa-trash fa-fw' style='color: var(--t5e-warning-accent-color);'></i>",
callback: () => FoundryAdapter.onActorItemDelete(itemParent, item),
condition: () => isUnlocked && item.isOwner && !item.compendium?.locked,
condition: () =>
item.canDelete &&
isUnlocked &&
item.isOwner &&
!item.compendium?.locked,
});
options.push({
name: 'DOCUMENT.DND5E.Activity',
icon: "<i class='fas fa-gear fa-fw'></i>",
callback: () => item.system.linkedActivity.sheet.render(true),
condition: () =>
!item.canDelete &&
item.system.linkedActivity &&
item.isOwner &&
!item.compendium?.locked,
});
} else {
options.push({
Expand All @@ -393,14 +409,18 @@ function getItemContextOptions(item: Item5e) {
if (SettingsProvider.settings.includeFlagsInSpellScrollCreation.get()) {
options.flags = item.flags;
}

const scroll = await dnd5e.documents.Item5e.createScrollFromSpell(item, options);

const scroll = await dnd5e.documents.Item5e.createScrollFromSpell(
item,
options
);
if (scroll) {
dnd5e.documents.Item5e.create(scroll, { parent: itemParent });
}
},
condition: () =>
item.type === 'spell' &&
!item.getFlag('dnd5e', 'cachedFor') &&
itemParent?.isOwner &&
!itemParent?.compendium?.locked,
group: 'action',
Expand Down
4 changes: 2 additions & 2 deletions src/features/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,8 @@ function getRangeTitles(item: Item5e): {
const firstActivity = item.system.activities?.contents[0] ?? {};

const rangeSubtitle =
(firstActivity.target.affects?.type ??
firstActivity.target.template?.type) &&
(firstActivity.target?.affects?.type ??
firstActivity.target?.template?.type) &&
item.labels?.target
? item.labels.target
: null;
Expand Down
126 changes: 63 additions & 63 deletions src/foundry/foundry-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,19 +749,25 @@ export const FoundryAdapter = {
): Promise<any> {
return new Roll(formula, rollData).roll(rollFnOptions);
},
openActorTypeConfig(actor: Actor5e) {
return new dnd5e.applications.actor.ActorTypeConfig(actor).render(true);
},
openCharacterActorTypeConfig(actor: Actor5e) {
if (actor.system.details.race?.id) {
return new dnd5e.applications.actor.ActorTypeConfig(
actor.system.details.race,
{ keyPath: 'system.type' }
).render(true);
}
renderCreatureTypeConfig(document: Actor5e) {
const raceId: string | undefined = document.system.details?.race?.id;

const documentToUpdate = raceId ? document.system.details.race : document;

const keyPath =
documentToUpdate.type === CONSTANTS.ITEM_TYPE_RACE
? // A species document
'type'
: // An actor without a species
'details.type';

const options: Record<string, any> = {
document: documentToUpdate,
keyPath,
};

warn(
'Unable to open actor type config for player character because they do not have a race.'
return new dnd5e.applications.shared.CreatureTypeConfig(options).render(
true
);
},
playDiceSound() {
Expand Down Expand Up @@ -858,74 +864,64 @@ export const FoundryAdapter = {
renderArmorConfig(actor: any) {
return new dnd5e.applications.actor.ActorArmorConfig(actor).render(true);
},
renderActorInitiativeConfig(actor: any) {
return new dnd5e.applications.actor.ActorInitiativeConfig(actor).render(
true
);
renderInitiativeConfig(document: any) {
return new dnd5e.applications.actor.InitiativeConfig({
document,
}).render(true);
},
renderActorAbilityConfig(actor: any, abbreviation: any) {
return new dnd5e.applications.actor.ActorAbilityConfig(
actor,
null,
abbreviation
).render(true);
renderAbilityConfig(document: any, key: any) {
return new dnd5e.applications.actor.AbilityConfig({
document,
key,
}).render(true);
},
renderActorMovementConfig(actor: any) {
return new dnd5e.applications.actor.ActorMovementConfig(actor).render(true);
renderMovementSensesConfig(document: any, type: 'movement' | 'senses') {
return new dnd5e.applications.shared.MovementSensesConfig({
document,
type,
}).render(true);
},
renderActorHitPointsDialog(actor: any) {
return new dnd5e.applications.actor.ActorHitPointsConfig(actor).render(
renderHitPointsDialog(document: any) {
return new dnd5e.applications.actor.HitPointsConfig({ document }).render(
true
);
},
renderActorHitDiceConfig(actor: any) {
return new dnd5e.applications.actor.ActorHitDiceConfig(actor).render(true);
renderHitDiceConfig(document: any) {
return new dnd5e.applications.actor.HitDiceConfig({ document }).render(
true
);
},
dialogConfirm(...args: any[]) {
return Dialog.confirm(...args);
},
renderActorSheetFlags(actor: any) {
return new dnd5e.applications.actor.ActorSheetFlags(actor).render(true);
},
renderToolSelector(actor: any) {
return new dnd5e.applications.actor.ToolSelector(actor, 'tool').render(
true
);
},
renderActorSensesConfig(actor: any) {
return new dnd5e.applications.actor.ActorSensesConfig(actor).render(true);
renderToolsConfig(document: any) {
return new dnd5e.applications.actor.ToolsConfig({
document,
trait: 'tool',
}).render(true);
},
renderTraitsSelector(actor: any, trait: string) {
return new dnd5e.applications.actor.TraitSelector(actor, trait).render(
true
);
renderTraitsConfig(document: any, trait: string) {
return new dnd5e.applications.actor.TraitsConfig({
document,
trait,
}).render(true);
},
renderWeaponsConfig(actor: any) {
return new dnd5e.applications.actor.WeaponsConfig({
document: actor,
trait: 'weapon',
}).render({ force: true });
},
renderProficiencyConfig(actor: any, property: string, key: string) {
return new dnd5e.applications.actor.ProficiencyConfig(actor, {
property,
renderSkillToolConfig(document: any, trait: 'skills' | 'tools', key: string) {
return new dnd5e.applications.actor.SkillToolConfig({
document,
trait,
key,
}).render(true);
},
renderItemTypeConfig(item: any) {
return new dnd5e.applications.actor.ActorTypeConfig(item, {
keyPath: 'system.type',
}).render(true);
},
renderItemMovementConfig(item: any) {
return new dnd5e.applications.actor.ActorMovementConfig(item, {
keyPath: 'system.movement',
}).render(true);
},
renderItemSensesConfig(item: any) {
return new dnd5e.applications.actor.ActorSensesConfig(item, {
keyPath: 'system.senses',
}).render(true);
},
renderSourceConfig(document: any, keyPath: string) {
return new dnd5e.applications.SourceConfig(
{ document: document },
Expand Down Expand Up @@ -1196,17 +1192,21 @@ export const FoundryAdapter = {
getJqueryWrappedElement(el: HTMLElement) {
return $(el);
},
openSpellSlotsConfig(actor: Actor5e) {
new dnd5e.applications.actor.ActorSpellSlotsConfig(actor).render(true);
openSpellSlotsConfig(document: any) {
new dnd5e.applications.actor.SpellSlotsConfig({ document }).render(true);
},
openSummonConfig(item: Item5e) {
new dnd5e.applications.item.SummoningConfig(item).render(true);
},
openDamageModificationConfig(actor: Actor5e) {
new dnd5e.applications.actor.DamageModificationConfig(actor).render(true);
openDamagesConfig(document: Actor5e, trait: 'dr' | 'di' | 'dv' | 'dm') {
new dnd5e.applications.actor.DamagesConfig({ document, trait }).render(
true
);
},
openActorConcentrationConfig(actor: Actor5e) {
new dnd5e.applications.actor.ActorConcentrationConfig(actor).render(true);
openConcentrationConfig(document: any) {
new dnd5e.applications.actor.ConcentrationConfig({
document: document,
}).render(true);
},
openStartingEquipmentConfig(item: Item5e) {
new dnd5e.applications.item.StartingEquipmentConfig(item).render(true);
Expand Down
1 change: 0 additions & 1 deletion src/foundry/foundry-and-system.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ declare global {
var Items: any;
var KeyboardManager: any;
var ModuleManagement: any;
var NumericTerm: any;
var renderTemplate: any;
var Roll: any;
var SortingHelpers: any;
Expand Down
9 changes: 6 additions & 3 deletions src/mixins/Tidy5eActorSheetBaseMixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ export function Tidy5eActorSheetBaseMixin(BaseApplication: any) {
options
);

return scroll?.toObject?.();
return scroll?.toObject?.() ?? false;
}

// Clean up data
Expand Down Expand Up @@ -402,12 +402,15 @@ export function Tidy5eActorSheetBaseMixin(BaseApplication: any) {
/**
* Stack identical consumables when a new one is dropped rather than creating a duplicate item.
*/
_onDropStackConsumables(itemData: any): Promise<Item5e> | null {
_onDropStackConsumables(
itemData: any,
{ container = null } = {}
): Promise<Item5e> | null {
const droppedSourceId =
itemData._stats?.compendiumSource ?? itemData.flags.core?.sourceId;
if (itemData.type !== 'consumable' || !droppedSourceId) return null;
const similarItem = this.actor.items.find((i: Item5e) => {
const sourceId = i.getFlag('core', 'sourceId');
const sourceId = i._stats?.compendiumSource;
return (
sourceId &&
sourceId === droppedSourceId &&
Expand Down
Loading

0 comments on commit 8dd3358

Please sign in to comment.