More WIP on 0.8.x migration

This commit is contained in:
Johannes Loher 2021-06-30 03:53:52 +02:00
parent ef01698178
commit 6b39284164
16 changed files with 427 additions and 321 deletions

View file

@ -5,12 +5,18 @@
import { ModifiableDataBaseTotal } from "../common/common-data"; import { ModifiableDataBaseTotal } from "../common/common-data";
import { DS4 } from "../config"; import { DS4 } from "../config";
import { DS4Item } from "../item/item"; import { ItemType } from "../item/item-data-source";
import { ItemType } from "../item/item-data"; import { DS4ArmorDataProperties, DS4ShieldDataProperties } from "../item/item-data-properties";
import { DS4ArmorPreparedData, DS4ShieldPreparedData } from "../item/item-prepared-data";
import { createCheckRoll } from "../rolls/check-factory"; import { createCheckRoll } from "../rolls/check-factory";
import { isAttribute, isTrait } from "./actor-data-source"; import { isAttribute, isTrait } from "./actor-data-source";
import { Check } from "./actor-data-properties"; import { Check } from "./actor-data-properties";
import { DS4Item } from "../item/item";
declare global {
interface DocumentClassConfig {
Actor: typeof DS4Actor;
}
}
/** /**
* The Actor class for DS4 * The Actor class for DS4
@ -84,9 +90,9 @@ export class DS4Actor extends Actor {
(changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[], e) => { (changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[], e) => {
if (e.data.disabled) return changes; if (e.data.disabled) return changes;
const item = this.getOriginatingItemOfActiveEffect(e); const item = this.getOriginatingItemOfActiveEffect(e);
if (item?.isNonEquippedEuipable()) return changes; // TODO: DS4Item if (item?.isNonEquippedEuipable()) return changes;
const factor = item?.activeEffectFactor ?? 1; // TODO: DS4Item const factor = item?.activeEffectFactor ?? 1;
const newChanges = e.data.changes.filter(predicate).flatMap((c) => { const newChanges = e.data.changes.filter(predicate).flatMap((c) => {
const changeSource = c.toObject(); const changeSource = c.toObject();
@ -110,8 +116,7 @@ export class DS4Actor extends Actor {
this.overrides = expandObject({ ...flattenObject(this.overrides), ...overrides }); this.overrides = expandObject({ ...flattenObject(this.overrides), ...overrides });
} }
// TODO: returns DS4Item | undefined protected getOriginatingItemOfActiveEffect(effect: ActiveEffect): DS4Item | undefined {
protected getOriginatingItemOfActiveEffect(effect: ActiveEffect): Item | undefined {
return this.items.find((item) => item.uuid === effect.data.origin); return this.items.find((item) => item.uuid === effect.data.origin);
} }
@ -272,7 +277,6 @@ export class DS4Actor extends Actor {
* Handle how changes to a Token attribute bar are applied to the Actor. * Handle how changes to a Token attribute bar are applied to the Actor.
* This only differs from the base implementation by also allowing negative values. * This only differs from the base implementation by also allowing negative values.
* @override * @override
* TODO: Adjust return type
*/ */
async modifyTokenAttribute( async modifyTokenAttribute(
attribute: string, attribute: string,

View file

@ -9,7 +9,7 @@ import { ModifiableDataBaseTotal } from "../../common/common-data";
import { DS4 } from "../../config"; import { DS4 } from "../../config";
import { getCanvas } from "../../helpers"; import { getCanvas } from "../../helpers";
import { DS4Item } from "../../item/item"; import { DS4Item } from "../../item/item";
import { DS4ItemData } from "../../item/item-data"; import { DS4ItemData } from "../../item/item-data-source";
import { getDS4Settings } from "../../settings"; import { getDS4Settings } from "../../settings";
import notifications from "../../ui/notifications"; import notifications from "../../ui/notifications";
import { DS4Actor } from "../actor"; import { DS4Actor } from "../actor";
@ -18,13 +18,13 @@ import { isCheck } from "../actor-data-properties";
/** /**
* The base Sheet class for all DS4 Actors * The base Sheet class for all DS4 Actors
*/ */
export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> { export class DS4ActorSheet extends ActorSheet<ActorSheet.Options> {
// TODO(types): Improve mergeObject in upstream so that it isn't necessary to provide all parameters (see https://github.com/League-of-Foundry-Developers/foundry-vtt-types/issues/272)
/** @override */ /** @override */
static get defaultOptions(): BaseEntitySheet.Options { static get defaultOptions(): ActorSheet.Options {
const superDefaultOptions = super.defaultOptions; // TODO: Improve
return mergeObject(superDefaultOptions, { // eslint-disable-next-line @typescript-eslint/ban-ts-comment
...superDefaultOptions, // @ts-ignore
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["ds4", "sheet", "actor"], classes: ["ds4", "sheet", "actor"],
height: 620, height: 620,
scrollY: [ scrollY: [
@ -105,7 +105,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
html.find(".item-edit").on("click", (ev) => { html.find(".item-edit").on("click", (ev) => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
const id = li.data("itemId"); const id = li.data("itemId");
const item = this.actor.getOwnedItem(id); const item = this.actor.getEmbeddedDocument("Item", id) as DS4Item; // TODO: Improve in upstream
if (!item) { if (!item) {
throw new Error(game.i18n.format("DS4.ErrorActorDoesNotHaveItem", { id, actor: this.actor.name })); throw new Error(game.i18n.format("DS4.ErrorActorDoesNotHaveItem", { id, actor: this.actor.name }));
} }
@ -118,7 +118,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
// Delete Inventory Item // Delete Inventory Item
html.find(".item-delete").on("click", (ev) => { html.find(".item-delete").on("click", (ev) => {
const li = $(ev.currentTarget).parents(".item"); const li = $(ev.currentTarget).parents(".item");
this.actor.deleteOwnedItem(li.data("itemId")); this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")]);
li.slideUp(200, () => this.render(false)); li.slideUp(200, () => this.render(false));
}); });
@ -133,23 +133,21 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
* Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset * Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
* @param event - The originating click event * @param event - The originating click event
*/ */
protected _onItemCreate(event: JQuery.ClickEvent): Promise<DS4ItemData> { protected _onItemCreate(event: JQuery.ClickEvent): void {
event.preventDefault(); event.preventDefault();
const header = event.currentTarget; const header = event.currentTarget;
// Get the type of item to create.
// Grab any data associated with this control.
const { type, ...data } = duplicate(header.dataset); const { type, ...data } = duplicate(header.dataset);
// Initialize a default name.
const name = `New ${type.capitalize()}`; const name = `New ${type.capitalize()}`;
// Prepare the item object.
const itemData = { const itemData = {
name: name, name: name,
type: type, type: type,
data: data, data: data,
}; };
// Finally, create the item! DS4Item.create(itemData, { parent: this.actor });
return this.actor.createOwnedItem(itemData);
} }
/** /**
@ -162,7 +160,8 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
ev.preventDefault(); ev.preventDefault();
const el: HTMLFormElement = $(ev.currentTarget).get(0); const el: HTMLFormElement = $(ev.currentTarget).get(0);
const id = $(ev.currentTarget).parents(".item").data("itemId"); const id = $(ev.currentTarget).parents(".item").data("itemId");
const item = duplicate(this.actor.getOwnedItem(id)); const item = this.actor.getEmbeddedDocument("Item", id) as DS4Item; // TODO: Improve in upstream
const itemObject = item.toObject();
const property: string | undefined = $(ev.currentTarget).data("property"); const property: string | undefined = $(ev.currentTarget).data("property");
// Early return: // Early return:
@ -175,8 +174,8 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
// Set new value // Set new value
const newValue = this.getValue(el); const newValue = this.getValue(el);
setProperty(item, property, newValue); setProperty(itemObject, property, newValue);
this.actor.updateOwnedItem(item); this.actor.updateEmbeddedDocuments("Item", [{ ...itemObject }]); // TODO: Improve in upstream
} }
/** /**
@ -241,7 +240,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
protected _onRollItem(event: JQuery.ClickEvent): void { protected _onRollItem(event: JQuery.ClickEvent): void {
event.preventDefault(); event.preventDefault();
const id = $(event.currentTarget).parents(".item").data("itemId"); const id = $(event.currentTarget).parents(".item").data("itemId");
const item = this.actor.getOwnedItem(id); const item = this.actor.getEmbeddedDocument("Item", id, { strict: true }) as DS4Item; // TODO: improve in upstream types
item.roll().catch((e) => notifications.error(e, { log: true })); item.roll().catch((e) => notifications.error(e, { log: true }));
} }
@ -277,14 +276,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Data<DS4Actor>> {
} }
/** @override */ /** @override */
protected async _onDropItem( protected async _onDropItem(event: DragEvent, data: ActorSheet.DropData.Item): Promise<unknown> {
event: DragEvent,
data: { type: "Item" } & (
| { data: DeepPartial<ActorSheet.OwnedItemData<DS4Actor>> }
| { pack: string }
| { id: string }
),
): Promise<boolean | undefined | ActorSheet.OwnedItemData<DS4Actor>> {
const item = await DS4Item.fromDropData(data); const item = await DS4Item.fromDropData(data);
if (item && !this.actor.canOwnItemType(item.data.type)) { if (item && !this.actor.canOwnItemType(item.data.type)) {
notifications.warn( notifications.warn(

View file

@ -27,6 +27,10 @@ export interface HasMax<T> {
max: T; max: T;
} }
export interface ModifiableDataBaseMax<T> extends ModifiableDataBase<T>, HasMax<T> {}
export interface ModifiableDataBaseTotalMax<T> extends ModifiableDataBaseMax<T>, HasTotal<T> {}
export interface ResourceDataBaseTotalMax<T> extends ResourceData<T>, HasBase<T>, HasTotal<T>, HasMax<T> {} export interface ResourceDataBaseTotalMax<T> extends ResourceData<T>, HasBase<T>, HasTotal<T>, HasMax<T> {}
export interface UsableResource<T> { export interface UsableResource<T> {

View file

@ -4,19 +4,18 @@
import { isCheck } from "../actor/actor-data-properties"; import { isCheck } from "../actor/actor-data-properties";
import { DS4Item } from "../item/item"; import { DS4Item } from "../item/item";
import { DS4ItemData } from "../item/item-data";
import { createRollCheckMacro } from "../macros/roll-check"; import { createRollCheckMacro } from "../macros/roll-check";
import { createRollItemMacro } from "../macros/roll-item"; import { createRollItemMacro } from "../macros/roll-item";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
export default function registerForHotbarDropHook(): void { export default function registerForHotbarDropHook(): void {
Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: { type: string } & Record<string, unknown>, slot: string) => { Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: HotbarDropData, slot: string) => {
switch (data.type) { switch (data.type) {
case "Item": { case "Item": {
if (!("data" in data)) { if (!isItemDropData(data) || !("data" in data)) {
return notifications.warn(game.i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems")); return notifications.warn(game.i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems"));
} }
const itemData = data.data as DS4ItemData; const itemData = data.data;
if (!DS4Item.rollableItemTypes.includes(itemData.type)) { if (!DS4Item.rollableItemTypes.includes(itemData.type)) {
return notifications.warn( return notifications.warn(
@ -38,3 +37,9 @@ export default function registerForHotbarDropHook(): void {
} }
}); });
} }
type HotbarDropData = ActorSheet.DropData.Item | ({ type: string } & Partial<Record<string, unknown>>);
function isItemDropData(dropData: HotbarDropData): dropData is ActorSheet.DropData.Item {
return dropData.type === "Item";
}

View file

@ -39,8 +39,8 @@ async function init() {
CONFIG.DS4 = DS4; CONFIG.DS4 = DS4;
// CONFIG.Actor.documentClass = DS4Actor; CONFIG.Actor.documentClass = DS4Actor;
// CONFIG.Item.documentClass = DS4Item; CONFIG.Item.documentClass = DS4Item;
CONFIG.Actor.typeLabels = DS4.i18n.actorTypes; CONFIG.Actor.typeLabels = DS4.i18n.actorTypes;
CONFIG.Item.typeLabels = DS4.i18n.itemTypes; CONFIG.Item.typeLabels = DS4.i18n.itemTypes;
@ -54,10 +54,10 @@ async function init() {
registerSystemSettings(); registerSystemSettings();
// Actors.unregisterSheet("core", ActorSheet); Actors.unregisterSheet("core", ActorSheet);
// Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true }); // Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true });
// Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true }); // Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
// Items.unregisterSheet("core", ItemSheet); Items.unregisterSheet("core", ItemSheet);
// Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true }); // Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
await registerHandlebarsPartials(); await registerHandlebarsPartials();

View file

@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { ModifiableDataBaseTotalMax } from "../common/common-data";
import {
DS4AlphabetDataSourceData,
DS4ArmorDataSourceData,
DS4EquipmentDataSourceData,
DS4LanguageDataSourceData,
DS4LootDataSourceData,
DS4RacialAbilityDataSourceData,
DS4ShieldDataSourceData,
DS4SpecialCreatureAbilityDataSourceData,
DS4SpellDataSourceData,
DS4TalentDataSourceData,
DS4WeaponDataSourceData,
} from "./item-data-source";
declare global {
interface DataConfig {
Item: DS4ItemDataProperties;
}
}
export type DS4ItemDataProperties =
| DS4WeaponDataProperties
| DS4ArmorDataProperties
| DS4ShieldDataProperties
| DS4SpellDataProperties
| DS4EquipmentDataProperties
| DS4LootDataProperties
| DS4TalentDataProperties
| DS4RacialAbilityDataProperties
| DS4LanguageDataProperties
| DS4AlphabetDataProperties
| DS4SpecialCreatureAbilityDataProperties;
export interface DS4WeaponDataProperties {
type: "weapon";
data: DS4WeaponDataPropertiesData;
}
export interface DS4ArmorDataProperties {
type: "armor";
data: DS4ArmorDataPropertiesData;
}
export interface DS4ShieldDataProperties {
type: "shield";
data: DS4ShieldDataPropertiesData;
}
export interface DS4SpellDataProperties {
type: "spell";
data: DS4SpellDataPropertiesData;
}
export interface DS4EquipmentDataProperties {
type: "equipment";
data: DS4EquipmentDataPropertiesData;
}
export interface DS4LootDataProperties {
type: "loot";
data: DS4LootDataPropertiesData;
}
export interface DS4TalentDataProperties {
type: "talent";
data: DS4TalentDataPropertiesData;
}
export interface DS4RacialAbilityDataProperties {
type: "racialAbility";
data: DS4RacialAbilityDataPropertiesData;
}
export interface DS4LanguageDataProperties {
type: "language";
data: DS4LanguageDataPropertiesData;
}
export interface DS4AlphabetDataProperties {
type: "alphabet";
data: DS4AlphabetDataPropertiesData;
}
export interface DS4SpecialCreatureAbilityDataProperties {
type: "specialCreatureAbility";
data: DS4SpecialCreatureAbilityDataPropertiesData;
}
// templates
interface DS4ItemDataPropertiesDataRollable {
rollable: boolean;
}
//types
interface DS4WeaponDataPropertiesData extends DS4WeaponDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4ArmorDataPropertiesData extends DS4ArmorDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4ShieldDataPropertiesData extends DS4ShieldDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4SpellDataPropertiesData extends DS4SpellDataSourceData, DS4ItemDataPropertiesDataRollable {
price: number | null;
}
interface DS4EquipmentDataPropertiesData extends DS4EquipmentDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4LootDataPropertiesData extends DS4LootDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4TalentDataPropertiesData extends DS4TalentDataSourceData, DS4ItemDataPropertiesDataRollable {
rank: ModifiableDataBaseTotalMax<number>;
}
interface DS4RacialAbilityDataPropertiesData
extends DS4RacialAbilityDataSourceData,
DS4ItemDataPropertiesDataRollable {}
interface DS4LanguageDataPropertiesData extends DS4LanguageDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4AlphabetDataPropertiesData extends DS4AlphabetDataSourceData, DS4ItemDataPropertiesDataRollable {}
interface DS4SpecialCreatureAbilityDataPropertiesData
extends DS4SpecialCreatureAbilityDataSourceData,
DS4ItemDataPropertiesDataRollable {}

View file

@ -0,0 +1,184 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
//
// SPDX-License-Identifier: MIT
import { ModifiableDataBaseMax } from "../common/common-data";
import { DS4 } from "../config";
declare global {
interface SourceConfig {
Item: DS4ItemDataSource;
}
}
export type ItemType = keyof typeof DS4.i18n.itemTypes;
export type DS4ItemDataSource =
| DS4WeaponDataSource
| DS4ArmorDataSource
| DS4ShieldDataSource
| DS4SpellDataSource
| DS4EquipmentDataSource
| DS4LootDataSource
| DS4TalentDataSource
| DS4RacialAbilityDataSource
| DS4LanguageDataSource
| DS4AlphabetDataSource
| DS4SpecialCreatureAbilityDataSource;
interface DS4WeaponDataSource {
type: "weapon";
data: DS4WeaponDataSourceData;
}
interface DS4ArmorDataSource {
type: "armor";
data: DS4ArmorDataSourceData;
}
interface DS4ShieldDataSource {
type: "shield";
data: DS4ShieldDataSourceData;
}
interface DS4SpellDataSource {
type: "spell";
data: DS4SpellDataSourceData;
}
interface DS4EquipmentDataSource {
type: "equipment";
data: DS4EquipmentDataSourceData;
}
interface DS4LootDataSource {
type: "loot";
data: DS4LootDataSourceData;
}
interface DS4TalentDataSource {
type: "talent";
data: DS4TalentDataSourceData;
}
interface DS4RacialAbilityDataSource {
type: "racialAbility";
data: DS4RacialAbilityDataSourceData;
}
interface DS4LanguageDataSource {
type: "language";
data: DS4LanguageDataSourceData;
}
interface DS4AlphabetDataSource {
type: "alphabet";
data: DS4AlphabetDataSourceData;
}
interface DS4SpecialCreatureAbilityDataSource {
type: "specialCreatureAbility";
data: DS4SpecialCreatureAbilityDataSourceData;
}
// templates
interface DS4ItemDataSourceDataBase {
description: string;
}
interface DS4ItemDataSourceDataPhysical {
quantity: number;
price: number;
availability: keyof typeof DS4.i18n.itemAvailabilities;
storageLocation: string;
}
export function isDS4ItemDataTypePhysical(input: foundry.data.ItemData["data"]): boolean {
return "quantity" in input && "price" in input && "availability" in input && "storageLocation" in input;
}
interface DS4ItemDataSourceDataEquipable {
equipped: boolean;
}
interface DS4ItemDataSourceDataProtective {
armorValue: number;
}
// types
export interface DS4WeaponDataSourceData
extends DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataPhysical,
DS4ItemDataSourceDataEquipable {
attackType: AttackType;
weaponBonus: number;
opponentDefense: number;
}
export type AttackType = keyof typeof DS4.i18n.attackTypes;
export interface DS4ArmorDataSourceData
extends DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataPhysical,
DS4ItemDataSourceDataEquipable,
DS4ItemDataSourceDataProtective {
armorMaterialType: keyof typeof DS4.i18n.armorMaterialTypes;
armorType: keyof typeof DS4.i18n.armorTypes;
}
export interface DS4ShieldDataSourceData
extends DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataPhysical,
DS4ItemDataSourceDataEquipable,
DS4ItemDataSourceDataProtective {}
export interface DS4SpellDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable {
spellType: keyof typeof DS4.i18n.spellTypes;
bonus: string;
spellCategory: keyof typeof DS4.i18n.spellCategories;
maxDistance: UnitData<DistanceUnit>;
effectRadius: UnitData<DistanceUnit>;
duration: UnitData<CustomTemporalUnit>;
cooldownDuration: UnitData<TemporalUnit>;
minimumLevels: {
healer: number | null;
wizard: number | null;
sorcerer: number | null;
};
}
export interface UnitData<UnitType> {
value: string;
unit: UnitType;
}
type DistanceUnit = keyof typeof DS4.i18n.distanceUnits;
type CustomTemporalUnit = keyof typeof DS4.i18n.customTemporalUnits;
export type TemporalUnit = keyof typeof DS4.i18n.temporalUnits;
export interface DS4EquipmentDataSourceData
extends DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataPhysical,
DS4ItemDataSourceDataEquipable {}
export interface DS4LootDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataPhysical {}
export interface DS4TalentDataSourceData extends DS4ItemDataSourceDataBase {
rank: ModifiableDataBaseMax<number>;
}
export type DS4RacialAbilityDataSourceData = DS4ItemDataSourceDataBase;
export type DS4LanguageDataSourceData = DS4ItemDataSourceDataBase;
export type DS4AlphabetDataSourceData = DS4ItemDataSourceDataBase;
export interface DS4SpecialCreatureAbilityDataSourceData extends DS4ItemDataSourceDataBase {
experiencePoints: number;
}

View file

@ -1,133 +0,0 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
// SPDX-FileCopyrightText: 2021 Oliver Rümpelein
// SPDX-FileCopyrightText: 2021 Gesina Schwalbe
//
// SPDX-License-Identifier: MIT
import { ModifiableDataBase } from "../common/common-data";
import { DS4 } from "../config";
export type ItemType = keyof typeof DS4.i18n.itemTypes;
export type DS4ItemData =
| DS4WeaponData
| DS4ArmorData
| DS4ShieldData
| DS4SpellData
| DS4EquipmentData
| DS4LootData
| DS4TalentData
| DS4RacialAbilityData
| DS4LanguageData
| DS4AlphabetData
| DS4SpecialCreatureAbilityData;
export interface DS4ItemDataHelper<T, U extends ItemType> extends Item.Data<T> {
type: U;
}
type DS4WeaponData = DS4ItemDataHelper<DS4WeaponDataData, "weapon">;
type DS4ArmorData = DS4ItemDataHelper<DS4ArmorDataData, "armor">;
type DS4ShieldData = DS4ItemDataHelper<DS4ShieldDataData, "shield">;
type DS4SpellData = DS4ItemDataHelper<DS4SpellDataData, "spell">;
type DS4EquipmentData = DS4ItemDataHelper<DS4EquipmentDataData, "equipment">;
type DS4LootData = DS4ItemDataHelper<DS4LootDataData, "loot">;
type DS4TalentData = DS4ItemDataHelper<DS4TalentDataData, "talent">;
type DS4RacialAbilityData = DS4ItemDataHelper<DS4RacialAbilityDataData, "racialAbility">;
type DS4LanguageData = DS4ItemDataHelper<DS4LanguageDataData, "language">;
type DS4AlphabetData = DS4ItemDataHelper<DS4AlphabetDataData, "alphabet">;
type DS4SpecialCreatureAbilityData = DS4ItemDataHelper<DS4SpecialCreatureAbilityDataData, "specialCreatureAbility">;
// templates
interface DS4ItemDataDataBase {
description: string;
}
interface DS4ItemDataDataPhysical {
quantity: number;
price: number;
availability: keyof typeof DS4.i18n.itemAvailabilities;
storageLocation: string;
}
export function isDS4ItemDataTypePhysical(input: DS4ItemData["data"]): boolean {
return "quantity" in input && "price" in input && "availability" in input && "storageLocation" in input;
}
interface DS4ItemDataDataEquipable {
equipped: boolean;
}
interface DS4ItemDataDataProtective {
armorValue: number;
}
export interface UnitData<UnitType> {
value: string;
unit: UnitType;
}
export type TemporalUnit = keyof typeof DS4.i18n.temporalUnits;
type CustomTemporalUnit = keyof typeof DS4.i18n.customTemporalUnits;
type DistanceUnit = keyof typeof DS4.i18n.distanceUnits;
// types
export interface DS4WeaponDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {
attackType: AttackType;
weaponBonus: number;
opponentDefense: number;
}
export type AttackType = keyof typeof DS4.i18n.attackTypes;
export interface DS4ArmorDataData
extends DS4ItemDataDataBase,
DS4ItemDataDataPhysical,
DS4ItemDataDataEquipable,
DS4ItemDataDataProtective {
armorMaterialType: keyof typeof DS4.i18n.armorMaterialTypes;
armorType: keyof typeof DS4.i18n.armorTypes;
}
export interface DS4TalentDataData extends DS4ItemDataDataBase {
rank: DS4TalentRank;
}
export interface DS4TalentRank extends ModifiableDataBase<number> {
max: number;
}
export interface DS4SpellDataData extends DS4ItemDataDataBase, DS4ItemDataDataEquipable {
spellType: keyof typeof DS4.i18n.spellTypes;
bonus: string;
spellCategory: keyof typeof DS4.i18n.spellCategories;
maxDistance: UnitData<DistanceUnit>;
effectRadius: UnitData<DistanceUnit>;
duration: UnitData<CustomTemporalUnit>;
cooldownDuration: UnitData<TemporalUnit>;
minimumLevels: {
healer: number | null;
wizard: number | null;
sorcerer: number | null;
};
}
export interface DS4ShieldDataData
extends DS4ItemDataDataBase,
DS4ItemDataDataPhysical,
DS4ItemDataDataEquipable,
DS4ItemDataDataProtective {}
export interface DS4EquipmentDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical, DS4ItemDataDataEquipable {}
export interface DS4LootDataData extends DS4ItemDataDataBase, DS4ItemDataDataPhysical {}
export type DS4RacialAbilityDataData = DS4ItemDataDataBase;
export type DS4LanguageDataData = DS4ItemDataDataBase;
export type DS4AlphabetDataData = DS4ItemDataDataBase;
export interface DS4SpecialCreatureAbilityDataData extends DS4ItemDataDataBase {
experiencePoints: number;
}

View file

@ -1,86 +0,0 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { HasTotal } from "../common/common-data";
import {
DS4AlphabetDataData,
DS4ArmorDataData,
DS4EquipmentDataData,
DS4ItemDataHelper,
DS4LanguageDataData,
DS4LootDataData,
DS4RacialAbilityDataData,
DS4ShieldDataData,
DS4SpecialCreatureAbilityDataData,
DS4SpellDataData,
DS4TalentDataData,
DS4TalentRank,
DS4WeaponDataData,
} from "./item-data";
export type DS4ItemPreparedData =
| DS4WeaponPreparedData
| DS4ArmorPreparedData
| DS4ShieldPreparedData
| DS4SpellPreparedData
| DS4EquipmentPreparedData
| DS4LootPreparedData
| DS4TalentPreparedData
| DS4RacialAbilityPreparedData
| DS4LanguagePreparedData
| DS4AlphabetPreparedData
| DS4SpecialCreatureAbilityPreparedData;
export type DS4WeaponPreparedData = DS4ItemDataHelper<DS4WeaponPreparedDataData, "weapon">;
export type DS4ArmorPreparedData = DS4ItemDataHelper<DS4ArmorPreparedDataData, "armor">;
export type DS4ShieldPreparedData = DS4ItemDataHelper<DS4ShieldPreparedDataData, "shield">;
export type DS4SpellPreparedData = DS4ItemDataHelper<DS4SpellPreparedDataData, "spell">;
export type DS4EquipmentPreparedData = DS4ItemDataHelper<DS4EquipmentPreparedDataData, "equipment">;
export type DS4LootPreparedData = DS4ItemDataHelper<DS4LootPreparedDataData, "loot">;
export type DS4TalentPreparedData = DS4ItemDataHelper<DS4TalentPreparedDataData, "talent">;
export type DS4RacialAbilityPreparedData = DS4ItemDataHelper<DS4RacialAbilityPreparedDataData, "racialAbility">;
export type DS4LanguagePreparedData = DS4ItemDataHelper<DS4LanguagePreparedDataData, "language">;
export type DS4AlphabetPreparedData = DS4ItemDataHelper<DS4AlphabetPreparedDataData, "alphabet">;
export type DS4SpecialCreatureAbilityPreparedData = DS4ItemDataHelper<
DS4SpecialCreatureAbilityPreparedDataData,
"specialCreatureAbility"
>;
// templates
interface DS4ItemPreparedDataDataRollable {
rollable: boolean;
}
//types
interface DS4WeaponPreparedDataData extends DS4WeaponDataData, DS4ItemPreparedDataDataRollable {}
interface DS4ArmorPreparedDataData extends DS4ArmorDataData, DS4ItemPreparedDataDataRollable {}
interface DS4ShieldPreparedDataData extends DS4ShieldDataData, DS4ItemPreparedDataDataRollable {}
interface DS4SpellPreparedDataData extends DS4SpellDataData, DS4ItemPreparedDataDataRollable {
price: number | null;
}
interface DS4EquipmentPreparedDataData extends DS4EquipmentDataData, DS4ItemPreparedDataDataRollable {}
interface DS4LootPreparedDataData extends DS4LootDataData, DS4ItemPreparedDataDataRollable {}
interface DS4TalentPreparedDataData extends DS4TalentDataData, DS4ItemPreparedDataDataRollable {
rank: DS4TalentPreparedRank;
}
interface DS4TalentPreparedRank extends DS4TalentRank, HasTotal<number> {}
interface DS4RacialAbilityPreparedDataData extends DS4RacialAbilityDataData, DS4ItemPreparedDataDataRollable {}
interface DS4LanguagePreparedDataData extends DS4LanguageDataData, DS4ItemPreparedDataDataRollable {}
interface DS4AlphabetPreparedDataData extends DS4AlphabetDataData, DS4ItemPreparedDataDataRollable {}
interface DS4SpecialCreatureAbilityPreparedDataData
extends DS4SpecialCreatureAbilityDataData,
DS4ItemPreparedDataDataRollable {}

View file

@ -7,17 +7,16 @@
import { DS4 } from "../config"; import { DS4 } from "../config";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
import { DS4Item } from "./item"; import { DS4Item } from "./item";
import { isDS4ItemDataTypePhysical } from "./item-data"; import { isDS4ItemDataTypePhysical } from "./item-data-source";
/** /**
* The Sheet class for DS4 Items * The Sheet class for DS4 Items
*/ */
export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> { export class DS4ItemSheet extends ItemSheet {
/** @override */ /** @override */
static get defaultOptions(): BaseEntitySheet.Options { static get defaultOptions(): ItemSheet.Options {
const superDefaultOptions = super.defaultOptions; const superDefaultOptions = super.defaultOptions;
return mergeObject(superDefaultOptions, { return mergeObject(superDefaultOptions, {
...superDefaultOptions,
width: 540, width: 540,
height: 400, height: 400,
classes: ["ds4", "sheet", "item"], classes: ["ds4", "sheet", "item"],
@ -45,11 +44,14 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> {
} }
/** @override */ /** @override */
setPosition(options: Partial<Application.Position> = {}): Application.Position & { height: number } { setPosition(options: Partial<Application.Position> = {}): (Application.Position & { height: number }) | undefined {
const position = super.setPosition(options); const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body"); if (position) {
const bodyHeight = position.height - 192; const sheetBody = this.element.find(".sheet-body");
sheetBody.css("height", bodyHeight); const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
}
return position; return position;
} }
@ -86,7 +88,7 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> {
} }
return effect.sheet.render(true); return effect.sheet.render(true);
case "delete": { case "delete": {
return this.item.deleteEmbeddedEntity("ActiveEffect", li.data("effectId")); return this.item.deleteEmbeddedDocuments("ActiveEffect", [li.data("effectId")]);
} }
} }
} }
@ -94,7 +96,7 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> {
/** /**
* Create a new ActiveEffect for the item using default data. * Create a new ActiveEffect for the item using default data.
*/ */
protected async _createActiveEffect(): Promise<ActiveEffect.Data> { protected async _createActiveEffect(): Promise<ActiveEffect | undefined> {
const label = `New Effect`; const label = `New Effect`;
const createData = { const createData = {
@ -104,7 +106,6 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Data<DS4Item>> {
transfer: true, transfer: true,
}; };
const effect = ActiveEffect.create(createData, this.item); return ActiveEffect.create(createData, { parent: this.item });
return effect.create({});
} }
} }

View file

@ -7,14 +7,19 @@ import { DS4Actor } from "../actor/actor";
import { DS4 } from "../config"; import { DS4 } from "../config";
import { createCheckRoll } from "../rolls/check-factory"; import { createCheckRoll } from "../rolls/check-factory";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
import { AttackType, DS4ItemData, ItemType } from "./item-data"; import { AttackType, ItemType } from "./item-data-source";
import { DS4ItemPreparedData } from "./item-prepared-data";
import { calculateSpellPrice } from "./type-specific-helpers/spell"; import { calculateSpellPrice } from "./type-specific-helpers/spell";
declare global {
interface DocumentClassConfig {
Item: typeof DS4Item;
}
}
/** /**
* The Item class for DS4 * The Item class for DS4
*/ */
export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> { export class DS4Item extends Item {
/** /**
* @override * @override
*/ */
@ -77,7 +82,7 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
} }
} }
protected async rollWeapon(this: this & { readonly isOwned: true }): Promise<void> { protected async rollWeapon(this: this & { readonly actor: DS4Actor }): Promise<void> {
if (!(this.data.type === "weapon")) { if (!(this.data.type === "weapon")) {
throw new Error( throw new Error(
game.i18n.format("DS4.ErrorWrongItemType", { game.i18n.format("DS4.ErrorWrongItemType", {
@ -99,21 +104,21 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
); );
} }
const actor = this.actor as unknown as DS4Actor; // TODO(types): Improve so that the concrete Actor type is known here const actor = this.actor;
const ownerDataData = actor.data.data; const ownerDataData = actor.data.data;
const weaponBonus = this.data.data.weaponBonus; const weaponBonus = this.data.data.weaponBonus;
const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType); const combatValue = await this.getCombatValueKeyForAttackType(this.data.data.attackType);
const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus; const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus;
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream rollMode: game.settings.get("core", "rollMode"),
maximumCoupResult: ownerDataData.rolling.maximumCoupResult, maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
flavor: game.i18n.format("DS4.ItemWeaponCheckFlavor", { actor: actor.name, weapon: this.name }), flavor: game.i18n.format("DS4.ItemWeaponCheckFlavor", { actor: actor.name, weapon: this.name }),
}); });
} }
protected async rollSpell(): Promise<void> { protected async rollSpell(this: this & { readonly actor: DS4Actor }): Promise<void> {
if (!(this.data.type === "spell")) { if (!(this.data.type === "spell")) {
throw new Error( throw new Error(
game.i18n.format("DS4.ErrorWrongItemType", { game.i18n.format("DS4.ErrorWrongItemType", {
@ -135,7 +140,7 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
); );
} }
const actor = this.actor as unknown as DS4Actor; // TODO(types): Improve so that the concrete Actor type is known here const actor = this.actor;
const ownerDataData = actor.data.data; const ownerDataData = actor.data.data;
const spellBonus = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined; const spellBonus = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined;
if (spellBonus === undefined) { if (spellBonus === undefined) {
@ -150,7 +155,7 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellBonus ?? 0); const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellBonus ?? 0);
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream rollMode: game.settings.get("core", "rollMode"),
maximumCoupResult: ownerDataData.rolling.maximumCoupResult, maximumCoupResult: ownerDataData.rolling.maximumCoupResult,
minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult, minimumFumbleResult: ownerDataData.rolling.minimumFumbleResult,
flavor: game.i18n.format("DS4.ItemSpellCheckFlavor", { actor: actor.name, spell: this.name }), flavor: game.i18n.format("DS4.ItemSpellCheckFlavor", { actor: actor.name, spell: this.name }),
@ -194,7 +199,7 @@ export class DS4Item extends Item<DS4ItemData, DS4ItemPreparedData> {
/** /**
* Type-guarding variant to check if the item is owned. * Type-guarding variant to check if the item is owned.
*/ */
isOwnedItem(): this is this & { readonly isOwned: true } { isOwnedItem(): this is this & { readonly isOwned: true; readonly actor: DS4Actor; readonly parent: DS4Actor } {
return this.isOwned; return this.isOwned;
} }
} }

View file

@ -3,9 +3,9 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { hoursPerDay, minutesPerHour, secondsPerMinute, secondsPerRound } from "../../common/time-helpers"; import { hoursPerDay, minutesPerHour, secondsPerMinute, secondsPerRound } from "../../common/time-helpers";
import { DS4SpellDataData, TemporalUnit, UnitData } from "../item-data"; import { DS4SpellDataSourceData, TemporalUnit, UnitData } from "../item-data-source";
export function calculateSpellPrice(data: DS4SpellDataData): number | null { export function calculateSpellPrice(data: DS4SpellDataSourceData): number | null {
const spellPriceFactor = calculateSpellPriceFactor(data.cooldownDuration); const spellPriceFactor = calculateSpellPriceFactor(data.cooldownDuration);
const baseSpellPrices = [ const baseSpellPrices = [
data.minimumLevels.healer !== null ? 10 + (data.minimumLevels.healer - 1) * 35 : null, data.minimumLevels.healer !== null ? 10 + (data.minimumLevels.healer - 1) * 35 : null,

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4ItemData } from "../item/item-data"; import { DS4ItemData } from "../item/item-data-source";
import notifications from "../ui/notifications"; import notifications from "../ui/notifications";
import { getActiveActor } from "./helpers"; import { getActiveActor } from "./helpers";

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4SpellDataData } from "../item/item-data"; import { DS4SpellDataSourceData } from "../item/item-data-source";
import logger from "../logger"; import logger from "../logger";
export async function migrate(): Promise<void> { export async function migrate(): Promise<void> {
@ -33,7 +33,7 @@ function getItemUpdateData(itemData: DeepPartial<Item.Data>) {
"-=data.scrollPrice": null, "-=data.scrollPrice": null,
"data.minimumLevels": { healer: null, wizard: null, sorcerer: null }, "data.minimumLevels": { healer: null, wizard: null, sorcerer: null },
}; };
if (((itemData.data as DS4SpellDataData).cooldownDuration.unit as string) === "custom") { if (((itemData.data as DS4SpellDataSourceData).cooldownDuration.unit as string) === "custom") {
updateData["data.cooldownDuration.unit"] = "rounds"; updateData["data.cooldownDuration.unit"] = "rounds";
} }
return updateData; return updateData;

View file

@ -170,29 +170,6 @@
"shield": { "shield": {
"templates": ["base", "physical", "equipable", "protective"] "templates": ["base", "physical", "equipable", "protective"]
}, },
"equipment": {
"templates": ["base", "physical", "equipable"]
},
"loot": {
"templates": ["base", "physical"]
},
"talent": {
"templates": ["base"],
"rank": {
"base": 0,
"max": 0,
"mod": 0
}
},
"racialAbility": {
"templates": ["base"]
},
"language": {
"templates": ["base"]
},
"alphabet": {
"templates": ["base"]
},
"spell": { "spell": {
"templates": ["base", "equipable"], "templates": ["base", "equipable"],
"spellType": "spellcasting", "spellType": "spellcasting",
@ -220,6 +197,29 @@
"sorcerer": null "sorcerer": null
} }
}, },
"equipment": {
"templates": ["base", "physical", "equipable"]
},
"loot": {
"templates": ["base", "physical"]
},
"talent": {
"templates": ["base"],
"rank": {
"base": 0,
"max": 0,
"mod": 0
}
},
"racialAbility": {
"templates": ["base"]
},
"language": {
"templates": ["base"]
},
"alphabet": {
"templates": ["base"]
},
"specialCreatureAbility": { "specialCreatureAbility": {
"templates": ["base"], "templates": ["base"],
"experiencePoints": 0 "experiencePoints": 0

View file

@ -841,7 +841,7 @@ __metadata:
"@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#foundry-0.8.x": "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#foundry-0.8.x":
version: 0.7.9-6 version: 0.7.9-6
resolution: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#commit=62c138c4ff2f6c3b19301db2b31e14e7c825f1e6" resolution: "@league-of-foundry-developers/foundry-vtt-types@https://github.com/League-of-Foundry-Developers/foundry-vtt-types.git#commit=f242ac76237f2099f946b10d642f0b3272ead043"
dependencies: dependencies:
"@types/jquery": ~3.5.5 "@types/jquery": ~3.5.5
"@types/simple-peer": ~9.11.0 "@types/simple-peer": ~9.11.0
@ -851,7 +851,7 @@ __metadata:
socket.io-client: 4.1.2 socket.io-client: 4.1.2
tinymce: 5.8.1 tinymce: 5.8.1
typescript: ^4.1.6 typescript: ^4.1.6
checksum: af1f4d3cfae69a5a0fab2ca3a301c9f755d7f5584bf1eaee37031b19fe805581b7588e6a2452553f874a68cb5475a5a8fce287ed8436cf0af24fa0527ea5c014 checksum: ae81444ddf4b36bff67a2483cc559ff64dd1fdaaada277439e2ed14a8ec8233f06723b9421be70033b38383d9c275de18b216307bb14b17feafc5866156b0ac3
languageName: node languageName: node
linkType: hard linkType: hard
@ -1505,9 +1505,9 @@ __metadata:
linkType: hard linkType: hard
"@types/sizzle@npm:*": "@types/sizzle@npm:*":
version: 2.3.2 version: 2.3.3
resolution: "@types/sizzle@npm:2.3.2" resolution: "@types/sizzle@npm:2.3.3"
checksum: 447a1c3f39f0e47ffdbccd1df58d63e8b67dc001f44f26f43ac8243db7834a3d956cebc8abe9272ecbdccfc8f4ec0ae74b811ccdad5b6cddaf8f0968513d618a checksum: 8f019f9e1b110b4fdfc08f8a3a8b8b87118a11f3ba11e159541b17f17498c0ef95e8efa0b818c9fed6911041f6b71ef8d44cf8c1c83b4cbb7bd14e4248892f4c
languageName: node languageName: node
linkType: hard linkType: hard
@ -8516,11 +8516,11 @@ fsevents@^1.2.7:
linkType: hard linkType: hard
"uglify-js@npm:^3.1.4": "uglify-js@npm:^3.1.4":
version: 3.13.0 version: 3.13.10
resolution: "uglify-js@npm:3.13.0" resolution: "uglify-js@npm:3.13.10"
bin: bin:
uglifyjs: bin/uglifyjs uglifyjs: bin/uglifyjs
checksum: bb35cfe5ce9735a9707b33628dbbef02ab4b62bdd3650a02e14dfe42f4ac3fe5e9d616352476605706ab651561abc66ef2877c3dcabf4bde5bf66cecec94f3e8 checksum: 2c8467faf68a0ba4da7a9539026dc996804f0e89f184ce0a6ceaa9a9c7e4e2ab78399caee8ebbebcd3df64a45b049585c4125e144f1c5992f9b61e81864d9535
languageName: node languageName: node
linkType: hard linkType: hard