Add files via upload

This commit is contained in:
celisej567
2021-08-05 17:54:51 +03:00
committed by GitHub
parent d58ad89d11
commit 76b55e2a87
57 changed files with 5632 additions and 0 deletions

123
code/tools/Balloon.cs Normal file
View File

@@ -0,0 +1,123 @@
namespace Sandbox.Tools
{
[Library( "tool_balloon", Title = "Balloons", Description = "Create Balloons!", Group = "construction" )]
public partial class BalloonTool : BaseTool
{
[Net]
public Color32 Tint { get; set; }
PreviewEntity previewModel;
public override void Activate()
{
base.Activate();
if ( Host.IsServer )
{
Tint = Color.Random.ToColor32();
}
}
protected override bool IsPreviewTraceValid( TraceResult tr )
{
if ( !base.IsPreviewTraceValid( tr ) )
return false;
if ( tr.Entity is BalloonEntity )
return false;
return true;
}
public override void CreatePreviews()
{
if ( TryCreatePreview( ref previewModel, "models/citizen_props/balloonregular01.vmdl" ) )
{
previewModel.RelativeToNormal = false;
}
}
public override void Simulate()
{
if ( previewModel.IsValid() )
{
previewModel.RenderColor = Tint;
}
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
bool useRope = Input.Pressed( InputButton.Attack1 );
if ( !useRope && !Input.Pressed( InputButton.Attack2 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit )
return;
if ( !tr.Entity.IsValid() )
return;
CreateHitEffects( tr.EndPos );
if ( tr.Entity is BalloonEntity )
return;
var ent = new BalloonEntity
{
Position = tr.EndPos,
};
ent.SetModel( "models/citizen_props/balloonregular01.vmdl" );
ent.PhysicsBody.GravityScale = -0.2f;
ent.RenderColor = Tint;
Tint = Color.Random.ToColor32();
if ( !useRope )
return;
var rope = Particles.Create( "particles/rope.vpcf" );
rope.SetEntity( 0, ent );
var attachEnt = tr.Body.IsValid() ? tr.Body.Entity : tr.Entity;
var attachLocalPos = tr.Body.Transform.PointToLocal( tr.EndPos ) * (1.0f / tr.Entity.Scale);
if ( attachEnt.IsWorld )
{
rope.SetPosition( 1, attachLocalPos );
}
else
{
rope.SetEntityBone( 1, attachEnt, tr.Bone, new Transform( attachLocalPos ) );
}
var spring = PhysicsJoint.Spring
.From( ent.PhysicsBody )
.To( tr.Body, tr.Body.Transform.PointToLocal( tr.EndPos ) )
.WithFrequency( 5.0f )
.WithDampingRatio( 0.7f )
.WithReferenceMass( ent.PhysicsBody.Mass )
.WithMinRestLength( 0 )
.WithMaxRestLength( 100 )
.WithCollisionsEnabled()
.Create();
spring.EnableAngularConstraint = false;
spring.OnBreak( () =>
{
rope?.Destroy( true );
spring.Remove();
} );
}
}
}
}

38
code/tools/BoxShooter.cs Normal file
View File

@@ -0,0 +1,38 @@
namespace Sandbox.Tools
{
[Library( "tool_boxgun", Title = "Box Shooter", Description = "Shoot boxes", Group = "fun" )]
public class BoxShooter : BaseTool
{
TimeSince timeSinceShoot;
public override void Simulate()
{
if ( Host.IsServer )
{
if ( Input.Pressed( InputButton.Attack1 ) )
{
ShootBox();
}
if ( Input.Down( InputButton.Attack2 ) && timeSinceShoot > 0.05f )
{
timeSinceShoot = 0;
ShootBox();
}
}
}
void ShootBox()
{
var ent = new Prop
{
Position = Owner.EyePos + Owner.EyeRot.Forward * 50,
Rotation = Owner.EyeRot
};
ent.SetModel( "models/citizen_props/crate01.vmdl" );
ent.Velocity = Owner.EyeRot.Forward * 1000;
}
}
}

38
code/tools/Color.cs Normal file
View File

@@ -0,0 +1,38 @@
using System;
namespace Sandbox.Tools
{
[Library( "tool_color", Title = "Color", Description = "Change render color and alpha of entities", Group = "construction" )]
public partial class ColorTool : BaseTool
{
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
if ( !Input.Pressed( InputButton.Attack1 ) ) return;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.UseHitboxes()
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
if ( tr.Entity is not ModelEntity modelEnt )
return;
modelEnt.RenderColor = Color.Random.ToColor32();
CreateHitEffects( tr.EndPos );
}
}
}
}

280
code/tools/GravGun.cs Normal file
View File

