OneMoreRoundWeapon.RoundRules.cs
using Sandbox;
using System;
/// <summary>
/// Temporary round-scoped weapon rules.
///
/// These are deliberately runtime-only and owner-local. They do not mutate the
/// authored .omrwep definition and are cleared whenever the owning PlayerState
/// leaves the special round state. This gives round modes such as High Noon a
/// precise fire gate and spread override without baking event rules into the
/// revolver definition.
/// </summary>
public partial class OneMoreRoundWeapon
{
private bool _temporaryPrimaryGateEnabled;
private float _temporaryPrimaryGateOpensAt;
private bool _temporaryPrimaryGateRequireRelease;
private bool _temporaryPrimaryGateReleased;
private bool _temporarySpreadOverrideEnabled;
private Vector2 _temporarySpreadOverride;
private bool _roundAllowsAim = true;
/// <summary>
/// Round-scoped ADS gate. This is separate from the authored AllowAim
/// capability so Hipfire Only never mutates the source .omrwep.
/// </summary>
public bool RoundAllowsAim =>
_roundAllowsAim;
public void SetRoundAimAllowed(
bool allowed
)
{
if ( _roundAllowsAim == allowed )
return;
_roundAllowsAim = allowed;
if ( !allowed && IsAiming )
{
SetAiming( false );
}
}
/// <summary>
/// Blocks primary fire until a synchronized absolute Time.Now.
///
/// When requireRelease is true, holding attack through the opening moment
/// does not count. The trigger must be released and pressed again.
/// </summary>
public void ConfigureTemporaryPrimaryGate(
float opensAt,
bool requireRelease
)
{
_temporaryPrimaryGateEnabled = true;
_temporaryPrimaryGateOpensAt = opensAt;
_temporaryPrimaryGateRequireRelease = requireRelease;
// If the trigger is already held, it must be released after this gate is
// configured before a shot can become valid.
_temporaryPrimaryGateReleased =
!requireRelease &&
Time.Now >= opensAt;
}
public void SetTemporarySpreadOverride(
Vector2 spread
)
{
_temporarySpreadOverrideEnabled = true;
_temporarySpreadOverride = new Vector2(
MathF.Max( spread.x, 0f ),
MathF.Max( spread.y, 0f )
);
}
public void ClearTemporaryRoundRules()
{
_temporaryPrimaryGateEnabled = false;
_temporaryPrimaryGateOpensAt = 0f;
_temporaryPrimaryGateRequireRelease = false;
_temporaryPrimaryGateReleased = false;
_temporarySpreadOverrideEnabled = false;
_temporarySpreadOverride = Vector2.Zero;
}
private void UpdateTemporaryPrimaryGate()
{
if ( !_temporaryPrimaryGateEnabled )
return;
bool attackDown =
Input.Down( "attack1" );
// Any held trigger before the gate opens disarms the release condition.
// This prevents pressing a fraction before DRAW and simply holding it.
if ( Time.Now < _temporaryPrimaryGateOpensAt )
{
if ( attackDown )
_temporaryPrimaryGateReleased = false;
return;
}
if (
_temporaryPrimaryGateRequireRelease &&
!attackDown
)
{
_temporaryPrimaryGateReleased = true;
}
}
private bool IsPrimaryBlockedByTemporaryGate()
{
if ( !_temporaryPrimaryGateEnabled )
return false;
if ( Time.Now < _temporaryPrimaryGateOpensAt )
return true;
if (
_temporaryPrimaryGateRequireRelease &&
!_temporaryPrimaryGateReleased
)
return true;
return false;
}
private bool TryGetTemporarySpreadOverride(
out Vector2 spread
)
{
spread =
_temporarySpreadOverride;
return _temporarySpreadOverrideEnabled;
}
private void NotifyRoundPrimaryShotFired()
{
PlayerState player =
Owner?
.GameObject
.Components
.Get<PlayerState>();
player?.
NotifyRoundPrimaryShotFired(
Time.Now
);
}
}