RobCo PALM
Download PALM on Nexus Mods · Source code
Personal Access & Loadout Manager is a released wheel menu for Fallout 4 VR. Hold right trigger + grab, point, and release either button to choose equipment, consumables, or a hand gesture. The wheel opens on Weapons and provides configuration pages for the RPS mods.
Install Fallout 4 VR, F4SEVR, and RPS UI Framework. Follow the Nexus requirements for FRIK and the VR Address Library as well. ROCK is optional for the wheel: it adds physical item handling and hand gestures with FRIK 0.79 / API 2.3 for current source. PAPER and SCISSORS settings appear through their respective integrations; SCISSORS remains in development.
The wheel consumes RPS UI's independent input service, with optional ROCK and
ROCK configuration integration. It supplies equipment/inventory UI and configuration panels;
it is not part of the shared panel-host API. Its own native section API lets mods
register named sections and item actions. The plugin file is wheelmenu.dll, its F4SE
registration is RobCoPALM, and its log is RobCoPALM.log.
Gesture and selection
Hold physical right trigger and right grab together for 0.25 seconds by default. Change the binding, hand, hold time, or Hold / release and Press / click mode under Config → PALM settings → Controls. Each button alone remains gameplay input. The complete combination is reserved during hold qualification and through both releases; adding trigger while already holding grip can open PALM and block ROCK's trigger-equip action. Native menus and unavailable gameplay state prevent wheel ownership.
In Hold / release mode, release either chord button over the last rendered selection to choose it. Release both buttons before opening again. The input callback owns release independently of the renderer; releasing without a valid rendered selection cancels safely. Config replaces the wheel at its anchor. The circular wheel uses transparent, fixed-size and circular-input panel flags; configuration panels opt in to the pointing hand's stick and grip-back routing.
Inventory and saved choices
The five categories are Aid, Food, Grenades, Weapons and Armor. Each category supports up to eight selected favorites and an enable switch. Unavailable favorites remain removable and retain their saved identity.
With ROCK available, physical aid/food/throwable handoffs use its
Grab.requestInventoryGrab command: exactly
one owned item transfers to a free hand, right preferred, without immediate use
or equip. Weapon/armor choices take the equipment path on a game task and retain
stack identity. An accepted command is not a successful handoff; terminal
results provide the actual outcome. Without ROCK, PALM retains native inventory
actions and controller pointers.
Favorites and category choices are saved in the F4SE co-save. A new/load/revert session resets prior inventory snapshots, pending actions and render tickets; missing or invalid wheel records do not inherit the previous save's favorites. Preview choices remain in memory and do not edit production game settings.
Configuration workshop
The top-level tabs are Items, PALM settings, RPS mods, and Spawner. PALM settings controls visible inventory categories, gestures, and registered mod sections. Hidden sections leave no empty gaps. The inner ring has at most ten visible entries, including the permanent Config entry; gestures occupies two entries. Config and the center Cancel action remain accessible with all other sections hidden.
ROCK and Developer tabs enumerate the compiled ROCK catalog, preserving its
section order, display labels, types, defaults and owning group. Writes go
through the modular Configuration interface discovered with ROCKAPI_QueryInterfaceV1; the menu does not own a separate ROCK catalog
or writer. Developer reset removes that override, and an empty developer file
is removed by ROCK.
PAPER and SCISSORS pages use their own configuration adapters and apply rules. PAPER configuration hot reload is automatic; its cache size limits require a new game session. SCISSORS applies through its own watcher. Wheel preferences and the spawn browser belong to the wheel, not to RPS UI. The spawn browser uses plugin identity when resolving catalog entries; its game actions must remain on the game task path.
PALM controls are stored under the user's Documents folder at
My Games/Fallout4VR/Mods_Config/RobCo_PALM/PALM.ini. Set pointer pitch and yaw
through PALM settings; these are adjustments from the calibrated pointer
direction, and their default is zero.
Native mod sections
Resolve GetPALMMenuApi from wheelmenu.dll and request version 1. The public
interface is owned by PALM's SDK/include/PALMMenuApi.h, with built-in icon IDs
in PALMIcons.h. It exposes registration, unregistration, and atomic item-snapshot
replacement. Up to ten mod sections may register, with eight items per section.
New sections start hidden until enabled by the user in PALM settings.
PALM copies names and item data. The consumer retains its selection callback and context until unregistration succeeds. Selection runs on the F4SE game task thread. Hold/release mode closes the wheel; press/click mode can keep it open, so consumers must not require closure before dispatch. The mod owns the action: an item may identify a vanilla operation, a ROCK feature, or a physical magazine object independent of ammo forms. Respect the owning feature's thread and lifetime rules.
Unregistration returns CallbackBusy while a callback is active; retain its
context and retry later. Removed or disabled items and unregistered sections
cannot dispatch queued selections. PALM clears extension item snapshots at a game
load; consumers republish from a task queued after successful PostLoadGame or
NewGame. The compiled physical-magazine example in PALM's SDK demonstrates this
API's discovery, registration, and publication calls.
Failure and dependency behavior
The wheel requires RPS UI readiness and its independent native input service. Physical item handoffs additionally require ROCK's inventory-transfer support and the corresponding interface permissions. If registration, presentation, input claim or rendered-selection delivery fails, it releases input ownership. Confirmed presentation failure disables the wheel for that session until the next game load.
A registered/open panel is not proof of a rendered frame. RPS UI can skip draws when scene depth or projection is invalid. Close/cancel/release paths must not wait for another render callback. The desktop preview validates layout and interaction logic; it does not validate stereo depth or in-game input.
Current modular dependencies
PALM's optional ROCK integration acquires Hands, Grab, Weapon, Input and Animation as needed; its separate configuration connection binds Configuration Read + Write under a ROCK-issued owner. It uses independent RPS UI input and panel services even when ROCK is unavailable. Current gestures integrate FRIK API 2.3.
The native section API remains version 1. registerSection copies identity/metadata and returns a non-reused handle, setItems atomically replaces up to eight items, and unregisterSection retires callback ownership. IDs and data are values, not engine pointers. Registration survives game loads but item snapshots are cleared; republish after successful load/new-game processing. CallbackBusy requires retaining context and retrying later.
Complete public declarations: PALMMenuApi.h
#pragma once
#include "PALMIcons.h"
#include <cstdint>
#include <type_traits>
namespace palm::api {
inline constexpr std::uint32_t kVersion=1;
inline constexpr const char* kExportName="GetPALMMenuApi";
inline constexpr const wchar_t* kModuleName=L"wheelmenu.dll";
inline constexpr std::uint32_t kMaxSections=10;
inline constexpr std::uint32_t kMaxItems=8;
inline constexpr std::uint32_t kMaxVisibleEntries=10; // Includes PALM's Config entry.
using SectionHandle=std::uint32_t;
enum class Result : std::uint32_t { Ok, InvalidArgument, NotFound, DuplicateId, CapacityReached, CallbackBusy, ItemUnavailable, InternalError };
enum class ItemFlag : std::uint32_t { Disabled=1, Equipped=2, ShowQuantity=4 };
// Called on the F4SE game task thread without PALM locks. In press/click mode
// the wheel remains open; consumers must not depend on it closing before dispatch.
// The mod owns the action: vanilla equipment, ROCK requests, or another feature.
// Never throw across this callback. Keep the module and context alive until
// unregisterSection succeeds. CallbackBusy means an invocation is still running;
// retry unregister later, without blocking a game/input/render callback.
using SelectCallback=void (*)(std::uint32_t itemId,std::uint64_t userData,void* context) noexcept;
struct SectionV1 {
std::uint32_t structSize{sizeof(SectionV1)};
Icon icon{Icon::Config};
char id[64]{}; // Stable, globally unique ASCII ID: letters, digits, '_', '-', '.'.
char name[48]{};
char modName[48]{};
SelectCallback onSelect{};
void* context{};
};
struct ItemV1 {
std::uint32_t structSize{sizeof(ItemV1)};
std::uint32_t id{}; // Nonzero, stable within this section. Not necessarily a FormID.
Icon icon{Icon::Config};
std::uint32_t quantity{};
std::uint32_t flags{};
char name[96]{};
std::uint64_t userData{};
};
// Thread-safe, bounded calls. Text and items are copied before returning; only
// the callback/context are borrowed. No engine pointers enter the API.
// Register once per plugin lifetime, replace the item snapshot when it changes,
// and unregister before releasing callback state. Handles are never reused.
// New sections start hidden; users enable them in PALM settings. Registration
// survives game loads; PALM empties item snapshots at the start of a game load.
// Republish from a game task queued after a successful PostLoadGame or NewGame.
// Removing a section or item cancels any queued selection that refers to it.
struct ApiV1 {
std::uint32_t version;
std::uint32_t byteSize;
Result (*registerSection)(const SectionV1*,SectionHandle*) noexcept;
Result (*unregisterSection)(SectionHandle) noexcept;
Result (*setItems)(SectionHandle,const ItemV1*,std::uint32_t count) noexcept;
};
using GetApi=const ApiV1* (*)(std::uint32_t version) noexcept;
static_assert(sizeof(void*)==8);
static_assert(sizeof(SectionV1)==184 && sizeof(ItemV1)==128 && sizeof(ApiV1)==32);
static_assert(std::is_standard_layout_v<SectionV1> && std::is_trivially_copyable_v<ItemV1>);
}
Complete public declarations: PALMIcons.h
#pragma once
#include <cstdint>
namespace palm::api {
// Stable built-in outline icons for API V1. Use the supplied names, not atlas UVs.
enum class Icon : std::uint32_t {
ThumbsUp, MiddleFinger, RockAndRoll, Peace, Pointing, Fist, OpenHand, Shaka,
Aid, Food, Grenades, Weapons, Armor, Config, LeftHand, RightHand,
Pistol10mm, Revolver, PipePistol, Deliverer, CombatRifle, HuntingRifle, AssaultRifle, CombatShotgun,
DoubleBarrel, SubmachineGun, LaserPistol, LaserRifle, PlasmaPistol, PlasmaRifle, InstituteRifle, GaussRifle,
Minigun, GatlingLaser, MissileLauncher, FatMan, Flamer, Cryolator, GammaGun, LaserMusket,
RailwayRifle, Broadsider, JunkJet, CombatKnife, BaseballBat, SuperSledge, PowerFist, DeathclawGauntlet,
Stimpak, RadAway, RadX, MedX, Psycho, Jet, Buffout, Mentats,
Addictol, Antibiotics, BloodPack, StealthBoy, Serum, Herbal, RefreshingBeverage, Chems,
NukaCola, NukaCherry, NukaQuantum, PurifiedWater, DirtyWater, Beer, Spirits, Wine,
RawMeat, CookedMeat, Vegetables, Mutfruit, PackagedFood, CannedFood, SweetRoll, Soup,
FragGrenade, Molotov, PlasmaGrenade, PulseGrenade, CryoGrenade, NukaGrenade, Mine, BottlecapMine,
Helmet, PowerArmor, Clothing, ChestArmor, LeftArm, RightArm, LeftLeg, RightLeg,
GrognakAxe, OfficerSword, Machete, Switchblade, Ripper, Shishkebab, PipeWrench, LeadPipe,
Baton, Knuckles, BoxingGlove, Board, AlienBlaster, FlareGun, Syringer, PipeRevolver,
Count, Automatic=Count
};
}