Player/PlayerPawn.Inventory.cs
public sealed partial class PlayerPawn
{
// -------------------------------------------------------------------------
// Inventory
// -------------------------------------------------------------------------
[Sync( SyncFlags.FromHost )] public NetList<Weapon> Weapons { get; private set; } = new();
// ActiveWeapon is the last-used weapon, used for UI display only (owner-written)
[Sync, Change( nameof( OnActiveWeaponChanged ) )] public Weapon ActiveWeapon { get; private set; }
private int _activeWeaponInput;
private void SetUpWeapons()
{
ClearWeapons();
AddWeapon( ResourceLibrary.Get<PrefabFile>( _primaryWeapon ), makeActive: true );
if ( PilotGame.Gamemode != FPGameMode.Instagib )
AddWeapon( ResourceLibrary.Get<PrefabFile>( _secondaryWeapon ) );
}
public void AddWeapon( PrefabFile prefab, bool makeActive = false )
{
if ( prefab == null ) return;
var weaponGo = SceneUtility.GetPrefabScene( prefab )?.Clone();
if ( weaponGo == null ) return;
weaponGo.SetParent( GameObject, false );
weaponGo.Enabled = true; // set enabled before NetworkSpawn so clients receive correct initial state
weaponGo.NetworkSpawn(); // host-owned so host can always call [Rpc.Broadcast] from weapon components
var weapon = weaponGo.Components.Get<Weapon>( FindMode.EverythingInSelfAndDescendants );
if ( weapon == null ) { weaponGo.Destroy(); return; }
AddWeapon( weapon, makeActive );
}
public void AddWeapon( Weapon weapon, bool makeActive = false )
{
Weapons.Add( weapon );
weapon.Player = this;
weapon.GameObject.Enabled = true; // ensure weapon is always active
if ( makeActive )
SetActiveWeapon( weapon );
}
public void RemoveWeapon( Weapon weapon )
{
if ( ActiveWeapon == weapon )
SetActiveWeapon( null );
Weapons.Remove( weapon );
weapon.GameObject.Destroy();
}
public void SetActiveWeapon( Weapon weapon )
{
if ( ActiveWeapon == weapon ) return;
ActiveWeapon = weapon;
}
private void OnActiveWeaponChanged( Weapon before, Weapon after ) { }
public Weapon GetSlot( int slot )
{
return slot >= 0 && slot < Weapons.Count ? Weapons[slot] : null;
}
public void SwitchActiveSlot( int direction, bool loop = false )
{
if ( Weapons.Count == 0 ) return;
var index = Weapons.IndexOf( ActiveWeapon );
index += direction;
if ( loop )
{
index = ((index % Weapons.Count) + Weapons.Count) % Weapons.Count;
}
else
{
index = index.Clamp( 0, Weapons.Count - 1 );
}
SetActiveWeapon( GetSlot( index ) );
}
public void SimulateInventory()
{
// Auto-select first weapon once weapons have replicated, or if current weapon was destroyed
if ( (ActiveWeapon == null || !ActiveWeapon.IsValid()) && Weapons.Count > 0 )
SetActiveWeapon( GetSlot( 0 ) );
// Use child component iteration rather than Weapons NetList to avoid
// race conditions where NetList updates arrive before weapon GOs on clients
foreach ( var w in Components.GetAll<Weapon>( FindMode.InChildren ) )
w.Simulate();
}
public void FrameSimulateInventory()
{
foreach ( var w in Components.GetAll<Weapon>( FindMode.InChildren ) )
w.FrameSimulate();
}
private void BuildInventoryInput()
{
if ( Input.Pressed( "Slot1" ) ) SetActiveWeapon( GetSlot( 0 ) );
if ( Input.Pressed( "Slot2" ) ) SetActiveWeapon( GetSlot( 1 ) );
}
public void ClearWeapons()
{
SetActiveWeapon( null );
foreach ( var w in Weapons.ToList() )
w?.GameObject?.Destroy();
Weapons.Clear();
}
}