Simplify ActiveEffect handling by a lot
This commit is contained in:
parent
1b715031a8
commit
66334d338f
2 changed files with 33 additions and 149 deletions
|
@ -1,7 +1,7 @@
|
||||||
import { ModifiableData } from "../common/common-data";
|
import { ModifiableData } from "../common/common-data";
|
||||||
import { DS4 } from "../config";
|
import { DS4 } from "../config";
|
||||||
import { DS4Item } from "../item/item";
|
import { DS4Item } from "../item/item";
|
||||||
import { DS4ItemData, ItemType } from "../item/item-data";
|
import { ItemType } from "../item/item-data";
|
||||||
import { DS4ActorData } from "./actor-data";
|
import { DS4ActorData } from "./actor-data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,12 +52,19 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||||
const changes = this.effects.reduce(
|
const changes = this.effects.reduce(
|
||||||
(changes: Array<ActiveEffectChange & { effect: ActiveEffect<DS4Actor> }>, e) => {
|
(changes: Array<ActiveEffectChange & { effect: ActiveEffect<DS4Actor> }>, e) => {
|
||||||
if (e.data.disabled) return changes;
|
if (e.data.disabled) return changes;
|
||||||
|
const item = this._getOriginatingItemOfActiveEffect(e);
|
||||||
|
if (item?.isNonEquippedEuipable()) return changes;
|
||||||
|
|
||||||
|
const factor = item?.activeEffectFactor ?? 1;
|
||||||
|
|
||||||
return changes.concat(
|
return changes.concat(
|
||||||
e.data.changes.filter(predicate).map((c) => {
|
e.data.changes.filter(predicate).flatMap((c) => {
|
||||||
const duplicatedChange = duplicate(c) as ActiveEffect.Change;
|
const duplicatedChange = duplicate(c) as ActiveEffect.Change;
|
||||||
duplicatedChange.priority = duplicatedChange.priority ?? duplicatedChange.mode * 10;
|
duplicatedChange.priority = duplicatedChange.priority ?? duplicatedChange.mode * 10;
|
||||||
return { ...duplicatedChange, effect: e };
|
return Array(factor).fill({
|
||||||
|
...duplicatedChange,
|
||||||
|
effect: e,
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -75,6 +82,13 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||||
this.overrides = expandObject({ ...flattenObject(this.overrides ?? {}), ...overrides });
|
this.overrides = expandObject({ ...flattenObject(this.overrides ?? {}), ...overrides });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _getOriginatingItemOfActiveEffect(effect: ActiveEffect<DS4Actor>): DS4Item | undefined {
|
||||||
|
const re = /.*OwnedItem\.(.*)/;
|
||||||
|
const id = effect.data.origin?.match(re)?.[1] ?? "";
|
||||||
|
const item = this.items.find((item) => item.id === id);
|
||||||
|
return item ?? undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
prepareDerivedData(): void {
|
prepareDerivedData(): void {
|
||||||
this._prepareCombatValues();
|
this._prepareCombatValues();
|
||||||
|
@ -123,7 +137,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||||
/**
|
/**
|
||||||
* Prepares the combat values of the actor.
|
* Prepares the combat values of the actor.
|
||||||
*/
|
*/
|
||||||
private _prepareCombatValues(): void {
|
protected _prepareCombatValues(): void {
|
||||||
const data = this.data.data;
|
const data = this.data.data;
|
||||||
const armorValueOfEquippedItems = this._calculateArmorValueOfEquippedItems();
|
const armorValueOfEquippedItems = this._calculateArmorValueOfEquippedItems();
|
||||||
data.combatValues.hitPoints.base =
|
data.combatValues.hitPoints.base =
|
||||||
|
@ -151,7 +165,7 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||||
/**
|
/**
|
||||||
* Calculates the total armor value of all equipped items.
|
* Calculates the total armor value of all equipped items.
|
||||||
*/
|
*/
|
||||||
private _calculateArmorValueOfEquippedItems(): number {
|
protected _calculateArmorValueOfEquippedItems(): number {
|
||||||
return this.items
|
return this.items
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (item.data.type === "armor" || item.data.type === "shield") {
|
if (item.data.type === "armor" || item.data.type === "shield") {
|
||||||
|
@ -185,148 +199,4 @@ export class DS4Actor extends Actor<DS4ActorData, DS4Item> {
|
||||||
const allowed = Hooks.call("modifyTokenAttribute", { attribute, value, isDelta, isBar }, updates);
|
const allowed = Hooks.call("modifyTokenAttribute", { attribute, value, isDelta, isBar }, updates);
|
||||||
return allowed !== false ? this.update(updates) : this;
|
return allowed !== false ? this.update(updates) : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
|
||||||
// TODO(types): Improve typing once it's fixed in upstream (arrays can be passed!)
|
|
||||||
createEmbeddedEntity(
|
|
||||||
embeddedName: "OwnedItem",
|
|
||||||
data: DeepPartial<DS4ItemData>,
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
): Promise<DS4ItemData>;
|
|
||||||
createEmbeddedEntity(
|
|
||||||
embeddedName: "ActiveEffect",
|
|
||||||
data: DeepPartial<DS4ItemData>,
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
): Promise<ActiveEffect.Data>;
|
|
||||||
createEmbeddedEntity(
|
|
||||||
embeddedName: "OwnedItem" | "ActiveEffect",
|
|
||||||
data: DeepPartial<DS4ItemData> | DeepPartial<ActiveEffect.Data>,
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
): Promise<DS4ItemData> | Promise<ActiveEffect.Data> {
|
|
||||||
if (embeddedName === "OwnedItem") {
|
|
||||||
preCreateOwnedItem.bind(this)(data);
|
|
||||||
return super.createEmbeddedEntity(embeddedName, data, options);
|
|
||||||
}
|
|
||||||
return super.createEmbeddedEntity(embeddedName, data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
// TODO(types): Improve typing once it's fixed in upstream
|
|
||||||
updateEmbeddedEntity(embeddedName: string, data: unknown[], options?: Entity.UpdateOptions): Promise<unknown[]>;
|
|
||||||
updateEmbeddedEntity(embeddedName: string, data: unknown, options?: Entity.UpdateOptions): Promise<unknown>;
|
|
||||||
updateEmbeddedEntity(
|
|
||||||
embeddedName: string,
|
|
||||||
updateData: unknown | unknown[],
|
|
||||||
options?: Record<string, unknown>,
|
|
||||||
): Promise<unknown> {
|
|
||||||
if (embeddedName === "OwnedItem") {
|
|
||||||
preUpdateOwnedItem.bind(this)(updateData as DeepPartial<DS4ItemData> | Array<DeepPartial<DS4ItemData>>);
|
|
||||||
}
|
|
||||||
return super.updateEmbeddedEntity(embeddedName, updateData, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If the item that is going to be created is equipable, set it to be non equipped and disable all ActiveEffects
|
|
||||||
* contained in the item
|
|
||||||
* @param itemData - The data of the item to be created
|
|
||||||
*/
|
|
||||||
function preCreateOwnedItem(itemData: DeepPartial<DS4ItemData> | DeepPartial<DS4ItemData>[]): void {
|
|
||||||
const dataArray = itemData instanceof Array ? itemData : [itemData];
|
|
||||||
dataArray.forEach((data) => {
|
|
||||||
if (data.data && "equipped" in data.data) {
|
|
||||||
data.effects = data.effects?.map((effect) => ({ ...effect, disabled: true }));
|
|
||||||
data.data.equipped = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
console.log(itemData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the equipped flag of one or more items changed, update all ActiveEffects originating from those items
|
|
||||||
* accordingly.
|
|
||||||
* @param updateData - The change that is going to be applied to the owned item(s)
|
|
||||||
*/
|
|
||||||
function preUpdateOwnedItem<T extends Actor>(
|
|
||||||
this: T,
|
|
||||||
updateData: DeepPartial<DS4ItemData> | Array<DeepPartial<DS4ItemData>>,
|
|
||||||
): void {
|
|
||||||
const dataArray = updateData instanceof Array ? updateData : [updateData];
|
|
||||||
dataArray.forEach((data) => {
|
|
||||||
if (data.data && "equipped" in data.data) {
|
|
||||||
const equipped = data.data.equipped;
|
|
||||||
const origin = `Actor.${this.id}.OwnedItem.${data._id}`;
|
|
||||||
const effects = this.effects
|
|
||||||
.filter((e) => e.data.origin === origin)
|
|
||||||
.map((e) => {
|
|
||||||
const effectData = duplicate(e.data);
|
|
||||||
effectData.disabled = !(equipped ?? true);
|
|
||||||
return effectData;
|
|
||||||
});
|
|
||||||
if (effects.length > 0)
|
|
||||||
this.updateEmbeddedEntity("ActiveEffect", (effects as unknown) as Record<string, unknown>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldTokenCreateEmbeddedEntity = ActorTokenHelpers.prototype.createEmbeddedEntity;
|
|
||||||
const oldTokenUpdateEmbeddedEntity = ActorTokenHelpers.prototype.updateEmbeddedEntity;
|
|
||||||
|
|
||||||
function tokenCreateEmbeddedEntity<T extends Actor, U>(
|
|
||||||
this: T,
|
|
||||||
embeddedName: "OwnedItem" | "ActiveEffect",
|
|
||||||
data: Expanded<U> extends DeepPartial<ActiveEffect.Data> | DeepPartial<DS4ItemData>
|
|
||||||
? U | U[]
|
|
||||||
:
|
|
||||||
| DeepPartial<ActiveEffect.Data>
|
|
||||||
| DeepPartial<ActiveEffect.Data>[]
|
|
||||||
| DeepPartial<DS4ItemData>
|
|
||||||
| DeepPartial<DS4ItemData>[],
|
|
||||||
options?: Entity.UpdateOptions,
|
|
||||||
) {
|
|
||||||
if (embeddedName === "OwnedItem") {
|
|
||||||
const itemDataArray =
|
|
||||||
data instanceof Array
|
|
||||||
? data.map((it) => expandObject(it) as DS4ItemData)
|
|
||||||
: [expandObject(data as Record<string, unknown>) as DS4ItemData];
|
|
||||||
preCreateOwnedItem.bind(this)(itemDataArray);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
// @ts-ignore
|
|
||||||
return oldTokenCreateEmbeddedEntity.bind(this)(embeddedName, itemDataArray, options);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
// @ts-ignore
|
|
||||||
return oldTokenCreateEmbeddedEntity.bind(this)(embeddedName, data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenUpdateEmbeddedEntity<T extends Actor, U>(
|
|
||||||
this: T,
|
|
||||||
embeddedName: "OwnedItem" | "ActiveEffect",
|
|
||||||
data: Expanded<U> extends
|
|
||||||
| (DeepPartial<ActiveEffect.Data> & { _id: string })
|
|
||||||
| (DeepPartial<DS4ItemData> & { _id: string })
|
|
||||||
? U | U[]
|
|
||||||
:
|
|
||||||
| (DeepPartial<ActiveEffect.Data> & { _id: string })
|
|
||||||
| (DeepPartial<ActiveEffect.Data> & { _id: string })[]
|
|
||||||
| (DeepPartial<DS4ItemData> & { _id: string })
|
|
||||||
| (DeepPartial<DS4ItemData> & { _id: string })[],
|
|
||||||
options?: Entity.UpdateOptions,
|
|
||||||
) {
|
|
||||||
if (embeddedName === "OwnedItem") {
|
|
||||||
const itemDataArray =
|
|
||||||
data instanceof Array
|
|
||||||
? data.map((it) => expandObject(it) as DS4ItemData)
|
|
||||||
: [expandObject(data as Record<string, unknown>) as DS4ItemData];
|
|
||||||
preUpdateOwnedItem.bind(this)(itemDataArray);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
// @ts-ignore
|
|
||||||
return oldTokenUpdateEmbeddedEntity.bind(this)(embeddedName, itemDataArray, options);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
// @ts-ignore
|
|
||||||
return oldTokenUpdateEmbeddedEntity.bind(this)(embeddedName, data, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
ActorTokenHelpers.prototype.createEmbeddedEntity = tokenCreateEmbeddedEntity;
|
|
||||||
ActorTokenHelpers.prototype.updateEmbeddedEntity = tokenUpdateEmbeddedEntity;
|
|
||||||
|
|
|
@ -18,4 +18,18 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
data.rank.total = data.rank.base + data.rank.mod;
|
data.rank.total = data.rank.base + data.rank.mod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isNonEquippedEuipable(): boolean {
|
||||||
|
return "equipped" in this.data.data && !this.data.data.equipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of times that active effect changes originating from this item should be applied.
|
||||||
|
*/
|
||||||
|
get activeEffectFactor(): number {
|
||||||
|
if (this.data.type === "talent") {
|
||||||
|
return this.data.data.rank.total ?? this.data.data.rank.base + this.data.data.rank.mod;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue