Player/PlayerInventory.cs
public sealed class PlayerInventory : Component, IPlayerEvent
{
[RequireComponent]
public Player Player { get; set; }
[Sync]
public Carryable Current { get; private set; }
public List<Item> Items => GetComponentsInChildren<Item>( true ).ToList();
public List<Carryable> Carryables => Items.OfType<Carryable>()
.OrderBy( x => x.InventorySlot )
.ThenBy( x => x.InventoryOrder )
.ToList();
public void GiveDefaultWeapons()
{
// Don't run any pickup notices when spawning in
using var _ = Player.NoNoticeScope();
Pickup( "weapons/crowbar/crowbar.prefab" );
// Pickup( "weapons/hands/hands.prefab" );
// Pickup( "weapons/camera/camera.prefab" );
Pickup( "weapons/glock/glock.prefab" );
Player.GiveAmmo( ResourceLibrary.Get<AmmoResource>( "weapons/ammo/9mm.ammo" ), 100, false );
if ( GameSettings.CheatMode )
{
GiveAll();
}
}
public void GiveAll()
{
Pickup( "weapons/mp5/mp5.prefab" );
Pickup( "weapons/gaussgun/gauss.prefab" );
Pickup( "weapons/python/python.prefab" );
Pickup( "weapons/handgrenade/hand_grenade.prefab" );
Pickup( "weapons/tripmine/tripmine.prefab" );
Pickup( "weapons/satchelcharge/satchelcharge.prefab" );
Pickup( "weapons/rpg/rpg.prefab" );
Pickup( "weapons/crossbow/crossbow.prefab" );
Pickup( "weapons/shotgun/shotgun.prefab" );
Pickup( "weapons/ratgun/rat_gun.prefab" );
Pickup( "weapons/gluongun/gluon_gun.prefab" );
Pickup( "weapons/hornetgun/hornetgun.prefab" );
var ammo = ResourceLibrary.GetAll<AmmoResource>();
foreach ( var a in ammo )
{
Player.GiveAmmo( a, 100, false );
}
}
public bool HasItem<T>() where T : Item
{
return Items.OfType<T>().Any();
}
public T GetItem<T>() where T : Item
{
return Items.OfType<T>().FirstOrDefault();
}
public bool HasWeapon<T>() where T : Carryable
{
return Carryables.OfType<T>().Any();
}
public T GetWeapon<T>() where T : Carryable
{
return Carryables.OfType<T>().FirstOrDefault();
}
public bool HasWeapon( GameObject prefab )
{
var carry = prefab.Components.Get<Carryable>( true );
if ( carry is null )
return false;
return Carryables.Where( x => x.GetType() == carry.GetType() ).FirstOrDefault().IsValid();
}
public void SwitchWeapon( Carryable weapon )
{
if ( weapon == Current )
return;
if ( Current.IsValid() )
{
Current.OnHolstered( Player );
Current.GameObject.Enabled = false;
}
Current = weapon;
if ( Current.IsValid() )
{
Current.OnEquipped( Player );
Current.GameObject.Enabled = true;
}
}
public Carryable GetBestWeapon()
{
return Carryables.OrderByDescending( x => x.Value ).FirstOrDefault();
}
public Carryable GetBestWeaponHolstered()
{
return Carryables
.Where( x => !x.ShouldAvoid )
.OrderByDescending( x => x.Value )
.Where( x => x != Current )
.FirstOrDefault();
}
public bool Pickup( string prefabName, bool notice = true )
{
if ( !Networking.IsHost )
return false;
var prefab = GameObject.GetPrefab( prefabName );
return Pickup( prefab, notice );
}
public bool Pickup( GameObject prefab, bool notice = true )
{
if ( !Networking.IsHost )
return false;
var item = prefab.GetComponent<Item>( true );
if ( item is null )
return false;
// Check if we already have this type of item
var existingItem = Items.FirstOrDefault( x => x.GetType() == item.GetType() );
if ( existingItem.IsValid() )
{
// Special case for weapons with ammo
if ( existingItem is BaseWeapon existingWeapon && item is BaseWeapon newWeapon && newWeapon.UsesAmmo )
{
return HandleAmmoPickup( existingWeapon, newWeapon, notice );
}
return false;
}
// Create and set up the new item
var clone = prefab.Clone( new CloneConfig { Parent = GameObject, StartEnabled = false } );
clone.NetworkSpawn( false, Network.Owner );
var newItem = clone.GetComponent<Item>( true );
Assert.NotNull( newItem );
newItem.OnAdded( Player );
HandlePickup( newItem, notice );
return true;
}
private bool HandleAmmoPickup( BaseWeapon existing, BaseWeapon weapon, bool notice )
{
var ammo = weapon.AmmoResource;
if ( ammo is null || Player.GetAmmoCount( ammo ) >= ammo.MaxAmount )
return false;
Player.GiveAmmo( ammo, weapon.UsesClips ? weapon.ClipContents : weapon.StartingAmmo, notice );
NotifyItemPickup( existing, true );
return true;
}
private void HandlePickup( Item item, bool notice = true )
{
IPlayerEvent.PostToGameObject( Player.GameObject, e => e.OnPickup( item ) );
if ( notice )
{
NotifyItemPickup( item );
}
}
public void Take( Item item, bool notices = true )
{
// Check for existing item of same type
var existing = Items.FirstOrDefault( x => x.GetType() == item.GetType() );
if ( existing.IsValid() )
{
if ( existing is BaseWeapon existingWeapon && item is BaseWeapon newWeapon )
{
HandleAmmoPickup( existingWeapon, newWeapon, notices );
}
item.DestroyGameObject();
return;
}
// Set up the new item
item.GameObject.Parent = GameObject;
item.Network.Refresh();
if ( Network.Owner is not null )
{
item.Network.AssignOwnership( Network.Owner );
}
else
{
item.Network.DropOwnership();
}
item.OnAdded( Player );
HandlePickup( item, notices );
}
[Rpc.Owner]
private void NotifyItemPickup( Item item, bool justAmmo = false )
{
if ( !item.IsValid() )
return;
if ( item is Carryable carryable && ShouldAutoswitchTo( carryable ) )
{
SwitchToCarryable( carryable );
}
if ( Player.IsLocalPlayer )
ILocalPlayerEvent.Post( e => e.OnPickup( item ) );
}
private bool ShouldAutoswitchTo( Carryable item )
{
if ( !item.IsValid() )
return false;
if ( !Current.IsValid() )
return true;
if ( !GamePreferences.AutoSwitch || Current.IsInUse() )
return false;
if ( item is BaseWeapon weapon && weapon.UsesAmmo )
{
var ammo = weapon.AmmoResource;
if ( ammo is not null && Player.GetAmmoCount( ammo ) < 1 )
return false;
}
return item.Value > Current.Value;
}
public void SwitchToCarryable( Carryable weapon )
{
if ( weapon == Current )
return;
if ( Current.IsValid() )
{
Current.OnHolstered( Player );
Current.GameObject.Enabled = false;
}
Current = weapon;
if ( Current.IsValid() )
{
Current.OnEquipped( Player );
Current.GameObject.Enabled = true;
}
}
protected override void OnUpdate()
{
if ( Current.IsValid() )
{
Current.OnPlayerUpdate( Player );
}
}
public void DropCoffin()
{
if ( !Networking.IsHost )
return;
var go = GameObject.Clone( "items/coffin/coffin.prefab" );
go.Name = $"Coffin for {GameObject.Name}";
go.WorldPosition = Player.EyeTransform.Position;
go.WorldRotation = Rotation.LookAt( Player.EyeTransform.Forward.WithZ( 0 ), Vector3.Up );
var coffin = go.GetComponent<Coffin>();
Assert.True( coffin.IsValid(), "Coffin not on coffin prefab" );
coffin.AmmoCounts = new( Player.AmmoCounts );
if ( go.GetComponent<Rigidbody>() is { } rb )
{
rb.Velocity = Player.Controller.Velocity + (Player.EyeTransform.Backward * 128);
}
go.NetworkSpawn( true, null );
// Transfer all items to the coffin
foreach ( var item in Items.ToArray() )
{
item.OnRemoved( Player );
item.Network.DropOwnership();
item.GameObject.Parent = go;
item.GameObject.Enabled = false;
item.Network.Refresh();
}
}
// IPlayerEvent implementation
void IPlayerEvent.OnSpawned() => GiveDefaultWeapons();
void IPlayerEvent.OnDied( IPlayerEvent.DiedParams args )
{
foreach ( var item in Items )
{
item.OnPlayerDeath( args );
}
}
void IPlayerEvent.OnPickup( Item item )
{
if ( item is BaseWeapon weapon && weapon.IsSelfAmmo )
Player.ShowNotice( $"{weapon.AmmoResource.AmmoType} x {weapon.StartingAmmo}" );
else
Player.ShowNotice( item.DisplayName );
}
void IPlayerEvent.OnCameraMove( ref Angles angles )
{
if ( Current.IsValid() )
Current.OnCameraMove( Player, ref angles );
}
void IPlayerEvent.OnCameraPostSetup( Sandbox.CameraComponent camera )
{
if ( Current.IsValid() )
Current.OnCameraSetup( Player, camera );
}
}