From 367a1d1a53b59a3de096d53ee04534dff12e6e4c Mon Sep 17 00:00:00 2001 From: Stijn Date: Sat, 7 Oct 2023 09:36:49 +0200 Subject: [PATCH] chore: start of conversion to new Player code --- .editorconfig | 2 +- code/.idea/.idea.code.dir/.idea/.gitignore | 13 + code/Base/BaseCarriable.cs | 140 +++++++++++ code/Base/BaseWeapon.cs | 144 +++++++++++ code/{ => Base}/Carriable.cs | 18 ++ code/{ => Base/Tools}/PreviewEntity.cs | 0 code/{ => Base/Tools}/Tool.CanTool.cs | 5 +- code/{ => Base/Tools}/Tool.Effects.cs | 0 code/{ => Base/Tools}/Tool.Preview.cs | 0 code/{ => Base/Tools}/Tool.cs | 26 +- code/{ => Base}/ViewModel.cs | 0 code/{ => Base/Weapons}/Weapon.cs | 12 +- code/Game.cs | 6 +- code/Inventory.cs | 53 ---- code/{ => Load}/IAutoload.cs | 0 code/{ => Load}/ReloadManager.cs | 0 code/Player.cs | 71 +----- code/Player/SandboxPlayer.cs | 43 ++++ code/Systems/Camera/FirstPersonCamera.cs | 18 ++ code/Systems/Camera/IPlayerCamera.cs | 11 + code/Systems/Camera/ThirdPersonCamera.cs | 33 +++ code/Systems/Inventory/BasePlayerInventory.cs | 116 +++++++++ code/Systems/Inventory/CariableHandedness.cs | 11 + code/Systems/Inventory/CariableHoldTypes.cs | 13 + code/Systems/Inventory/ICariable.cs | 23 ++ .../Player/Animations/BasePlayerAnimator.cs | 34 +++ code/Systems/Player/BasePlayer.Audio.cs | 12 + code/Systems/Player/BasePlayer.Clothing.cs | 20 ++ code/Systems/Player/BasePlayer.Damage.cs | 101 ++++++++ code/Systems/Player/BasePlayer.Footstep.cs | 44 ++++ code/Systems/Player/BasePlayer.Input.cs | 74 ++++++ .../Player/BasePlayer.Ragdoll.cs} | 17 +- code/Systems/Player/BasePlayer.Spawn.cs | 71 ++++++ code/Systems/Player/BasePlayer.Trace.cs | 19 ++ code/Systems/Player/BasePlayer.cs | 98 ++++++++ .../Player/Controller/BasePlayerController.cs | 234 ++++++++++++++++++ code/{ => Systems/Player}/IStopUsing.cs | 0 code/VertexMeshBuilder.cs | 1 + code/entities/Entity.ownership.cs | 9 +- code/entities/wirebox/Player.wire.cs | 1 + code/tools/Constraint.cs | 3 +- code/tools/Duplicator.cs | 13 +- code/tools/GravGun.cs | 7 +- code/tools/NoCollide.cs | 5 +- code/tools/PhysGun.cs | 9 +- code/tools/Remover.cs | 6 +- code/ui/InventoryBar.cs | 7 +- code/weapons/Fists.cs | 16 +- code/weapons/Flashlight.cs | 15 +- code/weapons/MP5.cs | 13 +- code/weapons/Pistol.cs | 4 +- code/weapons/RPG.cs | 15 +- code/weapons/Shotgun.cs | 15 +- sandboxplus.sln | 77 ++++++ 54 files changed, 1474 insertions(+), 224 deletions(-) create mode 100644 code/.idea/.idea.code.dir/.idea/.gitignore create mode 100644 code/Base/BaseCarriable.cs create mode 100644 code/Base/BaseWeapon.cs rename code/{ => Base}/Carriable.cs (59%) rename code/{ => Base/Tools}/PreviewEntity.cs (100%) rename code/{ => Base/Tools}/Tool.CanTool.cs (85%) rename code/{ => Base/Tools}/Tool.Effects.cs (100%) rename code/{ => Base/Tools}/Tool.Preview.cs (100%) rename code/{ => Base/Tools}/Tool.cs (90%) rename code/{ => Base}/ViewModel.cs (100%) rename code/{ => Base/Weapons}/Weapon.cs (95%) delete mode 100644 code/Inventory.cs rename code/{ => Load}/IAutoload.cs (100%) rename code/{ => Load}/ReloadManager.cs (100%) create mode 100644 code/Player/SandboxPlayer.cs create mode 100644 code/Systems/Camera/FirstPersonCamera.cs create mode 100644 code/Systems/Camera/IPlayerCamera.cs create mode 100644 code/Systems/Camera/ThirdPersonCamera.cs create mode 100644 code/Systems/Inventory/BasePlayerInventory.cs create mode 100644 code/Systems/Inventory/CariableHandedness.cs create mode 100644 code/Systems/Inventory/CariableHoldTypes.cs create mode 100644 code/Systems/Inventory/ICariable.cs create mode 100644 code/Systems/Player/Animations/BasePlayerAnimator.cs create mode 100644 code/Systems/Player/BasePlayer.Audio.cs create mode 100644 code/Systems/Player/BasePlayer.Clothing.cs create mode 100644 code/Systems/Player/BasePlayer.Damage.cs create mode 100644 code/Systems/Player/BasePlayer.Footstep.cs create mode 100644 code/Systems/Player/BasePlayer.Input.cs rename code/{Player.Ragdoll.cs => Systems/Player/BasePlayer.Ragdoll.cs} (80%) create mode 100644 code/Systems/Player/BasePlayer.Spawn.cs create mode 100644 code/Systems/Player/BasePlayer.Trace.cs create mode 100644 code/Systems/Player/BasePlayer.cs create mode 100644 code/Systems/Player/Controller/BasePlayerController.cs rename code/{ => Systems/Player}/IStopUsing.cs (100%) create mode 100644 sandboxplus.sln 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 +