Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/module/actor/creature/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ abstract class CreaturePF2e<
domain["derived-from-land"] = true;
}
const statistic = selected.derivedFromLand
? landSpeed.extend({ type, base: selected.value, source: selected.source })
? landSpeed.extend({ type, base: selected.value, source: selected.source, derivedFromLand: true })
: new SpeedStatistic(this, {
type,
base: selected.value,
Expand Down
9 changes: 7 additions & 2 deletions src/module/rules/rule-element/base-speed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@ class BaseSpeedRuleElement extends RuleElement<BaseSpeedRuleSchema> {
if (!Number.isInteger(value)) this.failValidation("Failed to resolve value");
return null;
}
// Whether this speed is derived from the creature's land speed
const landTotal = this.actor.system.movement?.speeds?.land?.value ?? 0;
// Whether this speed is derived from the land speed.
// If choices are between constant and land speed, only true if land speed was chosen.
const derivedFromLand =
type !== "land" && typeof this.value === "string" && this.value.includes("movement.speeds.land.value");
type !== "land" &&
typeof this.value === "string" &&
this.value.includes("movement.speeds.land.value") &&
value === landTotal;
return { type: type, value, source: this.getReducedLabel(), derivedFromLand };
};
}
Expand Down
16 changes: 16 additions & 0 deletions src/module/rules/rule-element/flat-modifier.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DeferredValueParams, MODIFIER_TYPES, Modifier, ModifierType } from "@actor/modifiers.ts";
import { AttributeString } from "@actor/types.ts";
import { MOVEMENT_TYPES } from "@actor/values.ts";
import { damageCategoriesUnique } from "@scripts/config/damage.ts";
import { DamageCategoryUnique } from "@system/damage/types.ts";
import {
Expand Down Expand Up @@ -129,6 +130,21 @@ class FlatModifierRuleElement extends RuleElement<FlatModifierSchema> {
if (selector === "null") continue;

const construct = (options: DeferredValueParams = {}): Modifier | null => {
// If same rule affects land- and another speed: omit non-land modifier when base is land-derived.
const testRollOptions = options.test ?? [];
const derivedFromLand = Array.isArray(testRollOptions)
? testRollOptions.includes("derived-from-land")
: testRollOptions.has("derived-from-land");
if (
selectors.includes("land-speed") &&
selector !== "land-speed" &&
selector.endsWith("-speed") &&
MOVEMENT_TYPES.includes(selector.replace(/-speed$/, "") as never) &&
derivedFromLand
) {
return null;
}

const resolvedValue = Number(this.resolveValue(this.value, 0, options)) || 0;
if (this.ignored) return null;

Expand Down
26 changes: 22 additions & 4 deletions src/module/system/statistic/speed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MovementType } from "@actor/types.ts";
import { extractModifierAdjustments, extractModifiers } from "@module/rules/helpers.ts";
import { ErrorPF2e, localizer } from "@util";
import { BaseStatistic } from "./base.ts";
import { BaseStatisticData, BaseStatisticTraceData } from "./data.ts";
import { BaseStatisticData, BaseStatisticTraceData, StatisticData } from "./data.ts";

class SpeedStatistic<TActor extends ActorPF2e, TType extends MovementType | "travel"> extends BaseStatistic<TActor> {
constructor(actor: TActor, options: SpeedStatisticData<TType>) {
Expand All @@ -30,6 +30,15 @@ class SpeedStatistic<TActor extends ActorPF2e, TType extends MovementType | "tra
this.modifiers = [...syntheticModifiers, ...additionalModifiers];
}

/** Roll options for this speed, including {@link SpeedStatisticData.derivedFromLand} when applicable. */
override createRollOptions(domains = this.domains): Set<string> {
const set = super.createRollOptions(domains);
if ((this.data as StatisticData & { derivedFromLand?: boolean }).derivedFromLand) {
set.add("derived-from-land");
}
return set;
}

/** The movement type for this statistic */
type: TType;

Expand Down Expand Up @@ -64,8 +73,15 @@ class SpeedStatistic<TActor extends ActorPF2e, TType extends MovementType | "tra

/** Derive a travel speed from this statistic. */
extend<TType extends MovementType | "travel">(options: ExtendParams<TType>): SpeedStatistic<TActor, TType> {
const { type, base = this.value, modifiers = [], source = this.source } = options;
return new SpeedStatistic(this.actor, { type, base, modifiers, domains: [`${type}-speed`], source });
const { type, base = this.value, modifiers = [], source = this.source, derivedFromLand } = options;
return new SpeedStatistic(this.actor, {
type,
base,
modifiers,
domains: [`${type}-speed`],
source,
derivedFromLand,
});
}

override getTraceData(): TType extends "land"
Expand Down Expand Up @@ -97,6 +113,8 @@ interface SpeedStatisticData<TType extends MovementType | "travel"> extends Omit
base?: number;
/** A feature, ancestry, effect, etc. from which this speed originated */
source?: string | null;
/** Is this speed based from land; used to prevent double-application of modifiers. */
derivedFromLand?: boolean;
}

interface SpeedStatisticTraceData<
Expand All @@ -115,7 +133,7 @@ interface LandSpeedStatisticTraceData extends SpeedStatisticTraceData<"land"> {

interface ExtendParams<TType extends MovementType | "travel"> extends Pick<
SpeedStatisticData<TType>,
"type" | "base" | "modifiers" | "source"
"type" | "base" | "modifiers" | "source" | "derivedFromLand"
> {}

export { SpeedStatistic };
Expand Down
Loading