Issue #31: radius-jewel framework#191
Conversation
Generic mechanic for "a jewel socketed in a tree socket modifies passive nodes inside its radius". Ships node-position math, in-radius scans, vanilla node-modifying-jewel identification, and a per-allocated-node mod replay pass — the foundation Timeless (#30) and Cluster (#21) follow-ups plug into. * pob_data::jewel_radius: canonical 3.16+ radii table (Small=960, Medium=1440, Large=1800, Very Large=2400, Massive=2880) plus the 3.15-and-older fallback, with a tree-version picker. * pob_engine::jewel_radius: node_position / nodes_in_radius / allocated_nodes_in_radius / identify_radius_jewel / apply_radius_jewels. Strips "to nearby allocated passives" / "from Passives in Radius" trailers before parsing so mod_parser mints canonical names (Life, Strength, ...) instead of long-form aliases. * Character::socketed_jewels stores SocketedJewels keyed by tree-socket NodeId; round-trips through CharacterSnapshot. * perform::init_env_with_bases runs apply_radius_jewels after item application, emitting one mod copy per in-radius allocated node sourced as Source::Passive(target_node). * Cluster / Abyss / Timeless / Charm jewels share the storage but are filtered out by identify_radius_jewel's subtype check; they route through their own dispatch paths in #21 / #30. Tests: * 11 unit tests in pob_data::jewel_radius and pob_engine::jewel_radius cover the radii table, position math, in-radius scans, identification heuristics, suffix stripping, and end-to-end mod emission. * 7 integration tests in radius_jewels.rs exercise the framework against the real 3_25.json tree fixture, including a regression asserting empty-alloc compute is unchanged from baseline. cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace are all clean (302 tests pass, up from 291 baseline). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
02d64b1 to
ec6220c
Compare
jonatanferm
left a comment
There was a problem hiding this comment.
[loop-status: lgtm] Foundation for the radius-jewel mechanic — Timeless (#30) and the named-unique handlers (Watcher's Eye etc.) build on this. Right architecture: pob_data::jewel_radius ships the canonical radii table with tree-version picker, pob_engine::jewel_radius exposes the math + scan + identification + replay; Character.socketed_jewels round-trips through CharacterSnapshot. Each in-radius allocated node receives a Source::Passive(target_node) attribution so breakdown shows the right contributing chain. HandlerKind enum is the right dispatch surface for unique-named handlers landing later. Conflict with #190 was a clean dual-storage situation (cluster's Character.jewels alongside radius's Character.socketed_jewels) — kept both since they target different mechanics; the PR body itself flags that the two will eventually unify. CI green post-rebase, +18 tests including end-to-end against the live 3.25 tree with a regression for empty-alloc baseline drift.
…hy Mind / Fertile Mind) (#226) Plug three named uniques into the framework PR #191 set up — each gets a bespoke `HandlerKind` variant and dedicated dispatch in `apply_radius_jewels`. Watcher's Eye exercises the aura-conditional path; the two transformer uniques exercise per-attribute mod transforms. - `WatchersEye`: bypasses the radius scan; mods land globally with their per-aura `AffectedBy<Aura>` Condition tag (emitted by `mod_parser`'s new dynamic `match_while_var_dyn` for "while affected by <Aura>" clauses). A new `detect_active_auras` pass in perform.rs flips on the matching conditions for every enabled aura/herald gem on the player. - `LifeToManaTransform` (Healthy Mind): walks each in-radius allocated node's `.stats`, finds Inc Life mods, emits Inc Mana mods at 200% scale sourced as the in-radius node so per-node breakdowns line up. - `DexToIntTransform` (Fertile Mind): per-attribute Base mod transform with a counter `-N Dex` so the source attribute is moved, not duplicated. Intuitive Leap and Pure Talent are deferred to follow-up slices — Intuitive Leap needs pathfind connectivity changes; Pure Talent needs class-conditional notable-buff machinery. Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Generic mechanic for "a jewel socketed in a tree socket modifies passive nodes inside its radius" — the foundation that Timeless jewels (#30) and Cluster jewels (#21) both build on. This PR ships the generic infrastructure plus end-to-end identification + application of vanilla node-modifying radius jewels (e.g.
10% increased Maximum Life to nearby allocated passiveson a Crimson Jewel).pob_data::jewel_radius: canonical 3.16+ radii table (Small=960, Medium=1440, Large=1800, Very Large=2400, Massive=2880) plus the 3.15-and-older fallback, with a tree-version picker that mirrors PoB'ssetJewelRadiiGlobally.pob_engine::jewel_radius: node-position math (group + orbit + orbit_index → Cartesian),nodes_in_radius/allocated_nodes_in_radiusfor scanning,identify_radius_jewelfor detecting vanilla radius jewels via mod text, andapply_radius_jewelsfor the per-allocated-node mod replay. The radius-suffix stripper (to nearby allocated passives,from Passives in Radius, …) letsmod_parsermint canonical names (Life,Strength) instead of long-form aliases.Character::socketed_jewels(SocketedJewelskeyed by tree-socket node id) round-trips throughCharacterSnapshot.perform::init_env_with_basescallsapply_radius_jewelsafter item application; each in-radius allocated node receives one mod copy sourced asSource::Passive(target_node)so the breakdown attributes the bonus to the in-radius node.identify_radius_jewel's subtype check — they keep their own dispatch paths in Cluster jewel sub-graph support #21 / Timeless jewels — keystone/notable replacement within radius #30.Deferred to follow-ups (tracked by their own issues):
HandlerKindenum onRadiusJewelenumerates the dispatch surface they'll plug into.<Slot name="Jewel <NodeId>" itemId="...">.Test plan
cargo fmt --all -- --checkclean.cargo clippy --workspace --all-targets -- -D warningsclean.cargo test --workspace— 302 tests pass (was 291 baseline).pob_data::jewel_radius+pob_engine::jewel_radiuscover the radii table, position math, scan, identification (Cluster/Abyss/Timeless skipped, non-radius rare jewel skipped, explicit ring sizes), suffix stripping, and end-to-end mod emission.crates/pob-engine/tests/radius_jewels.rsexercise the framework against the real3_25.jsontree fixture, including a regression that empty-alloc compute is byte-identical to baseline (no drift when no nodes are in radius).10% increased Maximum Life to nearby allocated passivesinto a real tree socket plants anInc Lifemod sourced from the in-radius allocated passive in the modDB.🤖 Generated with Claude Code