From 71db52c5443a7bf82d9a23a770994a42b043be04 Mon Sep 17 00:00:00 2001 From: Leonardo Bishop Date: Thu, 27 Jul 2023 22:11:31 +0100 Subject: Initial commit --- code/weapon/Knife.cs | 37 +++++++ code/weapon/Pistol.cs | 52 ++++++++++ code/weapon/Weapon.cs | 226 +++++++++++++++++++++++++++++++++++++++++ code/weapon/WeaponViewModel.cs | 22 ++++ 4 files changed, 337 insertions(+) create mode 100644 code/weapon/Knife.cs create mode 100644 code/weapon/Pistol.cs create mode 100644 code/weapon/Weapon.cs create mode 100644 code/weapon/WeaponViewModel.cs (limited to 'code/weapon') diff --git a/code/weapon/Knife.cs b/code/weapon/Knife.cs new file mode 100644 index 0000000..1bc74ed --- /dev/null +++ b/code/weapon/Knife.cs @@ -0,0 +1,37 @@ +using Sandbox; + +namespace MurderGame; + +public partial class Knife : Weapon +{ + public override string ModelPath => "weapons/swb/melee/bayonet/w_bayonet.vmdl"; + public override string ViewModelPath => "weapons/swb/melee/bayonet/v_bayonet.vmdl"; + public override string HandsModelPath => "weapons/swb/hands/rebel/v_hands_rebel.vmdl"; + public override float PrimaryRate => 1f; + + public Knife() + { + Ammo = -1; + MaxAmmo = -1; + } + + [ClientRpc] + protected virtual void ShootEffects(bool hit) + { + Game.AssertClient(); + + Pawn.SetAnimParameter( "b_attack", true ); + ViewModelEntity?.SetAnimParameter( hit ? "swing" : "swing_miss" , true ); + } + + public override void PrimaryAttack() + { + Pawn.PlaySound( "bayonet.slash" ); + ShootEffects( Melee( 100, 100 ) ); + } + + protected override void Animate() + { + Pawn.SetAnimParameter( "holdtype", (int)CitizenAnimationHelper.HoldTypes.Swing ); + } +} diff --git a/code/weapon/Pistol.cs b/code/weapon/Pistol.cs new file mode 100644 index 0000000..567eb3e --- /dev/null +++ b/code/weapon/Pistol.cs @@ -0,0 +1,52 @@ +using Sandbox; + +namespace MurderGame; + +public partial class Pistol : Weapon +{ + public override string ModelPath => "weapons/rust_pistol/rust_pistol.vmdl"; + public override string ViewModelPath => "weapons/rust_pistol/v_rust_pistol.vmdl"; + + public Pistol() + { + MaxAmmo = 1; + } + + [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() + { + if (Ammo > 0) + { + --Ammo; + ShootEffects(); + Pawn?.PlaySound( "rust_pistol.shoot" ); + ShootBullet( 100, 100, 1 ); + } + } + + public override void Reload() + { + ReloadEffects(); + } + + [ClientRpc] + protected virtual void ReloadEffects() + { + ViewModelEntity?.SetAnimParameter( "reload", true ); + } + + protected override void Animate() + { + Pawn?.SetAnimParameter( "holdtype", (int)CitizenAnimationHelper.HoldTypes.Pistol ); + } +} diff --git a/code/weapon/Weapon.cs b/code/weapon/Weapon.cs new file mode 100644 index 0000000..2af8ba6 --- /dev/null +++ b/code/weapon/Weapon.cs @@ -0,0 +1,226 @@ +using Sandbox; +using System.Collections.Generic; + +namespace MurderGame; + +public partial class Weapon : AnimatedEntity +{ + public WeaponViewModel ViewModelEntity { get; protected set; } + public BaseViewModel HandModelEntity { get; protected set; } + + public Player Pawn => Owner as Player; + + public AnimatedEntity EffectEntity => Camera.FirstPersonViewer == Owner ? ViewModelEntity : this; + + public virtual string ViewModelPath => null; + public virtual string ModelPath => null; + public virtual string HandsModelPath => null; + + public virtual float PrimaryRate => 5f; + public virtual float ReloadTime => 3.5f; + + [Net, Predicted] public TimeSince TimeSincePrimaryAttack { get; set; } + + [Net, Predicted] public TimeSince TimeSinceReload { get; set; } + [Net, Predicted] public TimeUntil TimeUntilReloadComplete { get; set; } + [Net, Predicted] public bool Reloading { get; set; } + + [Net, Predicted] public int Ammo { get; set; } + [Net, Predicted] public int MaxAmmo { get; set; } + + public override void Spawn() + { + EnableHideInFirstPerson = true; + EnableShadowInFirstPerson = true; + EnableDrawing = false; + + if ( ModelPath != null ) + { + SetModel( ModelPath ); + } + } + + public void ChangeOwner( Player pawn ) + { + Owner = pawn; + SetParent( pawn, true ); + } + + public void OnEquip( Player pawn ) + { + if (Owner == null) + { + Owner = pawn; + SetParent( pawn, true ); + } + EnableDrawing = true; + CreateViewModel( To.Single( pawn ) ); + } + + public void OnHolster() + { + Reloading = false; + EnableDrawing = false; + DestroyViewModel( To.Single( Owner ) ); + Owner = null; + } + + public override void Simulate( IClient player ) + { + Animate(); + if (Reloading && TimeUntilReloadComplete) + { + Reloading = false; + Ammo = MaxAmmo; + } + + if ( CanPrimaryAttack() ) + { + using ( LagCompensation() ) + { + TimeSincePrimaryAttack = 0; + PrimaryAttack(); + } + } + else if (Input.Down("reload") && !Reloading && Ammo != MaxAmmo) + { + Reload(); + Reloading = true; + TimeUntilReloadComplete = ReloadTime; + } + } + + public virtual bool CanPrimaryAttack() + { + if ( !Owner.IsValid() || !Input.Down( "attack1" ) ) return false; + + var rate = PrimaryRate; + if ( rate <= 0 ) return true; + + return !Reloading && TimeSincePrimaryAttack > (1 / rate); + } + + public virtual void PrimaryAttack() + { + } + + public virtual void Reload() + { + } + + protected virtual void Animate() + { + } + + 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", "livingplayer", "npc" ) + .Ignore( this ) + .Size( radius ); + + if ( !underWater ) + trace = trace.WithAnyTags( "water" ); + + var tr = trace.Run(); + + if ( tr.Hit ) + yield return tr; + } + + 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; + + foreach ( var tr in TraceBullet( pos, pos + forward * 5000, bulletSize ) ) + { + tr.Surface.DoBulletImpact( tr ); + + if ( !Game.IsServer ) continue; + if ( !tr.Entity.IsValid() || !tr.Entity.Tags.Has("player") ) continue; + + using ( Prediction.Off() ) + { + var damageInfo = DamageInfo.FromBullet( tr.EndPosition, forward * 100 * force, damage ) + .UsingTraceResult( tr ) + .WithAttacker( Owner ) + .WithWeapon( this ); + + tr.Entity.TakeDamage( damageInfo ); + } + } + } + + public virtual void ShootBullet( float force, float damage, float bulletSize ) + { + Game.SetRandomSeed( Time.Tick ); + + var ray = Owner.AimRay; + ShootBullet( ray.Position, ray.Forward, 0, force, damage, bulletSize ); + } + + public virtual bool Melee( float force, float damage ) + { + var ray = Owner.AimRay; + var forward = ray.Forward.Normal; + var pos = ray.Position; + bool hit = false; + + foreach (var tr in TraceBullet(pos, pos + forward * 50, 20)) + { + tr.Surface.DoBulletImpact(tr); + hit = true; + + if ( !Game.IsServer ) continue; + if ( !tr.Entity.IsValid() || !tr.Entity.Tags.Has("player") ) continue; + + using (Prediction.Off()) + { + var damageInfo = DamageInfo.FromBullet( tr.EndPosition, forward.Normal * 100 * force, damage ) + .UsingTraceResult(tr) + .WithAttacker(Owner) + .WithWeapon(this); + + tr.Entity.TakeDamage(damageInfo); + } + } + return hit; + } + + [ClientRpc] + public void CreateViewModel() + { + if ( ViewModelPath == null ) return; + + var vm = new WeaponViewModel( this ); + vm.Model = Model.Load( ViewModelPath ); + vm.Owner = Owner; + ViewModelEntity = vm; + if (!string.IsNullOrEmpty(HandsModelPath)) + { + HandModelEntity = new BaseViewModel(); + HandModelEntity.Owner = Owner; + HandModelEntity.EnableViewmodelRendering = true; + HandModelEntity.SetModel(HandsModelPath); + HandModelEntity.SetParent(ViewModelEntity, true); + } + } + + [ClientRpc] + public void DestroyViewModel() + { + if ( ViewModelEntity.IsValid() ) + { + ViewModelEntity.Delete(); + } + if ( HandModelEntity.IsValid() ) + { + HandModelEntity.Delete(); + } + } +} diff --git a/code/weapon/WeaponViewModel.cs b/code/weapon/WeaponViewModel.cs new file mode 100644 index 0000000..97004fe --- /dev/null +++ b/code/weapon/WeaponViewModel.cs @@ -0,0 +1,22 @@ +using Sandbox; + +namespace MurderGame; + +public partial class WeaponViewModel : BaseViewModel +{ + protected Weapon Weapon { get; init; } + + public WeaponViewModel( Weapon weapon ) + { + Weapon = weapon; + EnableShadowCasting = false; + EnableViewmodelRendering = true; + } + + public override void PlaceViewmodel() + { + base.PlaceViewmodel(); + + Camera.Main.SetViewModelCamera( 80f, 1, 500 ); + } +} -- cgit v1.2.3-70-g09d2