Create FPS Test minigame with other shit
This commit is contained in:
@@ -13,6 +13,7 @@ public partial class Lucker : Entity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity this Player currently controls
|
/// The entity this Player currently controls
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Net]
|
||||||
public Entity Pawn { get; set; }
|
public Entity Pawn { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -70,18 +71,20 @@ public partial class Lucker : Entity
|
|||||||
public override void Simulate( IClient cl )
|
public override void Simulate( IClient cl )
|
||||||
{
|
{
|
||||||
base.Simulate( cl );
|
base.Simulate( cl );
|
||||||
|
Pawn?.Simulate(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FrameSimulate( IClient cl )
|
public override void FrameSimulate( IClient cl )
|
||||||
{
|
{
|
||||||
base.FrameSimulate( cl );
|
base.FrameSimulate( cl );
|
||||||
Camera?.Update();
|
Camera?.Update();
|
||||||
|
Pawn?.FrameSimulate(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void BuildInput()
|
public override void BuildInput()
|
||||||
{
|
{
|
||||||
base.BuildInput();
|
base.BuildInput();
|
||||||
Camera?.BuildInput();
|
Camera?.BuildInput();
|
||||||
|
Pawn?.BuildInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ public partial class MinigameManager : Entity
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadedMinigame = AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault();
|
// LoadedMinigame = AvailableMinigames.OrderBy( _ => Guid.NewGuid() ).FirstOrDefault();
|
||||||
|
LoadedMinigame = AvailableMinigames.Find( minigame => minigame.Name == "FPS Test" );
|
||||||
ChatBox.AddInformation( To.Everyone, $"Starting {LoadedMinigame.Name}" );
|
ChatBox.AddInformation( To.Everyone, $"Starting {LoadedMinigame.Name}" );
|
||||||
LoadedMinigame.Initialize( players );
|
LoadedMinigame.Initialize( players );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ namespace LuckerGame.Components.Lucker.Cameras;
|
|||||||
public abstract class AbstractCamera : EntityComponent<Entities.Lucker>, ISingletonComponent
|
public abstract class AbstractCamera : EntityComponent<Entities.Lucker>, ISingletonComponent
|
||||||
{
|
{
|
||||||
public virtual bool ShouldShowCursor => false;
|
public virtual bool ShouldShowCursor => false;
|
||||||
protected Vector3 CameraPosition { get; set; }
|
public Vector3 CameraPosition { get; set; }
|
||||||
protected Rotation CameraRotation { get; set; }
|
public Rotation CameraRotation { get; set; }
|
||||||
protected float FieldOfView { get; set; }
|
public float FieldOfView { get; set; }
|
||||||
protected IEntity FirstPersonViewer { get; set; }
|
public IEntity FirstPersonViewer { get; set; }
|
||||||
protected Transform SoundSource { get; set; }
|
public Transform SoundSource { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles any input-independent camera updates (ie following a pawn)
|
/// Handles any input-independent camera updates (ie following a pawn)
|
||||||
|
|||||||
43
code/EntityComponents/Lucker/Cameras/FpsCamera.cs
Normal file
43
code/EntityComponents/Lucker/Cameras/FpsCamera.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Components.Lucker.Cameras;
|
||||||
|
|
||||||
|
public class FpsCamera : AbstractCamera
|
||||||
|
{
|
||||||
|
protected override void UpdateCameraParameters()
|
||||||
|
{
|
||||||
|
if ( Entity.Pawn is not Minigames.FpsTest.Pawn pawn)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraRotation = pawn.ViewAngles.ToRotation();
|
||||||
|
FieldOfView = Screen.CreateVerticalFieldOfView( Game.Preferences.FieldOfView );
|
||||||
|
FirstPersonViewer = pawn;
|
||||||
|
CameraPosition = pawn.EyePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BuildInput()
|
||||||
|
{
|
||||||
|
if ( Input.StopProcessing )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( Entity.Pawn is not Minigames.FpsTest.Pawn pawn )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var look = Input.AnalogLook;
|
||||||
|
|
||||||
|
if ( pawn.ViewAngles.pitch is > 90f or < -90f )
|
||||||
|
{
|
||||||
|
look = look.WithYaw( look.yaw * -1f );
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewAngles = pawn.ViewAngles;
|
||||||
|
viewAngles += look;
|
||||||
|
viewAngles.pitch = viewAngles.pitch.Clamp( -89f, 89f );
|
||||||
|
viewAngles.roll = 0f;
|
||||||
|
pawn.ViewAngles = viewAngles.Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ namespace LuckerGame.Components.Lucker.Cameras;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A top down camera that can be
|
/// A top down camera that can be
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class RTSCamera : AbstractCamera, ISingletonComponent
|
public partial class RTSCamera : AbstractCamera
|
||||||
{
|
{
|
||||||
public override bool ShouldShowCursor => true;
|
public override bool ShouldShowCursor => true;
|
||||||
private const float MaxDistance = 400f;
|
private const float MaxDistance = 400f;
|
||||||
|
|||||||
49
code/Minigames/FpsTest/FpsTestMinigame.cs
Normal file
49
code/Minigames/FpsTest/FpsTestMinigame.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LuckerGame.Components.Lucker.Cameras;
|
||||||
|
using LuckerGame.Entities;
|
||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest;
|
||||||
|
|
||||||
|
[Library("mg_fps_test")]
|
||||||
|
public class FpsTestMinigame : Minigame
|
||||||
|
{
|
||||||
|
public override string Name => "FPS Test";
|
||||||
|
private List<Lucker> Players { get; set; }
|
||||||
|
|
||||||
|
public override void Initialize( List<Lucker> players )
|
||||||
|
{
|
||||||
|
Players = players;
|
||||||
|
Players.ForEach( player =>
|
||||||
|
{
|
||||||
|
player.Components.Create<FpsCamera>();
|
||||||
|
Pawn fpsPawn = new Pawn();
|
||||||
|
fpsPawn.SetupOwner(player);
|
||||||
|
player.Pawn = fpsPawn;
|
||||||
|
|
||||||
|
// Get all of the spawnpoints
|
||||||
|
var spawnpoints = Entity.All.OfType<SpawnPoint>();
|
||||||
|
|
||||||
|
// chose a random one
|
||||||
|
var randomSpawnPoint = spawnpoints.OrderBy( x => Guid.NewGuid() ).FirstOrDefault();
|
||||||
|
|
||||||
|
// if it exists, place the pawn there
|
||||||
|
if ( randomSpawnPoint != null )
|
||||||
|
{
|
||||||
|
var tx = randomSpawnPoint.Transform;
|
||||||
|
tx.Position = tx.Position + Vector3.Up * 50.0f; // raise it up
|
||||||
|
player.Position = tx.Position;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Tick()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Cleanup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
164
code/Minigames/FpsTest/Pawn.cs
Normal file
164
code/Minigames/FpsTest/Pawn.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
using Sandbox;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using LuckerGame.Minigames.FpsTest.Weapons;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest;
|
||||||
|
|
||||||
|
public partial class Pawn : AnimatedEntity
|
||||||
|
{
|
||||||
|
[Net, Predicted]
|
||||||
|
public FpsWeapon ActiveFpsWeapon { get; set; }
|
||||||
|
|
||||||
|
public Vector3 InputDirection
|
||||||
|
{
|
||||||
|
get => PropertyProxy?.InputDirection ?? Vector3.Zero;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if ( PropertyProxy == null ) return;
|
||||||
|
PropertyProxy.InputDirection = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Angles ViewAngles {
|
||||||
|
get => PropertyProxy?.ViewAngles ?? Angles.Zero;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if ( PropertyProxy == null ) return;
|
||||||
|
PropertyProxy.ViewAngles = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Position a player should be looking from in world space.
|
||||||
|
/// </summary>
|
||||||
|
[Browsable( false )]
|
||||||
|
public Vector3 EyePosition
|
||||||
|
{
|
||||||
|
get => Transform.PointToWorld( EyeLocalPosition );
|
||||||
|
set => EyeLocalPosition = Transform.PointToLocal( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Position a player should be looking from in local to the entity coordinates.
|
||||||
|
/// </summary>
|
||||||
|
[Net, Predicted, Browsable( false )]
|
||||||
|
public Vector3 EyeLocalPosition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotation of the entity's "eyes", i.e. rotation for the camera when this entity is used as the view entity.
|
||||||
|
/// </summary>
|
||||||
|
[Browsable( false )]
|
||||||
|
public Rotation EyeRotation
|
||||||
|
{
|
||||||
|
get => Transform.RotationToWorld( EyeLocalRotation );
|
||||||
|
set => EyeLocalRotation = Transform.RotationToLocal( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[Net, Predicted, Browsable( false )]
|
||||||
|
public Rotation EyeLocalRotation { get; set; }
|
||||||
|
|
||||||
|
public BBox Hull
|
||||||
|
{
|
||||||
|
get => new
|
||||||
|
(
|
||||||
|
new Vector3( -16, -16, 0 ),
|
||||||
|
new Vector3( 16, 16, 64 )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BindComponent] public PawnController Controller { get; }
|
||||||
|
[BindComponent] public PawnAnimator Animator { get; }
|
||||||
|
|
||||||
|
public override Ray AimRay => new Ray( EyePosition, EyeRotation.Forward );
|
||||||
|
|
||||||
|
private PawnPropertyProxyComponent PropertyProxy { get => Owner?.Components.Get<PawnPropertyProxyComponent>(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the entity is first created
|
||||||
|
/// </summary>
|
||||||
|
public override void Spawn()
|
||||||
|
{
|
||||||
|
|
||||||
|
SetModel( "models/citizen/citizen.vmdl" );
|
||||||
|
|
||||||
|
EnableDrawing = true;
|
||||||
|
EnableHideInFirstPerson = true;
|
||||||
|
EnableShadowInFirstPerson = true;
|
||||||
|
|
||||||
|
Components.Create<PawnController>();
|
||||||
|
Components.Create<PawnAnimator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetActiveWeapon( FpsWeapon fpsWeapon )
|
||||||
|
{
|
||||||
|
ActiveFpsWeapon?.OnHolster();
|
||||||
|
ActiveFpsWeapon = fpsWeapon;
|
||||||
|
ActiveFpsWeapon.OnEquip( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DressFromClient( IClient cl )
|
||||||
|
{
|
||||||
|
var c = new ClothingContainer();
|
||||||
|
c.LoadFromClient( cl );
|
||||||
|
c.DressEntity( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Simulate( IClient cl )
|
||||||
|
{
|
||||||
|
SimulateRotation();
|
||||||
|
Controller?.Simulate( cl );
|
||||||
|
Animator?.Simulate();
|
||||||
|
ActiveFpsWeapon?.Simulate( cl );
|
||||||
|
EyeLocalPosition = Vector3.Up * (64f * Scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void BuildInput()
|
||||||
|
{
|
||||||
|
InputDirection = Input.AnalogMove;
|
||||||
|
|
||||||
|
if ( Input.StopProcessing )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void FrameSimulate( IClient cl )
|
||||||
|
{
|
||||||
|
SimulateRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TraceResult TraceBBox( Vector3 start, Vector3 end, float liftFeet = 0.0f )
|
||||||
|
{
|
||||||
|
return TraceBBox( start, end, Hull.Mins, Hull.Maxs, liftFeet );
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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, end )
|
||||||
|
.Size( mins, maxs )
|
||||||
|
.WithAnyTags( "solid", "playerclip", "passbullets" )
|
||||||
|
.Ignore( this )
|
||||||
|
.Run();
|
||||||
|
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetupOwner( Entity owner )
|
||||||
|
{
|
||||||
|
Owner = owner;
|
||||||
|
owner.Components.GetOrCreate<PawnPropertyProxyComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SimulateRotation()
|
||||||
|
{
|
||||||
|
EyeRotation = ViewAngles.ToRotation();
|
||||||
|
Rotation = ViewAngles.WithPitch( 0f ).ToRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
code/Minigames/FpsTest/PawnAnimator.cs
Normal file
20
code/Minigames/FpsTest/PawnAnimator.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest;
|
||||||
|
|
||||||
|
public class PawnAnimator : EntityComponent<Pawn>, 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
174
code/Minigames/FpsTest/PawnController.cs
Normal file
174
code/Minigames/FpsTest/PawnController.cs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
using Sandbox;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest;
|
||||||
|
|
||||||
|
public class PawnController : EntityComponent<Pawn>
|
||||||
|
{
|
||||||
|
public int StepSize => 24;
|
||||||
|
public int GroundAngle => 45;
|
||||||
|
public int JumpSpeed => 300;
|
||||||
|
public float Gravity => 800f;
|
||||||
|
|
||||||
|
HashSet<string> ControllerEvents = new( StringComparer.OrdinalIgnoreCase );
|
||||||
|
|
||||||
|
bool Grounded => Entity.GroundEntity.IsValid();
|
||||||
|
|
||||||
|
public void Simulate( IClient cl )
|
||||||
|
{
|
||||||
|
ControllerEvents.Clear();
|
||||||
|
|
||||||
|
var movement = Entity.InputDirection.Normal;
|
||||||
|
var angles = Entity.ViewAngles.WithPitch( 0 );
|
||||||
|
var moveVector = Rotation.From( angles ) * movement * 320f;
|
||||||
|
var groundEntity = CheckForGround();
|
||||||
|
|
||||||
|
if ( groundEntity.IsValid() )
|
||||||
|
{
|
||||||
|
if ( !Grounded )
|
||||||
|
{
|
||||||
|
Entity.Velocity = Entity.Velocity.WithZ( 0 );
|
||||||
|
AddEvent( "grounded" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity.Velocity = Accelerate( Entity.Velocity, moveVector.Normal, moveVector.Length, 200.0f * ( Input.Down( "run" ) ? 2.5f : 1f ), 7.5f );
|
||||||
|
Entity.Velocity = ApplyFriction( Entity.Velocity, 4.0f );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Entity.Velocity = Accelerate( Entity.Velocity, moveVector.Normal, moveVector.Length, 100, 20f );
|
||||||
|
Entity.Velocity += Vector3.Down * Gravity * Time.Delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( Input.Pressed( "jump" ) )
|
||||||
|
{
|
||||||
|
DoJump();
|
||||||
|
}
|
||||||
|
|
||||||
|
var mh = new MoveHelper( Entity.Position, Entity.Velocity );
|
||||||
|
mh.Trace = mh.Trace.Size( Entity.Hull ).Ignore( Entity );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
22
code/Minigames/FpsTest/PawnPropertyProxyComponent.cs
Normal file
22
code/Minigames/FpsTest/PawnPropertyProxyComponent.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using LuckerGame.Entities;
|
||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component attached to a Lucker that will allow us to use [ClientInput] in the actual minigame's Pawn.
|
||||||
|
/// </summary>
|
||||||
|
/// <
|
||||||
|
/// <para>
|
||||||
|
/// This is because [ClientInput] only works for the Pawn owned by the Client (Lucker), and the Lucker's components.
|
||||||
|
/// We create this component in the Minigame's Pawn, attach it to the Lucker, and use these properties from there.
|
||||||
|
/// </para>
|
||||||
|
public class PawnPropertyProxyComponent : EntityComponent<Lucker>
|
||||||
|
{
|
||||||
|
[ClientInput]
|
||||||
|
public Vector3 InputDirection { get; set; }
|
||||||
|
|
||||||
|
[ClientInput]
|
||||||
|
public Angles ViewAngles { get; set; }
|
||||||
|
}
|
||||||
205
code/Minigames/FpsTest/Weapons/FpsWeapon.cs
Normal file
205
code/Minigames/FpsTest/Weapons/FpsWeapon.cs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest.Weapons;
|
||||||
|
|
||||||
|
public partial class FpsWeapon : AnimatedEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The View Model's entity, only accessible clientside.
|
||||||
|
/// </summary>
|
||||||
|
public WeaponViewModel ViewModelEntity { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An accessor to grab our Pawn.
|
||||||
|
/// </summary>
|
||||||
|
public Pawn Pawn => Owner as Pawn;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This'll decide which entity to fire effects from. If we're in first person, the View Model, otherwise, this.
|
||||||
|
/// </summary>
|
||||||
|
public AnimatedEntity EffectEntity => Camera.FirstPersonViewer == Owner ? ViewModelEntity : this;
|
||||||
|
|
||||||
|
public virtual string ViewModelPath => null;
|
||||||
|
public virtual string ModelPath => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How often you can shoot this gun.
|
||||||
|
/// </summary>
|
||||||
|
public virtual float PrimaryRate => 5.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long since we last shot this gun.
|
||||||
|
/// </summary>
|
||||||
|
[Net, Predicted] public TimeSince TimeSincePrimaryAttack { get; set; }
|
||||||
|
|
||||||
|
public override void Spawn()
|
||||||
|
{
|
||||||
|
EnableHideInFirstPerson = true;
|
||||||
|
EnableShadowInFirstPerson = true;
|
||||||
|
EnableDrawing = false;
|
||||||
|
|
||||||
|
if ( ModelPath != null )
|
||||||
|
{
|
||||||
|
SetModel( ModelPath );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when <see cref="Pawn.SetActiveWeapon(FpsWeapon)"/> is called for this weapon.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pawn"></param>
|
||||||
|
public void OnEquip( Pawn pawn )
|
||||||
|
{
|
||||||
|
Owner = pawn;
|
||||||
|
SetParent( pawn, true );
|
||||||
|
EnableDrawing = true;
|
||||||
|
CreateViewModel( To.Single( pawn ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the weapon is either removed from the player, or holstered.
|
||||||
|
/// </summary>
|
||||||
|
public void OnHolster()
|
||||||
|
{
|
||||||
|
EnableDrawing = false;
|
||||||
|
DestroyViewModel( To.Single( Owner ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called from <see cref="Pawn.Simulate(IClient)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="player"></param>
|
||||||
|
public override void Simulate( IClient player )
|
||||||
|
{
|
||||||
|
Animate();
|
||||||
|
|
||||||
|
if ( CanPrimaryAttack() )
|
||||||
|
{
|
||||||
|
using ( LagCompensation() )
|
||||||
|
{
|
||||||
|
TimeSincePrimaryAttack = 0;
|
||||||
|
PrimaryAttack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called every <see cref="Simulate(IClient)"/> to see if we can shoot our gun.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual bool CanPrimaryAttack()
|
||||||
|
{
|
||||||
|
if ( !Owner.IsValid() || !Input.Down( "attack1" ) ) return false;
|
||||||
|
|
||||||
|
var rate = PrimaryRate;
|
||||||
|
if ( rate <= 0 ) return true;
|
||||||
|
|
||||||
|
return TimeSincePrimaryAttack > (1 / rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when your gun shoots.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void PrimaryAttack()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Useful for setting anim parameters based off the current weapon.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void Animate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public virtual IEnumerable<TraceResult> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shoot a single bullet
|
||||||
|
/// </summary>
|
||||||
|
public virtual void ShootBullet( Vector3 pos, Vector3 dir, float spread, float force, float damage, float bulletSize )
|
||||||
|
{
|
||||||
|
var forward = dir;
|
||||||
|
forward += (Vector3.Random + Vector3.Random + Vector3.Random + Vector3.Random) * spread * 0.25f;
|
||||||
|
forward = forward.Normal;
|
||||||
|
|
||||||
|
//
|
||||||
|
// ShootBullet is coded in a way where we can have bullets pass through shit
|
||||||
|
// or bounce off shit, in which case it'll return multiple results
|
||||||
|
//
|
||||||
|
foreach ( var tr in TraceBullet( pos, pos + forward * 5000, bulletSize ) )
|
||||||
|
{
|
||||||
|
tr.Surface.DoBulletImpact( tr );
|
||||||
|
|
||||||
|
if ( !Game.IsServer ) continue;
|
||||||
|
if ( !tr.Entity.IsValid() ) continue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We turn predictiuon off for this, so any exploding effects don't get culled etc
|
||||||
|
//
|
||||||
|
using ( Prediction.Off() )
|
||||||
|
{
|
||||||
|
var damageInfo = DamageInfo.FromBullet( tr.EndPosition, forward * 100 * force, damage )
|
||||||
|
.UsingTraceResult( tr )
|
||||||
|
.WithAttacker( Owner )
|
||||||
|
.WithWeapon( this );
|
||||||
|
|
||||||
|
tr.Entity.TakeDamage( damageInfo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shoot a single bullet from owners view point
|
||||||
|
/// </summary>
|
||||||
|
public virtual void ShootBullet( float spread, float force, float damage, float bulletSize )
|
||||||
|
{
|
||||||
|
Game.SetRandomSeed( Time.Tick );
|
||||||
|
|
||||||
|
var ray = Owner.AimRay;
|
||||||
|
ShootBullet( ray.Position, ray.Forward, spread, force, damage, bulletSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
[ClientRpc]
|
||||||
|
private void CreateViewModel()
|
||||||
|
{
|
||||||
|
if ( ViewModelPath == null ) return;
|
||||||
|
|
||||||
|
var vm = new WeaponViewModel( this );
|
||||||
|
vm.Model = Model.Load( ViewModelPath );
|
||||||
|
ViewModelEntity = vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ClientRpc]
|
||||||
|
public void DestroyViewModel()
|
||||||
|
{
|
||||||
|
if ( ViewModelEntity.IsValid() )
|
||||||
|
{
|
||||||
|
ViewModelEntity.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
code/Minigames/FpsTest/Weapons/Pistol.cs
Normal file
32
code/Minigames/FpsTest/Weapons/Pistol.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest.Weapons;
|
||||||
|
|
||||||
|
public partial class Pistol : FpsWeapon
|
||||||
|
{
|
||||||
|
public override string ModelPath => "weapons/rust_pistol/rust_pistol.vmdl";
|
||||||
|
public override string ViewModelPath => "weapons/rust_pistol/v_rust_pistol.vmdl";
|
||||||
|
|
||||||
|
[ClientRpc]
|
||||||
|
protected virtual void ShootEffects()
|
||||||
|
{
|
||||||
|
Game.AssertClient();
|
||||||
|
|
||||||
|
Particles.Create( "particles/pistol_muzzleflash.vpcf", EffectEntity, "muzzle" );
|
||||||
|
|
||||||
|
Pawn.SetAnimParameter( "b_attack", true );
|
||||||
|
ViewModelEntity?.SetAnimParameter( "fire", true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PrimaryAttack()
|
||||||
|
{
|
||||||
|
ShootEffects();
|
||||||
|
Pawn.PlaySound( "rust_pistol.shoot" );
|
||||||
|
ShootBullet( 0.1f, 100, 20, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Animate()
|
||||||
|
{
|
||||||
|
Pawn.SetAnimParameter( "holdtype", (int)CitizenAnimationHelper.HoldTypes.Pistol );
|
||||||
|
}
|
||||||
|
}
|
||||||
22
code/Minigames/FpsTest/Weapons/WeaponViewModel.cs
Normal file
22
code/Minigames/FpsTest/Weapons/WeaponViewModel.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Sandbox;
|
||||||
|
|
||||||
|
namespace LuckerGame.Minigames.FpsTest.Weapons;
|
||||||
|
|
||||||
|
public partial class WeaponViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
protected FpsWeapon FpsWeapon { get; init; }
|
||||||
|
|
||||||
|
public WeaponViewModel( FpsWeapon fpsWeapon )
|
||||||
|
{
|
||||||
|
FpsWeapon = fpsWeapon;
|
||||||
|
EnableShadowCasting = false;
|
||||||
|
EnableViewmodelRendering = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PlaceViewmodel()
|
||||||
|
{
|
||||||
|
base.PlaceViewmodel();
|
||||||
|
|
||||||
|
Camera.Main.SetViewModelCamera( 80f, 1, 500 );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@using System
|
||||||
@using LuckerGame.Components.Lucker.Cameras
|
@using LuckerGame.Components.Lucker.Cameras
|
||||||
@using Sandbox
|
@using Sandbox
|
||||||
@using Sandbox.UI
|
@using Sandbox.UI
|
||||||
@@ -5,12 +6,19 @@
|
|||||||
@attribute [StyleSheet]
|
@attribute [StyleSheet]
|
||||||
@inherits Panel
|
@inherits Panel
|
||||||
|
|
||||||
|
<root>
|
||||||
@if (ShouldShowCursor)
|
@if (ShouldShowCursor)
|
||||||
{
|
{
|
||||||
<root/>
|
<div class="cursor-catch"></div>
|
||||||
}
|
}
|
||||||
|
</root>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
protected override int BuildHash()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(ShouldShowCursor);
|
||||||
|
}
|
||||||
|
|
||||||
private bool ShouldShowCursor => Game.LocalClient.Pawn?.Components.Get<AbstractCamera>()?.ShouldShowCursor ?? false;
|
private bool ShouldShowCursor => Game.LocalClient.Pawn?.Components.Get<AbstractCamera>()?.ShouldShowCursor ?? false;
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
CameraCursor {
|
CameraCursor {
|
||||||
|
.cursor-catch {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user