@@ -0,0 +1,280 @@
using Sandbox;
using Sandbox.Joints;
using System;
using System.Linq;
[Library( "gravgun" )]
public partial class GravGun : Carriable
{
public override string ViewModelPath => "weapons/rust_pistol/v_rust_pistol.vmdl";
private PhysicsBody holdBody;
private WeldJoint holdJoint;
public PhysicsBody HeldBody { get; private set; }
public Rotation HeldRot { get; private set; }
public ModelEntity HeldEntity { get; private set; }
protected virtual float MaxPullDistance => 2000.0f;
protected virtual float MaxPushDistance => 500.0f;
protected virtual float LinearFrequency => 10.0f;
protected virtual float LinearDampingRatio => 1.0f;
protected virtual float AngularFrequency => 10.0f;
protected virtual float AngularDampingRatio => 1.0f;
protected virtual float PullForce => 20.0f;
protected virtual float PushForce => 1000.0f;
protected virtual float ThrowForce => 2000.0f;
protected virtual float HoldDistance => 100.0f;
protected virtual float AttachDistance => 150.0f;
protected virtual float DropCooldown => 0.5f;
protected virtual float BreakLinearForce => 2000.0f;
private TimeSince timeSinceDrop;
public override void Spawn()
{
base.Spawn();
SetModel( "weapons/rust_pistol/rust_pistol.vmdl" );
CollisionGroup = CollisionGroup.Weapon;
SetInteractsAs( CollisionLayer.Debris );
}
public override void Simulate( Client client )
{
if ( Owner is not Player owner ) return;
if ( !IsServer )
return;
using ( Prediction.Off() )
{
var eyePos = owner.EyePos;
var eyeRot = owner.EyeRot;
var eyeDir = owner.EyeRot.Forward;
if ( HeldBody.IsValid() && HeldBody.PhysicsGroup != null )
{
if ( holdJoint.IsValid && !holdJoint.IsActive )
{
GrabEnd();
}
else if ( Input.Pressed( InputButton.Attack1 ) )
{
if ( HeldBody.PhysicsGroup.BodyCount > 1 )
{
// Don't throw ragdolls as hard
HeldBody.PhysicsGroup.ApplyImpulse( eyeDir * (ThrowForce * 0.5f), true );
HeldBody.PhysicsGroup.ApplyAngularImpulse( Vector3.Random * ThrowForce, true );
}
else
{
HeldBody.ApplyImpulse( eyeDir * (HeldBody.Mass * ThrowForce) );
HeldBody.ApplyAngularImpulse( Vector3.Random * (HeldBody.Mass * ThrowForce) );
}
GrabEnd();
}
else if ( Input.Pressed( InputButton.Attack2 ) )
{
timeSinceDrop = 0;
GrabEnd();
}
else
{
GrabMove( eyePos, eyeDir, eyeRot );
}
return;
}
if ( timeSinceDrop < DropCooldown )
return;
var tr = Trace.Ray( eyePos, eyePos + eyeDir * MaxPullDistance )
.UseHitboxes()
.Ignore( owner, false )
.Radius( 2.0f )
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit || !tr.Body.IsValid() || !tr.Entity.IsValid() || tr.Entity.IsWorld )
return;
if ( tr.Entity.PhysicsGroup == null )
return;
var modelEnt = tr.Entity as ModelEntity;
if ( !modelEnt.IsValid() )
return;
var body = tr.Body;
if ( Input.Pressed( InputButton.Attack1 ) )
{
if ( tr.Distance < MaxPushDistance && !IsBodyGrabbed( body ) )
{
var pushScale = 1.0f - Math.Clamp( tr.Distance / MaxPushDistance, 0.0f, 1.0f );
body.ApplyImpulseAt( tr.EndPos, eyeDir * (body.Mass * (PushForce * pushScale)) );
}
}
else if ( Input.Down( InputButton.Attack2 ) )
{
var physicsGroup = tr.Entity.PhysicsGroup;
if ( physicsGroup.BodyCount > 1 )
{
body = modelEnt.PhysicsBody;
if ( !body.IsValid() )
return;
}
if ( eyePos.Distance( body.Position ) <= AttachDistance )
{
GrabStart( modelEnt, body, eyePos + eyeDir * HoldDistance, eyeRot );
}
else if ( !IsBodyGrabbed( body ) )
{
physicsGroup.ApplyImpulse( eyeDir * -PullForce, true );
}
}
}
}
private void Activate()
{
if ( !holdBody.IsValid() )
{
holdBody = new PhysicsBody
{
BodyType = PhysicsBodyType.Keyframed
};
}
}
private void Deactivate()
{
GrabEnd();
holdBody?.Remove();
holdBody = null;
}
public override void ActiveStart( Entity ent )
{
base.ActiveStart( ent );
if ( IsServer )
{
Activate();
}
}
public override void ActiveEnd( Entity ent, bool dropped )
{
base.ActiveEnd( ent, dropped );
if ( IsServer )
{
Deactivate();
}
}
protected override void OnDestroy()
{
base.OnDestroy();
if ( IsServer )
{
Deactivate();
}
}
public override void OnCarryDrop( Entity dropper )
{
}
private static bool IsBodyGrabbed( PhysicsBody body )
{
// There for sure is a better way to deal with this
if ( All.OfType<PhysGun>().Any( x => x?.HeldBody?.PhysicsGroup == body?.PhysicsGroup ) ) return true;
if ( All.OfType<GravGun>().Any( x => x?.HeldBody?.PhysicsGroup == body?.PhysicsGroup ) ) return true;
return false;
}
private void GrabStart( ModelEntity entity, PhysicsBody body, Vector3 grabPos, Rotation grabRot )
{
if ( !body.IsValid() )
return;
if ( body.PhysicsGroup == null )
return;
if ( IsBodyGrabbed( body ) )
return;
GrabEnd();
HeldBody = body;
HeldRot = grabRot.Inverse * HeldBody.Rotation;
holdBody.Position = grabPos;
holdBody.Rotation = HeldBody.Rotation;
HeldBody.Wake();
HeldBody.EnableAutoSleeping = false;
holdJoint = PhysicsJoint.Weld
.From( holdBody )
.To( HeldBody, HeldBody.LocalMassCenter )
.WithLinearSpring( LinearFrequency, LinearDampingRatio, 0.0f )
.WithAngularSpring( AngularFrequency, AngularDampingRatio, 0.0f )
.Breakable( HeldBody.Mass * BreakLinearForce, 0 )
.Create();
HeldEntity = entity;
var client = GetClientOwner();
client?.Pvs.Add( HeldEntity );
}
private void GrabEnd()
{
if ( holdJoint.IsValid )
{
holdJoint.Remove();
}
if ( HeldBody.IsValid() )
{
HeldBody.EnableAutoSleeping = true;
}
if ( HeldEntity.IsValid() )
{
var client = GetClientOwner();
client?.Pvs.Remove( HeldEntity );
}
HeldBody = null;
HeldRot = Rotation.Identity;
HeldEntity = null;
}
private void GrabMove( Vector3 startPos, Vector3 dir, Rotation rot )
{
if ( !HeldBody.IsValid() )
return;
holdBody.Position = startPos + dir * HoldDistance;
holdBody.Rotation = rot * HeldRot;
}
public override bool IsUsable( Entity user )
{
return Owner == null || HeldBody.IsValid();
}
}

