mirror of
https://github.com/celisej567/gm_construct_port.git
synced 2026-09-27 16:16:14 +03:00
Add files via upload
This commit is contained in:
37
code/entities/BalloonEntity.cs
Normal file
37
code/entities/BalloonEntity.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Sandbox;
|
||||
|
||||
[Library( "ent_balloon", Title = "Balloon", Spawnable = true )]
|
||||
public partial class BalloonEntity : Prop
|
||||
{
|
||||
private static float GravityScale => -0.2f;
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
SetModel( "models/citizen_props/balloonregular01.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
PhysicsBody.GravityScale = GravityScale;
|
||||
RenderColor = Color.Random.ToColor32();
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
PlaySound( "balloon_pop_cute" );
|
||||
}
|
||||
|
||||
[Event.Physics.PostStep]
|
||||
public void OnPostPhysicsStep()
|
||||
{
|
||||
if ( !this.IsValid() )
|
||||
return;
|
||||
|
||||
var body = PhysicsBody;
|
||||
if ( !body.IsValid() )
|
||||
return;
|
||||
|
||||
body.GravityScale = GravityScale;
|
||||
}
|
||||
}
|
||||
43
code/entities/BouncyBall.cs
Normal file
43
code/entities/BouncyBall.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
|
||||
[Library( "ent_bouncyball", Title = "Bouncy Ball", Spawnable = true )]
|
||||
public partial class BouncyBallEntity : Prop, IUse
|
||||
{
|
||||
public float MaxSpeed { get; set; } = 1000.0f;
|
||||
public float SpeedMul { get; set; } = 1.2f;
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
SetModel( "models/ball/ball.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
Scale = Rand.Float( 0.5f, 2.0f );
|
||||
RenderColor = Color.Random.ToColor32();
|
||||
}
|
||||
|
||||
protected override void OnPhysicsCollision( CollisionEventData eventData )
|
||||
{
|
||||
var speed = eventData.PreVelocity.Length;
|
||||
var direction = Vector3.Reflect( eventData.PreVelocity.Normal, eventData.Normal.Normal ).Normal;
|
||||
Velocity = direction * MathF.Min( speed * SpeedMul, MaxSpeed );
|
||||
}
|
||||
|
||||
public bool IsUsable( Entity user )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnUse( Entity user )
|
||||
{
|
||||
if ( user is Player player )
|
||||
{
|
||||
player.Health += 10;
|
||||
|
||||
Delete();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
64
code/entities/DirectionalGravity.cs
Normal file
64
code/entities/DirectionalGravity.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Sandbox;
|
||||
using System.Linq;
|
||||
|
||||
[Library( "directional_gravity", Title = "Directional Gravity", Spawnable = true )]
|
||||
public partial class DirectionalGravity : Prop
|
||||
{
|
||||
bool enabled = false;
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
DeleteOthers();
|
||||
|
||||
SetModel( "models/arrow.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
private void DeleteOthers()
|
||||
{
|
||||
// Only allow one of these to be spawned at a time
|
||||
foreach ( var ent in All.OfType<DirectionalGravity>()
|
||||
.Where( x => x.IsValid() && x != this ) )
|
||||
{
|
||||
ent.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if ( IsServer )
|
||||
{
|
||||
PhysicsWorld.UseDefaultGravity();
|
||||
PhysicsWorld.WakeAllBodies();
|
||||
}
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
[Event.Physics.PostStep]
|
||||
public void OnPostPhysicsStep()
|
||||
{
|
||||
if ( !IsServer )
|
||||
return;
|
||||
|
||||
if ( !enabled )
|
||||
return;
|
||||
|
||||
if ( !this.IsValid() )
|
||||
return;
|
||||
|
||||
var gravity = Rotation.Down * 800.0f;
|
||||
|
||||
if ( gravity != PhysicsWorld.Gravity )
|
||||
{
|
||||
PhysicsWorld.Gravity = gravity;
|
||||
PhysicsWorld.WakeAllBodies();
|
||||
}
|
||||
}
|
||||
}
|
||||
146
code/entities/DroneEntity.cs
Normal file
146
code/entities/DroneEntity.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
|
||||
[Library( "ent_drone", Title = "Drone", Spawnable = true )]
|
||||
public partial class DroneEntity : Prop
|
||||
{
|
||||
public virtual float altitudeAcceleration => 2000;
|
||||
public virtual float movementAcceleration => 5000;
|
||||
public virtual float yawSpeed => 150;
|
||||
public virtual float uprightSpeed => 5000;
|
||||
public virtual float uprightDot => 0.5f;
|
||||
public virtual float leanWeight => 0.5f;
|
||||
public virtual float leanMaxVelocity => 1000;
|
||||
|
||||
private struct DroneInputState
|
||||
{
|
||||
public Vector3 movement;
|
||||
public float throttle;
|
||||
public float pitch;
|
||||
public float yaw;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
movement = Vector3.Zero;
|
||||
pitch = 0;
|
||||
yaw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private DroneInputState currentInput;
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
SetModel( "entities/drone/drone.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
}
|
||||
|
||||
[Event.Physics.PostStep]
|
||||
public void OnPostPhysicsStep()
|
||||
{
|
||||
if ( !PhysicsBody.IsValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var body = PhysicsBody;
|
||||
var transform = Transform;
|
||||
|
||||
body.LinearDrag = 1.0f;
|
||||
body.AngularDrag = 1.0f;
|
||||
body.LinearDamping = 4.0f;
|
||||
body.AngularDamping = 4.0f;
|
||||
|
||||
var yawRot = Rotation.From( new Angles( 0, Rotation.Angles().yaw, 0 ) );
|
||||
var worldMovement = yawRot * currentInput.movement;
|
||||
var velocityDirection = body.Velocity.WithZ( 0 );
|
||||
var velocityMagnitude = velocityDirection.Length;
|
||||
velocityDirection = velocityDirection.Normal;
|
||||
|
||||
var velocityScale = (velocityMagnitude / leanMaxVelocity).Clamp( 0, 1 );
|
||||
var leanDirection = worldMovement.LengthSquared == 0.0f
|
||||
? -velocityScale * velocityDirection
|
||||
: worldMovement;
|
||||
|
||||
var targetUp = (Vector3.Up + leanDirection * leanWeight * velocityScale).Normal;
|
||||
var currentUp = transform.NormalToWorld( Vector3.Up );
|
||||
var alignment = Math.Max( Vector3.Dot( targetUp, currentUp ), 0 );
|
||||
|
||||
bool hasCollision = false;
|
||||
bool isGrounded = false;
|
||||
|
||||
if ( !hasCollision || isGrounded )
|
||||
{
|
||||
var hoverForce = isGrounded && currentInput.throttle <= 0 ? Vector3.Zero : -1 * transform.NormalToWorld( Vector3.Up ) * -800.0f;
|
||||
var movementForce = isGrounded ? Vector3.Zero : worldMovement * movementAcceleration;
|
||||
var altitudeForce = transform.NormalToWorld( Vector3.Up ) * currentInput.throttle * altitudeAcceleration;
|
||||
var totalForce = hoverForce + movementForce + altitudeForce;
|
||||
body.ApplyForce( (totalForce * alignment) * body.Mass );
|
||||
}
|
||||
|
||||
if ( !hasCollision && !isGrounded )
|
||||
{
|
||||
var spinTorque = Transform.NormalToWorld( new Vector3( 0, 0, currentInput.yaw * yawSpeed ) );
|
||||
var uprightTorque = Vector3.Cross( currentUp, targetUp ) * uprightSpeed;
|
||||
var uprightAlignment = alignment < uprightDot ? 0 : alignment;
|
||||
var totalTorque = spinTorque * alignment + uprightTorque * uprightAlignment;
|
||||
body.ApplyTorque( (totalTorque * alignment) * body.Mass );
|
||||
}
|
||||
}
|
||||
|
||||
public override void Simulate( Client owner )
|
||||
{
|
||||
if ( owner == null ) return;
|
||||
if ( !IsServer ) return;
|
||||
|
||||
using ( Prediction.Off() )
|
||||
{
|
||||
currentInput.Reset();
|
||||
var x = (Input.Down( InputButton.Forward ) ? -1 : 0) + (Input.Down( InputButton.Back ) ? 1 : 0);
|
||||
var y = (Input.Down( InputButton.Right ) ? 1 : 0) + (Input.Down( InputButton.Left ) ? -1 : 0);
|
||||
currentInput.movement = new Vector3( x, y, 0 ).Normal;
|
||||
currentInput.throttle = (Input.Down( InputButton.Run ) ? 1 : 0) + (Input.Down( InputButton.Duck ) ? -1 : 0);
|
||||
currentInput.yaw = -Input.MouseDelta.x;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetInput()
|
||||
{
|
||||
currentInput.Reset();
|
||||
}
|
||||
|
||||
private readonly Vector3[] turbinePositions = new Vector3[]
|
||||
{
|
||||
new Vector3( -35.37f, 35.37f, 10.0f ),
|
||||
new Vector3( 35.37f, 35.37f, 10.0f ),
|
||||
new Vector3( 35.37f, -35.37f, 10.0f ),
|
||||
new Vector3( -35.37f, -35.37f, 10.0f )
|
||||
};
|
||||
|
||||
public override void OnNewModel( Model model )
|
||||
{
|
||||
base.OnNewModel( model );
|
||||
|
||||
if ( IsClient )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private float spinAngle;
|
||||
|
||||
[Event.Frame]
|
||||
public void OnFrame()
|
||||
{
|
||||
spinAngle += 10000.0f * Time.Delta;
|
||||
spinAngle %= 360.0f;
|
||||
|
||||
for ( int i = 0; i < turbinePositions.Length; ++i )
|
||||
{
|
||||
var transform = Transform.ToWorld( new Transform( turbinePositions[i] * Scale, Rotation.From( new Angles( 0, spinAngle, 0 ) ) ) );
|
||||
transform.Scale = Scale;
|
||||
SetBoneTransform( i, transform );
|
||||
}
|
||||
}
|
||||
}
|
||||
33
code/entities/LampEntity.cs
Normal file
33
code/entities/LampEntity.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Sandbox;
|
||||
|
||||
[Library( "ent_lamp", Title = "Lamp", Spawnable = true )]
|
||||
public partial class LampEntity : SpotLightEntity, IUse
|
||||
{
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
SetModel( "models/torch/torch.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
}
|
||||
|
||||
public bool IsUsable( Entity user )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnUse( Entity user )
|
||||
{
|
||||
Enabled = !Enabled;
|
||||
|
||||
PlaySound( Enabled ? "flashlight-on" : "flashlight-off" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
PhysicsGroup?.Wake();
|
||||
Delete();
|
||||
}
|
||||
}
|
||||
33
code/entities/LightEntity.cs
Normal file
33
code/entities/LightEntity.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Sandbox;
|
||||
|
||||
[Library( "ent_light", Title = "Light", Spawnable = true )]
|
||||
public partial class LightEntity : PointLightEntity, IUse
|
||||
{
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
SetModel( "models/light/light_tubular.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
}
|
||||
|
||||
public bool IsUsable( Entity user )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnUse( Entity user )
|
||||
{
|
||||
Enabled = !Enabled;
|
||||
|
||||
PlaySound( Enabled ? "flashlight-on" : "flashlight-off" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
PhysicsGroup?.Wake();
|
||||
Delete();
|
||||
}
|
||||
}
|
||||
103
code/entities/NoiseTest.cs
Normal file
103
code/entities/NoiseTest.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Sandbox;
|
||||
|
||||
[Library( "noise_test", Title = "Noise Test", Spawnable = true )]
|
||||
public partial class NoiseTest : Prop
|
||||
{
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
SetModel( "models/citizen_props/balloonregular01.vmdl" );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
}
|
||||
|
||||
[Event.Frame]
|
||||
public void OnFrame()
|
||||
{
|
||||
var pos = Position;
|
||||
var right = Rotation.Right * 4;
|
||||
var forward = Rotation.Forward * 4;
|
||||
var up = Rotation.Up * 50;
|
||||
var offset = Time.Now * 2.0f;
|
||||
var offsetz = Time.Now * 0.1f;
|
||||
|
||||
var mode = (int)((Time.Now * 0.3f) % 5);
|
||||
|
||||
switch ( mode )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
DebugOverlay.Text( pos, "Perlin" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
DebugOverlay.Text( pos, "SparseConvolution" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
DebugOverlay.Text( pos, "SparseConvolutionNormalized" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
DebugOverlay.Text( pos, "Turbulence" );
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
{
|
||||
DebugOverlay.Text( pos, "Fractal" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var size = 100;
|
||||
|
||||
pos -= right * size * 0.5f;
|
||||
pos -= forward * size * 0.5f;
|
||||
|
||||
for ( float x = 0; x < size; x++ )
|
||||
for ( float y = 0; y < size; y++ )
|
||||
{
|
||||
float val = 0;
|
||||
|
||||
switch ( mode )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
val = Noise.Perlin( x * 0.1f + offset, y * 0.1f, offsetz ) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
val = Noise.SparseConvolution( x * 0.1f + offset, y * 0.1f, offsetz ) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
val = Noise.SparseConvolutionNormalized( x * 0.1f + offset, y * 0.1f, offsetz ) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
val = Noise.Turbulence( 2, x * 0.1f + offset, y * 0.1f, offsetz ) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
val = Noise.Fractal( 2, x * 0.1f + offset, y * 0.1f, offsetz ) * 0.5f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var start = pos + x * right + y * forward;
|
||||
DebugOverlay.Line( start, start + up * val, Color.Lerp( Color.Red, Color.Green, (val + 1.0f) / 2.0f ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
46
code/entities/ThrusterEntity.Effects.cs
Normal file
46
code/entities/ThrusterEntity.Effects.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using Sandbox;
|
||||
|
||||
public partial class ThrusterEntity
|
||||
{
|
||||
private Particles effects;
|
||||
|
||||
[Event.Frame]
|
||||
public void OnFrame()
|
||||
{
|
||||
UpdateEffects();
|
||||
}
|
||||
|
||||
protected void CreateEffects()
|
||||
{
|
||||
if ( effects != null )
|
||||
return;
|
||||
|
||||
effects = Particles.Create( "particles/physgun_end_nohit.vpcf" );
|
||||
}
|
||||
|
||||
protected virtual void KillEffects()
|
||||
{
|
||||
if ( effects == null )
|
||||
return;
|
||||
|
||||
effects.Destroy( false );
|
||||
effects = null;
|
||||
}
|
||||
|
||||
protected virtual void UpdateEffects()
|
||||
{
|
||||
if ( Enabled )
|
||||
{
|
||||
CreateEffects();
|
||||
}
|
||||
else
|
||||
{
|
||||
KillEffects();
|
||||
}
|
||||
|
||||
if ( effects == null )
|
||||
return;
|
||||
|
||||
effects.SetPosition( 0, Position + Rotation.Up * 20 );
|
||||
}
|
||||
}
|
||||
50
code/entities/ThrusterEntity.cs
Normal file
50
code/entities/ThrusterEntity.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Sandbox;
|
||||
|
||||
[Library( "ent_thruster" )]
|
||||
public partial class ThrusterEntity : Prop, IUse
|
||||
{
|
||||
public float Force = 1000.0f;
|
||||
public bool Massless = false;
|
||||
public PhysicsBody TargetBody;
|
||||
|
||||
[Net]
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
[Event.Physics.PostStep]
|
||||
public virtual void OnPostPhysicsStep()
|
||||
{
|
||||
if ( IsServer && Enabled )
|
||||
{
|
||||
if ( TargetBody.IsValid() )
|
||||
{
|
||||
TargetBody.ApplyForceAt( Position, Rotation.Down * (Massless ? Force * TargetBody.Mass : Force) );
|
||||
}
|
||||
else if ( PhysicsBody.IsValid() )
|
||||
{
|
||||
PhysicsBody.ApplyForce( Rotation.Down * (Massless ? Force * PhysicsBody.Mass : Force) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUsable( Entity user )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnUse( Entity user )
|
||||
{
|
||||
Enabled = !Enabled;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if ( IsClient )
|
||||
{
|
||||
KillEffects();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
code/entities/WheelEntity.cs
Normal file
25
code/entities/WheelEntity.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Joints;
|
||||
|
||||
[Library( "ent_wheel" )]
|
||||
public partial class WheelEntity : Prop
|
||||
{
|
||||
public RevoluteJoint Joint;
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if ( Joint.IsValid )
|
||||
{
|
||||
Joint.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdatePropData( Model model )
|
||||
{
|
||||
base.UpdatePropData( model );
|
||||
|
||||
Health = -1;
|
||||
}
|
||||
}
|
||||
34
code/entities/car/CarAnimator.cs
Normal file
34
code/entities/car/CarAnimator.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
namespace Sandbox
|
||||
{
|
||||
public class CarAnimator : PawnAnimator
|
||||
{
|
||||
public override void Simulate()
|
||||
{
|
||||
ResetParams();
|
||||
|
||||
SetParam( "b_grounded", true );
|
||||
SetParam( "b_sit", true );
|
||||
|
||||
var eyeAngles = (Pawn.Rotation.Inverse * Pawn.EyeRot).Angles();
|
||||
eyeAngles.pitch = eyeAngles.pitch.Clamp( -25, 70 );
|
||||
eyeAngles.yaw = eyeAngles.yaw.Clamp( -90, 90 );
|
||||
|
||||
var aimPos = Pawn.EyePos + (Pawn.Rotation * Rotation.From( eyeAngles )).Forward * 200;
|
||||
|
||||
SetLookAt( "aim_eyes", aimPos );
|
||||
SetLookAt( "aim_head", aimPos );
|
||||
SetLookAt( "aim_body", aimPos );
|
||||
|
||||
if ( Pawn.ActiveChild is BaseCarriable carry )
|
||||
{
|
||||
carry.SimulateAnimator( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetParam( "holdtype", 0 );
|
||||
SetParam( "aim_body_weight", 0.5f );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
228
code/entities/car/CarCamera.cs
Normal file
228
code/entities/car/CarCamera.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
|
||||
public class CarCamera : Camera
|
||||
{
|
||||
protected virtual float MinFov => 80.0f;
|
||||
protected virtual float MaxFov => 100.0f;
|
||||
protected virtual float MaxFovSpeed => 1000.0f;
|
||||
protected virtual float FovSmoothingSpeed => 4.0f;
|
||||
protected virtual float OrbitCooldown => 0.6f;
|
||||
protected virtual float OrbitSmoothingSpeed => 25.0f;
|
||||
protected virtual float OrbitReturnSmoothingSpeed => 4.0f;
|
||||
protected virtual float MinOrbitPitch => -25.0f;
|
||||
protected virtual float MaxOrbitPitch => 70.0f;
|
||||
protected virtual float FixedOrbitPitch => 10.0f;
|
||||
protected virtual float OrbitHeight => 35.0f;
|
||||
protected virtual float OrbitDistance => 150.0f;
|
||||
protected virtual float MaxOrbitReturnSpeed => 100.0f;
|
||||
protected virtual float MinCarPitch => -60.0f;
|
||||
protected virtual float MaxCarPitch => 60.0f;
|
||||
protected virtual float FirstPersonPitch => 10.0f;
|
||||
protected virtual float CarPitchSmoothingSpeed => 1.0f;
|
||||
protected virtual float CollisionRadius => 8.0f;
|
||||
protected virtual float ShakeSpeed => 10.0f;
|
||||
protected virtual float ShakeSpeedThreshold => 1500.0f;
|
||||
protected virtual float ShakeMaxSpeed => 2500.0f;
|
||||
protected virtual float ShakeMaxLength => 1.0f;
|
||||
|
||||
private bool orbitEnabled;
|
||||
private TimeSince timeSinceOrbit;
|
||||
private Angles orbitAngles;
|
||||
private Rotation orbitYawRot;
|
||||
private Rotation orbitPitchRot;
|
||||
private float currentFov;
|
||||
private float carPitch;
|
||||
private bool firstPerson;
|
||||
|
||||
public override void Activated()
|
||||
{
|
||||
var pawn = Local.Pawn;
|
||||
if ( pawn == null ) return;
|
||||
|
||||
orbitEnabled = false;
|
||||
timeSinceOrbit = 0.0f;
|
||||
orbitAngles = Angles.Zero;
|
||||
orbitYawRot = Rotation.Identity;
|
||||
orbitPitchRot = Rotation.Identity;
|
||||
currentFov = MinFov;
|
||||
carPitch = 0;
|
||||
firstPerson = false;
|
||||
|
||||
var car = (pawn as SandboxPlayer)?.Vehicle as CarEntity;
|
||||
if ( !car.IsValid() ) return;
|
||||
|
||||
orbitYawRot = firstPerson ? Rotation.Identity : Rotation.FromYaw( car.Rotation.Yaw() );
|
||||
orbitPitchRot = firstPerson ? Rotation.FromPitch( FirstPersonPitch ) : Rotation.Identity;
|
||||
orbitAngles = (orbitYawRot * orbitPitchRot).Angles();
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
var pawn = Local.Pawn;
|
||||
if ( pawn == null ) return;
|
||||
|
||||
var car = (pawn as SandboxPlayer)?.Vehicle as CarEntity;
|
||||
if ( !car.IsValid() ) return;
|
||||
|
||||
var body = car.PhysicsBody;
|
||||
if ( !body.IsValid() )
|
||||
return;
|
||||
|
||||
var speed = car.MovementSpeed;
|
||||
var speedAbs = Math.Abs( speed );
|
||||
|
||||
if ( orbitEnabled && timeSinceOrbit > OrbitCooldown )
|
||||
orbitEnabled = false;
|
||||
|
||||
var carRot = car.Rotation;
|
||||
carPitch = carPitch.LerpTo( car.Grounded ? carRot.Pitch().Clamp( MinCarPitch, MaxCarPitch ) * (speed < 0.0f ? -1.0f : 1.0f) : 0.0f, Time.Delta * CarPitchSmoothingSpeed );
|
||||
|
||||
if ( orbitEnabled )
|
||||
{
|
||||
var slerpAmount = Time.Delta * OrbitSmoothingSpeed;
|
||||
|
||||
orbitYawRot = Rotation.Slerp( orbitYawRot, Rotation.From( 0.0f, orbitAngles.yaw, 0.0f ), slerpAmount );
|
||||
orbitPitchRot = Rotation.Slerp( orbitPitchRot, Rotation.From( orbitAngles.pitch + carPitch, 0.0f, 0.0f ), slerpAmount );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( firstPerson )
|
||||
{
|
||||
var targetYaw = 0;
|
||||
var targetPitch = FirstPersonPitch;
|
||||
var slerpAmount = Time.Delta * OrbitReturnSmoothingSpeed;
|
||||
|
||||
orbitYawRot = Rotation.Slerp( orbitYawRot, Rotation.FromYaw( targetYaw ), slerpAmount );
|
||||
orbitPitchRot = Rotation.Slerp( orbitPitchRot, Rotation.FromPitch( targetPitch ), slerpAmount );
|
||||
}
|
||||
else
|
||||
{
|
||||
var targetPitch = FixedOrbitPitch.Clamp( MinOrbitPitch, MaxOrbitPitch );
|
||||
var targetYaw = !firstPerson && speed < 0.0f ? carRot.Yaw() + 180.0f : carRot.Yaw();
|
||||
var slerpAmount = MaxOrbitReturnSpeed > 0.0f ? Time.Delta * (speedAbs / MaxOrbitReturnSpeed).Clamp( 0.0f, OrbitReturnSmoothingSpeed ) : 1.0f;
|
||||
|
||||
orbitYawRot = Rotation.Slerp( orbitYawRot, Rotation.FromYaw( targetYaw ), slerpAmount );
|
||||
orbitPitchRot = Rotation.Slerp( orbitPitchRot, Rotation.FromPitch( targetPitch + carPitch ), slerpAmount );
|
||||
}
|
||||
|
||||
orbitAngles.pitch = orbitPitchRot.Pitch();
|
||||
orbitAngles.yaw = orbitYawRot.Yaw();
|
||||
orbitAngles = orbitAngles.Normal;
|
||||
}
|
||||
|
||||
if ( firstPerson )
|
||||
{
|
||||
DoFirstPerson();
|
||||
}
|
||||
else
|
||||
{
|
||||
DoThirdPerson( car, body );
|
||||
}
|
||||
|
||||
currentFov = MaxFovSpeed > 0.0f ? currentFov.LerpTo( MinFov.LerpTo( MaxFov, speedAbs / MaxFovSpeed ), Time.Delta * FovSmoothingSpeed ) : MaxFov;
|
||||
FieldOfView = currentFov;
|
||||
|
||||
ApplyShake( speedAbs );
|
||||
}
|
||||
|
||||
private void DoFirstPerson()
|
||||
{
|
||||
var pawn = Local.Pawn;
|
||||
if ( pawn == null ) return;
|
||||
|
||||
Pos = pawn.EyePos;
|
||||
Rot = pawn.Rotation * (orbitYawRot * orbitPitchRot);
|
||||
|
||||
Viewer = pawn;
|
||||
}
|
||||
|
||||
private void DoThirdPerson( CarEntity car, PhysicsBody body )
|
||||
{
|
||||
Rot = orbitYawRot * orbitPitchRot;
|
||||
|
||||
var carPos = car.Position + car.Rotation * (body.LocalMassCenter * car.Scale);
|
||||
var startPos = carPos;
|
||||
var targetPos = startPos + Rot.Backward * (OrbitDistance * car.Scale) + (Vector3.Up * (OrbitHeight * car.Scale));
|
||||
|
||||
var tr = Trace.Ray( startPos, targetPos )
|
||||
.Ignore( car )
|
||||
.Radius( Math.Clamp( CollisionRadius * car.Scale, 2.0f, 10.0f ) )
|
||||
.WorldOnly()
|
||||
.Run();
|
||||
|
||||
Pos = tr.EndPos;
|
||||
|
||||
Viewer = null;
|
||||
}
|
||||
|
||||
public override void BuildInput( InputBuilder input )
|
||||
{
|
||||
base.BuildInput( input );
|
||||
|
||||
var pawn = Local.Pawn;
|
||||
if ( pawn == null ) return;
|
||||
|
||||
var car = (pawn as SandboxPlayer)?.Vehicle as CarEntity;
|
||||
if ( !car.IsValid() ) return;
|
||||
|
||||
if ( input.Pressed( InputButton.View ) )
|
||||
{
|
||||
firstPerson = !firstPerson;
|
||||
orbitYawRot = firstPerson ? Rotation.Identity : Rotation.FromYaw( car.Rotation.Yaw() );
|
||||
orbitPitchRot = firstPerson ? Rotation.FromPitch( FirstPersonPitch ) : Rotation.Identity;
|
||||
orbitAngles = (orbitYawRot * orbitPitchRot).Angles();
|
||||
orbitEnabled = false;
|
||||
timeSinceOrbit = 0.0f;
|
||||
}
|
||||
|
||||
if ( (Math.Abs( input.AnalogLook.pitch ) + Math.Abs( input.AnalogLook.yaw )) > 0.0f )
|
||||
{
|
||||
if ( !orbitEnabled )
|
||||
{
|
||||
orbitAngles = (orbitYawRot * orbitPitchRot).Angles();
|
||||
orbitAngles = orbitAngles.Normal;
|
||||
|
||||
orbitYawRot = Rotation.From( 0.0f, orbitAngles.yaw, 0.0f );
|
||||
orbitPitchRot = Rotation.From( orbitAngles.pitch, 0.0f, 0.0f );
|
||||
}
|
||||
|
||||
orbitEnabled = true;
|
||||
timeSinceOrbit = 0.0f;
|
||||
|
||||
orbitAngles.yaw += input.AnalogLook.yaw;
|
||||
orbitAngles.pitch += input.AnalogLook.pitch;
|
||||
orbitAngles = orbitAngles.Normal;
|
||||
orbitAngles.pitch = orbitAngles.pitch.Clamp( MinOrbitPitch, MaxOrbitPitch );
|
||||
}
|
||||
|
||||
if ( firstPerson )
|
||||
{
|
||||
input.ViewAngles = (car.Rotation * Rotation.From( orbitAngles )).Angles();
|
||||
}
|
||||
else
|
||||
{
|
||||
input.ViewAngles = orbitEnabled ? orbitAngles : car.Rotation.Angles();
|
||||
}
|
||||
|
||||
input.ViewAngles = input.ViewAngles.Normal;
|
||||
}
|
||||
|
||||
private void ApplyShake( float speed )
|
||||
{
|
||||
if ( speed < ShakeSpeedThreshold )
|
||||
return;
|
||||
|
||||
var pos = (Time.Now % MathF.PI) * ShakeSpeed;
|
||||
var length = (speed - ShakeSpeedThreshold) / (ShakeMaxSpeed - ShakeSpeedThreshold);
|
||||
length = length.Clamp( 0, ShakeMaxLength );
|
||||
|
||||
float x = Noise.Perlin( pos, 0, 0 ) * length;
|
||||
float y = Noise.Perlin( pos, 5.0f, 0 ) * length;
|
||||
|
||||
Pos += Rot.Right * x + Rot.Up * y;
|
||||
Rot *= Rotation.FromAxis( Vector3.Up, x );
|
||||
Rot *= Rotation.FromAxis( Vector3.Right, y );
|
||||
}
|
||||
}
|
||||
|
||||
37
code/entities/car/CarController.cs
Normal file
37
code/entities/car/CarController.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Sandbox;
|
||||
|
||||
[Library]
|
||||
public class CarController : PawnController
|
||||
{
|
||||
public override void FrameSimulate()
|
||||
{
|
||||
base.FrameSimulate();
|
||||
|
||||
Simulate();
|
||||
}
|
||||
|
||||
public override void Simulate()
|
||||
{
|
||||
var player = Pawn as SandboxPlayer;
|
||||
if ( !player.IsValid() ) return;
|
||||
|
||||
var car = player.Vehicle as CarEntity;
|
||||
if ( !car.IsValid() ) return;
|
||||
|
||||
car.Simulate( Client );
|
||||
|
||||
if ( player.Vehicle == null )
|
||||
{
|
||||
Position = car.Position + car.Rotation.Up * (100 * car.Scale);
|
||||
Velocity += car.Rotation.Right * (200 * car.Scale);
|
||||
return;
|
||||
}
|
||||
|
||||
EyeRot = Input.Rotation;
|
||||
EyePosLocal = Vector3.Up * (64 - 10) * car.Scale;
|
||||
Velocity = car.Velocity;
|
||||
|
||||
SetTag( "noclip" );
|
||||
SetTag( "sitting" );
|
||||
}
|
||||
}
|
||||
561
code/entities/car/CarEntity.cs
Normal file
561
code/entities/car/CarEntity.cs
Normal file
@@ -0,0 +1,561 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[Library( "ent_car", Title = "Car", Spawnable = true )]
|
||||
public partial class CarEntity : Prop, IUse
|
||||
{
|
||||
[ConVar.Replicated( "debug_car" )]
|
||||
public static bool debug_car { get; set; } = false;
|
||||
|
||||
[ConVar.Replicated( "car_accelspeed" )]
|
||||
public static float car_accelspeed { get; set; } = 500.0f;
|
||||
|
||||
private CarWheel frontLeft;
|
||||
private CarWheel frontRight;
|
||||
private CarWheel backLeft;
|
||||
private CarWheel backRight;
|
||||
|
||||
private float frontLeftDistance;
|
||||
private float frontRightDistance;
|
||||
private float backLeftDistance;
|
||||
private float backRightDistance;
|
||||
|
||||
private bool frontWheelsOnGround;
|
||||
private bool backWheelsOnGround;
|
||||
private float accelerateDirection;
|
||||
private float airRoll;
|
||||
private float airTilt;
|
||||
private float grip;
|
||||
private TimeSince timeSinceDriverLeft;
|
||||
|
||||
[Net] private float WheelSpeed { get; set; }
|
||||
[Net] private float TurnDirection { get; set; }
|
||||
[Net] private float AccelerationTilt { get; set; }
|
||||
[Net] private float TurnLean { get; set; }
|
||||
|
||||
[Net] public float MovementSpeed { get; private set; }
|
||||
[Net] public bool Grounded { get; private set; }
|
||||
|
||||
private struct InputState
|
||||
{
|
||||
public float throttle;
|
||||
public float turning;
|
||||
public float breaking;
|
||||
public float tilt;
|
||||
public float roll;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
throttle = 0;
|
||||
turning = 0;
|
||||
breaking = 0;
|
||||
tilt = 0;
|
||||
roll = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private InputState currentInput;
|
||||
|
||||
public CarEntity()
|
||||
{
|
||||
frontLeft = new CarWheel( this );
|
||||
frontRight = new CarWheel( this );
|
||||
backLeft = new CarWheel( this );
|
||||
backRight = new CarWheel( this );
|
||||
}
|
||||
|
||||
[Net] public Player driver { get; private set; }
|
||||
|
||||
private ModelEntity chassis_axle_rear;
|
||||
private ModelEntity chassis_axle_front;
|
||||
private ModelEntity wheel0;
|
||||
private ModelEntity wheel1;
|
||||
private ModelEntity wheel2;
|
||||
private ModelEntity wheel3;
|
||||
|
||||
public override void Spawn()
|
||||
{
|
||||
base.Spawn();
|
||||
|
||||
var modelName = "models/car/car.vmdl";
|
||||
|
||||
SetModel( modelName );
|
||||
SetupPhysicsFromModel( PhysicsMotionType.Dynamic, false );
|
||||
SetInteractsExclude( CollisionLayer.Player );
|
||||
EnableSelfCollisions = false;
|
||||
|
||||
var trigger = new ModelEntity
|
||||
{
|
||||
Parent = this,
|
||||
Position = Position,
|
||||
Rotation = Rotation,
|
||||
EnableTouch = true,
|
||||
CollisionGroup = CollisionGroup.Trigger,
|
||||
Transmit = TransmitType.Never,
|
||||
EnableSelfCollisions = false,
|
||||
};
|
||||
|
||||
trigger.SetModel( modelName );
|
||||
trigger.SetupPhysicsFromModel( PhysicsMotionType.Keyframed, false );
|
||||
}
|
||||
|
||||
public override void ClientSpawn()
|
||||
{
|
||||
base.ClientSpawn();
|
||||
|
||||
{
|
||||
var vehicle_fuel_tank = new ModelEntity();
|
||||
vehicle_fuel_tank.SetModel( "entities/modular_vehicle/vehicle_fuel_tank.vmdl" );
|
||||
vehicle_fuel_tank.Transform = Transform;
|
||||
vehicle_fuel_tank.Parent = this;
|
||||
vehicle_fuel_tank.LocalPosition = new Vector3( 0.75f, 0, 0 ) * 40.0f;
|
||||
}
|
||||
|
||||
{
|
||||
chassis_axle_front = new ModelEntity();
|
||||
chassis_axle_front.SetModel( "entities/modular_vehicle/chassis_axle_front.vmdl" );
|
||||
chassis_axle_front.Transform = Transform;
|
||||
chassis_axle_front.Parent = this;
|
||||
chassis_axle_front.LocalPosition = new Vector3( 1.05f, 0, 0.35f ) * 40.0f;
|
||||
|
||||
{
|
||||
wheel0 = new ModelEntity();
|
||||
wheel0.SetModel( "entities/modular_vehicle/wheel_a.vmdl" );
|
||||
wheel0.SetParent( chassis_axle_front, "Wheel_Steer_R", new Transform( Vector3.Zero, Rotation.From( 0, 180, 0 ) ) );
|
||||
}
|
||||
|
||||
{
|
||||
wheel1 = new ModelEntity();
|
||||
wheel1.SetModel( "entities/modular_vehicle/wheel_a.vmdl" );
|
||||
wheel1.SetParent( chassis_axle_front, "Wheel_Steer_L", new Transform( Vector3.Zero, Rotation.From( 0, 0, 0 ) ) );
|
||||
}
|
||||
|
||||
{
|
||||
var chassis_steering = new ModelEntity();
|
||||
chassis_steering.SetModel( "entities/modular_vehicle/chassis_steering.vmdl" );
|
||||
chassis_steering.SetParent( chassis_axle_front, "Axle_front_Center", new Transform( Vector3.Zero, Rotation.From( -90, 180, 0 ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
chassis_axle_rear = new ModelEntity();
|
||||
chassis_axle_rear.SetModel( "entities/modular_vehicle/chassis_axle_rear.vmdl" );
|
||||
chassis_axle_rear.Transform = Transform;
|
||||
chassis_axle_rear.Parent = this;
|
||||
chassis_axle_rear.LocalPosition = new Vector3( -1.05f, 0, 0.35f ) * 40.0f;
|
||||
|
||||
{
|
||||
var chassis_transmission = new ModelEntity();
|
||||
chassis_transmission.SetModel( "entities/modular_vehicle/chassis_transmission.vmdl" );
|
||||
chassis_transmission.SetParent( chassis_axle_rear, "Axle_Rear_Center", new Transform( Vector3.Zero, Rotation.From( -90, 180, 0 ) ) );
|
||||
}
|
||||
|
||||
{
|
||||
wheel2 = new ModelEntity();
|
||||
wheel2.SetModel( "entities/modular_vehicle/wheel_a.vmdl" );
|
||||
wheel2.SetParent( chassis_axle_rear, "Axle_Rear_Center", new Transform( Vector3.Left * (0.7f * 40), Rotation.From( 0, 90, 0 ) ) );
|
||||
}
|
||||
|
||||
{
|
||||
wheel3 = new ModelEntity();
|
||||
wheel3.SetModel( "entities/modular_vehicle/wheel_a.vmdl" );
|
||||
wheel3.SetParent( chassis_axle_rear, "Axle_Rear_Center", new Transform( Vector3.Right * (0.7f * 40), Rotation.From( 0, -90, 0 ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if ( driver is SandboxPlayer player )
|
||||
{
|
||||
RemoveDriver( player );
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetInput()
|
||||
{
|
||||
currentInput.Reset();
|
||||
}
|
||||
|
||||
[Event.Tick.Server]
|
||||
protected void Tick()
|
||||
{
|
||||
if ( driver is SandboxPlayer player )
|
||||
{
|
||||
if ( player.LifeState != LifeState.Alive || player.Vehicle != this )
|
||||
{
|
||||
RemoveDriver( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Simulate( Client owner )
|
||||
{
|
||||
if ( owner == null ) return;
|
||||
if ( !IsServer ) return;
|
||||
|
||||
using ( Prediction.Off() )
|
||||
{
|
||||
currentInput.Reset();
|
||||
|
||||
if ( Input.Pressed( InputButton.Use ) )
|
||||
{
|
||||
if ( owner.Pawn is SandboxPlayer player && !player.IsUseDisabled() )
|
||||
{
|
||||
RemoveDriver( player );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currentInput.throttle = (Input.Down( InputButton.Forward ) ? 1 : 0) + (Input.Down( InputButton.Back ) ? -1 : 0);
|
||||
currentInput.turning = (Input.Down( InputButton.Left ) ? 1 : 0) + (Input.Down( InputButton.Right ) ? -1 : 0);
|
||||
currentInput.breaking = (Input.Down( InputButton.Jump ) ? 1 : 0);
|
||||
currentInput.tilt = (Input.Down( InputButton.Run ) ? 1 : 0) + (Input.Down( InputButton.Duck ) ? -1 : 0);
|
||||
currentInput.roll = (Input.Down( InputButton.Left ) ? 1 : 0) + (Input.Down( InputButton.Right ) ? -1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Event.Physics.PreStep]
|
||||
public void OnPrePhysicsStep()
|
||||
{
|
||||
if ( !IsServer )
|
||||
return;
|
||||
|
||||
var selfBody = PhysicsBody;
|
||||
if ( !selfBody.IsValid() )
|
||||
return;
|
||||
|
||||
var body = selfBody.SelfOrParent;
|
||||
if ( !body.IsValid() )
|
||||
return;
|
||||
|
||||
var dt = Time.Delta;
|
||||
|
||||
body.DragEnabled = false;
|
||||
|
||||
var rotation = selfBody.Rotation;
|
||||
|
||||
accelerateDirection = currentInput.throttle.Clamp( -1, 1 ) * (1.0f - currentInput.breaking);
|
||||
TurnDirection = TurnDirection.LerpTo( currentInput.turning.Clamp( -1, 1 ), 1.0f - MathF.Pow( 0.001f, dt ) );
|
||||
|
||||
airRoll = airRoll.LerpTo( currentInput.roll.Clamp( -1, 1 ), 1.0f - MathF.Pow( 0.0001f, dt ) );
|
||||
airTilt = airTilt.LerpTo( currentInput.tilt.Clamp( -1, 1 ), 1.0f - MathF.Pow( 0.0001f, dt ) );
|
||||
|
||||
float targetTilt = 0;
|
||||
float targetLean = 0;
|
||||
|
||||
var localVelocity = rotation.Inverse * body.Velocity;
|
||||
|
||||
if ( backWheelsOnGround || frontWheelsOnGround )
|
||||
{
|
||||
var forwardSpeed = MathF.Abs( localVelocity.x );
|
||||
var speedFraction = MathF.Min( forwardSpeed / 500.0f, 1 );
|
||||
|
||||
targetTilt = accelerateDirection.Clamp( -1.0f, 1.0f );
|
||||
targetLean = speedFraction * TurnDirection;
|
||||
}
|
||||
|
||||
AccelerationTilt = AccelerationTilt.LerpTo( targetTilt, 1.0f - MathF.Pow( 0.01f, dt ) );
|
||||
TurnLean = TurnLean.LerpTo( targetLean, 1.0f - MathF.Pow( 0.01f, dt ) );
|
||||
|
||||
if ( backWheelsOnGround )
|
||||
{
|
||||
var forwardSpeed = MathF.Abs( localVelocity.x );
|
||||
var speedFactor = 1.0f - (forwardSpeed / 5000.0f).Clamp( 0.0f, 1.0f );
|
||||
var acceleration = speedFactor * (accelerateDirection < 0.0f ? car_accelspeed * 0.5f : car_accelspeed) * accelerateDirection * dt;
|
||||
var impulse = rotation * new Vector3( acceleration, 0, 0 );
|
||||
body.Velocity += impulse;
|
||||
}
|
||||
|
||||
RaycastWheels( rotation, true, out frontWheelsOnGround, out backWheelsOnGround, dt );
|
||||
var onGround = frontWheelsOnGround || backWheelsOnGround;
|
||||
var fullyGrounded = (frontWheelsOnGround && backWheelsOnGround);
|
||||
Grounded = onGround;
|
||||
|
||||
if ( fullyGrounded )
|
||||
{
|
||||
body.Velocity += PhysicsWorld.Gravity * dt;
|
||||
}
|
||||
|
||||
body.GravityScale = fullyGrounded ? 0 : 1;
|
||||
|
||||
bool canAirControl = false;
|
||||
|
||||
var v = rotation * localVelocity.WithZ( 0 );
|
||||
var vDelta = MathF.Pow((v.Length / 1000.0f).Clamp( 0, 1 ), 5.0f).Clamp(0, 1);
|
||||
if ( vDelta < 0.01f ) vDelta = 0;
|
||||
|
||||
if ( debug_car )
|
||||
{
|
||||
DebugOverlay.Line( body.MassCenter, body.MassCenter + rotation.Forward.Normal * 100, Color.White, 0, false );
|
||||
DebugOverlay.Line( body.MassCenter, body.MassCenter + v.Normal * 100, Color.Green, 0, false );
|
||||
}
|
||||
|
||||
var angle = ( rotation.Forward.Normal * MathF.Sign( localVelocity.x )).Normal.Dot( v.Normal ).Clamp( 0.0f, 1.0f );
|
||||
angle = angle.LerpTo( 1.0f, 1.0f - vDelta );
|
||||
grip = grip.LerpTo( angle, 1.0f - MathF.Pow( 0.001f, dt ) );
|
||||
|
||||
if ( debug_car )
|
||||
{
|
||||
DebugOverlay.ScreenText( new Vector2( 200, 200 ), $"{grip}" );
|
||||
}
|
||||
|
||||
var angularDamping = 0.0f;
|
||||
angularDamping = angularDamping.LerpTo( 5.0f, grip );
|
||||
|
||||
body.LinearDamping = 0.0f;
|
||||
body.AngularDamping = fullyGrounded ? angularDamping : 0.5f;
|
||||
|
||||
if ( onGround )
|
||||
{
|
||||
localVelocity = rotation.Inverse * body.Velocity;
|
||||
WheelSpeed = localVelocity.x;
|
||||
var turnAmount = frontWheelsOnGround ? (MathF.Sign( localVelocity.x ) * 25.0f * CalculateTurnFactor( TurnDirection, MathF.Abs( localVelocity.x ) ) * dt) : 0.0f;
|
||||
body.AngularVelocity += rotation * new Vector3( 0, 0, turnAmount );
|
||||
|
||||
airRoll = 0;
|
||||
airTilt = 0;
|
||||
|
||||
var forwardGrip = 0.1f;
|
||||
forwardGrip = forwardGrip.LerpTo( 0.9f, currentInput.breaking );
|
||||
body.Velocity = VelocityDamping( Velocity, rotation, new Vector3( forwardGrip, grip, 0 ), dt );
|
||||
}
|
||||
else
|
||||
{
|
||||
var s = selfBody.Position + (rotation * selfBody.LocalMassCenter);
|
||||
var tr = Trace.Ray( s, s + rotation.Down * 50 )
|
||||
.Ignore( this )
|
||||
.Run();
|
||||
|
||||
if ( debug_car )
|
||||
DebugOverlay.Line( tr.StartPos, tr.EndPos, tr.Hit ? Color.Red : Color.Green );
|
||||
|
||||
canAirControl = !tr.Hit;
|
||||
}
|
||||
|
||||
if ( canAirControl && (airRoll != 0 || airTilt != 0) )
|
||||
{
|
||||
var offset = 50 * Scale;
|
||||
var s = selfBody.Position + (rotation * selfBody.LocalMassCenter) + (rotation.Right * airRoll * offset) + (rotation.Down * (10 * Scale));
|
||||
var tr = Trace.Ray( s, s + rotation.Up * (25 * Scale) )
|
||||
.Ignore( this )
|
||||
.Run();
|
||||
|
||||
if ( debug_car )
|
||||
DebugOverlay.Line( tr.StartPos, tr.EndPos );
|
||||
|
||||
bool dampen = false;
|
||||
|
||||
if ( currentInput.roll.Clamp( -1, 1 ) != 0 )
|
||||
{
|
||||
var force = tr.Hit ? 400.0f : 100.0f;
|
||||
var roll = tr.Hit ? currentInput.roll.Clamp( -1, 1 ) : airRoll;
|
||||
body.ApplyForceAt( selfBody.MassCenter + rotation.Left * (offset * roll), (rotation.Down * roll) * (roll * (body.Mass * force)) );
|
||||
|
||||
if ( debug_car )
|
||||
DebugOverlay.Sphere( selfBody.MassCenter + rotation.Left * (offset * roll), 8, Color.Red );
|
||||
|
||||
dampen = true;
|
||||
}
|
||||
|
||||
if ( !tr.Hit && currentInput.tilt.Clamp( -1, 1 ) != 0 )
|
||||
{
|
||||
var force = 200.0f;
|
||||
body.ApplyForceAt( selfBody.MassCenter + rotation.Forward * (offset * airTilt), (rotation.Down * airTilt) * (airTilt * (body.Mass * force)) );
|
||||
|
||||
if ( debug_car )
|
||||
DebugOverlay.Sphere( selfBody.MassCenter + rotation.Forward * (offset * airTilt), 8, Color.Green );
|
||||
|
||||
dampen = true;
|
||||
}
|
||||
|
||||
if ( dampen )
|
||||
body.AngularVelocity = VelocityDamping( body.AngularVelocity, rotation, 0.95f, dt );
|
||||
}
|
||||
|
||||
localVelocity = rotation.Inverse * body.Velocity;
|
||||
MovementSpeed = localVelocity.x;
|
||||
}
|
||||
|
||||
private static float CalculateTurnFactor( float direction, float speed )
|
||||
{
|
||||
var turnFactor = MathF.Min( speed / 500.0f, 1 );
|
||||
var yawSpeedFactor = 1.0f - (speed / 1000.0f).Clamp( 0, 0.6f );
|
||||
|
||||
return direction * turnFactor * yawSpeedFactor;
|
||||
}
|
||||
|
||||
private static Vector3 VelocityDamping( Vector3 velocity, Rotation rotation, Vector3 damping, float dt )
|
||||
{
|
||||
var localVelocity = rotation.Inverse * velocity;
|
||||
var dampingPow = new Vector3( MathF.Pow( 1.0f - damping.x, dt ), MathF.Pow( 1.0f - damping.y, dt ), MathF.Pow( 1.0f - damping.z, dt ) );
|
||||
return rotation * (localVelocity * dampingPow);
|
||||
}
|
||||
|
||||
private void RaycastWheels( Rotation rotation, bool doPhysics, out bool frontWheels, out bool backWheels, float dt )
|
||||
{
|
||||
float forward = 42;
|
||||
float right = 32;
|
||||
|
||||
var frontLeftPos = rotation.Forward * forward + rotation.Right * right + rotation.Up * 20;
|
||||
var frontRightPos = rotation.Forward * forward - rotation.Right * right + rotation.Up * 20;
|
||||
var backLeftPos = -rotation.Forward * forward + rotation.Right * right + rotation.Up * 20;
|
||||
var backRightPos = -rotation.Forward * forward - rotation.Right * right + rotation.Up * 20;
|
||||
|
||||
var tiltAmount = AccelerationTilt * 2.5f;
|
||||
var leanAmount = TurnLean * 2.5f;
|
||||
|
||||
float length = 20.0f;
|
||||
|
||||
frontWheels =
|
||||
frontLeft.Raycast( length + tiltAmount - leanAmount, doPhysics, frontLeftPos * Scale, ref frontLeftDistance, dt ) |
|
||||
frontRight.Raycast( length + tiltAmount + leanAmount, doPhysics, frontRightPos * Scale, ref frontRightDistance, dt );
|
||||
|
||||
backWheels =
|
||||
backLeft.Raycast( length - tiltAmount - leanAmount, doPhysics, backLeftPos * Scale, ref backLeftDistance, dt ) |
|
||||
backRight.Raycast( length - tiltAmount + leanAmount, doPhysics, backRightPos * Scale, ref backRightDistance, dt );
|
||||
}
|
||||
|
||||
float wheelAngle = 0.0f;
|
||||
float wheelRevolute = 0.0f;
|
||||
|
||||
[Event.Frame]
|
||||
public void OnFrame()
|
||||
{
|
||||
wheelAngle = wheelAngle.LerpTo( TurnDirection * 25, 1.0f - MathF.Pow( 0.001f, Time.Delta ) );
|
||||
wheelRevolute += (WheelSpeed / (14.0f * Scale)).RadianToDegree() * Time.Delta;
|
||||
|
||||
var wheelRotRight = Rotation.From( -wheelAngle, 180, -wheelRevolute );
|
||||
var wheelRotLeft = Rotation.From( wheelAngle, 0, wheelRevolute );
|
||||
var wheelRotBackRight = Rotation.From( 0, 90, -wheelRevolute );
|
||||
var wheelRotBackLeft = Rotation.From( 0, -90, wheelRevolute );
|
||||
|
||||
RaycastWheels( Rotation, false, out _, out _, Time.Delta );
|
||||
|
||||
float frontOffset = 20.0f - Math.Min( frontLeftDistance, frontRightDistance );
|
||||
float backOffset = 20.0f - Math.Min( backLeftDistance, backRightDistance );
|
||||
|
||||
chassis_axle_front.SetBoneTransform( "Axle_front_Center", new Transform( Vector3.Up * frontOffset ), false );
|
||||
chassis_axle_rear.SetBoneTransform( "Axle_Rear_Center", new Transform( Vector3.Up * backOffset ), false );
|
||||
|
||||
wheel0.LocalRotation = wheelRotRight;
|
||||
wheel1.LocalRotation = wheelRotLeft;
|
||||
wheel2.LocalRotation = wheelRotBackRight;
|
||||
wheel3.LocalRotation = wheelRotBackLeft;
|
||||
}
|
||||
|
||||
private void RemoveDriver( SandboxPlayer player )
|
||||
{
|
||||
driver = null;
|
||||
player.Vehicle = null;
|
||||
player.VehicleController = null;
|
||||
player.VehicleAnimator = null;
|
||||
player.VehicleCamera = null;
|
||||
player.Parent = null;
|
||||
player.PhysicsBody.Enabled = true;
|
||||
player.PhysicsBody.Position = player.Position;
|
||||
|
||||
timeSinceDriverLeft = 0;
|
||||
|
||||
ResetInput();
|
||||
}
|
||||
|
||||
public bool OnUse( Entity user )
|
||||
{
|
||||
if ( user is SandboxPlayer player && player.Vehicle == null && timeSinceDriverLeft > 1.0f )
|
||||
{
|
||||
player.Vehicle = this;
|
||||
player.VehicleController = new CarController();
|
||||
player.VehicleAnimator = new CarAnimator();
|
||||
player.VehicleCamera = new CarCamera();
|
||||
player.Parent = this;
|
||||
player.LocalPosition = Vector3.Up * 10;
|
||||
player.LocalRotation = Rotation.Identity;
|
||||
player.LocalScale = 1;
|
||||
player.PhysicsBody.Enabled = false;
|
||||
|
||||
driver = player;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsUsable( Entity user )
|
||||
{
|
||||
return driver == null;
|
||||
}
|
||||
|
||||
public override void StartTouch( Entity other )
|
||||
{
|
||||
base.StartTouch( other );
|
||||
|
||||
if ( !IsServer )
|
||||
return;
|
||||
|
||||
var body = PhysicsBody;
|
||||
if ( !body.IsValid() )
|
||||
return;
|
||||
|
||||
body = body.SelfOrParent;
|
||||
if ( !body.IsValid() )
|
||||
return;
|
||||
|
||||
if ( other is SandboxPlayer player && player.Vehicle == null )
|
||||
{
|
||||
var speed = body.Velocity.Length;
|
||||
var forceOrigin = Position + Rotation.Down * Rand.Float( 20, 30 );
|
||||
var velocity = (player.Position - forceOrigin).Normal * speed;
|
||||
var angularVelocity = body.AngularVelocity;
|
||||
|
||||
OnPhysicsCollision( new CollisionEventData
|
||||
{
|
||||
Entity = player,
|
||||
Pos = player.Position + Vector3.Up * 50,
|
||||
Velocity = velocity,
|
||||
PreVelocity = velocity,
|
||||
PostVelocity = velocity,
|
||||
PreAngularVelocity = angularVelocity,
|
||||
Speed = speed,
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPhysicsCollision( CollisionEventData eventData )
|
||||
{
|
||||
if ( !IsServer )
|
||||
return;
|
||||
|
||||
if ( eventData.Entity is SandboxPlayer player && player.Vehicle != null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var propData = GetModelPropData();
|
||||
|
||||
var minImpactSpeed = propData.MinImpactDamageSpeed;
|
||||
if ( minImpactSpeed <= 0.0f ) minImpactSpeed = 500;
|
||||
|
||||
var impactDmg = propData.ImpactDamage;
|
||||
if ( impactDmg <= 0.0f ) impactDmg = 10;
|
||||
|
||||
var speed = eventData.Speed;
|
||||
|
||||
if ( speed > minImpactSpeed )
|
||||
{
|
||||
if ( eventData.Entity.IsValid() && eventData.Entity != this )
|
||||
{
|
||||
var damage = speed / minImpactSpeed * impactDmg * 1.2f;
|
||||
eventData.Entity.TakeDamage( DamageInfo.Generic( damage )
|
||||
.WithFlag( DamageFlags.PhysicsImpact )
|
||||
.WithFlag( DamageFlags.Vehicle )
|
||||
.WithAttacker( driver != null ? driver : this, driver != null ? this : null )
|
||||
.WithPosition( eventData.Pos )
|
||||
.WithForce( eventData.PreVelocity ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
code/entities/car/CarWheel.cs
Normal file
75
code/entities/car/CarWheel.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
|
||||
struct CarWheel
|
||||
{
|
||||
private readonly CarEntity parent;
|
||||
|
||||
private float _previousLength;
|
||||
private float _currentLength;
|
||||
|
||||
public CarWheel( CarEntity parent )
|
||||
{
|
||||
this.parent = parent;
|
||||
_previousLength = 0;
|
||||
_currentLength = 0;
|
||||
}
|
||||
|
||||
public bool Raycast( float length, bool doPhysics, Vector3 offset, ref float wheel, float dt )
|
||||
{
|
||||
var position = parent.Position;
|
||||
var rotation = parent.Rotation;
|
||||
|
||||
var wheelAttachPos = position + offset;
|
||||
var wheelExtend = wheelAttachPos - rotation.Up * (length * parent.Scale);
|
||||
|
||||
var tr = Trace.Ray( wheelAttachPos, wheelExtend )
|
||||
.Ignore( parent )
|
||||
.Ignore( parent.driver )
|
||||
.Run();
|
||||
|
||||
wheel = length * tr.Fraction;
|
||||
var wheelRadius = (14 * parent.Scale);
|
||||
|
||||
if ( !doPhysics && CarEntity.debug_car )
|
||||
{
|
||||
var wheelPosition = tr.Hit ? tr.EndPos : wheelExtend;
|
||||
wheelPosition += rotation.Up * wheelRadius;
|
||||
|
||||
if ( tr.Hit )
|
||||
{
|
||||
DebugOverlay.Circle( wheelPosition, rotation * Rotation.FromYaw( 90 ), wheelRadius, Color.Red.WithAlpha( 0.5f ), false );
|
||||
DebugOverlay.Line( tr.StartPos, tr.EndPos, Color.Red, 0, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugOverlay.Circle( wheelPosition, rotation * Rotation.FromYaw( 90 ), wheelRadius, Color.Green.WithAlpha( 0.5f ), false );
|
||||
DebugOverlay.Line( wheelAttachPos, wheelExtend, Color.Green, 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !tr.Hit || !doPhysics )
|
||||
{
|
||||
return tr.Hit;
|
||||
}
|
||||
|
||||
var body = parent.PhysicsBody.SelfOrParent;
|
||||
|
||||
_previousLength = _currentLength;
|
||||
_currentLength = (length * parent.Scale) - tr.Distance;
|
||||
|
||||
var springVelocity = (_currentLength - _previousLength) / dt;
|
||||
var springForce = body.Mass * 50.0f * _currentLength;
|
||||
var damperForce = body.Mass * (1.5f + (1.0f - tr.Fraction) * 3.0f) * springVelocity;
|
||||
var velocity = body.GetVelocityAtPoint( wheelAttachPos );
|
||||
var speed = velocity.Length;
|
||||
var speedDot = MathF.Abs( speed ) > 0.0f ? MathF.Abs( MathF.Min( Vector3.Dot( velocity, rotation.Up.Normal ) / speed, 0.0f ) ) : 0.0f;
|
||||
var speedAlongNormal = speedDot * speed;
|
||||
var correctionMultiplier = (1.0f - tr.Fraction) * (speedAlongNormal / 1000.0f);
|
||||
var correctionForce = correctionMultiplier * 50.0f * speedAlongNormal / dt;
|
||||
|
||||
body.ApplyImpulseAt( wheelAttachPos, tr.Normal * (springForce + damperForce + correctionForce) * dt );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user