MP5.cs
using System;
using System.Diagnostics;
using Sandbox;
public sealed class MP5 : Component, IPlayerReplicatorEvents, IPlayerHealthEvent
{
[Property]
List<SkinnedModelRenderer> Viewmodels;
[Property]
List<ModelRenderer> Worldmodels;
[Property]
List<SkinnedModelRenderer> Playermodels;
[Property]
List<CameraComponent> Cameras;
[Property]
public float FireDelay { get; set; } = 0.1f;
[Property]
public GameObject MuzzleFlare { get; set; }
[Property]
public SoundEvent FireSound { get; set; }
[Property]
public int Damage { get; set; } = 6;
[Property]
public Texture Crosshair { get; set; }
[Property]
public GameObject TempImpact { get; set; }
TimeSince lastFired;
protected override void OnEnabled()
{
base.OnEnabled();
lastFired = 0;
FindViewmodels();
FindWorldmodels();
FindPlayermodels();
FindCameras();
}
protected override void OnUpdate()
{
base.OnUpdate();
var isAttacking = false;
if ( lastFired > FireDelay && Input.Down( "Attack1" ) )
{
lastFired = 0;
isAttacking = true;
Shoot();
foreach ( var vm in Viewmodels )
{
var muzzle = vm.GetAttachmentObject( "muzzle" );
foreach (var emitter in muzzle.GetComponentsInChildren<ParticleEmitter>())
{
emitter.Emit(emitter.GetComponent<ParticleEffect>());
}
}
foreach ( var vm in Worldmodels )
{
var muzzle = vm.GetAttachmentObject( "muzzle" );
foreach ( var emitter in muzzle.GetComponentsInChildren<ParticleEmitter>() )
{
emitter.Emit( emitter.GetComponent<ParticleEffect>() );
}
}
}
foreach ( var vm in Viewmodels )
{
vm.Set( "b_attack", isAttacking );
var player = vm.GetComponentInParent<PlayerController>();
var move_vectors = player.WishVelocity;
vm.Set( "move_bob", move_vectors.IsNearlyZero() ? 0.0f : 1.0f );
vm.Set( "move_x", move_vectors.x );
vm.Set( "move_y", move_vectors.y );
vm.Set( "move_z", move_vectors.z );
}
foreach ( var vm in Playermodels )
{
vm.Set( "b_attack", isAttacking );
}
DrawReticules();
// DrawTraces();
/*
foreach ( var player in Scene.Components.GetAll<PlayerController>( FindMode.EnabledInSelfAndDescendants ) )
{
if ( !player.GetComponent<Health>().IsAlive ) continue;
var cam = player.GetComponentInChildren<CameraComponent>();
var trace = Scene.Trace.Ray( cam.WorldTransform.ForwardRay, 5000f )
.WithoutTags( "player" )
.UseHitPosition( true )
.Run();
if (trace.Hit)
{
DebugOverlay.Line( cam.WorldPosition, trace.HitPosition );
}
for ( var offset = -15; offset < 15; offset += 1 )
{
var offsetRay = new Ray( cam.WorldPosition, (cam.WorldRotation * Rotation.FromPitch( offset )).Forward );
var newTrace = Scene.Trace.Ray( offsetRay, 5000f )
.WithoutTags( "player" )
.UseHitPosition( true )
.Run();
if ( newTrace.Hit )
{
DebugOverlay.Line( cam.WorldPosition, newTrace.HitPosition );
}
}
}
*/
}
void FindViewmodels()
{
Viewmodels = Scene.Components.GetAll<SkinnedModelRenderer>( FindMode.EnabledInSelfAndDescendants )
.Where( m => m.GameObject.Name == "v_mp5" )
.ToList();
var cameraControllers = Scene.Components.GetAll<LocalCameraControl>( FindMode.EnabledInSelfAndDescendants );
foreach ( var vm in Viewmodels )
{
vm.Set( "b_deploy_skip", true );
foreach ( var cam in cameraControllers )
{
if ( vm.GameObject.IsAncestor( cam.GameObject ) ) continue;
vm.Tags.Set( cam.ExcludeRenderTag, true );
}
}
}
void FindWorldmodels()
{
Worldmodels = Scene.Components.GetAll<ModelRenderer>( FindMode.EnabledInSelfAndDescendants )
.Where( m => m.GameObject.Name == "w_mp5" )
.ToList();
}
void FindPlayermodels()
{
Playermodels = Scene.Components.GetAll<SkinnedModelRenderer>( FindMode.EnabledInSelfAndDescendants )
.Where( m => m.GameObject.Name == "Body" )
.ToList();
}
void FindCameras()
{
Cameras = Scene.Components.GetAll<PlayerController>( FindMode.EnabledInSelfAndChildren )
.Select( pc => pc.GetComponentInChildren<CameraComponent>() )
.ToList();
}
void IPlayerReplicatorEvents.PlayerSpawned( GameObject player )
{
// find viewmodel
var viewmodel = player.Components.GetAll<SkinnedModelRenderer>( FindMode.EnabledInSelfAndDescendants )
.First( m => m.GameObject.Name == "v_mp5" );
Viewmodels.Add( viewmodel );
var cameraControllers = Scene.Components.GetAll<LocalCameraControl>( FindMode.EnabledInSelfAndDescendants );
foreach ( var vm in Viewmodels )
{
foreach ( var cam in cameraControllers )
{
if ( vm.GameObject.IsAncestor( cam.GameObject ) ) continue;
vm.Tags.Set( cam.ExcludeRenderTag, true );
}
}
// find worldmodel
var worldmodel = player.Components.GetAll<ModelRenderer>( FindMode.EnabledInSelfAndDescendants )
.First( m => m.GameObject.Name == "w_mp5" );
Worldmodels.Add( worldmodel );
FindPlayermodels();
FindCameras();
}
void IPlayerHealthEvent.OnDied( GameObject player )
{
Viewmodels.Remove( Viewmodels.Find( smr => smr.GameObject.IsAncestor( player ) ) );
Worldmodels.Remove( Worldmodels.Find( mr => mr.GameObject.IsAncestor( player ) ) );
Playermodels.Remove( Playermodels.Find( smr => smr.GameObject.IsAncestor( player ) ) );
Cameras.Remove( Cameras.Find( cam => cam.GameObject.IsAncestor( player ) ) );
}
/*
List<SceneTraceResult> traces = [];
SceneTraceResult? hitPosition = null;
List<Vector3> Breakpoints = [];
Ray XhRay;
List<Line> CyanLines;
List<Line> GreenLines;
List<Line> RedLines;
void DrawTraces()
{
foreach (var trace in traces)
{
var forward = Rotation.FromToRotation( trace.StartPosition, trace.HitPosition );
DebugOverlay.Line( new Line( trace.StartPosition + forward.Forward, trace.HitPosition ) );
}
var xforward = Rotation.FromToRotation( XhRay.Position, XhRay.Position + XhRay.Forward );
DebugOverlay.Line( new Line( XhRay.Position + xforward.Forward, XhRay.Position + XhRay.Forward * 5000f ), Color.Cyan );
foreach (var bp in Breakpoints)
{
DebugOverlay.Sphere( new Sphere( bp, 5 ), Color.Cyan );
}
if (hitPosition.HasValue)
{
DebugOverlay.Sphere( new Sphere( hitPosition.Value.HitPosition, 5 ) );
}
foreach (var line in CyanLines)
{
DebugOverlay.Line( line, Color.Cyan );
}
foreach ( var line in RedLines )
{
DebugOverlay.Line( line, Color.Red );
}
foreach ( var line in GreenLines )
{
DebugOverlay.Line( line, Color.Green );
}
}
*/
SceneTraceResult? MostFittingEnemy(Ray ray)
{
// shoot a thin, big cylinder that only hits enemies to see if we can autoaim
List<SceneTraceResult> traces = Scene.Trace.Cylinder( 200f, 10f, ray, 5000f )
.WithAnyTags( "enemy" )
.UseHitPosition( true )
.RunAll()
.ToList();
/*
Breakpoints = [];
XhRay = ray;
CyanLines = [];
GreenLines = [];
RedLines = [];
*/
/*
while (true)
{
var incomplete = Scene.Trace.Cylinder( 200f, 10f, ray, 5000f )
.WithAnyTags( "enemy" )
.UseHitPosition( true );
foreach ( var otherTrace in traces )
{
incomplete = incomplete.IgnoreGameObject( otherTrace.GameObject );
}
var trace = incomplete.Run();
if ( !trace.Hit ) break;
traces.Add( trace );
}
*/
if ( traces.Count == 0 ) return null;
var tracesWithDistances =
traces.Select( trace =>
{
// find out how far from the crosshair we are
var breakPoint = ray.Project( trace.Distance );
var distance = breakPoint.DistanceSquared( trace.HitPosition );
return (trace, distance, breakPoint);
} ).ToList();
tracesWithDistances.Sort( ( a, b ) => a.Item2.CompareTo( b.Item2 ) );
// SceneTraceResult? best = null;
foreach (var trace in tracesWithDistances)
{
// Breakpoints.Add( trace.breakPoint );
// Log.Info( $"distance {trace.Item2} hit {trace.trace.GameObject.Name}" );
// if ( best.HasValue ) continue;
// we want to hit it as close to the crosshair as possible
var traceFromBreakPoint = Scene.Trace.Ray( trace.breakPoint, trace.trace.GameObject.WorldPosition + new Vector3(0, 0, 8) )
.WithTag( "enemy" )
.Run();
if ( !traceFromBreakPoint.Hit ) continue;
// CyanLines.Add( new Line( traceFromBreakPoint.StartPosition, traceFromBreakPoint.HitPosition ) );
// Log.Info( $"brp {traceFromBreakPoint.HitPosition}" );
// now, make sure we can actually hit that
var directTrace = Scene.Trace.Ray(ray.Position, traceFromBreakPoint.HitPosition)
.WithoutTags( "player" )
.UseHitPosition( true )
.IgnoreGameObject(trace.trace.GameObject)
.Run();
if ( directTrace.Hit )
{
// Log.Info( "direct trace hit something else" );
// RedLines.Add( new Line( ray.Position, traceFromBreakPoint.HitPosition ) );
continue;
}
// GreenLines.Add( new Line( ray.Position, traceFromBreakPoint.HitPosition ) );
var tr = trace.trace;
tr.HitPosition = traceFromBreakPoint.HitPosition;
return tr;
}
return null;
}
void Shoot()
{
var playedSound = false;
foreach ( var player in Scene.GetAll<PlayerController>() )
{
if ( !player.GetComponent<Health>().IsAlive ) continue;
if ( !playedSound )
{
Sound.Play( FireSound, player.WorldPosition );
playedSound = true;
}
var cam = player.GetComponentInChildren<CameraComponent>();
var pitch = 0f;
var sideToSide = Rotation.FromYaw( Game.Random.Float( -1, 1 ) );
var ray = new Ray( cam.WorldPosition, (cam.WorldRotation * sideToSide).Forward );
var trace = MostFittingEnemy( ray );
// hitPosition = trace;
if (!trace.HasValue)
{
// if we didn't hit an enemy, just shoot straight forwards for wall impacts
trace = Scene.Trace.Ray( ray, 5000f )
.WithoutTags( "player" )
.UseHitPosition( true )
.Run();
}
IDamageable damageable = null;
if ( trace.Value.Hit ) damageable = trace.Value.GameObject.Components.Get<IDamageable>();
// TODO
player.Components.GetAll<SkinnedModelRenderer>().First( sm => sm.GameObject.Name == "v_mp5" ).Set( "aim_pitch_inertia", pitch );
if ( damageable is not null )
{
damageable.OnDamage( new DamageInfo()
{
Damage = Damage,
Attacker = GameObject,
Position = trace.Value.HitPosition,
} );
}
if ( !trace.HasValue || !trace.Value.Hit ) return;
if (TempImpact is not null)
{
var impact = TempImpact.Clone( trace.Value.HitPosition, Rotation.LookAt( trace.Value.Normal ) );
impact.SetParent( trace.Value.GameObject );
impact.AddComponent<TemporaryEffect>().DestroyAfterSeconds = 120;
}
else
{
var prefab = trace.Value.Surface.PrefabCollection.BulletImpact;
if ( prefab is not null )
{
var impact = prefab.Clone( trace.Value.HitPosition, trace.Value.Normal.EulerAngles.ToRotation().Inverse );
impact.SetParent( trace.Value.GameObject );
impact.AddComponent<TemporaryEffect>().DestroyAfterSeconds = 120;
}
if ( trace.Value.Surface.SoundCollection.Bullet is not null ) Sound.Play( trace.Value.Surface.SoundCollection.Bullet, trace.Value.HitPosition );
}
/*
var impact = new GameObject(trace.GameObject, false);
var particle = impact.AddComponent<LegacyParticleSystem>();
Log.Info( trace.Surface.ImpactEffects.Bullet[0] );
var system = ParticleSystem.Load( trace.Surface.ImpactEffects.Bullet[0] );
Log.Info( system );
particle.Particles = system;
impact.WorldPosition = trace.HitPosition;
impact.WorldRotation = Rotation.LookAt( trace.Normal );
impact.AddComponent<TemporaryEffect>().DestroyAfterSeconds = 120;
impact.Enabled = true;
*/
}
}
void DrawReticules()
{
if ( Crosshair is null || !Crosshair.IsValid ) return;
foreach ( var cam in Cameras )
{
var hud = cam.Hud;
var screenRect = cam.ScreenRect;
hud.DrawTexture( Crosshair, new Rect( -40, -20, 80, 40 ) + screenRect.Center, Color.FromRgba( 0xFFFFFF50 ) );
}
}
}