83
code/tools/Lamp.cs Normal file
View File

@@ -0,0 +1,83 @@
namespace Sandbox.Tools
{
[Library( "tool_lamp", Title = "Lamps", Description = "Directional light source that casts shadows", Group = "construction" )]
public partial class LampTool : BaseTool
{
PreviewEntity previewModel;
private string Model => "models/torch/torch.vmdl";
protected override bool IsPreviewTraceValid( TraceResult tr )
{
if ( !base.IsPreviewTraceValid( tr ) )
return false;
if ( tr.Entity is LampEntity )
return false;
return true;
}
public override void CreatePreviews()
{
if ( TryCreatePreview( ref previewModel, Model ) )
{
previewModel.RelativeToNormal = false;
previewModel.OffsetBounds = true;
previewModel.PositionOffset = -previewModel.CollisionBounds.Center;
}
}
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
if ( !Input.Pressed( InputButton.Attack1 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
CreateHitEffects( tr.EndPos );
if ( tr.Entity is LampEntity lamp )
{
// TODO: Set properties
lamp.Flicker = !lamp.Flicker;
return;
}
lamp = new LampEntity
{
Enabled = true,
DynamicShadows = true,
Range = 512,
Falloff = 1.0f,
LinearAttenuation = 0.0f,
QuadraticAttenuation = 1.0f,
InnerConeAngle = 25,
OuterConeAngle = 45,
Brightness = 10,
Color = Color.Random,
Rotation = Rotation.Identity
};
lamp.SetModel( Model );
lamp.SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
lamp.Position = tr.EndPos + -lamp.CollisionBounds.Center + tr.Normal * lamp.CollisionBounds.Size * 0.5f;
}
}
}
}

57
code/tools/LeafBlower.cs Normal file
View File

@@ -0,0 +1,57 @@
namespace Sandbox.Tools
{
[Library( "tool_leafblower", Title = "Leaf Blower", Description = "Blow me", Group = "fun" )]
public partial class LeafBlowerTool : BaseTool
{
protected virtual float Force => 128;
protected virtual float MaxDistance => 512;
protected virtual bool Massless => true;
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
bool push = Input.Down( InputButton.Attack1 );
if ( !push && !Input.Down( InputButton.Attack2 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit )
return;
if ( !tr.Entity.IsValid() )
return;
if ( tr.Entity.IsWorld )
return;
var body = tr.Body;
if ( !body.IsValid() )
return;
var direction = tr.EndPos - tr.StartPos;
var distance = direction.Length;
var ratio = (1.0f - (distance / MaxDistance)).Clamp( 0, 1 ) * (push ? 1.0f : -1.0f);
var force = direction * (Force * ratio);
if ( Massless )
{
force *= body.Mass;
}
body.ApplyForceAt( tr.EndPos, force );
}
}
}
}

116
code/tools/Light.cs Normal file
View File

