From 8d2cff77d7f39523b5d8ebbb2751dd3a77294baf Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Thu, 17 Feb 2022 00:17:59 +0100 Subject: [PATCH] refactor: use subclasses for different item types --- lang/de.json | 2 +- lang/en.json | 2 +- .../calculate-spell-price.spec.ts} | 4 +- src/actor/actor.ts | 3 +- src/hooks/init.ts | 5 +- src/item/alphabet/alphabet-data-properties.ts | 13 ++ src/item/alphabet/alphabet-data-source.ts | 12 ++ src/item/alphabet/alphabet.ts | 11 + src/item/armor/armor-data-properties.ts | 13 ++ src/item/armor/armor-data-source.ts | 25 +++ src/item/armor/armor.ts | 11 + .../equipment/equipment-data-properties.ts | 13 ++ src/item/equipment/equipment-data-source.ts | 19 ++ src/item/equipment/equipment.ts | 11 + src/item/item-data-properties-base.ts | 7 + src/item/item-data-properties.ts | 140 ++----------- src/item/item-data-source-base.ts | 30 +++ src/item/item-data-source.ts | 191 ++---------------- src/item/item-sheet.ts | 2 +- src/item/item.ts | 160 +-------------- src/item/language/language-data-properties.ts | 13 ++ src/item/language/language-data-source.ts | 12 ++ src/item/language/language.ts | 11 + src/item/loot/loot-data-properties.ts | 13 ++ src/item/loot/loot-data-source.ts | 12 ++ src/item/loot/loot.ts | 11 + src/item/proxy.ts | 50 +++++ .../racial-ability-data-properties.ts | 15 ++ .../racial-ability-data-source.ts | 12 ++ src/item/racial-ability/racial-ability.ts | 11 + src/item/shield/shield-data-properties.ts | 13 ++ src/item/shield/shield-data-source.ts | 21 ++ src/item/shield/shield.ts | 11 + ...pecial-creature-ability-data-properties.ts | 15 ++ .../special-creature-ability-data-source.ts | 14 ++ .../special-creature-ability.ts | 11 + .../calculate-spell-price.ts} | 2 +- src/item/spell/spell-data-properties.ts | 15 ++ src/item/spell/spell-data-source.ts | 35 ++++ src/item/spell/spell.ts | 63 ++++++ src/item/talent/talent-data-properties.ts | 16 ++ src/item/talent/talent-data-source.ts | 15 ++ src/item/talent/talent.ts | 23 +++ src/item/weapon/weapon-data-properties.ts | 13 ++ src/item/weapon/weapon-data-source.ts | 26 +++ src/item/weapon/weapon.ts | 88 ++++++++ src/migrations/005.ts | 7 +- 47 files changed, 766 insertions(+), 456 deletions(-) rename spec/item/{type-specific-helpers/spell.spec.ts => spell/calculate-spell-price.spec.ts} (98%) create mode 100644 src/item/alphabet/alphabet-data-properties.ts create mode 100644 src/item/alphabet/alphabet-data-source.ts create mode 100644 src/item/alphabet/alphabet.ts create mode 100644 src/item/armor/armor-data-properties.ts create mode 100644 src/item/armor/armor-data-source.ts create mode 100644 src/item/armor/armor.ts create mode 100644 src/item/equipment/equipment-data-properties.ts create mode 100644 src/item/equipment/equipment-data-source.ts create mode 100644 src/item/equipment/equipment.ts create mode 100644 src/item/item-data-properties-base.ts create mode 100644 src/item/item-data-source-base.ts create mode 100644 src/item/language/language-data-properties.ts create mode 100644 src/item/language/language-data-source.ts create mode 100644 src/item/language/language.ts create mode 100644 src/item/loot/loot-data-properties.ts create mode 100644 src/item/loot/loot-data-source.ts create mode 100644 src/item/loot/loot.ts create mode 100644 src/item/proxy.ts create mode 100644 src/item/racial-ability/racial-ability-data-properties.ts create mode 100644 src/item/racial-ability/racial-ability-data-source.ts create mode 100644 src/item/racial-ability/racial-ability.ts create mode 100644 src/item/shield/shield-data-properties.ts create mode 100644 src/item/shield/shield-data-source.ts create mode 100644 src/item/shield/shield.ts create mode 100644 src/item/special-creature-ability/special-creature-ability-data-properties.ts create mode 100644 src/item/special-creature-ability/special-creature-ability-data-source.ts create mode 100644 src/item/special-creature-ability/special-creature-ability.ts rename src/item/{type-specific-helpers/spell.ts => spell/calculate-spell-price.ts} (93%) create mode 100644 src/item/spell/spell-data-properties.ts create mode 100644 src/item/spell/spell-data-source.ts create mode 100644 src/item/spell/spell.ts create mode 100644 src/item/talent/talent-data-properties.ts create mode 100644 src/item/talent/talent-data-source.ts create mode 100644 src/item/talent/talent.ts create mode 100644 src/item/weapon/weapon-data-properties.ts create mode 100644 src/item/weapon/weapon-data-source.ts create mode 100644 src/item/weapon/weapon.ts diff --git a/lang/de.json b/lang/de.json index 8e6fbd7a..2c0dfd5f 100644 --- a/lang/de.json +++ b/lang/de.json @@ -257,11 +257,11 @@ "DS4.ErrorSlayingDiceRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten.", "DS4.ErrorInvalidNumberOfDice": "Ungültige Anzahl an Würfeln.", "DS4.ErrorInvalidActorType": "Ungültiger Aktortyp '{type}'.", + "DS4.ErrorInvalidItemType": "Ungültiger Itemtyp '{type}'.", "DS4.ErrorDuringMigration": "Fehler während der Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).", "DS4.ErrorDuringCompendiumMigration": "Fehler während der Aktualisierung Kompendiums '{pack}' für DS4 von Migrationsversion {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).", "DS4.ErrorCannotRollUnownedItem": "Für das Item '{name}' ({id}) kann nicht gewürfelt werden, da es keinem Aktor gehört.", "DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.", - "DS4.ErrorWrongItemType": "Ein Item vom Type '{expectedType}' wurde erwartet aber das Item '{name}' ({id}) ist vom Typ '{actualType}'.", "DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffsarten: {expectedTypes}", "DS4.ErrorUnexpectedAttribute": "Unerwartetes Attribut '{actualAttribute}', erwartete Attribute: {expectedTypes}", "DS4.ErrorUnexpectedTrait": "Unerwartete Eigenschaft '{actualTrait}', erwartete Eigenschaften: {expectedTypes}", diff --git a/lang/en.json b/lang/en.json index cd8632c0..19973936 100644 --- a/lang/en.json +++ b/lang/en.json @@ -257,11 +257,11 @@ "DS4.ErrorSlayingDiceRecursionLimitExceeded": "Maximum recursion depth for slaying dice roll exceeded.", "DS4.ErrorInvalidNumberOfDice": "Invalid number of dice.", "DS4.ErrorInvalidActorType": "Invalid actor type '{type}'.", + "DS4.ErrorInvalidItemType": "Invalid item type '{type}'.", "DS4.ErrorDuringMigration": "Error while migrating DS4 system from migration version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).", "DS4.ErrorDuringCompendiumMigration": "Error while migrating compendium '{pack}' for DS4 from migration version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).", "DS4.ErrorCannotRollUnownedItem": "Rolling for item '{name}' ({id})is not possible because it is not owned.", "DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.", - "DS4.ErrorWrongItemType": "Expected an item of type '{expectedType}' but item '{name}' ({id}) is of type '{actualType}'.", "DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}", "DS4.ErrorUnexpectedAttribute": "Unexpected attribute '{actualAttribute}', expected it to be one of: {expectedTypes}", "DS4.ErrorUnexpectedTrait": "Unexpected trait '{actualTrait}', expected it to be one of: {expectedTypes}", diff --git a/spec/item/type-specific-helpers/spell.spec.ts b/spec/item/spell/calculate-spell-price.spec.ts similarity index 98% rename from spec/item/type-specific-helpers/spell.spec.ts rename to spec/item/spell/calculate-spell-price.spec.ts index e454c959..4b0262b2 100644 --- a/spec/item/type-specific-helpers/spell.spec.ts +++ b/spec/item/spell/calculate-spell-price.spec.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: MIT -import { CooldownDuration, DS4SpellDataSourceData } from "../../../src/item/item-data-source"; -import { calculateSpellPrice } from "../../../src/item/type-specific-helpers/spell"; +import { CooldownDuration, DS4SpellDataSourceData } from "../../../src/item/spell/spell-data-source"; +import { calculateSpellPrice } from "../../../src/item/spell/calculate-spell-price"; const defaultData: DS4SpellDataSourceData = { description: "", diff --git a/src/actor/actor.ts b/src/actor/actor.ts index 8f46b81a..32d5da70 100644 --- a/src/actor/actor.ts +++ b/src/actor/actor.ts @@ -6,9 +6,10 @@ import { ModifiableDataBaseTotal } from "../common/common-data"; import { DS4 } from "../config"; import { getGame } from "../helpers"; +import { DS4ArmorDataProperties } from "../item/armor/armor-data-properties"; import { DS4Item } from "../item/item"; -import { DS4ArmorDataProperties, DS4ShieldDataProperties } from "../item/item-data-properties"; import { ItemType } from "../item/item-data-source"; +import { DS4ShieldDataProperties } from "../item/shield/shield-data-properties"; import { createCheckRoll } from "../rolls/check-factory"; import { Check } from "./actor-data-properties-base"; import { isAttribute, isTrait } from "./actor-data-source-base"; diff --git a/src/hooks/init.ts b/src/hooks/init.ts index ce0eca18..5efa4c8b 100644 --- a/src/hooks/init.ts +++ b/src/hooks/init.ts @@ -26,6 +26,7 @@ import { DS4Roll } from "../rolls/roll"; import registerSlayingDiceModifier from "../rolls/slaying-dice-modifier"; import { registerSystemSettings } from "../settings"; import { DS4TokenDocument } from "../token-document"; +import { DS4ItemProxy } from "../item/proxy"; export default function registerForInitHook(): void { Hooks.once("init", init); @@ -36,7 +37,7 @@ async function init() { getGame().ds4 = { DS4Actor: DS4ActorProxy, - DS4Item, + DS4Item: DS4ItemProxy, DS4, createCheckRoll, migration, @@ -46,7 +47,7 @@ async function init() { CONFIG.DS4 = DS4; CONFIG.Actor.documentClass = DS4ActorProxy; - CONFIG.Item.documentClass = DS4Item; + CONFIG.Item.documentClass = DS4ItemProxy; CONFIG.ActiveEffect.documentClass = DS4ActiveEffect; CONFIG.ChatMessage.documentClass = DS4ChatMessage; CONFIG.Token.documentClass = DS4TokenDocument; diff --git a/src/item/alphabet/alphabet-data-properties.ts b/src/item/alphabet/alphabet-data-properties.ts new file mode 100644 index 00000000..36e3100e --- /dev/null +++ b/src/item/alphabet/alphabet-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4AlphabetDataSourceData } from "./alphabet-data-source"; + +export interface DS4AlphabetDataProperties { + type: "alphabet"; + data: DS4AlphabetDataPropertiesData; +} + +interface DS4AlphabetDataPropertiesData extends DS4AlphabetDataSourceData, DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/alphabet/alphabet-data-source.ts b/src/item/alphabet/alphabet-data-source.ts new file mode 100644 index 00000000..c659add4 --- /dev/null +++ b/src/item/alphabet/alphabet-data-source.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataSourceDataBase } from "../item-data-source-base"; + +export interface DS4AlphabetDataSource { + type: "alphabet"; + data: DS4AlphabetDataSourceData; +} + +export type DS4AlphabetDataSourceData = DS4ItemDataSourceDataBase; diff --git a/src/item/alphabet/alphabet.ts b/src/item/alphabet/alphabet.ts new file mode 100644 index 00000000..d710b896 --- /dev/null +++ b/src/item/alphabet/alphabet.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Alphabet extends DS4Item {} + +export interface DS4Alphabet { + data: foundry.data.ItemData & { type: "alphabet"; _source: { type: "alphabet" } }; +} diff --git a/src/item/armor/armor-data-properties.ts b/src/item/armor/armor-data-properties.ts new file mode 100644 index 00000000..943b7ced --- /dev/null +++ b/src/item/armor/armor-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4ArmorDataSourceData } from "./armor-data-source"; + +export interface DS4ArmorDataProperties { + type: "armor"; + data: DS4ArmorDataPropertiesData; +} + +interface DS4ArmorDataPropertiesData extends DS4ArmorDataSourceData, DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/armor/armor-data-source.ts b/src/item/armor/armor-data-source.ts new file mode 100644 index 00000000..9a689d48 --- /dev/null +++ b/src/item/armor/armor-data-source.ts @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4 } from "../../config"; +import { + DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataProtective, +} from "../item-data-source-base"; + +export interface DS4ArmorDataSource { + type: "armor"; + data: DS4ArmorDataSourceData; +} + +export interface DS4ArmorDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataProtective { + armorMaterialType: keyof typeof DS4.i18n.armorMaterialTypes; + armorType: keyof typeof DS4.i18n.armorTypes; +} diff --git a/src/item/armor/armor.ts b/src/item/armor/armor.ts new file mode 100644 index 00000000..9c18fb6e --- /dev/null +++ b/src/item/armor/armor.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Armor extends DS4Item {} + +export interface DS4Armor { + data: foundry.data.ItemData & { type: "armor"; _source: { type: "armor" } }; +} diff --git a/src/item/equipment/equipment-data-properties.ts b/src/item/equipment/equipment-data-properties.ts new file mode 100644 index 00000000..c6493d6a --- /dev/null +++ b/src/item/equipment/equipment-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4EquipmentDataSourceData } from "./equipment-data-source"; + +export interface DS4EquipmentDataProperties { + type: "equipment"; + data: DS4EquipmentDataPropertiesData; +} + +interface DS4EquipmentDataPropertiesData extends DS4EquipmentDataSourceData, DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/equipment/equipment-data-source.ts b/src/item/equipment/equipment-data-source.ts new file mode 100644 index 00000000..0d96f00d --- /dev/null +++ b/src/item/equipment/equipment-data-source.ts @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { + DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataPhysical, +} from "../item-data-source-base"; + +export interface DS4EquipmentDataSource { + type: "equipment"; + data: DS4EquipmentDataSourceData; +} + +export interface DS4EquipmentDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable {} diff --git a/src/item/equipment/equipment.ts b/src/item/equipment/equipment.ts new file mode 100644 index 00000000..f5e7d455 --- /dev/null +++ b/src/item/equipment/equipment.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Equipment extends DS4Item {} + +export interface DS4Equipment { + data: foundry.data.ItemData & { type: "equipment"; _source: { type: "equipment" } }; +} diff --git a/src/item/item-data-properties-base.ts b/src/item/item-data-properties-base.ts new file mode 100644 index 00000000..a8a38380 --- /dev/null +++ b/src/item/item-data-properties-base.ts @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +export interface DS4ItemDataPropertiesDataRollable { + rollable: boolean; +} diff --git a/src/item/item-data-properties.ts b/src/item/item-data-properties.ts index 3dcda46f..54383451 100644 --- a/src/item/item-data-properties.ts +++ b/src/item/item-data-properties.ts @@ -1,21 +1,18 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher +// SPDX-FileCopyrightText: 2022 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"; +import { DS4AlphabetDataProperties } from "./alphabet/alphabet-data-properties"; +import { DS4ArmorDataProperties } from "./armor/armor-data-properties"; +import { DS4EquipmentDataProperties } from "./equipment/equipment-data-properties"; +import { DS4LanguageDataProperties } from "./language/language-data-properties"; +import { DS4LootDataProperties } from "./loot/loot-data-properties"; +import { DS4RacialAbilityDataProperties } from "./racial-ability/racial-ability-data-properties"; +import { DS4ShieldDataProperties } from "./shield/shield-data-properties"; +import { DS4SpecialCreatureAbilityDataProperties } from "./special-creature-ability/special-creature-ability-data-properties"; +import { DS4SpellDataProperties } from "./spell/spell-data-properties"; +import { DS4TalentDataProperties } from "./talent/talent-data-properties"; +import { DS4WeaponDataProperties } from "./weapon/weapon-data-properties"; declare global { interface DataConfig { @@ -24,107 +21,14 @@ declare global { } 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 {} + | DS4ArmorDataProperties + | DS4EquipmentDataProperties + | DS4LanguageDataProperties + | DS4LootDataProperties + | DS4RacialAbilityDataProperties + | DS4ShieldDataProperties + | DS4SpecialCreatureAbilityDataProperties + | DS4SpellDataProperties + | DS4TalentDataProperties + | DS4WeaponDataProperties; diff --git a/src/item/item-data-source-base.ts b/src/item/item-data-source-base.ts new file mode 100644 index 00000000..7b020446 --- /dev/null +++ b/src/item/item-data-source-base.ts @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// SPDX-FileCopyrightText: 2021 Oliver Rümpelein +// SPDX-FileCopyrightText: 2021 Gesina Schwalbe +// +// SPDX-License-Identifier: MIT + +import { DS4 } from "../config"; + +export interface DS4ItemDataSourceDataBase { + description: string; +} + +export 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; +} + +export interface DS4ItemDataSourceDataEquipable { + equipped: boolean; +} + +export interface DS4ItemDataSourceDataProtective { + armorValue: number; +} diff --git a/src/item/item-data-source.ts b/src/item/item-data-source.ts index f75dafa5..a43a8b3a 100644 --- a/src/item/item-data-source.ts +++ b/src/item/item-data-source.ts @@ -1,11 +1,19 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// SPDX-FileCopyrightText: 2021 Oliver Rümpelein -// SPDX-FileCopyrightText: 2021 Gesina Schwalbe +// SPDX-FileCopyrightText: 2022 Johannes Loher // // SPDX-License-Identifier: MIT -import { ModifiableDataBaseMax } from "../common/common-data"; import { DS4 } from "../config"; +import { DS4AlphabetDataSource } from "./alphabet/alphabet-data-source"; +import { DS4ArmorDataSource } from "./armor/armor-data-source"; +import { DS4EquipmentDataSource } from "./equipment/equipment-data-source"; +import { DS4LanguageDataSource } from "./language/language-data-source"; +import { DS4LootDataSource } from "./loot/loot-data-source"; +import { DS4RacialAbilityDataSource } from "./racial-ability/racial-ability-data-source"; +import { DS4ShieldDataSource } from "./shield/shield-data-source"; +import { DS4SpecialCreatureAbilityDataSource } from "./special-creature-ability/special-creature-ability-data-source"; +import { DS4SpellDataSource } from "./spell/spell-data-source"; +import { DS4TalentDataSource } from "./talent/talent-data-source"; +import { DS4WeaponDataSource } from "./weapon/weapon-data-source"; declare global { interface SourceConfig { @@ -16,169 +24,14 @@ declare global { 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 type CooldownDuration = keyof typeof DS4.i18n.cooldownDurations; - -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: CooldownDuration; - 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 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; -} + | DS4ArmorDataSource + | DS4EquipmentDataSource + | DS4LanguageDataSource + | DS4LootDataSource + | DS4RacialAbilityDataSource + | DS4ShieldDataSource + | DS4SpecialCreatureAbilityDataSource + | DS4SpellDataSource + | DS4TalentDataSource + | DS4WeaponDataSource; diff --git a/src/item/item-sheet.ts b/src/item/item-sheet.ts index 77fac4af..245b1cfe 100644 --- a/src/item/item-sheet.ts +++ b/src/item/item-sheet.ts @@ -9,7 +9,7 @@ import { DS4 } from "../config"; import { getGame } from "../helpers"; import notifications from "../ui/notifications"; import { enforce } from "../utils"; -import { isDS4ItemDataTypePhysical } from "./item-data-source"; +import { isDS4ItemDataTypePhysical } from "./item-data-source-base"; /** * The Sheet class for DS4 Items diff --git a/src/item/item.ts b/src/item/item.ts index ed875897..78e7d5dc 100644 --- a/src/item/item.ts +++ b/src/item/item.ts @@ -3,12 +3,8 @@ // // SPDX-License-Identifier: MIT -import { DS4 } from "../config"; import { getGame } from "../helpers"; -import { createCheckRoll } from "../rolls/check-factory"; -import notifications from "../ui/notifications"; -import { AttackType, ItemType } from "./item-data-source"; -import { calculateSpellPrice } from "./type-specific-helpers/spell"; +import { ItemType } from "./item-data-source"; declare global { interface DocumentClassConfig { @@ -27,18 +23,7 @@ export class DS4Item extends Item { /** @override */ prepareDerivedData(): void { - if (this.data.type === "talent") { - const data = this.data.data; - data.rank.total = data.rank.base + data.rank.mod; - } - if (this.data.type === "weapon" || this.data.type === "spell") { - this.data.data.rollable = this.data.data.equipped; - } else { - this.data.data.rollable = false; - } - if (this.data.type === "spell") { - this.data.data.price = calculateSpellPrice(this.data.data); - } + this.data.data.rollable = false; } isNonEquippedEuipable(): boolean { @@ -49,9 +34,6 @@ export class DS4Item extends Item { * The number of times that active effect changes originating from this item should be applied. */ get activeEffectFactor(): number | undefined { - if (this.data.type === "talent") { - return this.data.data.rank.total; - } return 1; } @@ -66,142 +48,8 @@ export class DS4Item extends Item { * Roll a check for an action with this item. * @param options - Additional options to customize the roll */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { - switch (this.data.type) { - case "weapon": - return this.rollWeapon(options); - case "spell": - return this.rollSpell(options); - default: - throw new Error( - getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type }), - ); - } - } - - protected async rollWeapon(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { - if (!(this.data.type === "weapon")) { - throw new Error( - getGame().i18n.format("DS4.ErrorWrongItemType", { - actualType: this.data.type, - expectedType: "weapon", - id: this.id, - name: this.name, - }), - ); - } - - if (!this.data.data.equipped) { - return notifications.warn( - getGame().i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { - name: this.name, - id: this.id, - type: this.data.type, - }), - ); - } - - if (!this.actor) { - throw new Error(getGame().i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id })); - } - - const ownerDataData = this.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; - - const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); - await createCheckRoll(checkTargetNumber, { - rollMode: getGame().settings.get("core", "rollMode"), - maximumCoupResult: ownerDataData.rolling.maximumCoupResult, - minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, - flavor: "DS4.ItemWeaponCheckFlavor", - flavorData: { actor: speaker.alias ?? this.actor.name, weapon: this.name }, - speaker, - }); - } - - protected async rollSpell(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { - if (!(this.data.type === "spell")) { - throw new Error( - getGame().i18n.format("DS4.ErrorWrongItemType", { - actualType: this.data.type, - expectedType: "spell", - id: this.id, - name: this.name, - }), - ); - } - - if (!this.data.data.equipped) { - return notifications.warn( - getGame().i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { - name: this.name, - id: this.id, - type: this.data.type, - }), - ); - } - - if (!this.actor) { - throw new Error(getGame().i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id })); - } - - const ownerDataData = this.actor.data.data; - const spellModifier = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined; - if (spellModifier === undefined) { - notifications.info( - getGame().i18n.format("DS4.InfoManuallyEnterSpellModifier", { - name: this.name, - spellModifier: this.data.data.bonus, - }), - ); - } - const spellType = this.data.data.spellType; - const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellModifier ?? 0); - - const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); - await createCheckRoll(checkTargetNumber, { - rollMode: getGame().settings.get("core", "rollMode"), - maximumCoupResult: ownerDataData.rolling.maximumCoupResult, - minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, - flavor: "DS4.ItemSpellCheckFlavor", - flavorData: { actor: speaker.alias ?? this.actor.name, spell: this.name }, - speaker, - }); - } - - protected async getCombatValueKeyForAttackType(attackType: AttackType): Promise<"meleeAttack" | "rangedAttack"> { - if (attackType === "meleeRanged") { - const { melee, ranged } = { ...DS4.i18n.attackTypes }; - const identifier = "attack-type-selection"; - return Dialog.prompt({ - title: getGame().i18n.localize("DS4.DialogAttackTypeSelection"), - content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { - selects: [ - { - label: getGame().i18n.localize("DS4.AttackType"), - identifier, - options: { melee, ranged }, - }, - ], - }), - label: getGame().i18n.localize("DS4.GenericOkButton"), - callback: (html) => { - const selectedAttackType = html.find(`#${identifier}`).val(); - if (selectedAttackType !== "melee" && selectedAttackType !== "ranged") { - throw new Error( - getGame().i18n.format("DS4.ErrorUnexpectedAttackType", { - actualType: selectedAttackType, - expectedTypes: "'melee', 'ranged'", - }), - ); - } - return `${selectedAttackType}Attack` as const; - }, - }); - } else { - return `${attackType}Attack` as const; - } + throw new Error(getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type })); } } diff --git a/src/item/language/language-data-properties.ts b/src/item/language/language-data-properties.ts new file mode 100644 index 00000000..d86bf6cc --- /dev/null +++ b/src/item/language/language-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4LanguageDataSourceData } from "./language-data-source"; + +export interface DS4LanguageDataProperties { + type: "language"; + data: DS4LanguageDataPropertiesData; +} + +interface DS4LanguageDataPropertiesData extends DS4LanguageDataSourceData, DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/language/language-data-source.ts b/src/item/language/language-data-source.ts new file mode 100644 index 00000000..bbe680ab --- /dev/null +++ b/src/item/language/language-data-source.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataSourceDataBase } from "../item-data-source-base"; + +export interface DS4LanguageDataSource { + type: "language"; + data: DS4LanguageDataSourceData; +} + +export type DS4LanguageDataSourceData = DS4ItemDataSourceDataBase; diff --git a/src/item/language/language.ts b/src/item/language/language.ts new file mode 100644 index 00000000..0faf53da --- /dev/null +++ b/src/item/language/language.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Language extends DS4Item {} + +export interface DS4Language { + data: foundry.data.ItemData & { type: "language"; _source: { type: "language" } }; +} diff --git a/src/item/loot/loot-data-properties.ts b/src/item/loot/loot-data-properties.ts new file mode 100644 index 00000000..dcb13ae6 --- /dev/null +++ b/src/item/loot/loot-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4LootDataSourceData } from "./loot-data-source"; + +export interface DS4LootDataProperties { + type: "loot"; + data: DS4LootDataPropertiesData; +} + +interface DS4LootDataPropertiesData extends DS4LootDataSourceData, DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/loot/loot-data-source.ts b/src/item/loot/loot-data-source.ts new file mode 100644 index 00000000..8cb48edf --- /dev/null +++ b/src/item/loot/loot-data-source.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataSourceDataBase, DS4ItemDataSourceDataPhysical } from "../item-data-source-base"; + +export interface DS4LootDataSource { + type: "loot"; + data: DS4LootDataSourceData; +} + +export interface DS4LootDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataPhysical {} diff --git a/src/item/loot/loot.ts b/src/item/loot/loot.ts new file mode 100644 index 00000000..34528207 --- /dev/null +++ b/src/item/loot/loot.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Loot extends DS4Item {} + +export interface DS4Loot { + data: foundry.data.ItemData & { type: "loot"; _source: { type: "loot" } }; +} diff --git a/src/item/proxy.ts b/src/item/proxy.ts new file mode 100644 index 00000000..58d4997f --- /dev/null +++ b/src/item/proxy.ts @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { getGame } from "../helpers"; +import { DS4Alphabet } from "./alphabet/alphabet"; +import { DS4Armor } from "./armor/armor"; +import { DS4Equipment } from "./equipment/equipment"; +import { DS4Item } from "./item"; +import { DS4Language } from "./language/language"; +import { DS4Loot } from "./loot/loot"; +import { DS4RacialAbility } from "./racial-ability/racial-ability"; +import { DS4Shield } from "./shield/shield"; +import { DS4SpecialCreatureAbility } from "./special-creature-ability/special-creature-ability"; +import { DS4Spell } from "./spell/spell"; +import { DS4Talent } from "./talent/talent"; +import { DS4Weapon } from "./weapon/weapon"; + +const handler = { + construct(_: typeof DS4Item, args: ConstructorParameters) { + switch (args[0]?.type) { + case "alphabet": + return new DS4Alphabet(...args); + case "armor": + return new DS4Armor(...args); + case "equipment": + return new DS4Equipment(...args); + case "language": + return new DS4Language(...args); + case "loot": + return new DS4Loot(...args); + case "racialAbility": + return new DS4RacialAbility(...args); + case "shield": + return new DS4Shield(...args); + case "specialCreatureAbility": + return new DS4SpecialCreatureAbility(...args); + case "spell": + return new DS4Spell(...args); + case "talent": + return new DS4Talent(...args); + case "weapon": + return new DS4Weapon(...args); + default: + throw new Error(getGame().i18n.format("DS4.ErrorInvalidItemType", { type: args[0]?.type })); + } + }, +}; + +export const DS4ItemProxy: typeof DS4Item = new Proxy(DS4Item, handler); diff --git a/src/item/racial-ability/racial-ability-data-properties.ts b/src/item/racial-ability/racial-ability-data-properties.ts new file mode 100644 index 00000000..45944609 --- /dev/null +++ b/src/item/racial-ability/racial-ability-data-properties.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4RacialAbilityDataSourceData } from "./racial-ability-data-source"; + +export interface DS4RacialAbilityDataProperties { + type: "racialAbility"; + data: DS4RacialAbilityDataPropertiesData; +} + +interface DS4RacialAbilityDataPropertiesData + extends DS4RacialAbilityDataSourceData, + DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/racial-ability/racial-ability-data-source.ts b/src/item/racial-ability/racial-ability-data-source.ts new file mode 100644 index 00000000..b3149ca7 --- /dev/null +++ b/src/item/racial-ability/racial-ability-data-source.ts @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataSourceDataBase } from "../item-data-source-base"; + +export interface DS4RacialAbilityDataSource { + type: "racialAbility"; + data: DS4RacialAbilityDataSourceData; +} + +export type DS4RacialAbilityDataSourceData = DS4ItemDataSourceDataBase; diff --git a/src/item/racial-ability/racial-ability.ts b/src/item/racial-ability/racial-ability.ts new file mode 100644 index 00000000..526f2533 --- /dev/null +++ b/src/item/racial-ability/racial-ability.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4RacialAbility extends DS4Item {} + +export interface DS4RacialAbility { + data: foundry.data.ItemData & { type: "racialAbility"; _source: { type: "racialAbility" } }; +} diff --git a/src/item/shield/shield-data-properties.ts b/src/item/shield/shield-data-properties.ts new file mode 100644 index 00000000..1064717b --- /dev/null +++ b/src/item/shield/shield-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4ShieldDataSourceData } from "./shield-data-source"; + +export interface DS4ShieldDataProperties { + type: "shield"; + data: DS4ShieldDataPropertiesData; +} + +interface DS4ShieldDataPropertiesData extends DS4ShieldDataSourceData, DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/shield/shield-data-source.ts b/src/item/shield/shield-data-source.ts new file mode 100644 index 00000000..493ae000 --- /dev/null +++ b/src/item/shield/shield-data-source.ts @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { + DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataProtective, +} from "../item-data-source-base"; + +export interface DS4ShieldDataSource { + type: "shield"; + data: DS4ShieldDataSourceData; +} + +export interface DS4ShieldDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataProtective {} diff --git a/src/item/shield/shield.ts b/src/item/shield/shield.ts new file mode 100644 index 00000000..16a6e683 --- /dev/null +++ b/src/item/shield/shield.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Shield extends DS4Item {} + +export interface DS4Shield { + data: foundry.data.ItemData & { type: "shield"; _source: { type: "shield" } }; +} diff --git a/src/item/special-creature-ability/special-creature-ability-data-properties.ts b/src/item/special-creature-ability/special-creature-ability-data-properties.ts new file mode 100644 index 00000000..213fd416 --- /dev/null +++ b/src/item/special-creature-ability/special-creature-ability-data-properties.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4SpecialCreatureAbilityDataSourceData } from "./special-creature-ability-data-source"; + +export interface DS4SpecialCreatureAbilityDataProperties { + type: "specialCreatureAbility"; + data: DS4SpecialCreatureAbilityDataPropertiesData; +} + +interface DS4SpecialCreatureAbilityDataPropertiesData + extends DS4SpecialCreatureAbilityDataSourceData, + DS4ItemDataPropertiesDataRollable {} diff --git a/src/item/special-creature-ability/special-creature-ability-data-source.ts b/src/item/special-creature-ability/special-creature-ability-data-source.ts new file mode 100644 index 00000000..2772b101 --- /dev/null +++ b/src/item/special-creature-ability/special-creature-ability-data-source.ts @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataSourceDataBase } from "../item-data-source-base"; + +export interface DS4SpecialCreatureAbilityDataSource { + type: "specialCreatureAbility"; + data: DS4SpecialCreatureAbilityDataSourceData; +} + +export interface DS4SpecialCreatureAbilityDataSourceData extends DS4ItemDataSourceDataBase { + experiencePoints: number; +} diff --git a/src/item/special-creature-ability/special-creature-ability.ts b/src/item/special-creature-ability/special-creature-ability.ts new file mode 100644 index 00000000..1919ae88 --- /dev/null +++ b/src/item/special-creature-ability/special-creature-ability.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4SpecialCreatureAbility extends DS4Item {} + +export interface DS4SpecialCreatureAbility { + data: foundry.data.ItemData & { type: "specialCreatureAbility"; _source: { type: "specialCreatureAbility" } }; +} diff --git a/src/item/type-specific-helpers/spell.ts b/src/item/spell/calculate-spell-price.ts similarity index 93% rename from src/item/type-specific-helpers/spell.ts rename to src/item/spell/calculate-spell-price.ts index c9949996..910fa7da 100644 --- a/src/item/type-specific-helpers/spell.ts +++ b/src/item/spell/calculate-spell-price.ts @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -import { CooldownDuration, DS4SpellDataSourceData } from "../item-data-source"; +import { CooldownDuration, DS4SpellDataSourceData } from "./spell-data-source"; export function calculateSpellPrice(data: DS4SpellDataSourceData): number | null { const spellPriceFactor = calculateSpellPriceFactor(data.cooldownDuration); diff --git a/src/item/spell/spell-data-properties.ts b/src/item/spell/spell-data-properties.ts new file mode 100644 index 00000000..2cb88079 --- /dev/null +++ b/src/item/spell/spell-data-properties.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4SpellDataSourceData } from "./spell-data-source"; + +export interface DS4SpellDataProperties { + type: "spell"; + data: DS4SpellDataPropertiesData; +} + +interface DS4SpellDataPropertiesData extends DS4SpellDataSourceData, DS4ItemDataPropertiesDataRollable { + price: number | null; +} diff --git a/src/item/spell/spell-data-source.ts b/src/item/spell/spell-data-source.ts new file mode 100644 index 00000000..23393c99 --- /dev/null +++ b/src/item/spell/spell-data-source.ts @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4 } from "../../config"; +import { DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable } from "../item-data-source-base"; + +export interface DS4SpellDataSource { + type: "spell"; + data: DS4SpellDataSourceData; +} + +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: CooldownDuration; + 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 TemporalUnit = keyof typeof DS4.i18n.temporalUnits; +export type CooldownDuration = keyof typeof DS4.i18n.cooldownDurations; diff --git a/src/item/spell/spell.ts b/src/item/spell/spell.ts new file mode 100644 index 00000000..efcf8f1a --- /dev/null +++ b/src/item/spell/spell.ts @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { getGame } from "../../helpers"; +import { createCheckRoll } from "../../rolls/check-factory"; +import notifications from "../../ui/notifications"; +import { DS4Item } from "../item"; +import { calculateSpellPrice } from "./calculate-spell-price"; + +export class DS4Spell extends DS4Item { + /** @override */ + prepareDerivedData(): void { + this.data.data.rollable = this.data.data.equipped; + this.data.data.price = calculateSpellPrice(this.data.data); + } + + /** @override */ + async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { + const game = getGame(); + + if (!this.data.data.equipped) { + return notifications.warn( + game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { + name: this.name, + id: this.id, + type: this.data.type, + }), + ); + } + + if (!this.actor) { + throw new Error(game.i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id })); + } + + const ownerDataData = this.actor.data.data; + const spellModifier = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined; + if (spellModifier === undefined) { + notifications.info( + game.i18n.format("DS4.InfoManuallyEnterSpellModifier", { + name: this.name, + spellModifier: this.data.data.bonus, + }), + ); + } + const spellType = this.data.data.spellType; + const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellModifier ?? 0); + + const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); + await createCheckRoll(checkTargetNumber, { + rollMode: game.settings.get("core", "rollMode"), + maximumCoupResult: ownerDataData.rolling.maximumCoupResult, + minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, + flavor: "DS4.ItemSpellCheckFlavor", + flavorData: { actor: speaker.alias ?? this.actor.name, spell: this.name }, + speaker, + }); + } +} + +export interface DS4Spell { + data: foundry.data.ItemData & { type: "spell"; _source: { type: "spell" } }; +} diff --git a/src/item/talent/talent-data-properties.ts b/src/item/talent/talent-data-properties.ts new file mode 100644 index 00000000..c4eec70c --- /dev/null +++ b/src/item/talent/talent-data-properties.ts @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { ModifiableDataBaseTotalMax } from "../../common/common-data"; +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4TalentDataSourceData } from "./talent-data-source"; + +export interface DS4TalentDataProperties { + type: "talent"; + data: DS4TalentDataPropertiesData; +} + +interface DS4TalentDataPropertiesData extends DS4TalentDataSourceData, DS4ItemDataPropertiesDataRollable { + rank: ModifiableDataBaseTotalMax; +} diff --git a/src/item/talent/talent-data-source.ts b/src/item/talent/talent-data-source.ts new file mode 100644 index 00000000..98a5288d --- /dev/null +++ b/src/item/talent/talent-data-source.ts @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { ModifiableDataBaseMax } from "../../common/common-data"; +import { DS4ItemDataSourceDataBase } from "../item-data-source-base"; + +export interface DS4TalentDataSource { + type: "talent"; + data: DS4TalentDataSourceData; +} + +export interface DS4TalentDataSourceData extends DS4ItemDataSourceDataBase { + rank: ModifiableDataBaseMax; +} diff --git a/src/item/talent/talent.ts b/src/item/talent/talent.ts new file mode 100644 index 00000000..dea5782f --- /dev/null +++ b/src/item/talent/talent.ts @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4Item } from "../item"; + +export class DS4Talent extends DS4Item { + /** @override */ + prepareDerivedData(): void { + super.prepareDerivedData(); + const data = this.data.data; + data.rank.total = data.rank.base + data.rank.mod; + } + + /** @override */ + get activeEffectFactor(): number | undefined { + return this.data.data.rank.total; + } +} + +export interface DS4Talent { + data: foundry.data.ItemData & { type: "talent"; _source: { type: "talent" } }; +} diff --git a/src/item/weapon/weapon-data-properties.ts b/src/item/weapon/weapon-data-properties.ts new file mode 100644 index 00000000..3a74c508 --- /dev/null +++ b/src/item/weapon/weapon-data-properties.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; +import { DS4WeaponDataSourceData } from "./weapon-data-source"; + +interface DS4WeaponDataPropertiesData extends DS4WeaponDataSourceData, DS4ItemDataPropertiesDataRollable {} + +export interface DS4WeaponDataProperties { + type: "weapon"; + data: DS4WeaponDataPropertiesData; +} diff --git a/src/item/weapon/weapon-data-source.ts b/src/item/weapon/weapon-data-source.ts new file mode 100644 index 00000000..72641e21 --- /dev/null +++ b/src/item/weapon/weapon-data-source.ts @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4 } from "../../config"; +import { + DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataEquipable, + DS4ItemDataSourceDataPhysical, +} from "../item-data-source-base"; + +export interface DS4WeaponDataSource { + type: "weapon"; + data: DS4WeaponDataSourceData; +} + +export interface DS4WeaponDataSourceData + extends DS4ItemDataSourceDataBase, + DS4ItemDataSourceDataPhysical, + DS4ItemDataSourceDataEquipable { + attackType: AttackType; + weaponBonus: number; + opponentDefense: number; +} + +export type AttackType = keyof typeof DS4.i18n.attackTypes; diff --git a/src/item/weapon/weapon.ts b/src/item/weapon/weapon.ts new file mode 100644 index 00000000..cd326d9d --- /dev/null +++ b/src/item/weapon/weapon.ts @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4 } from "../../config"; +import { getGame } from "../../helpers"; +import { createCheckRoll } from "../../rolls/check-factory"; +import notifications from "../../ui/notifications"; +import { DS4Item } from "../item"; +import { AttackType } from "./weapon-data-source"; + +export class DS4Weapon extends DS4Item { + /** @override */ + prepareDerivedData(): void { + this.data.data.rollable = this.data.data.equipped; + } + + /** @override */ + async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { + const game = getGame(); + if (!this.data.data.equipped) { + return notifications.warn( + game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { + name: this.name, + id: this.id, + type: this.data.type, + }), + ); + } + + if (!this.actor) { + throw new Error(game.i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id })); + } + + const ownerDataData = this.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; + + const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); + await createCheckRoll(checkTargetNumber, { + rollMode: getGame().settings.get("core", "rollMode"), + maximumCoupResult: ownerDataData.rolling.maximumCoupResult, + minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, + flavor: "DS4.ItemWeaponCheckFlavor", + flavorData: { actor: speaker.alias ?? this.actor.name, weapon: this.name }, + speaker, + }); + } + + private async getCombatValueKeyForAttackType(attackType: AttackType): Promise<"meleeAttack" | "rangedAttack"> { + if (attackType === "meleeRanged") { + const { melee, ranged } = { ...DS4.i18n.attackTypes }; + const identifier = "attack-type-selection"; + return Dialog.prompt({ + title: getGame().i18n.localize("DS4.DialogAttackTypeSelection"), + content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { + selects: [ + { + label: getGame().i18n.localize("DS4.AttackType"), + identifier, + options: { melee, ranged }, + }, + ], + }), + label: getGame().i18n.localize("DS4.GenericOkButton"), + callback: (html) => { + const selectedAttackType = html.find(`#${identifier}`).val(); + if (selectedAttackType !== "melee" && selectedAttackType !== "ranged") { + throw new Error( + getGame().i18n.format("DS4.ErrorUnexpectedAttackType", { + actualType: selectedAttackType, + expectedTypes: "'melee', 'ranged'", + }), + ); + } + return `${selectedAttackType}Attack` as const; + }, + }); + } else { + return `${attackType}Attack` as const; + } + } +} + +export interface DS4Weapon { + data: foundry.data.ItemData & { type: "weapon"; _source: { type: "weapon" } }; +} diff --git a/src/migrations/005.ts b/src/migrations/005.ts index 80d0fc8f..16919ce6 100644 --- a/src/migrations/005.ts +++ b/src/migrations/005.ts @@ -2,7 +2,6 @@ // // SPDX-License-Identifier: MIT -import { CooldownDuration } from "../item/item-data-source"; import { getActorUpdateDataGetter, getCompendiumMigrator, @@ -45,10 +44,10 @@ function getItemUpdateData(itemData: Partial) return updateData; } -function migrateCooldownDuration(cooldownDurationValue?: string, cooldownDurationUnit?: string) { +function migrateCooldownDuration(cooldownDurationValue = "", cooldownDurationUnit = "") { if (Number.isNumeric(cooldownDurationValue)) { - const value = Number.fromString(cooldownDurationValue!); - const rounds = getRounds(cooldownDurationUnit ?? "", value); + const value = Number.fromString(cooldownDurationValue); + const rounds = getRounds(cooldownDurationUnit, value); if (rounds * secondsPerRound > secondsPerDay) { return "d20d";