From 7e40449e6820cacc969c16f5697209f142ca3d9c Mon Sep 17 00:00:00 2001 From: Alexander Zielonka Date: Mon, 27 Apr 2026 14:04:54 +0200 Subject: [PATCH] feat(01-02): add apply-attribute-boost dependency from Plan 01-01 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rule 3 deviation: Plan 01-01 (wave 0) work not yet merged into this worktree base (096edbf). Plan 01-02 imports applyAttributeBoost and AbilityAbbreviation from this module — required for types.ts and recompute-derived-stats.ts to compile. Content matches 01-01 plan exactly, so orchestrator merge will be clean. - export type AbilityScore = number - export type AbilityAbbreviation = 'STR'|'DEX'|'CON'|'INT'|'WIS'|'CHA' - export function applyAttributeBoost(score) — PF2e boost-cap-at-18 (Pitfall #8) - export function isValidBoostSet(targets) — exactly 4 distinct --- .../leveling/lib/apply-attribute-boost.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 server/src/modules/leveling/lib/apply-attribute-boost.ts diff --git a/server/src/modules/leveling/lib/apply-attribute-boost.ts b/server/src/modules/leveling/lib/apply-attribute-boost.ts new file mode 100644 index 0000000..d5a586b --- /dev/null +++ b/server/src/modules/leveling/lib/apply-attribute-boost.ts @@ -0,0 +1,26 @@ +/** + * Pure function module — no NestJS, no Prisma, no I/O. + * PF2e Boost Cap rule: +2 if score < 18, +1 if score >= 18 (Pitfall #8). + * See: .planning/phases/01-level-up-pf2e-regelkonform/01-RESEARCH.md §Pattern 3 + */ + +export type AbilityScore = number; + +export type AbilityAbbreviation = 'STR' | 'DEX' | 'CON' | 'INT' | 'WIS' | 'CHA'; + +/** + * Applies a single PF2e attribute boost to a score. + * - Score below 18 → +2 + * - Score at or above 18 → +1 (cap rule, prevents Pitfall #8) + */ +export function applyAttributeBoost(currentScore: AbilityScore): AbilityScore { + return currentScore >= 18 ? currentScore + 1 : currentScore + 2; +} + +/** + * Validates a level-up boost set: must be exactly 4 distinct abilities. + * PF2e: at boost levels (5/10/15/20), the player picks 4 different attributes. + */ +export function isValidBoostSet(targets: readonly string[]): boolean { + return targets.length === 4 && new Set(targets).size === 4; +}