@@ -0,0 +1,116 @@
namespace Sandbox.Tools
{
[Library( "tool_light", Title = "Lights", Description = "A dynamic point light", Group = "construction" )]
public partial class LightTool : BaseTool
{
PreviewEntity previewModel;
private string Model => "models/light/light_tubular.vmdl";
protected override bool IsPreviewTraceValid( TraceResult tr )
{
if ( !base.IsPreviewTraceValid( tr ) )
return false;
if ( tr.Entity is LightEntity )
return false;
return true;
}
public override void CreatePreviews()
{
if ( TryCreatePreview( ref previewModel, Model ) )
{
previewModel.RelativeToNormal = false;
previewModel.OffsetBounds = true;
previewModel.PositionOffset = -previewModel.CollisionBounds.Center;
}
}
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
bool useRope = Input.Pressed( InputButton.Attack1 );
if ( !useRope && !Input.Pressed( InputButton.Attack2 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
CreateHitEffects( tr.EndPos );
if ( tr.Entity is LightEntity )
{
// TODO: Set properties
return;
}
var light = new LightEntity
{
Enabled = true,
DynamicShadows = false,
Range = 128,
Falloff = 1.0f,
LinearAttenuation = 0.0f,
QuadraticAttenuation = 1.0f,
Brightness = 1,
Color = Color.Random,
};
light.UseFogNoShadows();
light.SetModel( Model );
light.SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
light.Position = tr.EndPos + -light.CollisionBounds.Center + tr.Normal * light.CollisionBounds.Size * 0.5f;
if ( !useRope )
return;
var rope = Particles.Create( "particles/rope.vpcf" );
rope.SetEntity( 0, light, Vector3.Down * 6.5f ); // Should be an attachment point
var attachEnt = tr.Body.IsValid() ? tr.Body.Entity : tr.Entity;
var attachLocalPos = tr.Body.Transform.PointToLocal( tr.EndPos ) * (1.0f / tr.Entity.Scale);
if ( attachEnt.IsWorld )
{
rope.SetPosition( 1, attachLocalPos );
}
else
{
rope.SetEntityBone( 1, attachEnt, tr.Bone, new Transform( attachLocalPos ) );
}
var spring = PhysicsJoint.Spring
.From( light.PhysicsBody, Vector3.Down * 6.5f )
.To( tr.Body, tr.Body.Transform.PointToLocal( tr.EndPos ) )
.WithFrequency( 5.0f )
.WithDampingRatio( 0.7f )
.WithReferenceMass( light.PhysicsBody.Mass )
.WithMinRestLength( 0 )
.WithMaxRestLength( 100 )
.WithCollisionsEnabled()
.Create();
spring.EnableAngularConstraint = false;
spring.OnBreak( () =>
{
rope?.Destroy( true );
spring.Remove();
} );
}
}
}
}

View File

@@ -0,0 +1,124 @@
using Sandbox;
using System.Linq;
public partial class PhysGun
{
Particles Beam;
Particles EndNoHit;
Vector3 lastBeamPos;
ModelEntity lastGrabbedEntity;
[Event.Frame]
public void OnFrame()
{
UpdateEffects();
}
protected virtual void KillEffects()
{
Beam?.Destroy( true );
Beam = null;
BeamActive = false;
EndNoHit?.Destroy( false );
EndNoHit = null;
if ( lastGrabbedEntity.IsValid() )
{
foreach ( var child in lastGrabbedEntity.Children.OfType<ModelEntity>() )
{
if ( child is Player )
continue;
child.GlowActive = false;
child.GlowState = GlowStates.GlowStateOff;
}
lastGrabbedEntity.GlowActive = false;
lastGrabbedEntity.GlowState = GlowStates.GlowStateOff;
lastGrabbedEntity = null;
}
}
protected virtual void UpdateEffects()
{
var owner = Owner;
if ( owner == null || !BeamActive || !IsActiveChild() )
{
KillEffects();
return;
}
var startPos = owner.EyePos;
var dir = owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTargetDistance )
.UseHitboxes()
.Ignore( owner )
.Run();
if ( Beam == null )
{
Beam = Particles.Create( "particles/physgun_beam.vpcf", tr.EndPos );
}
Beam.SetEntityAttachment( 0, EffectEntity, "muzzle", true );
if ( GrabbedEntity.IsValid() && !GrabbedEntity.IsWorld )
{
var physGroup = GrabbedEntity.PhysicsGroup;
if ( physGroup != null && GrabbedBone >= 0 )
{
var physBody = physGroup.GetBody( GrabbedBone );
if ( physBody != null )
{
Beam.SetPosition( 1, physBody.Transform.PointToWorld( GrabbedPos ) );
}
}
else
{
Beam.SetEntity( 1, GrabbedEntity, GrabbedPos, true );
}
lastBeamPos = GrabbedEntity.Position + GrabbedEntity.Rotation * GrabbedPos;
EndNoHit?.Destroy( false );
EndNoHit = null;
if ( GrabbedEntity is ModelEntity modelEnt )
{
lastGrabbedEntity = modelEnt;
modelEnt.GlowState = GlowStates.GlowStateOn;
modelEnt.GlowDistanceStart = 0;
modelEnt.GlowDistanceEnd = 1000;
modelEnt.GlowColor = new Color( 0.1f, 1.0f, 1.0f, 1.0f );
modelEnt.GlowActive = true;
foreach ( var child in lastGrabbedEntity.Children.OfType<ModelEntity>() )
{
if ( child is Player )
continue;
child.GlowState = GlowStates.GlowStateOn;
child.GlowDistanceStart = 0;
child.GlowDistanceEnd = 1000;
child.GlowColor = new Color( 0.1f, 1.0f, 1.0f, 1.0f );
child.GlowActive = true;
}
}
}
else
{
lastBeamPos = tr.EndPos;// Vector3.Lerp( lastBeamPos, tr.EndPos, Time.Delta * 10 );
Beam.SetPosition( 1, lastBeamPos );
if ( EndNoHit == null )
EndNoHit = Particles.Create( "particles/physgun_end_nohit.vpcf", lastBeamPos );
EndNoHit.SetPosition( 0, lastBeamPos );
}
}
}

419
code/tools/PhysGun.cs Normal file
View File

