Weapons/ToolGun/Modes/Duplicator/LinkedGameObjectBuilder.cs


public class LinkedGameObjectBuilder
{
	public List<GameObject> Objects { get; } = new();

	/// <summary>
	/// Reject players and any objects with players as descendants. This is used for the duplicator to avoid accidentally duping the player and all their attachments.
	/// </summary>
	public bool RejectPlayers { get; set; } = false;

	/// <summary>
	/// Adds a GameObject. Won't find connections.
	/// </summary>
	public bool Add( GameObject obj )
	{
		if ( !obj.IsValid() ) return false;
		if ( obj.Tags.Contains( "world" ) ) return false;
		if ( Objects.Contains( obj ) ) return false;
		if ( obj.GetComponent<MapInstance>() is not null ) return false;
		if ( RejectPlayers && HasDescendantWithTag( obj, "player" ) ) return false;

		Objects.Add( obj );
		return true;
	}

	/// <summary>
	/// Add a GameObject with all connected GameObjects
	/// </summary>
	public void AddConnected( GameObject source )
	{
		if ( !source.IsValid() ) return;

		//
		// we're only interested in root objects
		//
		source = source.Root;

		// If we can't add this then don't add children
		// because we must have already added it, or it's the world.
		if ( !Add( source ) ) return;

		foreach ( var rb in source.GetComponentsInChildren<Rigidbody>() )
		{
			foreach ( var joint in rb.Joints )
			{
				AddConnected( joint.Object1 );
				AddConnected( joint.Object2 );
			}
		}

		foreach ( var collider in source.GetComponentsInChildren<Collider>() )
		{
			foreach ( var joint in collider.Joints )
			{
				AddConnected( joint.Object1 );
				AddConnected( joint.Object2 );
			}
		}

		foreach ( var link in source.GetComponentsInChildren<ManualLink>() )
		{
			if ( link.Body.IsValid() )
				AddConnected( link.Body );
		}

		// If any children have a physics filter, also connect them.
		foreach ( var filter in source.GetComponentsInChildren<PhysicsFilter>( includeSelf: false ) )
		{
			AddConnected( filter.GameObject );
		}
	}

	public void RemoveDeletedObjects()
	{
		Objects.RemoveAll( x => !x.IsValid() || x.IsDestroyed );
	}

	static bool HasDescendantWithTag( GameObject obj, string tag )
	{
		foreach ( var child in obj.Children )
		{
			if ( child.Tags.Has( tag ) ) return true;
			if ( HasDescendantWithTag( child, tag ) ) return true;
		}
		return false;
	}

	public void Clear()
	{
		Objects.Clear();
	}
}