Skip to main content

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

FlagWhat it enables
FiringGripOwnershipThe firing grip becomes a physical ownership role managed by the authority.
PrimaryDetachThe current firing hand may detach while the weapon remains under valid physical ownership.
AmbidextrousHandoffThe opposite physical hand can become the firing hand through a valid handoff.
GripZoneEquipGrabbing the physical firing-grip zone can equip/claim the weapon.
GripZoneHoverHapticsProvides bounded hover feedback near that equip zone.
FiringGripProximitySupportReplaces ROCK's configured proximity radius while leased; visual-only support itself remains a core behavior.
EquippedWeaponShoulderStashAllows the equipped weapon to move through the shoulder-stash interaction.
PipboyTriggerHandEquipAllows the Pip-Boy-side trigger hand to participate in the authority's equip behavior.
EquipVisualBridgeABI-compatible tuning switch; ROCK's visual bridge and native attach recovery are now unconditional correctness services.

Dependency rules are enforced:

  • PrimaryDetach, AmbidextrousHandoff, GripZoneEquip, EquippedWeaponShoulderStash, and PipboyTriggerHandEquip require FiringGripOwnership;
  • EquippedWeaponShoulderStash also requires PrimaryDetach.

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.

FieldValid rangeUnit / purpose
gripZoneEquipRadiusGameUnits0.25–30.0Equip-zone radius
gripZoneEquipSettleSeconds0.0–5.0Time inside the zone before equip
firingGripReattachRadiusGameUnits0.25–30.0Reattach distance
gripZoneHoverHapticIntensity0.0–1.0Hover feedback strength
firingGripProximitySupportRadiusGameUnits0.25–30.0Visual support proximity
weaponGripHapticDurationSeconds0.01–0.50Grip pulse duration
firingGripAttachHapticIntensity0.0–1.0Attach feedback strength
firingGripDetachHapticIntensity0.0–1.0Detach feedback strength
supportGripHapticIntensity0.0–1.0Support-grip feedback strength
firingGripPromotionRadiusGameUnits0.25–30.0Opposite-hand promotion distance
leftFiringAimYawDegrees-30.0–30.0Left-firing aim correction
leftFiringAimPitchDegrees-30.0–30.0Left-firing aim correction
each leftFiringAimOffsetGameUnits axis-15.0–15.0Left-firing position correction
equipVisualBridgeTimeoutSeconds0.25–5.0Native render recovery timeout
equipVisualBridgeBlendSeconds0.0–1.0Visual 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:

FlagMeaning
AuthorityActiveA non-expired policy owner exists.
FixedHandLeftROCK's fixed/configured firing hand is left.
FiringHandLeftCurrent physical firing ownership is left.
LeftFiringInfrastructureAvailableThe current runtime can support left firing.
ManualOwnershipActiveA physical grip currently owns firing-hand policy.
PartCarryActiveWeapon ownership is temporarily carried through a part grip.
FiringGripOccupiedA hand currently occupies the firing grip.
WeaponPresentA 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; OwnerConflict is 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.