@@ -0,0 +1,419 @@
using Sandbox;
using Sandbox.Joints;
using System;
using System.Linq;
[Library( "physgun" )]
public partial class PhysGun : Carriable
{
public override string ViewModelPath => "weapons/rust_pistol/v_rust_pistol.vmdl";
protected PhysicsBody holdBody;
protected WeldJoint holdJoint;
protected PhysicsBody heldBody;
protected Vector3 heldPos;
protected Rotation heldRot;
protected float holdDistance;
protected bool grabbing;
protected virtual float MinTargetDistance => 0.0f;
protected virtual float MaxTargetDistance => 10000.0f;
protected virtual float LinearFrequency => 20.0f;
protected virtual float LinearDampingRatio => 1.0f;
protected virtual float AngularFrequency => 20.0f;
protected virtual float AngularDampingRatio => 1.0f;
protected virtual float TargetDistanceSpeed => 50.0f;
protected virtual float RotateSpeed => 0.2f;
protected virtual float RotateSnapAt => 45.0f;
[Net] public bool BeamActive { get; set; }
[Net] public Entity GrabbedEntity { get; set; }
[Net] public int GrabbedBone { get; set; }
[Net] public Vector3 GrabbedPos { get; set; }
public PhysicsBody HeldBody => heldBody;
public override void Spawn()
{
base.Spawn();
SetModel( "weapons/rust_pistol/rust_pistol.vmdl" );
CollisionGroup = CollisionGroup.Weapon;
SetInteractsAs( CollisionLayer.Debris );
}
public override void Simulate( Client client )
{
if ( Owner is not Player owner ) return;
var eyePos = owner.EyePos;
var eyeDir = owner.EyeRot.Forward;
var eyeRot = Rotation.From( new Angles( 0.0f, owner.EyeRot.Angles().yaw, 0.0f ) );
if ( Input.Pressed( InputButton.Attack1 ) )
{
(Owner as AnimEntity)?.SetAnimBool( "b_attack", true );
if ( !grabbing )
grabbing = true;
}
bool grabEnabled = grabbing && Input.Down( InputButton.Attack1 );
bool wantsToFreeze = Input.Pressed( InputButton.Attack2 );
if ( GrabbedEntity.IsValid() && wantsToFreeze )
{
(Owner as AnimEntity)?.SetAnimBool( "b_attack", true );
}
BeamActive = grabEnabled;
if ( IsServer )
{
using ( Prediction.Off() )
{
if ( !holdBody.IsValid() )
return;
if ( grabEnabled )
{
if ( heldBody.IsValid() )
{
UpdateGrab( eyePos, eyeRot, eyeDir, wantsToFreeze );
}
else
{
TryStartGrab( owner, eyePos, eyeRot, eyeDir );
}
}
else if ( grabbing )
{
GrabEnd();
}
if ( !grabbing && Input.Pressed( InputButton.Reload ) )
{
TryUnfreezeAll( owner, eyePos, eyeRot, eyeDir );
}
}
}
if ( BeamActive )
{
Input.MouseWheel = 0;
}
}
private static bool IsBodyGrabbed( PhysicsBody body )
{
// There for sure is a better way to deal with this
if ( All.OfType<PhysGun>().Any( x => x?.HeldBody?.PhysicsGroup == body?.PhysicsGroup ) ) return true;
if ( All.OfType<GravGun>().Any( x => x?.HeldBody?.PhysicsGroup == body?.PhysicsGroup ) ) return true;
return false;
}
private void TryUnfreezeAll( Player owner, Vector3 eyePos, Rotation eyeRot, Vector3 eyeDir )
{
var tr = Trace.Ray( eyePos, eyePos + eyeDir * MaxTargetDistance )
.UseHitboxes()
.Ignore( owner, false )
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() || tr.Entity.IsWorld ) return;
var rootEnt = tr.Entity.Root;
if ( !rootEnt.IsValid() ) return;
var physicsGroup = rootEnt.PhysicsGroup;
if ( physicsGroup == null ) return;
bool unfrozen = false;
for ( int i = 0; i < physicsGroup.BodyCount; ++i )
{
var body = physicsGroup.GetBody( i );
if ( !body.IsValid() ) continue;
if ( body.BodyType == PhysicsBodyType.Static )
{
body.BodyType = PhysicsBodyType.Dynamic;
unfrozen = true;
}
}
if ( unfrozen )
{
var freezeEffect = Particles.Create( "particles/physgun_freeze.vpcf" );
freezeEffect.SetPosition( 0, tr.EndPos );
}
}
private void TryStartGrab( Player owner, Vector3 eyePos, Rotation eyeRot, Vector3 eyeDir )
{
var tr = Trace.Ray( eyePos, eyePos + eyeDir * MaxTargetDistance )
.UseHitboxes()
.Ignore( owner, false )
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() || !tr.Body.IsValid() || tr.Entity.IsWorld ) return;
var rootEnt = tr.Entity.Root;
var body = tr.Body;
if ( tr.Entity.Parent.IsValid() )
{
if ( rootEnt.IsValid() && rootEnt.PhysicsGroup != null )
{
body = rootEnt.PhysicsGroup.GetBody( 0 );
}
}
if ( !body.IsValid() )
return;
//
// Don't move keyframed
//
if ( body.BodyType == PhysicsBodyType.Keyframed )
return;
// Unfreeze
if ( body.BodyType == PhysicsBodyType.Static )
{
body.BodyType = PhysicsBodyType.Dynamic;
}
if ( IsBodyGrabbed( body ) )
return;
GrabInit( body, eyePos, tr.EndPos, eyeRot );
GrabbedEntity = rootEnt;
GrabbedPos = body.Transform.PointToLocal( tr.EndPos );
GrabbedBone = tr.Entity.PhysicsGroup.GetBodyIndex( body );
var client = GetClientOwner();
if ( client != null )
{
client.Pvs.Add( GrabbedEntity );
}
}
private void UpdateGrab( Vector3 eyePos, Rotation eyeRot, Vector3 eyeDir, bool wantsToFreeze )
{
if ( wantsToFreeze )
{
heldBody.BodyType = PhysicsBodyType.Static;
if ( GrabbedEntity.IsValid() )
{
var freezeEffect = Particles.Create( "particles/physgun_freeze.vpcf" );
freezeEffect.SetPosition( 0, heldBody.Transform.PointToWorld( GrabbedPos ) );
}
GrabEnd();
return;
}
MoveTargetDistance( Input.MouseWheel * TargetDistanceSpeed );
bool rotating = Input.Down( InputButton.Use );
bool snapping = false;
if ( rotating )
{
EnableAngularSpring( Input.Down( InputButton.Run ) ? 100.0f : 0.0f );
DoRotate( eyeRot, Input.MouseDelta * RotateSpeed );
snapping = Input.Down( InputButton.Run );
}
else
{
DisableAngularSpring();
}
GrabMove( eyePos, eyeDir, eyeRot, snapping );
}
private void EnableAngularSpring( float scale )
{
if ( holdJoint.IsValid )
{
holdJoint.AngularDampingRatio = AngularDampingRatio * scale;
holdJoint.AngularFrequency = AngularFrequency * scale;
}
}
private void DisableAngularSpring()
{
if ( holdJoint.IsValid )
{
holdJoint.AngularDampingRatio = 0.0f;
holdJoint.AngularFrequency = 0.0f;
}
}
private void Activate()
{
if ( !IsServer )
return;
if ( !holdBody.IsValid() )
{
holdBody = new PhysicsBody
{
BodyType = PhysicsBodyType.Keyframed
};
}
}
private void Deactivate()
{
if ( IsServer )
{
GrabEnd();
holdBody?.Remove();
holdBody = null;
}
KillEffects();
}
public override void ActiveStart( Entity ent )
{
base.ActiveStart( ent );
Activate();
}
public override void ActiveEnd( Entity ent, bool dropped )
{
base.ActiveEnd( ent, dropped );
Deactivate();
}
protected override void OnDestroy()
{
base.OnDestroy();
Deactivate();
}
public override void OnCarryDrop( Entity dropper )
{
}
private void GrabInit( PhysicsBody body, Vector3 startPos, Vector3 grabPos, Rotation rot )
{
if ( !body.IsValid() )
return;
GrabEnd();
grabbing = true;
heldBody = body;
holdDistance = Vector3.DistanceBetween( startPos, grabPos );
holdDistance = holdDistance.Clamp( MinTargetDistance, MaxTargetDistance );
heldPos = heldBody.Transform.PointToLocal( grabPos );
heldRot = rot.Inverse * heldBody.Rotation;
holdBody.Position = grabPos;
holdBody.Rotation = heldBody.Rotation;
heldBody.Wake();
heldBody.EnableAutoSleeping = false;
holdJoint = PhysicsJoint.Weld
.From( holdBody )
.To( heldBody, heldPos )
.WithLinearSpring( LinearFrequency, LinearDampingRatio, 0.0f )
.WithAngularSpring( 0.0f, 0.0f, 0.0f )
.Create();
}
private void GrabEnd()
{
if ( holdJoint.IsValid )
{
holdJoint.Remove();
}
if ( heldBody.IsValid() )
{
heldBody.EnableAutoSleeping = true;
}
var client = GetClientOwner();
if ( client != null && GrabbedEntity.IsValid() )
{
client.Pvs.Remove( GrabbedEntity );
}
heldBody = null;
GrabbedEntity = null;
grabbing = false;
}
private void GrabMove( Vector3 startPos, Vector3 dir, Rotation rot, bool snapAngles )
{
if ( !heldBody.IsValid() )
return;
holdBody.Position = startPos + dir * holdDistance;
holdBody.Rotation = rot * heldRot;
if ( snapAngles )
{
var angles = holdBody.Rotation.Angles();
holdBody.Rotation = Rotation.From(
MathF.Round( angles.pitch / RotateSnapAt ) * RotateSnapAt,
MathF.Round( angles.yaw / RotateSnapAt ) * RotateSnapAt,
MathF.Round( angles.roll / RotateSnapAt ) * RotateSnapAt
);
}
}
private void MoveTargetDistance( float distance )
{
holdDistance += distance;
holdDistance = holdDistance.Clamp( MinTargetDistance, MaxTargetDistance );
}
protected virtual void DoRotate( Rotation eye, Vector3 input )
{
var localRot = eye;
localRot *= Rotation.FromAxis( Vector3.Up, input.x );
localRot *= Rotation.FromAxis( Vector3.Right, input.y );
localRot = eye.Inverse * localRot;
heldRot = localRot * heldRot;
}
public override void BuildInput( InputBuilder owner )
{
if ( !GrabbedEntity.IsValid() )
return;
if ( !owner.Down( InputButton.Attack1 ) )
return;
if ( owner.Down( InputButton.Use ) )
{
owner.ViewAngles = owner.OriginalViewAngles;
}
}
public override bool IsUsable( Entity user )
{
return Owner == null || HeldBody.IsValid();
}
}

