implement basic active effects
This commit is contained in:
parent
2c8878bd94
commit
85ec5faec2
4 changed files with 123 additions and 3 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -2718,7 +2718,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"foundry-pc-types": {
|
"foundry-pc-types": {
|
||||||
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#ac45653fdec5fb935bf7db72889cb40cd6b80b20",
|
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#3779bbbd30dbb04fa8f18615496882d6c66e1af4",
|
||||||
"from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
"from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
|
@ -1,9 +1,64 @@
|
||||||
import { ModifiableData } from "../common/common-data";
|
import { ModifiableData } from "../common/common-data";
|
||||||
|
import { DS4 } from "../config";
|
||||||
import { DS4Item } from "../item/item";
|
import { DS4Item } from "../item/item";
|
||||||
import { DS4Armor, DS4ItemDataType, DS4Shield, ItemType } from "../item/item-data";
|
import { DS4Armor, DS4EquippableItemDataType, DS4ItemDataType, DS4Shield, ItemType } from "../item/item-data";
|
||||||
import { DS4ActorDataType } from "./actor-data";
|
import { DS4ActorDataType } from "./actor-data";
|
||||||
|
|
||||||
|
type DS4ActiveEffect = ActiveEffect<DS4ActorDataType, DS4ItemDataType, DS4Actor, DS4Item>;
|
||||||
|
|
||||||
export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item> {
|
export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item> {
|
||||||
|
/** @override */
|
||||||
|
prepareData(): void {
|
||||||
|
this.data = duplicate(this._data);
|
||||||
|
if (!this.data.img) this.data.img = CONST.DEFAULT_TOKEN;
|
||||||
|
if (!this.data.name) this.data.name = "New " + this.entity;
|
||||||
|
this.prepareBaseData();
|
||||||
|
this.prepareEmbeddedEntities();
|
||||||
|
this.applyActiveEffectsToNonDerivedData();
|
||||||
|
this.prepareDerivedData();
|
||||||
|
this.applyActiveEffectsToDerivedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyActiveEffectsToNonDerivedData(): void {
|
||||||
|
this.applyActiveEffectsFiltered((change) => !this.derivedDataProperties.includes(change.key));
|
||||||
|
}
|
||||||
|
|
||||||
|
applyActiveEffectsToDerivedData(): void {
|
||||||
|
this.applyActiveEffectsFiltered((change) => this.derivedDataProperties.includes(change.key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply ActiveEffectChanges to the Actor data which are caused by ActiveEffects and satisfy the given predicate.
|
||||||
|
*
|
||||||
|
* @param predicate The predicate that ActiveEffectChanges need to satisfy in order to be applied
|
||||||
|
*/
|
||||||
|
applyActiveEffectsFiltered(predicate: (change: ActiveEffectChange) => boolean): void {
|
||||||
|
const overrides = {};
|
||||||
|
|
||||||
|
// Organize non-disabled effects by their application priority
|
||||||
|
const changes = this.effects.reduce((changes: Array<ActiveEffectChange & { effect: DS4ActiveEffect }>, e) => {
|
||||||
|
if (e.data.disabled) return changes;
|
||||||
|
|
||||||
|
return changes.concat(
|
||||||
|
e.data.changes.filter(predicate).map((c) => {
|
||||||
|
c = duplicate(c);
|
||||||
|
c.priority = c.priority ?? c.mode * 10;
|
||||||
|
return { ...c, effect: e };
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
changes.sort((a, b) => a.priority - b.priority);
|
||||||
|
|
||||||
|
// Apply all changes
|
||||||
|
for (const change of changes) {
|
||||||
|
const result = change.effect.apply(this, change);
|
||||||
|
if (result !== null) overrides[change.key] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand the set of final overrides
|
||||||
|
this["overrides"] = expandObject({ ...flattenObject(this["overrides"] ?? {}), ...overrides });
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
prepareDerivedData(): void {
|
prepareDerivedData(): void {
|
||||||
const data = this.data;
|
const data = this.data;
|
||||||
|
@ -18,6 +73,13 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item>
|
||||||
this._prepareCombatValues();
|
this._prepareCombatValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The list of properties that are dericed, in dot notation */
|
||||||
|
get derivedDataProperties(): Array<string> {
|
||||||
|
return Object.keys(DS4.combatValues)
|
||||||
|
.map((combatValue) => `data.combatValues.${combatValue}.base`)
|
||||||
|
.concat("data.combatValues.hitPoints.max");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of item types that can be owned by this actor.
|
* The list of item types that can be owned by this actor.
|
||||||
*/
|
*/
|
||||||
|
@ -113,4 +175,61 @@ export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, 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 */
|
||||||
|
createEmbeddedEntity(
|
||||||
|
embeddedName: string,
|
||||||
|
createData: Record<string, unknown> | Array<Record<string, unknown>>,
|
||||||
|
options?: Record<string, unknown>,
|
||||||
|
): Promise<this> {
|
||||||
|
if (embeddedName === "OwnedItem") {
|
||||||
|
this._preCreateOwnedItem((createData as unknown) as ItemData<DS4ItemDataType>);
|
||||||
|
}
|
||||||
|
return super.createEmbeddedEntity(embeddedName, createData, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the item that is going to be created is equippable, set it to be non equipped and disable all ActiveEffects
|
||||||
|
* contained in the item
|
||||||
|
* @param itemData The data of the item to be created
|
||||||
|
*/
|
||||||
|
private _preCreateOwnedItem(itemData: ItemData<DS4ItemDataType>): void {
|
||||||
|
if ("equipped" in itemData.data) {
|
||||||
|
itemData.effects = itemData.effects.map((effect) => ({ ...effect, disabled: true }));
|
||||||
|
const equippableUpdateData = itemData as ItemData<DS4EquippableItemDataType>;
|
||||||
|
equippableUpdateData.data.equipped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
updateEmbeddedEntity(
|
||||||
|
embeddedName: string,
|
||||||
|
updateData: Record<string, unknown> | Array<Record<string, unknown>>,
|
||||||
|
options?: Record<string, unknown>,
|
||||||
|
): Promise<this> {
|
||||||
|
if (embeddedName === "OwnedItem") {
|
||||||
|
this._preUpdateOwnedItem(updateData as Partial<ItemData<DS4ItemDataType>>);
|
||||||
|
}
|
||||||
|
return super.updateEmbeddedEntity(embeddedName, updateData, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the equipped flag of an item changed, update all ActiveEffects originating from that item accordingly.
|
||||||
|
* @param updateData The change that is going to be applied to the owned item
|
||||||
|
*/
|
||||||
|
private _preUpdateOwnedItem(updateData: Partial<ItemData<DS4ItemDataType>>): void {
|
||||||
|
if ("equipped" in updateData.data) {
|
||||||
|
const equippableUpdateData = updateData as Partial<ItemData<DS4EquippableItemDataType>>;
|
||||||
|
const origin = `Actor.${this.id}.OwnedItem.${updateData._id}`;
|
||||||
|
const effects = this.effects
|
||||||
|
.filter((e) => e.data.origin === origin)
|
||||||
|
.map((e) => {
|
||||||
|
const data = duplicate(e.data);
|
||||||
|
data.disabled = !equippableUpdateData.data.equipped;
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
if (effects.length > 0)
|
||||||
|
this.updateEmbeddedEntity("ActiveEffect", (effects as unknown) as Record<string, unknown>);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ export type DS4ItemDataType =
|
||||||
| DS4Alphabet
|
| DS4Alphabet
|
||||||
| DS4SpecialCreatureAbility;
|
| DS4SpecialCreatureAbility;
|
||||||
|
|
||||||
|
export type DS4EquippableItemDataType = DS4Weapon | DS4Armor | DS4Shield | DS4Trinket;
|
||||||
|
|
||||||
// types
|
// types
|
||||||
|
|
||||||
interface DS4Weapon extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable {
|
interface DS4Weapon extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable {
|
||||||
|
|
|
@ -34,7 +34,6 @@ export class DS4ItemSheet extends ItemSheet<DS4ItemDataType, DS4Item> {
|
||||||
actor: this.item.actor,
|
actor: this.item.actor,
|
||||||
isPhysical: isDS4ItemDataTypePhysical(this.item.data.data),
|
isPhysical: isDS4ItemDataTypePhysical(this.item.data.data),
|
||||||
};
|
};
|
||||||
console.log(data);
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue