From 7c7d30854c98247b30a4cbd0ef9d2e0c16b89a9f Mon Sep 17 00:00:00 2001
From: Johannes Loher <johannes.loher@fg4f.de>
Date: Mon, 22 Mar 2021 09:04:45 +0100
Subject: [PATCH 1/3] Separate data and _data for DS4Item

---
 package.json                           |  2 +-
 src/module/actor/actor-data.ts         | 34 +++++------
 src/module/actor/actor.ts              |  8 +--
 src/module/actor/sheets/actor-sheet.ts |  2 +-
 src/module/common/common-data.ts       |  9 +++
 src/module/global.d.ts                 |  6 ++
 src/module/item/item-data.ts           | 40 ++++++-------
 src/module/item/item-prepared-data.ts  | 80 ++++++++++++++++++++++++++
 src/module/item/item.ts                |  9 ++-
 src/module/migrations.ts               |  2 +-
 src/module/rolls/check-factory.ts      |  2 +-
 yarn.lock                              | 10 ++--
 12 files changed, 150 insertions(+), 54 deletions(-)
 create mode 100644 src/module/global.d.ts
 create mode 100644 src/module/item/item-prepared-data.ts

diff --git a/package.json b/package.json
index 5ac79213..771fe7f0 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
         "postinstall": "husky install"
     },
     "devDependencies": {
-        "@league-of-foundry-developers/foundry-vtt-types": "^0.7.9-3",
+        "@league-of-foundry-developers/foundry-vtt-types": "^0.7.9-4",
         "@rollup/plugin-node-resolve": "^11.2.0",
         "@types/fs-extra": "^9.0.8",
         "@types/jest": "^26.0.20",
diff --git a/src/module/actor/actor-data.ts b/src/module/actor/actor-data.ts
index 7fcb8398..10ebedcf 100644
--- a/src/module/actor/actor-data.ts
+++ b/src/module/actor/actor-data.ts
@@ -1,4 +1,4 @@
-import { ModifiableData, ResourceData, UsableResource } from "../common/common-data";
+import { ModifiableDataTotal, ResourceData, UsableResource } from "../common/common-data";
 import { DS4 } from "../config";
 import { DS4ItemData } from "../item/item-data";
 
@@ -21,29 +21,29 @@ interface DS4ActorDataDataBase {
 }
 
 interface DS4ActorDataDataAttributes {
-    body: ModifiableData<number>;
-    mobility: ModifiableData<number>;
-    mind: ModifiableData<number>;
+    body: ModifiableDataTotal<number>;
+    mobility: ModifiableDataTotal<number>;
+    mind: ModifiableDataTotal<number>;
 }
 
 interface DS4ActorDataDataTraits {
-    strength: ModifiableData<number>;
-    constitution: ModifiableData<number>;
-    agility: ModifiableData<number>;
-    dexterity: ModifiableData<number>;
-    intellect: ModifiableData<number>;
-    aura: ModifiableData<number>;
+    strength: ModifiableDataTotal<number>;
+    constitution: ModifiableDataTotal<number>;
+    agility: ModifiableDataTotal<number>;
+    dexterity: ModifiableDataTotal<number>;
+    intellect: ModifiableDataTotal<number>;
+    aura: ModifiableDataTotal<number>;
 }
 
 interface DS4ActorDataDataCombatValues {
     hitPoints: ResourceData<number>;
-    defense: ModifiableData<number>;
-    initiative: ModifiableData<number>;
-    movement: ModifiableData<number>;
-    meleeAttack: ModifiableData<number>;
-    rangedAttack: ModifiableData<number>;
-    spellcasting: ModifiableData<number>;
-    targetedSpellcasting: ModifiableData<number>;
+    defense: ModifiableDataTotal<number>;
+    initiative: ModifiableDataTotal<number>;
+    movement: ModifiableDataTotal<number>;
+    meleeAttack: ModifiableDataTotal<number>;
+    rangedAttack: ModifiableDataTotal<number>;
+    spellcasting: ModifiableDataTotal<number>;
+    targetedSpellcasting: ModifiableDataTotal<number>;
 }
 
 interface DS4ActorDataDataRolling {
diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts
index 0e4cbb0d..b89f03e6 100644
--- a/src/module/actor/actor.ts
+++ b/src/module/actor/actor.ts
@@ -1,4 +1,4 @@
-import { ModifiableData } from "../common/common-data";
+import { ModifiableDataTotal } from "../common/common-data";
 import { DS4 } from "../config";
 import { DS4Item } from "../item/item";
 import { ItemType } from "../item/item-data";
@@ -32,11 +32,11 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
 
         const attributes = data.data.attributes;
         Object.values(attributes).forEach(
-            (attribute: ModifiableData<number>) => (attribute.total = attribute.base + attribute.mod),
+            (attribute: ModifiableDataTotal<number>) => (attribute.total = attribute.base + attribute.mod),
         );
 
         const traits = data.data.traits;
-        Object.values(traits).forEach((trait: ModifiableData<number>) => (trait.total = trait.base + trait.mod));
+        Object.values(traits).forEach((trait: ModifiableDataTotal<number>) => (trait.total = trait.base + trait.mod));
     }
 
     applyActiveEffectsToBaseData(): void {
@@ -184,7 +184,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
             (data.attributes.mind.total ?? 0) + (data.traits.dexterity.total ?? 0) - armorValueOfEquippedItems;
 
         Object.values(data.combatValues).forEach(
-            (combatValue: ModifiableData<number>) => (combatValue.total = combatValue.base + combatValue.mod),
+            (combatValue: ModifiableDataTotal<number>) => (combatValue.total = combatValue.base + combatValue.mod),
         );
     }
 
diff --git a/src/module/actor/sheets/actor-sheet.ts b/src/module/actor/sheets/actor-sheet.ts
index 470f33ea..069fce43 100644
--- a/src/module/actor/sheets/actor-sheet.ts
+++ b/src/module/actor/sheets/actor-sheet.ts
@@ -160,7 +160,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
         ev.preventDefault();
         const el: HTMLFormElement = $(ev.currentTarget).get(0);
         const id = $(ev.currentTarget).parents(".item").data("itemId");
-        const item = duplicate<DS4Item, "lenient">(this.actor.getOwnedItem(id));
+        const item = duplicate(this.actor.getOwnedItem(id));
         const property: string | undefined = $(ev.currentTarget).data("property");
 
         // Early return:
diff --git a/src/module/common/common-data.ts b/src/module/common/common-data.ts
index d23b61d0..5956d6f1 100644
--- a/src/module/common/common-data.ts
+++ b/src/module/common/common-data.ts
@@ -1,6 +1,15 @@
 export interface ModifiableData<T> {
     base: T;
     mod: T;
+}
+
+export interface HasTotal<T> {
+    total: T;
+}
+
+export interface ModifiableDataTotal<T> {
+    base: T;
+    mod: T;
     total?: T;
 }
 
diff --git a/src/module/global.d.ts b/src/module/global.d.ts
new file mode 100644
index 00000000..c560d623
--- /dev/null
+++ b/src/module/global.d.ts
@@ -0,0 +1,6 @@
+declare namespace ClientSettings {
+    interface Values {
+        "ds4.systemMigrationVersion": number;
+        "ds4.useSlayingDiceForAutomatedChecks": boolean;
+    }
+}
diff --git a/src/module/item/item-data.ts b/src/module/item/item-data.ts
index c28fbe86..fbae27e3 100644
--- a/src/module/item/item-data.ts
+++ b/src/module/item/item-data.ts
@@ -16,7 +16,7 @@ export type DS4ItemData =
     | DS4AlphabetData
     | DS4SpecialCreatureAbilityData;
 
-interface DS4ItemDataHelper<T, U extends ItemType> extends Item.Data<T> {
+export interface DS4ItemDataHelper<T, U extends ItemType> extends Item.Data<T> {
     type: U;
 }
 
@@ -34,17 +34,13 @@ type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbility
 
 export type AttackType = keyof typeof DS4["i18n"]["attackTypes"];
 
-interface DS4WeaponDataData
-    extends DS4ItemDataDataBase,
-        DS4ItemDataDataPhysical,
-        DS4ItemDataDataEquipable,
-        DS4ItemDataDataRollable {
+export interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {
     attackType: AttackType;
     weaponBonus: number;
     opponentDefense: number;
 }
 
-interface DS4ArmorDataData
+export interface DS4ArmorDataData
     extends DS4ItemDataDataBase,
         DS4ItemDataDataPhysical,
         DS4ItemDataDataEquipable,
@@ -53,15 +49,15 @@ interface DS4ArmorDataData
     armorType: "body" | "helmet" | "vambrace" | "greaves" | "vambraceGreaves";
 }
 
-interface DS4TalentDataData extends DS4ItemDataDataBase {
+export interface DS4TalentDataData extends DS4ItemDataDataBase {
     rank: DS4TalentRank;
 }
 
-interface DS4TalentRank extends ModifiableData<number> {
+export interface DS4TalentRank extends ModifiableData<number> {
     max: number;
 }
 
-interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable, DS4ItemDataDataRollable {
+export interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable {
     spellType: "spellcasting" | "targetedSpellcasting";
     bonus: string;
     spellCategory:
@@ -81,17 +77,23 @@ interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable
     scrollPrice: number;
 }
 
-interface DS4ShieldDataData
+export interface DS4ShieldDataData
     extends DS4ItemDataDataBase,
         DS4ItemDataDataPhysical,
         DS4ItemDataDataEquipable,
         DS4ItemDataDataProtective {}
-interface DS4EquipmentDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {}
-interface DS4LootDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical {}
-type DS4RacialAbilityDataData = DS4ItemDataDataBase;
-type DS4LanguageDataData = DS4ItemDataDataBase;
-type DS4AlphabetDataData = DS4ItemDataDataBase;
-interface DS4SpecialCreatureAbilityDataData extends DS4ItemDataDataBase {
+
+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;
 }
 
@@ -115,10 +117,6 @@ interface DS4ItemDataDataEquipable {
     equipped: boolean;
 }
 
-interface DS4ItemDataDataRollable {
-    rollable?: boolean;
-}
-
 interface DS4ItemDataDataProtective {
     armorValue: number;
 }
diff --git a/src/module/item/item-prepared-data.ts b/src/module/item/item-prepared-data.ts
new file mode 100644
index 00000000..981383f4
--- /dev/null
+++ b/src/module/item/item-prepared-data.ts
@@ -0,0 +1,80 @@
+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;
+
+type DS4WeaponPreparedData = DS4ItemDataHelper<DS4WeaponPreparedDataData, "weapon">;
+type DS4ArmorPreparedData = DS4ItemDataHelper<DS4ArmorPreparedDataData, "armor">;
+type DS4ShieldPreparedData = DS4ItemDataHelper<DS4ShieldPreparedDataData, "shield">;
+type DS4SpellPreparedData = DS4ItemDataHelper<DS4SpellPreparedDataData, "spell">;
+type DS4EquipmentPreparedData = DS4ItemDataHelper<DS4EquipmentPreparedDataData, "equipment">;
+type DS4LootPreparedData = DS4ItemDataHelper<DS4LootPreparedDataData, "loot">;
+type DS4TalentPreparedData = DS4ItemDataHelper<DS4TalentPreparedDataData, "talent">;
+type DS4RacialAbilityPreparedData = DS4ItemDataHelper<DS4RacialAbilityPreparedDataData, "racialAbility">;
+type DS4LanguagePreparedData = DS4ItemDataHelper<DS4LanguagePreparedDataData, "language">;
+type DS4AlphabetPreparedData = DS4ItemDataHelper<DS4AlphabetPreparedDataData, "alphabet">;
+type DS4SpecialCreatureAbilityPreparedData = DS4ItemDataHelper<
+    DS4SpecialCreatureAbilityPreparedDataData,
+    "specialCreatureAbility"
+>;
+
+interface DS4WeaponPreparedDataData extends DS4WeaponDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4ArmorPreparedDataData extends DS4ArmorDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4ShieldPreparedDataData extends DS4ShieldDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4SpellPreparedDataData extends DS4SpellDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4EquipmentPreparedDataData extends DS4EquipmentDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4LootPreparedDataData extends DS4LootDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4TalentPreparedDataData extends DS4TalentDataData, DS4ItemPreparedDataDataRollable {
+    rank: DS4TalentPreparedRank;
+}
+
+interface DS4TalentPreparedRank extends DS4TalentRank, HasTotal<number> {
+    max: number;
+}
+
+interface DS4RacialAbilityPreparedDataData extends DS4RacialAbilityDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4LanguagePreparedDataData extends DS4LanguageDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4AlphabetPreparedDataData extends DS4AlphabetDataData, DS4ItemPreparedDataDataRollable {}
+
+interface DS4SpecialCreatureAbilityPreparedDataData
+    extends DS4SpecialCreatureAbilityDataData,
+        DS4ItemPreparedDataDataRollable {}
+
+// templates
+
+interface DS4ItemPreparedDataDataRollable {
+    rollable: boolean;
+}
diff --git a/src/module/item/item.ts b/src/module/item/item.ts
index 07b5dc47..1c93e68a 100644
--- a/src/module/item/item.ts
+++ b/src/module/item/item.ts
@@ -3,11 +3,12 @@ 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";
 
 /**
  * The Item class for DS4
  */
-export class DS4Item extends Item<DS4ItemData> {
+export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
     /**
      * @override
      */
@@ -23,6 +24,8 @@ export class DS4Item extends Item<DS4ItemData> {
         }
         if (this.data.type === "weapon" || this.data.type === "spell") {
             this.data.data.rollable = this.data.data.equipped;
+        } else {
+            this.data.data.rollable = false;
         }
     }
 
@@ -94,7 +97,7 @@ export class DS4Item extends Item<DS4ItemData> {
         const checkTargetNumber = (ownerDataData.combatValues[combatValue].total as number) + weaponBonus;
 
         await createCheckRoll(checkTargetNumber, {
-            rollMode: game.settings.get("core", "rollMode"),
+            rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream
             maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
             minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
             flavor: game.i18n.format("DS4.ItemWeaponCheckFlavor", { actor: actor.name, weapon: this.name }),
@@ -138,7 +141,7 @@ export class DS4Item extends Item<DS4ItemData> {
         const checkTargetNumber = (ownerDataData.combatValues[spellType].total as number) + (spellBonus ?? 0);
 
         await createCheckRoll(checkTargetNumber, {
-            rollMode: game.settings.get("core", "rollMode"),
+            rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream
             maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
             minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
             flavor: game.i18n.format("DS4.ItemSpellCheckFlavor", { actor: actor.name, spell: this.name }),
diff --git a/src/module/migrations.ts b/src/module/migrations.ts
index e9a157c7..525400c5 100644
--- a/src/module/migrations.ts
+++ b/src/module/migrations.ts
@@ -9,7 +9,7 @@ async function migrate(): Promise<void> {
         return;
     }
 
-    const oldMigrationVersion: number = game.settings.get("ds4", "systemMigrationVersion");
+    const oldMigrationVersion = game.settings.get("ds4", "systemMigrationVersion");
 
     const targetMigrationVersion = migrations.length;
 
diff --git a/src/module/rolls/check-factory.ts b/src/module/rolls/check-factory.ts
index 5498acab..475c50fc 100644
--- a/src/module/rolls/check-factory.ts
+++ b/src/module/rolls/check-factory.ts
@@ -79,7 +79,7 @@ export async function createCheckRoll(
     const newOptions: Partial<DS4CheckFactoryOptions> = {
         maximumCoupResult: gmModifierData.maximumCoupResult ?? options.maximumCoupResult,
         minimumFumbleResult: gmModifierData.minimumFumbleResult ?? options.minimumFumbleResult,
-        useSlayingDice: game.settings.get("ds4", "useSlayingDiceForAutomatedChecks") ?? false,
+        useSlayingDice: game.settings.get("ds4", "useSlayingDiceForAutomatedChecks"),
         rollMode: gmModifierData.rollMode ?? options.rollMode,
         flavor: options.flavor,
     };
diff --git a/yarn.lock b/yarn.lock
index 21e5aac0..e2748004 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -647,9 +647,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@league-of-foundry-developers/foundry-vtt-types@npm:^0.7.9-3":
-  version: 0.7.9-3
-  resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.7.9-3"
+"@league-of-foundry-developers/foundry-vtt-types@npm:^0.7.9-4":
+  version: 0.7.9-4
+  resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.7.9-4"
   dependencies:
     "@types/howler": 2.2.1
     "@types/jquery": 3.5.1
@@ -659,7 +659,7 @@ __metadata:
     pixi.js: 5.3.4
     tinymce: 5.6.2
     typescript: ^4.1.4
-  checksum: 75524c7aa78ddb77cad1a9d041af30ae5bbd708f5b26568dabbb3d913a4643aefcc6f2ed80e1e76b3c17050579665eab155f035f840db6397691cf68eeee9b3f
+  checksum: 9aeee3d49c20dd69e736abd50347e43f5cfdcfdca7fba5af9fb321250cc3ca78758daed14fe6328005f3b606ed0ace49bf2562d7262912c1461132718ab793b8
   languageName: node
   linkType: hard
 
@@ -2891,7 +2891,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "dungeonslayers4@workspace:."
   dependencies:
-    "@league-of-foundry-developers/foundry-vtt-types": ^0.7.9-3
+    "@league-of-foundry-developers/foundry-vtt-types": ^0.7.9-4
     "@rollup/plugin-node-resolve": ^11.2.0
     "@types/fs-extra": ^9.0.8
     "@types/jest": ^26.0.20

From 17633ced68f1753f7d2d428249a0ffb6060e88f7 Mon Sep 17 00:00:00 2001
From: Johannes Loher <johannes.loher@fg4f.de>
Date: Mon, 22 Mar 2021 10:18:43 +0100
Subject: [PATCH 2/3] Separate types for data and _data for DS4Actor

---
 src/module/actor/actor-data.ts          | 64 +++++++++++-----------
 src/module/actor/actor-prepared-data.ts | 69 ++++++++++++++++++++++++
 src/module/actor/actor.ts               | 15 +++---
 src/module/actor/sheets/actor-sheet.ts  | 13 +++--
 src/module/common/common-data.ts        | 27 +++++-----
 src/module/item/item-data.ts            | 70 +++++++++++++------------
 src/module/item/item-prepared-data.ts   | 18 +++----
 7 files changed, 174 insertions(+), 102 deletions(-)
 create mode 100644 src/module/actor/actor-prepared-data.ts

diff --git a/src/module/actor/actor-data.ts b/src/module/actor/actor-data.ts
index 10ebedcf..dffd84d9 100644
--- a/src/module/actor/actor-data.ts
+++ b/src/module/actor/actor-data.ts
@@ -1,4 +1,4 @@
-import { ModifiableDataTotal, ResourceData, UsableResource } from "../common/common-data";
+import { ModifiableData, ModifiableDataBase, ResourceData, UsableResource } from "../common/common-data";
 import { DS4 } from "../config";
 import { DS4ItemData } from "../item/item-data";
 
@@ -6,50 +6,48 @@ export type DS4ActorData = DS4CharacterData | DS4CreatureData;
 
 type ActorType = keyof typeof DS4.i18n.actorTypes;
 
-interface DS4ActorDataHelper<T, U extends ActorType> extends Actor.Data<T, DS4ItemData> {
+export interface DS4ActorDataHelper<T, U extends ActorType> extends Actor.Data<T, DS4ItemData> {
     type: U;
 }
 
 type DS4CharacterData = DS4ActorDataHelper<DS4CharacterDataData, "character">;
 type DS4CreatureData = DS4ActorDataHelper<DS4CreatureDataData, "creature">;
 
+// templates
+
 interface DS4ActorDataDataBase {
     attributes: DS4ActorDataDataAttributes;
     traits: DS4ActorDataDataTraits;
     combatValues: DS4ActorDataDataCombatValues;
-    rolling: DS4ActorDataDataRolling;
 }
 
 interface DS4ActorDataDataAttributes {
-    body: ModifiableDataTotal<number>;
-    mobility: ModifiableDataTotal<number>;
-    mind: ModifiableDataTotal<number>;
+    body: ModifiableDataBase<number>;
+    mobility: ModifiableDataBase<number>;
+    mind: ModifiableDataBase<number>;
 }
 
 interface DS4ActorDataDataTraits {
-    strength: ModifiableDataTotal<number>;
-    constitution: ModifiableDataTotal<number>;
-    agility: ModifiableDataTotal<number>;
-    dexterity: ModifiableDataTotal<number>;
-    intellect: ModifiableDataTotal<number>;
-    aura: ModifiableDataTotal<number>;
+    strength: ModifiableDataBase<number>;
+    constitution: ModifiableDataBase<number>;
+    agility: ModifiableDataBase<number>;
+    dexterity: ModifiableDataBase<number>;
+    intellect: ModifiableDataBase<number>;
+    aura: ModifiableDataBase<number>;
 }
 
 interface DS4ActorDataDataCombatValues {
     hitPoints: ResourceData<number>;
-    defense: ModifiableDataTotal<number>;
-    initiative: ModifiableDataTotal<number>;
-    movement: ModifiableDataTotal<number>;
-    meleeAttack: ModifiableDataTotal<number>;
-    rangedAttack: ModifiableDataTotal<number>;
-    spellcasting: ModifiableDataTotal<number>;
-    targetedSpellcasting: ModifiableDataTotal<number>;
+    defense: ModifiableData<number>;
+    initiative: ModifiableData<number>;
+    movement: ModifiableData<number>;
+    meleeAttack: ModifiableData<number>;
+    rangedAttack: ModifiableData<number>;
+    spellcasting: ModifiableData<number>;
+    targetedSpellcasting: ModifiableData<number>;
 }
 
-interface DS4ActorDataDataRolling {
-    maximumCoupResult?: number;
-    minimumFumbleResult?: number;
-}
+// types
 
 interface DS4CharacterDataData extends DS4ActorDataDataBase {
     baseInfo: DS4CharacterDataDataBaseInfo;
@@ -58,25 +56,25 @@ interface DS4CharacterDataData extends DS4ActorDataDataBase {
     profile: DS4CharacterDataDataProfile;
     currency: DS4CharacterDataDataCurrency;
 }
-interface DS4CharacterDataDataBaseInfo {
+export interface DS4CharacterDataDataBaseInfo {
     race: string;
     class: string;
     heroClass: string;
     culture: string;
 }
-interface DS4CharacterDataDataProgression {
+export interface DS4CharacterDataDataProgression {
     level: number;
     experiencePoints: number;
     talentPoints: UsableResource<number>;
     progressPoints: UsableResource<number>;
 }
 
-interface DS4CharacterDataDataLanguage {
+export interface DS4CharacterDataDataLanguage {
     languages: string;
     alphabets: string;
 }
 
-interface DS4CharacterDataDataProfile {
+export interface DS4CharacterDataDataProfile {
     biography: string;
     gender: string;
     birthday: string;
@@ -89,7 +87,7 @@ interface DS4CharacterDataDataProfile {
     specialCharacteristics: string;
 }
 
-interface DS4CharacterDataDataCurrency {
+export interface DS4CharacterDataDataCurrency {
     gold: number;
     silver: number;
     copper: number;
@@ -99,11 +97,7 @@ interface DS4CreatureDataData extends DS4ActorDataDataBase {
     baseInfo: DS4CreatureDataDataBaseInfo;
 }
 
-type CreatureType = "animal" | "construct" | "humanoid" | "magicalEntity" | "plantBeing" | "undead";
-
-type SizeCategory = "tiny" | "small" | "normal" | "large" | "huge" | "colossal";
-
-interface DS4CreatureDataDataBaseInfo {
+export interface DS4CreatureDataDataBaseInfo {
     loot: string;
     foeFactor: number;
     creatureType: CreatureType;
@@ -111,3 +105,7 @@ interface DS4CreatureDataDataBaseInfo {
     experiencePoints: number;
     description: string;
 }
+
+type CreatureType = "animal" | "construct" | "humanoid" | "magicalEntity" | "plantBeing" | "undead";
+
+type SizeCategory = "tiny" | "small" | "normal" | "large" | "huge" | "colossal";
diff --git a/src/module/actor/actor-prepared-data.ts b/src/module/actor/actor-prepared-data.ts
new file mode 100644
index 00000000..fcbe0139
--- /dev/null
+++ b/src/module/actor/actor-prepared-data.ts
@@ -0,0 +1,69 @@
+import { ModifiableDataBaseTotal, ResourceDataBaseTotalMax } from "../common/common-data";
+import {
+    DS4ActorDataHelper,
+    DS4CharacterDataDataBaseInfo,
+    DS4CharacterDataDataCurrency,
+    DS4CharacterDataDataLanguage,
+    DS4CharacterDataDataProfile,
+    DS4CharacterDataDataProgression,
+    DS4CreatureDataDataBaseInfo,
+} from "./actor-data";
+
+export type DS4ActorPreparedData = DS4CharacterPreparedData | DS4CreaturePreparedData;
+
+type DS4CharacterPreparedData = DS4ActorDataHelper<DS4CharacterPreparedDataData, "character">;
+type DS4CreaturePreparedData = DS4ActorDataHelper<DS4CreaturePreparedDataData, "creature">;
+
+// templates
+
+interface DS4ActorPreparedDataDataBase {
+    attributes: DS4ActorPreparedDataDataAttributes;
+    traits: DS4ActorPreparedDataDataTraits;
+    combatValues: DS4ActorPreparedDataDataCombatValues;
+    rolling: DS4ActorPreparedDataDataRolling;
+}
+
+interface DS4ActorPreparedDataDataAttributes {
+    body: ModifiableDataBaseTotal<number>;
+    mobility: ModifiableDataBaseTotal<number>;
+    mind: ModifiableDataBaseTotal<number>;
+}
+
+interface DS4ActorPreparedDataDataTraits {
+    strength: ModifiableDataBaseTotal<number>;
+    constitution: ModifiableDataBaseTotal<number>;
+    agility: ModifiableDataBaseTotal<number>;
+    dexterity: ModifiableDataBaseTotal<number>;
+    intellect: ModifiableDataBaseTotal<number>;
+    aura: ModifiableDataBaseTotal<number>;
+}
+
+interface DS4ActorPreparedDataDataCombatValues {
+    hitPoints: ResourceDataBaseTotalMax<number>;
+    defense: ModifiableDataBaseTotal<number>;
+    initiative: ModifiableDataBaseTotal<number>;
+    movement: ModifiableDataBaseTotal<number>;
+    meleeAttack: ModifiableDataBaseTotal<number>;
+    rangedAttack: ModifiableDataBaseTotal<number>;
+    spellcasting: ModifiableDataBaseTotal<number>;
+    targetedSpellcasting: ModifiableDataBaseTotal<number>;
+}
+
+interface DS4ActorPreparedDataDataRolling {
+    maximumCoupResult: number;
+    minimumFumbleResult: number;
+}
+
+// types
+
+interface DS4CharacterPreparedDataData extends DS4ActorPreparedDataDataBase {
+    baseInfo: DS4CharacterDataDataBaseInfo;
+    progression: DS4CharacterDataDataProgression;
+    language: DS4CharacterDataDataLanguage;
+    profile: DS4CharacterDataDataProfile;
+    currency: DS4CharacterDataDataCurrency;
+}
+
+interface DS4CreaturePreparedDataData extends DS4ActorPreparedDataDataBase {
+    baseInfo: DS4CreatureDataDataBaseInfo;
+}
diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts
index b89f03e6..bccfe3c3 100644
--- a/src/module/actor/actor.ts
+++ b/src/module/actor/actor.ts
@@ -1,16 +1,17 @@
-import { ModifiableDataTotal } from "../common/common-data";
+import { ModifiableDataBaseTotal } from "../common/common-data";
 import { DS4 } from "../config";
 import { DS4Item } from "../item/item";
 import { ItemType } from "../item/item-data";
 import { DS4ActorData } from "./actor-data";
+import { DS4ActorPreparedData } from "./actor-prepared-data";
 
 /**
  * The Actor class for DS4
  */
-export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
+export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData> {
     /** @override */
     prepareData(): void {
-        this.data = duplicate(this._data) as DS4ActorData;
+        this.data = duplicate(this._data) as DS4ActorPreparedData;
         if (!this.data.img) this.data.img = CONST.DEFAULT_TOKEN;
         if (!this.data.name) this.data.name = "New " + this.entity;
         this.prepareBaseData();
@@ -32,11 +33,13 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
 
         const attributes = data.data.attributes;
         Object.values(attributes).forEach(
-            (attribute: ModifiableDataTotal<number>) => (attribute.total = attribute.base + attribute.mod),
+            (attribute: ModifiableDataBaseTotal<number>) => (attribute.total = attribute.base + attribute.mod),
         );
 
         const traits = data.data.traits;
-        Object.values(traits).forEach((trait: ModifiableDataTotal<number>) => (trait.total = trait.base + trait.mod));
+        Object.values(traits).forEach(
+            (trait: ModifiableDataBaseTotal<number>) => (trait.total = trait.base + trait.mod),
+        );
     }
 
     applyActiveEffectsToBaseData(): void {
@@ -184,7 +187,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
             (data.attributes.mind.total ?? 0) + (data.traits.dexterity.total ?? 0) - armorValueOfEquippedItems;
 
         Object.values(data.combatValues).forEach(
-            (combatValue: ModifiableDataTotal<number>) => (combatValue.total = combatValue.base + combatValue.mod),
+            (combatValue: ModifiableDataBaseTotal<number>) => (combatValue.total = combatValue.base + combatValue.mod),
         );
     }
 
diff --git a/src/module/actor/sheets/actor-sheet.ts b/src/module/actor/sheets/actor-sheet.ts
index 069fce43..dd8dfd47 100644
--- a/src/module/actor/sheets/actor-sheet.ts
+++ b/src/module/actor/sheets/actor-sheet.ts
@@ -1,4 +1,4 @@
-import { ModifiableMaybeData } from "../../common/common-data";
+import { ModifiableDataBaseTotal } from "../../common/common-data";
 import { DS4 } from "../../config";
 import { DS4Item } from "../../item/item";
 import { DS4ItemData } from "../../item/item-data";
@@ -75,17 +75,16 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
 
     protected _addTooltipsToData(data: ActorSheet.Data<DS4Actor>): ActorSheet.Data<DS4Actor> {
         const valueGroups = [data.data.attributes, data.data.traits, data.data.combatValues];
+
         valueGroups.forEach((valueGroup) => {
-            Object.values(valueGroup).forEach(
-                (attribute: ModifiableMaybeData<number | null> & { tooltip?: string }) => {
-                    attribute.tooltip = this._getTooltipForValue(attribute);
-                },
-            );
+            Object.values(valueGroup).forEach((attribute: ModifiableDataBaseTotal<number> & { tooltip?: string }) => {
+                attribute.tooltip = this._getTooltipForValue(attribute);
+            });
         });
         return data;
     }
 
-    protected _getTooltipForValue(value: ModifiableMaybeData<number | null>): string {
+    protected _getTooltipForValue(value: ModifiableDataBaseTotal<number>): string {
         return `${value.base} (${game.i18n.localize("DS4.TooltipBaseValue")}) + ${value.mod} (${game.i18n.localize(
             "DS4.TooltipModifier",
         )}) ➞ ${game.i18n.localize("DS4.TooltipEffects")} ➞ ${value.total}`;
diff --git a/src/module/common/common-data.ts b/src/module/common/common-data.ts
index 5956d6f1..8850062f 100644
--- a/src/module/common/common-data.ts
+++ b/src/module/common/common-data.ts
@@ -1,29 +1,30 @@
 export interface ModifiableData<T> {
-    base: T;
     mod: T;
 }
 
+export interface HasBase<T> {
+    base: T;
+}
+export interface ModifiableDataBase<T> extends ModifiableData<T>, HasBase<T> {}
+
 export interface HasTotal<T> {
     total: T;
 }
 
-export interface ModifiableDataTotal<T> {
-    base: T;
-    mod: T;
-    total?: T;
-}
+export interface ModifiableDataTotal<T> extends ModifiableData<T>, HasTotal<T> {}
 
-export interface ModifiableMaybeData<T> {
-    base?: T;
-    mod: T;
-    total?: T;
-}
+export interface ModifiableDataBaseTotal<T> extends ModifiableDataBase<T>, HasTotal<T> {}
 
-export interface ResourceData<T> extends ModifiableMaybeData<T> {
+export interface ResourceData<T> extends ModifiableData<T> {
     value: T;
-    max?: T;
 }
 
+export interface HasMax<T> {
+    max: T;
+}
+
+export interface ResourceDataBaseTotalMax<T> extends ResourceData<T>, HasBase<T>, HasTotal<T>, HasMax<T> {}
+
 export interface UsableResource<T> {
     total: T;
     used: T;
diff --git a/src/module/item/item-data.ts b/src/module/item/item-data.ts
index fbae27e3..7fa85fab 100644
--- a/src/module/item/item-data.ts
+++ b/src/module/item/item-data.ts
@@ -1,4 +1,4 @@
-import { ModifiableData } from "../common/common-data";
+import { ModifiableDataBase } from "../common/common-data";
 import { DS4 } from "../config";
 
 export type ItemType = keyof typeof DS4.i18n.itemTypes;
@@ -32,7 +32,38 @@ type DS4LanguageData = DS4ItemDataHelper<DS4LanguageDataData, "language">;
 type DS4AlphabetData = DS4ItemDataHelper<DS4AlphabetDataData, "alphabet">;
 type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbilityDataData, "specialCreatureAbility">;
 
-export type AttackType = keyof typeof DS4["i18n"]["attackTypes"];
+// templates
+
+interface DS4ItemDataDataBase {
+    description: string;
+}
+interface DS4ItemDataDataPhysical {
+    quantity: number;
+    price: number;
+    availability: "hamlet" | "village" | "city" | "elves" | "dwarves" | "nowhere" | "unset";
+    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;
+}
+
+interface UnitData<UnitType> {
+    value: string;
+    unit: UnitType;
+}
+type TemporalUnit = "rounds" | "minutes" | "hours" | "days" | "custom";
+type DistanceUnit = "meter" | "kilometer" | "custom";
+
+// types
 
 export interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {
     attackType: AttackType;
@@ -40,6 +71,8 @@ export interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataP
     opponentDefense: number;
 }
 
+export type AttackType = keyof typeof DS4["i18n"]["attackTypes"];
+
 export interface DS4ArmorDataData
     extends DS4ItemDataDataBase,
         DS4ItemDataDataPhysical,
@@ -53,7 +86,7 @@ export interface DS4TalentDataData extends DS4ItemDataDataBase {
     rank: DS4TalentRank;
 }
 
-export interface DS4TalentRank extends ModifiableData<number> {
+export interface DS4TalentRank extends ModifiableDataBase<number> {
     max: number;
 }
 
@@ -96,34 +129,3 @@ export type DS4AlphabetDataData = DS4ItemDataDataBase;
 export interface DS4SpecialCreatureAbilityDataData extends DS4ItemDataDataBase {
     experiencePoints: number;
 }
-
-// templates
-
-interface DS4ItemDataDataBase {
-    description: string;
-}
-interface DS4ItemDataDataPhysical {
-    quantity: number;
-    price: number;
-    availability: "hamlet" | "village" | "city" | "elves" | "dwarves" | "nowhere" | "unset";
-    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;
-}
-
-interface UnitData<UnitType> {
-    value: string;
-    unit: UnitType;
-}
-type TemporalUnit = "rounds" | "minutes" | "hours" | "days" | "custom";
-type DistanceUnit = "meter" | "kilometer" | "custom";
diff --git a/src/module/item/item-prepared-data.ts b/src/module/item/item-prepared-data.ts
index 981383f4..4dede57f 100644
--- a/src/module/item/item-prepared-data.ts
+++ b/src/module/item/item-prepared-data.ts
@@ -43,6 +43,14 @@ type DS4SpecialCreatureAbilityPreparedData = DS4ItemDataHelper<
     "specialCreatureAbility"
 >;
 
+// templates
+
+interface DS4ItemPreparedDataDataRollable {
+    rollable: boolean;
+}
+
+//types
+
 interface DS4WeaponPreparedDataData extends DS4WeaponDataData, DS4ItemPreparedDataDataRollable {}
 
 interface DS4ArmorPreparedDataData extends DS4ArmorDataData, DS4ItemPreparedDataDataRollable {}
@@ -59,9 +67,7 @@ interface DS4TalentPreparedDataData extends DS4TalentDataData, DS4ItemPreparedDa
     rank: DS4TalentPreparedRank;
 }
 
-interface DS4TalentPreparedRank extends DS4TalentRank, HasTotal<number> {
-    max: number;
-}
+interface DS4TalentPreparedRank extends DS4TalentRank, HasTotal<number> {}
 
 interface DS4RacialAbilityPreparedDataData extends DS4RacialAbilityDataData, DS4ItemPreparedDataDataRollable {}
 
@@ -72,9 +78,3 @@ interface DS4AlphabetPreparedDataData extends DS4AlphabetDataData, DS4ItemPrepar
 interface DS4SpecialCreatureAbilityPreparedDataData
     extends DS4SpecialCreatureAbilityDataData,
         DS4ItemPreparedDataDataRollable {}
-
-// templates
-
-interface DS4ItemPreparedDataDataRollable {
-    rollable: boolean;
-}

From cb5ad74a71f2b1dc7b5dad16f0d74da32cb93105 Mon Sep 17 00:00:00 2001
From: Johannes Loher <johannes.loher@fg4f.de>
Date: Mon, 22 Mar 2021 10:28:37 +0100
Subject: [PATCH 3/3] Remove some unnecessary casts and fallbacks

---
 src/module/actor/actor.ts | 20 +++++++++-----------
 src/module/item/item.ts   |  4 ++--
 2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts
index bccfe3c3..a8515444 100644
--- a/src/module/actor/actor.ts
+++ b/src/module/actor/actor.ts
@@ -75,7 +75,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
 
                 return changes.concat(
                     e.data.changes.filter(predicate).flatMap((c) => {
-                        const duplicatedChange = duplicate(c) as ActiveEffect.Change;
+                        const duplicatedChange = duplicate(c);
                         duplicatedChange.priority = duplicatedChange.priority ?? duplicatedChange.mode * 10;
                         return Array(factor).fill({
                             ...duplicatedChange,
@@ -172,19 +172,17 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
         const data = this.data.data;
         const armorValueOfEquippedItems = this._calculateArmorValueOfEquippedItems();
 
-        data.combatValues.hitPoints.base =
-            (data.attributes.body.total ?? 0) + (data.traits.constitution.total ?? 0) + 10;
+        data.combatValues.hitPoints.base = data.attributes.body.total + data.traits.constitution.total + 10;
         data.combatValues.defense.base =
-            (data.attributes.body.total ?? 0) + (data.traits.constitution.total ?? 0) + armorValueOfEquippedItems;
-        data.combatValues.initiative.base = (data.attributes.mobility.total ?? 0) + (data.traits.agility.total ?? 0);
-        data.combatValues.movement.base = (data.attributes.mobility.total ?? 0) / 2 + 1;
-        data.combatValues.meleeAttack.base = (data.attributes.body.total ?? 0) + (data.traits.strength.total ?? 0);
-        data.combatValues.rangedAttack.base =
-            (data.attributes.mobility.total ?? 0) + (data.traits.dexterity.total ?? 0);
+            data.attributes.body.total + data.traits.constitution.total + armorValueOfEquippedItems;
+        data.combatValues.initiative.base = data.attributes.mobility.total + data.traits.agility.total;
+        data.combatValues.movement.base = data.attributes.mobility.total / 2 + 1;
+        data.combatValues.meleeAttack.base = data.attributes.body.total + data.traits.strength.total;
+        data.combatValues.rangedAttack.base = data.attributes.mobility.total + data.traits.dexterity.total;
         data.combatValues.spellcasting.base =
-            (data.attributes.mind.total ?? 0) + (data.traits.aura.total ?? 0) - armorValueOfEquippedItems;
+            data.attributes.mind.total + data.traits.aura.total - armorValueOfEquippedItems;
         data.combatValues.targetedSpellcasting.base =
-            (data.attributes.mind.total ?? 0) + (data.traits.dexterity.total ?? 0) - armorValueOfEquippedItems;
+            data.attributes.mind.total + data.traits.dexterity.total - armorValueOfEquippedItems;
 
         Object.values(data.combatValues).forEach(
             (combatValue: ModifiableDataBaseTotal<number>) => (combatValue.total = combatValue.base + combatValue.mod),
diff --git a/src/module/item/item.ts b/src/module/item/item.ts
index 1c93e68a..68a1ffb5 100644
--- a/src/module/item/item.ts
+++ b/src/module/item/item.ts
@@ -94,7 +94,7 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
         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 as number) + weaponBonus;
+        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
@@ -138,7 +138,7 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
             );
         }
         const spellType = this.data.data.spellType;
-        const checkTargetNumber = (ownerDataData.combatValues[spellType].total as number) + (spellBonus ?? 0);
+        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