42
code/tools/Remover.cs Normal file
View File

@@ -0,0 +1,42 @@
namespace Sandbox.Tools
{
[Library( "tool_remover", Title = "Remover", Description = "Remove entities", Group = "construction" )]
public partial class RemoverTool : BaseTool
{
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
if ( !Input.Pressed( InputButton.Attack1 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() )
return;
if ( tr.Entity is Player )
return;
CreateHitEffects( tr.EndPos );
if ( tr.Entity.IsWorld )
return;
tr.Entity.Delete();
var particle = Particles.Create( "particles/physgun_freeze.vpcf" );
particle.SetPosition( 0, tr.Entity.Position );
}
}
}
}

54
code/tools/Resizer.cs Normal file
View File

@@ -0,0 +1,54 @@
using System;
namespace Sandbox.Tools
{
[Library( "tool_resizer", Title = "Resizer", Description = "Change the scale of things", Group = "construction" )]
public partial class ResizerTool : BaseTool
{
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
int resizeDir = 0;
var reset = false;
if ( Input.Down( InputButton.Attack1 ) ) resizeDir = 1;
else if ( Input.Down( InputButton.Attack2 ) ) resizeDir = -1;
else if ( Input.Pressed( InputButton.Reload ) ) reset = true;
else return;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.UseHitboxes()
.HitLayer( CollisionLayer.Debris )
.Run();
if ( !tr.Hit || !tr.Entity.IsValid() || tr.Entity.PhysicsGroup == null )
return;
// Disable resizing lights for now
if ( tr.Entity is LightEntity || tr.Entity is LampEntity )
return;
var scale = reset ? 1.0f : Math.Clamp( tr.Entity.Scale + ((0.5f * Time.Delta) * resizeDir), 0.4f, 4.0f );
if ( tr.Entity.Scale != scale )
{
tr.Entity.Scale = scale;
tr.Entity.PhysicsGroup.RebuildMass();
tr.Entity.PhysicsGroup.Wake();
}
if ( Input.Pressed( InputButton.Attack1 ) || Input.Pressed( InputButton.Attack2 ) || reset )
{
CreateHitEffects( tr.EndPos );
}
}
}
}
}

