Weapons/ToolGun/ToolMode.Helpers.cs
public abstract partial class ToolMode
{
/// <summary>
/// A point in the world selected by the current player's toolgun. We try to keep this as minimal as possible
/// while catering for as much as possible.
/// </summary>
public struct SelectionPoint
{
public GameObject GameObject { get; set; }
public Transform LocalTransform { get; set; }
/// <summary>
/// Returns true if GameObject is valid - which means we hit something
/// </summary>
public bool IsValid()
{
return GameObject.IsValid();
}
/// <summary>
/// Returns the position of the hit - in the world space. This is the transformed position of the LocalTransform relative to the GameObject.
/// </summary>
/// <returns></returns>
public Vector3 WorldPosition()
{
return GameObject.WorldTransform.PointToWorld( LocalTransform.Position );
}
/// <summary>
/// Returns the transform of the hit
/// </summary>
/// <returns></returns>
public Transform WorldTransform()
{
return GameObject.WorldTransform.ToWorld( LocalTransform );
}
/// <summary>
/// Returns true if this object is a part of the static map
/// </summary>
public bool IsWorld => GameObject.Tags.Has( "world" );
/// <summary>
/// Returns true if this object is a player
/// </summary>
public bool IsPlayer => GameObject.Tags.Has( "player" );
}
/// <summary>
/// Get a SelectionPoint along a ray.
/// </summary>
public SelectionPoint TraceFromRay( Ray ray, float distance, GameObject source )
{
var trace = Scene.Trace.Ray( ray, distance )
.IgnoreGameObjectHierarchy( source );
if ( TraceIgnoreTags.Any() )
trace = trace.WithoutTags( TraceIgnoreTags.ToArray() );
if ( TraceHitboxes )
trace = trace.UseHitboxes();
var tr = trace.Run();
var sp = new SelectionPoint
{
GameObject = tr.GameObject,
LocalTransform = tr.GameObject?.WorldTransform.ToLocal( new Transform( tr.EndPosition, Rotation.LookAt( tr.Normal ) ) ) ?? global::Transform.Zero
};
if ( sp.IsValid() )
{
// Ask the object if it allows toolgun interaction (Ownable and others can reject via IToolgunEvent)
var selectEvent = new IToolgunEvent.SelectEvent { User = Player.Network.Owner };
sp.GameObject.Root.RunEvent<IToolgunEvent>( x => x.OnToolgunSelect( selectEvent ) );
if ( selectEvent.Cancelled ) return default;
}
return sp;
}
/// <summary>
/// Get a SelectionPoint from the tool gun owner's eyes.
/// </summary>
public SelectionPoint TraceSelect()
{
var player = Toolgun?.Owner;
if ( !player.IsValid() ) return default;
return TraceFromRay( player.EyeTransform.ForwardRay, 4096, player.GameObject );
}
/// <summary>
/// Given a clicked point on a, and a clicked point on b, return a transform that places the objects so the points are touching
/// </summary>
public Transform GetEasyModePlacement( SelectionPoint a, SelectionPoint b )
{
var go = a.GameObject.Network.RootGameObject ?? a.GameObject;
var tx = b.WorldTransform();
tx.Rotation = tx.Rotation * a.LocalTransform.Rotation.Inverse * new Angles( 180, 0, 0 );
tx.Position += tx.Rotation * -a.LocalTransform.Position * go.WorldScale;
tx.Scale = go.WorldScale;
return tx;
}
/// <summary>
/// Helper to apply physics properties from a model to a GameObject's <see cref="Rigidbody"/>
/// </summary>
/// <param name="go"></param>
protected void ApplyPhysicsProperties( GameObject go )
{
var model = go.GetComponentInChildren<ModelRenderer>();
if ( !model.IsValid() ) return;
if ( model.Model.Physics is null ) return;
if ( model.Model.Physics.Parts.Count == 1 )
{
var part = model.Model.Physics.Parts[0];
var rb = go.GetComponent<Rigidbody>();
rb.MassOverride = part.Mass;
rb.LinearDamping = part.LinearDamping;
rb.AngularDamping = part.AngularDamping;
rb.OverrideMassCenter = part.OverrideMassCenter;
rb.MassCenterOverride = part.MassCenterOverride;
rb.GravityScale = part.GravityScale;
var props = go.GetOrAddComponent<PhysicalProperties>();
props.Mass = part.Mass;
props.GravityScale = part.GravityScale;
}
}
}