diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index ca7ed09e..c1ab2402 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -5,12 +5,18 @@ import { ModifiableDataBaseTotal } from "../common/common-data"; import { DS4 } from "../config"; -import { DS4Item } from "../item/item"; -import { ItemType } from "../item/item-data"; -import { DS4ArmorPreparedData, DS4ShieldPreparedData } from "../item/item-prepared-data"; +import { ItemType } from "../item/item-data-source"; +import { DS4ArmorDataProperties, DS4ShieldDataProperties } from "../item/item-data-properties"; import { createCheckRoll } from "../rolls/check-factory"; import { isAttribute, isTrait } from "./actor-data-source"; import { Check } from "./actor-data-properties"; +import { DS4Item } from "../item/item"; + +declare global { + interface DocumentClassConfig { + Actor: typeof DS4Actor; + } +} /** * The Actor class for DS4 @@ -84,9 +90,9 @@ export class DS4Actor extends Actor { (changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[], e) => { if (e.data.disabled) return changes; const item = this.getOriginatingItemOfActiveEffect(e); - if (item?.isNonEquippedEuipable()) return changes; // TODO: DS4Item + if (item?.isNonEquippedEuipable()) return changes; - const factor = item?.activeEffectFactor ?? 1; // TODO: DS4Item + const factor = item?.activeEffectFactor ?? 1; const newChanges = e.data.changes.filter(predicate).flatMap((c) => { const changeSource = c.toObject(); @@ -110,8 +116,7 @@ export class DS4Actor extends Actor { this.overrides = expandObject({ ...flattenObject(this.overrides), ...overrides }); } - // TODO: returns DS4Item | undefined - protected getOriginatingItemOfActiveEffect(effect: ActiveEffect): Item | undefined { + protected getOriginatingItemOfActiveEffect(effect: ActiveEffect): DS4Item | undefined { return this.items.find((item) => item.uuid === effect.data.origin); } @@ -272,7 +277,6 @@ export class DS4Actor extends Actor { * Handle how changes to a Token attribute bar are applied to the Actor. * This only differs from the base implementation by also allowing negative values. * @override - * TODO: Adjust return type */ async modifyTokenAttribute( attribute: string, diff --git a/src/module/actor/sheets/actor-sheet.ts b/src/module/actor/sheets/actor-sheet.ts index 6aad960a..6e734ad7 100644 --- a/src/module/actor/sheets/actor-sheet.ts +++ b/src/module/actor/sheets/actor-sheet.ts @@ -9,7 +9,7 @@ import { ModifiableDataBaseTotal } from "../../common/common-data"; import { DS4 } from "../../config"; import { getCanvas } from "../../helpers"; import { DS4Item } from "../../item/item"; -import { DS4ItemData } from "../../item/item-data"; +import { DS4ItemData } from "../../item/item-data-source"; import { getDS4Settings } from "../../settings"; import notifications from "../../ui/notifications"; import { DS4Actor } from "../actor"; @@ -18,13 +18,13 @@ import { isCheck } from "../actor-data-properties"; /** * The base Sheet class for all DS4 Actors */ -export class DS4ActorSheet extends ActorSheet> { - // TODO(types): Improve mergeObject in upstream so that it isn't necessary to provide all parameters (see https://github.com/League-of-Foundry-Developers/foundry-vtt-types/issues/272) +export class DS4ActorSheet extends ActorSheet { /** @override */ - static get defaultOptions(): BaseEntitySheet.Options { - const superDefaultOptions = super.defaultOptions; - return mergeObject(superDefaultOptions, { - ...superDefaultOptions, + static get defaultOptions(): ActorSheet.Options { + // TODO: Improve + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return foundry.utils.mergeObject(super.defaultOptions, { classes: ["ds4", "sheet", "actor"], height: 620, scrollY: [ @@ -105,7 +105,7 @@ export class DS4ActorSheet extends ActorSheet> { html.find(".item-edit").on("click", (ev) => { const li = $(ev.currentTarget).parents(".item"); const id = li.data("itemId"); - const item = this.actor.getOwnedItem(id); + const item = this.actor.getEmbeddedDocument("Item", id) as DS4Item; // TODO: Improve in upstream if (!item) { throw new Error(game.i18n.format("DS4.ErrorActorDoesNotHaveItem", { id, actor: this.actor.name })); } @@ -118,7 +118,7 @@ export class DS4ActorSheet extends ActorSheet> { // Delete Inventory Item html.find(".item-delete").on("click", (ev) => { const li = $(ev.currentTarget).parents(".item"); - this.actor.deleteOwnedItem(li.data("itemId")); + this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")]); li.slideUp(200, () => this.render(false)); }); @@ -133,23 +133,21 @@ export class DS4ActorSheet extends ActorSheet> { * Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset * @param event - The originating click event */ - protected _onItemCreate(event: JQuery.ClickEvent): Promise { + protected _onItemCreate(event: JQuery.ClickEvent): void { event.preventDefault(); const header = event.currentTarget; - // Get the type of item to create. - // Grab any data associated with this control. + const { type, ...data } = duplicate(header.dataset); - // Initialize a default name. + const name = `New ${type.capitalize()}`; - // Prepare the item object. + const itemData = { name: name, type: type, data: data, }; - // Finally, create the item! - return this.actor.createOwnedItem(itemData); + DS4Item.create(itemData, { parent: this.actor }); } /** @@ -162,7 +160,8 @@ export class DS4ActorSheet extends ActorSheet> { ev.preventDefault(); const el: HTMLFormElement = $(ev.currentTarget).get(0); const id = $(ev.currentTarget).parents(".item").data("itemId"); - const item = duplicate(this.actor.getOwnedItem(id)); + const item = this.actor.getEmbeddedDocument("Item", id) as DS4Item; // TODO: Improve in upstream + const itemObject = item.toObject(); const property: string | undefined = $(ev.currentTarget).data("property"); // Early return: @@ -175,8 +174,8 @@ export class DS4ActorSheet extends ActorSheet> { // Set new value const newValue = this.getValue(el); - setProperty(item, property, newValue); - this.actor.updateOwnedItem(item); + setProperty(itemObject, property, newValue); + this.actor.updateEmbeddedDocuments("Item", [{ ...itemObject }]); // TODO: Improve in upstream } /** @@ -241,7 +240,7 @@ export class DS4ActorSheet extends ActorSheet> { protected _onRollItem(event: JQuery.ClickEvent): void { event.preventDefault(); const id = $(event.currentTarget).parents(".item").data("itemId"); - const item = this.actor.getOwnedItem(id); + const item = this.actor.getEmbeddedDocument("Item", id, { strict: true }) as DS4Item; // TODO: improve in upstream types item.roll().catch((e) => notifications.error(e, { log: true })); } @@ -277,14 +276,7 @@ export class DS4ActorSheet extends ActorSheet> { } /** @override */ - protected async _onDropItem( - event: DragEvent, - data: { type: "Item" } & ( - | { data: DeepPartial> } - | { pack: string } - | { id: string } - ), - ): Promise> { + protected async _onDropItem(event: DragEvent, data: ActorSheet.DropData.Item): Promise { const item = await DS4Item.fromDropData(data); if (item && !this.actor.canOwnItemType(item.data.type)) { notifications.warn( diff --git a/src/module/common/common-data.ts b/src/module/common/common-data.ts index 31cd6c5d..fee14ddf 100644 --- a/src/module/common/common-data.ts +++ b/src/module/common/common-data.ts @@ -27,6 +27,10 @@ export interface HasMax { max: T; } +export interface ModifiableDataBaseMax extends ModifiableDataBase, HasMax {} + +export interface ModifiableDataBaseTotalMax extends ModifiableDataBaseMax, HasTotal {} + export interface ResourceDataBaseTotalMax extends ResourceData, HasBase, HasTotal, HasMax {} export interface UsableResource { diff --git a/src/module/hooks/hotbar-drop.ts b/src/module/hooks/hotbar-drop.ts index 53997a74..cc4d0028 100644 --- a/src/module/hooks/hotbar-drop.ts +++ b/src/module/hooks/hotbar-drop.ts @@ -4,19 +4,18 @@ import { isCheck } from "../actor/actor-data-properties"; import { DS4Item } from "../item/item"; -import { DS4ItemData } from "../item/item-data"; import { createRollCheckMacro } from "../macros/roll-check"; import { createRollItemMacro } from "../macros/roll-item"; import notifications from "../ui/notifications"; export default function registerForHotbarDropHook(): void { - Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: { type: string } & Record, slot: string) => { + Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: HotbarDropData, slot: string) => { switch (data.type) { case "Item": { - if (!("data" in data)) { + if (!isItemDropData(data) || !("data" in data)) { return notifications.warn(game.i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems")); } - const itemData = data.data as DS4ItemData; + const itemData = data.data; if (!DS4Item.rollableItemTypes.includes(itemData.type)) { return notifications.warn( @@ -38,3 +37,9 @@ export default function registerForHotbarDropHook(): void { } }); } + +type HotbarDropData = ActorSheet.DropData.Item | ({ type: string } & Partial>); + +function isItemDropData(dropData: HotbarDropData): dropData is ActorSheet.DropData.Item { + return dropData.type === "Item"; +} diff --git a/src/module/hooks/init.ts b/src/module/hooks/init.ts index 76b78eb5..0525fd2b 100644 --- a/src/module/hooks/init.ts +++ b/src/module/hooks/init.ts @@ -39,8 +39,8 @@ async function init() { CONFIG.DS4 = DS4; - // CONFIG.Actor.documentClass = DS4Actor; - // CONFIG.Item.documentClass = DS4Item; + CONFIG.Actor.documentClass = DS4Actor; + CONFIG.Item.documentClass = DS4Item; CONFIG.Actor.typeLabels = DS4.i18n.actorTypes; CONFIG.Item.typeLabels = DS4.i18n.itemTypes; @@ -54,10 +54,10 @@ async function init() { registerSystemSettings(); - // Actors.unregisterSheet("core", ActorSheet); + Actors.unregisterSheet("core", ActorSheet); // Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true }); // Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true }); - // Items.unregisterSheet("core", ItemSheet); + Items.unregisterSheet("core", ItemSheet); // Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true }); await registerHandlebarsPartials(); diff --git a/src/module/item/item-data-properties.ts b/src/module/item/item-data-properties.ts new file mode 100644 index 00000000..3dcda46f --- /dev/null +++ b/src/module/item/item-data-properties.ts @@ -0,0 +1,130 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { ModifiableDataBaseTotalMax } from "../common/common-data"; +import { + DS4AlphabetDataSourceData, + DS4ArmorDataSourceData, + DS4EquipmentDataSourceData, + DS4LanguageDataSourceData, + DS4LootDataSourceData, + DS4RacialAbilityDataSourceData, + DS4ShieldDataSourceData, + DS4SpecialCreatureAbilityDataSourceData, + DS4SpellDataSourceData, + DS4TalentDataSourceData, + DS4WeaponDataSourceData, +} from "./item-data-source"; + +declare global { + interface DataConfig { + Item: DS4ItemDataProperties; + } +} + +export type DS4ItemDataProperties = + | DS4WeaponDataProperties + | DS4ArmorDataProperties + | DS4ShieldDataProperties + | DS4SpellDataProperties + | DS4EquipmentDataProperties + | DS4LootDataProperties + | DS4TalentDataProperties + | DS4RacialAbilityDataProperties + | DS4LanguageDataProperties + | DS4AlphabetDataProperties + | DS4SpecialCreatureAbilityDataProperties; + +export interface DS4WeaponDataProperties { + type: "weapon"; + data: DS4WeaponDataPropertiesData; +} + +export interface DS4ArmorDataProperties { + type: "armor"; + data: DS4ArmorDataPropertiesData; +} + +export interface DS4ShieldDataProperties { + type: "shield"; + data: DS4ShieldDataPropertiesData; +} + +export interface DS4SpellDataProperties { + type: "spell"; + data: DS4SpellDataPropertiesData; +} + +export interface DS4EquipmentDataProperties { + type: "equipment"; + data: DS4EquipmentDataPropertiesData; +} + +export interface DS4LootDataProperties { + type: "loot"; + data: DS4LootDataPropertiesData; +} + +export interface DS4TalentDataProperties { + type: "talent"; + data: DS4TalentDataPropertiesData; +} + +export interface DS4RacialAbilityDataProperties { + type: "racialAbility"; + data: DS4RacialAbilityDataPropertiesData; +} + +export interface DS4LanguageDataProperties { + type: "language"; + data: DS4LanguageDataPropertiesData; +} + +export interface DS4AlphabetDataProperties { + type: "alphabet"; + data: DS4AlphabetDataPropertiesData; +} + +export interface DS4SpecialCreatureAbilityDataProperties { + type: "specialCreatureAbility"; + data: DS4SpecialCreatureAbilityDataPropertiesData; +} + +// templates + +interface DS4ItemDataPropertiesDataRollable { + rollable: boolean; +} + +//types + +interface DS4WeaponDataPropertiesData extends DS4WeaponDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4ArmorDataPropertiesData extends DS4ArmorDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4ShieldDataPropertiesData extends DS4ShieldDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4SpellDataPropertiesData extends DS4SpellDataSourceData, DS4ItemDataPropertiesDataRollable { + price: number | null; +} + +interface DS4EquipmentDataPropertiesData extends DS4EquipmentDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4LootDataPropertiesData extends DS4LootDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4TalentDataPropertiesData extends DS4TalentDataSourceData, DS4ItemDataPropertiesDataRollable { + rank: ModifiableDataBaseTotalMax; +} + +interface DS4RacialAbilityDataPropertiesData + extends DS4RacialAbilityDataSourceData, + DS4ItemDataPropertiesDataRollable {} + +interface DS4LanguageDataPropertiesData extends DS4LanguageDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4AlphabetDataPropertiesData extends DS4AlphabetDataSourceData, DS4ItemDataPropertiesDataRollable {} + +interface DS4SpecialCreatureAbilityDataPropertiesData + extends DS4SpecialCreatureAbilityDataSourceData, + DS4ItemDataPropertiesDataRollable {} diff --git a/src/module/item/item-data-source.ts b/src/module/item/item-data-source.ts new file mode 100644 index 00000000..dc7f4c4c --- /dev/null +++ b/src/module/item/item-data-source.ts @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// SPDX-FileCopyrightText: 2021 Oliver Rümpelein +// SPDX-FileCopyrightText: 2021 Gesina Schwalbe +// +// SPDX-License-Identifier: MIT + +import { ModifiableDataBaseMax } from "../common/common-data"; +import { DS4 } from "../config"; + +declare global { + interface SourceConfig { + Item: DS4ItemDataSource; + } +} + +export type ItemType = keyof typeof DS4.i18n.itemTypes; + +export type DS4ItemDataSource = + | DS4WeaponDataSource + | DS4ArmorDataSource + | DS4ShieldDataSource + | DS4SpellDataSource + | DS4EquipmentDataSource + | DS4LootDataSource + | DS4TalentDataSource + | DS4RacialAbilityDataSource + | DS4LanguageDataSource + | DS4AlphabetDataSource + | DS4SpecialCreatureAbilityDataSource; + +interface DS4WeaponDataSource { + type: "weapon"; + data: DS4WeaponDataSourceData; +} + +interface DS4ArmorDataSource { + type: "armor"; + data: DS4ArmorDataSourceData; +} + +interface DS4ShieldDataSource { + type: "shield"; + data: DS4ShieldDataSourceData; +} + +interface DS4SpellDataSource { + type: "spell"; + data: DS4SpellDataSourceData; +} + +interface DS4EquipmentDataSource { + type: "equipment"; + data: DS4EquipmentDataSourceData; +} + +interface DS4LootDataSource { + type: "loot"; + data: DS4LootDataSourceData; +} + +interface DS4TalentDataSource { + type: "talent"; + data: DS4TalentDataSourceData; +} + +interface DS4RacialAbilityDataSource { + type: "racialAbility"; + data: DS4RacialAbilityDataSourceData; +} + +interface DS4LanguageDataSource { + type: "language"; + data: DS4LanguageDataSourceData; +} + +interface DS4AlphabetDataSource { + type: "alphabet"; + data: DS4AlphabetDataSourceData; +} + +interface DS4SpecialCreatureAbilityDataSource { + type: "specialCreatureAbility"; + data: DS4SpecialCreatureAbilityDataSourceData; +} + +// templates + +interface DS4ItemDataSourceDataBase { + description: string; +} + +interface DS4ItemDataSourceDataPhysical { + quantity: number; + price: number; + availability: keyof typeof DS4.i18n.itemAvailabilities; + storageLocation: string; +} + +export function isDS4ItemDataTypePhysical(input: foundry.data.ItemData["data"]): boolean { + return "quantity" in input && "price" in input && "availability" in input && "storageLocation" in input; +} + +interface DS4ItemDataSourceDataEquipable { + equipped: boolean; +} + +interface DS4ItemDataSourceDataProtective { + armorValue: number; +} + +// types + +export interface DS4WeaponDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable { + attackType: AttackType; + weaponBonus: number; + opponentDefense: number; +} + +export type AttackType = keyof typeof DS4.i18n.attackTypes; + +export interface DS4ArmorDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataProtective { + armorMaterialType: keyof typeof DS4.i18n.armorMaterialTypes; + armorType: keyof typeof DS4.i18n.armorTypes; +} + +export interface DS4ShieldDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataProtective {} + +export interface DS4SpellDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable { + spellType: keyof typeof DS4.i18n.spellTypes; + bonus: string; + spellCategory: keyof typeof DS4.i18n.spellCategories; + maxDistance: UnitData; + effectRadius: UnitData; + duration: UnitData; + cooldownDuration: UnitData; + minimumLevels: { + healer: number | null; + wizard: number | null; + sorcerer: number | null; + }; +} + +export interface UnitData { + value: string; + unit: UnitType; +} + +type DistanceUnit = keyof typeof DS4.i18n.distanceUnits; + +type CustomTemporalUnit = keyof typeof DS4.i18n.customTemporalUnits; + +export type TemporalUnit = keyof typeof DS4.i18n.temporalUnits; + +export interface DS4EquipmentDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable {} + +export interface DS4LootDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataPhysical {} + +export interface DS4TalentDataSourceData extends DS4ItemDataSourceDataBase { + rank: ModifiableDataBaseMax; +} + +export type DS4RacialAbilityDataSourceData = DS4ItemDataSourceDataBase; + +export type DS4LanguageDataSourceData = DS4ItemDataSourceDataBase; + +export type DS4AlphabetDataSourceData = DS4ItemDataSourceDataBase; + +export interface DS4SpecialCreatureAbilityDataSourceData extends DS4ItemDataSourceDataBase { + experiencePoints: number; +} diff --git a/src/module/item/item-data.ts b/src/module/item/item-data.ts deleted file mode 100644 index 9a0f44a1..00000000 --- a/src/module/item/item-data.ts +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// SPDX-FileCopyrightText: 2021 Oliver Rümpelein -// SPDX-FileCopyrightText: 2021 Gesina Schwalbe -// -// SPDX-License-Identifier: MIT - -import { ModifiableDataBase } from "../common/common-data"; -import { DS4 } from "../config"; - -export type ItemType = keyof typeof DS4.i18n.itemTypes; - -export type DS4ItemData = - | DS4WeaponData - | DS4ArmorData - | DS4ShieldData - | DS4SpellData - | DS4EquipmentData - | DS4LootData - | DS4TalentData - | DS4RacialAbilityData - | DS4LanguageData - | DS4AlphabetData - | DS4SpecialCreatureAbilityData; - -export interface DS4ItemDataHelper extends Item.Data { - type: U; -} - -type DS4WeaponData = DS4ItemDataHelper; -type DS4ArmorData = DS4ItemDataHelper; -type DS4ShieldData = DS4ItemDataHelper; -type DS4SpellData = DS4ItemDataHelper; -type DS4EquipmentData = DS4ItemDataHelper; -type DS4LootData = DS4ItemDataHelper; -type DS4TalentData = DS4ItemDataHelper; -type DS4RacialAbilityData = DS4ItemDataHelper; -type DS4LanguageData = DS4ItemDataHelper; -type DS4AlphabetData = DS4ItemDataHelper; -type DS4SpecialCreatureAbilityData = DS4ItemDataHelper; - -// templates - -interface DS4ItemDataDataBase { - description: string; -} -interface DS4ItemDataDataPhysical { - quantity: number; - price: number; - availability: keyof typeof DS4.i18n.itemAvailabilities; - storageLocation: string; -} - -export function isDS4ItemDataTypePhysical(input: DS4ItemData["data"]): boolean { - return "quantity" in input && "price" in input && "availability" in input && "storageLocation" in input; -} - -interface DS4ItemDataDataEquipable { - equipped: boolean; -} - -interface DS4ItemDataDataProtective { - armorValue: number; -} - -export interface UnitData { - value: string; - unit: UnitType; -} -export type TemporalUnit = keyof typeof DS4.i18n.temporalUnits; -type CustomTemporalUnit = keyof typeof DS4.i18n.customTemporalUnits; -type DistanceUnit = keyof typeof DS4.i18n.distanceUnits; - -// types - -export interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable { - attackType: AttackType; - weaponBonus: number; - opponentDefense: number; -} - -export type AttackType = keyof typeof DS4.i18n.attackTypes; - -export interface DS4ArmorDataData - extends DS4ItemDataDataBase, - DS4ItemDataDataPhysical, - DS4ItemDataDataEquipable, - DS4ItemDataDataProtective { - armorMaterialType: keyof typeof DS4.i18n.armorMaterialTypes; - armorType: keyof typeof DS4.i18n.armorTypes; -} - -export interface DS4TalentDataData extends DS4ItemDataDataBase { - rank: DS4TalentRank; -} - -export interface DS4TalentRank extends ModifiableDataBase { - max: number; -} - -export interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable { - spellType: keyof typeof DS4.i18n.spellTypes; - bonus: string; - spellCategory: keyof typeof DS4.i18n.spellCategories; - maxDistance: UnitData; - effectRadius: UnitData; - duration: UnitData; - cooldownDuration: UnitData; - minimumLevels: { - healer: number | null; - wizard: number | null; - sorcerer: number | null; - }; -} - -export interface DS4ShieldDataData - extends DS4ItemDataDataBase, - DS4ItemDataDataPhysical, - DS4ItemDataDataEquipable, - DS4ItemDataDataProtective {} - -export interface DS4EquipmentDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {} - -export interface DS4LootDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical {} - -export type DS4RacialAbilityDataData = DS4ItemDataDataBase; - -export type DS4LanguageDataData = DS4ItemDataDataBase; - -export type DS4AlphabetDataData = DS4ItemDataDataBase; - -export interface DS4SpecialCreatureAbilityDataData extends DS4ItemDataDataBase { - experiencePoints: number; -} diff --git a/src/module/item/item-prepared-data.ts b/src/module/item/item-prepared-data.ts deleted file mode 100644 index 180a9909..00000000 --- a/src/module/item/item-prepared-data.ts +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// -// SPDX-License-Identifier: MIT - -import { HasTotal } from "../common/common-data"; -import { - DS4AlphabetDataData, - DS4ArmorDataData, - DS4EquipmentDataData, - DS4ItemDataHelper, - DS4LanguageDataData, - DS4LootDataData, - DS4RacialAbilityDataData, - DS4ShieldDataData, - DS4SpecialCreatureAbilityDataData, - DS4SpellDataData, - DS4TalentDataData, - DS4TalentRank, - DS4WeaponDataData, -} from "./item-data"; - -export type DS4ItemPreparedData = - | DS4WeaponPreparedData - | DS4ArmorPreparedData - | DS4ShieldPreparedData - | DS4SpellPreparedData - | DS4EquipmentPreparedData - | DS4LootPreparedData - | DS4TalentPreparedData - | DS4RacialAbilityPreparedData - | DS4LanguagePreparedData - | DS4AlphabetPreparedData - | DS4SpecialCreatureAbilityPreparedData; - -export type DS4WeaponPreparedData = DS4ItemDataHelper; -export type DS4ArmorPreparedData = DS4ItemDataHelper; -export type DS4ShieldPreparedData = DS4ItemDataHelper; -export type DS4SpellPreparedData = DS4ItemDataHelper; -export type DS4EquipmentPreparedData = DS4ItemDataHelper; -export type DS4LootPreparedData = DS4ItemDataHelper; -export type DS4TalentPreparedData = DS4ItemDataHelper; -export type DS4RacialAbilityPreparedData = DS4ItemDataHelper; -export type DS4LanguagePreparedData = DS4ItemDataHelper; -export type DS4AlphabetPreparedData = DS4ItemDataHelper; -export type DS4SpecialCreatureAbilityPreparedData = DS4ItemDataHelper< - DS4SpecialCreatureAbilityPreparedDataData, - "specialCreatureAbility" ->; - -// templates - -interface DS4ItemPreparedDataDataRollable { - rollable: boolean; -} - -//types - -interface DS4WeaponPreparedDataData extends DS4WeaponDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4ArmorPreparedDataData extends DS4ArmorDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4ShieldPreparedDataData extends DS4ShieldDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4SpellPreparedDataData extends DS4SpellDataData, DS4ItemPreparedDataDataRollable { - price: number | null; -} - -interface DS4EquipmentPreparedDataData extends DS4EquipmentDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4LootPreparedDataData extends DS4LootDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4TalentPreparedDataData extends DS4TalentDataData, DS4ItemPreparedDataDataRollable { - rank: DS4TalentPreparedRank; -} - -interface DS4TalentPreparedRank extends DS4TalentRank, HasTotal {} - -interface DS4RacialAbilityPreparedDataData extends DS4RacialAbilityDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4LanguagePreparedDataData extends DS4LanguageDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4AlphabetPreparedDataData extends DS4AlphabetDataData, DS4ItemPreparedDataDataRollable {} - -interface DS4SpecialCreatureAbilityPreparedDataData - extends DS4SpecialCreatureAbilityDataData, - DS4ItemPreparedDataDataRollable {} diff --git a/src/module/item/item-sheet.ts b/src/module/item/item-sheet.ts index 4b2c9273..7d496423 100644 --- a/src/module/item/item-sheet.ts +++ b/src/module/item/item-sheet.ts @@ -7,17 +7,16 @@ import { DS4 } from "../config"; import notifications from "../ui/notifications"; import { DS4Item } from "./item"; -import { isDS4ItemDataTypePhysical } from "./item-data"; +import { isDS4ItemDataTypePhysical } from "./item-data-source"; /** * The Sheet class for DS4 Items */ -export class DS4ItemSheet extends ItemSheet> { +export class DS4ItemSheet extends ItemSheet { /** @override */ - static get defaultOptions(): BaseEntitySheet.Options { + static get defaultOptions(): ItemSheet.Options { const superDefaultOptions = super.defaultOptions; return mergeObject(superDefaultOptions, { - ...superDefaultOptions, width: 540, height: 400, classes: ["ds4", "sheet", "item"], @@ -45,11 +44,14 @@ export class DS4ItemSheet extends ItemSheet> { } /** @override */ - setPosition(options: Partial = {}): Application.Position & { height: number } { + setPosition(options: Partial = {}): (Application.Position & { height: number }) | undefined { const position = super.setPosition(options); - const sheetBody = this.element.find(".sheet-body"); - const bodyHeight = position.height - 192; - sheetBody.css("height", bodyHeight); + if (position) { + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + } + return position; } @@ -86,7 +88,7 @@ export class DS4ItemSheet extends ItemSheet> { } return effect.sheet.render(true); case "delete": { - return this.item.deleteEmbeddedEntity("ActiveEffect", li.data("effectId")); + return this.item.deleteEmbeddedDocuments("ActiveEffect", [li.data("effectId")]); } } } @@ -94,7 +96,7 @@ export class DS4ItemSheet extends ItemSheet> { /** * Create a new ActiveEffect for the item using default data. */ - protected async _createActiveEffect(): Promise { + protected async _createActiveEffect(): Promise { const label = `New Effect`; const createData = { @@ -104,7 +106,6 @@ export class DS4ItemSheet extends ItemSheet> { transfer: true, }; - const effect = ActiveEffect.create(createData, this.item); - return effect.create({}); + return ActiveEffect.create(createData, { parent: this.item }); } } diff --git a/src/module/item/item.ts b/src/module/item/item.ts index da8ec63b..928bd89f 100644 --- a/src/module/item/item.ts +++ b/src/module/item/item.ts @@ -7,14 +7,19 @@ import { DS4Actor } from "../actor/actor"; import { DS4 } from "../config"; import { createCheckRoll } from "../rolls/check-factory"; import notifications from "../ui/notifications"; -import { AttackType, DS4ItemData, ItemType } from "./item-data"; -import { DS4ItemPreparedData } from "./item-prepared-data"; +import { AttackType, ItemType } from "./item-data-source"; import { calculateSpellPrice } from "./type-specific-helpers/spell"; +declare global { + interface DocumentClassConfig { + Item: typeof DS4Item; + } +} + /** * The Item class for DS4 */ -export class DS4Item extends Item { +export class DS4Item extends Item { /** * @override */ @@ -77,7 +82,7 @@ export class DS4Item extends Item { } } - protected async rollWeapon(this: this & { readonly isOwned: true }): Promise { + protected async rollWeapon(this: this & { readonly actor: DS4Actor }): Promise { if (!(this.data.type === "weapon")) { throw new Error( game.i18n.format("DS4.ErrorWrongItemType", { @@ -99,21 +104,21 @@ export class DS4Item extends Item { ); } - const actor = this.actor as unknown as DS4Actor; // TODO(types): Improve so that the concrete Actor type is known here + const actor = this.actor; const ownerDataData = actor.data.data; const weaponBonus = this.data.data.weaponBonus; const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType); const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus; await createCheckRoll(checkTargetNumber, { - rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream + rollMode: game.settings.get("core", "rollMode"), maximumCoupResult: ownerDataData.rolling.maximumCoupResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, flavor: game.i18n.format("DS4.ItemWeaponCheckFlavor", { actor: actor.name, weapon: this.name }), }); } - protected async rollSpell(): Promise { + protected async rollSpell(this: this & { readonly actor: DS4Actor }): Promise { if (!(this.data.type === "spell")) { throw new Error( game.i18n.format("DS4.ErrorWrongItemType", { @@ -135,7 +140,7 @@ export class DS4Item extends Item { ); } - const actor = this.actor as unknown as DS4Actor; // TODO(types): Improve so that the concrete Actor type is known here + const actor = this.actor; const ownerDataData = actor.data.data; const spellBonus = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined; if (spellBonus === undefined) { @@ -150,7 +155,7 @@ export class DS4Item extends Item { const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellBonus ?? 0); await createCheckRoll(checkTargetNumber, { - rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream + rollMode: game.settings.get("core", "rollMode"), maximumCoupResult: ownerDataData.rolling.maximumCoupResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, flavor: game.i18n.format("DS4.ItemSpellCheckFlavor", { actor: actor.name, spell: this.name }), @@ -194,7 +199,7 @@ export class DS4Item extends Item { /** * Type-guarding variant to check if the item is owned. */ - isOwnedItem(): this is this & { readonly isOwned: true } { + isOwnedItem(): this is this & { readonly isOwned: true; readonly actor: DS4Actor; readonly parent: DS4Actor } { return this.isOwned; } } diff --git a/src/module/item/type-specific-helpers/spell.ts b/src/module/item/type-specific-helpers/spell.ts index 3fd17974..e8820b43 100644 --- a/src/module/item/type-specific-helpers/spell.ts +++ b/src/module/item/type-specific-helpers/spell.ts @@ -3,9 +3,9 @@ // SPDX-License-Identifier: MIT import { hoursPerDay, minutesPerHour, secondsPerMinute, secondsPerRound } from "../../common/time-helpers"; -import { DS4SpellDataData, TemporalUnit, UnitData } from "../item-data"; +import { DS4SpellDataSourceData, TemporalUnit, UnitData } from "../item-data-source"; -export function calculateSpellPrice(data: DS4SpellDataData): number | null { +export function calculateSpellPrice(data: DS4SpellDataSourceData): number | null { const spellPriceFactor = calculateSpellPriceFactor(data.cooldownDuration); const baseSpellPrices = [ data.minimumLevels.healer !== null ? 10 + (data.minimumLevels.healer - 1) * 35 : null, diff --git a/src/module/macros/roll-item.ts b/src/module/macros/roll-item.ts index f5c25361..8392d454 100644 --- a/src/module/macros/roll-item.ts +++ b/src/module/macros/roll-item.ts @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -import { DS4ItemData } from "../item/item-data"; +import { DS4ItemData } from "../item/item-data-source"; import notifications from "../ui/notifications"; import { getActiveActor } from "./helpers"; diff --git a/src/module/migrations/004.ts b/src/module/migrations/004.ts index 555fa160..40072535 100644 --- a/src/module/migrations/004.ts +++ b/src/module/migrations/004.ts @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -import { DS4SpellDataData } from "../item/item-data"; +import { DS4SpellDataSourceData } from "../item/item-data-source"; import logger from "../logger"; export async function migrate(): Promise { @@ -33,7 +33,7 @@ function getItemUpdateData(itemData: DeepPartial) { "-=data.scrollPrice": null, "data.minimumLevels": { healer: null, wizard: null, sorcerer: null }, }; - if (((itemData.data as DS4SpellDataData).cooldownDuration.unit as string) === "custom") { + if (((itemData.data as DS4SpellDataSourceData).cooldownDuration.unit as string) === "custom") { updateData["data.cooldownDuration.unit"] = "rounds"; } return updateData; diff --git a/src/template.json b/src/template.json index 935c1d8a..b7378316 100644 --- a/src/template.json +++ b/src/template.json @@ -170,29 +170,6 @@ "shield": { "templates": ["base", "physical", "equipable", "protective"] }, - "equipment": { - "templates": ["base", "physical", "equipable"] - }, - "loot": { - "templates": ["base", "physical"] - }, - "talent": { - "templates": ["base"], - "rank": { - "base": 0, - "max": 0, - "mod": 0 - } - }, - "racialAbility": { - "templates": ["base"] - }, - "language": { - "templates": ["base"] - }, - "alphabet": { - "templates": ["base"] - }, "spell": { "templates": ["base", "equipable"], "spellType": "spellcasting", @@ -220,6 +197,29 @@ "sorcerer": null } }, + "equipment": { + "templates": ["base", "physical", "equipable"] + }, + "loot": { + "templates": ["base", "physical"] + }, + "talent": { + "templates": ["base"], + "rank": { + "base": 0, + "max": 0, + "mod": 0 + } + }, + "racialAbility": { + "templates": ["base"] + }, + "language": { + "templates": ["base"] + }, + "alphabet": { + "templates": ["base"] + }, "specialCreatureAbility": { "templates": ["base"], "experiencePoints": 0 diff --git a/yarn.lock b/yarn.lock index 00e40566..08498fc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -841,7 +841,7 @@ __metadata: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#foundry-0.8.x": version: 0.7.9-6 - resolution: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#commit=62c138c4ff2f6c3b19301db2b31e14e7c825f1e6" + resolution: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#commit=f242ac76237f2099f946b10d642f0b3272ead043" dependencies: "@types/jquery": ~3.5.5 "@types/simple-peer": ~9.11.0 @@ -851,7 +851,7 @@ __metadata: socket.io-client: 4.1.2 tinymce: 5.8.1 typescript: ^4.1.6 - checksum: af1f4d3cfae69a5a0fab2ca3a301c9f755d7f5584bf1eaee37031b19fe805581b7588e6a2452553f874a68cb5475a5a8fce287ed8436cf0af24fa0527ea5c014 + checksum: ae81444ddf4b36bff67a2483cc559ff64dd1fdaaada277439e2ed14a8ec8233f06723b9421be70033b38383d9c275de18b216307bb14b17feafc5866156b0ac3 languageName: node linkType: hard @@ -1505,9 +1505,9 @@ __metadata: linkType: hard "@types/sizzle@npm:*": - version: 2.3.2 - resolution: "@types/sizzle@npm:2.3.2" - checksum: 447a1c3f39f0e47ffdbccd1df58d63e8b67dc001f44f26f43ac8243db7834a3d956cebc8abe9272ecbdccfc8f4ec0ae74b811ccdad5b6cddaf8f0968513d618a + version: 2.3.3 + resolution: "@types/sizzle@npm:2.3.3" + checksum: 8f019f9e1b110b4fdfc08f8a3a8b8b87118a11f3ba11e159541b17f17498c0ef95e8efa0b818c9fed6911041f6b71ef8d44cf8c1c83b4cbb7bd14e4248892f4c languageName: node linkType: hard @@ -8516,11 +8516,11 @@ fsevents@^1.2.7: linkType: hard "uglify-js@npm:^3.1.4": - version: 3.13.0 - resolution: "uglify-js@npm:3.13.0" + version: 3.13.10 + resolution: "uglify-js@npm:3.13.10" bin: uglifyjs: bin/uglifyjs - checksum: bb35cfe5ce9735a9707b33628dbbef02ab4b62bdd3650a02e14dfe42f4ac3fe5e9d616352476605706ab651561abc66ef2877c3dcabf4bde5bf66cecec94f3e8 + checksum: 2c8467faf68a0ba4da7a9539026dc996804f0e89f184ce0a6ceaa9a9c7e4e2ab78399caee8ebbebcd3df64a45b049585c4125e144f1c5992f9b61e81864d9535 languageName: node linkType: hard