122
code/tools/Rope.cs Normal file
View File

@@ -0,0 +1,122 @@
namespace Sandbox.Tools
{
[Library( "tool_rope", Title = "Rope", Description = "Join two things together with a rope", Group = "construction" )]
public partial class RopeTool : BaseTool
{
private PhysicsBody targetBody;
private int targetBone;
private Vector3 localOrigin1;
private Vector3 globalOrigin1;
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
if ( !Input.Pressed( InputButton.Attack1 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit )
return;
if ( !tr.Body.IsValid() )
return;
if ( !tr.Entity.IsValid() )
return;
if ( tr.Entity is not ModelEntity )
return;
if ( !targetBody.IsValid() )
{
targetBody = tr.Body;
targetBone = tr.Bone;
globalOrigin1 = tr.EndPos;
localOrigin1 = tr.Body.Transform.PointToLocal( globalOrigin1 );
CreateHitEffects( tr.EndPos );
return;
}
if ( targetBody == tr.Body )
return;
var rope = Particles.Create( "particles/rope.vpcf" );
if ( targetBody.Entity.IsWorld )
{
rope.SetPosition( 0, localOrigin1 );
}
else
{
rope.SetEntityBone( 0, targetBody.Entity, targetBone, new Transform( localOrigin1 * (1.0f / targetBody.Entity.Scale) ) );
}
var localOrigin2 = tr.Body.Transform.PointToLocal( tr.EndPos );
if ( tr.Entity.IsWorld )
{
rope.SetPosition( 1, localOrigin2 );
}
else
{
rope.SetEntityBone( 1, tr.Entity, tr.Bone, new Transform( localOrigin2 * (1.0f / tr.Entity.Scale) ) );
}
var spring = PhysicsJoint.Spring
.From( targetBody, localOrigin1 )
.To( tr.Body, localOrigin2 )
.WithFrequency( 5.0f )
.WithDampingRatio( 0.7f )
.WithReferenceMass( targetBody.Mass )
.WithMinRestLength( 0 )
.WithMaxRestLength( tr.EndPos.Distance( globalOrigin1 ) )
.WithCollisionsEnabled()
.Create();
spring.EnableAngularConstraint = false;
spring.OnBreak( () =>
{
rope?.Destroy( true );
spring.Remove();
} );
CreateHitEffects( tr.EndPos );
Reset();
}
}
private void Reset()
{
targetBody = null;
targetBone = -1;
localOrigin1 = default;
}
public override void Activate()
{
base.Activate();
Reset();
}
public override void Deactivate()
{
base.Deactivate();
Reset();
}
}
}

89
code/tools/Thruster.cs Normal file
View File

