Pickups System — System Guarantees Matrix¶
This page defines the runtime guarantees provided by the RevFramework Pickups system.
What a guarantee means
A guarantee means the system always behaves this way when used through its supported APIs and runtime components.
If something is not listed here, it should not be relied upon as a system invariant.
Purpose¶
The guarantees matrix answers the question:
“What behaviour can gameplay code safely depend on?”
This is different from:
| Document | Purpose |
|---|---|
| Overview | What the system does |
| Mental Model | How the system is structured |
| Public API | What developers can call |
| Guarantees Matrix | What behaviour is guaranteed |
Core Pickup Guarantees¶
| Area | Guarantee | Notes |
|---|---|---|
| Consumption | A pickup can only be consumed once per physics step | Prevents duplicate triggers from multi-collider actors |
| Effect execution | A successful pickup produces a single effect execution chain | Decorators wrap the effect into one deterministic chain |
| Destroy behaviour | A pickup either destroys or re-arms, never both | Controlled by destroyOnUse or respawn configuration |
| Trigger routing | Trigger events are routed through IPickupTriggerReceiver when using relay components | Relays normalize physics callbacks |
| Actor resolution | Actor GameObject passed to the pickup is resolved consistently from the trigger source | Rigidbody root is preferred when present |
Effect Guarantees¶
| Area | Guarantee | Notes |
|---|---|---|
| Application routing | When using built-in runtime components, effects are applied via PickupEffectRunner | Ensures correct dispatch between standard and context-aware paths |
| Context routing | Context-aware effects receive ItemUseContext when provided | Runner selects the correct entrypoint automatically |
| Null target safety | Effects that do not implement IEffectAllowsNullDamageable will not run without an IDamageable | Enforced by PickupEffect |
| Context optionality | ItemUseContext values may be null or unset | Effects must tolerate missing data |
| Composite effects | Composite effects execute child effects in declared order | Each child enforces its own gating and cooldown rules |
Decorator Guarantees¶
| Area | Guarantee | Notes |
|---|---|---|
| Order | Decorators execute in deterministic priority order | Lower priority wraps first |
| Wrapping | Decorators form a single nested execution chain around the core effect | Built by PickupEffectFactory |
| Cancellation | A decorator may cancel execution of the wrapped effect chain | Cancelling skips the wrapped effect and any inner decorators |
| Chain stability | Decorator structure is fixed after factory construction | Runtime does not mutate the chain |
Authority Guarantees¶
Every system that mutates state exposes the same kind of authority seam, and the differences that do exist — absent default, scope, discovery, what happens when your implementation throws — are tabulated in the cross-system authority reference. They are real differences: seven interfaces across six systems, one of them assigned rather than discovered, and four systems with no seam at all. Nothing in Pickups linked to the comparison before, so it was there and unreachable.
| Area | Guarantee | Notes |
|---|---|---|
| Authority check | The shipped trigger pickups — TriggerPickup and UnifiedPickup2D — do not execute effects when authority explicitly denies the action | Checked through PickupAuthority on every accepted enter |
| Interactable pickups | ✔ InteractablePickupBase checks, since 1.3.0. Every subclass is covered without writing a gate | Resolved in TryPickup before DoPickup runs, so a denied actor never reaches your delivery code. Before 1.3.0 the base enforced nothing and only InventoryPickupInteractable checked |
| Optional authority | If no authority implementation is resolved, pickups proceed normally | Authority is optional |
Other IPickupTriggerReceivers | ❌ Not gated. LootPickupPayload rides the same trigger plumbing and never resolves IPickupAuthority, so a scene-wide denying binder does not stop a loot pickup | Deliberate: Loot's gate is the delivery boundary, where the receiving Inventory or Currency authority answers. See the cross-system authority reference |
| How a denial is reported | Two shapes, neither of them a result type. TriggerPickup and UnifiedPickup2D return silently; every InteractablePickupBase subclass reports an ordinary failed attempt — PickupFailed, onPickupFailed and the fail feedback | Indistinguishable from no item, no service or a full bag. Pickups has no result type by design; branch inside your authority, not on the outcome |
| Effect cooldown after a refusal | ✔ Not started. PickupEffect consults the reporting seam first and opens the per-owner window only for a delivery that happened | Before 1.3.0 a refused attempt burned the cooldown, and the actor's retry was refused by a window opened for nothing |
| Resolver consistency | Authority resolution follows a consistent lookup path (context → scene → global fallback) | Ensures predictable behaviour |
| Per-pickup-hierarchy authority | A binder above a pickup — on its prefab or on a parent it sits under — gates those pickups only | The hierarchy step is checked first and is never cached per scene, so two pickups under different parents each resolve their own |
| Scope of the answer | Resolution starts at the pickup, never at the actor. The actor is only the argument to HasAuthority | A binder placed on a player does not scope the answer to that player: a world pickup does not walk through the player, so the binder is found by the scene-root step and asked about every actor. Express per-actor rules inside your implementation, from the actor it is handed |
Interactables are gated by the base, since 1.3.0
This row read as an unconditional guarantee before 1.2.0, when it was not one; 1.2.0 corrected it to say the base enforced nothing and scheduled the check for this release. The check now lives in TryPickup, which resolves PickupAuthority and refuses before calling DoPickup.
If you do not use pickup authority, nothing changes. Resolve returns null when no implementation is in reach and the pickup proceeds, which is the optional-authority rule the rest of the system already follows.
Migrating from 1.2.0. A subclass that already resolved and tested authority inside DoPickup can drop that check — leaving it is harmless, it resolves twice for the same answer. A subclass deliberately left ungated in a project that has an IPickupAuthority is now gated by it: that is the one behaviour change. If a particular pickup must ignore the scene's authority, scope the authority rather than the pickup — a binder on the pickup's own prefab or parent answers for that hierarchy only.
Feedback Guarantees¶
| Area | Guarantee | Notes |
|---|---|---|
| Success feedback | Success feedback executes after a successful pickup | Triggered by runtime components |
| Failure feedback | Failure feedback executes when pickup conditions fail | Effect is not applied in this case |
| Feedback isolation | Feedback components do not affect effect execution logic | Purely presentation/UX |
Runtime Component Guarantees¶
| Area | Guarantee | Notes |
|---|---|---|
| Interaction modes | Interactable pickups support Auto, Press, and Hold modes | Defined by PickupMode |
| Prompt behaviour | Prompt UI is shown only when interaction requires input | Hidden for Auto mode |
| Facing checks | Facing requirements are optional and configurable | Controlled by dot threshold; -1 (the default) disables the check entirely |
| Which plane facing is measured in | facingPlane — XZ for top-down, XY for side-on, Auto (the default) to keep the pre-1.3.0 guess | Auto picks the second axis per call from that pickup's geometry, so a raised pickup can be refused while the actor looks straight at it. Name the plane your game uses |
| Where the facing provider is looked for | On the actor, then its children — the same walk IInputService gets | Before 1.3.0 the root only, so a provider on a rig or sprite child was never found and the transform answered instead |
| Respawn behaviour | Respawn restores visibility and interaction state without duplicating objects | Same instance reused |
| Duplicate prevention | Trigger-based pickups guard against multi-collider double-consume within the same physics step | Internal latch mechanism |
Integration Guarantees¶
| Area | Guarantee | Notes |
|---|---|---|
| System independence | Pickups does not require other gameplay systems to function | Fully standalone |
| Integration model | External systems integrate through effects or decorators | No hard dependencies |
| Determinism | Integration behaviour runs within the same effect chain | Preserves execution order |
Explicit Non-Guarantees¶
The system intentionally does not guarantee
- Inventory behaviour
- Health rules or clamping policies
- Status effect stacking logic
- Save/load persistence
- Multiplayer replication policy
- Input system implementation
- UI behaviour outside pickup components
- External system availability
These responsibilities belong to other systems or the host project.
Summary¶
The Pickups system guarantees
- deterministic effect-chain execution
- safe and consistent trigger routing
- deterministic decorator wrapping
- optional authority gating
- isolated feedback behaviour
- predictable interaction flows
Everything else is intentionally delegated to integrations or higher-level gameplay systems.