Skip to content

Hostile Consumer Panels

This folder contains hostile-consumer teachable panels for the Pickups system.

These panels are intentionally written as if they lived in a completely separate Unity project consuming RevFramework from the outside.

They verify that the Pickups public API surface is complete, stable, and usable without internal access.


What "Hostile Consumer" Means

Panels in this folder are restricted to using only:

  • Public APIs
  • Documented runtime components
  • Supported extension seams
  • Teaching UI utilities

They may not:

  • reference .Internal namespaces
  • use reflection to access private state
  • call undocumented .Core helpers
  • depend on sample/demo utilities
  • rewire the composition of scene objects they do not own

If a panel requires any of those behaviours, the panel must not live in this folder.


Panels in This Folder

Panel Teaches
PickupsQuickstartPanel The smallest honest flow: bind an actor and a PickupEffect, preflight which of the three dispatch paths the runner will take, then apply through PickupEffectRunner.TryApply and print whether the payload was delivered or refused.
PickupsDecoratorsAndApplyPathPanel The live decorator chain from outer wrapper to core effect, then application through the same supported runtime path — where a cancelled decorator or a live cooldown comes back as a refusal.
PickupsWorldAndInteractablesPanel Spawning world pickups from a runtime prefab catalog, configuring an authored TriggerPickup, and reporting which IPickupTriggerReceiver the prefab actually carries.

What the World Panel Does Not Do

PickupsWorldAndInteractablesPanel does not add a pickup component to what it spawns, and the reason is the lesson.

TriggerRelay2D and TriggerRelay3D resolve a single IPickupTriggerReceiver in Awake and cache it. Awake has already run by the time Instantiate returns, so a TriggerPickup added after that is never wired to the relay: it sits on the object receiving nothing, and every pickup you then see succeed was delivered by something else. The panel used to add one and report success anyway.

It now reads the instance the same way the relay does — GetComponent<IPickupTriggerReceiver>() on the spawned object — and says which component it found, or says plainly that there is none and that this cannot be repaired from here. The pickup component belongs on the prefab. LootPickupSpawner carries the same rule for the same reason.

It also does not drive interaction: mode, interact key, hold duration, facing threshold and respawn are InteractablePickupBase fields authored on a prefab. The panel spawns such prefabs and names what it found; walking up and pressing the key is what exercises them.


Why the World Panel Still Counts

PickupsWorldAndInteractablesPanel calls AddComponent, which normally disqualifies a panel from this folder. It stays here, and the distinction is worth stating because it is the line the whole folder rests on.

The panel adds components to an object it instantiated itself — a prefab instance it spawned to demonstrate world pickups — and only in one narrow case: a SphereCollider and a TriggerRelay3D, both shipped public runtime types, when the prefab has no collider at all. Adding a relay late is the safe direction of the ordering rule above, because the relay resolves in its own Awake and finds a receiver the prefab already authored.

That is what a consumer writes when spawning a pickup at runtime. It is not the same as reaching into an object the scene already owned and rebuilding it, which is what the panels in a Demos/ folder are permitted to do and these are not.

The rule is whose object is being changed, not whether AddComponent appears.


Reading These Panels

Copy the service calls and the result handling. Ignore the IMGUI layout — it is scaffolding, not a UI recommendation.

Each panel shows the result the runtime actually returned rather than a hand-computed prediction. Where a panel displays its own explanatory breakdown, the runner's result is the one to trust.

Concretely: the two apply panels call PickupEffectRunner.TryApply(...), which returns a bool saying whether the payload reached the actor, and they render false as a refusal. The void Apply(...) and ApplyTo(...) overloads discard that answer, which is why a panel built on them could show a green "Applied…" over a cooldown that had refused the effect. If you copy one thing out of these panels, copy that call.


Related