Add files via upload

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

View 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;
}
}

View 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;
}
}

View 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();
}
}
}

View 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 );
}
}
}

View 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();
}
}

View 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
View 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 ) );
}
}
}

View 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 );
}
}

View 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();
}
}
}

View 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;
}
}

View 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 );
}
}
}
}

View 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 );
}
}

View 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" );
}
}

View 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 ) );
}
}
}
}

View 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;
}
}