WeaponInteractionContracts.cs
using Sandbox;
using System;
/// <summary>
/// Minimal aim state exposed by a held item to camera, locomotion and HUD
/// integration code. Consumers should depend on this contract rather than a
/// concrete weapon class whenever they only need to know whether the item is
/// currently aiming.
/// </summary>
public interface IWeaponAimState
{
bool IsAiming { get; }
}
/// <summary>
/// Optional richer optic state used by camera/scope presentation. Consumers can
/// fall back to IWeaponAimState for ordinary weapons that do not expose it.
/// </summary>
public interface IWeaponOpticState : IWeaponAimState
{
float AimFraction { get; }
float AimAccuracyBlend { get; }
bool IsScopePresentationActive { get; }
float CurrentOpticSensitivityScale { get; }
/// <summary>
/// Current PiP peripheral-camera FOV multiplier after ADS blending. 1 means
/// the world/periphery remains at its ordinary gameplay FOV.
/// </summary>
float CurrentPeripheralAimFovScale { get; }
/// <summary>
/// Current additional viewmodel-camera FOV multiplier after ADS blending.
/// 1 preserves the existing viewmodel projection.
/// </summary>
float CurrentViewModelAimFovScale { get; }
OMROpticProfile ActiveOpticProfile { get; }
}
/// <summary>
/// Narrow handling contract exposed by a held item to character locomotion.
///
/// The movement controller does not need to know how a weapon implements ADS,
/// sprint suppression, attachments or other internal mechanics. It only needs
/// the resolved restrictions/scales relevant to movement.
/// </summary>
public interface IWeaponHandlingState : IWeaponAimState
{
/// <summary>
/// True while the current weapon state should prevent physical sprint from
/// resolving. OMR currently maps this to ADS, but another weapon/game may
/// also use it for a heavy action or another handling state.
/// </summary>
bool BlocksSprint { get; }
/// <summary>
/// General movement multiplier contributed by the held item. Kept at 1 for
/// the current OMR arsenal; the contract exists now so later handling stats do
/// not require another locomotion dependency rewrite.
/// </summary>
float MovementSpeedScale { get; }
/// <summary>
/// Additional movement multiplier while aiming. Kept at 1 for the current
/// OMR arsenal until ADS movement tuning moves into WeaponDefinition.
/// </summary>
float AimingMovementSpeedScale { get; }
/// <summary>
/// Gives locomotion temporary priority over ADS while sprint intent is active.
/// The item decides how that suppression is represented internally.
/// </summary>
void SetSprintAimSuppressed( bool suppressed );
}
/// <summary>
/// Character/controller-side source of weapon-relevant locomotion facts.
///
/// The reusable weapon layer consumes this interface instead of reaching into
/// a specific movement implementation such as PlayerSprint or PlayerSlide.
/// Another S&box game can provide the same contract from a different controller.
/// </summary>
public interface IWeaponLocomotionProvider
{
WeaponLocomotionSnapshot WeaponLocomotion { get; }
}
/// <summary>
/// Read-only snapshot of the physical movement facts that weapon mechanics and
/// first-person presentation are allowed to consume.
///
/// It deliberately contains resolved facts rather than input buttons. Intent is
/// included only where it matters to weapon handling (currently sprint intent).
/// </summary>
public readonly struct WeaponLocomotionSnapshot
{
public bool IsValid { get; }
public bool IsGrounded { get; }
public bool IsAirborne { get; }
public bool IsCrouched { get; }
public bool IsSliding { get; }
public bool WantsSprint { get; }
public bool IsSprinting { get; }
public Vector3 Velocity { get; }
public Vector3 HorizontalVelocity { get; }
public Vector3 Acceleration { get; }
public float HorizontalSpeed { get; }
public float VerticalVelocity { get; }
public float ReferenceMoveSpeed { get; }
public float NormalizedMoveSpeed { get; }
public float LandingImpulse { get; }
public static WeaponLocomotionSnapshot Invalid => default;
public WeaponLocomotionSnapshot(
bool isValid,
bool isGrounded,
bool isAirborne,
bool isCrouched,
bool isSliding,
bool wantsSprint,
bool isSprinting,
Vector3 velocity,
Vector3 acceleration,
float referenceMoveSpeed,
float landingImpulse
)
{
IsValid = isValid;
IsGrounded = isGrounded;
IsAirborne = isAirborne;
IsCrouched = isCrouched;
IsSliding = isSliding;
WantsSprint = wantsSprint;
IsSprinting = isSprinting;
Velocity = velocity;
HorizontalVelocity = velocity.WithZ( 0f );
Acceleration = acceleration;
HorizontalSpeed = HorizontalVelocity.Length;
VerticalVelocity = velocity.z;
ReferenceMoveSpeed = MathF.Max( referenceMoveSpeed, 1f );
NormalizedMoveSpeed = MathX.Clamp(
HorizontalSpeed / ReferenceMoveSpeed,
0f,
1f
);
LandingImpulse = MathF.Max( landingImpulse, 0f );
}
/// <summary>
/// Conservative fallback for BaseCombatWeapon owners whose controller does
/// not provide IWeaponLocomotionProvider. It preserves basic movement,
/// crouch and airborne behavior while leaving game-specific sprint/slide
/// states false.
/// </summary>
public static WeaponLocomotionSnapshot FromPlayerController(
PlayerController controller
)
{
if ( controller is null )
return Invalid;
return new WeaponLocomotionSnapshot(
isValid: true,
isGrounded: controller.IsOnGround,
isAirborne: controller.IsAirborne,
isCrouched: controller.IsDucking,
isSliding: false,
wantsSprint: false,
isSprinting: false,
velocity: controller.Velocity,
acceleration: Vector3.Zero,
referenceMoveSpeed: MathF.Max( controller.RunSpeed, 1f ),
landingImpulse: 0f
);
}
}