From 1e5c6393a6b29eb00dbb8fb137d86647cb0c356b Mon Sep 17 00:00:00 2001 From: Leonardo Bishop Date: Fri, 28 Jul 2023 22:06:03 +0100 Subject: Add TryUnstuck and death overlay --- code/pawn/component/PlayerAnimator.cs | 21 +++ code/pawn/component/PlayerInventory.cs | 126 ++++++++++++++++ code/pawn/component/PlayerSpectator.cs | 48 ++++++ code/pawn/component/movement/BaseController.cs | 9 ++ code/pawn/component/movement/PlayerController.cs | 182 +++++++++++++++++++++++ 5 files changed, 386 insertions(+) create mode 100644 code/pawn/component/PlayerAnimator.cs create mode 100644 code/pawn/component/PlayerInventory.cs create mode 100644 code/pawn/component/PlayerSpectator.cs create mode 100644 code/pawn/component/movement/BaseController.cs create mode 100644 code/pawn/component/movement/PlayerController.cs (limited to 'code/pawn/component') diff --git a/code/pawn/component/PlayerAnimator.cs b/code/pawn/component/PlayerAnimator.cs new file mode 100644 index 0000000..4cd4e3f --- /dev/null +++ b/code/pawn/component/PlayerAnimator.cs @@ -0,0 +1,21 @@ +using Sandbox; +using System; + +namespace MurderGame; + +public class PlayerAnimator : EntityComponent, ISingletonComponent +{ + public void Simulate() + { + var helper = new CitizenAnimationHelper( Entity ); + helper.WithVelocity( Entity.Velocity ); + helper.WithLookAt( Entity.EyePosition + Entity.EyeRotation.Forward * 100 ); + helper.HoldType = CitizenAnimationHelper.HoldTypes.None; + helper.IsGrounded = Entity.GroundEntity.IsValid(); + + if ( Entity.Controller.HasEvent( "jump" ) ) + { + helper.TriggerJump(); + } + } +} diff --git a/code/pawn/component/PlayerInventory.cs b/code/pawn/component/PlayerInventory.cs new file mode 100644 index 0000000..55fa3ed --- /dev/null +++ b/code/pawn/component/PlayerInventory.cs @@ -0,0 +1,126 @@ +using Sandbox; +using System; + +namespace MurderGame; + +public partial class PlayerInventory : EntityComponent +{ + const int MIN_SLOT = 1; + const int MAX_SLOT = 2; + + const int UNARMED_SLOT = 1; + const int PRIMARY_SLOT = 2; + + [Net] + public Weapon PrimaryWeapon { get; private set; } + + [Net] + public int ActiveSlot { get; set; } + + [Net] + public bool AllowPickup { get; set; } = true; + + public Weapon GetCurrentWeapon() + { + return ActiveSlot switch + { + PRIMARY_SLOT => PrimaryWeapon, + _ => null, + }; + } + + public void SetPrimaryWeapon( Weapon weapon ) + { + PrimaryWeapon?.OnHolster(); + PrimaryWeapon?.Delete(); + PrimaryWeapon = weapon; + if (weapon != null) + { + weapon.ChangeOwner( Entity ); + } + if (ActiveSlot == PRIMARY_SLOT) + { + weapon?.OnEquip( Entity ); + } + } + + private void PrevSlot() + { + if (ActiveSlot > MIN_SLOT) + { + --ActiveSlot; + } + else + { + ActiveSlot = MAX_SLOT; + } + } + private void NextSlot() + { + if (ActiveSlot < MAX_SLOT) + { + ++ActiveSlot; + } + else + { + ActiveSlot = MIN_SLOT; + } + } + + public void Simulate(IClient cl) + { + var currentWeapon = GetCurrentWeapon(); + var currentSlot = ActiveSlot; + + if (Input.Released("SlotPrev")) + { + PrevSlot(); + } + else if (Input.Released("SlotNext")) + { + NextSlot(); + } + else if (Input.Down("Slot1")) + { + ActiveSlot = 1; + } + else if (Input.Down("Slot2")) + { + ActiveSlot = 2; + } + + if (ActiveSlot != currentSlot) + { + currentWeapon?.OnHolster(); + GetCurrentWeapon()?.OnEquip( Entity ); + } + GetCurrentWeapon()?.Simulate( cl ); + } + + public void Holster() + { + Weapon weapon = GetCurrentWeapon(); + weapon?.OnHolster(); + } + + public void Clear() + { + Holster(); + SetPrimaryWeapon( null ); + } + + public void SpillContents(Vector3 location, Vector3 velocity) + { + Holster(); + if (PrimaryWeapon is not null and Pistol ) + { + PrimaryWeapon.ChangeOwner( null ); + DroppedWeapon droppedWeapon = new( (Pistol)PrimaryWeapon ); + droppedWeapon.CopyFrom( PrimaryWeapon ); + droppedWeapon.Position = location; + droppedWeapon.Velocity = velocity; + } + Clear(); + } + +} diff --git a/code/pawn/component/PlayerSpectator.cs b/code/pawn/component/PlayerSpectator.cs new file mode 100644 index 0000000..c468de0 --- /dev/null +++ b/code/pawn/component/PlayerSpectator.cs @@ -0,0 +1,48 @@ +using Sandbox; +using System.Collections.Generic; +using System.Linq; + +namespace MurderGame; + +public class PlayerSpectator : EntityComponent +{ + public Player Target { get; set; } + + public void Simulate() + { + if (Target == null || !Target.IsValid() || Target.LifeState == LifeState.Dead) + { + var targets = GetTargets(); + if ( targets.Count == 0 ) + { + Target = null; + return; + } + var nextTarget = targets.First(); + Target = (Player)nextTarget.Pawn; + } + } + + public void FrameSimulate( Player player ) + { + if ( Target == null || !Target.IsValid() || Target.LifeState == LifeState.Dead ) return; + + // SimulateRotation(player); + Camera.Rotation = Target.EyeRotation; + Camera.FieldOfView = Screen.CreateVerticalFieldOfView( Game.Preferences.FieldOfView ); + + Camera.FirstPersonViewer = Target; + Camera.Position = Target.EyePosition; + } + + protected void SimulateRotation(Player player) + { + player.EyeRotation = Target.ViewAngles.ToRotation(); + player.Rotation = Target.ViewAngles.WithPitch( 0f ).ToRotation(); + } + + public List GetTargets() + { + return Game.Clients.Where(c => c.Pawn is Player player && player.CurrentTeam != Team.Spectator && player.LifeState == LifeState.Alive).ToList(); + } +} diff --git a/code/pawn/component/movement/BaseController.cs b/code/pawn/component/movement/BaseController.cs new file mode 100644 index 0000000..0b92c75 --- /dev/null +++ b/code/pawn/component/movement/BaseController.cs @@ -0,0 +1,9 @@ +using Sandbox; + +namespace MurderGame; + +//TODO make spectatro a controller +public class BaseController : EntityComponent +{ + public Player Player { get; set; } +} diff --git a/code/pawn/component/movement/PlayerController.cs b/code/pawn/component/movement/PlayerController.cs new file mode 100644 index 0000000..5df9a33 --- /dev/null +++ b/code/pawn/component/movement/PlayerController.cs @@ -0,0 +1,182 @@ +using Sandbox; +using System; +using System.Collections.Generic; + +namespace MurderGame; + +public class PlayerController : EntityComponent +{ + public int StepSize => 24; + public int GroundAngle => 45; + public int JumpSpeed => 300; + public float Gravity => 800f; + public float SpeedMultiplier { get; set; } = 1; + + HashSet ControllerEvents = new( StringComparer.OrdinalIgnoreCase ); + + bool Grounded => Entity.GroundEntity.IsValid(); + + public void Simulate( Player player ) + { + ControllerEvents.Clear(); + + var movement = Entity.InputDirection.Normal; + var angles = Entity.ViewAngles.WithPitch( 0 ); + var moveVector = Rotation.From( angles ) * movement * 320f; + var groundEntity = CheckForGround(); + var team = Entity.CurrentTeam; + + if ( groundEntity.IsValid() ) + { + if ( !Grounded ) + { + Entity.Velocity = Entity.Velocity.WithZ( 0 ); + AddEvent( "grounded" ); + } + var sprintMultiplier = TeamOperations.CanSprint( team ) ? (Input.Down( "run" ) ? 2.5f : 1f) : 1f; + + Entity.Velocity = Accelerate( Entity.Velocity, moveVector.Normal, moveVector.Length, SpeedMultiplier * 200.0f * sprintMultiplier, 7.5f ); + Entity.Velocity = ApplyFriction( Entity.Velocity, 4.0f ); + } + else + { + Entity.Velocity = Accelerate( Entity.Velocity, moveVector.Normal, moveVector.Length, SpeedMultiplier * 100, 20f ); + Entity.Velocity += Vector3.Down * Gravity * Time.Delta * (1/SpeedMultiplier); + } + + if ( Input.Pressed( "jump" ) ) + { + DoJump(); + } + + var mh = new MoveHelper( Entity.Position, Entity.Velocity ); + mh.Trace = mh.Trace.Size( Entity.Hull ).Ignore( Entity ); + + if (mh.TryUnstuck()) + { + Entity.Position = mh.Position; + Entity.Velocity = mh.Velocity; + } + + if ( mh.TryMoveWithStep( Time.Delta, StepSize ) > 0 ) + { + if ( Grounded ) + { + mh.Position = StayOnGround( mh.Position ); + } + Entity.Position = mh.Position; + Entity.Velocity = mh.Velocity; + } + Entity.GroundEntity = groundEntity; + } + + void DoJump() + { + if ( Grounded ) + { + Entity.Velocity = ApplyJump( Entity.Velocity, "jump" ); + } + } + + Entity CheckForGround() + { + if ( Entity.Velocity.z > 100f ) + return null; + + var trace = Entity.TraceBBox( Entity.Position, Entity.Position + Vector3.Down, 2f ); + + if ( !trace.Hit ) + return null; + + if ( trace.Normal.Angle( Vector3.Up ) > GroundAngle ) + return null; + + return trace.Entity; + } + + Vector3 ApplyFriction( Vector3 input, float frictionAmount ) + { + float StopSpeed = 100.0f; + + var speed = input.Length; + if ( speed < 0.1f ) return input; + + // Bleed off some speed, but if we have less than the bleed + // threshold, bleed the threshold amount. + float control = (speed < StopSpeed) ? StopSpeed : speed; + + // Add the amount to the drop amount. + var drop = control * Time.Delta * frictionAmount; + + // scale the velocity + float newspeed = speed - drop; + if ( newspeed < 0 ) newspeed = 0; + if ( newspeed == speed ) return input; + + newspeed /= speed; + input *= newspeed; + + return input; + } + + Vector3 Accelerate( Vector3 input, Vector3 wishdir, float wishspeed, float speedLimit, float acceleration ) + { + if ( speedLimit > 0 && wishspeed > speedLimit ) + wishspeed = speedLimit; + + var currentspeed = input.Dot( wishdir ); + var addspeed = wishspeed - currentspeed; + + if ( addspeed <= 0 ) + return input; + + var accelspeed = acceleration * Time.Delta * wishspeed; + + if ( accelspeed > addspeed ) + accelspeed = addspeed; + + input += wishdir * accelspeed; + + return input; + } + + Vector3 ApplyJump( Vector3 input, string jumpType ) + { + AddEvent( jumpType ); + + return input + Vector3.Up * JumpSpeed; + } + + Vector3 StayOnGround( Vector3 position ) + { + var start = position + Vector3.Up * 2; + var end = position + Vector3.Down * StepSize; + + // See how far up we can go without getting stuck + var trace = Entity.TraceBBox( position, start ); + start = trace.EndPosition; + + // Now trace down from a known safe position + trace = Entity.TraceBBox( start, end ); + + if ( trace.Fraction <= 0 ) return position; + if ( trace.Fraction >= 1 ) return position; + if ( trace.StartedSolid ) return position; + if ( Vector3.GetAngle( Vector3.Up, trace.Normal ) > GroundAngle ) return position; + + return trace.EndPosition; + } + + public bool HasEvent( string eventName ) + { + return ControllerEvents.Contains( eventName ); + } + + void AddEvent( string eventName ) + { + if ( HasEvent( eventName ) ) + return; + + ControllerEvents.Add( eventName ); + } +} -- cgit v1.2.3-70-g09d2