@@ -0,0 +1,89 @@
namespace Sandbox.Tools
{
[Library( "tool_thruster", Title = "Thruster", Description = "A rocket type thing that can push forwards and backward", Group = "construction" )]
public partial class ThrusterTool : BaseTool
{
PreviewEntity previewModel;
bool massless = true;
public override void CreatePreviews()
{
if ( TryCreatePreview( ref previewModel, "models/thruster/thrusterprojector.vmdl" ) )
{
previewModel.RotationOffset = Rotation.FromAxis( Vector3.Right, -90 );
}
}
protected override bool IsPreviewTraceValid( TraceResult tr )
{
if ( !base.IsPreviewTraceValid( tr ) )
return false;
if ( tr.Entity is ThrusterEntity )
return false;
return true;
}
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
if ( Input.Pressed( InputButton.Attack2 ) )
{
massless = !massless;
}
if ( !Input.Pressed( InputButton.Attack1 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit )
return;
if ( !tr.Entity.IsValid() )
return;
var attached = !tr.Entity.IsWorld && tr.Body.IsValid() && tr.Body.PhysicsGroup != null && tr.Body.Entity.IsValid();
if ( attached && tr.Entity is not Prop )
return;
CreateHitEffects( tr.EndPos );
if ( tr.Entity is ThrusterEntity )
{
// TODO: Set properties
return;
}
var ent = new ThrusterEntity
{
Position = tr.EndPos,
Rotation = Rotation.LookAt( tr.Normal, dir ) * Rotation.From( new Angles( 90, 0, 0 ) ),
PhysicsEnabled = !attached,
EnableSolidCollisions = !attached,
TargetBody = attached ? tr.Body : null,
Massless = massless
};
if ( attached )
{
ent.SetParent( tr.Body.Entity, tr.Body.PhysicsGroup.GetBodyBoneName( tr.Body ) );
}
ent.SetModel( "models/thruster/thrusterprojector.vmdl" );
}
}
}
}

96
code/tools/Weld.cs Normal file
View File

@@ -0,0 +1,96 @@
namespace Sandbox.Tools
{
[Library( "tool_weld", Title = "Weld", Description = "Weld stuff together", Group = "construction" )]
public partial class WeldTool : BaseTool
{
private Prop target;
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit || !tr.Body.IsValid() || !tr.Entity.IsValid() || tr.Entity.IsWorld )
return;
if ( tr.Entity.PhysicsGroup == null || tr.Entity.PhysicsGroup.BodyCount > 1 )
return;
if ( tr.Entity is not Prop prop )
return;
if ( Input.Pressed( InputButton.Attack1 ) )
{
if ( prop.Root is not Prop rootProp )
{
return;
}
if ( target == rootProp )
return;
if ( !target.IsValid() )
{
target = rootProp;
}
else
{
target.Weld( rootProp );
target = null;
}
}
else if ( Input.Pressed( InputButton.Attack2 ) )
{
prop.Unweld( true );
Reset();
}
else if ( Input.Pressed( InputButton.Reload ) )
{
if ( prop.Root is not Prop rootProp )
{
return;
}
rootProp.Unweld();
Reset();
}
else
{
return;
}
CreateHitEffects( tr.EndPos );
}
}
private void Reset()
{
target = null;
}
public override void Activate()
{
base.Activate();
Reset();
}
public override void Deactivate()
{
base.Deactivate();
Reset();
}
}
}

83
code/tools/Wheel.cs Normal file
View File

@@ -0,0 +1,83 @@
namespace Sandbox.Tools
{
[Library( "tool_wheel", Title = "Wheel", Description = "A wheel that you can turn on and off (but actually can't yet)", Group = "construction" )]
public partial class WheelTool : BaseTool
{
PreviewEntity previewModel;
protected override bool IsPreviewTraceValid( TraceResult tr )
{
if ( !base.IsPreviewTraceValid( tr ) )
return false;
if ( tr.Entity is WheelEntity )
return false;
return true;
}
public override void CreatePreviews()
{
if ( TryCreatePreview( ref previewModel, "models/citizen_props/wheel01.vmdl" ) )
{
previewModel.RotationOffset = Rotation.FromAxis( Vector3.Up, 90 );
}
}
public override void Simulate()
{
if ( !Host.IsServer )
return;
using ( Prediction.Off() )
{
if ( !Input.Pressed( InputButton.Attack1 ) )
return;
var startPos = Owner.EyePos;
var dir = Owner.EyeRot.Forward;
var tr = Trace.Ray( startPos, startPos + dir * MaxTraceDistance )
.Ignore( Owner )
.Run();
if ( !tr.Hit )
return;
if ( !tr.Entity.IsValid() )
return;
var attached = !tr.Entity.IsWorld && tr.Body.IsValid() && tr.Body.PhysicsGroup != null && tr.Body.Entity.IsValid();
if ( attached && tr.Entity is not Prop )
return;
CreateHitEffects( tr.EndPos );
if ( tr.Entity is WheelEntity )
{
// TODO: Set properties
return;
}
var ent = new WheelEntity
{
Position = tr.EndPos,
Rotation = Rotation.LookAt( tr.Normal ) * Rotation.From( new Angles( 0, 90, 0 ) ),
};
ent.SetModel( "models/citizen_props/wheel01.vmdl" );
ent.PhysicsBody.Mass = tr.Body.Mass;
ent.Joint = PhysicsJoint.Revolute
.From( ent.PhysicsBody )
.To( tr.Body )
.WithPivot( tr.EndPos )
.WithBasis( Rotation.LookAt( tr.Normal ) * Rotation.From( new Angles( 90, 0, 0 ) ) )
.Create();
}
}
}
}