Code/ModelViewer/ModelViewerCharacterController/ModelViewerInteract.cs
using Sandbox;
public sealed class ModelViewerInteract : Component
{
[Property] private GameObject Eye { get; set; }
[Property] private GameObject Camera { get; set; }
[Property] public float ShootDamage { get; set; } = 9.0f;
public GameObject currentlyCarriedObject = null;
private float distance = 0;
private Rotation localRotation;
private Vector3 localMassCenter;
protected override void OnStart()
{
base.OnStart();
}
SoundEvent shootSound = Cloud.SoundEvent( "mdlresrc.toolgunshoot" );
[Property] public GameObject ImpactEffect { get; set; }
[Property] public GameObject DecalEffect { get; set; }
[Property] public ModelViewerPlayerController PlayerController { get; set; }
protected override void OnUpdate()
{
Eye.WorldRotation = Camera.WorldRotation;
var tr = Scene.PhysicsWorld.Trace.Ray( Eye.WorldPosition, Eye.WorldPosition + Eye.WorldRotation.Forward * 500 )
.WithoutTags( "player" )
.Run();
if ( PlayerController.CurrentCameraMode != ModelViewerPlayerController.CameraMode.Character ) return;
if ( Input.Pressed( "attack2" ) && currentlyCarriedObject == null )
{
var snd = Sound.Play( shootSound, WorldPosition );
snd.Volume = 0.25f;
if ( tr.Body.IsValid() )
{
var breakobject = tr.Body.GetGameObject();
var damage = new DamageInfo( ShootDamage, GameObject, GameObject );
if ( tr.Body is not null )
{
tr.Body.ApplyImpulseAt( tr.HitPosition, tr.Direction * 200.0f * tr.Body.Mass.Clamp( 0, 200 ) );
//Sound.Play( tr.Surface.Sounds.Bullet, WorldPosition );
if ( ImpactEffect is not null )
{
ImpactEffect.Clone( new Transform( tr.HitPosition + tr.Normal * 2.0f, Rotation.LookAt( tr.Normal ) ) );
}
if ( DecalEffect is not null )
{
var decal = DecalEffect.Clone( new Transform( tr.HitPosition + tr.Normal * 2.0f, Rotation.LookAt( -tr.Normal, Vector3.Random ), Random.Shared.Float( 0.8f, 1.2f ) ) );
decal.SetParent( tr.Body.GetGameObject() );
}
}
foreach ( var damageable in breakobject.Components.GetAll<IDamageable>() )
{
damageable.OnDamage( damage );
}
}
}
if ( Input.Pressed( "use" ) )
{
if ( tr.Body.IsValid() )
{
var interactedObject = tr.Body.GetGameObject();
var interactComponent = interactedObject.Components.Get<ModelRenderer>();
var physicsComponent = interactedObject.Components.Get<Rigidbody>();
if ( interactComponent != null && physicsComponent.IsValid() )
{
// Pick up the object
currentlyCarriedObject = interactedObject;
// Get the local rotation so we can convert it back to a global target rotation
localRotation = Eye.WorldRotation.Inverse * physicsComponent.WorldRotation;
// Keep the mass center to remove it from target position
localMassCenter = tr.Body.LocalMassCenter;
// Distance from the eye to mass center
distance = Eye.WorldPosition.Distance( tr.Body.MassCenter );
targetRotation = Eye.WorldRotation.Inverse * physicsComponent.WorldRotation;
HoldRot = physicsComponent.WorldRotation;
}
}
else
{
Sound.Play( "player_used" );
}
}
distance += Input.MouseWheel.y * 5;
distance = Math.Clamp( distance, 20, 500 );
if ( Input.Released( "use" ) )
{
if ( currentlyCarriedObject != null )
{
// Release the object
currentlyCarriedObject = null;
PlayerController.IsHoldingObject = false;
}
}
if ( Input.Pressed( "attack1" ) )
{
if ( currentlyCarriedObject != null )
{
//throw the object
var physicsBody = currentlyCarriedObject.Components.Get<Rigidbody>();
physicsBody.Velocity = Eye.WorldRotation.Forward * 500;
physicsBody.AngularVelocity = Vector3.Zero;
// Release the object
currentlyCarriedObject = null;
PlayerController.IsHoldingObject = false;
}
}
}
Rotation targetRotation;
Rotation HoldRot;
float RotateSnapAt = 45.0f;
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
if ( !currentlyCarriedObject.IsValid() )
return;
var physicsBody = currentlyCarriedObject.Components.Get<Rigidbody>();
if ( !physicsBody.IsValid() )
return;
// Target position is a distance away from the eye, minus center of mass offset
var currentPosition = physicsBody.WorldPosition;
var targetPosition = Eye.WorldPosition + Eye.WorldRotation.Forward * distance;
targetPosition -= physicsBody.WorldRotation * localMassCenter;
// Calculate the velocity needed to move from current to target position
var velocity = physicsBody.Velocity;
Vector3.SmoothDamp( currentPosition, targetPosition, ref velocity, 0.2f, Time.Delta );
physicsBody.Velocity = velocity;
// Add the eye rotation back onto the local rotation to make it a global rotation
var currentRotation = physicsBody.WorldRotation;
//targetRotation = Eye.WorldRotation * localRotation;
var eyerot = Rotation.From( new Angles( 0.0f, Camera.WorldRotation.y, 0.0f ) );
if ( Input.Down( "attack2" ) && currentlyCarriedObject != null )
{
PlayerController.IsHoldingObject = true;
DoRotation( eyerot, Input.MouseDelta );
}
else
{
PlayerController.IsHoldingObject = false;
}
HoldRot = Camera.WorldRotation * targetRotation;
if ( Input.Down( "run" ) )
{
var angles = HoldRot.Angles();
HoldRot = Rotation.From(
MathF.Round( angles.pitch / RotateSnapAt ) * RotateSnapAt,
MathF.Round( angles.yaw / RotateSnapAt ) * RotateSnapAt,
MathF.Round( angles.roll / RotateSnapAt ) * RotateSnapAt
);
}
// Calculate the velocity needed to move from current to target rotation
var angvelocity = physicsBody.AngularVelocity;
Rotation.SmoothDamp( currentRotation, HoldRot, ref angvelocity, 0.075f, Time.Delta );
physicsBody.AngularVelocity = angvelocity;
}
public void DoRotation( Rotation eye, Vector3 input )
{
var localRot = eye;
localRot *= Rotation.FromAxis( Vector3.Up, input.x * 0.125f );
localRot *= Rotation.FromAxis( Vector3.Right, input.y * 0.125f );
localRot = eye.Inverse * localRot;
targetRotation = localRot * targetRotation;
}
}