Weapons/BaseWeapon/BaseWeapon.Reloading.cs
using System.Threading;
public partial class BaseWeapon
{
/// <summary>
/// Should we consume 1 bullet per reload instead of filling the clip?
/// </summary>
[Property, Feature( "Ammo" )]
public bool IncrementalReloading { get; set; } = false;
/// <summary>
/// Can we cancel reloads?
/// </summary>
[Property, Feature( "Ammo" )]
public bool CanCancelReload { get; set; } = true;
private CancellationTokenSource reloadToken;
private bool isReloading;
public bool CanReload()
{
if ( !UsesClips ) return false;
if ( ClipContents >= ClipMaxSize ) return false;
if ( isReloading ) return false;
var owner = Owner;
if ( !owner.IsValid() || owner.GetAmmoCount( AmmoResource ) <= 0 )
return false;
return true;
}
public bool IsReloading() => isReloading;
public virtual void CancelReload()
{
if ( reloadToken?.IsCancellationRequested == false )
{
reloadToken?.Cancel();
isReloading = false;
}
}
public virtual async void OnReloadStart()
{
if ( !CanReload() )
return;
CancelReload();
try
{
reloadToken = new CancellationTokenSource();
isReloading = true;
await ReloadAsync( reloadToken.Token );
}
finally
{
reloadToken?.Dispose();
reloadToken = null;
}
}
[Rpc.Broadcast]
private void BroadcastReload()
{
if ( !Owner.IsValid() ) return;
Assert.True( Owner.Controller.IsValid(), "BaseWeapon::BroadcastReload - Player Controller is invalid!" );
Assert.True( Owner.Controller.Renderer.IsValid(), "BaseWeapon::BroadcastReload - Renderer is invalid!" );
Owner.Controller.Renderer.Set( "b_reload", true );
}
protected virtual async Task ReloadAsync( CancellationToken ct )
{
try
{
IWeaponEvent.PostToGameObject( ViewModel, x => x.OnReloadStart() );
BroadcastReload();
while ( ClipContents < ClipMaxSize && !ct.IsCancellationRequested )
{
await Task.DelaySeconds( ReloadTime, ct );
var owner = Owner;
if ( !owner.IsValid() )
break;
var needed = IncrementalReloading ? 1 : (ClipMaxSize - ClipContents);
var available = owner.SubtractAmmoCount( AmmoResource, needed );
if ( available <= 0 )
break;
IWeaponEvent.PostToGameObject( ViewModel, x => x.OnIncrementalReload() );
ClipContents += available;
}
if ( ClipContents > 0 )
{
IWeaponEvent.PostToGameObject( ViewModel, x => x.OnReloadFinish() );
}
}
catch ( OperationCanceledException ) { }
finally
{
reloadToken?.Cancel();
isReloading = false;
}
}
}