Skip to content

Add product effect override callbacks for players/NPCs#56

Merged
ifBars merged 1 commit intoifBars:stablefrom
JumbleBumble:ProductEffects
Mar 15, 2026
Merged

Add product effect override callbacks for players/NPCs#56
ifBars merged 1 commit intoifBars:stablefrom
JumbleBumble:ProductEffects

Conversation

@JumbleBumble
Copy link

@JumbleBumble JumbleBumble commented Mar 13, 2026

Implement a callback system in ProductManager to allow registration, removal, and clearing of custom effect handlers for both player and NPC product effects. Add Harmony patches to intercept effect application and invoke registered callbacks, with optional fallback to base behavior. Update documentation with usage examples for the new override system, enabling modders to customize or extend product effect logic.

Release Notes

Product Effect Override Callbacks

  • Introduced a callback system in ProductManager to enable custom effect handlers for both player and NPC product effects
  • Added Harmony patches to intercept product effect application for all concrete item types
  • Implemented effect resolution logic to extract effects from product instances and their definitions via reflection
  • Added public APIs to register, replace, remove, and clear effect callbacks with optional fallback to default behavior
  • Implemented dynamic NPC wrapper resolution to map concrete NPCs to API NPCs during effect application
  • Added comprehensive documentation with usage examples for the new override system

Contributor Statistics

Author Lines Added Lines Removed
Jumble 545 0

Implement a callback system in ProductManager to allow registration, removal, and clearing of custom effect handlers for both player and NPC product effects. Add Harmony patches to intercept effect application and invoke registered callbacks, with optional fallback to base behavior. Update documentation with usage examples for the new override system, enabling modders to customize or extend product effect logic.
@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

The pull request introduces a centralized product effect callback interception system. A new Harmony patch intercepts effect applications to players and NPCs, resolving effects and invoking registered callbacks before conditionally applying default behavior. ProductManager adds public callback registration APIs with control over default effect execution, and documentation describes the callback patterns.

Changes

Cohort / File(s) Summary
Effect Callback System Implementation
S1API/Internal/Patches/ProductEffectPatches.cs, S1API/Products/ProductManager.cs
Introduces Harmony prefix patch to intercept ApplyEffectsToPlayer/ApplyEffectsToNPC methods, resolve effects via reflection and wrapper fallbacks, and invoke registered callbacks. ProductManager adds public SetEffectCallback/SetNpcEffectCallback APIs with allowDefaultEffect control, plus removal and clearing methods. Internal invocation helpers coordinate callback execution and default effect application.
Documentation
S1API/docs/products-api.md
Adds "Overriding product effect behavior with callbacks" section with examples for player and NPC callback registration, including SetEffectCallback, RemoveEffectCallback, ClearEffectCallbacks patterns and corresponding NPC variants.

Sequence Diagram

sequenceDiagram
    participant Player/NPC as Player or NPC
    participant Game as Game Code
    participant Patch as ProductEffectPatches
    participant Manager as ProductManager
    participant Effect as Effect Application

    Player/NPC->>Game: ApplyEffectsToPlayer/NPC
    Game->>Patch: ApplyEffects_Prefix (Harmony intercept)
    Patch->>Patch: ResolveEffects from ProductInstance
    Patch->>Manager: TryInvokeEffectCallback/NpcCallback
    Manager->>Manager: Lookup registered callback
    alt Callback Registered
        Manager->>Manager: Invoke callback (catch errors)
        Manager-->>Patch: Return (callback_found, allowDefaultEffect)
    else No Callback
        Manager-->>Patch: Return (not_found, n/a)
    end
    alt allowDefaultEffect = true
        Patch->>Effect: Apply default effect behavior
    end
    Patch-->>Game: Return false (suppress original)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hop, hop, the effects now dance,
Through callbacks woven with care's advance,
Patches catch what once ran free,
Managers orchestrate, harmoniously!
Effects bend to custom will with glee. 🎯✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.18% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main feature being added: a callback system for overriding product effect behavior for both players and NPCs.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can customize the tone of the review comments and chat replies.

Configure the tone_instructions setting to customize the tone of the review comments and chat replies. For example, you can set the tone to Act like a strict teacher, Act like a pirate and more.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@S1API/docs/products-api.md`:
- Around line 84-110: Update the docs to explicitly state that
ProductManager.SetEffectCallback (and removals via
ProductManager.RemoveEffectCallback / ClearEffectCallbacks) apply only to the
local player: note that the runtime routing in ProductEffectPatches only invokes
callbacks when the effect target equals the local player, so callbacks will not
run for other players or NPCs; clarify this behavior next to the Player
callbacks section and when describing allowDefaultEffect so modders do not
expect non-local targets to be intercepted.

In `@S1API/Internal/Patches/ProductEffectPatches.cs`:
- Around line 79-130: The patch currently always returns false after
ResolveEffects succeeds, altering base behavior even when no callbacks are
registered; modify the logic in the prefix (around ResolveEffects, effects, and
invokedEffectIds) to pre-scan effects and detect if any effect.ID has a
registered override by calling ProductManager.TryInvokeEffectCallback (for
players) and ProductManager.TryInvokeNpcEffectCallback (for NPCs) before
applying changes—if no effect IDs have any registered player/NPC callback for
this target, return true to leave the original method untouched; otherwise
proceed with the existing loop and return false only when at least one effect ID
is handled by a registered callback.
- Around line 42-61: TargetMethods() is returning duplicate MethodBase entries
because it scans all ProductItemInstance subtypes including abstract types and
uses GetMethod without DeclaredOnly so inherited base methods appear for every
child; update the scan to skip abstract types (check type.IsAbstract), call
GetMethod with BindingFlags.DeclaredOnly added to the existing flags for both
"ApplyEffectsToPlayer" and "ApplyEffectsToNPC", and deduplicate the resulting
sequence (e.g., call .Distinct()) before casting to MethodBase so each actual
declared implementation is patched only once.

In `@S1API/Products/ProductManager.cs`:
- Around line 264-298: The lookup methods TryInvokeEffectCallback and
TryInvokeNpcEffectCallback currently execute registration.Callback and thus can
let exceptions suppress the base effect; change them to only perform lookup and
expose the callback without invoking it: keep the early null/whitespace checks
and dictionary lookup against EffectCallbacks / NpcEffectCallbacks, set
allowDefaultEffect = registration.AllowDefaultEffect, and return the delegate
via a new out parameter (e.g., out Action<Player> playerCallback and out
Action<NPC> npcCallback) instead of calling registration.Callback inside those
methods; then update the caller ProductEffectPatches.ApplyEffects_Prefix to
invoke the returned delegate inside a try/catch so exceptions don’t affect
allowDefaultEffect or the decision to apply the default effect.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e72d58f-44a8-4f38-9043-08bba874cff2

📥 Commits

Reviewing files that changed from the base of the PR and between f53a98c and 9bc78b3.

📒 Files selected for processing (3)
  • S1API/Internal/Patches/ProductEffectPatches.cs
  • S1API/Products/ProductManager.cs
  • S1API/docs/products-api.md

@ifBars ifBars self-assigned this Mar 14, 2026
@ifBars ifBars added the enhancement New feature or request label Mar 14, 2026
Copy link
Owner

@ifBars ifBars left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your PR 😄

@ifBars ifBars merged commit 70eb35e into ifBars:stable Mar 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants