fix(rules): shield of faith grants its SRD deflection AC buff (+2, +1 per six caster levels, max +5) via a new BuffEffect flat scaling
This commit is contained in:
@@ -6461,7 +6461,10 @@ class CombatEngine:
|
||||
effect, target, caster=caster, saved=saved,
|
||||
empower=empower, maximize=maximize, spell_name=spell_name,
|
||||
)
|
||||
return self._apply_buff_effect(effect, target, spell_name=spell_name)
|
||||
return self._apply_buff_effect(
|
||||
effect, target, spell_name=spell_name,
|
||||
caster_level=caster.combatant.level,
|
||||
)
|
||||
|
||||
def _apply_damage_effect(
|
||||
self,
|
||||
@@ -6678,16 +6681,42 @@ class CombatEngine:
|
||||
return f"+{actual} HP"
|
||||
|
||||
def _apply_buff_effect(
|
||||
self, effect: BuffEffect, target: CombatantState, *, spell_name: str
|
||||
self,
|
||||
effect: BuffEffect,
|
||||
target: CombatantState,
|
||||
*,
|
||||
spell_name: str,
|
||||
caster_level: int,
|
||||
) -> str:
|
||||
# PF1e caster-level scaling on buff values (SRD shield of faith
|
||||
# "+2 deflection, +1 per six caster levels (maximum +5)"): the flat
|
||||
# extra is added to EVERY modifier of the buff.
|
||||
modifiers = effect.modifiers
|
||||
if effect.scaling_flat is not None:
|
||||
extra = min(
|
||||
effect.scaling_flat.max_amount,
|
||||
(caster_level // effect.scaling_flat.per_levels)
|
||||
* effect.scaling_flat.amount,
|
||||
)
|
||||
if extra > 0:
|
||||
modifiers = tuple(
|
||||
StatModifier(
|
||||
target=mod.target,
|
||||
value=mod.value + extra,
|
||||
bonus_type=mod.bonus_type,
|
||||
condition=mod.condition,
|
||||
weapon_filter=mod.weapon_filter,
|
||||
)
|
||||
for mod in modifiers
|
||||
)
|
||||
target.active_buffs.append(
|
||||
ActiveBuff(
|
||||
modifiers=effect.modifiers,
|
||||
modifiers=modifiers,
|
||||
rounds_left=effect.duration_rounds,
|
||||
source=f"spell: {spell_name}",
|
||||
)
|
||||
)
|
||||
return f"buff {len(effect.modifiers)} mods ({effect.duration_rounds}r)"
|
||||
return f"buff {len(modifiers)} mods ({effect.duration_rounds}r)"
|
||||
|
||||
def _apply_penalty_effect(
|
||||
self,
|
||||
@@ -6912,7 +6941,11 @@ class CombatEngine:
|
||||
)
|
||||
if isinstance(effect, BuffEffect):
|
||||
duration = effect.duration_rounds * 2 if has_extend else effect.duration_rounds
|
||||
return BuffEffect(modifiers=effect.modifiers, duration_rounds=duration)
|
||||
return BuffEffect(
|
||||
modifiers=effect.modifiers,
|
||||
duration_rounds=duration,
|
||||
scaling_flat=effect.scaling_flat,
|
||||
)
|
||||
if isinstance(effect, PenaltyEffect):
|
||||
duration = (
|
||||
effect.duration_rounds_per_level * 2
|
||||
|
||||
@@ -213,8 +213,17 @@ class HealEffect:
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BuffEffect:
|
||||
"""Transient stat modifiers (PF1e buff spells).
|
||||
|
||||
``scaling_flat`` (SRD shield of faith "+2 deflection, +1 per six caster
|
||||
levels (maximum +5)", divine favor "+1 per three caster levels (maximum
|
||||
+3)") adds the resolved flat bonus to EVERY modifier's value at cast
|
||||
time, mirroring ``DamageEffect.effective_formula``'s flat scaling.
|
||||
"""
|
||||
|
||||
modifiers: tuple[StatModifier, ...]
|
||||
duration_rounds: int
|
||||
scaling_flat: DamageFlatScaling | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -611,9 +620,12 @@ def _parse_heal_effect(effect: dict[str, Json]) -> HealEffect:
|
||||
|
||||
|
||||
def _parse_buff_effect(effect: dict[str, Json]) -> BuffEffect:
|
||||
flat_raw = effect.get("scaling_flat")
|
||||
scaling_flat = None if flat_raw is None else _parse_flat_scaling(flat_raw)
|
||||
return BuffEffect(
|
||||
modifiers=tuple(_parse_modifier(m) for m in _as_list(effect["modifiers"])),
|
||||
duration_rounds=_as_int(effect.get("duration_rounds", 0)),
|
||||
scaling_flat=scaling_flat,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user