diff --git a/.editorconfig b/.editorconfig
index fb22870..0799bd4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -60,7 +60,7 @@ csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_open_brace = all
+csharp_new_line_before_open_brace = none
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
diff --git a/code/.idea/.idea.code.dir/.idea/.gitignore b/code/.idea/.idea.code.dir/.idea/.gitignore
new file mode 100644
index 0000000..37a2731
--- /dev/null
+++ b/code/.idea/.idea.code.dir/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/contentModel.xml
+/.idea.code.iml
+/modules.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/code/Base/BaseCarriable.cs b/code/Base/BaseCarriable.cs
new file mode 100644
index 0000000..635b6cd
--- /dev/null
+++ b/code/Base/BaseCarriable.cs
@@ -0,0 +1,140 @@
+using Sandbox.Systems.Inventory;
+
+namespace Sandbox.Base;
+
+///
+/// An entity that can be carried in the player's inventory and hands.
+///
+[Title( "Carriable" ), Icon( "luggage" )]
+public partial class BaseCarriable : AnimatedEntity, ICariable {
+ public virtual string ViewModelPath => null;
+ public BaseViewModel ViewModelEntity { get; protected set; }
+
+ public CariableHoldTypes HoldType { get; protected set; } = CariableHoldTypes.Pistol;
+ public CariableHandedness Handedness { get; protected set; } = CariableHandedness.Both;
+
+ public float AimBodyWeight { get; protected set; } = 1.0f;
+
+ public override void Spawn() {
+ base.Spawn();
+
+ PhysicsEnabled = true;
+ UsePhysicsCollision = true;
+ EnableHideInFirstPerson = true;
+ EnableShadowInFirstPerson = true;
+ }
+
+ public virtual bool CanCarry( Entity carrier ) {
+ return true;
+ }
+
+ public virtual bool CanDrop( Entity carrier ) {
+ return true;
+ }
+
+ public virtual void OnCarry( Entity carrier ) {
+ if ( Game.IsClient ) return;
+
+ SetParent( carrier, true );
+ Owner = carrier;
+ EnableAllCollisions = false;
+ EnableDrawing = false;
+ }
+
+ public virtual void OnDrop( Entity dropper ) {
+ if ( Game.IsClient ) return;
+
+ SetParent( null );
+ Owner = null;
+ EnableDrawing = true;
+ EnableAllCollisions = true;
+ }
+
+ ///
+ /// This entity has become the active entity. This most likely
+ /// means a player was carrying it in their inventory and now
+ /// has it in their hands.
+ ///
+ public virtual void ActiveStart( Entity ent ) {
+ EnableDrawing = true;
+
+ //
+ // If we're the local player (clientside) create viewmodel
+ // and any HUD elements that this weapon wants
+ //
+ if ( IsLocalPawn ) {
+ DestroyViewModel();
+ DestroyHudElements();
+
+ CreateViewModel();
+ CreateHudElements();
+ }
+ }
+
+ ///
+ /// This entity has stopped being the active entity. This most
+ /// likely means a player was holding it but has switched away
+ /// or dropped it (in which case dropped = true)
+ ///
+ public virtual void ActiveEnd( Entity ent, bool dropped ) {
+ //
+ // If we're just holstering, then hide us
+ //
+ if ( !dropped ) {
+ EnableDrawing = false;
+ }
+
+ if ( Game.IsClient ) {
+ DestroyViewModel();
+ DestroyHudElements();
+ }
+ }
+
+ protected override void OnDestroy() {
+ base.OnDestroy();
+
+ if ( Game.IsClient && ViewModelEntity.IsValid() ) {
+ DestroyViewModel();
+ DestroyHudElements();
+ }
+ }
+
+ ///
+ /// Create the viewmodel. You can override this in your base classes if you want
+ /// to create a certain viewmodel entity.
+ ///
+ public virtual void CreateViewModel() {
+ Game.AssertClient();
+
+ if ( string.IsNullOrEmpty( ViewModelPath ) )
+ return;
+
+ ViewModelEntity = new BaseViewModel();
+ ViewModelEntity.Position = Position;
+ ViewModelEntity.Owner = Owner;
+ ViewModelEntity.EnableViewmodelRendering = true;
+ ViewModelEntity.SetModel( ViewModelPath );
+ }
+
+ ///
+ /// We're done with the viewmodel - delete it
+ ///
+ public virtual void DestroyViewModel() {
+ ViewModelEntity?.Delete();
+ ViewModelEntity = null;
+ }
+
+ public virtual void CreateHudElements() {
+ }
+
+ public virtual void DestroyHudElements() {
+
+ }
+
+ ///
+ /// Utility - return the entity we should be spawning particles from etc
+ ///
+ public virtual ModelEntity EffectEntity =>
+ (ViewModelEntity.IsValid() && IsFirstPersonMode) ? ViewModelEntity : this;
+
+}
diff --git a/code/Base/BaseWeapon.cs b/code/Base/BaseWeapon.cs
new file mode 100644
index 0000000..b1c7bae
--- /dev/null
+++ b/code/Base/BaseWeapon.cs
@@ -0,0 +1,144 @@
+namespace Sandbox.Base
+{
+ ///
+ /// A common base we can use for weapons so we don't have to implement the logic over and over
+ /// again. Feel free to not use this and to implement it however you want to.
+ ///
+ [Title( "Base Weapon" ), Icon( "sports_martial_arts" )]
+ public partial class BaseWeapon : BaseCarriable
+ {
+ public virtual float PrimaryRate => 5.0f;
+ public virtual float SecondaryRate => 15.0f;
+
+ public override void Spawn()
+ {
+ base.Spawn();
+
+ Tags.Add( "item" );
+ }
+
+ [Net, Predicted]
+ public TimeSince TimeSincePrimaryAttack { get; set; }
+
+ [Net, Predicted]
+ public TimeSince TimeSinceSecondaryAttack { get; set; }
+
+ public override void Simulate( IClient player )
+ {
+ if ( CanReload() )
+ {
+ Reload();
+ }
+
+ //
+ // Reload could have changed our owner
+ //
+ if ( !Owner.IsValid() )
+ return;
+
+ if ( CanPrimaryAttack() )
+ {
+ using ( LagCompensation() )
+ {
+ TimeSincePrimaryAttack = 0;
+ AttackPrimary();
+ }
+ }
+
+ //
+ // AttackPrimary could have changed our owner
+ //
+ if ( !Owner.IsValid() )
+ return;
+
+ if ( CanSecondaryAttack() )
+ {
+ using ( LagCompensation() )
+ {
+ TimeSinceSecondaryAttack = 0;
+ AttackSecondary();
+ }
+ }
+ }
+
+ public virtual bool CanReload()
+ {
+ if ( !Owner.IsValid() || !Input.Down( "reload" ) ) return false;
+
+ return true;
+ }
+
+ public virtual void Reload()
+ {
+
+ }
+
+ public virtual bool CanPrimaryAttack()
+ {
+ if ( !Owner.IsValid() || !Input.Down( "attack1" ) ) return false;
+
+ var rate = PrimaryRate;
+ if ( rate <= 0 ) return true;
+
+ return TimeSincePrimaryAttack > (1 / rate);
+ }
+
+ public virtual void AttackPrimary()
+ {
+
+ }
+
+ public virtual bool CanSecondaryAttack()
+ {
+ if ( !Owner.IsValid() || !Input.Down( "attack2" ) ) return false;
+
+ var rate = SecondaryRate;
+ if ( rate <= 0 ) return true;
+
+ return TimeSinceSecondaryAttack > (1 / rate);
+ }
+
+ public virtual void AttackSecondary()
+ {
+
+ }
+
+ ///
+ /// Does a trace from start to end, does bullet impact effects. Coded as an IEnumerable so you can return multiple
+ /// hits, like if you're going through layers or ricocheting or something.
+ ///
+ public virtual IEnumerable TraceBullet( Vector3 start, Vector3 end, float radius = 2.0f )
+ {
+ bool underWater = Trace.TestPoint( start, "water" );
+
+ var trace = Trace.Ray( start, end )
+ .UseHitboxes()
+ .WithAnyTags( "solid", "player", "npc" )
+ .Ignore( this )
+ .Size( radius );
+
+ //
+ // If we're not underwater then we can hit water
+ //
+ if ( !underWater )
+ trace = trace.WithAnyTags( "water" );
+
+ var tr = trace.Run();
+
+ if ( tr.Hit )
+ yield return tr;
+
+ //
+ // Another trace, bullet going through thin material, penetrating water surface?
+ //
+ }
+
+ public override Sound PlaySound( string soundName, string attachment )
+ {
+ if ( Owner.IsValid() )
+ return Owner.PlaySound( soundName, attachment );
+
+ return base.PlaySound( soundName, attachment );
+ }
+ }
+}
diff --git a/code/Carriable.cs b/code/Base/Carriable.cs
similarity index 59%
rename from code/Carriable.cs
rename to code/Base/Carriable.cs
index 67901d3..536afcb 100644
--- a/code/Carriable.cs
+++ b/code/Base/Carriable.cs
@@ -1,4 +1,6 @@
using Sandbox;
+using Sandbox.Systems.Inventory;
+using BaseCarriable = Sandbox.Base.BaseCarriable;
public partial class Carriable : BaseCarriable, IUse
{
@@ -28,4 +30,20 @@ public virtual bool IsUsable( Entity user )
{
return Owner == null;
}
+
+ public virtual bool CanUnequip( Entity owner ) {
+ return true;
+ }
+
+ public void OnUnEquip( Entity owner ) {
+ OnDrop(owner);
+ }
+
+ public bool CanEquip( Entity owner ) {
+ return CanCarry( owner );
+ }
+
+ public void OnEquip( Entity owner ) {
+ OnCarry( owner );
+ }
}
diff --git a/code/PreviewEntity.cs b/code/Base/Tools/PreviewEntity.cs
similarity index 100%
rename from code/PreviewEntity.cs
rename to code/Base/Tools/PreviewEntity.cs
diff --git a/code/Tool.CanTool.cs b/code/Base/Tools/Tool.CanTool.cs
similarity index 85%
rename from code/Tool.CanTool.cs
rename to code/Base/Tools/Tool.CanTool.cs
index 3b5e564..c8b605a 100644
--- a/code/Tool.CanTool.cs
+++ b/code/Base/Tools/Tool.CanTool.cs
@@ -1,16 +1,17 @@
using Sandmod.Permission;
+using Sandbox.Systems.Player;
namespace Sandbox
{
public class CanToolParams
{
- public Player player;
+ public BasePlayer player;
public string toolName;
public TraceResult tr;
public Entity entity;
public bool preventDefault = false;
- public static TraceResult RunCanTool( Player player, string toolName, TraceResult tr )
+ public static TraceResult RunCanTool( BasePlayer player, string toolName, TraceResult tr )
{
if ( Game.IsClient )
{
diff --git a/code/Tool.Effects.cs b/code/Base/Tools/Tool.Effects.cs
similarity index 100%
rename from code/Tool.Effects.cs
rename to code/Base/Tools/Tool.Effects.cs
diff --git a/code/Tool.Preview.cs b/code/Base/Tools/Tool.Preview.cs
similarity index 100%
rename from code/Tool.Preview.cs
rename to code/Base/Tools/Tool.Preview.cs
diff --git a/code/Tool.cs b/code/Base/Tools/Tool.cs
similarity index 90%
rename from code/Tool.cs
rename to code/Base/Tools/Tool.cs
index b9f1181..26e9895 100644
--- a/code/Tool.cs
+++ b/code/Base/Tools/Tool.cs
@@ -1,4 +1,4 @@
-using Sandbox;
+using Sandbox.Systems.Player;
using Sandbox.Tools;
using Sandbox.UI;
@@ -75,7 +75,7 @@ private void UpdateCurrentTool( IClient owner )
if ( CurrentTool != null )
{
CurrentTool.Parent = this;
- CurrentTool.Owner = owner.Pawn as Player;
+ CurrentTool.Owner = owner.Pawn as BasePlayer;
CurrentTool.Activate();
}
}
@@ -166,7 +166,7 @@ public override void BuildInput()
CurrentTool?.BuildInput();
}
- public override void OnCarryDrop( Entity dropper )
+ public override void OnDrop( Entity dropper )
{
}
@@ -195,13 +195,6 @@ public void OnFrame()
}
}
- public override void SimulateAnimator( CitizenAnimationHelper anim )
- {
- anim.HoldType = CitizenAnimationHelper.HoldTypes.Pistol;
- anim.Handedness = CitizenAnimationHelper.Hand.Right;
- anim.AimBodyWeight = 1.0f;
- }
-
public static void SetActiveTool( string toolId )
{
ConsoleSystem.Run( "tool_current", toolId );
@@ -217,7 +210,7 @@ public partial class BaseTool : BaseNetworkable
public Tool Parent { get; set; }
[Net]
- public Player Owner { get; set; }
+ public BasePlayer Owner { get; set; }
protected virtual float MaxTraceDistance => 10000.0f;
@@ -264,15 +257,8 @@ public virtual void OnFrame()
Parent?.CreateHitEffects( pos, normal, continuous );
}
- public virtual TraceResult DoTrace( bool checkCanTool = true )
- {
- var startPos = Owner.EyePosition;
- var dir = Owner.EyeRotation.Forward;
-
- var tr = Trace.Ray( startPos, startPos + (dir * MaxTraceDistance) )
- .WithAnyTags( "solid", "nocollide" )
- .Ignore( Owner )
- .Run();
+ public virtual TraceResult DoTrace( bool checkCanTool = true ) {
+ var tr = Owner.TraceRay( MaxTraceDistance );
if ( checkCanTool && tr.Entity.IsValid() && !tr.Entity.IsWorld )
{
diff --git a/code/ViewModel.cs b/code/Base/ViewModel.cs
similarity index 100%
rename from code/ViewModel.cs
rename to code/Base/ViewModel.cs
diff --git a/code/Weapon.cs b/code/Base/Weapons/Weapon.cs
similarity index 95%
rename from code/Weapon.cs
rename to code/Base/Weapons/Weapon.cs
index d61ee03..4628646 100644
--- a/code/Weapon.cs
+++ b/code/Base/Weapons/Weapon.cs
@@ -1,5 +1,7 @@
-using Sandbox;
-using System.Collections.Generic;
+
+using Sandbox.Systems.Inventory;
+using Sandbox.Systems.Player;
+using BaseWeapon = Sandbox.Base.BaseWeapon;
public partial class Weapon : BaseWeapon, IUse
{
@@ -120,12 +122,12 @@ public bool OnUse( Entity user )
public virtual bool IsUsable( Entity user )
{
- var player = user as Player;
+ var player = user as BasePlayer;
if ( Owner != null ) return false;
- if ( player.Inventory is Inventory inventory )
+ if ( player.Inventory is BasePlayerInventory inventory )
{
- return inventory.CanAdd( this );
+ return inventory.CanAddToEntity( this );
}
return true;
diff --git a/code/Game.cs b/code/Game.cs
index b55505b..c1c2217 100644
--- a/code/Game.cs
+++ b/code/Game.cs
@@ -4,6 +4,8 @@
global using System;
global using Sandbox;
using Sandmod.Permission;
+using Sandbox.Systems.Player;
+using BaseCarriable = Sandbox.Base.BaseCarriable;
partial class SandboxGame : GameManager
{
@@ -53,7 +55,7 @@ protected override void OnDestroy()
[ConCmd.Admin( "giveall" )]
public static void GiveAll()
{
- var player = ConsoleSystem.Caller.Pawn as SandboxPlayer;
+ var player = ConsoleSystem.Caller.Pawn as BasePlayer;
player.Inventory.Add( new Pistol() );
player.Inventory.Add( new MP5() );
@@ -64,7 +66,7 @@ public static void GiveAll()
[ConCmd.Server( "spawn" )]
public static async Task Spawn( string modelname )
{
- var owner = ConsoleSystem.Caller?.Pawn as Player;
+ var owner = ConsoleSystem.Caller?.Pawn as BasePlayer;
if ( ConsoleSystem.Caller == null )
return;
diff --git a/code/Inventory.cs b/code/Inventory.cs
deleted file mode 100644
index d0b2d0c..0000000
--- a/code/Inventory.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using Sandbox;
-using System;
-using System.Linq;
-
-partial class Inventory : BaseInventory
-{
- public Inventory( Player player ) : base( player )
- {
- }
-
- public override bool CanAdd( Entity entity )
- {
- if ( !entity.IsValid() )
- return false;
-
- if ( !base.CanAdd( entity ) )
- return false;
-
- return !IsCarryingType( entity.GetType() );
- }
-
- public override bool Add( Entity entity, bool makeActive = false )
- {
- if ( !entity.IsValid() )
- return false;
-
- if ( IsCarryingType( entity.GetType() ) )
- return false;
-
- return base.Add( entity, makeActive );
- }
-
- public bool IsCarryingType( Type t )
- {
- return List.Any( x => x?.GetType() == t );
- }
-
- public override bool Drop( Entity ent )
- {
- if ( !Game.IsServer )
- return false;
-
- if ( !Contains( ent ) )
- return false;
-
- if ( ent is BaseCarriable bc )
- {
- bc.OnCarryDrop( Owner );
- }
-
- return ent.Parent == null;
- }
-}
diff --git a/code/IAutoload.cs b/code/Load/IAutoload.cs
similarity index 100%
rename from code/IAutoload.cs
rename to code/Load/IAutoload.cs
diff --git a/code/ReloadManager.cs b/code/Load/ReloadManager.cs
similarity index 100%
rename from code/ReloadManager.cs
rename to code/Load/ReloadManager.cs
diff --git a/code/Player.cs b/code/Player.cs
index 8212684..149e469 100644
--- a/code/Player.cs
+++ b/code/Player.cs
@@ -1,6 +1,7 @@
using Sandbox;
using Sandmod.Permission;
using System.Numerics;
+using BaseCarriable = Sandbox.Base.BaseCarriable;
public partial class SandboxPlayer : Player
{
@@ -24,7 +25,7 @@ public partial class SandboxPlayer : Player
public SandboxPlayer()
{
- Inventory = new Inventory( this );
+
}
///
@@ -69,53 +70,6 @@ public override void Respawn()
base.Respawn();
}
- public override void OnKilled()
- {
- base.OnKilled();
-
- if ( lastDamage.HasTag( "vehicle" ) )
- {
- Particles.Create( "particles/impact.flesh.bloodpuff-big.vpcf", lastDamage.Position );
- Particles.Create( "particles/impact.flesh-big.vpcf", lastDamage.Position );
- PlaySound( "kersplat" );
- }
-
- BecomeRagdollOnClient( Velocity, lastDamage.Position, lastDamage.Force, lastDamage.BoneIndex, lastDamage.HasTag( "bullet" ), lastDamage.HasTag( "blast" ) );
-
- Controller = null;
-
- EnableAllCollisions = false;
- EnableDrawing = false;
-
- foreach ( var child in Children )
- {
- child.EnableDrawing = false;
- }
-
- Inventory.DropActive();
- Inventory.DeleteContents();
-
- Event.Run( "player.killed", this );
- }
-
- public override void TakeDamage( DamageInfo info )
- {
- if ( info.Attacker.IsValid() )
- {
- if ( info.Attacker.Tags.Has( $"{PhysGun.GrabbedTag}{Client.SteamId}" ) )
- return;
- }
-
- if ( info.Hitbox.HasTag( "head" ) )
- {
- info.Damage *= 10.0f;
- }
-
- lastDamage = info;
-
- base.TakeDamage( info );
- }
-
public override PawnController GetActiveController()
{
if ( DevController != null ) return DevController;
@@ -208,15 +162,6 @@ public void ToggleNoclip()
}
}
- [ConCmd.Admin( "kill" )]
- static void DoPlayerSuicide()
- {
- if ( ConsoleSystem.Caller.Pawn is SandboxPlayer basePlayer )
- {
- basePlayer.TakeDamage( new DamageInfo { Damage = basePlayer.Health * 99 } );
- }
- }
-
Entity lastWeapon;
@@ -269,7 +214,7 @@ void SimulateAnimation( PawnController controller )
}
else
{
- animHelper.HoldType = CitizenAnimationHelper.HoldTypes.None;
+ animHelper.HoldType = CariableHoldTypes.None;
animHelper.AimBodyWeight = 0.5f;
}
@@ -338,13 +283,5 @@ public override void FrameSimulate( IClient cl )
Camera.Main.SetViewModelCamera( 90f );
}
}
-
- [Event( "entity.spawned" )]
- public static void OnSpawned( Entity spawned, Entity owner )
- {
- if ( owner is Player player )
- {
- spawned.SetPlayerOwner( player );
- }
- }
+
}
diff --git a/code/Player/SandboxPlayer.cs b/code/Player/SandboxPlayer.cs
new file mode 100644
index 0000000..660c4be
--- /dev/null
+++ b/code/Player/SandboxPlayer.cs
@@ -0,0 +1,43 @@
+using Sandbox.Systems.Inventory;
+using Sandbox.Systems.Player;
+
+namespace Sandbox.Player;
+
+public partial class SandboxPlayer: BasePlayer {
+ public override void Respawn() {
+ base.Respawn();
+
+ Inventory.Add( new PhysGun(), true );
+ Inventory.Add( new GravGun() );
+ Inventory.Add( new Tool() );
+ Inventory.Add( new Pistol() );
+ Inventory.Add( new MP5() );
+ Inventory.Add( new Flashlight() );
+ Inventory.Add( new Fists() );
+ }
+
+ public override void TakeDamage( DamageInfo info ) {
+
+ if ( LifeState != LifeState.Alive )
+ return;
+
+ if ( info.Attacker.IsValid() )
+ {
+ if ( info.Attacker.Tags.Has( $"{PhysGun.GrabbedTag}{Client.SteamId}" ) )
+ return;
+ }
+
+
+ base.TakeDamage(info);
+
+ }
+
+ [Event( "entity.spawned" )]
+ public static void OnSpawned( Entity spawned, Entity owner )
+ {
+ if ( owner is BasePlayer player )
+ {
+ spawned.SetPlayerOwner( player );
+ }
+ }
+}
diff --git a/code/Systems/Camera/FirstPersonCamera.cs b/code/Systems/Camera/FirstPersonCamera.cs
new file mode 100644
index 0000000..040c0ae
--- /dev/null
+++ b/code/Systems/Camera/FirstPersonCamera.cs
@@ -0,0 +1,18 @@
+using Sandbox.Systems.Player;
+
+namespace Sandbox.Systems.Camera;
+
+public partial class FirstPersonCamera: EntityComponent, ISingletonComponent, IPlayerCamera {
+
+ public BasePlayer Player => Entity;
+
+ public virtual void Update( ) {
+ Sandbox.Camera.Rotation = Player.EyeRotation;
+ Sandbox.Camera.FieldOfView = Game.Preferences.FieldOfView;
+ Sandbox.Camera.Position = Player.EyePosition;
+ Sandbox.Camera.FieldOfView = Game.Preferences.FieldOfView;
+ Sandbox.Camera.FirstPersonViewer = Player;
+ Sandbox.Camera.ZNear = 0.5f;
+ }
+
+}
diff --git a/code/Systems/Camera/IPlayerCamera.cs b/code/Systems/Camera/IPlayerCamera.cs
new file mode 100644
index 0000000..9e61eb4
--- /dev/null
+++ b/code/Systems/Camera/IPlayerCamera.cs
@@ -0,0 +1,11 @@
+using Sandbox.Internal;
+
+namespace Sandbox.Systems.Camera;
+
+public interface IPlayerCamera: IComponent, INetworkTable {
+
+ void Update( );
+
+ void Remove();
+
+}
diff --git a/code/Systems/Camera/ThirdPersonCamera.cs b/code/Systems/Camera/ThirdPersonCamera.cs
new file mode 100644
index 0000000..d9d8f8f
--- /dev/null
+++ b/code/Systems/Camera/ThirdPersonCamera.cs
@@ -0,0 +1,33 @@
+using Sandbox.Systems.Player;
+
+namespace Sandbox.Systems.Camera;
+
+public partial class ThirdPersonCamera: EntityComponent, ISingletonComponent, IPlayerCamera {
+
+ public BasePlayer Player => Entity;
+
+ public virtual void Update() {
+ Sandbox.Camera.Rotation = Player.EyeRotation;
+ Sandbox.Camera.FieldOfView = Game.Preferences.FieldOfView;
+ var up = -Player.GravityDirection;
+ Sandbox.Camera.FirstPersonViewer = null;
+
+ var center = Player.Position + (up * 64);
+
+ var pos = center;
+ var rot = Rotation.FromAxis( up, -16 ) * Sandbox.Camera.Rotation;
+
+ float distance = 130.0f * Player.Scale;
+ var targetPos = pos + rot.Right * ((Player.CollisionBounds.Mins.x + 32) * Player.Scale);
+ targetPos += rot.Forward * -distance;
+
+ var tr = Trace.Ray( pos, targetPos )
+ .WithAnyTags( "solid" )
+ .Ignore( Player )
+ .Radius( 8 )
+ .Run();
+
+ Sandbox.Camera.Position = tr.EndPosition;
+ }
+
+}
diff --git a/code/Systems/Inventory/BasePlayerInventory.cs b/code/Systems/Inventory/BasePlayerInventory.cs
new file mode 100644
index 0000000..697c713
--- /dev/null
+++ b/code/Systems/Inventory/BasePlayerInventory.cs
@@ -0,0 +1,116 @@
+using Sandbox.Systems.Player;
+
+namespace Sandbox.Systems.Inventory;
+
+public partial class BasePlayerInventory: EntityComponent, ISingletonComponent
+{
+ [Net] public IList Items { get; set; }
+ [Net, Predicted] public ICariable ActiveCariable { get; set; }
+
+ public bool Add( ICariable cariable, bool makeActive = true ) {
+ if ( Items.Contains( cariable ) ) {
+ return false;
+ }
+ Items.Add(cariable);
+ if ( makeActive ) {
+ SetActive( cariable );
+ }
+ return true;
+ }
+
+ public bool Remove( ICariable cariable, bool drop = false ) {
+ var success = Items.Remove( cariable );
+ if ( success && drop ) {
+ // TODO - Drop the item on the ground
+ }
+
+ return success;
+ }
+
+ public void SetActive( ICariable cariable ) {
+ var currentCariable = ActiveCariable;
+ var owner = Entity;
+ if ( currentCariable.IsValid( ) ) { //TODO difference between IsValid and IsValid() ??
+ if ( !currentCariable.CanCarry( owner ) ) {
+ return;
+ }
+ currentCariable.OnDrop(owner);
+ ActiveCariable = null;
+ }
+
+ if ( !cariable.CanCarry( owner ) ) {
+ return;
+ }
+
+ ActiveCariable = cariable;
+ cariable?.OnCarry( owner );
+ }
+
+ protected override void OnDeactivate()
+ {
+ if ( Game.IsServer )
+ {
+ Items.ToList().ForEach( x => x.Delete() );
+ }
+ }
+
+ public ICariable GetSlot( int slot )
+ {
+ return Items.ElementAtOrDefault( slot ) ?? null;
+ }
+
+ private int GetSlotIndexFromInput( string slot )
+ {
+ return slot switch
+ {
+ "slot1" => 0,
+ "slot2" => 1,
+ "slot3" => 2,
+ "slot4" => 3,
+ "slot5" => 4,
+ _ => -1
+ };
+ }
+
+ private void TrySlotFromInput( string slot ) {
+ if ( Entity is not BasePlayer player ) {
+ return;
+ }
+ if ( !Input.Pressed( slot ) ) {
+ return;
+ }
+
+ Input.ReleaseAction( slot );
+
+ if ( GetSlot( GetSlotIndexFromInput( slot ) ) is { } cariable )
+ {
+ player.ActiveCariableInput = cariable;
+ }
+ }
+
+ public void BuildInput()
+ {
+ TrySlotFromInput( "slot1" );
+ TrySlotFromInput( "slot2" );
+ TrySlotFromInput( "slot3" );
+ TrySlotFromInput( "slot4" );
+ TrySlotFromInput( "slot5" );
+
+ ActiveCariable?.BuildInput();
+ }
+
+ public void Simulate( IClient cl )
+ {
+ if ( Entity is not BasePlayer player ) {
+ return;
+ }
+ if ( player.ActiveCariableInput != null && ActiveCariable != player.ActiveCariableInput )
+ {
+ SetActive( player.ActiveCariableInput );
+ player.ActiveCariableInput = null;
+ }
+
+ ActiveCariable?.Simulate( cl );
+ }
+
+}
diff --git a/code/Systems/Inventory/CariableHandedness.cs b/code/Systems/Inventory/CariableHandedness.cs
new file mode 100644
index 0000000..7aeccca
--- /dev/null
+++ b/code/Systems/Inventory/CariableHandedness.cs
@@ -0,0 +1,11 @@
+namespace Sandbox;
+
+///
+/// Describes the handedness of a weapon, which hand (or both) we hold the weapon in.
+///
+public enum CariableHandedness
+{
+ Both,
+ Right,
+ Left
+}
diff --git a/code/Systems/Inventory/CariableHoldTypes.cs b/code/Systems/Inventory/CariableHoldTypes.cs
new file mode 100644
index 0000000..4ca36aa
--- /dev/null
+++ b/code/Systems/Inventory/CariableHoldTypes.cs
@@ -0,0 +1,13 @@
+namespace Sandbox;
+
+public enum CariableHoldTypes
+{
+ None,
+ Pistol,
+ Rifle,
+ Shotgun,
+ HoldItem,
+ Punch,
+ Swing,
+ RPG
+}
diff --git a/code/Systems/Inventory/ICariable.cs b/code/Systems/Inventory/ICariable.cs
new file mode 100644
index 0000000..d320ef3
--- /dev/null
+++ b/code/Systems/Inventory/ICariable.cs
@@ -0,0 +1,23 @@
+namespace Sandbox.Systems.Inventory;
+
+public interface ICariable: IEntity {
+
+ CariableHoldTypes HoldType { get; }
+ CariableHandedness Handedness { get; }
+
+ float AimBodyWeight { get; }
+
+ bool CanDrop( Entity owner );
+
+ void OnDrop( Entity owner );
+
+ bool CanCarry( Entity owner );
+
+ void OnCarry( Entity owner );
+
+
+ // Entity methods
+
+ void Simulate( IClient cl );
+ void BuildInput();
+}
diff --git a/code/Systems/Player/Animations/BasePlayerAnimator.cs b/code/Systems/Player/Animations/BasePlayerAnimator.cs
new file mode 100644
index 0000000..a678be9
--- /dev/null
+++ b/code/Systems/Player/Animations/BasePlayerAnimator.cs
@@ -0,0 +1,34 @@
+using Sandbox.Physics;
+
+namespace Sandbox.Systems.Player.Animations;
+
+public partial class BasePlayerAnimator: EntityComponent, ISingletonComponent
+{
+
+ public BasePlayer Player => Entity;
+ public Controller.BasePlayerController Controller => Player.Controller;
+
+ public virtual void Simulate( IClient cl )
+ {
+ CitizenAnimationHelper animHelper = new CitizenAnimationHelper( Player );
+
+ animHelper.WithWishVelocity( Controller.WishVelocity );
+ animHelper.WithVelocity( Controller.Velocity );
+ animHelper.WithLookAt( Player.EyePosition + Player.EyeRotation.Forward * 100.0f, 1.0f, 1.0f, 0.5f );
+ animHelper.AimAngle = Player.EyeRotation;
+ animHelper.FootShuffle = 0f;
+ animHelper.DuckLevel = MathX.Lerp( animHelper.DuckLevel, 1 - Controller.CurrentEyeHeight.Remap( 30, 72, 0, 1 ).Clamp( 0, 1 ), Time.Delta * 10.0f );
+ animHelper.VoiceLevel = (Game.IsClient && cl.IsValid()) ? cl.Voice.LastHeard < 0.5f ? cl.Voice.CurrentLevel : 0.0f : 0.0f;
+ animHelper.IsGrounded = Controller.GroundEntity != null;
+ animHelper.IsSwimming = Player.GetWaterLevel() >= 0.5f;
+ animHelper.IsWeaponLowered = false;
+
+ var cariable = Player.ActiveCariable;
+ if ( cariable.IsValid() ) {
+ Player.SetAnimParameter( "holdtype", (int)cariable.HoldType );
+ Player.SetAnimParameter( "holdtype_handedness", (int)cariable.Handedness );
+ animHelper.AimBodyWeight = cariable.AimBodyWeight;
+ }
+ }
+
+}
diff --git a/code/Systems/Player/BasePlayer.Audio.cs b/code/Systems/Player/BasePlayer.Audio.cs
new file mode 100644
index 0000000..da777d7
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Audio.cs
@@ -0,0 +1,12 @@
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ [ClientRpc]
+ public void SetAudioEffect( string effectName, float strength, float velocity = 20f, float fadeOut = 4f )
+ {
+ Audio.SetEffect( effectName, strength, velocity: 20.0f, fadeOut: 4.0f * strength );
+ }
+
+}
diff --git a/code/Systems/Player/BasePlayer.Clothing.cs b/code/Systems/Player/BasePlayer.Clothing.cs
new file mode 100644
index 0000000..07f7b32
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Clothing.cs
@@ -0,0 +1,20 @@
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ public ClothingContainer Clothing { get; protected set; }
+
+ ///
+ /// Set the clothes to whatever the player is wearing
+ ///
+ public void SetupClothing()
+ {
+ Clothing = new();
+
+ Clothing.ClearEntities();
+ Clothing.LoadFromClient( Client );
+ Clothing.DressEntity( this );
+ }
+
+}
diff --git a/code/Systems/Player/BasePlayer.Damage.cs b/code/Systems/Player/BasePlayer.Damage.cs
new file mode 100644
index 0000000..ee21ede
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Damage.cs
@@ -0,0 +1,101 @@
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ ///
+ /// The information for the last piece of damage this player took.
+ ///
+ public DamageInfo LastDamage { get; protected set; }
+
+
+ public override void TakeDamage( DamageInfo info )
+ {
+ if ( LifeState != LifeState.Alive )
+ return;
+
+ // Check for headshot damage
+ var isHeadshot = info.Hitbox.HasTag( "head" );
+ if ( isHeadshot )
+ {
+ info.Damage *= 2.5f;
+ }
+
+ // Check if we got hit by a bullet, if we did, play a sound.
+ if ( info.HasTag( "bullet" ) )
+ {
+ Sound.FromScreen( To.Single( Client ), "sounds/player/damage_taken_shot.sound" );
+ }
+
+ // Play a deafening effect if we get hit by blast damage.
+ if ( info.HasTag( "blast" ) )
+ {
+ SetAudioEffect( To.Single( Client ), "flasthbang", info.Damage.LerpInverse( 0, 60 ) );
+ }
+
+ if ( Health > 0 && info.Damage > 0 )
+ {
+ Health -= info.Damage;
+
+ if ( Health <= 0 )
+ {
+ Health = 0;
+ OnKilled();
+ }
+ }
+
+ this.ProceduralHitReaction( info );
+ }
+
+ public override void OnKilled()
+ {
+ if ( LifeState == LifeState.Alive )
+ {
+ if ( LastDamage.HasTag( "vehicle" ) )
+ {
+ Particles.Create( "particles/impact.flesh.bloodpuff-big.vpcf", LastDamage.Position );
+ Particles.Create( "particles/impact.flesh-big.vpcf", LastDamage.Position );
+ PlaySound( "kersplat" );
+ }
+
+ CreateRagdoll( Controller.Velocity, LastDamage.Position, LastDamage.Force,
+ LastDamage.BoneIndex, LastDamage.HasTag( "bullet" ), LastDamage.HasTag( "blast" ) );
+
+ LifeState = LifeState.Dead;
+ EnableAllCollisions = false;
+ EnableDrawing = false;
+
+ Controller.Remove();
+ Animator.Remove();
+ Inventory.Remove();
+ Camera.Remove();
+
+ // Disable all children as well.
+ Children.OfType()
+ .ToList()
+ .ForEach( x => x.EnableDrawing = false );
+
+ AsyncRespawn();
+ }
+ }
+
+
+
+ [ConCmd.Server( "kill" )]
+ public static void DoSuicide()
+ {
+ if ( ConsoleSystem.Caller.Pawn is BasePlayer player ) {
+ player.TakeDamage( DamageInfo.Generic( 1000f ) );
+ }
+ }
+
+ [ConCmd.Admin( "sethp" )]
+ public static void SetHP( float value )
+ {
+ if ( ConsoleSystem.Caller.Pawn is BasePlayer player ) {
+ player.Health = value;
+ }
+ }
+
+
+}
diff --git a/code/Systems/Player/BasePlayer.Footstep.cs b/code/Systems/Player/BasePlayer.Footstep.cs
new file mode 100644
index 0000000..e1220f2
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Footstep.cs
@@ -0,0 +1,44 @@
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ ///
+ /// How long since the player last played a footstep sound.
+ ///
+ public TimeSince TimeSinceFootstep { get; protected set; } = 0;
+
+ ///
+ /// Called clientside every time we fire the footstep anim event.
+ ///
+ public override void OnAnimEventFootstep( Vector3 pos, int foot, float volume )
+ {
+ if ( !Game.IsClient )
+ return;
+
+ if ( LifeState != LifeState.Alive )
+ return;
+
+ if ( TimeSinceFootstep < 0.2f )
+ return;
+
+ volume *= GetFootstepVolume();
+
+ TimeSinceFootstep = 0;
+
+ var tr = Trace.Ray( pos, pos + GravityDirection * 20 )
+ .Radius( 1 )
+ .Ignore( this )
+ .Run();
+
+ if ( !tr.Hit ) return;
+
+ tr.Surface.DoFootstep( this, tr, foot, volume );
+ }
+
+ protected float GetFootstepVolume()
+ {
+ return Controller.Velocity.WithZ( 0 ).Length.LerpInverse( 0.0f, 200.0f ) * 1f;
+ }
+
+}
diff --git a/code/Systems/Player/BasePlayer.Input.cs b/code/Systems/Player/BasePlayer.Input.cs
new file mode 100644
index 0000000..aae5255
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Input.cs
@@ -0,0 +1,74 @@
+using System.ComponentModel;
+
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ ///
+ /// Should be Input.AnalogMove
+ ///
+ [ClientInput] public Vector2 MoveInput { get; protected set; }
+
+ ///
+ /// Normalized accumulation of Input.AnalogLook
+ ///
+ [ClientInput] public Angles LookInput { get; protected set; }
+
+ ///
+ /// ?
+ ///
+ [ClientInput] public Entity ActiveWeaponInput { get; set; }
+
+ ///
+ /// Position a player should be looking from in world space.
+ ///
+ [Browsable( false )]
+ public Vector3 EyePosition
+ {
+ get => Transform.PointToWorld( EyeLocalPosition );
+ set => EyeLocalPosition = Transform.PointToLocal( value );
+ }
+
+ ///
+ /// Position a player should be looking from in local to the entity coordinates.
+ ///
+ [Net, Predicted, Browsable( false )]
+ public Vector3 EyeLocalPosition { get; set; }
+
+ ///
+ /// Rotation of the entity's "eyes", i.e. rotation for the camera when this entity is used as the view entity.
+ ///
+ [Browsable( false )]
+ public Rotation EyeRotation
+ {
+ get => Transform.RotationToWorld( EyeLocalRotation );
+ set => EyeLocalRotation = Transform.RotationToLocal( value );
+ }
+
+ ///
+ /// Rotation of the entity's "eyes", i.e. rotation for the camera when this entity is used as the view entity. In local to the entity coordinates.
+ ///
+ [Net, Predicted, Browsable( false )]
+ public Rotation EyeLocalRotation { get; set; }
+
+ ///
+ /// Override the aim ray to use the player's eye position and rotation.
+ ///
+ public override Ray AimRay => new Ray( EyePosition, EyeRotation.Forward );
+
+ public override void BuildInput()
+ {
+ if ( Game.LocalClient.Components.Get() != null ) return;
+
+ Inventory?.BuildInput();
+
+ MoveInput = Input.AnalogMove;
+ var lookInput = (LookInput + Input.AnalogLook).Normal;
+
+ // Since we're a FPS game, let's clamp the player's pitch between -90, and 90.
+ LookInput = lookInput.WithPitch( lookInput.pitch.Clamp( -90f, 90f ) );
+ }
+
+
+}
diff --git a/code/Player.Ragdoll.cs b/code/Systems/Player/BasePlayer.Ragdoll.cs
similarity index 80%
rename from code/Player.Ragdoll.cs
rename to code/Systems/Player/BasePlayer.Ragdoll.cs
index 24639d2..3385800 100644
--- a/code/Player.Ragdoll.cs
+++ b/code/Systems/Player/BasePlayer.Ragdoll.cs
@@ -1,9 +1,10 @@
-using Sandbox;
+namespace Sandbox.Systems.Player;
-partial class SandboxPlayer
+public partial class BasePlayer
{
+
[ClientRpc]
- private void BecomeRagdollOnClient( Vector3 velocity, Vector3 forcePos, Vector3 force, int bone, bool impulse, bool blast )
+ private void CreateRagdoll( Vector3 velocity, Vector3 forcePos, Vector3 force, int bone, bool bullet, bool blast )
{
var ent = new ModelEntity();
ent.Tags.Add( "ragdoll", "solid", "debris" );
@@ -39,7 +40,7 @@ private void BecomeRagdollOnClient( Vector3 velocity, Vector3 forcePos, Vector3
clothing.CopyMaterialGroup( e );
}
- if ( impulse )
+ if ( bullet )
{
PhysicsBody body = bone > 0 ? ent.GetBonePhysicsBody( bone ) : null;
@@ -57,17 +58,13 @@ private void BecomeRagdollOnClient( Vector3 velocity, Vector3 forcePos, Vector3
{
if ( ent.PhysicsGroup != null )
{
- ent.PhysicsGroup.AddVelocity( (Position - (forcePos + Vector3.Down * 100.0f)).Normal * (force.Length * 0.2f) );
+ ent.PhysicsGroup.AddVelocity( (Position - (forcePos + GravityDirection * 100.0f)).Normal * (force.Length * 0.2f) );
var angularDir = (Rotation.FromYaw( 90 ) * force.WithZ( 0 ).Normal).Normal;
ent.PhysicsGroup.AddAngularVelocity( angularDir * (force.Length * 0.02f) );
}
}
- Corpse = ent;
-
- if ( IsLocalPawn )
- Corpse.EnableDrawing = false;
-
ent.DeleteAsync( 10.0f );
}
+
}
diff --git a/code/Systems/Player/BasePlayer.Spawn.cs b/code/Systems/Player/BasePlayer.Spawn.cs
new file mode 100644
index 0000000..93faf3b
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Spawn.cs
@@ -0,0 +1,71 @@
+using Sandbox.Systems.Camera;
+using Sandbox.Systems.Inventory;
+using Sandbox.Systems.Player.Animations;
+
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ ///
+ /// The model your player will use.
+ ///
+ static Model PlayerModel = Model.Load( "models/citizen/citizen.vmdl" );
+
+ ///
+ /// When the player is first created. This isn't called when a player respawns.
+ ///
+ public override void Spawn()
+ {
+ Model = PlayerModel;
+ Predictable = true;
+
+ // Default properties
+ EnableDrawing = true;
+ EnableHideInFirstPerson = true;
+ EnableShadowInFirstPerson = true;
+ EnableLagCompensation = true;
+ EnableHitboxes = true;
+
+ Tags.Add( "player" );
+ }
+
+ ///
+ /// Called when a player respawns, think of this as a soft spawn - we're only reinitializing transient data here.
+ ///
+ public virtual void Respawn()
+ {
+ SetupPhysicsFromAABB( PhysicsMotionType.Keyframed, new Vector3( -16, -16, 0 ), new Vector3( 16, 16, 72 ) );
+
+ this.ClearWaterLevel();
+ Health = 100;
+ LifeState = LifeState.Alive;
+ EnableAllCollisions = true;
+ EnableDrawing = true;
+
+ // Re-enable all children.
+ Children.OfType()
+ .ToList()
+ .ForEach( x => x.EnableDrawing = true );
+
+ // We need a player controller to work with any kind of mechanics.
+ Components.Create();
+ Velocity = Vector3.Zero;
+
+ Components.Create();
+ Components.Create();
+ Components.Create();
+
+ SetupClothing();
+
+ GameManager.Current?.MoveToSpawnpoint( this );
+ ResetInterpolation();
+ }
+
+ private async void AsyncRespawn()
+ {
+ await GameTask.DelaySeconds( 3f );
+ Respawn();
+ }
+
+}
diff --git a/code/Systems/Player/BasePlayer.Trace.cs b/code/Systems/Player/BasePlayer.Trace.cs
new file mode 100644
index 0000000..18430da
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.Trace.cs
@@ -0,0 +1,19 @@
+using System.ComponentModel;
+
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer
+{
+
+ public TraceResult TraceRay( float maxDistance ) {
+ var startPos = EyePosition;
+ var dir = EyeRotation.Forward;
+
+ var tr = Trace.Ray( startPos, startPos + (dir * maxDistance) )
+ .WithAnyTags( "solid", "nocollide" )
+ .Ignore( Owner )
+ .Run();
+ }
+
+
+}
diff --git a/code/Systems/Player/BasePlayer.cs b/code/Systems/Player/BasePlayer.cs
new file mode 100644
index 0000000..1a8b47e
--- /dev/null
+++ b/code/Systems/Player/BasePlayer.cs
@@ -0,0 +1,98 @@
+using Sandbox.Systems.Camera;
+using Sandbox.Systems.Inventory;
+using Sandbox.Systems.Player.Animations;
+
+namespace Sandbox.Systems.Player;
+
+public partial class BasePlayer: AnimatedEntity
+{
+ /**
+ * System controllers
+ */
+
+ ///
+ /// The controller is responsible for player movement and setting up EyePosition / EyeRotation.
+ ///
+ [BindComponent] public Controller.BasePlayerController Controller { get; }
+
+ ///
+ /// The animator is responsible for animating the player's current model.
+ ///
+ [BindComponent] public BasePlayerAnimator Animator { get; }
+
+ ///
+ /// The player's camera.
+ ///
+ [BindComponent] public IPlayerCamera Camera { get; }
+
+ /**
+ * Inventory
+ */
+
+ ///
+ /// The inventory is responsible for storing cariable items for a player to use.
+ ///
+ [BindComponent] public BasePlayerInventory Inventory { get; }
+
+ ///
+ /// Accessor for getting a player's active cariable item.
+ ///
+ public ICariable ActiveCariable => Inventory?.ActiveCariable;
+
+ /*
+ * Gravity
+ */
+
+ ///
+ /// Active gravitational force
+ ///
+ [Net] public Vector3 Gravity { get; set; } = Game.PhysicsWorld.Gravity;
+ ///
+ /// Active gravitational force direction
+ ///
+ [Net] public Vector3 GravityDirection { get; set; } = Game.PhysicsWorld.Gravity.Normal;
+
+ /**
+ * Inputs
+ */
+ [ClientInput] public ICariable ActiveCariableInput { get; set; }
+
+
+ ///
+ /// Called every server and client tick.
+ ///
+ ///
+ public override void Simulate( IClient cl )
+ {
+ Controller?.Simulate( cl );
+ Animator?.Simulate( cl );
+ Inventory?.Simulate( cl );
+ }
+
+ ///
+ /// Called every frame clientside.
+ ///
+ ///
+ public override void FrameSimulate( IClient cl )
+ {
+ if ( Input.Pressed( InputButton.View ) ) {
+ var isFirstPerson = Camera is FirstPersonCamera;
+ Camera.Remove();
+ if ( isFirstPerson) {
+ Components.Create();
+ }else {
+ Components.Create();
+ }
+ }
+
+ Controller?.FrameSimulate( cl );
+ Camera?.Update( );
+ }
+
+
+
+
+
+
+
+}
diff --git a/code/Systems/Player/Controller/BasePlayerController.cs b/code/Systems/Player/Controller/BasePlayerController.cs
new file mode 100644
index 0000000..2dcee00
--- /dev/null
+++ b/code/Systems/Player/Controller/BasePlayerController.cs
@@ -0,0 +1,234 @@
+namespace Sandbox.Systems.Player.Controller;
+
+public class BasePlayerController: EntityComponent, ISingletonComponent {
+
+ public BasePlayer Player => Entity;
+
+ // Pawn Controller
+ internal HashSet Events;
+ internal HashSet Tags;
+
+
+ public IClient Client { get; protected set; }
+ public Vector3 Position { get; set; }
+ public Rotation Rotation { get; set; }
+ public Vector3 Velocity { get; set; }
+ public Rotation EyeRotation { get; set; }
+ public Vector3 EyeLocalPosition { get; set; }
+ public Vector3 BaseVelocity { get; set; }
+ public Entity GroundEntity { get; set; }
+ public Vector3 GroundNormal { get; set; }
+
+ public Vector3 WishVelocity { get; set; }
+
+ //BasePlayerController (game base)
+
+ [ConVar.Replicated( "debug_playercontroller" )]
+ public static bool Debug { get; set; } = false;
+
+ ///
+ /// Any bbox traces we do will be offset by this amount.
+ /// todo: this needs to be predicted
+ ///
+ public Vector3 TraceOffset;
+
+ // SPACEBUILD ADDED
+
+ [Net, Predicted] public float CurrentEyeHeight { get; set; } = 64f;
+
+ public Rotation LastInputRotation = Rotation.Identity;
+
+
+ public void FromPlayer( )
+ {
+ Position = Player.Position;
+ Rotation = Player.Rotation;
+ Velocity = Player.Velocity;
+
+ EyeRotation = Player.EyeRotation;
+ EyeLocalPosition = Player.EyeLocalPosition;
+
+ BaseVelocity = Player.BaseVelocity;
+ GroundEntity = Player.GroundEntity;
+ WishVelocity = Player.Velocity;
+ }
+
+ public void ToPlayer( )
+ {
+ Player.Position = Position;
+ Player.Velocity = Velocity;
+ Player.Rotation = Rotation;
+ Player.GroundEntity = GroundEntity;
+ Player.BaseVelocity = BaseVelocity;
+
+ Player.EyeLocalPosition = EyeLocalPosition;
+ Player.EyeRotation = EyeRotation;
+ }
+
+ ///
+ /// This is what your logic should be going in
+ ///
+ public virtual void Simulate()
+ {
+ // Nothing
+ }
+
+ ///
+ /// This is called every frame on the client only
+ ///
+ public virtual void FrameSimulate()
+ {
+ Game.AssertClient();
+ }
+
+ ///
+ /// Call OnEvent for each event
+ ///
+ public virtual void RunEvents( BasePlayerController additionalController )
+ {
+ if ( Events == null ) return;
+
+ foreach ( var e in Events )
+ {
+ OnEvent( e );
+ additionalController?.OnEvent( e );
+ }
+ }
+
+ ///
+ /// An event has been triggered - maybe handle it
+ ///
+ public virtual void OnEvent( string name )
+ {
+
+ }
+
+ ///
+ /// Returns true if we have this event
+ ///
+ public bool HasEvent( string eventName )
+ {
+ if ( Events == null ) return false;
+ return Events.Contains( eventName );
+ }
+
+ ///
+ ///
+ public bool HasTag( string tagName )
+ {
+ if ( Tags == null ) return false;
+ return Tags.Contains( tagName );
+ }
+
+ ///
+ /// Allows the controller to pass events to other systems
+ /// while staying abstracted.
+ /// For example, it could pass a "jump" event, which could then
+ /// be picked up by the playeranimator to trigger a jump animation,
+ /// and picked up by the player to play a jump sound.
+ ///
+ public void AddEvent( string eventName )
+ {
+ // TODO - shall we allow passing data with the event?
+
+ if ( Events == null ) Events = new HashSet();
+
+ if ( Events.Contains( eventName ) )
+ return;
+
+ Events.Add( eventName );
+ }
+
+ ///
+ ///
+ public void SetTag( string tagName )
+ {
+ // TODO - shall we allow passing data with the event?
+
+ Tags ??= new HashSet();
+
+ if ( Tags.Contains( tagName ) )
+ return;
+
+ Tags.Add( tagName );
+ }
+
+ ///
+ /// Allow the controller to tweak input. Empty by default.
+ ///
+ public virtual void BuildInput()
+ {
+
+ }
+
+ public void Simulate( IClient client )
+ {
+ Events?.Clear();
+ Tags?.Clear();
+
+ Client = client;
+
+ FromPlayer();
+
+ Simulate();
+
+ ToPlayer();
+ }
+
+ public void FrameSimulate( IClient client )
+ {
+ Client = client;
+
+ FromPlayer();
+
+ FrameSimulate();
+
+ ToPlayer();
+
+ EyeRotation = Player.LookInput.ToRotation();
+ }
+
+ ///
+ /// Traces the bbox and returns the trace result.
+ /// LiftFeet will move the start position up by this amount, while keeping the top of the bbox at the same
+ /// position. This is good when tracing down because you won't be tracing through the ceiling above.
+ ///
+ public virtual TraceResult TraceBBox( Vector3 start, Vector3 end, Vector3 mins, Vector3 maxs, float liftFeet = 0.0f )
+ {
+ if ( liftFeet > 0 )
+ {
+ start += Vector3.Up * liftFeet;
+ maxs = maxs.WithZ( maxs.z - liftFeet );
+ }
+
+ var tr = Trace.Ray( start + TraceOffset, end + TraceOffset )
+ .Size( mins, maxs )
+ .WithAnyTags( "solid", "playerclip", "passbullets", "player" )
+ .Ignore( Player )
+ .Run();
+
+ tr.EndPosition -= TraceOffset;
+ return tr;
+ }
+
+ ///
+ /// This calls TraceBBox with the right sized bbox. You should derive this in your controller if you
+ /// want to use the built in functions
+ ///
+ public virtual TraceResult TraceBBox( Vector3 start, Vector3 end, float liftFeet = 0.0f )
+ {
+ return TraceBBox( start, end, Vector3.One * -1, Vector3.One, liftFeet );
+ }
+
+ ///
+ /// This is temporary, get the hull size for the player's collision
+ ///
+ public virtual BBox GetHull()
+ {
+ return new BBox( -10, 10 );
+ }
+
+
+
+
+}
diff --git a/code/IStopUsing.cs b/code/Systems/Player/IStopUsing.cs
similarity index 100%
rename from code/IStopUsing.cs
rename to code/Systems/Player/IStopUsing.cs
diff --git a/code/VertexMeshBuilder.cs b/code/VertexMeshBuilder.cs
index bf54bfd..096092b 100644
--- a/code/VertexMeshBuilder.cs
+++ b/code/VertexMeshBuilder.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
+using Sandbox.Systems.Player;
namespace Sandbox
{
diff --git a/code/entities/Entity.ownership.cs b/code/entities/Entity.ownership.cs
index 4ba2a3f..ea4a5ae 100644
--- a/code/entities/Entity.ownership.cs
+++ b/code/entities/Entity.ownership.cs
@@ -1,15 +1,14 @@
-using System.Collections.Generic;
-using Sandbox;
+using Sandbox.Systems.Player;
public static class EntityOwnershipExtensions
{
- public static Dictionary PlayerOwners { get; set; } = new();
- public static Player GetPlayerOwner( this Entity ent )
+ public static Dictionary PlayerOwners { get; set; } = new();
+ public static BasePlayer GetPlayerOwner( this Entity ent )
{
Game.AssertServer();
return PlayerOwners.GetValueOrDefault( ent );
}
- public static void SetPlayerOwner( this Entity ent, Player player )
+ public static void SetPlayerOwner( this Entity ent, BasePlayer player )
{
Game.AssertServer();
PlayerOwners[ent] = player;
diff --git a/code/entities/wirebox/Player.wire.cs b/code/entities/wirebox/Player.wire.cs
index 6bd8de9..ad48a95 100644
--- a/code/entities/wirebox/Player.wire.cs
+++ b/code/entities/wirebox/Player.wire.cs
@@ -6,6 +6,7 @@ partial class SandboxPlayer
[GameEvent.Client.Frame]
public void OnFrame()
{
+ //TODO user player.traceRay(200)
var startPos = EyePosition;
var dir = EyeRotation.Forward;
diff --git a/code/tools/Constraint.cs b/code/tools/Constraint.cs
index 6cc98a0..b139991 100644
--- a/code/tools/Constraint.cs
+++ b/code/tools/Constraint.cs
@@ -4,6 +4,7 @@
using Sandbox.UI;
using Sandbox.UI.Construct;
using Sandbox.Physics;
+using Sandbox.Systems.Player;
namespace Sandbox.Tools
{
@@ -66,7 +67,7 @@ private enum ConstraintToolStage {
private const float RotateSpeed = 30.0f;
// Dynamic entrypoint for optional Wirebox support, if installed
- public static Action> CreateWireboxConstraintController;
+ public static Action> CreateWireboxConstraintController;
private static bool WireboxSupport
{
get => CreateWireboxConstraintController != null;
diff --git a/code/tools/Duplicator.cs b/code/tools/Duplicator.cs
index 316f496..6dfe169 100644
--- a/code/tools/Duplicator.cs
+++ b/code/tools/Duplicator.cs
@@ -7,6 +7,7 @@
using System.Linq;
using System.Text;
using System.Text.Json;
+using Sandbox.Systems.Player;
namespace Sandbox
{
@@ -469,7 +470,7 @@ public void Spawn( Dictionary spawnedEnts )
joint.Collisions = collisions;
joint.EnableAngularConstraint = enableAngularConstraint;
joint.EnableLinearConstraint = enableLinearConstraint;
- Event.Run( "joint.spawned", joint, (Player)null );
+ Event.Run( "joint.spawned", joint, (BasePlayer)null );
}
}
@@ -508,14 +509,14 @@ public List getGhosts()
public class DuplicatorPasteJob
{
- Player owner;
+ BasePlayer owner;
DuplicatorData data;
Transform origin;
Stopwatch timeUsed = new Stopwatch();
Stopwatch timeElapsed = new Stopwatch();
Dictionary entList = new Dictionary();
Dictionary entData = new();
- public DuplicatorPasteJob( Player owner_, DuplicatorData data_, Transform origin_ )
+ public DuplicatorPasteJob( BasePlayer owner_, DuplicatorData data_, Transform origin_ )
{
owner = owner_;
data = data_;
@@ -644,7 +645,7 @@ public partial class DuplicatorTool : BaseTool
[ConVar.ClientData( "tool_duplicator_area_size", Help = "Area copy size", Saved = true )]
public static float AreaSize { get; set; } = 250;
- public static Dictionary Pasting = new();
+ public static Dictionary Pasting = new();
void GetAttachedEntities( Entity baseEnt, List ents, List joints )
{
@@ -736,9 +737,9 @@ static List GetJoints( List ents )
static DuplicatorTool getTool( IEntity player )
{
if ( player == null ) return null;
- var inventory = (player as Player).Inventory;
+ var inventory = (player as BasePlayer).Inventory;
if ( inventory == null ) return null;
- if ( inventory.Active is not Tool tool ) return null;
+ if ( inventory.ActiveCariable is not Tool tool ) return null;
if ( tool == null ) return null;
if ( tool.CurrentTool is not DuplicatorTool dupe ) return null;
return dupe;
diff --git a/code/tools/GravGun.cs b/code/tools/GravGun.cs
index ef028cb..c05a1cd 100644
--- a/code/tools/GravGun.cs
+++ b/code/tools/GravGun.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Sandbox.Systems.Player;
[Spawnable]
[Library( "gravgun" )]
@@ -178,7 +179,7 @@ public void SetViewModelParam( string param, bool value = true )
public override void Simulate( IClient client )
{
- if ( Owner is not Player owner ) return;
+ if ( Owner is not BasePlayer owner ) return;
SetAnimParameter( "prongs", ProngsState );
ViewModelEntity?.SetAnimParameter( "prongs", ProngsState );
@@ -529,10 +530,10 @@ public override bool IsUsable( Entity user )
return Owner == null || HeldBody.IsValid();
}
- public override void OnCarryDrop( Entity dropper )
+ public override void OnDrop( Entity dropper )
{
GrabEnd();
- base.OnCarryDrop( dropper );
+ base.OnDrop( dropper );
}
}
diff --git a/code/tools/NoCollide.cs b/code/tools/NoCollide.cs
index 58bdd9c..331772d 100644
--- a/code/tools/NoCollide.cs
+++ b/code/tools/NoCollide.cs
@@ -1,4 +1,5 @@
using System;
+using Sandbox.Systems.Player;
namespace Sandbox.Tools
{
@@ -22,7 +23,7 @@ public override void Simulate()
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
- if ( tr.Entity is Player )
+ if ( tr.Entity is BasePlayer )
return;
if ( tr.Entity is not ModelEntity modelEnt )
@@ -40,7 +41,7 @@ public override void Simulate()
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
- if ( tr.Entity is Player )
+ if ( tr.Entity is BasePlayer )
return;
if ( tr.Entity is not ModelEntity modelEnt )
diff --git a/code/tools/PhysGun.cs b/code/tools/PhysGun.cs
index f677e4e..2de6091 100644
--- a/code/tools/PhysGun.cs
+++ b/code/tools/PhysGun.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Sandbox.Systems.Player;
[Spawnable]
[Library( "physgun" )]
@@ -278,7 +279,7 @@ private void TryUnfreezeAll( Vector3 eyePos, Rotation eyeRot, Vector3 eyeDir )
.Ignore( this )
.OnTraceEvent( Owner ) // SandboxPlus addition for Stargate support
.Run();
- tr = CanToolParams.RunCanTool( Owner as Player, ClassName, tr );
+ tr = CanToolParams.RunCanTool( Owner as BasePlayer, ClassName, tr );
if ( !tr.Hit || !tr.Entity.IsValid() || tr.Entity.IsWorld ) return;
if ( tr.Entity.Tags.Has( PhysgunBlockTag ) ) return;
@@ -320,7 +321,7 @@ private void TryStartGrab( Vector3 eyePos, Rotation eyeRot, Vector3 eyeDir )
.Ignore( this )
.OnTraceEvent( Owner ) // SandboxPlus addition for Stargate support
.Run();
- tr = CanToolParams.RunCanTool( Owner as Player, ClassName, tr );
+ tr = CanToolParams.RunCanTool( Owner as BasePlayer, ClassName, tr );
if ( !tr.Hit || !tr.Entity.IsValid() || tr.Entity.IsWorld || tr.StartedSolid ) return;
if ( tr.Entity.Tags.Has( PhysgunBlockTag ) ) return;
@@ -600,7 +601,7 @@ public void StopBeamSound()
BeamSoundPlaying = false;
}
- public override void OnCarryDrop( Entity dropper )
+ public override void OnDrop( Entity dropper )
{
if ( Input.Pressed( "drop" ) && Input.Down( "run" ) )
{
@@ -610,6 +611,6 @@ public override void OnCarryDrop( Entity dropper )
StopBeamSound( To.Single( dropper ) );
- base.OnCarryDrop( dropper );
+ base.OnDrop( dropper );
}
}
diff --git a/code/tools/Remover.cs b/code/tools/Remover.cs
index 775d585..c869f21 100644
--- a/code/tools/Remover.cs
+++ b/code/tools/Remover.cs
@@ -1,4 +1,6 @@
-namespace Sandbox.Tools
+using Sandbox.Systems.Player;
+
+namespace Sandbox.Tools
{
[Library( "tool_remover", Title = "Remover", Description = "Remove entities", Group = "construction" )]
public partial class RemoverTool : BaseTool
@@ -18,7 +20,7 @@ public override void Simulate()
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
- if ( tr.Entity is Player )
+ if ( tr.Entity is BasePlayer )
return;
CreateHitEffects( tr.EndPosition, tr.Normal );
diff --git a/code/ui/InventoryBar.cs b/code/ui/InventoryBar.cs
index d5b02b6..951fd82 100644
--- a/code/ui/InventoryBar.cs
+++ b/code/ui/InventoryBar.cs
@@ -1,6 +1,7 @@
using Sandbox;
using Sandbox.UI;
using System.Collections.Generic;
+using Sandbox.Systems.Player;
public class InventoryBar : Panel
{
@@ -49,7 +50,7 @@ private static void UpdateIcon( Entity ent, InventoryIcon inventoryIcon, int i )
[Event.Client.BuildInput]
public void ProcessClientInput()
{
- var player = Game.LocalPawn as SandboxPlayer;
+ var player = Game.LocalPawn as BasePlayer;
if ( player == null )
return;
@@ -57,7 +58,7 @@ public void ProcessClientInput()
if ( inventory == null )
return;
- if ( player.ActiveChild is PhysGun physgun && physgun.BeamActive )
+ if ( player.ActiveCariable is PhysGun physgun && physgun.BeamActive )
{
return;
}
@@ -72,7 +73,7 @@ public void ProcessClientInput()
if ( Input.Pressed( "slot8" ) ) SetActiveSlot( inventory, 7 );
if ( Input.Pressed( "slot9" ) ) SetActiveSlot( inventory, 8 );
- if ( !player.SuppressScrollWheelInventory && Input.MouseWheel != 0 )
+ if ( Input.MouseWheel != 0 )
{
SwitchActiveSlot( inventory, -Input.MouseWheel );
}
diff --git a/code/weapons/Fists.cs b/code/weapons/Fists.cs
index cfa0236..3533fc8 100644
--- a/code/weapons/Fists.cs
+++ b/code/weapons/Fists.cs
@@ -8,6 +8,13 @@ partial class Fists : Weapon
public override float PrimaryRate => 2.0f;
public override float SecondaryRate => 2.0f;
+ public Fists()
+ {
+ HoldType = CariableHoldTypes.Punch;
+ Handedness = CariableHandedness.Both;
+ AimBodyWeight = 1.0f;
+ }
+
public override bool CanReload()
{
return false;
@@ -37,15 +44,8 @@ public override void AttackSecondary()
Attack( false );
}
- public override void OnCarryDrop( Entity dropper )
- {
- }
-
- public override void SimulateAnimator( CitizenAnimationHelper anim )
+ public override void OnDrop( Entity dropper )
{
- anim.HoldType = CitizenAnimationHelper.HoldTypes.Punch;
- anim.Handedness = CitizenAnimationHelper.Hand.Both;
- anim.AimBodyWeight = 1.0f;
}
public override void CreateViewModel()
diff --git a/code/weapons/Flashlight.cs b/code/weapons/Flashlight.cs
index a3dd513..d65e686 100644
--- a/code/weapons/Flashlight.cs
+++ b/code/weapons/Flashlight.cs
@@ -17,6 +17,13 @@ partial class Flashlight : Weapon
TimeSince timeSinceLightToggled;
+ public Flashlight()
+ {
+ HoldType = CariableHoldTypes.Pistol;
+ Handedness = CariableHandedness.Right;
+ AimBodyWeight = 1.0f;
+ }
+
public override void Spawn()
{
base.Spawn();
@@ -200,11 +207,5 @@ public override void ActiveEnd( Entity ent, bool dropped )
}
}
}
-
- public override void SimulateAnimator( CitizenAnimationHelper anim )
- {
- anim.HoldType = CitizenAnimationHelper.HoldTypes.Pistol;
- anim.Handedness = CitizenAnimationHelper.Hand.Right;
- anim.AimBodyWeight = 1.0f;
- }
+
}
diff --git a/code/weapons/MP5.cs b/code/weapons/MP5.cs
index de9abb8..9753cea 100644
--- a/code/weapons/MP5.cs
+++ b/code/weapons/MP5.cs
@@ -9,6 +9,13 @@ partial class MP5 : Weapon
public override float ReloadTime => 2.5f;
private ParticleSystem EjectBrass;
+ public MP5()
+ {
+ HoldType = CariableHoldTypes.Rifle;
+ Handedness = CariableHandedness.Both;
+ AimBodyWeight = 1.0f;
+ }
+
public override void Spawn()
{
base.Spawn();
@@ -77,10 +84,4 @@ protected override void ShootEffects()
}
}
- public override void SimulateAnimator( CitizenAnimationHelper anim )
- {
- anim.HoldType = CitizenAnimationHelper.HoldTypes.Rifle;
- anim.Handedness = CitizenAnimationHelper.Hand.Both;
- anim.AimBodyWeight = 1.0f;
- }
}
diff --git a/code/weapons/Pistol.cs b/code/weapons/Pistol.cs
index b7ccb5a..81cb74e 100644
--- a/code/weapons/Pistol.cs
+++ b/code/weapons/Pistol.cs
@@ -1,6 +1,4 @@
-using Sandbox;
-
-[Spawnable]
+[Spawnable]
[Library( "weapon_pistol", Title = "Pistol" )]
partial class Pistol : Weapon
{
diff --git a/code/weapons/RPG.cs b/code/weapons/RPG.cs
index 896e8c5..17abff5 100644
--- a/code/weapons/RPG.cs
+++ b/code/weapons/RPG.cs
@@ -10,6 +10,13 @@ partial class RPG : Weapon
public override float SecondaryRate => 1.0f;
public override float ReloadTime => 5.0f;
+ public RPG()
+ {
+ HoldType = CariableHoldTypes.RPG;
+ Handedness = CariableHandedness.Both;
+ AimBodyWeight = 1.0f;
+ }
+
public override void Spawn()
{
base.Spawn();
@@ -51,11 +58,5 @@ protected override void ShootEffects()
ViewModelEntity?.SetAnimParameter( "fire", true );
}
-
- public override void SimulateAnimator( CitizenAnimationHelper anim )
- {
- anim.HoldType = CitizenAnimationHelper.HoldTypes.RPG;
- anim.Handedness = CitizenAnimationHelper.Hand.Both;
- anim.AimBodyWeight = 1.0f;
- }
+
}
diff --git a/code/weapons/Shotgun.cs b/code/weapons/Shotgun.cs
index 0087f0e..b83986f 100644
--- a/code/weapons/Shotgun.cs
+++ b/code/weapons/Shotgun.cs
@@ -9,6 +9,13 @@ partial class Shotgun : Weapon
public override float SecondaryRate => 1;
public override float ReloadTime => 0.5f;
+ public Shotgun()
+ {
+ HoldType = CariableHoldTypes.Shotgun;
+ Handedness = CariableHandedness.Both;
+ AimBodyWeight = 1.0f;
+ }
+
public override void Spawn()
{
base.Spawn();
@@ -90,11 +97,5 @@ protected virtual void FinishReload()
{
ViewModelEntity?.SetAnimParameter( "reload_finished", true );
}
-
- public override void SimulateAnimator( CitizenAnimationHelper anim )
- {
- anim.HoldType = CitizenAnimationHelper.HoldTypes.Shotgun;
- anim.Handedness = CitizenAnimationHelper.Hand.Both;
- anim.AimBodyWeight = 1.0f;
- }
+
}
diff --git a/sandboxplus.sln b/sandboxplus.sln
new file mode 100644
index 0000000..a69eb23
--- /dev/null
+++ b/sandboxplus.sln
@@ -0,0 +1,77 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29926.136
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spacelib", "d:\github\sbox-libs\spacelib\code\spacelib.csproj", "{3ECBB63C-DFA0-4F55-9917-CA90FBB4A4B1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sandboxplus", "code\sandboxplus.csproj", "{57C3535E-4174-EB44-3EBC-642D1A07F5E7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Base Library", "E:\SteamLibrary\steamapps\common\sbox\addons\base\code\Base Library.csproj", "{3CFCECAD-98E9-581B-90CF-75E5F2B140CA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Menu Addon", "E:\SteamLibrary\steamapps\common\sbox\addons\menu\code\Menu Addon.csproj", "{F7B302F1-3AFB-3BF7-9D5A-B3787E45E792}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Base Editor Library", "E:\SteamLibrary\steamapps\common\sbox\addons\tools\\code\Base Editor Library.csproj", "{0DDD0665-950C-B4D6-56B9-A5A4C73C7423}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entity Prefab Editor", "E:\SteamLibrary\steamapps\common\sbox\editor\EntityPrefabEditor\Entity Prefab Editor.csproj", "{74E6592C-05FA-C388-F145-5E3568E733DE}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shader Graph", "E:\SteamLibrary\steamapps\common\sbox\editor\ShaderGraph\Shader Graph.csproj", "{3696C4FE-B5DF-4E46-283A-A0E0E0468C82}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{7E84B5F6-56F9-A3B2-F215-7A71616B85A2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{91C47821-78DC-6EB2-D97E-14E2D5EF1C9F}"
+EndProject
+
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3ECBB63C-DFA0-4F55-9917-CA90FBB4A4B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3ECBB63C-DFA0-4F55-9917-CA90FBB4A4B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3ECBB63C-DFA0-4F55-9917-CA90FBB4A4B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3ECBB63C-DFA0-4F55-9917-CA90FBB4A4B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {57C3535E-4174-EB44-3EBC-642D1A07F5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {57C3535E-4174-EB44-3EBC-642D1A07F5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {57C3535E-4174-EB44-3EBC-642D1A07F5E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {57C3535E-4174-EB44-3EBC-642D1A07F5E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3CFCECAD-98E9-581B-90CF-75E5F2B140CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3CFCECAD-98E9-581B-90CF-75E5F2B140CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3CFCECAD-98E9-581B-90CF-75E5F2B140CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3CFCECAD-98E9-581B-90CF-75E5F2B140CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7B302F1-3AFB-3BF7-9D5A-B3787E45E792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7B302F1-3AFB-3BF7-9D5A-B3787E45E792}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7B302F1-3AFB-3BF7-9D5A-B3787E45E792}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B302F1-3AFB-3BF7-9D5A-B3787E45E792}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0DDD0665-950C-B4D6-56B9-A5A4C73C7423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0DDD0665-950C-B4D6-56B9-A5A4C73C7423}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0DDD0665-950C-B4D6-56B9-A5A4C73C7423}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0DDD0665-950C-B4D6-56B9-A5A4C73C7423}.Release|Any CPU.Build.0 = Release|Any CPU
+ {74E6592C-05FA-C388-F145-5E3568E733DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {74E6592C-05FA-C388-F145-5E3568E733DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {74E6592C-05FA-C388-F145-5E3568E733DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {74E6592C-05FA-C388-F145-5E3568E733DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3696C4FE-B5DF-4E46-283A-A0E0E0468C82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3696C4FE-B5DF-4E46-283A-A0E0E0468C82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3696C4FE-B5DF-4E46-283A-A0E0E0468C82}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3696C4FE-B5DF-4E46-283A-A0E0E0468C82}.Release|Any CPU.Build.0 = Release|Any CPU
+
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {3ECBB63C-DFA0-4F55-9917-CA90FBB4A4B1} = {7E84B5F6-56F9-A3B2-F215-7A71616B85A2}
+ {3CFCECAD-98E9-581B-90CF-75E5F2B140CA} = {7E84B5F6-56F9-A3B2-F215-7A71616B85A2}
+ {F7B302F1-3AFB-3BF7-9D5A-B3787E45E792} = {7E84B5F6-56F9-A3B2-F215-7A71616B85A2}
+ {0DDD0665-950C-B4D6-56B9-A5A4C73C7423} = {91C47821-78DC-6EB2-D97E-14E2D5EF1C9F}
+ {74E6592C-05FA-C388-F145-5E3568E733DE} = {91C47821-78DC-6EB2-D97E-14E2D5EF1C9F}
+ {3696C4FE-B5DF-4E46-283A-A0E0E0468C82} = {91C47821-78DC-6EB2-D97E-14E2D5EF1C9F}
+
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FD1E2EC4-D69D-418D-8FBF-4CA3864E5C8C}
+ EndGlobalSection
+EndGlobal
+