Skip to content

Commit

Permalink
Update effect badge update sanitation for v12 (foundryvtt#14668)
Browse files Browse the repository at this point in the history
Update effect badge updates for v12
  • Loading branch information
CarlosFdez committed May 29, 2024
1 parent 277a92a commit ae06e6b
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 31 deletions.
6 changes: 5 additions & 1 deletion src/module/item/abstract-effect/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ interface EffectBadgeValueSource extends EffectBadgeBaseSource {
/** The initial value of this badge */
initial?: number;
} | null;
min?: never;
max?: never;
}

interface EffectBadgeValue extends EffectBadgeValueSource, EffectBadgeBase {
interface EffectBadgeValue extends Omit<EffectBadgeValueSource, "min" | "max">, EffectBadgeBase {
min: number;
max: number;
}
Expand All @@ -64,6 +66,8 @@ interface EffectBadgeFormulaSource extends EffectBadgeBaseSource {
value: string;
evaluate?: boolean;
reevaluate?: BadgeReevaluationEventType | null;
min?: never;
max?: never;
}

type BadgeReevaluationEventType = "initiative-roll" | "turn-start" | "turn-end";
Expand Down
2 changes: 1 addition & 1 deletion src/module/item/effect/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ interface EffectSystemSource extends AbstractEffectSystemSource {
}

interface EffectSystemData
extends Omit<EffectSystemSource, "description" | "fromSpell">,
extends Omit<EffectSystemSource, "badge" | "description" | "fromSpell">,
Omit<AbstractEffectSystemData, "level"> {
expired: boolean;
badge: EffectBadge | null;
Expand Down
50 changes: 21 additions & 29 deletions src/module/item/effect/document.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ActorPF2e } from "@actor";
import type { BadgeReevaluationEventType, EffectBadge, EffectBadgeSource } from "@item/abstract-effect/data.ts";
import type { BadgeReevaluationEventType, EffectBadge } from "@item/abstract-effect/data.ts";
import { AbstractEffectPF2e, EffectBadgeFormulaSource, EffectBadgeValueSource } from "@item/abstract-effect/index.ts";
import { reduceItemName } from "@item/helpers.ts";
import { ChatMessagePF2e } from "@module/chat-message/index.ts";
Expand Down Expand Up @@ -158,7 +158,7 @@ class EffectPF2e<TParent extends ActorPF2e | null = ActorPF2e | null> extends Ab
}

protected override async _preUpdate(
changed: DeepPartial<this["_source"]>,
changed: DeepPartial<EffectSource>,
options: DocumentModificationContext<TParent>,
user: UserPF2e,
): Promise<boolean | void> {
Expand All @@ -170,52 +170,44 @@ class EffectPF2e<TParent extends ActorPF2e | null = ActorPF2e | null> extends Ab
if (duration.value === -1) duration.value = 1;
}

type BadgeUpdateData = DeepPartial<EffectBadgeSource> & DeepPartial<Record<string, unknown>>;
const currentBadge = this.system.badge;
const badgeChange = (changed.system?.badge ?? null) as BadgeUpdateData | null;
if (badgeChange) {
const badgeTypeChanged = badgeChange?.type && badgeChange.type !== currentBadge?.type;
const labels =
"labels" in badgeChange && Array.isArray(badgeChange.labels)
? badgeChange.labels
: currentBadge?.labels;

// Run all badge change checks. As of V12, incoming data is not diffed, so we check the merged result
if (changed.system?.badge) {
const badgeSource = this._source.system.badge;
const badgeChange = fu.mergeObject(changed.system.badge, badgeSource ?? {}, { overwrite: false });
const badgeTypeChanged = badgeChange.type !== badgeSource?.type;
if (badgeTypeChanged) {
// If the badge type changes, reset the value and min/max
badgeChange.value = 1;
} else if (currentBadge?.type === "counter") {
const [minValue, maxValue] = ((): [number, number] => {
const configuredMin = Number(badgeChange.min ?? currentBadge.min);
const configuredMax = Number(badgeChange.max ?? currentBadge.max);

// Even if there are labels setting a max, an AE may have reduced the max (ex: oracle curses)
return labels ? [1, Math.min(labels.length, configuredMax)] : [configuredMin, configuredMax];
})();
} else if (badgeChange.type === "counter") {
// Clamp to the counter value, or delete if decremented to 0
const labels = badgeChange.labels;
const [minValue, maxValue] = labels
? [1, Math.min(labels.length, badgeChange.max ?? Infinity)]
: [badgeChange.min ?? 1, badgeChange.max ?? Infinity];

// Delete the item if it goes below the minimum value, but only if it is embedded
if (typeof badgeChange.value === "number" && badgeChange.value < minValue && this.actor) {
await this.actor.deleteEmbeddedDocuments("Item", [this.id]);
return false;
}

const currentValue = Number(badgeChange.value ?? currentBadge.value ?? 1);
badgeChange.value = Math.clamp(currentValue, minValue, maxValue);
badgeChange.value = Math.clamp(badgeChange.value, minValue, maxValue);
}

// Delete min/max under certain conditions. Foundry is a bit shakey with -= behavior in _preUpdates
if (badgeTypeChanged || labels || badgeChange.min === null) {
// Delete min/max under certain conditions.
if (badgeTypeChanged || badgeChange.labels || badgeChange.min === null) {
delete badgeChange.min;
if ("min" in (this._source.system.badge ?? {})) badgeChange["-=min"] = null;
if (badgeSource) fu.mergeObject(badgeChange, { "-=min": null });
}
if (badgeTypeChanged || labels || badgeChange.max === null) {
if (badgeTypeChanged || badgeChange.labels || badgeChange.max === null) {
delete badgeChange.max;
if ("max" in (this._source.system.badge ?? {})) badgeChange["-=max"] = null;
if (badgeSource) fu.mergeObject(badgeChange, { "-=max": null });
}

// remove loop when type changes or labels are removed
if (badgeChange["-=labels"] === null || badgeTypeChanged) {
if ("loop" in badgeChange && (!badgeChange.labels || badgeTypeChanged)) {
delete badgeChange.loop;
if ("loop" in (this._source.system.badge ?? {})) badgeChange["-=loop"] = null;
if (badgeSource) fu.mergeObject(badgeChange, { "-=loop": null });
}
}

Expand Down

0 comments on commit ae06e6b

Please sign in to comment.