diff --git a/code/Entities/Lucker.cs b/code/Entities/Lucker.cs index 3394cd4..92494b8 100644 --- a/code/Entities/Lucker.cs +++ b/code/Entities/Lucker.cs @@ -13,6 +13,7 @@ public partial class Lucker : Entity /// /// The entity this Player currently controls /// + [Net] public Entity Pawn { get; set; } /// @@ -70,18 +71,20 @@ public partial class Lucker : Entity public override void Simulate( IClient cl ) { base.Simulate( cl ); - + Pawn?.Simulate(cl); } public override void FrameSimulate( IClient cl ) { base.FrameSimulate( cl ); Camera?.Update(); + Pawn?.FrameSimulate(cl); } public override void BuildInput() { base.BuildInput(); Camera?.BuildInput(); + Pawn?.BuildInput(); } } diff --git a/code/Entities/MinigameManager.cs b/code/Entities/MinigameManager.cs index 0832c8b..32a6a3f 100644 --- a/code/Entities/MinigameManager.cs +++ b/code/Entities/MinigameManager.cs @@ -29,7 +29,8 @@ public partial class MinigameManager : Entity 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}" ); LoadedMinigame.Initialize( players ); } diff --git a/code/EntityComponents/Lucker/Cameras/AbstractCamera.cs b/code/EntityComponents/Lucker/Cameras/AbstractCamera.cs index 0d9e6d7..08645c5 100644 --- a/code/EntityComponents/Lucker/Cameras/AbstractCamera.cs +++ b/code/EntityComponents/Lucker/Cameras/AbstractCamera.cs @@ -6,11 +6,11 @@ namespace LuckerGame.Components.Lucker.Cameras; public abstract class AbstractCamera : EntityComponent, ISingletonComponent { public virtual bool ShouldShowCursor => false; - protected Vector3 CameraPosition { get; set; } - protected Rotation CameraRotation { get; set; } - protected float FieldOfView { get; set; } - protected IEntity FirstPersonViewer { get; set; } - protected Transform SoundSource { get; set; } + public Vector3 CameraPosition { get; set; } + public Rotation CameraRotation { get; set; } + public float FieldOfView { get; set; } + public IEntity FirstPersonViewer { get; set; } + public Transform SoundSource { get; set; } /// /// Handles any input-independent camera updates (ie following a pawn) diff --git a/code/EntityComponents/Lucker/Cameras/FpsCamera.cs b/code/EntityComponents/Lucker/Cameras/FpsCamera.cs new file mode 100644 index 0000000..d5d2284 --- /dev/null +++ b/code/EntityComponents/Lucker/Cameras/FpsCamera.cs @@ -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; + } +} diff --git a/code/EntityComponents/Lucker/Cameras/RTSCamera.cs b/code/EntityComponents/Lucker/Cameras/RTSCamera.cs index e107fba..b9b4243 100644 --- a/code/EntityComponents/Lucker/Cameras/RTSCamera.cs +++ b/code/EntityComponents/Lucker/Cameras/RTSCamera.cs @@ -7,7 +7,7 @@ namespace LuckerGame.Components.Lucker.Cameras; /// /// A top down camera that can be /// -public partial class RTSCamera : AbstractCamera, ISingletonComponent +public partial class RTSCamera : AbstractCamera { public override bool ShouldShowCursor => true; private const float MaxDistance = 400f; diff --git a/code/Minigames/FpsTest/FpsTestMinigame.cs b/code/Minigames/FpsTest/FpsTestMinigame.cs new file mode 100644 index 0000000..a518a20 --- /dev/null +++ b/code/Minigames/FpsTest/FpsTestMinigame.cs @@ -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 Players { get; set; } + + public override void Initialize( List players ) + { + Players = players; + Players.ForEach( player => + { + player.Components.Create(); + Pawn fpsPawn = new Pawn(); + fpsPawn.SetupOwner(player); + player.Pawn = fpsPawn; + + // Get all of the spawnpoints + var spawnpoints = Entity.All.OfType(); + + // 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() + { + } +} diff --git a/code/Minigames/FpsTest/Pawn.cs b/code/Minigames/FpsTest/Pawn.cs new file mode 100644 index 0000000..9214303 --- /dev/null +++ b/code/Minigames/FpsTest/Pawn.cs @@ -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; + } + } + + /// + /// 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; } + + 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(); } + + /// + /// Called when the entity is first created + /// + public override void Spawn() + { + + SetModel( "models/citizen/citizen.vmdl" ); + + EnableDrawing = true; + EnableHideInFirstPerson = true; + EnableShadowInFirstPerson = true; + + Components.Create(); + Components.Create(); + } + + 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(); + } + + protected void SimulateRotation() + { + EyeRotation = ViewAngles.ToRotation(); + Rotation = ViewAngles.WithPitch( 0f ).ToRotation(); + } +} diff --git a/code/Minigames/FpsTest/PawnAnimator.cs b/code/Minigames/FpsTest/PawnAnimator.cs new file mode 100644 index 0000000..ad062cc --- /dev/null +++ b/code/Minigames/FpsTest/PawnAnimator.cs @@ -0,0 +1,20 @@ +using Sandbox; + +namespace LuckerGame.Minigames.FpsTest; + +public class PawnAnimator : 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/Minigames/FpsTest/PawnController.cs b/code/Minigames/FpsTest/PawnController.cs new file mode 100644 index 0000000..60e035c --- /dev/null +++ b/code/Minigames/FpsTest/PawnController.cs @@ -0,0 +1,174 @@ +using Sandbox; +using System; +using System.Collections.Generic; + +namespace LuckerGame.Minigames.FpsTest; + +public class PawnController : EntityComponent +{ + public int StepSize => 24; + public int GroundAngle => 45; + public int JumpSpeed => 300; + public float Gravity => 800f; + + HashSet 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 ); + } +} diff --git a/code/Minigames/FpsTest/PawnPropertyProxyComponent.cs b/code/Minigames/FpsTest/PawnPropertyProxyComponent.cs new file mode 100644 index 0000000..2702831 --- /dev/null +++ b/code/Minigames/FpsTest/PawnPropertyProxyComponent.cs @@ -0,0 +1,22 @@ +using System.ComponentModel; +using LuckerGame.Entities; +using Sandbox; + +namespace LuckerGame.Minigames.FpsTest; + +/// +/// Component attached to a Lucker that will allow us to use [ClientInput] in the actual minigame's Pawn. +/// +/// < +/// +/// 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. +/// +public class PawnPropertyProxyComponent : EntityComponent +{ + [ClientInput] + public Vector3 InputDirection { get; set; } + + [ClientInput] + public Angles ViewAngles { get; set; } +} diff --git a/code/Minigames/FpsTest/Weapons/FpsWeapon.cs b/code/Minigames/FpsTest/Weapons/FpsWeapon.cs new file mode 100644 index 0000000..251ce19 --- /dev/null +++ b/code/Minigames/FpsTest/Weapons/FpsWeapon.cs @@ -0,0 +1,205 @@ +using System.Collections.Generic; +using Sandbox; + +namespace LuckerGame.Minigames.FpsTest.Weapons; + +public partial class FpsWeapon : AnimatedEntity +{ + /// + /// The View Model's entity, only accessible clientside. + /// + public WeaponViewModel ViewModelEntity { get; protected set; } + + /// + /// An accessor to grab our Pawn. + /// + public Pawn Pawn => Owner as Pawn; + + /// + /// This'll decide which entity to fire effects from. If we're in first person, the View Model, otherwise, this. + /// + public AnimatedEntity EffectEntity => Camera.FirstPersonViewer == Owner ? ViewModelEntity : this; + + public virtual string ViewModelPath => null; + public virtual string ModelPath => null; + + /// + /// How often you can shoot this gun. + /// + public virtual float PrimaryRate => 5.0f; + + /// + /// How long since we last shot this gun. + /// + [Net, Predicted] public TimeSince TimeSincePrimaryAttack { get; set; } + + public override void Spawn() + { + EnableHideInFirstPerson = true; + EnableShadowInFirstPerson = true; + EnableDrawing = false; + + if ( ModelPath != null ) + { + SetModel( ModelPath ); + } + } + + /// + /// Called when is called for this weapon. + /// + /// + public void OnEquip( Pawn pawn ) + { + Owner = pawn; + SetParent( pawn, true ); + EnableDrawing = true; + CreateViewModel( To.Single( pawn ) ); + } + + /// + /// Called when the weapon is either removed from the player, or holstered. + /// + public void OnHolster() + { + EnableDrawing = false; + DestroyViewModel( To.Single( Owner ) ); + } + + /// + /// Called from . + /// + /// + public override void Simulate( IClient player ) + { + Animate(); + + if ( CanPrimaryAttack() ) + { + using ( LagCompensation() ) + { + TimeSincePrimaryAttack = 0; + PrimaryAttack(); + } + } + } + + /// + /// Called every to see if we can shoot our gun. + /// + /// + public virtual bool CanPrimaryAttack() + { + if ( !Owner.IsValid() || !Input.Down( "attack1" ) ) return false; + + var rate = PrimaryRate; + if ( rate <= 0 ) return true; + + return TimeSincePrimaryAttack > (1 / rate); + } + + /// + /// Called when your gun shoots. + /// + public virtual void PrimaryAttack() + { + } + + /// + /// Useful for setting anim parameters based off the current weapon. + /// + protected virtual void Animate() + { + } + + /// + /// 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; + } + + /// + /// Shoot a single bullet + /// + 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 ); + } + } + } + + /// + /// Shoot a single bullet from owners view point + /// + 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(); + } + } +} diff --git a/code/Minigames/FpsTest/Weapons/Pistol.cs b/code/Minigames/FpsTest/Weapons/Pistol.cs new file mode 100644 index 0000000..f68ef54 --- /dev/null +++ b/code/Minigames/FpsTest/Weapons/Pistol.cs @@ -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 ); + } +} diff --git a/code/Minigames/FpsTest/Weapons/WeaponViewModel.cs b/code/Minigames/FpsTest/Weapons/WeaponViewModel.cs new file mode 100644 index 0000000..1bfc004 --- /dev/null +++ b/code/Minigames/FpsTest/Weapons/WeaponViewModel.cs @@ -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 ); + } +} diff --git a/code/UI/HudComponents/CameraCursor.razor b/code/UI/HudComponents/CameraCursor.razor index e32e736..a24cc16 100644 --- a/code/UI/HudComponents/CameraCursor.razor +++ b/code/UI/HudComponents/CameraCursor.razor @@ -1,3 +1,4 @@ +@using System @using LuckerGame.Components.Lucker.Cameras @using Sandbox @using Sandbox.UI @@ -5,12 +6,19 @@ @attribute [StyleSheet] @inherits Panel -@if (ShouldShowCursor) -{ - -} + + @if (ShouldShowCursor) + { +
+ } +
@code { + protected override int BuildHash() + { + return HashCode.Combine(ShouldShowCursor); + } + private bool ShouldShowCursor => Game.LocalClient.Pawn?.Components.Get()?.ShouldShowCursor ?? false; } \ No newline at end of file diff --git a/code/UI/HudComponents/CameraCursor.razor.scss b/code/UI/HudComponents/CameraCursor.razor.scss index 82e3b95..7316c9f 100644 --- a/code/UI/HudComponents/CameraCursor.razor.scss +++ b/code/UI/HudComponents/CameraCursor.razor.scss @@ -1,3 +1,5 @@ CameraCursor { - pointer-events: all; + .cursor-catch { + pointer-events: all; + } } \ No newline at end of file