From f137095304f456b06229e4d17ee8249e974fceaf Mon Sep 17 00:00:00 2001 From: Leonardo Bishop Date: Fri, 28 Jul 2023 13:37:17 +0100 Subject: Add spectator mode --- code/Game.cs | 10 +++++++ code/pawn/Player.cs | 12 ++++++-- code/pawn/PlayerSpectator.cs | 48 +++++++++++++++++++++++++++++ code/ui/Hud.razor | 1 + code/ui/spectator/Spectator.razor | 63 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 code/pawn/PlayerSpectator.cs create mode 100644 code/ui/spectator/Spectator.razor diff --git a/code/Game.cs b/code/Game.cs index f235df0..24fd79d 100644 --- a/code/Game.cs +++ b/code/Game.cs @@ -1,5 +1,6 @@  using Sandbox; +using Sandbox.UI; using System; using System.Collections.Generic; using System.Linq; @@ -64,6 +65,15 @@ public partial class MurderGame : Sandbox.GameManager tx.Position = tx.Position + Vector3.Up * 50.0f; pawn.Transform = tx; } + + ChatBox.Say( client.Name + " joined the game" ); + } + + public override void ClientDisconnect( IClient client, NetworkDisconnectionReason reason ) + { + base.ClientDisconnect(client, reason ); + + ChatBox.Say( client.Name + " left the game (" + reason.ToString() + ")" ); } } diff --git a/code/pawn/Player.cs b/code/pawn/Player.cs index a1b126a..68d383d 100644 --- a/code/pawn/Player.cs +++ b/code/pawn/Player.cs @@ -47,6 +47,7 @@ public partial class Player : AnimatedEntity [BindComponent] public PlayerController Controller { get; } [BindComponent] public PlayerAnimator Animator { get; } [BindComponent] public PlayerInventory Inventory { get; } + [BindComponent] public PlayerSpectator Spectator { get; } public Ragdoll PlayerRagdoll { get; set; } public ClothingContainer PlayerClothingContainer { get; set; } @@ -74,6 +75,7 @@ public partial class Player : AnimatedEntity Tags.Add( "livingplayer" ); EnableAllCollisions = true; EnableDrawing = true; + Components.Remove( Spectator ); Components.Create(); Components.Create(); Components.Create(); @@ -85,6 +87,7 @@ public partial class Player : AnimatedEntity { DisablePlayer(); DeleteRagdoll(); + Components.RemoveAll(); } public void DeleteRagdoll() @@ -118,6 +121,7 @@ public partial class Player : AnimatedEntity ragdoll.PhysicsGroup.AddVelocity(LastAttackForce / 100); PlayerClothingContainer.DressEntity( ragdoll ); PlayerRagdoll = ragdoll; + Components.Create(); } public override void TakeDamage( DamageInfo info ) @@ -150,6 +154,7 @@ public partial class Player : AnimatedEntity Controller?.Simulate( cl ); Animator?.Simulate(); Inventory?.Simulate( cl ); + Spectator?.Simulate(); EyeLocalPosition = Vector3.Up * (64f * Scale); } @@ -174,10 +179,13 @@ public partial class Player : AnimatedEntity ViewAngles = viewAngles.Normal; } - bool IsThirdPerson { get; set; } = false; - public override void FrameSimulate( IClient cl ) { + if (Spectator != null) + { + Spectator.FrameSimulate(this); + return; + } SimulateRotation(); Camera.Rotation = ViewAngles.ToRotation(); diff --git a/code/pawn/PlayerSpectator.cs b/code/pawn/PlayerSpectator.cs new file mode 100644 index 0000000..c468de0 --- /dev/null +++ b/code/pawn/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/ui/Hud.razor b/code/ui/Hud.razor index 7ec500e..cacf26b 100644 --- a/code/ui/Hud.razor +++ b/code/ui/Hud.razor @@ -19,6 +19,7 @@ + @code diff --git a/code/ui/spectator/Spectator.razor b/code/ui/spectator/Spectator.razor new file mode 100644 index 0000000..f6fe8af --- /dev/null +++ b/code/ui/spectator/Spectator.razor @@ -0,0 +1,63 @@ +@using Sandbox; +@using Sandbox.UI; +@using System; + +@namespace MurderGame +@inherits Panel + + + +@if (Spectating) { +
+
Spectating @TargetName
+
+} + +@code +{ + public bool Spectating { get; set; } + public string TargetName { get; set; } + + protected override int BuildHash() + { + var localPawn = Game.LocalPawn; + if (localPawn is Player player) + { + var spectator = player.Spectator; + if (spectator != null) + { + var target = spectator.Target; + Spectating = true; + TargetName = (target != null && target.IsValid() && target.LifeState == LifeState.Alive) ? target.Client.Name : ""; + return HashCode.Combine(Spectating.GetHashCode(), TargetName.GetHashCode()); + } + } + if (Spectating) + { + Spectating = false; + } + return Spectating.GetHashCode(); + } +} -- cgit v1.2.3-70-g09d2