Concept: The Settings-Provider Bridge

The four input modifiers need per-player values (deadzone, sensitivity, inversion). Rather than importing a settings class, they read a small interface - ICrimsonInputSettingsProvider - that your ULocalPlayer implements. Input stays ignorant of where settings come from; settings stay ignorant of input.

How a modifier finds the provider

Every modifier resolves the provider the same way, at evaluation time:

cpp
// UEnhancedPlayerInput -> owning APlayerController -> its ULocalPlayer -> the interface
APlayerController* PC = Cast<APlayerController>(PlayerInput->GetOuter());
ICrimsonInputSettingsProvider* Provider = PC ? Cast<ICrimsonInputSettingsProvider>(PC->GetLocalPlayer()) : nullptr;

If the cast fails (no provider), each modifier returns the input unchanged - so a project that never implements the interface simply gets vanilla behavior, never a crash.

C++ virtuals vs K2_ events

The interface exposes two parallel surfaces. The C++ virtuals (GetGamepadLookStickDeadZone, GetInvertVerticalAxis, ...) are what the modifiers call; their default implementations forward to the matching K2_ BlueprintNativeEvent. Override the virtuals in a C++ local player for zero overhead, or the K2_ events in a Blueprint local player - both reach the same modifier.

Modifier readsProvider method
Deadzone (move / look)GetGamepadMoveStickDeadZone / GetGamepadLookStickDeadZone
Sensitivity (normal / ADS)GetGamepadLookSensitivityPreset / GetGamepadTargetingSensitivityPreset
Invert axesGetInvertVerticalAxis / GetInvertHorizontalAxis
Named scalar propertiesGetSharedSettingsObject (C++ only; null by default)
GetSharedSettingsObject is opt-in
UCrimsonSettingBasedScalar reads named float/double properties off GetSharedSettingsObject() by reflection (caching the FProperty*). That method returns null by default, so the scalar modifier is inert until you return your settings object from it.
Filled on the full stack, your job standalone
On the full stack, CrimsonCore's UCrimsonCoreLocalPlayer (the project's local player) implements this interface and forwards each value to UCrimsonSettingsShared in CrimsonSettings - so the modifiers are driven by the player's saved settings out of the box. Standalone (CrimsonInput alone) it is your job: UCrimsonCommonLocalPlayer does not implement the interface (CrimsonCommon may not depend on CrimsonInput or CrimsonSettings), so implement it on your own local player and point it at your settings. The split keeps CrimsonInput free of any settings-backend dependency.