mirror of
https://github.com/celisej567/gm_construct_port.git
synced 2026-01-04 02:10:11 +03:00
Add files via upload
This commit is contained in:
123
code/tools/Balloon.cs
Normal file
123
code/tools/Balloon.cs
Normal 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
38
code/tools/BoxShooter.cs
Normal 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
38
code/tools/Color.cs
Normal 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
280
code/tools/GravGun.cs
Normal 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
83
code/tools/Lamp.cs
Normal 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
57
code/tools/LeafBlower.cs
Normal 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
116
code/tools/Light.cs
Normal 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();
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
code/tools/PhysGun.Effects.cs
Normal file
124
code/tools/PhysGun.Effects.cs
Normal 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
419
code/tools/PhysGun.cs
Normal 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
42
code/tools/Remover.cs
Normal 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
54
code/tools/Resizer.cs
Normal 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
122
code/tools/Rope.cs
Normal 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
89
code/tools/Thruster.cs
Normal 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
96
code/tools/Weld.cs
Normal 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
83
code/tools/Wheel.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user