WIP: Update to 0.8.x

This commit is contained in:
Johannes Loher 2021-06-30 02:17:54 +02:00
parent 344302be69
commit ef01698178
12 changed files with 352 additions and 250 deletions

View file

@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { ModifiableDataBaseTotal, ResourceDataBaseTotalMax } from "../common/common-data";
import { DS4 } from "../config";
import {
DS4CharacterDataSourceDataBaseInfo,
DS4CharacterDataSourceDataCurrency,
DS4CharacterDataSourceDataProfile,
DS4CharacterDataSourceDataProgression,
DS4CharacterDataSourceDataSlayerPoints,
DS4CreatureDataSourceDataBaseInfo,
} from "./actor-data-source";
declare global {
interface DataConfig {
Actor: DS4ActorDataProperties;
}
}
export type DS4ActorDataProperties = DS4CharacterDataProperties | DS4CreatureDataProperties;
interface DS4CharacterDataProperties {
type: "character";
data: DS4CharacterDataPropertiesData;
}
interface DS4CreatureDataProperties {
type: "creature";
data: DS4CreatureDataPropertiesData;
}
// templates
interface DS4ActorDataPropertiesDataBase {
attributes: DS4ActorDataPropertiesDataAttributes;
traits: DS4ActorDataPropertiesDataTraits;
combatValues: DS4ActorDataPropertiesDataCombatValues;
rolling: DS4ActorDataPropertiesDataRolling;
checks: DS4ActorDataPropertiesDataChecks;
}
type DS4ActorDataPropertiesDataAttributes = {
[Key in keyof typeof DS4.i18n.attributes]: ModifiableDataBaseTotal<number>;
};
type DS4ActorDataPropertiesDataTraits = { [Key in keyof typeof DS4.i18n.traits]: ModifiableDataBaseTotal<number> };
type DS4ActorDataPropertiesDataCombatValues = {
[Key in keyof typeof DS4.i18n.combatValues]: Key extends "hitPoints"
? ResourceDataBaseTotalMax<number>
: ModifiableDataBaseTotal<number>;
};
interface DS4ActorDataPropertiesDataRolling {
maximumCoupResult: number;
minimumFumbleResult: number;
}
type DS4ActorDataPropertiesDataChecks = {
[key in Check]: number;
};
export type Check = keyof typeof DS4.i18n.checks;
export function isCheck(value: string): value is Check {
return Object.keys(DS4.i18n.checks).includes(value);
}
// types
interface DS4CreatureDataPropertiesData extends DS4ActorDataPropertiesDataBase {
baseInfo: DS4CreatureDataSourceDataBaseInfo;
}
interface DS4CharacterDataPropertiesData extends DS4ActorDataPropertiesDataBase {
baseInfo: DS4CharacterDataSourceDataBaseInfo;
progression: DS4CharacterDataSourceDataProgression;
profile: DS4CharacterDataSourceDataProfile;
currency: DS4CharacterDataSourceDataCurrency;
slayerPoints: DS4CharacterDataPropertiesDataSlayerPoints;
}
export interface DS4CharacterDataPropertiesDataSlayerPoints extends DS4CharacterDataSourceDataSlayerPoints {
max: number;
}

View file

