Equipped-weapon handling authority
This API lets one standalone addon choose high-level equipped-weapon handling policy while ROCK remains the low-level executor for hand physics, weapon-node ownership, input routing, haptics, and inventory interaction.
It is designed for ambidextrous weapon handling, firing-hand detach and handoff, physical grip-zone equip, shoulder stashing, and tuned grip feedback. It does not change the player's physical controller identity or Fallout 4 VR's native handedness setting.
Ownership model
Equipped-weapon handling is a singleton rolling lease. One consumer owns the
policy at a time; a second owner receives OwnerConflict. If the owner stops
refreshing, unregisters, faults, changes generation, or loses ROCK, behavior
fails closed to ROCK's configured firing-hand policy.
Request EquippedWeaponHandlingAuthority during registration and check that it
was granted before publishing.
setEquippedWeaponHandlingAuthorityV1 · slot 48
RockProviderResultV1 setEquippedWeaponHandlingAuthorityV1(
std::uint64_t ownerToken,
const RockProviderEquippedWeaponHandlingRequestV1* request);
Publishes or refreshes the complete policy. A request needs exact size, supported version, at least one implemented flag, finite in-range tuning, nonzero lease, and matching optional generation guards. Leases are clamped to 120 frames.
Behavior flags
| Flag | What it enables |
|---|---|
FiringGripOwnership | The firing grip becomes a physical ownership role managed by the authority. |
PrimaryDetach | The current firing hand may detach while the weapon remains under valid physical ownership. |
AmbidextrousHandoff | The opposite physical hand can become the firing hand through a valid handoff. |
GripZoneEquip | Grabbing the physical firing-grip zone can equip/claim the weapon. |
GripZoneHoverHaptics | Provides bounded hover feedback near that equip zone. |
FiringGripProximitySupport | Replaces ROCK's configured proximity radius while leased; visual-only support itself remains a core behavior. |
EquippedWeaponShoulderStash | Allows the equipped weapon to move through the shoulder-stash interaction. |
PipboyTriggerHandEquip | Allows the Pip-Boy-side trigger hand to participate in the authority's equip behavior. |
EquipVisualBridge | ABI-compatible tuning switch; ROCK's visual bridge and native attach recovery are now unconditional correctness services. |
Dependency rules are enforced:
PrimaryDetach,AmbidextrousHandoff,GripZoneEquip,EquippedWeaponShoulderStash, andPipboyTriggerHandEquiprequireFiringGripOwnership;EquippedWeaponShoulderStashalso requiresPrimaryDetach.
GripZoneHoverHaptics and FiringGripProximitySupport can be selected without
claiming firing-grip ownership when an addon only wants those bounded policy
adjustments.
Tuning ranges
Out-of-range or non-finite values reject the entire transaction.
| Field | Valid range | Unit / purpose |
|---|---|---|
gripZoneEquipRadiusGameUnits | 0.25–30.0 | Equip-zone radius |
gripZoneEquipSettleSeconds | 0.0–5.0 | Time inside the zone before equip |
firingGripReattachRadiusGameUnits | 0.25–30.0 | Reattach distance |
gripZoneHoverHapticIntensity | 0.0–1.0 | Hover feedback strength |
firingGripProximitySupportRadiusGameUnits | 0.25–30.0 | Visual support proximity |
weaponGripHapticDurationSeconds | 0.01–0.50 | Grip pulse duration |
firingGripAttachHapticIntensity | 0.0–1.0 | Attach feedback strength |
firingGripDetachHapticIntensity | 0.0–1.0 | Detach feedback strength |
supportGripHapticIntensity | 0.0–1.0 | Support-grip feedback strength |
firingGripPromotionRadiusGameUnits | 0.25–30.0 | Opposite-hand promotion distance |
leftFiringAimYawDegrees | -30.0–30.0 | Left-firing aim correction |
leftFiringAimPitchDegrees | -30.0–30.0 | Left-firing aim correction |
each leftFiringAimOffsetGameUnits axis | -15.0–15.0 | Left-firing position correction |
equipVisualBridgeTimeoutSeconds | 0.25–5.0 | Native render recovery timeout |
equipVisualBridgeBlendSeconds | 0.0–1.0 | Visual handoff blend |
Defaults in the structure are conservative and valid. Change only values your addon owns as user-facing policy.
using namespace rock::provider;
RockProviderEquippedWeaponHandlingRequestV1 request{};
request.flags =
static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingFlagV1::FiringGripOwnership) |
static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingFlagV1::PrimaryDetach) |
static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingFlagV1::AmbidextrousHandoff) |
static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingFlagV1::GripZoneEquip) |
static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingFlagV1::GripZoneHoverHaptics);
request.leaseFrames = 3;
request.gripZoneEquipRadiusGameUnits = 3.5f;
request.gripZoneEquipSettleSeconds = 0.12f;
request.firingGripPromotionRadiusGameUnits = 5.0f;
request.worldGeneration = frame.worldGeneration;
request.skeletonGeneration = frame.skeletonGeneration;
request.providerGeneration = frame.providerGeneration;
const auto result = RockProviderApi::inst->setEquippedWeaponHandlingAuthorityV1(
ownerToken, &request);
Refresh the same complete request before its lease expires. This is not a patch API: omitted flags are disabled by the replacement.
clearEquippedWeaponHandlingAuthorityV1 · slot 49
RockProviderResultV1 clearEquippedWeaponHandlingAuthorityV1(
std::uint64_t ownerToken);
Releases this owner's policy and returns ROCK to its normal configured
behavior. If another owner holds the singleton, the call returns
OwnerConflict instead of clearing someone else's state.
getEquippedWeaponHandlingStateV1 · slot 50
bool getEquippedWeaponHandlingStateV1(
RockProviderEquippedWeaponHandlingStateV1* outState);
Returns a synchronized value snapshot without requiring an owner token. The output must advertise its exact current size. It includes:
- active authority flags, owner, and expiry frame;
- weapon form ID and generation key;
- fixed configured firing hand and current physical firing hand;
- runtime-state flags.
Runtime flags:
| Flag | Meaning |
|---|---|
AuthorityActive | A non-expired policy owner exists. |
FixedHandLeft | ROCK's fixed/configured firing hand is left. |
FiringHandLeft | Current physical firing ownership is left. |
LeftFiringInfrastructureAvailable | The current runtime can support left firing. |
ManualOwnershipActive | A physical grip currently owns firing-hand policy. |
PartCarryActive | Weapon ownership is temporarily carried through a part grip. |
FiringGripOccupied | A hand currently occupies the firing grip. |
WeaponPresent | A usable equipped weapon is present. |
Do not infer state only from the requested flags. For example,
AmbidextrousHandoff enables a behavior, while FiringHandLeft reports what is
actually true this frame.
RockProviderEquippedWeaponHandlingStateV1 handling{};
if (RockProviderApi::inst->getEquippedWeaponHandlingStateV1(&handling)) {
const bool authorityLive =
(handling.runtimeFlags & static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingRuntimeFlagV1::AuthorityActive)) != 0;
const bool leftOwnsFiring =
(handling.runtimeFlags & static_cast<std::uint32_t>(
RockProviderEquippedWeaponHandlingRuntimeFlagV1::FiringHandLeft)) != 0;
}
requestEquippedWeaponHandV1 · slot 86
RockProviderResultV1 requestEquippedWeaponHandV1(
std::uint64_t ownerToken,
const RockProviderEquippedWeaponHandRequestV1* request);
Requests canonical physical ownership for the weapon that is already equipped. It does not select an inventory item and it does not bypass ROCK's normal right-native / persistent-left carry executor.
The caller must own the active handling lease. hand must be Right or
Left; a left request additionally requires AmbidextrousHandoff. Optional
weapon form/generation and world/skeleton/provider generations bind the request
to current state. Call only from ROCK's owner frame/animation thread.
RockProviderEquippedWeaponHandRequestV1 handRequest{};
handRequest.hand = RockProviderHand::Left;
handRequest.weaponFormId = frame.weaponFormId;
handRequest.weaponGenerationKey = frame.weaponGenerationKey;
handRequest.worldGeneration = frame.worldGeneration;
handRequest.skeletonGeneration = frame.skeletonGeneration;
handRequest.providerGeneration = frame.providerGeneration;
const auto requested = RockProviderApi::inst->requestEquippedWeaponHandV1(
ownerToken, &handRequest);
Ok means the requested hand is already effective. RequestQueued means the
canonical transition is armed but has not settled. Observe
getEquippedWeaponHandlingStateV1 and provider events rather than issuing the
same request every frame.
What to build with it
- a left/right agnostic firing-grip addon;
- physical weapon pickup from an authored grip zone;
- seamless firing-hand handoff while preserving the native handedness setting;
- detach-to-manipulate reload sequences with part carry;
- shoulder holster/stash behavior for the equipped weapon;
- accessibility presets with larger grip zones and tuned haptic confirmation;
- a state visualizer that explains current fixed hand, physical owner, and bridge/recovery state.
Integration rules
- Do not run a second independent equip/attach state machine beside ROCK.
- Do not retain an authority lease when your feature is inactive.
- Use generation guards on rolling publications.
- Check every
RockProviderResultV1;OwnerConflictis a real ownership boundary, not a retry loop. - Read runtime state to confirm effects rather than assuming a request applied.
- Clear explicitly on shutdown and let lease expiry handle abnormal loss.