Game/Weapon/AmmoInventory.cs
/// <summary>
/// Stores shared ammo pools on a player, keyed by <see cref="AmmoResource"/>.
/// Add this component to the player prefab alongside <see cref="PlayerInventory"/>.
/// </summary>
public sealed class AmmoInventory : Component
{
/// <summary>
/// Ammo pool: resource path → current count.
/// Host-authoritative so server-side pickups replicate correctly to the owning client.
/// </summary>
[Sync( SyncFlags.FromHost )] public NetDictionary<string, int> Pool { get; set; } = new();
/// <summary>
/// Returns the current ammo count for the given resource.
/// </summary>
public int GetAmmo( AmmoResource resource )
{
if ( resource is null ) return 0;
return Pool.TryGetValue( resource.ResourcePath, out var count ) ? count : 0;
}
/// <summary>
/// Sets the ammo count for the given resource directly, clamped to [0, max].
/// Routes through the host when called from a client.
/// </summary>
public void SetAmmo( AmmoResource resource, int value )
{
if ( resource is null ) return;
if ( !Networking.IsHost ) { SetAmmoRpc( resource, Math.Clamp( value, 0, resource.MaxReserve ) ); return; }
Pool[resource.ResourcePath] = Math.Clamp( value, 0, resource.MaxReserve );
}
/// <summary>
/// Adds ammo to the pool for the given resource (clamped to max).
/// Returns the actual amount added (optimistic when called from a client).
/// </summary>
public int AddAmmo( AmmoResource resource, int count )
{
if ( resource is null ) return 0;
if ( !Networking.IsHost ) { AddAmmoRpc( resource, count ); return count; }
var current = GetAmmo( resource );
var space = resource.MaxReserve - current;
var toAdd = Math.Min( count, space );
if ( toAdd <= 0 ) return 0;
Pool[resource.ResourcePath] = current + toAdd;
return toAdd;
}
/// <summary>
/// Attempts to consume <paramref name="count"/> ammo from the pool.
/// Returns <c>true</c> and deducts the ammo if successful (optimistic when called from a client).
/// </summary>
public bool TakeAmmo( AmmoResource resource, int count )
{
if ( resource is null ) return false;
if ( !Networking.IsHost ) { TakeAmmoRpc( resource, count ); return GetAmmo( resource ) >= count; }
var current = GetAmmo( resource );
if ( current < count ) return false;
Pool[resource.ResourcePath] = current - count;
return true;
}
/// <summary>
/// Returns true if there is at least <paramref name="count"/> ammo in the pool.
/// </summary>
public bool HasAmmo( AmmoResource resource, int count = 1 )
{
return GetAmmo( resource ) >= count;
}
[Rpc.Host]
private void SetAmmoRpc( AmmoResource resource, int value )
{
Pool[resource.ResourcePath] = value;
}
[Rpc.Host]
private void AddAmmoRpc( AmmoResource resource, int count )
{
var current = Pool.TryGetValue( resource.ResourcePath, out var c ) ? c : 0;
var toAdd = Math.Min( count, resource.MaxReserve - current );
if ( toAdd > 0 )
Pool[resource.ResourcePath] = current + toAdd;
}
[Rpc.Host]
private void TakeAmmoRpc( AmmoResource resource, int count )
{
var current = Pool.TryGetValue( resource.ResourcePath, out var c ) ? c : 0;
if ( current >= count )
Pool[resource.ResourcePath] = current - count;
}
}