aboutsummaryrefslogtreecommitdiffstats
path: root/code
diff options
context:
space:
mode:
Diffstat (limited to 'code')
-rw-r--r--code/Game.cs1
-rw-r--r--code/pawn/Player.Use.cs117
-rw-r--r--code/pawn/Player.cs62
-rw-r--r--code/pawn/component/PlayerAnimator.cs (renamed from code/pawn/PlayerAnimator.cs)0
-rw-r--r--code/pawn/component/PlayerInventory.cs (renamed from code/pawn/PlayerInventory.cs)0
-rw-r--r--code/pawn/component/PlayerSpectator.cs (renamed from code/pawn/PlayerSpectator.cs)0
-rw-r--r--code/pawn/component/movement/BaseController.cs9
-rw-r--r--code/pawn/component/movement/PlayerController.cs (renamed from code/pawn/PlayerController.cs)11
-rw-r--r--code/phase/AssignPhase.cs7
-rw-r--r--code/phase/PlayPhase.cs8
-rw-r--r--code/phase/WaitPhase.cs31
-rw-r--r--code/ui/Hud.razor1
-rw-r--r--code/ui/overlay/DeathOverlay.Network.cs18
-rw-r--r--code/ui/overlay/DeathOverlay.razor89
-rw-r--r--code/weapon/Weapon.cs2
15 files changed, 339 insertions, 17 deletions
diff --git a/code/Game.cs b/code/Game.cs
index 24fd79d..ad04fc8 100644
--- a/code/Game.cs
+++ b/code/Game.cs
@@ -56,6 +56,7 @@ public partial class MurderGame : Sandbox.GameManager
// Create a pawn for this client to play with
var pawn = new Player();
client.Pawn = pawn;
+ pawn.Spawn();
var spawnpoints = Entity.All.OfType<SpawnPoint>();
var randomSpawnPoint = spawnpoints.OrderBy( x => Guid.NewGuid() ).FirstOrDefault();
diff --git a/code/pawn/Player.Use.cs b/code/pawn/Player.Use.cs
new file mode 100644
index 0000000..3f35052
--- /dev/null
+++ b/code/pawn/Player.Use.cs
@@ -0,0 +1,117 @@
+using Sandbox;
+
+namespace MurderGame;
+
+public partial class Player
+{
+ public Entity Using { get; protected set; }
+
+ protected virtual void TickPlayerUse()
+ {
+ // This is serverside only
+ if ( !Game.IsServer ) return;
+
+ // Turn prediction off
+ using ( Prediction.Off() )
+ {
+ if ( Input.Pressed( "use" ) )
+ {
+ Using = FindUsable();
+
+ if ( Using == null )
+ {
+ UseFail();
+ return;
+ }
+ }
+
+ if ( !Input.Down( "use" ) )
+ {
+ StopUsing();
+ return;
+ }
+
+ if ( !Using.IsValid() )
+ return;
+
+ // If we move too far away or something we should probably ClearUse()?
+
+ //
+ // If use returns true then we can keep using it
+ //
+ if ( Using is IUse use && use.OnUse( this ) )
+ return;
+
+ StopUsing();
+ }
+ }
+
+ /// <summary>
+ /// Player tried to use something but there was nothing there.
+ /// Tradition is to give a disappointed boop.
+ /// </summary>
+ protected virtual void UseFail()
+ {
+ PlaySound( "player_use_fail" );
+ }
+
+ /// <summary>
+ /// If we're using an entity, stop using it
+ /// </summary>
+ protected virtual void StopUsing()
+ {
+ Using = null;
+ }
+
+ /// <summary>
+ /// Returns if the entity is a valid usable entity
+ /// </summary>
+ protected bool IsValidUseEntity( Entity e )
+ {
+ if ( e == null ) return false;
+ if ( e is not IUse use ) return false;
+ if ( !use.IsUsable( this ) ) return false;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Find a usable entity for this player to use
+ /// </summary>
+ protected virtual Entity FindUsable()
+ {
+ // First try a direct 0 width line
+ var tr = Trace.Ray( EyePosition, EyePosition + EyeRotation.Forward * 85 )
+ .Ignore( this )
+ .Run();
+
+ // See if any of the parent entities are usable if we ain't.
+ var ent = tr.Entity;
+ while ( ent.IsValid() && !IsValidUseEntity( ent ) )
+ {
+ ent = ent.Parent;
+ }
+
+ // Nothing found, try a wider search
+ if ( !IsValidUseEntity( ent ) )
+ {
+ tr = Trace.Ray( EyePosition, EyePosition + EyeRotation.Forward * 85 )
+ .Radius( 2 )
+ .Ignore( this )
+ .Run();
+
+ // See if any of the parent entities are usable if we ain't.
+ ent = tr.Entity;
+ while ( ent.IsValid() && !IsValidUseEntity( ent ) )
+ {
+ ent = ent.Parent;
+ }
+ }
+
+ // Still no good? Bail.
+ if ( !IsValidUseEntity( ent ) ) return null;
+
+ return ent;
+ }
+}
+
diff --git a/code/pawn/Player.cs b/code/pawn/Player.cs
index 68d383d..cd2f83a 100644
--- a/code/pawn/Player.cs
+++ b/code/pawn/Player.cs
@@ -36,8 +36,8 @@ public partial class Player : AnimatedEntity
{
get => new
(
- new Vector3( -12, -12, 0 ),
- new Vector3( 12, 12, 64 )
+ new Vector3( -16, -16, 0 ),
+ new Vector3( 16, 16, 72 )
);
}
@@ -49,6 +49,7 @@ public partial class Player : AnimatedEntity
[BindComponent] public PlayerInventory Inventory { get; }
[BindComponent] public PlayerSpectator Spectator { get; }
+ [Net]
public Ragdoll PlayerRagdoll { get; set; }
public ClothingContainer PlayerClothingContainer { get; set; }
@@ -57,6 +58,9 @@ public partial class Player : AnimatedEntity
public override Ray AimRay => new Ray( EyePosition, EyeRotation.Forward );
+ [Net, Predicted]
+ public TimeSince TimeSinceDeath { get; set; } = 0;
+
public override void Spawn()
{
SetModel( "models/citizen/citizen.vmdl" );
@@ -66,21 +70,29 @@ public partial class Player : AnimatedEntity
EnableDrawing = false;
EnableHideInFirstPerson = true;
EnableShadowInFirstPerson = true;
+
SetupPhysicsFromAABB( PhysicsMotionType.Keyframed, Hull.Mins, Hull.Maxs );
EnableSolidCollisions = false;
+
+ Health = 0f;
+ LifeState = LifeState.Dead;
}
public void Respawn()
{
+ DeleteRagdoll();
Tags.Add( "livingplayer" );
+
EnableAllCollisions = true;
EnableDrawing = true;
- Components.Remove( Spectator );
+
+ Components.RemoveAll();
Components.Create<PlayerController>();
Components.Create<PlayerAnimator>();
Components.Create<PlayerInventory>();
+
Health = 100f;
- DeleteRagdoll();
+ LifeState = LifeState.Alive;
}
public void Cleanup()
@@ -101,19 +113,27 @@ public partial class Player : AnimatedEntity
public void DisablePlayer()
{
- EnableAllCollisions = false;
- LifeState = LifeState.Dead;
Tags.Remove( "livingplayer" );
+
+ EnableAllCollisions = false;
+ EnableDrawing = false;
+
Inventory?.Clear();
Components.RemoveAll();
- EnableDrawing = false;
+
+ LifeState = LifeState.Dead;
}
public override void OnKilled()
{
+ TimeSinceDeath = 0;
+
Inventory?.SpillContents(EyePosition, new Vector3(0,0,0));
+
DisablePlayer();
+
Event.Run( MurderEvent.Kill, LastAttacker, this );
+
var ragdoll = new Ragdoll();
ragdoll.Position = Position;
ragdoll.Rotation = Rotation;
@@ -121,7 +141,8 @@ public partial class Player : AnimatedEntity
ragdoll.PhysicsGroup.AddVelocity(LastAttackForce / 100);
PlayerClothingContainer.DressEntity( ragdoll );
PlayerRagdoll = ragdoll;
- Components.Create<PlayerSpectator>();
+
+ DeathOverlay.Show( To.Single( Client ) );
}
public override void TakeDamage( DamageInfo info )
@@ -151,11 +172,22 @@ public partial class Player : AnimatedEntity
public override void Simulate( IClient cl )
{
SimulateRotation();
- Controller?.Simulate( cl );
+ TickPlayerUse();
+
+ Controller?.Simulate( this );
Animator?.Simulate();
Inventory?.Simulate( cl );
Spectator?.Simulate();
+
EyeLocalPosition = Vector3.Up * (64f * Scale);
+
+ if (Game.IsServer && Spectator == null && LifeState == LifeState.Dead && TimeSinceDeath > 3)
+ {
+ Log.Info( "Spectator created" );
+ DeathOverlay.Hide( To.Single( Client ) );
+ Components.Create<PlayerSpectator>();
+ }
+
}
public override void BuildInput()
@@ -186,13 +218,23 @@ public partial class Player : AnimatedEntity
Spectator.FrameSimulate(this);
return;
}
+ else if (Controller != null)
+ {
+ //TOOD move below logic to controller
+ }
SimulateRotation();
Camera.Rotation = ViewAngles.ToRotation();
Camera.FieldOfView = Screen.CreateVerticalFieldOfView( Game.Preferences.FieldOfView );
Camera.FirstPersonViewer = this;
- Camera.Position = EyePosition;
+ if (PlayerRagdoll != null && PlayerRagdoll.IsValid)
+ {
+ Camera.Position = PlayerRagdoll.Position;
+ } else
+ {
+ Camera.Position = EyePosition;
+ }
}
public TraceResult TraceBBox( Vector3 start, Vector3 end, float liftFeet = 0.0f )
diff --git a/code/pawn/PlayerAnimator.cs b/code/pawn/component/PlayerAnimator.cs
index 4cd4e3f..4cd4e3f 100644
--- a/code/pawn/PlayerAnimator.cs
+++ b/code/pawn/component/PlayerAnimator.cs
diff --git a/code/pawn/PlayerInventory.cs b/code/pawn/component/PlayerInventory.cs
index 55fa3ed..55fa3ed 100644
--- a/code/pawn/PlayerInventory.cs
+++ b/code/pawn/component/PlayerInventory.cs
diff --git a/code/pawn/PlayerSpectator.cs b/code/pawn/component/PlayerSpectator.cs
index c468de0..c468de0 100644
--- a/code/pawn/PlayerSpectator.cs
+++ b/code/pawn/component/PlayerSpectator.cs
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<Player>
+{
+ public Player Player { get; set; }
+}
diff --git a/code/pawn/PlayerController.cs b/code/pawn/component/movement/PlayerController.cs
index d638bb0..5df9a33 100644
--- a/code/pawn/PlayerController.cs
+++ b/code/pawn/component/movement/PlayerController.cs
@@ -6,7 +6,7 @@ namespace MurderGame;
public class PlayerController : EntityComponent<Player>
{
- public int StepSize => 12;
+ public int StepSize => 24;
public int GroundAngle => 45;
public int JumpSpeed => 300;
public float Gravity => 800f;
@@ -16,7 +16,7 @@ public class PlayerController : EntityComponent<Player>
bool Grounded => Entity.GroundEntity.IsValid();
- public void Simulate( IClient cl )
+ public void Simulate( Player player )
{
ControllerEvents.Clear();
@@ -52,6 +52,12 @@ public class PlayerController : EntityComponent<Player>
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 )
@@ -61,7 +67,6 @@ public class PlayerController : EntityComponent<Player>
Entity.Position = mh.Position;
Entity.Velocity = mh.Velocity;
}
-
Entity.GroundEntity = groundEntity;
}
diff --git a/code/phase/AssignPhase.cs b/code/phase/AssignPhase.cs
index 26c9fc1..0ae6bd2 100644
--- a/code/phase/AssignPhase.cs
+++ b/code/phase/AssignPhase.cs
@@ -21,9 +21,10 @@ public class AssignPhase : BasePhase
var detectivesNeeded = 1;
var murderersNeeded = 1;
- List<SpawnPoint> spawnpoints = Entity.All.OfType<SpawnPoint>().OrderBy( x => Guid.NewGuid() ).ToList();
+ Random random = new();
+ List<SpawnPoint> spawnpoints = Entity.All.OfType<SpawnPoint>().OrderBy( _ => random.Next() ).ToList();
var clients = Game.Clients.ToList();
- foreach ( int i in Enumerable.Range( 0, clients.Count ).OrderBy( x => Guid.NewGuid() ) )
+ foreach ( int i in Enumerable.Range( 0, clients.Count ).OrderBy( _ => random.Next() ) )
{
var client = clients[i];
if (client.Pawn != null)
@@ -60,7 +61,7 @@ public class AssignPhase : BasePhase
var spawnpoint = spawnpoints[0];
spawnpoints.RemoveAt( 0 );
var tx = spawnpoint.Transform;
- tx.Position = tx.Position + Vector3.Up * 50.0f;
+ tx.Position = tx.Position + Vector3.Up * 10.0f;
pawn.Transform = tx;
RoleOverlay.Show( To.Single( client ) );
diff --git a/code/phase/PlayPhase.cs b/code/phase/PlayPhase.cs
index 118529e..b6210b3 100644
--- a/code/phase/PlayPhase.cs
+++ b/code/phase/PlayPhase.cs
@@ -47,6 +47,7 @@ public class PlayPhase : BasePhase
{
Log.Info( "Removing blind from " + entity.Name );
BlindedOverlay.Hide( To.Single( entity ) );
+ DeathOverlay.Hide( To.Single( entity ) );
if (entity is Player pawn && pawn.IsValid() )
{
if (pawn.Controller != null) pawn.Controller.SpeedMultiplier = 1;
@@ -103,20 +104,25 @@ public class PlayPhase : BasePhase
}
Player victimPlayer = (Player)victim;
Player killerPlayer = (Player)killer;
+
Team victimTeam = victimPlayer.CurrentTeam;
Team killerTeam = killerPlayer.CurrentTeam;
+
victimPlayer.CurrentTeam = Team.Spectator;
Log.Info( victimPlayer + " died to " + killerPlayer );
+
if (victimTeam != Team.Murderer && killerTeam != Team.Murderer)
{
Log.Info( killerPlayer + " shot a bystander");
+
ChatBox.Say( killerPlayer.Client.Name + " killed an innocent bystander" );
+
BlindedOverlay.Show( To.Single( killer ) );
+
if (killerPlayer.Controller != null) killerPlayer.Controller.SpeedMultiplier = 0.3f;
if (killerPlayer.Inventory != null)
{
- Log.Info( killerPlayer + "bonk");
killerPlayer.Inventory.AllowPickup = false;
killerPlayer.Inventory.SpillContents(killerPlayer.EyePosition, killerPlayer.AimRay.Forward);
}
diff --git a/code/phase/WaitPhase.cs b/code/phase/WaitPhase.cs
index 4f2e876..9eab094 100644
--- a/code/phase/WaitPhase.cs
+++ b/code/phase/WaitPhase.cs
@@ -23,6 +23,7 @@ public class WaitPhase : BasePhase
{
base.NextPhase = new AssignPhase();
base.IsFinished = true;
+ return;
}
else if (CountIn && !_isCountDown)
{
@@ -34,6 +35,36 @@ public class WaitPhase : BasePhase
_isCountDown = false;
base.TimeLeft = -1;
}
+
+ foreach (var client in Game.Clients)
+ {
+ if (client.Pawn == null)
+ {
+ var pawn = new Player();
+ client.Pawn = pawn;
+
+ var spawnpoints = Entity.All.OfType<SpawnPoint>();
+ var randomSpawnPoint = spawnpoints.OrderBy( x => Guid.NewGuid() ).FirstOrDefault();
+ if ( randomSpawnPoint != null )
+ {
+ var tx = randomSpawnPoint.Transform;
+ tx.Position = tx.Position + Vector3.Up * 50.0f;
+ pawn.Transform = tx;
+ }
+
+ pawn.CurrentTeam = Team.Bystander;
+ pawn.Spawn();
+ pawn.Respawn();
+ } else
+ {
+ var pawn = (Player)client.Pawn;
+ if (pawn.LifeState == LifeState.Dead)
+ {
+ pawn.CurrentTeam = Team.Bystander;
+ pawn.Respawn();
+ }
+ }
+ }
}
public override void HandleClientJoin( ClientJoinedEvent e )
diff --git a/code/ui/Hud.razor b/code/ui/Hud.razor
index cacf26b..9fcdfdc 100644
--- a/code/ui/Hud.razor
+++ b/code/ui/Hud.razor
@@ -13,6 +13,7 @@
<root>
<BlindedOverlay/>
<RoleOverlay/>
+ <DeathOverlay/>
<ChatBox/>
<VoiceList/>
<PhaseTimer/>
diff --git a/code/ui/overlay/DeathOverlay.Network.cs b/code/ui/overlay/DeathOverlay.Network.cs
new file mode 100644
index 0000000..f1f3bb2
--- /dev/null
+++ b/code/ui/overlay/DeathOverlay.Network.cs
@@ -0,0 +1,18 @@
+using Sandbox;
+
+namespace MurderGame;
+
+public partial class DeathOverlay
+{
+ [ClientRpc]
+ public static void Show( )
+ {
+ if (Instance != null) Instance.ShowOverlay = true;
+ }
+
+ [ClientRpc]
+ public static void Hide()
+ {
+ if (Instance != null) Instance.ShowOverlay = false;
+ }
+}
diff --git a/code/ui/overlay/DeathOverlay.razor b/code/ui/overlay/DeathOverlay.razor
new file mode 100644
index 0000000..15848f6
--- /dev/null
+++ b/code/ui/overlay/DeathOverlay.razor
@@ -0,0 +1,89 @@
+@using Sandbox;
+@using Sandbox.UI;
+
+@namespace MurderGame
+@inherits Panel
+
+<style>
+@@keyframes fadeIn
+{
+ 0% { opacity: 0; }
+ 100% { opacity: 1; }
+}
+deathoverlay {
+}
+.overlay {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100vw;
+ height: 100vh;
+
+ background-color: rgba(80, 1, 1, 0.90);
+ backdrop-filter-blur: 64px;
+ animation-name: fadeIn;
+ animation-duration: 2.5s;
+ animation-iteration-count: 1;
+ animation-timing-function: ease-in;
+ animation-fill-mode: forwards;
+}
+.box {
+ backdrop-filter-blur: 8px;
+ background-color: rgba(0, 0, 0, 0.20);
+ padding: 10px;
+ color: white;
+ font-weight: 700;
+ font-size: 35px;
+ font-family: "Roboto";
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 300px;
+}
+.box-container {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100vw;
+ height: 100vh;
+
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-direction: column;
+}
+</style>
+
+@if (ShowOverlay)
+{
+<div class="overlay">
+</div>
+<div class="box-container">
+ <div class="box">
+ You have died
+ </div>
+</div>
+}
+
+@code
+{
+ public static DeathOverlay Instance { get; private set; }
+
+ public DeathOverlay()
+ {
+ Instance = this;
+ }
+
+ public bool ShowOverlay { get; set; }
+
+ protected override int BuildHash()
+ {
+ var clientPawn = Game.LocalPawn;
+ if (clientPawn is Player player)
+ {
+ ShowOverlay = player.LifeState == LifeState.Dead && player.Spectator == null;
+ }
+ return ShowOverlay.GetHashCode();
+ }
+
+} \ No newline at end of file
diff --git a/code/weapon/Weapon.cs b/code/weapon/Weapon.cs
index 4519adc..6d6fc20 100644
--- a/code/weapon/Weapon.cs
+++ b/code/weapon/Weapon.cs
@@ -195,11 +195,13 @@ public partial class Weapon : AnimatedEntity
[ClientRpc]
public void CreateViewModel()
{
+ DestroyViewModel();
if ( ViewModelPath == null ) return;
var vm = new WeaponViewModel( this );
vm.Model = Model.Load( ViewModelPath );
vm.Owner = Owner;
+ vm.Parent = Game.LocalPawn;
ViewModelEntity = vm;
if (!string.IsNullOrEmpty(HandsModelPath))
{