@ -7,50 +7,56 @@
import { ModifiableData, ModifiableDataBase, ResourceData, UsableResource } from "../common/common-data";
import { DS4 } from "../config";
import { DS4ItemData } from "../item/item-data";
export type DS4ActorData = DS4CharacterData | DS4CreatureData;
type ActorType = keyof typeof DS4.i18n.actorTypes;
export interface DS4ActorDataHelper<T, U extends ActorType> extends Actor.Data<T, DS4ItemData> {
type: U;
declare global {
interface SourceConfig {
Actor: DS4ActorDataSource;
}
}
type DS4CharacterData = DS4ActorDataHelper<DS4CharacterDataData, "character">;
type DS4CreatureData = DS4ActorDataHelper<DS4CreatureDataData, "creature">;
export type DS4ActorDataSource = DS4CharacterDataSource | DS4CreatureDataSource;
interface DS4CharacterDataSource {
type: "character";
data: DS4CharacterDataSourceData;
}
interface DS4CreatureDataSource {
type: "creature";
data: DS4CreatureDataSourceData;
}
// templates
interface DS4ActorDataDataBase {
attributes: DS4ActorDataDataAttributes;
traits: DS4ActorDataDataTraits;
combatValues: DS4ActorDataDataCombatValues;
interface DS4ActorDataSourceDataBase {
attributes: DS4ActorDataSourceDataAttributes;
traits: DS4ActorDataSourceDataTraits;
combatValues: DS4ActorDataSourceDataCombatValues;
}
type DS4ActorDataDataAttributes = { [Key in keyof typeof DS4.i18n.attributes]: ModifiableDataBase<number> };
type DS4ActorDataSourceDataAttributes = { [Key in keyof typeof DS4.i18n.attributes]: ModifiableDataBase<number> };
type Attribute = keyof DS4ActorDataDataAttributes;
type Attribute = keyof DS4ActorDataSourceDataAttributes;
export function isAttribute(value: unknown): value is Attribute {
return (Object.keys(DS4.i18n.attributes) as Array<unknown>).includes(value);
}
type DS4ActorDataDataTraits = { [Key in keyof typeof DS4.i18n.traits]: ModifiableDataBase<number> };
type DS4ActorDataSourceDataTraits = { [Key in keyof typeof DS4.i18n.traits]: ModifiableDataBase<number> };
type Trait = keyof DS4ActorDataDataTraits;
type Trait = keyof DS4ActorDataSourceDataTraits;
export function isTrait(value: unknown): value is Trait {
return (Object.keys(DS4.i18n.traits) as Array<unknown>).includes(value);
}
type DS4ActorDataDataCombatValues = {
type DS4ActorDataSourceDataCombatValues = {
[Key in keyof typeof DS4.i18n.combatValues]: Key extends "hitPoints"
? ResourceData<number>
: ModifiableData<number>;
};
type CombatValue = keyof DS4ActorDataDataCombatValues;
type CombatValue = keyof DS4ActorDataSourceDataCombatValues;
export function isCombatValue(value: string): value is CombatValue {
return (Object.keys(DS4.i18n.combatValues) as Array<unknown>).includes(value);
@ -58,33 +64,44 @@ export function isCombatValue(value: string): value is CombatValue {
// types
interface DS4CharacterDataData extends DS4ActorDataDataBase {
baseInfo: DS4CharacterDataDataBaseInfo;
progression: DS4CharacterDataDataProgression;
language: DS4CharacterDataDataLanguage;
profile: DS4CharacterDataDataProfile;
currency: DS4CharacterDataDataCurrency;
slayerPoints: DS4CharacterDataDataSlayerPoints;
interface DS4CreatureDataSourceData extends DS4ActorDataSourceDataBase {
baseInfo: DS4CreatureDataSourceDataBaseInfo;
}
export interface DS4CharacterDataDataBaseInfo {
export interface DS4CreatureDataSourceDataBaseInfo {
loot: string;
foeFactor: number;
creatureType: CreatureType;
sizeCategory: SizeCategory;
experiencePoints: number;
description: string;
}
type CreatureType = keyof typeof DS4.i18n.creatureTypes;
type SizeCategory = keyof typeof DS4.i18n.creatureSizeCategories;
interface DS4CharacterDataSourceData extends DS4ActorDataSourceDataBase {
baseInfo: DS4CharacterDataSourceDataBaseInfo;
progression: DS4CharacterDataSourceDataProgression;
profile: DS4CharacterDataSourceDataProfile;
currency: DS4CharacterDataSourceDataCurrency;
slayerPoints: DS4CharacterDataSourceDataSlayerPoints;
}
export interface DS4CharacterDataSourceDataBaseInfo {
race: string;
class: string;
heroClass: string;
culture: string;
}
export interface DS4CharacterDataDataProgression {
export interface DS4CharacterDataSourceDataProgression {
level: number;
experiencePoints: number;
talentPoints: UsableResource<number>;
progressPoints: UsableResource<number>;
}
export interface DS4CharacterDataDataLanguage {
languages: string;
alphabets: string;
}
export interface DS4CharacterDataDataProfile {
export interface DS4CharacterDataSourceDataProfile {
biography: string;
gender: string;
birthday: string;
@ -97,29 +114,12 @@ export interface DS4CharacterDataDataProfile {
specialCharacteristics: string;
}
export interface DS4CharacterDataDataCurrency {
export interface DS4CharacterDataSourceDataCurrency {
gold: number;
silver: number;
copper: number;
}
export interface DS4CharacterDataDataSlayerPoints {
export interface DS4CharacterDataSourceDataSlayerPoints {
value: number;
}
interface DS4CreatureDataData extends DS4ActorDataDataBase {
baseInfo: DS4CreatureDataDataBaseInfo;
}
export interface DS4CreatureDataDataBaseInfo {
loot: string;
foeFactor: number;
creatureType: CreatureType;
sizeCategory: SizeCategory;
experiencePoints: number;
description: string;
}
type CreatureType = keyof typeof DS4.i18n.creatureTypes;
type SizeCategory = keyof typeof DS4.i18n.creatureSizeCategories;

View file

@ -1,77 +0,0 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { ModifiableDataBaseTotal, ResourceDataBaseTotalMax } from "../common/common-data";
import { DS4 } from "../config";
import {
DS4ActorDataHelper,
DS4CharacterDataDataBaseInfo,
DS4CharacterDataDataCurrency,
DS4CharacterDataDataLanguage,
DS4CharacterDataDataProfile,
DS4CharacterDataDataProgression,
DS4CharacterDataDataSlayerPoints,
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;
checks: DS4ActorPreparedDataDataChecks;
}
type DS4ActorPreparedDataDataAttributes = {
[Key in keyof typeof DS4.i18n.attributes]: ModifiableDataBaseTotal<number>;
};
type DS4ActorPreparedDataDataTraits = { [Key in keyof typeof DS4.i18n.traits]: ModifiableDataBaseTotal<number> };
type DS4ActorPreparedDataDataCombatValues = {
[Key in keyof typeof DS4.i18n.combatValues]: Key extends "hitPoints"
? ResourceDataBaseTotalMax<number>
: ModifiableDataBaseTotal<number>;
};
interface DS4ActorPreparedDataDataRolling {
maximumCoupResult: number;
minimumFumbleResult: number;
}
export type Check = keyof typeof DS4.i18n.checks;
export function isCheck(value: string): value is Check {
return Object.keys(DS4.i18n.checks).includes(value);
}
type DS4ActorPreparedDataDataChecks = {
[key in Check]: number;
};
// types
interface DS4CharacterPreparedDataData extends DS4ActorPreparedDataDataBase {
baseInfo: DS4CharacterDataDataBaseInfo;
progression: DS4CharacterDataDataProgression;
language: DS4CharacterDataDataLanguage;
profile: DS4CharacterDataDataProfile;
currency: DS4CharacterDataDataCurrency;
slayerPoints: DS4CharacterPreparedDataDataSlayerPoints;
}
export interface DS4CharacterPreparedDataDataSlayerPoints extends DS4CharacterDataDataSlayerPoints {
max: number;
}
interface DS4CreaturePreparedDataData extends DS4ActorPreparedDataDataBase {
baseInfo: DS4CreatureDataDataBaseInfo;
}

View file

@ -9,18 +9,16 @@ import { DS4Item } from "../item/item";
import { ItemType } from "../item/item-data";
import { DS4ArmorPreparedData, DS4ShieldPreparedData } from "../item/item-prepared-data";
import { createCheckRoll } from "../rolls/check-factory";
import { DS4ActorData, isAttribute, isTrait } from "./actor-data";
import { Check, DS4ActorPreparedData } from "./actor-prepared-data";
import { isAttribute, isTrait } from "./actor-data-source";
import { Check } from "./actor-data-properties";
/**
* The Actor class for DS4
*/
export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData> {
export class DS4Actor extends Actor {
/** @override */
prepareData(): void {
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.data.reset();
this.prepareBaseData();
this.prepareEmbeddedEntities();
this.applyActiveEffectsToBaseData();
@ -49,6 +47,15 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
);
}
/**
* @override
* We override this with an empty implementation because we have our own custom way of applying
* {@link ActiveEffect}s and {@link Actor#prepareEmbeddedEntities} calls this.
*/
applyActiveEffects(): void {
return;
}
applyActiveEffectsToBaseData(): void {
// reset overrides because our variant of applying active effects does not set them, it only adds overrides
this.overrides = {};
@ -68,32 +75,30 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
*
* @param predicate - The predicate that ActiveEffectChanges need to satisfy in order to be applied
*/
applyActiveEffectsFiltered(predicate: (change: ActiveEffectChange) => boolean): void {
applyActiveEffectsFiltered(predicate: (change: foundry.data.ActiveEffectData["changes"][number]) => boolean): void {
const overrides: Record<string, unknown> = {};
// Organize non-disabled effects by their application priority
const changes = this.effects.reduce(
(changes: Array<ActiveEffectChange & { effect: ActiveEffect<DS4Actor> }>, e) => {
if (e.data.disabled) return changes;
const item = this._getOriginatingItemOfActiveEffect(e);
if (item?.isNonEquippedEuipable()) return changes;
const changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[] =
this.effects.reduce(
(changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[], e) => {
if (e.data.disabled) return changes;
const item = this.getOriginatingItemOfActiveEffect(e);
if (item?.isNonEquippedEuipable()) return changes; // TODO: DS4Item
const factor = item?.activeEffectFactor ?? 1;
const factor = item?.activeEffectFactor ?? 1; // TODO: DS4Item
return changes.concat(
e.data.changes.filter(predicate).flatMap((c) => {
const duplicatedChange = duplicate(c);
duplicatedChange.priority = duplicatedChange.priority ?? duplicatedChange.mode * 10;
return Array(factor).fill({
...duplicatedChange,
effect: e,
});
}),
);
},
[],
);
changes.sort((a, b) => a.priority - b.priority);
const newChanges = e.data.changes.filter(predicate).flatMap((c) => {
const changeSource = c.toObject();
changeSource.priority = changeSource.priority ?? changeSource.mode * 10;
return Array(factor).fill({ ...changeSource, effect: e });
});
return changes.concat(newChanges);
},
[],
);
changes.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
// Apply all changes
for (const change of changes) {
@ -105,8 +110,9 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
this.overrides = expandObject({ ...flattenObject(this.overrides), ...overrides });
}
protected _getOriginatingItemOfActiveEffect(effect: ActiveEffect<DS4Actor>): DS4Item | undefined {
return this.items.find((item) => item.uuid === effect.data.origin) ?? undefined;
// TODO: returns DS4Item | undefined
protected getOriginatingItemOfActiveEffect(effect: ActiveEffect): Item | undefined {
return this.items.find((item) => item.uuid === effect.data.origin);
}
/**
@ -216,7 +222,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
return this.items
.map((item) => item.data)
.filter(
(data): data is DS4ArmorPreparedData | DS4ShieldPreparedData =>
(data): data is foundry.data.ItemData & (DS4ArmorDataProperties | DS4ShieldDataProperties) =>
data.type === "armor" || data.type === "shield",
)
.filter((data) => data.data.equipped)
@ -266,8 +272,14 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
* Handle how changes to a Token attribute bar are applied to the Actor.
* This only differs from the base implementation by also allowing negative values.
* @override
* TODO: Adjust return type
*/
async modifyTokenAttribute(attribute: string, value: number, isDelta = false, isBar = true): Promise<this> {
async modifyTokenAttribute(
attribute: string,
value: number,
isDelta = false,
isBar = true,
): Promise<this | undefined> {
const current = getProperty(this.data.data, attribute);
// Determine the updates to make to the actor data
@ -291,7 +303,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
*/
async rollCheck(check: Check): Promise<void> {
await createCheckRoll(this.data.data.checks[check], {
rollMode: game.settings.get("core", "rollMode") as Const.DiceRollMode, // TODO(types): Type this setting in upstream
rollMode: game.settings.get("core", "rollMode"),
maximumCoupResult: this.data.data.rolling.maximumCoupResult,
minimumFumbleResult: this.data.data.rolling.minimumFumbleResult,
flavor: game.i18n.format("DS4.ActorCheckFlavor", { actor: this.name, check: DS4.i18n.checks[check] }),
@ -306,7 +318,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item, DS4ActorPreparedData>
const { attribute, trait } = await this.selectAttributeAndTrait();
const checkTargetNumber = this.data.data.attributes[attribute].total + this.data.data.traits[trait].total;
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: this.data.data.rolling.maximumCoupResult,
minimumFumbleResult: this.data.data.rolling.minimumFumbleResult,
flavor: game.i18n.format("DS4.ActorGenericCheckFlavor", {

View file

@ -13,7 +13,7 @@ import { DS4ItemData } from "../../item/item-data";
import { getDS4Settings } from "../../settings";
import notifications from "../../ui/notifications";
import { DS4Actor } from "../actor";
import { isCheck } from "../actor-prepared-data";
import { isCheck } from "../actor-data-properties";
/**
* The base Sheet class for all DS4 Actors