OMRFirstPersonTuning.cs
using Sandbox;
using System;
/// <summary>
/// Hot-edit friendly first-person presentation tuning for One More Round.
///
/// Revision 21 deliberately keeps presentation separate from gameplay:
/// - no setting in this resource changes AimRay, spread, damage or movement physics;
/// - camera feel is applied through ICameraModifier;
/// - weapon feel is driven through the authored Facepunch viewmodel AnimGraph;
/// - OMR never translates or rotates the spawned viewmodel root.
///
/// This gives us one place to tune first-person feel without structurally
/// hotloading the network player prefab.
/// </summary>
[AssetType(
Name = "OMR First Person Feel",
Extension = "omrfp",
Category = "One More Round/Gameplay"
)]
public sealed partial class OMRFirstPersonTuning : GameResource
{
// =========================================================
// GENERAL
// =========================================================
[Group( "General" ), Title( "Enable Camera Feel" ), Description( "Master switch for OMR camera bob, roll, movement FOV and slide camera presentation. ADS zoom itself remains weapon-owned." )]
public bool EnableCameraFeel { get; set; } = true;
[Group( "General" ), Title( "Enable Viewmodel Animation Feel" ), Description( "Drives the authored Facepunch viewmodel movement and look-inertia AnimGraph parameters. This never moves the viewmodel GameObject itself." )]
public bool EnableViewModelAnimationFeel { get; set; } = true;
// =========================================================
// CAMERA - MOVEMENT
// =========================================================
[Group( "Camera - Movement" ), Title( "Enable Camera Bob" ), Description( "Adds subtle positional footstep movement to the gameplay camera while grounded and moving." )]
public bool EnableCameraBob { get; set; } = true;
[Group( "Camera - Movement" ), Range( 0.2f, 5f ), Title( "Bob Frequency" ), Description( "Base speed of the camera footstep cycle." )]
public float CameraBobFrequency { get; set; } = 2.0f;
[Group( "Camera - Movement" ), Range( 0f, 5f ), Title( "Vertical Bob" ), Description( "Up/down camera movement produced by footsteps." )]
public float CameraBobVerticalAmount { get; set; } = 1.2f;
[Group( "Camera - Movement" ), Range( 0f, 5f ), Title( "Horizontal Bob" ), Description( "Side-to-side camera movement produced by footsteps." )]
public float CameraBobHorizontalAmount { get; set; } = 0.8f;
[Group( "Camera - Movement" ), Range( 1f, 2.5f ), Title( "Sprint Bob Multiplier" ), Description( "Additional camera-bob intensity/frequency while the locomotion resolver says the player is physically sprinting." )]
public float CameraSprintBobMultiplier { get; set; } = 1.25f;
[Group( "Camera - Movement" ), Title( "Enable Strafe Roll" ), Description( "Tilts the camera slightly while moving sideways." )]
public bool EnableStrafeRoll { get; set; } = true;
[Group( "Camera - Movement" ), Range( 0f, 8f ), Title( "Maximum Strafe Roll" ), Description( "Maximum camera roll, in degrees, at full lateral movement." )]
public float MaximumStrafeRoll { get; set; } = 1.5f;
[Group( "Camera - Movement" ), Range( 0.01f, 0.5f ), Title( "Movement Smoothing" ), Description( "How softly movement amount and strafe roll react. Lower values react faster." ), Advanced]
public float MovementSmoothing { get; set; } = 0.08f;
[Group( "Camera - Movement" ), Range( 0.01f, 0.5f ), Title( "Sprint Smoothing" ), Description( "How softly camera presentation enters and leaves the sprint state. Lower values react faster." ), Advanced]
public float SprintSmoothing { get; set; } = 0.08f;
// =========================================================
// CAMERA - FOV
// =========================================================
[Group( "Camera - FOV" ), Title( "Enable Movement FOV" ), Description( "Widens the gameplay camera while moving. Sprint receives a separate additional boost instead of sharing the same speed band." )]
public bool EnableMovementFov { get; set; } = true;
[Group( "Camera - FOV" ), Range( 0f, 20f ), Title( "Movement FOV Boost" ), Description( "FOV added when normal movement reaches the configured base movement speed." )]
public float MovementFovBoost { get; set; } = 7.0f;
[Group( "Camera - FOV" ), Range( 0f, 20f ), Title( "Additional Sprint FOV Boost" ), Description( "Extra FOV added only while sprint is physically resolved. This is added on top of Movement FOV Boost." )]
public float SprintFovBoost { get; set; } = 5.0f;
[Group( "Camera - FOV" ), Range( 0f, 12f ), Title( "Slide FOV Boost" ), Description( "Additional FOV while sliding." )]
public float SlideFovBoost { get; set; } = 2.5f;
// =========================================================
// CAMERA - SLIDE / LANDING
// =========================================================
[Group( "Camera - Slide / Landing" ), Title( "Enable Slide Camera" ), Description( "Adds a small camera drop and lateral glide during slide." )]
public bool EnableSlideCamera { get; set; } = true;
[Group( "Camera - Slide / Landing" ), Range( 0f, 6f ), Title( "Slide Camera Drop" ), Description( "Additional camera lowering during slide. The physical crouched eye height is already handled by PlayerController." )]
public float SlideCameraDrop { get; set; } = 1.3f;
[Group( "Camera - Slide / Landing" ), Range( 0f, 3f ), Title( "Slide Side Sway" ), Description( "Slow lateral camera glide while sliding." )]
public float SlideSwayAmount { get; set; } = 0.5f;
[Group( "Camera - Slide / Landing" ), Range( 0.1f, 4f ), Title( "Slide Sway Frequency" ), Description( "Speed of the lateral slide-camera glide." )]
public float SlideSwayFrequency { get; set; } = 0.85f;
[Group( "Camera - Slide / Landing" ), Range( 0f, 1f ), Title( "Slide Strafe Roll Reduction" ), Description( "How much normal strafe roll is suppressed while sliding." )]
public float SlideStrafeRollReduction { get; set; } = 0.65f;
[Group( "Camera - Slide / Landing" ), Range( 0.01f, 0.5f ), Title( "Slide Smoothing" ), Description( "How softly camera presentation enters and leaves slide." ), Advanced]
public float SlideSmoothing { get; set; } = 0.07f;
[Group( "Camera - Slide / Landing" ), Title( "Enable Landing Response" ), Description( "Adds a small transient pitch punch on sufficiently hard landings." )]
public bool EnableLandingResponse { get; set; } = true;
[Group( "Camera - Slide / Landing" ), Range( 0f, 10f ), Title( "Landing Pitch" ), Description( "Maximum camera pitch response for a hard landing." )]
public float LandingPitchAmount { get; set; } = 2.0f;
[Group( "Camera - Slide / Landing" ), Range( 0f, 1f ), Title( "Landing Into Slide Scale" ), Description( "Multiplier applied to landing punch when the landing immediately becomes a slide." )]
public float LandingSlidePunchMultiplier { get; set; } = 0.35f;
// =========================================================
// ADS - CAMERA
// =========================================================
[Group( "ADS - Camera" ), Title( "Stabilize Camera In ADS" ), Description( "Reduces camera movement noise while aiming. Weapon alignment and ADS zoom remain controlled by the weapon/AnimGraph." )]
public bool EnableAdsCameraStabilization { get; set; } = true;
[Group( "ADS - Camera" ), Range( 0.01f, 0.5f ), Title( "ADS Camera Smoothing" ), Description( "How softly camera effects transition between hipfire and ADS." ), Advanced]
public float AdsCameraSmoothing { get; set; } = 0.08f;
[Group( "ADS - Camera" ), Range( 0f, 1f ), Title( "Bob Remaining" ), Description( "Amount of normal camera footstep bob remaining at full ADS." )]
public float AdsCameraBobScale { get; set; } = 0.18f;
[Group( "ADS - Camera" ), Range( 0f, 1f ), Title( "Strafe Roll Remaining" ), Description( "Amount of normal camera strafe roll remaining at full ADS." )]
public float AdsStrafeRollScale { get; set; } = 0.20f;
[Group( "ADS - Camera" ), Range( 0f, 1f ), Title( "Movement FOV Remaining" ), Description( "Amount of movement/sprint/slide FOV expansion remaining at full ADS. Zero lets weapon zoom fully dominate." )]
public float AdsMovementFovScale { get; set; } = 0.0f;
[Group( "ADS - Camera" ), Range( 0f, 1f ), Title( "Slide Camera Remaining" ), Description( "Amount of slide positional camera movement remaining at full ADS." )]
public float AdsSlideCameraScale { get; set; } = 0.35f;
// =========================================================
// ADS - SENSITIVITY
// =========================================================
[Group( "ADS - Sensitivity" ), Title( "Zoom-Matched Sensitivity" ), Description( "Scales look sensitivity from the actually composed camera FOV, so weapons with different zoom levels remain consistent." )]
public bool EnableZoomMatchedSensitivity { get; set; } = true;
[Group( "ADS - Sensitivity" ), Range( 0.05f, 1f ), Title( "Minimum Sensitivity Scale" ), Description( "Lowest multiplier zoom matching may apply." )]
public float MinimumZoomSensitivityScale { get; set; } = 0.20f;
[Group( "ADS - Sensitivity" ), Range( 0.01f, 0.5f ), Title( "Sensitivity Smoothing" ), Description( "How softly look sensitivity reacts to zoom changes." ), Advanced]
public float SensitivitySmoothing { get; set; } = 0.04f;
// =========================================================
// VIEWMODEL - AUTHORED ANIMATION
// =========================================================
[Group( "Viewmodel - Authored Animation" ), Range( 0f, 2f ), Title( "Movement Bob Strength" ), Description( "Scales the Facepunch viewmodel AnimGraph move_bob parameter. This produces weapon/arm movement without changing the viewmodel root transform." )]
public float ViewModelMovementBobScale { get; set; } = 1.0f;
[Group( "Viewmodel - Authored Animation" ), Range( 0f, 1f ), Title( "ADS Movement Bob Remaining" ), Description( "Amount of authored viewmodel movement bob remaining while fully aiming." )]
public float ViewModelAdsBobScale { get; set; } = 0.12f;
[Group( "Viewmodel - Authored Animation" ), Title( "Use Sprint Animation" ), Description( "Drives the Facepunch b_sprint parameter from OMR's physically resolved sprint state." )]
public bool EnableViewModelSprintAnimation { get; set; } = true;
[Group( "Viewmodel - Authored Animation" ), Title( "Use Jump Animation" ), Description( "Triggers the Facepunch b_jump parameter when the local player leaves the ground from a jump." )]
public bool EnableViewModelJumpAnimation { get; set; } = true;
[Group( "Viewmodel - Authored Animation" ), Range( 0.01f, 0.5f ), Title( "Movement Animation Smoothing" ), Description( "How softly the move_bob parameter follows physical movement speed." ), Advanced]
public float ViewModelMovementSmoothing { get; set; } = 0.08f;
// =========================================================
// VIEWMODEL - LOOK WEIGHT
// =========================================================
[Group( "Viewmodel - Look Weight" ), Title( "Enable Look Inertia" ), Description( "Uses Facepunch's aim_pitch_inertia / aim_yaw_inertia AnimGraph parameters to make the weapon lag behind fast camera turns without physically moving its root." )]
public bool EnableViewModelLookInertia { get; set; } = true;
[Group( "Viewmodel - Look Weight" ), Range( 1f, 30f ), Title( "Follow Speed" ), Description( "How quickly the animated weapon lag catches up with the player's aim. Lower feels heavier; higher feels tighter." )]
public float ViewModelLookFollowSpeed { get; set; } = 8.0f;
[Group( "Viewmodel - Look Weight" ), Range( 0f, 3f ), Title( "Inertia Strength" ), Description( "Multiplier for authored look-lag animation. This affects animation only, not aim direction." )]
public float ViewModelLookInertiaStrength { get; set; } = 1.0f;
[Group( "Viewmodel - Look Weight" ), Range( 0f, 20f ), Title( "Maximum Inertia" ), Description( "Maximum value sent to the authored inertia parameters. Keeps very fast mouse movement bounded." )]
public float ViewModelMaximumLookInertia { get; set; } = 6.0f;
[Group( "Viewmodel - Look Weight" ), Range( 0f, 1f ), Title( "ADS Inertia Remaining" ), Description( "Amount of authored look inertia remaining while fully aiming." )]
public float ViewModelAdsInertiaScale { get; set; } = 0.12f;
[Group( "Viewmodel - Look Weight" ), Range( 0.01f, 0.5f ), Title( "Inertia Output Smoothing" ), Description( "Extra smoothing applied before inertia values are sent to the AnimGraph." ), Advanced]
public float ViewModelInertiaSmoothing { get; set; } = 0.045f;
// =========================================================
// FIRST-PERSON SHADOW
// =========================================================
[Group( "First Person Shadow" ), Title( "Use Full Body Shadow" ), Description( "Creates an owner-local shadows-only copy of the animated Citizen so first person casts a complete body silhouette." )]
public bool UseFullBodyShadow { get; set; } = true;
[Group( "First Person Shadow" ), Title( "Disable Viewmodel Shadows" ), Description( "Prevents the dedicated first-person arms/weapon model from also casting a floating-arms shadow." )]
public bool DisableViewModelShadows { get; set; } = true;
// =========================================================
// DIAGNOSTICS
// =========================================================
[Group( "Diagnostics" ), Title( "Log Presentation Setup" ), Description( "Prints first-person presentation setup once per local player." )]
public bool LogPresentationSetup { get; set; } = false;
[Group( "Diagnostics" ), Title( "Log Camera State" ), Description( "Once per second logs movement, sprint, slide, ADS blend and resulting movement FOV. Useful for verifying state wiring." )]
public bool LogCameraState { get; set; } = false;
[Group( "Diagnostics" ), Title( "Log Viewmodel State" ), Description( "Once per second logs the values OMR sends to the Facepunch viewmodel AnimGraph." )]
public bool LogViewModelState { get; set; } = false;
public int ContentHash()
{
HashCode hash = new();
hash.Add( EnableCameraFeel );
hash.Add( EnableViewModelAnimationFeel );
hash.Add( EnableCameraBob );
hash.Add( CameraBobFrequency );
hash.Add( CameraBobVerticalAmount );
hash.Add( CameraBobHorizontalAmount );
hash.Add( CameraSprintBobMultiplier );
hash.Add( EnableStrafeRoll );
hash.Add( MaximumStrafeRoll );
hash.Add( MovementSmoothing );
hash.Add( SprintSmoothing );
hash.Add( EnableMovementFov );
hash.Add( MovementFovBoost );
hash.Add( SprintFovBoost );
hash.Add( SlideFovBoost );
hash.Add( EnableSlideCamera );
hash.Add( SlideCameraDrop );
hash.Add( SlideSwayAmount );
hash.Add( SlideSwayFrequency );
hash.Add( SlideStrafeRollReduction );
hash.Add( SlideSmoothing );
hash.Add( EnableLandingResponse );
hash.Add( LandingPitchAmount );
hash.Add( LandingSlidePunchMultiplier );
hash.Add( EnableAdsCameraStabilization );
hash.Add( AdsCameraSmoothing );
hash.Add( AdsCameraBobScale );
hash.Add( AdsStrafeRollScale );
hash.Add( AdsMovementFovScale );
hash.Add( AdsSlideCameraScale );
hash.Add( EnableZoomMatchedSensitivity );
hash.Add( MinimumZoomSensitivityScale );
hash.Add( SensitivitySmoothing );
hash.Add( ViewModelMovementBobScale );
hash.Add( ViewModelAdsBobScale );
hash.Add( EnableViewModelSprintAnimation );
hash.Add( EnableViewModelJumpAnimation );
hash.Add( ViewModelMovementSmoothing );
hash.Add( EnableViewModelLookInertia );
hash.Add( ViewModelLookFollowSpeed );
hash.Add( ViewModelLookInertiaStrength );
hash.Add( ViewModelMaximumLookInertia );
hash.Add( ViewModelAdsInertiaScale );
hash.Add( ViewModelInertiaSmoothing );
hash.Add( UseFullBodyShadow );
hash.Add( DisableViewModelShadows );
hash.Add( LogPresentationSetup );
hash.Add( LogCameraState );
hash.Add( LogViewModelState );
return hash.ToHashCode();
}
}
public static class OMRFirstPersonResources
{
public const string DefaultTuningPath = "gameplay/default.omrfp";
public static OMRFirstPersonTuning DefaultTuning =>
ResourceLibrary.Get<OMRFirstPersonTuning>( DefaultTuningPath );
}