🔍 s&box Package Code Search

Search C# source code, UI razor templates, shaders, and configs across s&box packages.

Showing code results for query: * (23 total matches found)
nolankicks.sceneloadingutility / Attributes.cs
Game library
using System;

namespace Sandbox.Events;

/// <summary>
/// Only valid on <see cref="IGameEventHandler{T}.OnGameEvent"/> implementations. Forces this
/// event handler to be invoked before any handlers not marked as early, except if more specific
/// constraints are given (i.e., <see cref="BeforeAttribute{T}"/>, <see cref="AfterAttribute{T}"/>).
/// </summary>
[AttributeUsage( AttributeTargets.Method )]
public sealed class EarlyAttribute : Attribute
{

}

/// <summary>
/// Only valid on <see cref="IGameEventHandler{T}.OnGameEvent"/> implementations. Forces this
/// event handler to be invoked after any handlers not marked as late, except if more specific
/// constraints are given (i.e., <see cref="BeforeAttribute{T}"/>, <see cref="AfterAttribute{T}"/>).
/// </summary>
[AttributeUsage( AttributeTargets.Method )]
public sealed class LateAttribute : Attribute
{

}

internal interface IBeforeAttribute
{
	Type Type { get; }
}

internal interface IAfterAttribute
{
	Type Type { get; }
}

/// <summary>
/// Only valid on <see cref="IGameEventHandler{T}.OnGameEvent"/> implementations. Forces this
/// event handler to be invoked before any handlers in the specified type.
/// </summary>
[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]
public sealed class BeforeAttribute<T> : Attribute, IBeforeAttribute
{
	Type IBeforeAttribute.Type => typeof(T);
}

/// <summary>
/// Only valid on <see cref="IGameEventHandler{T}.OnGameEvent"/> implementations. Forces this
/// event handler to be invoked after any handlers in the specified type.
/// </summary>
[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]
public sealed class AfterAttribute<T> : Attribute, IAfterAttribute
{
	Type IAfterAttribute.Type => typeof( T );
}
nolankicks.sceneloadingutility / SortingHelper.cs
Game library
using System.Collections.Generic;
using System.Linq;

namespace Sandbox.Events;

/// <summary>
/// Generate an ordering based on a set of first-most and last-most items, and
/// individual constraints between pairs of items. All first-most items will be
/// ordered before all last-most items, and any other items will be put in the
/// middle unless forced to be elsewhere by a constraint.
/// </summary>
internal class SortingHelper
{
	public record struct SortConstraint( int EarlierIndex, int LaterIndex )
	{
		public SortConstraint Complement => new ( LaterIndex, EarlierIndex );
	}

	private readonly int _itemCount;

	private readonly HashSet<SortConstraint> _initialConstraints = new HashSet<SortConstraint>();

	private readonly HashSet<int> _first = new HashSet<int>();
	private readonly HashSet<int> _last = new HashSet<int>();

	public SortingHelper( int itemCount )
	{
		_itemCount = itemCount;
	}

	public void AddConstraint( int earlierIndex, int laterIndex )
	{
		_initialConstraints.Add( new SortConstraint( earlierIndex, laterIndex ) );
	}

	public void AddFirst( int earlierIndex )
	{
		_first.Add( earlierIndex );
	}

	public void AddLast( int laterIndex )
	{
		_last.Add( laterIndex );
	}

	public bool Sort( List<int> result, out SortConstraint invalidConstraint )
	{
		var middle = new HashSet<int>();

		for ( var index = 0; index < _itemCount; ++index )
		{
			if ( !_first.Contains( index ) && !_last.Contains( index ) )
				middle.Add( index );
		}

		var allConstraints = new HashSet<SortConstraint>();
		var newConstraints = new Queue<SortConstraint>();
		var beforeDict = new Dictionary<int, HashSet<int>>();
		var afterDict = new Dictionary<int, HashSet<int>>();

		bool AddWorkingConstraint( int earlierIndex, int laterIndex, out SortConstraint constraint )
		{
			constraint = new SortConstraint( earlierIndex, laterIndex );

			if ( allConstraints.Contains( constraint.Complement ) )
				return false;

			if ( !allConstraints.Add( constraint ) )
				return true;

			newConstraints.Enqueue( constraint );

			if ( !beforeDict.TryGetValue( earlierIndex, out var before ) )
				beforeDict.Add( earlierIndex, before = new HashSet<int>() );

			if ( !afterDict.TryGetValue( laterIndex, out var after ) )
				afterDict.Add( laterIndex, after = new HashSet<int>() );

			before.Add( laterIndex );
			after.Add( earlierIndex );

			return true;
		}

		// Add initial constraints

		foreach ( var initialConstraint in _initialConstraints )
		{
			if ( !AddWorkingConstraint( initialConstraint.EarlierIndex, initialConstraint.LaterIndex, out invalidConstraint ) )
				return false;
		}

		// Everything in _first should be before everything in _last

		foreach ( var earlierIndex in _first )
		{
			foreach ( var laterIndex in _last )
			{
				if ( !AddWorkingConstraint( earlierIndex, laterIndex, out invalidConstraint ) )
					return false;
			}
		}

		// Keep propagating constraints until nothing changes

		while ( newConstraints.TryDequeue( out var nextConstraint ) )
		{
			// if a < b, and b < c, then a < c etc

			if ( beforeDict.TryGetValue( nextConstraint.LaterIndex, out var before ) )
			{
				foreach ( var laterIndex in before )
				{
					if ( !AddWorkingConstraint( nextConstraint.EarlierIndex, laterIndex, out invalidConstraint ) )
						return false;
				}
			}

			if ( afterDict.TryGetValue( nextConstraint.EarlierIndex, out var after ) )
			{
				foreach ( var earlierIndex in after )
				{
					if ( !AddWorkingConstraint( earlierIndex, nextConstraint.LaterIndex, out invalidConstraint ) )
					{
						return false;
					}
				}
			}
		}

		// Now if we have any items that aren't using GroupOrder.First, and haven't
		// determined that they are ordered before another item with GroupOrder.First,
		// we can safely order them after all GroupOrder.First items. And vice versa.

		foreach ( var middleIndex in middle )
		{
			var isBeforeAnyFirst = beforeDict.TryGetValue( middleIndex, out var before )
				&& before.Any( x => _first.Contains( x ) );

			var isAfterAnyLast = afterDict.TryGetValue( middleIndex, out var after )
				&& after.Any( x => _last.Contains( x ) );

			if ( !isBeforeAnyFirst )
			{
				foreach ( var earlierIndex in _first )
					AddWorkingConstraint( earlierIndex, middleIndex, out invalidConstraint );
			}

			if ( !isAfterAnyLast )
			{
				foreach ( var laterIndex in _last )
					AddWorkingConstraint( middleIndex, laterIndex, out invalidConstraint );
			}
		}

		// Now lets add items to the final ordering if all items that should be sorted
		// before them are already added to that ordering. We'll implement this by choosing
		// items that have an empty list / don't appear in afterDict, and update that
		// dictionary as we go.

		var earliestRemaining = new Queue<int>();

		// First, seed the queue with everything that's already not ordered after anything

		for ( var index = 0; index < _itemCount; ++index )
		{
			if ( !afterDict.ContainsKey( index ) )
			{
				earliestRemaining.Enqueue( index );
			}
		}

		result.Clear();

		while ( earliestRemaining.TryDequeue( out var nextIndex ) )
		{
			result.Add( nextIndex );

			foreach ( var laterIndex in beforeDict.TryGetValue( nextIndex, out var laterIndices )
				? laterIndices : Enumerable.Empty<int>() )
			{
				var beforeLater = afterDict[laterIndex];
				beforeLater.Remove( nextIndex );

				if ( beforeLater.Count == 0 )
					earliestRemaining.Enqueue( laterIndex );
			}
		}

		invalidConstraint = default;
		return result.Count == _itemCount;
	}
}
nolankicks.sceneloadingutility / UnitTests/LibraryTest.cs
UnitTest library
using Sandbox;

[TestClass]
public partial class LibraryTests
{
	[TestMethod]
	public void SceneTest()
	{
		var scene = new Scene();
		using ( scene.Push() )
		{
			var go = new GameObject();

			Assert.AreEqual( 1, scene.Directory.GameObjectCount );
		}
	}

}
nolankicks.sceneloadingutility / PlayerPusher.cs
Game library
public sealed class PlayerPusher : Component
{
	[Property] public float Radius { get; set; } = 100;

	protected override void DrawGizmos()
	{
		base.DrawGizmos();

		Gizmo.Draw.LineSphere( Vector3.Zero, Radius );
	}

	public static Vector3 GetPushVector( in Vector3 position, Scene scene, GameObject ignore )
	{
		Vector3 vec = default;

		foreach ( var pusher in scene.GetAllComponents<PlayerPusher>() )
		{
			if ( pusher.GameObject.IsAncestor( ignore ) )
				continue;

			pusher.Collect( position, ref vec );
		}

		return vec;
	}

	private void Collect( Vector3 position, ref Vector3 output )
	{
		var delta = (position - Transform.Position);
		if ( delta.Length > Radius ) return;

		delta.z = 0; // ignore z

		var distanceDelta = (delta.Length / Radius);

		output += delta.Normal * (1.0f - distanceDelta);
	}
}
nolankicks.sceneloadingutility / PlayerFootsteps.cs
Game library
public sealed class PlayerFootsteps : Component
{
	[Property] SkinnedModelRenderer Source { get; set; }

	protected override void OnEnabled()
	{
		if ( Source is null )
			return;

		Source.OnFootstepEvent += OnEvent;
	}

	protected override void OnDisabled()
	{
		if ( Source is null )
			return;

		Source.OnFootstepEvent -= OnEvent;
	}

	TimeSince timeSinceStep;

	private void OnEvent( SceneModel.FootstepEvent e )
	{
		if ( timeSinceStep < 0.2f )
			return;

		var tr = Scene.Trace
			.Ray( e.Transform.Position + Vector3.Up * 20, e.Transform.Position + Vector3.Up * -20 )
			.Run();

		if ( !tr.Hit )
			return;

		if ( tr.Surface is null )
			return;

		timeSinceStep = 0;

		var sound = e.FootId == 0 ? tr.Surface.Sounds.FootLeft : tr.Surface.Sounds.FootRight;
		if ( sound is null ) return;

		var handle = Sound.Play( sound, tr.HitPosition + tr.Normal * 5 );
		handle.Volume *= e.Volume;
		handle.Update();
	}
}
nolankicks.sceneloadingutility / BouncyBone.cs
Game library
public sealed class BouncyBone : TransformProxyComponent
{
	JiggleBoneState state = new JiggleBoneState();

	[Property]
	public Vector3 Influence { get; set; } = new Vector3( 1, 1, 1 );

	[Property, Range( 0, 50.0f )]
	public float Stiffness { get; set; } = 1;

	[Property, Range( 0, 50.0f )]
	public float Damping { get; set; } = 1;

	Transform LocalJigglePosition;
	TransformSpring springer;

	protected override void OnEnabled()
	{
		springer = new TransformSpring();
		springer.Transform = Transform.World;
		LocalJigglePosition = springer.Transform;

		base.OnEnabled();


	}

	protected override void OnUpdate()
	{
		var oldPos = LocalJigglePosition;

		using ( Transform.DisableProxy() )
		{
			var worldTx = Transform.World;

			springer.Stiffness = Stiffness;
			springer.Damping = Damping;
			springer.UpdateSpring( Transform.World, Time.Delta );

			var tx = GameObject.Parent.Transform.World.ToLocal( springer.Transform );
			LocalJigglePosition = tx;
		}

		if ( oldPos != LocalJigglePosition )
		{
			MarkTransformChanged();
		}
	}

	public override Transform GetLocalTransform()
	{
		return LocalJigglePosition;
	}
}


public struct TransformSpring
{
	public Transform Transform;

	private Vector3 velocityPosition;
	private Vector3 velocityScale;
	private Rotation velocityRotation = Rotation.Identity;

	public float Stiffness = 1.5f;  // Spring stiffness, higher is stiffer
	public float Damping = 1.0f;      // Damping, higher is less oscillation

	public TransformSpring()
	{
		Transform = global::Transform.Zero;
	}

	public void UpdateSpring( Transform target, float deltaTime )
	{
		Transform.Position = SpringLerp( Transform.Position, target.Position, ref velocityPosition, deltaTime );
		Transform.Scale = SpringLerp( Transform.Scale, target.Scale, ref velocityScale, deltaTime );
		Transform.Rotation = target.Rotation;
	}

	private Vector3 SpringLerp( Vector3 current, Vector3 target, ref Vector3 velocity, float deltaTime )
	{
		float omega = 2f * MathF.PI * Stiffness;
		float damper = MathF.Exp( -Damping * deltaTime * omega );

		Vector3 displacement = current - target;
		Vector3 springForce = -omega * omega * displacement;
		Vector3 dampingForce = -2f * omega * Damping * velocity;

		Vector3 acceleration = springForce + dampingForce;
		velocity = (velocity + acceleration * deltaTime) * damper;
		return target + displacement + velocity * deltaTime;
	}


}
nolankicks.sceneloadingutility / GameEvent.cs
Game library
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace Sandbox.Events;

/// <summary>
/// Interface for event payloads that can be listened for by <see cref="IGameEventHandler{T}"/>s.
/// </summary>
public interface IGameEvent { }

/// <summary>
/// Interface for components that handle game events with a payload of type <see cref="T"/>.
/// </summary>
/// <typeparam name="T">Event payload type.</typeparam>
public interface IGameEventHandler<in T>
	where T : IGameEvent
{
	/// <summary>
	/// Called when an event with payload of type <see cref="T"/> is dispatched on a <see cref="GameObject"/>
	/// that contains this component, including on a descendant.
	/// </summary>
	/// <param name="eventArgs">Event payload.</param>
	void OnGameEvent( T eventArgs );
}

/// <summary>
/// Helper for dispatching game events in a scene.
/// </summary>
public static class GameEvent
{
	private static Dictionary<Type, IReadOnlyDictionary<Type, int>> HandlerOrderingCache { get; } = new();

	/// <summary>
	/// Notifies all <see cref="IGameEventHandler{T}"/> components that are within <paramref name="root"/>,
	/// with a payload of type <typeparamref name="T"/>.
	/// </summary>
	public static void Dispatch<T>( this GameObject root, T eventArgs )
		where T : IGameEvent
	{
		var handlers = (root is Scene scene
			? scene.GetAllComponents<IGameEventHandler<T>>() // I think this is more efficient?
			: root.Components.GetAll<IGameEventHandler<T>>())
			.ToArray();

		if ( !HandlerOrderingCache.TryGetValue( typeof(T), out var ordering ) || handlers.Any( x => !ordering.ContainsKey( x.GetType() ) ) )
		{
			ordering = HandlerOrderingCache[typeof(T)] = GetHandlerOrdering<T>();
		}

		List<Exception>? exceptions = null;

		foreach ( var handler in handlers.OrderBy( x => ordering[x.GetType()] ) )
		{
			try
			{
				handler.OnGameEvent( eventArgs );
			}
			catch ( Exception e )
			{
				exceptions ??= new();
				exceptions.Add( e );
			}
		}

		switch ( exceptions?.Count )
		{
			case 1:
				Log.Error( exceptions[0] );
				break;

			case > 1:
				Log.Error( new AggregateException( exceptions ) );
				break;
		}
	}

	private static bool IsImplementingMethodName( string methodName )
	{
		if ( methodName == nameof(IGameEventHandler<IGameEvent>.OnGameEvent) )
		{
			return true;
		}

		return methodName.StartsWith( "Sandbox.Events.IGameEventHandler<" ) && methodName.EndsWith( ">.OnGameEvent" );
	}

	private static MethodDescription? GetImplementation<T>( TypeDescription type )
	{
		foreach ( var method in type.Methods )
		{
			if ( method.IsStatic ) continue;
			if ( method.Parameters.Length != 1 ) continue;
			if ( method.Parameters[0].ParameterType != typeof( T ) ) continue;

			if ( !IsImplementingMethodName( method.Name ) ) continue;

			return method;
		}

		return null;
	}

	private static IReadOnlyDictionary<Type, int> GetHandlerOrdering<T>()
		where T : IGameEvent
	{
		var types = TypeLibrary.GetTypes<IGameEventHandler<T>>().ToArray();
		var helper = new SortingHelper( types.Length );

		for ( var i = 0; i < types.Length; ++i )
		{
			var type = types[i];
			var method = GetImplementation<T>( type );

			if ( method is null )
			{
				Log.Warning( $"Can't find {nameof( IGameEventHandler<T> )}<{typeof( T ).Name}> implementation in {type.Name}!" );
				continue;
			}

			foreach ( var attrib in method.Attributes )
			{
				switch ( attrib )
				{
					case EarlyAttribute:
						helper.AddFirst( i );
						break;

					case LateAttribute:
						helper.AddLast( i );
						break;

					case IBeforeAttribute before:
						for ( var j = 0; j < types.Length; ++j )
						{
							if ( i == j ) continue;

							var other = types[j];

							if ( before.Type.IsAssignableFrom( other.TargetType ) )
							{
								helper.AddConstraint( i, j );
							}
						}

						break;

					case IAfterAttribute after:
						for ( var j = 0; j < types.Length; ++j )
						{
							if ( i == j ) continue;

							var other = types[j];

							if ( after.Type.IsAssignableFrom( other.TargetType ) )
							{
								helper.AddConstraint( j, i );
							}
						}

						break;
				}
			}
		}

		var ordering = new List<int>();

		if ( !helper.Sort( ordering, out var invalid ) )
		{
			Log.Error( $"Invalid event ordering constraint between {types[invalid.EarlierIndex].Name} and {types[invalid.LaterIndex].Name}!" );
			return ImmutableDictionary<Type, int>.Empty;
		}

		return Enumerable.Range( 0, ordering.Count )
			.ToImmutableDictionary( i => types[ordering[i]].TargetType, i => i );
	}
}

public delegate void GameEventAction<in T>( T eventArgs )
	where T : IGameEvent;

/// <summary>
/// Base class for components that expose game events to Action Graph.
/// </summary>
public abstract class GameEventComponent<T> : Component, IGameEventHandler<T>
	where T : IGameEvent
{
	/// <summary>
	/// Action invoked when the <typeparamref name="T"/> event is dispatched.
	/// </summary>
	[Property]
	public GameEventAction<T>? OnEvent { get; set; }

	/// <summary>
	/// If this component is within a state machine, optional state to transition
	/// to when this event is dispatched.
	/// </summary>
	[Property]
	public StateComponent? NextState { get; set; }

	void IGameEventHandler<T>.OnGameEvent( T eventArgs )
	{
		OnEvent?.Invoke( eventArgs );

		if ( NextState is not null )
		{
			Components.GetInAncestorsOrSelf<StateMachineComponent>()?.Transition( NextState );
		}
	}
}
nolankicks.sceneloadingutility / StateMachine.cs
Game library
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox.Diagnostics;

namespace Sandbox.Events;

/// <summary>
/// <para>
/// A state machine containing a set of <see cref="StateComponent"/>s. The <see cref="GameObject"/> containing
/// the currently active state will be enabled (including its ancestors), and all other objects containing states
/// are disabled.
/// </para>
/// <para>
/// The currently active state is controlled by the owner, and synchronised over the network. When a transition occurs,
/// a <see cref="LeaveStateEvent"/> is dispatched on the old state's containing object, followed by a
/// <see cref="EnterStateEvent"/> event on the object containing the new state. These events are only dispatched
/// on the owner.
/// </para>
/// </summary>
[Title( "State Machine" ), Category( "State Machines" )]
public sealed class StateMachineComponent : Component
{
	private StateComponent? _currentState;

	/// <summary>
	/// How many instant state transitions in a row until we throw an error?
	/// </summary>
	public const int MaxInstantTransitions = 16;

	/// <summary>
	/// Which state is currently active?
	/// </summary>
	[Property, Sync]
	public StateComponent? CurrentState
	{
		get => _currentState;
		set
		{
			if ( _currentState == value ) return;
			_currentState = value;

			if ( Network.IsProxy )
			{
				EnableActiveStates( false );
			}
		}
	}

	/// <summary>
	/// Which state will we transition to next, at <see cref="NextStateTime"/>?
	/// </summary>
	[Sync]
	public StateComponent? NextState { get; set; }

	/// <summary>
	/// What time will we transition to <see cref="NextState"/>?
	/// </summary>
	[Sync]
	public float NextStateTime { get; set; }

	/// <summary>
	/// All states found on descendant objects.
	/// </summary>
	public IEnumerable<StateComponent> States => Components.GetAll<StateComponent>( FindMode.EverythingInSelfAndDescendants );

	protected override void OnStart()
	{
		foreach ( var state in States )
		{
			state.Enabled = false;
			state.GameObject.Enabled = state.GameObject == GameObject;
		}

		if ( !Network.IsProxy && CurrentState is { } current )
		{
			Transition( current );
		}
	}

	private void EnableActiveStates( bool dispatch )
	{
		var current = CurrentState;
		var active = current?.GetAncestors() ?? Array.Empty<StateComponent>();
		var activeSet = active.ToHashSet();

		var toDeactivate = new Queue<StateComponent>( States.Where( x => x.Enabled && !activeSet.Contains( x ) ).Reverse() );
		var toActivate = new Queue<StateComponent>( active.Where( x => !x.Enabled ) );

		if ( current != null )
		{
			toActivate.Enqueue( current );
		}

		while ( toDeactivate.TryDequeue( out var next ) )
		{
			next.Leave( dispatch );

			if ( toDeactivate.All( x => x.GameObject != next.GameObject ) && toActivate.All( x => x.GameObject != next.GameObject ) )
			{
				next.GameObject.Enabled = false;
			}
		}

		while ( toActivate.TryDequeue( out var next ) )
		{
			next.GameObject.Enabled = true;

			next.Enter( dispatch );
		}
	}

	protected override void OnFixedUpdate()
	{
		if ( Network.IsProxy )
		{
			return;
		}

		if ( CurrentState is not { } current )
		{
			return;
		}

		current.Update();

		var transitions = 0;

		while ( transitions++ < MaxInstantTransitions )
		{
			if ( NextState is not { } next || !(Time.Now >= NextStateTime) )
			{
				return;
			}

			if ( next.DefaultNextState is not null )
			{
				Transition( next.DefaultNextState, next.DefaultDuration );
			}
			else
			{
				ClearTransition();
			}

			CurrentState = next;

			EnableActiveStates( true );
		}
	}

	/// <summary>
	/// Queue up a transition to the given state. This will occur at the end of
	/// a fixed update on the state machine.
	/// </summary>
	public void Transition( StateComponent next, float delaySeconds = 0f )
	{
		Assert.NotNull( next );
		Assert.False( Network.IsProxy );

		NextState = next;
		NextStateTime = Time.Now + delaySeconds;
	}

	/// <summary>
	/// Removes any pending transitions, so this state machine will remain in the
	/// current state until another transition is queued with <see cref="Transition"/>.
	/// </summary>
	public void ClearTransition()
	{
		Assert.False( Network.IsProxy );

		NextState = null;
		NextStateTime = float.PositiveInfinity;
	}
}
nolankicks.sceneloadingutility / __gen_RazorNamespace.cs
Game library
global using Microsoft.AspNetCore.Components; 
global using Microsoft.AspNetCore.Components.Rendering;
nolankicks.sceneloadingutility / UnitTests/UnitTest.cs
UnitTest library
global using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestInit
{
	[AssemblyInitialize]
	public static void ClassInitialize( TestContext context )
	{
		Sandbox.Application.InitUnitTest();
	}
}
nolankicks.sceneloadingutility / Code/SceneLoadingExample.cs
Game library
using System.Linq;
using Microsoft.VisualBasic;
using Sandbox;
using SceneLoading;
public sealed class ChangeSceneTrigger : Component, Component.ITriggerListener
{
	[Property] public SceneFile sceneFile { get; set; }
	[Property] public GameObject PrefabTest { get; set; }

	void ITriggerListener.OnTriggerEnter( Sandbox.Collider other )
	{
		if ( other.GameObject.Tags.Has( "player" ) )
		{
			LoadScene();
		}
	}

	void ITriggerListener.OnTriggerExit( Sandbox.Collider other )
	{

	}

	public void LoadScene()
	{
		var customScene = new CustomScene( sceneFile );
		if ( customScene.GetAllObjectsByType( typeof( SkinnedModelRenderer ) ).Count() == 0 )
		{
			customScene.CreateObject( new GameObject() );
		}

		customScene.LoadScene();
	}
}
nolankicks.sceneloadingutility / MyLibraryComponent.cs
Game library
using Sandbox;

/// <summary>
/// This is a component - in your library!
/// </summary>
[Title( "Screen Shot Library - My Component" )]
public class MyLibraryComponent : Component
{

}
nolankicks.sceneloadingutility / SceneLoadingExample.cs
Game library
using System.Linq;
using Microsoft.VisualBasic;
using Sandbox;
using SceneLoading;
public sealed class ChangeSceneTrigger : Component, Component.ITriggerListener
{
	[Property] public SceneFile sceneFile { get; set; }
	[Property] public GameObject PrefabTest { get; set; }

	void ITriggerListener.OnTriggerEnter( Sandbox.Collider other )
	{
		if ( other.GameObject.Tags.Has( "player" ) )
		{
			LoadScene();
		}
	}

	void ITriggerListener.OnTriggerExit( Sandbox.Collider other )
	{

	}

	public void LoadScene()
	{
		var customScene = new CustomScene( sceneFile );
		if ( customScene.GetAllObjectsByType( typeof( SkinnedModelRenderer ) ).Count() == 0 )
		{
			customScene.CreateObject( new GameObject() );
		}

		customScene.LoadScene();
	}
}
nolankicks.sceneloadingutility / State.cs
Game library
using System;
using System.Collections.Generic;

namespace Sandbox.Events;

/// <summary>
/// Marks a <see cref="GameObject"/> as a state in a state machine. There must be a
/// <see cref="StateMachineComponent"/> on an ancestor object for this to function.
/// The object containing this state (and all ancestors) will be enabled when the state
/// machine transitions to this state, and will disable again when this state is exited.
/// States may be nested within each other.
/// </summary>
[Title( "State" ), Category( "State Machines" )]
public sealed class StateComponent : Component
{
	private StateMachineComponent? _stateMachine;

	/// <summary>
	/// Which state machine does this state belong to?
	/// </summary>
	public StateMachineComponent StateMachine =>
		_stateMachine ??= Components.GetInAncestorsOrSelf<StateMachineComponent>();

	/// <summary>
	/// Which state is this nested in, if any?
	/// </summary>
	public StateComponent? Parent => Components.GetInAncestors<StateComponent>( true );

	/// <summary>
	/// Transition to this state by default.
	/// </summary>
	[Property]
	public StateComponent? DefaultNextState { get; set; }

	/// <summary>
	/// If <see cref="DefaultNextState"/> is given, transition after this delay in seconds.
	/// </summary>
	[Property, HideIf( nameof( DefaultNextState ), null )]
	public float DefaultDuration { get; set; }

	/// <summary>
	/// Event dispatched on the owner when this state is entered.
	/// </summary>
	[Property]
	public event Action? OnEnterState;

	/// <summary>
	/// Event dispatched on the owner while this state is active.
	/// </summary>
	[Property]
	public event Action? OnUpdateState;

	/// <summary>
	/// Event dispatched on the owner when this state is exited.
	/// </summary>
	[Property]
	public event Action? OnLeaveState;

	internal void Enter( bool dispatch )
	{
		Enabled = true;

		if ( dispatch )
		{
			OnEnterState?.Invoke();
			GameObject.Dispatch( new EnterStateEvent( this ) );
		}
	}

	internal void Update()
	{
		OnUpdateState?.Invoke();
		Scene.Dispatch( new UpdateStateEvent( this ) );
	}

	internal void Leave( bool dispatch )
	{
		if ( dispatch )
		{
			OnLeaveState?.Invoke();
			GameObject.Dispatch( new LeaveStateEvent( this ) );
		}

		Enabled = false;
	}

	/// <summary>
	/// Queue up a transition to the given state. This will occur at the end of
	/// a fixed update on the state machine.
	/// </summary>
	public void Transition( StateComponent next, float delaySeconds = 0f )
	{
		StateMachine.Transition( next, delaySeconds );
	}

	/// <summary>
	/// Queue up a transition to the default next state.
	/// </summary>
	public void Transition()
	{
		StateMachine.Transition( DefaultNextState! );
	}

	internal IReadOnlyList<StateComponent> GetAncestors()
	{
		var list = new List<StateComponent>();

		var parent = Parent;

		while ( parent != null )
		{
			list.Add( parent );
			parent = parent.Parent;
		}

		list.Reverse();

		return list;
	}
}

/// <summary>
/// Event dispatched on the owner when a <see cref="StateMachineComponent"/> changes state.
/// Only invoked on components on the same object as the new state.
/// </summary>
public record EnterStateEvent( StateComponent State ) : IGameEvent;

/// <inheritdoc cref="EnterStateEvent"/>
[Title( "Enter State Event" ), Group( "State Machines" ), Icon( "electric_bolt" )]
public sealed class EnterStateEventComponent : GameEventComponent<EnterStateEvent> { }

/// <summary>
/// Event dispatched on the owner when a <see cref="StateMachineComponent"/> changes state.
/// Only invoked on components on the same object as the old state.
/// </summary>
public record LeaveStateEvent( StateComponent State ) : IGameEvent;

/// <inheritdoc cref="LeaveStateEvent"/>
[Title( "Leave State Event" ), Group( "State Machines" ), Icon( "electric_bolt" )]
public sealed class LeaveStateEventComponent : GameEventComponent<LeaveStateEvent> { }

/// <summary>
/// Event dispatched on the owner every fixed update while a <see cref="StateComponent"/> is active.
/// Only invoked on components on the same object as the state.
/// </summary>
public record UpdateStateEvent( StateComponent State ) : IGameEvent;

/// <inheritdoc cref="UpdateStateEvent"/>
[Title( "Update State Event" ), Group( "State Machines" ), Icon( "electric_bolt" )]
public sealed class UpdateStateEventComponent : GameEventComponent<UpdateStateEvent> { }
nolankicks.sceneloadingutility / SceneLoadingUtility.cs
Game library
using System;
using System.Collections.Generic;
using System.Data;
using System.Dynamic;
using System.Formats.Tar;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Microsoft.CSharp.RuntimeBinder;
using Sandbox;
using Sandbox.ActionGraphs;
namespace SceneLoading
{
	public class SceneLoadingUtility
	{
		public static void LoadScene( SceneFile sceneFile, SceneLoadingResource sceneLoadingResource )
		{
			var objects = sceneFile.GameObjects;
			Game.ActiveScene.Load( new SceneFile() );

			foreach ( var obj in objects )
			{
				var gameObject = Game.ActiveScene.CreateObject();
				gameObject.Deserialize( obj );
			}

			foreach ( var clone in sceneLoadingResource.SceneLoadingClasses )
			{
				bool gameObjectSpawned = false;

				foreach ( var componentType in clone.ComponentTypes )
				{

					if ( clone.Flags == LoadingFlags.CheckForComponents && Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).Count() > 0 )
					{
						Log.Info( "Component found, skipping" );
						continue;
					}
					else if ( clone.Flags == LoadingFlags.DestroyFirst && Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).Count() > 0 )
					{
						Log.Info( "Component found, replacing" );
						Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).FirstOrDefault()?.GameObject.Destroy();
						if ( gameObjectSpawned ) return;
						var gb = clone.Prefab.Clone();
						gb.BreakFromPrefab();
						if ( clone.NetworkSpawn ) gb.NetworkSpawn( null );
						gameObjectSpawned = true;
						continue;
					}
					else if ( clone.Flags == LoadingFlags.DestroyAll && Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).Count() > 0 )
					{
						Log.Info( "Component found, replacing all" );
						foreach ( var component in Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ) )
						{
							component.GameObject.Destroy();
						}
						if ( gameObjectSpawned ) return;
						var gb = clone.Prefab.Clone();
						gb.BreakFromPrefab();
						if ( clone.NetworkSpawn ) gb.NetworkSpawn( null );
						gameObjectSpawned = true;
						continue;
					}

					if ( !gameObjectSpawned )
					{
						var obj = clone.Prefab.Clone();
						obj.BreakFromPrefab();
						if ( clone.NetworkSpawn ) obj.NetworkSpawn( null );
						gameObjectSpawned = true;
					}
				}
			}
		}
	}
	public enum LoadingFlags
	{
		None,
		[Description( "Checks if a component is in the scene, if it is, the prefab will not be spawned" )]
		CheckForComponents,
		[Description( "Checks if a component is in the scene, if it is, the first one will be destroyed, and the prefab will be spawned" )]
		DestroyFirst,
		[Description( "Checks if a component is in the scene, if it is, all of them will be destroyed and the prefab will be spawned" )]
		DestroyAll,

	}


	[GameResource( "SceneLoadingResource", "loading", "A resource that allows spawning of prefabs on scene start", Icon = "public" )]
	public class SceneLoadingResource : GameResource
	{
		public List<SceneLoadingClass> SceneLoadingClasses { get; set; } = new();
	}
	public class SceneLoadingClass
	{
		public GameObject Prefab { get; set; }
		public LoadingFlags Flags { get; set; } = LoadingFlags.None;
		public List<Type> ComponentTypes { get; set; }
		public bool NetworkSpawn { get; set; } = false;


		public SceneLoadingClass()
		{
			Prefab = null;
			Flags = LoadingFlags.None;
			ComponentTypes = null;
		}

		public SceneLoadingClass( GameObject prefab, LoadingFlags flags, List<Type> componentType )
		{
			Prefab = prefab;
			Flags = flags;
			ComponentTypes = componentType;
		}
	}
	[Description( "A custom scene that allows for manipulation of the scene file before loading" )]
	public class CustomScene
	{
		[Description( "The scenefile you are manipulating, override to change it" )] public virtual SceneFile sceneFileDupe { get; set; }
		public string RawScene { get; private set; }
		public Scene newScene { get; private set; } = new();
		[Description( "Called when a scene object is created, return the object you want to spawn, or null to use the default object" )]
		public Action<JsonObject> OnSceneObjectCreated { get; set; }
		public Action<JsonObject[]> BeforeSceneLoaded { get; set; }
		public CustomScene( SceneFile sceneFile )
		{
			sceneFileDupe = new SceneFile();
			sceneFileDupe.GameObjects = sceneFile.GameObjects
				.Select( obj => JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) ) )
				.ToArray();
		}

		internal void LoadSceneInternal()
		{
			var finalScene = new SceneFile();
			var finalList = new List<JsonObject>();
			foreach ( var obj in sceneFileDupe.GameObjects )
			{
				OnSceneObjectCreated?.Invoke( obj );
				finalList.Add( obj );
			}

			finalScene.GameObjects = finalList.ToArray();
			BeforeSceneLoaded?.Invoke( finalScene.GameObjects );
			Game.ActiveScene.Load( finalScene );
		}
		[Description( "Load the custom scene" )]
		public void LoadScene()
		{
			LoadSceneInternal();
		}

		[Description( "Create a GameObject within the custom scene" )]
		public void CreateObject( GameObject gameObject )
		{
			var clone = gameObject.Clone();
			var objects = sceneFileDupe.GameObjects.ToList();
			objects.Add( clone.Serialize() );
			Log.Info( clone.Serialize().ToString() );
			sceneFileDupe.GameObjects = objects.ToArray();
		}

		[Description( "Remove a GameObject from the custom scene" )]
		public void RemoveObject( JsonNode obj )
		{
			List<JsonObject> gameObjects = sceneFileDupe.GameObjects.ToList();
			var selectedObject = gameObjects.Find( x => x == obj );
			gameObjects.Remove( selectedObject );
			sceneFileDupe.GameObjects = gameObjects.ToArray();

		}



		public void RemoveComponentByType( JsonObject obj, Type type )
		{
			var preObj = JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) );
			if ( obj.TryGetPropertyValue( "Components", out var jsonnode ) && jsonnode is not null )
			{
				var jsonString = jsonnode.ToString();
				if ( !string.IsNullOrWhiteSpace( jsonString ) )
				{
					var components = JsonSerializer.Deserialize<List<JsonNode>>( jsonString );
					var component = components.Find( x => x["__type"]?.ToString() == type.ToString() );
					if ( component is not null )
					{
						components.Remove( component );
						obj["Components"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );
					}
				}

				var gbList = sceneFileDupe.GameObjects.ToList();
				if ( gbList.Find( x => x == obj ) is null )
				{
					var parentNode = FindParent( preObj );
					var parent = gbList.Find( x => x == parentNode );
					if ( parent != null && parent.TryGetPropertyValue( "Children", out var childrenJsonNode ) && childrenJsonNode != null )
					{
						if ( childrenJsonNode is JsonArray childrenArray )
						{
							UpdateChildComponentsRecursively( childrenArray, obj );
						}
						else
						{
							Log.Warning( "The 'Children' node is not of type 'JsonArray'." );
						}
					}
				}
				else
				{
					gbList[gbList.FindIndex( x => x == obj )] = obj;
				}
				sceneFileDupe.GameObjects = gbList.ToArray();
			}
		}

		private void UpdateChildComponentsRecursively( JsonArray childrenArray, JsonObject obj )
		{
			foreach ( var child in childrenArray )
			{
				if ( child is JsonObject childObject )
				{
					if ( childObject["__guid"].ToString() == obj["__guid"].ToString() )
					{
						childObject["Components"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( obj["Components"] ) );
					}
					else if ( childObject.TryGetPropertyValue( "Children", out var nestedChildrenJsonNode ) && nestedChildrenJsonNode is JsonArray nestedChildrenArray )
					{
						UpdateChildComponentsRecursively( nestedChildrenArray, obj );
					}
				}
			}
		}

		public void AddComponent( JsonObject obj, JsonObject newComponent )
		{
			var preObj = JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) );
			if ( obj.TryGetPropertyValue( "Components", out var jsonnode ) && jsonnode is not null )
			{
				var jsonString = jsonnode.ToString();
				if ( !string.IsNullOrWhiteSpace( jsonString ) )
				{
					var components = JsonSerializer.Deserialize<List<JsonNode>>( jsonString );
					components.Add( newComponent );
					obj["Components"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );
				}
				else
				{
					var components = new List<JsonNode> { newComponent };
					obj["Components"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );
				}

				var gbList = sceneFileDupe.GameObjects.ToList();
				if ( gbList.Find( x => x == obj ) is null )
				{
					var parentNode = FindParent( preObj );
					var parent = gbList.Find( x => x == parentNode );
					if ( parent != null && parent.TryGetPropertyValue( "Children", out var childrenJsonNode ) && childrenJsonNode != null )
					{
						if ( childrenJsonNode is JsonArray childrenArray )
						{
							UpdateChildComponentsRecursively( childrenArray, obj );
						}
						else
						{
							Log.Warning( "The 'Children' node is not of type 'JsonArray'." );
						}
					}
				}
				else
				{
					gbList[gbList.FindIndex( x => x == obj )] = obj;
				}
				sceneFileDupe.GameObjects = gbList.ToArray();
			}
		}

		public void AddComponentByType( JsonObject obj, Type componentType )
		{
			var preObj = JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) );
			if ( obj.TryGetPropertyValue( "Components", out var jsonnode ) && jsonnode is not null )
			{
				var jsonString = jsonnode.ToString();
				var components = !string.IsNullOrWhiteSpace( jsonString )
					? JsonSerializer.Deserialize<List<JsonNode>>( jsonString )
					: new List<JsonNode>();

				var newComponent = new JsonObject
				{
					["__type"] = componentType.ToString()
				};

				components.Add( newComponent );
				obj["Components"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );

				var gbList = sceneFileDupe.GameObjects.ToList();
				if ( gbList.Find( x => x == obj ) is null )
				{
					var parentNode = FindParent( preObj );
					var parent = gbList.Find( x => x == parentNode );
					if ( parent != null && parent.TryGetPropertyValue( "Children", out var childrenJsonNode ) && childrenJsonNode != null )
					{
						if ( childrenJsonNode is JsonArray childrenArray )
						{
							UpdateChildComponentsRecursively( childrenArray, obj );
						}
						else
						{
							Log.Warning( "The 'Children' node is not of type 'JsonArray'." );
						}
					}
				}
				else
				{
					gbList[gbList.FindIndex( x => x == obj )] = obj;
				}
				sceneFileDupe.GameObjects = gbList.ToArray();
			}
		}

		public JsonObject FindParent( JsonNode children )
		{
			foreach ( var obj in sceneFileDupe.GameObjects )
			{
				var parent = FindParentRecursive( obj, children );
				if ( parent != null )
				{
					return parent;
				}
			}
			return null;
		}

		private JsonObject FindParentRecursive( JsonObject parent, JsonNode children )
		{
			if ( parent.TryGetPropertyValue( "Children", out var childrenJsonNode ) )
			{
				var childrenList = JsonSerializer.Deserialize<List<JsonObject>>( childrenJsonNode.ToString() );
				if ( childrenList != null )
				{
					foreach ( var child in childrenList )
					{
						if ( child != null && child["__guid"].ToString() == children["__guid"].ToString() )
						{
							return parent;
						}

						var foundParent = FindParentRecursive( child, children );
						if ( foundParent != null )
						{
							return parent;
						}
					}
				}
			}
			return null;
		}

		public IEnumerable<JsonObject> GetAllObjectsByType( Type type )
		{
			return GetAllObjectsByTypeRecursive( sceneFileDupe.GameObjects, type );
		}

		public IEnumerable<JsonObject> GetAllObjectsByGuid( string guid )
		{
			return GetAllObjectsByGuidRecursive( sceneFileDupe.GameObjects, guid );
		}

		private IEnumerable<JsonObject> GetAllObjectsByTypeRecursive( IEnumerable<JsonObject> gameObjects, Type type )
		{
			foreach ( var obj in gameObjects )
			{
				if ( obj.TryGetPropertyValue( "Components", out var jsonnode ) && jsonnode is not null )
				{
					var jsonString = jsonnode.ToString();
					if ( !string.IsNullOrWhiteSpace( jsonString ) )
					{
						var components = JsonSerializer.Deserialize<List<JsonNode>>( jsonString );
						if ( components.Any( component => component["__type"]?.ToString() == type.ToString() ) )
						{
							yield return obj;
						}
					}
				}

				if ( obj.TryGetPropertyValue( "Children", out var childrenJsonNode ) && childrenJsonNode is not null )
				{
					var childrenString = childrenJsonNode.ToString();
					if ( !string.IsNullOrWhiteSpace( childrenString ) )
					{
						var children = JsonSerializer.Deserialize<List<JsonObject>>( childrenString );
						foreach ( var child in GetAllObjectsByTypeRecursive( children, type ) )
						{
							yield return child;
						}
					}
				}
			}
		}

		private IEnumerable<JsonObject> GetAllObjectsByGuidRecursive( IEnumerable<JsonObject> gameObjects, string guid )
		{
			foreach ( var obj in gameObjects )
			{
				if ( obj.TryGetPropertyValue( "__guid", out var jsonNode ) && jsonNode is not null )
				{
					var objectGuid = jsonNode.ToString();
					if ( !string.IsNullOrWhiteSpace( objectGuid ) && objectGuid == guid )
					{
						yield return obj;
					}
				}

				if ( obj.TryGetPropertyValue( "Children", out var childrenJsonNode ) && childrenJsonNode is not null )
				{
					var childrenString = childrenJsonNode.ToString();
					if ( !string.IsNullOrWhiteSpace( childrenString ) )
					{
						var children = JsonSerializer.Deserialize<List<JsonObject>>( childrenString );
						foreach ( var child in GetAllObjectsByGuidRecursive( children, guid ) )
						{
							yield return child;
						}
					}
				}
			}
		}
		public IEnumerable<JsonNode> GetAllObjectsByName( string name )
		{
			var objects = sceneFileDupe.GameObjects;
			foreach ( var obj in objects )
			{
				obj.TryGetPropertyValue( "Name", out var objName );
				if ( objName is not null && objName.ToString() == name )
				{
					yield return obj;
				}
			}
		}
	}

}
nolankicks.sceneloadingutility / Editor/SceneLoadingResourceCustomEditor.cs
Editor library
using Editor;
using Sandbox;
using SceneLoading;

//[CustomEditor(typeof(SceneLoadingClass))]
public sealed class SceneLoadingResourceCustomEditor : ControlWidget
{
	public SceneLoadingResourceCustomEditor( SerializedProperty property ) : base( property )
	{
		Layout = Layout.Column();

		if ( property.IsNull )
		{
			property.SetValue( new SceneLoadingClass() );
		}

		var so = property.GetValue<SceneLoadingClass>()?.GetSerialized();
		if ( so is null ) return;
		var controlSheet = new ControlSheet();
		controlSheet.AddObject( so );
		Layout.Add( controlSheet );
	}
}
nolankicks.sceneloadingutility / PlayerController.cs
Game library
using Sandbox.Citizen;

[Group( "Walker" )]
[Title( "Walker - Player Controller" )]
public sealed class PlayerController : Component
{
	[Property] public CharacterController CharacterController { get; set; }
	[Property] public float CrouchMoveSpeed { get; set; } = 64.0f;
	[Property] public float WalkMoveSpeed { get; set; } = 190.0f;
	[Property] public float RunMoveSpeed { get; set; } = 190.0f;
	[Property] public float SprintMoveSpeed { get; set; } = 320.0f;

	[Property] public CitizenAnimationHelper AnimationHelper { get; set; }

	[Sync] public bool Crouching { get; set; }
	[Sync] public Angles EyeAngles { get; set; }
	[Sync] public Vector3 WishVelocity { get; set; }

	public bool WishCrouch;
	public float EyeHeight = 64;

	protected override void OnUpdate()
	{
		if ( !IsProxy )
		{
			MouseInput();
			Transform.Rotation = new Angles( 0, EyeAngles.yaw, 0 );
		}

		UpdateAnimation();
	}

	protected override void OnFixedUpdate()
	{
		if ( IsProxy )
			return;

		CrouchingInput();
		MovementInput();
	}

	private void MouseInput()
	{
		var e = EyeAngles;
		e += Input.AnalogLook;
		e.pitch = e.pitch.Clamp( -90, 90 );
		e.roll = 0.0f;
		EyeAngles = e;
	}

	float CurrentMoveSpeed
	{
		get
		{
			if ( Crouching ) return CrouchMoveSpeed;
			if ( Input.Down( "run" ) ) return SprintMoveSpeed;
			if ( Input.Down( "walk" ) ) return WalkMoveSpeed;

			return RunMoveSpeed;
		}
	}

	RealTimeSince lastGrounded;
	RealTimeSince lastUngrounded;
	RealTimeSince lastJump;

	float GetFriction()
	{
		if ( CharacterController.IsOnGround ) return 6.0f;

		// air friction
		return 0.2f;
	}

	private void MovementInput()
	{
		if ( CharacterController is null )
			return;

		var cc = CharacterController;

		Vector3 halfGravity = Scene.PhysicsWorld.Gravity * Time.Delta * 0.5f;

		WishVelocity = Input.AnalogMove;

		if ( lastGrounded < 0.2f && lastJump > 0.3f && Input.Pressed( "jump" ) )
		{
			lastJump = 0;
			cc.Punch( Vector3.Up * 300 );
		}

		if ( !WishVelocity.IsNearlyZero() )
		{
			WishVelocity = new Angles( 0, EyeAngles.yaw, 0 ).ToRotation() * WishVelocity;
			WishVelocity = WishVelocity.WithZ( 0 );
			WishVelocity = WishVelocity.ClampLength( 1 );
			WishVelocity *= CurrentMoveSpeed;

			if ( !cc.IsOnGround )
			{
				WishVelocity = WishVelocity.ClampLength( 50 );
			}
		}


		cc.ApplyFriction( GetFriction() );

		if ( cc.IsOnGround )
		{
			cc.Accelerate( WishVelocity );
			cc.Velocity = CharacterController.Velocity.WithZ( 0 );
		}
		else
		{
			cc.Velocity += halfGravity;
			cc.Accelerate( WishVelocity );

		}

		//
		// Don't walk through other players, let them push you out of the way
		//
		var pushVelocity = PlayerPusher.GetPushVector( Transform.Position + Vector3.Up * 40.0f, Scene, GameObject );
		if ( !pushVelocity.IsNearlyZero() )
		{
			var travelDot = cc.Velocity.Dot( pushVelocity.Normal );
			if ( travelDot < 0 )
			{
				cc.Velocity -= pushVelocity.Normal * travelDot * 0.6f;
			}

			cc.Velocity += pushVelocity * 128.0f;
		}

		cc.Move();

		if ( !cc.IsOnGround )
		{
			cc.Velocity += halfGravity;
		}
		else
		{
			cc.Velocity = cc.Velocity.WithZ( 0 );
		}

		if ( cc.IsOnGround )
		{
			lastGrounded = 0;
		}
		else
		{
			lastUngrounded = 0;
		}
	}
	float DuckHeight = (64 - 36);

	bool CanUncrouch()
	{
		if ( !Crouching ) return true;
		if ( lastUngrounded < 0.2f ) return false;

		var tr = CharacterController.TraceDirection( Vector3.Up * DuckHeight );
		return !tr.Hit; // hit nothing - we can!
	}

	public void CrouchingInput()
	{
		WishCrouch = Input.Down( "duck" );

		if ( WishCrouch == Crouching )
			return;

		// crouch
		if ( WishCrouch )
		{
			CharacterController.Height = 36;
			Crouching = WishCrouch;

			// if we're not on the ground, slide up our bbox so when we crouch
			// the bottom shrinks, instead of the top, which will mean we can reach
			// places by crouch jumping that we couldn't.
			if ( !CharacterController.IsOnGround )
			{
				CharacterController.MoveTo( Transform.Position += Vector3.Up * DuckHeight, false );
				Transform.ClearLerp();
				EyeHeight -= DuckHeight;
			}

			return;
		}

		// uncrouch
		if ( !WishCrouch )
		{
			if ( !CanUncrouch() ) return;

			CharacterController.Height = 64;
			Crouching = WishCrouch;
			return;
		}


	}

	private void UpdateCamera()
	{
		var camera = Scene.GetAllComponents<CameraComponent>().Where( x => x.IsMainCamera ).FirstOrDefault();
		if ( camera is null ) return;

		var targetEyeHeight = Crouching ? 28 : 64;
		EyeHeight = EyeHeight.LerpTo( targetEyeHeight, RealTime.Delta * 10.0f );

		var targetCameraPos = Transform.Position + new Vector3( 0, 0, EyeHeight );

		// smooth view z, so when going up and down stairs or ducking, it's smooth af
		if ( lastUngrounded > 0.2f )
		{
			targetCameraPos.z = camera.Transform.Position.z.LerpTo( targetCameraPos.z, RealTime.Delta * 25.0f );
		}

		camera.Transform.Position = targetCameraPos;
		camera.Transform.Rotation = EyeAngles;
		camera.FieldOfView = Preferences.FieldOfView;
	}

	protected override void OnPreRender()
	{
		UpdateBodyVisibility();

		if ( IsProxy )
			return;

		UpdateCamera();
	}

	private void UpdateAnimation()
	{
		if ( AnimationHelper is null || CharacterController is null ) return;


		var wv = WishVelocity.Length;

		AnimationHelper.WithWishVelocity( WishVelocity );
		AnimationHelper.WithVelocity( CharacterController.Velocity );
		AnimationHelper.IsGrounded = CharacterController.IsOnGround;
		AnimationHelper.DuckLevel = Crouching ? 1.0f : 0.0f;

		AnimationHelper.MoveStyle = wv < 160f ? CitizenAnimationHelper.MoveStyles.Walk : CitizenAnimationHelper.MoveStyles.Run;

		var lookDir = EyeAngles.ToRotation().Forward * 1024;
		AnimationHelper.WithLook( lookDir, 1, 0.5f, 0.25f );
	}

	private void UpdateBodyVisibility()
	{
		if ( AnimationHelper is null )
			return;

		var renderMode = ModelRenderer.ShadowRenderType.On;
		if ( !IsProxy ) renderMode = ModelRenderer.ShadowRenderType.ShadowsOnly;

		AnimationHelper.Target.RenderType = renderMode;

		foreach ( var clothing in AnimationHelper.Target.Components.GetAll<ModelRenderer>( FindMode.InChildren ) )
		{
			if ( !clothing.Tags.Has( "clothing" ) )
				continue;

			clothing.RenderType = renderMode;
		}
	}

}
nolankicks.sceneloadingutility / JiggleBone.cs
Game library
public sealed class JiggleBone : TransformProxyComponent
{
	JiggleBoneState state = new JiggleBoneState();

	[Property]
	public Vector3 StartPoint = new Vector3( 0, 0, 0 );

	[Property]
	public Vector3 EndPoint = new Vector3( 32, 0, 0 );

	[Property, Range( 0, 2 )]
	public float Speed { get; set; } = 1.0f;

	[Property, Range( 0, 2 )]
	public float Stiffness { get; set; } = 1.0f;

	[Property, Range( 0, 2 )]
	public float Damping { get; set; } = 1.0f;

	[Property, Range( 0, 100 )]
	public float Radius { get; set; } = 40.0f;

	[Property, Range( 0, 100 )]
	public float Mass { get; set; } = 1.0f;

	Transform LocalJigglePosition;

	protected override void OnEnabled()
	{
		LocalJigglePosition = Transform.Local;

		base.OnEnabled();

		state = new JiggleBoneState();
	}

	protected override void OnUpdate()
	{
		var oldPos = LocalJigglePosition;



		using ( Transform.DisableProxy() )
		{
			var worldTx = Transform.World;

			var startPoint = worldTx.PointToWorld( StartPoint );
			var endPoint = worldTx.PointToWorld( EndPoint );

			//Gizmo.Draw.LineSphere( startPoint, 1 );
			//Gizmo.Draw.LineSphere( endPoint, 1 );

			state.Extent = (endPoint - startPoint);
			state.Stiffness = Stiffness;
			state.Damping = Damping;
			state.Radius = Radius;
			state.Mass = Mass;

			state.Update( startPoint, Time.Delta * Speed * 16.0f );

			var tx = worldTx.RotateAround( startPoint, state.Rotation );
			LocalJigglePosition = GameObject.Parent.Transform.World.ToLocal( tx );
		}

		if ( oldPos != LocalJigglePosition )
		{
			MarkTransformChanged();
		}
	}

	protected override void DrawGizmos()
	{
		base.DrawGizmos();

		if ( !Gizmo.IsSelected )
			return;

		using ( Transform.DisableProxy() )
		{
			Gizmo.Transform = Transform.World;
			Gizmo.Draw.IgnoreDepth = false;
			Gizmo.Draw.Color = Gizmo.Colors.Yaw.WithAlpha( 0.5f );
			Gizmo.Draw.Line( StartPoint, EndPoint );
			Gizmo.Draw.LineBBox( BBox.FromPositionAndSize( StartPoint, 5 ) );
			Gizmo.Draw.LineBBox( BBox.FromPositionAndSize( EndPoint, 5 ) );
			Gizmo.Draw.LineSphere( EndPoint, Radius * 2.0f, 4 );
		}
	}

	public override Transform GetLocalTransform()
	{
		return LocalJigglePosition;
	}
}

class JiggleBoneState
{
	public Vector3 Extent = new Vector3( 32, 0, 0 );

	public Vector3 Position { get; set; }
	public Rotation Rotation { get; set; }
	public float Stiffness { get; set; } = 1.0f;
	public float Damping { get; set; } = 1.0f;
	public float Radius { get; set; } = 10.0f;
	public float Gravity { get; set; } = 1.0f;
	public float Mass { get; set; } = 1.0f;


	Vector3 basePosition;
	Vector3 velocity;

	public JiggleBoneState()
	{

	}

	internal void Update( Vector3 position, float timeDelta )
	{
		basePosition = position + Extent;

		// initialization
		if ( Position == default )
		{
			Position = basePosition;
		}

		// Calculate spring force based on displacement from the cube
		Vector3 displacement = Position - basePosition;
		Vector3 springForce = -Stiffness * displacement;

		// Calculate acceleration (Newton's second law)
		Vector3 acceleration = springForce / Mass;

		// Update velocity (integrate acceleration)
		velocity += acceleration * timeDelta;

		// Apply exponential damping
		velocity *= (float)Math.Exp( -Damping * timeDelta );

		// Update position (integrate velocity)
		Position += velocity * timeDelta;

		{
			var diff = Position - basePosition;
			var diffLen = diff.Length;
			if ( diffLen > Radius )
			{
				Position = basePosition + diff.Normal * Radius;
				//velocity = velocity.AddClamped( -diff * 2.0f, diff.Length );
			}
		}

		// Store the rotation offset result
		Rotation = Rotation.FromToRotation( basePosition - position, Position - position );

		//Gizmo.Draw.IgnoreDepth = true;
		//Gizmo.Draw.Line( position, Position );
		//Gizmo.Draw.Line( basePosition, Position );
	}
}
nolankicks.sceneloadingutility / BlankPostProcess.cs
Game library
using System;
using Sandbox;


//Only uses to get the scene camera
public sealed class BlankPostProcess : PostProcess
{
	IDisposable renderHook;
	public SceneCamera sceneCam { get; set; }

	protected override void OnEnabled()
	{
		renderHook = Camera.AddHookBeforeOverlay( "My Post Processing", 1000, RenderEffect );
	}

	protected override void OnDisabled()
	{
		renderHook?.Dispose();
		renderHook = null;
	}

	RenderAttributes attributes = new RenderAttributes();

	public void RenderEffect( SceneCamera camera )
	{
		if ( !camera.EnablePostProcessing )
			return;
		sceneCam = camera;
	}
}
nolankicks.sceneloadingutility / .obj/__compiler_extra.cs
Game library
global using static Sandbox.Internal.GlobalGameNamespace;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "Scene Loading Utility" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "sceneloadingutility" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "nolankicks" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "nolankicks.sceneloadingutility" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "8/10/2024 7:55:20 PM" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "17" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]

[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v7.0", FrameworkDisplayName = ".NET 7.0" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.120.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.120.0")]
Debug: View Raw JSON Response
{
    "TotalCount": 23,
    "Files": [
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "Attributes.cs",
            "FileName": "Attributes.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "\r\nusing System;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// <summary>\r\n/// Only valid on <see cref=\"IGameEventHandler{T}.OnGameEvent\"/> implementations. Forces this\r\n/// event handler to be invoked before any handlers not marked as early, except if more specific\r\n/// constraints are given (i.e., <see cref=\"BeforeAttribute{T}\"/>, <see cref=\"AfterAttribute{T}\"/>).\r\n/// </summary>\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class EarlyAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\n/// <summary>\r\n/// Only valid on <see cref=\"IGameEventHandler{T}.OnGameEvent\"/> implementations. Forces this\r\n/// event handler to be invoked after any handlers not marked as late, except if more specific\r\n/// constraints are given (i.e., <see cref=\"BeforeAttribute{T}\"/>, <see cref=\"AfterAttribute{T}\"/>).\r\n/// </summary>\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class LateAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\ninternal interface IBeforeAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\ninternal interface IAfterAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\n/// <summary>\r\n/// Only valid on <see cref=\"IGameEventHandler{T}.OnGameEvent\"/> implementations. Forces this\r\n/// event handler to be invoked before any handlers in the specified type.\r\n/// </summary>\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class BeforeAttribute<T> : Attribute, IBeforeAttribute\r\n{\r\n\tType IBeforeAttribute.Type => typeof(T);\r\n}\r\n\r\n/// <summary>\r\n/// Only valid on <see cref=\"IGameEventHandler{T}.OnGameEvent\"/> implementations. Forces this\r\n/// event handler to be invoked after any handlers in the specified type.\r\n/// </summary>\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class AfterAttribute<T> : Attribute, IAfterAttribute\r\n{\r\n\tType IAfterAttribute.Type => typeof( T );\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "SortingHelper.cs",
            "FileName": "SortingHelper.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System.Collections.Generic;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// <summary>\r\n/// Generate an ordering based on a set of first-most and last-most items, and\r\n/// individual constraints between pairs of items. All first-most items will be\r\n/// ordered before all last-most items, and any other items will be put in the\r\n/// middle unless forced to be elsewhere by a constraint.\r\n/// </summary>\r\ninternal class SortingHelper\r\n{\r\n\tpublic record struct SortConstraint( int EarlierIndex, int LaterIndex )\r\n\t{\r\n\t\tpublic SortConstraint Complement => new ( LaterIndex, EarlierIndex );\r\n\t}\r\n\r\n\tprivate readonly int _itemCount;\r\n\r\n\tprivate readonly HashSet<SortConstraint> _initialConstraints = new HashSet<SortConstraint>();\r\n\r\n\tprivate readonly HashSet<int> _first = new HashSet<int>();\r\n\tprivate readonly HashSet<int> _last = new HashSet<int>();\r\n\r\n\tpublic SortingHelper( int itemCount )\r\n\t{\r\n\t\t_itemCount = itemCount;\r\n\t}\r\n\r\n\tpublic void AddConstraint( int earlierIndex, int laterIndex )\r\n\t{\r\n\t\t_initialConstraints.Add( new SortConstraint( earlierIndex, laterIndex ) );\r\n\t}\r\n\r\n\tpublic void AddFirst( int earlierIndex )\r\n\t{\r\n\t\t_first.Add( earlierIndex );\r\n\t}\r\n\r\n\tpublic void AddLast( int laterIndex )\r\n\t{\r\n\t\t_last.Add( laterIndex );\r\n\t}\r\n\r\n\tpublic bool Sort( List<int> result, out SortConstraint invalidConstraint )\r\n\t{\r\n\t\tvar middle = new HashSet<int>();\r\n\r\n\t\tfor ( var index = 0; index < _itemCount; ++index )\r\n\t\t{\r\n\t\t\tif ( !_first.Contains( index ) && !_last.Contains( index ) )\r\n\t\t\t\tmiddle.Add( index );\r\n\t\t}\r\n\r\n\t\tvar allConstraints = new HashSet<SortConstraint>();\r\n\t\tvar newConstraints = new Queue<SortConstraint>();\r\n\t\tvar beforeDict = new Dictionary<int, HashSet<int>>();\r\n\t\tvar afterDict = new Dictionary<int, HashSet<int>>();\r\n\r\n\t\tbool AddWorkingConstraint( int earlierIndex, int laterIndex, out SortConstraint constraint )\r\n\t\t{\r\n\t\t\tconstraint = new SortConstraint( earlierIndex, laterIndex );\r\n\r\n\t\t\tif ( allConstraints.Contains( constraint.Complement ) )\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tif ( !allConstraints.Add( constraint ) )\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tnewConstraints.Enqueue( constraint );\r\n\r\n\t\t\tif ( !beforeDict.TryGetValue( earlierIndex, out var before ) )\r\n\t\t\t\tbeforeDict.Add( earlierIndex, before = new HashSet<int>() );\r\n\r\n\t\t\tif ( !afterDict.TryGetValue( laterIndex, out var after ) )\r\n\t\t\t\tafterDict.Add( laterIndex, after = new HashSet<int>() );\r\n\r\n\t\t\tbefore.Add( laterIndex );\r\n\t\t\tafter.Add( earlierIndex );\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// Add initial constraints\r\n\r\n\t\tforeach ( var initialConstraint in _initialConstraints )\r\n\t\t{\r\n\t\t\tif ( !AddWorkingConstraint( initialConstraint.EarlierIndex, initialConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\t// Everything in _first should be before everything in _last\r\n\r\n\t\tforeach ( var earlierIndex in _first )\r\n\t\t{\r\n\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t{\r\n\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Keep propagating constraints until nothing changes\r\n\r\n\t\twhile ( newConstraints.TryDequeue( out var nextConstraint ) )\r\n\t\t{\r\n\t\t\t// if a < b, and b < c, then a < c etc\r\n\r\n\t\t\tif ( beforeDict.TryGetValue( nextConstraint.LaterIndex, out var before ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in before )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( nextConstraint.EarlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif ( afterDict.TryGetValue( nextConstraint.EarlierIndex, out var after ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in after )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, nextConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now if we have any items that aren't using GroupOrder.First, and haven't\r\n\t\t// determined that they are ordered before another item with GroupOrder.First,\r\n\t\t// we can safely order them after all GroupOrder.First items. And vice versa.\r\n\r\n\t\tforeach ( var middleIndex in middle )\r\n\t\t{\r\n\t\t\tvar isBeforeAnyFirst = beforeDict.TryGetValue( middleIndex, out var before )\r\n\t\t\t\t&& before.Any( x => _first.Contains( x ) );\r\n\r\n\t\t\tvar isAfterAnyLast = afterDict.TryGetValue( middleIndex, out var after )\r\n\t\t\t\t&& after.Any( x => _last.Contains( x ) );\r\n\r\n\t\t\tif ( !isBeforeAnyFirst )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in _first )\r\n\t\t\t\t\tAddWorkingConstraint( earlierIndex, middleIndex, out invalidConstraint );\r\n\t\t\t}\r\n\r\n\t\t\tif ( !isAfterAnyLast )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t\t\tAddWorkingConstraint( middleIndex, laterIndex, out invalidConstraint );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now lets add items to the final ordering if all items that should be sorted\r\n\t\t// before them are already added to that ordering. We'll implement this by choosing\r\n\t\t// items that have an empty list / don't appear in afterDict, and update that\r\n\t\t// dictionary as we go.\r\n\r\n\t\tvar earliestRemaining = new Queue<int>();\r\n\r\n\t\t// First, seed the queue with everything that's already not ordered after anything\r\n\r\n\t\tfor ( var index = 0; index < _itemCount; ++index )\r\n\t\t{\r\n\t\t\tif ( !afterDict.ContainsKey( index ) )\r\n\t\t\t{\r\n\t\t\t\tearliestRemaining.Enqueue( index );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tresult.Clear();\r\n\r\n\t\twhile ( earliestRemaining.TryDequeue( out var nextIndex ) )\r\n\t\t{\r\n\t\t\tresult.Add( nextIndex );\r\n\r\n\t\t\tforeach ( var laterIndex in beforeDict.TryGetValue( nextIndex, out var laterIndices )\r\n\t\t\t\t? laterIndices : Enumerable.Empty<int>() )\r\n\t\t\t{\r\n\t\t\t\tvar beforeLater = afterDict[laterIndex];\r\n\t\t\t\tbeforeLater.Remove( nextIndex );\r\n\r\n\t\t\t\tif ( beforeLater.Count == 0 )\r\n\t\t\t\t\tearliestRemaining.Enqueue( laterIndex );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tinvalidConstraint = default;\r\n\t\treturn result.Count == _itemCount;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "UnitTests/LibraryTest.cs",
            "FileName": "LibraryTest.cs",
            "PackageType": "library",
            "CodeKind": "UnitTest",
            "AssetVersionId": 65380,
            "Code": "using Sandbox;\r\n\r\n[TestClass]\r\npublic partial class LibraryTests\r\n{\r\n\t[TestMethod]\r\n\tpublic void SceneTest()\r\n\t{\r\n\t\tvar scene = new Scene();\r\n\t\tusing ( scene.Push() )\r\n\t\t{\r\n\t\t\tvar go = new GameObject();\r\n\r\n\t\t\tAssert.AreEqual( 1, scene.Directory.GameObjectCount );\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "PlayerPusher.cs",
            "FileName": "PlayerPusher.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "\r\npublic sealed class PlayerPusher : Component\r\n{\r\n\t[Property] public float Radius { get; set; } = 100;\r\n\r\n\tprotected override void DrawGizmos()\r\n\t{\r\n\t\tbase.DrawGizmos();\r\n\r\n\t\tGizmo.Draw.LineSphere( Vector3.Zero, Radius );\r\n\t}\r\n\r\n\tpublic static Vector3 GetPushVector( in Vector3 position, Scene scene, GameObject ignore )\r\n\t{\r\n\t\tVector3 vec = default;\r\n\r\n\t\tforeach ( var pusher in scene.GetAllComponents<PlayerPusher>() )\r\n\t\t{\r\n\t\t\tif ( pusher.GameObject.IsAncestor( ignore ) )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tpusher.Collect( position, ref vec );\r\n\t\t}\r\n\r\n\t\treturn vec;\r\n\t}\r\n\r\n\tprivate void Collect( Vector3 position, ref Vector3 output )\r\n\t{\r\n\t\tvar delta = (position - Transform.Position);\r\n\t\tif ( delta.Length > Radius ) return;\r\n\r\n\t\tdelta.z = 0; // ignore z\r\n\r\n\t\tvar distanceDelta = (delta.Length / Radius);\r\n\r\n\t\toutput += delta.Normal * (1.0f - distanceDelta);\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "PlayerFootsteps.cs",
            "FileName": "PlayerFootsteps.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "\r\npublic sealed class PlayerFootsteps : Component\r\n{\r\n\t[Property] SkinnedModelRenderer Source { get; set; }\r\n\r\n\tprotected override void OnEnabled()\r\n\t{\r\n\t\tif ( Source is null )\r\n\t\t\treturn;\r\n\r\n\t\tSource.OnFootstepEvent += OnEvent;\r\n\t}\r\n\r\n\tprotected override void OnDisabled()\r\n\t{\r\n\t\tif ( Source is null )\r\n\t\t\treturn;\r\n\r\n\t\tSource.OnFootstepEvent -= OnEvent;\r\n\t}\r\n\r\n\tTimeSince timeSinceStep;\r\n\r\n\tprivate void OnEvent( SceneModel.FootstepEvent e )\r\n\t{\r\n\t\tif ( timeSinceStep < 0.2f )\r\n\t\t\treturn;\r\n\r\n\t\tvar tr = Scene.Trace\r\n\t\t\t.Ray( e.Transform.Position + Vector3.Up * 20, e.Transform.Position + Vector3.Up * -20 )\r\n\t\t\t.Run();\r\n\r\n\t\tif ( !tr.Hit )\r\n\t\t\treturn;\r\n\r\n\t\tif ( tr.Surface is null )\r\n\t\t\treturn;\r\n\r\n\t\ttimeSinceStep = 0;\r\n\r\n\t\tvar sound = e.FootId == 0 ? tr.Surface.Sounds.FootLeft : tr.Surface.Sounds.FootRight;\r\n\t\tif ( sound is null ) return;\r\n\r\n\t\tvar handle = Sound.Play( sound, tr.HitPosition + tr.Normal * 5 );\r\n\t\thandle.Volume *= e.Volume;\r\n\t\thandle.Update();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "BouncyBone.cs",
            "FileName": "BouncyBone.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "public sealed class BouncyBone : TransformProxyComponent\r\n{\r\n\tJiggleBoneState state = new JiggleBoneState();\r\n\r\n\t[Property]\r\n\tpublic Vector3 Influence { get; set; } = new Vector3( 1, 1, 1 );\r\n\r\n\t[Property, Range( 0, 50.0f )]\r\n\tpublic float Stiffness { get; set; } = 1;\r\n\r\n\t[Property, Range( 0, 50.0f )]\r\n\tpublic float Damping { get; set; } = 1;\r\n\r\n\tTransform LocalJigglePosition;\r\n\tTransformSpring springer;\r\n\r\n\tprotected override void OnEnabled()\r\n\t{\r\n\t\tspringer = new TransformSpring();\r\n\t\tspringer.Transform = Transform.World;\r\n\t\tLocalJigglePosition = springer.Transform;\r\n\r\n\t\tbase.OnEnabled();\r\n\r\n\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tvar oldPos = LocalJigglePosition;\r\n\r\n\t\tusing ( Transform.DisableProxy() )\r\n\t\t{\r\n\t\t\tvar worldTx = Transform.World;\r\n\r\n\t\t\tspringer.Stiffness = Stiffness;\r\n\t\t\tspringer.Damping = Damping;\r\n\t\t\tspringer.UpdateSpring( Transform.World, Time.Delta );\r\n\r\n\t\t\tvar tx = GameObject.Parent.Transform.World.ToLocal( springer.Transform );\r\n\t\t\tLocalJigglePosition = tx;\r\n\t\t}\r\n\r\n\t\tif ( oldPos != LocalJigglePosition )\r\n\t\t{\r\n\t\t\tMarkTransformChanged();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic override Transform GetLocalTransform()\r\n\t{\r\n\t\treturn LocalJigglePosition;\r\n\t}\r\n}\r\n\r\n\r\npublic struct TransformSpring\r\n{\r\n\tpublic Transform Transform;\r\n\r\n\tprivate Vector3 velocityPosition;\r\n\tprivate Vector3 velocityScale;\r\n\tprivate Rotation velocityRotation = Rotation.Identity;\r\n\r\n\tpublic float Stiffness = 1.5f;  // Spring stiffness, higher is stiffer\r\n\tpublic float Damping = 1.0f;      // Damping, higher is less oscillation\r\n\r\n\tpublic TransformSpring()\r\n\t{\r\n\t\tTransform = global::Transform.Zero;\r\n\t}\r\n\r\n\tpublic void UpdateSpring( Transform target, float deltaTime )\r\n\t{\r\n\t\tTransform.Position = SpringLerp( Transform.Position, target.Position, ref velocityPosition, deltaTime );\r\n\t\tTransform.Scale = SpringLerp( Transform.Scale, target.Scale, ref velocityScale, deltaTime );\r\n\t\tTransform.Rotation = target.Rotation;\r\n\t}\r\n\r\n\tprivate Vector3 SpringLerp( Vector3 current, Vector3 target, ref Vector3 velocity, float deltaTime )\r\n\t{\r\n\t\tfloat omega = 2f * MathF.PI * Stiffness;\r\n\t\tfloat damper = MathF.Exp( -Damping * deltaTime * omega );\r\n\r\n\t\tVector3 displacement = current - target;\r\n\t\tVector3 springForce = -omega * omega * displacement;\r\n\t\tVector3 dampingForce = -2f * omega * Damping * velocity;\r\n\r\n\t\tVector3 acceleration = springForce + dampingForce;\r\n\t\tvelocity = (velocity + acceleration * deltaTime) * damper;\r\n\t\treturn target + displacement + velocity * deltaTime;\r\n\t}\r\n\r\n\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "GameEvent.cs",
            "FileName": "GameEvent.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Collections.Immutable;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// <summary>\r\n/// Interface for event payloads that can be listened for by <see cref=\"IGameEventHandler{T}\"/>s.\r\n/// </summary>\r\npublic interface IGameEvent { }\r\n\r\n/// <summary>\r\n/// Interface for components that handle game events with a payload of type <see cref=\"T\"/>.\r\n/// </summary>\r\n/// <typeparam name=\"T\">Event payload type.</typeparam>\r\npublic interface IGameEventHandler<in T>\r\n\twhere T : IGameEvent\r\n{\r\n\t/// <summary>\r\n\t/// Called when an event with payload of type <see cref=\"T\"/> is dispatched on a <see cref=\"GameObject\"/>\r\n\t/// that contains this component, including on a descendant.\r\n\t/// </summary>\r\n\t/// <param name=\"eventArgs\">Event payload.</param>\r\n\tvoid OnGameEvent( T eventArgs );\r\n}\r\n\r\n/// <summary>\r\n/// Helper for dispatching game events in a scene.\r\n/// </summary>\r\npublic static class GameEvent\r\n{\r\n\tprivate static Dictionary<Type, IReadOnlyDictionary<Type, int>> HandlerOrderingCache { get; } = new();\r\n\r\n\t/// <summary>\r\n\t/// Notifies all <see cref=\"IGameEventHandler{T}\"/> components that are within <paramref name=\"root\"/>,\r\n\t/// with a payload of type <typeparamref name=\"T\"/>.\r\n\t/// </summary>\r\n\tpublic static void Dispatch<T>( this GameObject root, T eventArgs )\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar handlers = (root is Scene scene\r\n\t\t\t? scene.GetAllComponents<IGameEventHandler<T>>() // I think this is more efficient?\r\n\t\t\t: root.Components.GetAll<IGameEventHandler<T>>())\r\n\t\t\t.ToArray();\r\n\r\n\t\tif ( !HandlerOrderingCache.TryGetValue( typeof(T), out var ordering ) || handlers.Any( x => !ordering.ContainsKey( x.GetType() ) ) )\r\n\t\t{\r\n\t\t\tordering = HandlerOrderingCache[typeof(T)] = GetHandlerOrdering<T>();\r\n\t\t}\r\n\r\n\t\tList<Exception>? exceptions = null;\r\n\r\n\t\tforeach ( var handler in handlers.OrderBy( x => ordering[x.GetType()] ) )\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\thandler.OnGameEvent( eventArgs );\r\n\t\t\t}\r\n\t\t\tcatch ( Exception e )\r\n\t\t\t{\r\n\t\t\t\texceptions ??= new();\r\n\t\t\t\texceptions.Add( e );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tswitch ( exceptions?.Count )\r\n\t\t{\r\n\t\t\tcase 1:\r\n\t\t\t\tLog.Error( exceptions[0] );\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase > 1:\r\n\t\t\t\tLog.Error( new AggregateException( exceptions ) );\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static bool IsImplementingMethodName( string methodName )\r\n\t{\r\n\t\tif ( methodName == nameof(IGameEventHandler<IGameEvent>.OnGameEvent) )\r\n\t\t{\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn methodName.StartsWith( \"Sandbox.Events.IGameEventHandler<\" ) && methodName.EndsWith( \">.OnGameEvent\" );\r\n\t}\r\n\r\n\tprivate static MethodDescription? GetImplementation<T>( TypeDescription type )\r\n\t{\r\n\t\tforeach ( var method in type.Methods )\r\n\t\t{\r\n\t\t\tif ( method.IsStatic ) continue;\r\n\t\t\tif ( method.Parameters.Length != 1 ) continue;\r\n\t\t\tif ( method.Parameters[0].ParameterType != typeof( T ) ) continue;\r\n\r\n\t\t\tif ( !IsImplementingMethodName( method.Name ) ) continue;\r\n\r\n\t\t\treturn method;\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate static IReadOnlyDictionary<Type, int> GetHandlerOrdering<T>()\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar types = TypeLibrary.GetTypes<IGameEventHandler<T>>().ToArray();\r\n\t\tvar helper = new SortingHelper( types.Length );\r\n\r\n\t\tfor ( var i = 0; i < types.Length; ++i )\r\n\t\t{\r\n\t\t\tvar type = types[i];\r\n\t\t\tvar method = GetImplementation<T>( type );\r\n\r\n\t\t\tif ( method is null )\r\n\t\t\t{\r\n\t\t\t\tLog.Warning( $\"Can't find {nameof( IGameEventHandler<T> )}<{typeof( T ).Name}> implementation in {type.Name}!\" );\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tforeach ( var attrib in method.Attributes )\r\n\t\t\t{\r\n\t\t\t\tswitch ( attrib )\r\n\t\t\t\t{\r\n\t\t\t\t\tcase EarlyAttribute:\r\n\t\t\t\t\t\thelper.AddFirst( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase LateAttribute:\r\n\t\t\t\t\t\thelper.AddLast( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IBeforeAttribute before:\r\n\t\t\t\t\t\tfor ( var j = 0; j < types.Length; ++j )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( before.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( i, j );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IAfterAttribute after:\r\n\t\t\t\t\t\tfor ( var j = 0; j < types.Length; ++j )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( after.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( j, i );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvar ordering = new List<int>();\r\n\r\n\t\tif ( !helper.Sort( ordering, out var invalid ) )\r\n\t\t{\r\n\t\t\tLog.Error( $\"Invalid event ordering constraint between {types[invalid.EarlierIndex].Name} and {types[invalid.LaterIndex].Name}!\" );\r\n\t\t\treturn ImmutableDictionary<Type, int>.Empty;\r\n\t\t}\r\n\r\n\t\treturn Enumerable.Range( 0, ordering.Count )\r\n\t\t\t.ToImmutableDictionary( i => types[ordering[i]].TargetType, i => i );\r\n\t}\r\n}\r\n\r\npublic delegate void GameEventAction<in T>( T eventArgs )\r\n\twhere T : IGameEvent;\r\n\r\n/// <summary>\r\n/// Base class for components that expose game events to Action Graph.\r\n/// </summary>\r\npublic abstract class GameEventComponent<T> : Component, IGameEventHandler<T>\r\n\twhere T : IGameEvent\r\n{\r\n\t/// <summary>\r\n\t/// Action invoked when the <typeparamref name=\"T\"/> event is dispatched.\r\n\t/// </summary>\r\n\t[Property]\r\n\tpublic GameEventAction<T>? OnEvent { get; set; }\r\n\r\n\t/// <summary>\r\n\t/// If this component is within a state machine, optional state to transition\r\n\t/// to when this event is dispatched.\r\n\t/// </summary>\r\n\t[Property]\r\n\tpublic StateComponent? NextState { get; set; }\r\n\r\n\tvoid IGameEventHandler<T>.OnGameEvent( T eventArgs )\r\n\t{\r\n\t\tOnEvent?.Invoke( eventArgs );\r\n\r\n\t\tif ( NextState is not null )\r\n\t\t{\r\n\t\t\tComponents.GetInAncestorsOrSelf<StateMachineComponent>()?.Transition( NextState );\r\n\t\t}\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "StateMachine.cs",
            "FileName": "StateMachine.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing Sandbox.Diagnostics;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// <summary>\r\n/// <para>\r\n/// A state machine containing a set of <see cref=\"StateComponent\"/>s. The <see cref=\"GameObject\"/> containing\r\n/// the currently active state will be enabled (including its ancestors), and all other objects containing states\r\n/// are disabled.\r\n/// </para>\r\n/// <para>\r\n/// The currently active state is controlled by the owner, and synchronised over the network. When a transition occurs,\r\n/// a <see cref=\"LeaveStateEvent\"/> is dispatched on the old state's containing object, followed by a\r\n/// <see cref=\"EnterStateEvent\"/> event on the object containing the new state. These events are only dispatched\r\n/// on the owner.\r\n/// </para>\r\n/// </summary>\r\n[Title( \"State Machine\" ), Category( \"State Machines\" )]\r\npublic sealed class StateMachineComponent : Component\r\n{\r\n\tprivate StateComponent? _currentState;\r\n\r\n\t/// <summary>\r\n\t/// How many instant state transitions in a row until we throw an error?\r\n\t/// </summary>\r\n\tpublic const int MaxInstantTransitions = 16;\r\n\r\n\t/// <summary>\r\n\t/// Which state is currently active?\r\n\t/// </summary>\r\n\t[Property, Sync]\r\n\tpublic StateComponent? CurrentState\r\n\t{\r\n\t\tget => _currentState;\r\n\t\tset\r\n\t\t{\r\n\t\t\tif ( _currentState == value ) return;\r\n\t\t\t_currentState = value;\r\n\r\n\t\t\tif ( Network.IsProxy )\r\n\t\t\t{\r\n\t\t\t\tEnableActiveStates( false );\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Which state will we transition to next, at <see cref=\"NextStateTime\"/>?\r\n\t/// </summary>\r\n\t[Sync]\r\n\tpublic StateComponent? NextState { get; set; }\r\n\r\n\t/// <summary>\r\n\t/// What time will we transition to <see cref=\"NextState\"/>?\r\n\t/// </summary>\r\n\t[Sync]\r\n\tpublic float NextStateTime { get; set; }\r\n\r\n\t/// <summary>\r\n\t/// All states found on descendant objects.\r\n\t/// </summary>\r\n\tpublic IEnumerable<StateComponent> States => Components.GetAll<StateComponent>( FindMode.EverythingInSelfAndDescendants );\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tforeach ( var state in States )\r\n\t\t{\r\n\t\t\tstate.Enabled = false;\r\n\t\t\tstate.GameObject.Enabled = state.GameObject == GameObject;\r\n\t\t}\r\n\r\n\t\tif ( !Network.IsProxy && CurrentState is { } current )\r\n\t\t{\r\n\t\t\tTransition( current );\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void EnableActiveStates( bool dispatch )\r\n\t{\r\n\t\tvar current = CurrentState;\r\n\t\tvar active = current?.GetAncestors() ?? Array.Empty<StateComponent>();\r\n\t\tvar activeSet = active.ToHashSet();\r\n\r\n\t\tvar toDeactivate = new Queue<StateComponent>( States.Where( x => x.Enabled && !activeSet.Contains( x ) ).Reverse() );\r\n\t\tvar toActivate = new Queue<StateComponent>( active.Where( x => !x.Enabled ) );\r\n\r\n\t\tif ( current != null )\r\n\t\t{\r\n\t\t\ttoActivate.Enqueue( current );\r\n\t\t}\r\n\r\n\t\twhile ( toDeactivate.TryDequeue( out var next ) )\r\n\t\t{\r\n\t\t\tnext.Leave( dispatch );\r\n\r\n\t\t\tif ( toDeactivate.All( x => x.GameObject != next.GameObject ) && toActivate.All( x => x.GameObject != next.GameObject ) )\r\n\t\t\t{\r\n\t\t\t\tnext.GameObject.Enabled = false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\twhile ( toActivate.TryDequeue( out var next ) )\r\n\t\t{\r\n\t\t\tnext.GameObject.Enabled = true;\r\n\r\n\t\t\tnext.Enter( dispatch );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnFixedUpdate()\r\n\t{\r\n\t\tif ( Network.IsProxy )\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif ( CurrentState is not { } current )\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tcurrent.Update();\r\n\r\n\t\tvar transitions = 0;\r\n\r\n\t\twhile ( transitions++ < MaxInstantTransitions )\r\n\t\t{\r\n\t\t\tif ( NextState is not { } next || !(Time.Now >= NextStateTime) )\r\n\t\t\t{\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif ( next.DefaultNextState is not null )\r\n\t\t\t{\r\n\t\t\t\tTransition( next.DefaultNextState, next.DefaultDuration );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tClearTransition();\r\n\t\t\t}\r\n\r\n\t\t\tCurrentState = next;\r\n\r\n\t\t\tEnableActiveStates( true );\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Queue up a transition to the given state. This will occur at the end of\r\n\t/// a fixed update on the state machine.\r\n\t/// </summary>\r\n\tpublic void Transition( StateComponent next, float delaySeconds = 0f )\r\n\t{\r\n\t\tAssert.NotNull( next );\r\n\t\tAssert.False( Network.IsProxy );\r\n\r\n\t\tNextState = next;\r\n\t\tNextStateTime = Time.Now + delaySeconds;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Removes any pending transitions, so this state machine will remain in the\r\n\t/// current state until another transition is queued with <see cref=\"Transition\"/>.\r\n\t/// </summary>\r\n\tpublic void ClearTransition()\r\n\t{\r\n\t\tAssert.False( Network.IsProxy );\r\n\r\n\t\tNextState = null;\r\n\t\tNextStateTime = float.PositiveInfinity;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "__gen_RazorNamespace.cs",
            "FileName": "__gen_RazorNamespace.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "global using Microsoft.AspNetCore.Components; \nglobal using Microsoft.AspNetCore.Components.Rendering;\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "UnitTests/UnitTest.cs",
            "FileName": "UnitTest.cs",
            "PackageType": "library",
            "CodeKind": "UnitTest",
            "AssetVersionId": 65380,
            "Code": "global using Microsoft.VisualStudio.TestTools.UnitTesting;\r\n\r\n[TestClass]\r\npublic class TestInit\r\n{\r\n\t[AssemblyInitialize]\r\n\tpublic static void ClassInitialize( TestContext context )\r\n\t{\r\n\t\tSandbox.Application.InitUnitTest();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "Code/SceneLoadingExample.cs",
            "FileName": "SceneLoadingExample.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System.Linq;\r\nusing Microsoft.VisualBasic;\r\nusing Sandbox;\r\nusing SceneLoading;\r\npublic sealed class ChangeSceneTrigger : Component, Component.ITriggerListener\r\n{\r\n\t[Property] public SceneFile sceneFile { get; set; }\r\n\t[Property] public GameObject PrefabTest { get; set; }\r\n\r\n\tvoid ITriggerListener.OnTriggerEnter( Sandbox.Collider other )\r\n\t{\r\n\t\tif ( other.GameObject.Tags.Has( \"player\" ) )\r\n\t\t{\r\n\t\t\tLoadScene();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid ITriggerListener.OnTriggerExit( Sandbox.Collider other )\r\n\t{\r\n\r\n\t}\r\n\r\n\tpublic void LoadScene()\r\n\t{\r\n\t\tvar customScene = new CustomScene( sceneFile );\r\n\t\tif ( customScene.GetAllObjectsByType( typeof( SkinnedModelRenderer ) ).Count() == 0 )\r\n\t\t{\r\n\t\t\tcustomScene.CreateObject( new GameObject() );\r\n\t\t}\r\n\r\n\t\tcustomScene.LoadScene();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "MyLibraryComponent.cs",
            "FileName": "MyLibraryComponent.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using Sandbox;\r\n\r\n/// <summary>\r\n/// This is a component - in your library!\r\n/// </summary>\r\n[Title( \"Screen Shot Library - My Component\" )]\r\npublic class MyLibraryComponent : Component\r\n{\r\n\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "SceneLoadingExample.cs",
            "FileName": "SceneLoadingExample.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System.Linq;\r\nusing Microsoft.VisualBasic;\r\nusing Sandbox;\r\nusing SceneLoading;\r\npublic sealed class ChangeSceneTrigger : Component, Component.ITriggerListener\r\n{\r\n\t[Property] public SceneFile sceneFile { get; set; }\r\n\t[Property] public GameObject PrefabTest { get; set; }\r\n\r\n\tvoid ITriggerListener.OnTriggerEnter( Sandbox.Collider other )\r\n\t{\r\n\t\tif ( other.GameObject.Tags.Has( \"player\" ) )\r\n\t\t{\r\n\t\t\tLoadScene();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid ITriggerListener.OnTriggerExit( Sandbox.Collider other )\r\n\t{\r\n\r\n\t}\r\n\r\n\tpublic void LoadScene()\r\n\t{\r\n\t\tvar customScene = new CustomScene( sceneFile );\r\n\t\tif ( customScene.GetAllObjectsByType( typeof( SkinnedModelRenderer ) ).Count() == 0 )\r\n\t\t{\r\n\t\t\tcustomScene.CreateObject( new GameObject() );\r\n\t\t}\r\n\r\n\t\tcustomScene.LoadScene();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "State.cs",
            "FileName": "State.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// <summary>\r\n/// Marks a <see cref=\"GameObject\"/> as a state in a state machine. There must be a\r\n/// <see cref=\"StateMachineComponent\"/> on an ancestor object for this to function.\r\n/// The object containing this state (and all ancestors) will be enabled when the state\r\n/// machine transitions to this state, and will disable again when this state is exited.\r\n/// States may be nested within each other.\r\n/// </summary>\r\n[Title( \"State\" ), Category( \"State Machines\" )]\r\npublic sealed class StateComponent : Component\r\n{\r\n\tprivate StateMachineComponent? _stateMachine;\r\n\r\n\t/// <summary>\r\n\t/// Which state machine does this state belong to?\r\n\t/// </summary>\r\n\tpublic StateMachineComponent StateMachine =>\r\n\t\t_stateMachine ??= Components.GetInAncestorsOrSelf<StateMachineComponent>();\r\n\r\n\t/// <summary>\r\n\t/// Which state is this nested in, if any?\r\n\t/// </summary>\r\n\tpublic StateComponent? Parent => Components.GetInAncestors<StateComponent>( true );\r\n\r\n\t/// <summary>\r\n\t/// Transition to this state by default.\r\n\t/// </summary>\r\n\t[Property]\r\n\tpublic StateComponent? DefaultNextState { get; set; }\r\n\r\n\t/// <summary>\r\n\t/// If <see cref=\"DefaultNextState\"/> is given, transition after this delay in seconds.\r\n\t/// </summary>\r\n\t[Property, HideIf( nameof( DefaultNextState ), null )]\r\n\tpublic float DefaultDuration { get; set; }\r\n\r\n\t/// <summary>\r\n\t/// Event dispatched on the owner when this state is entered.\r\n\t/// </summary>\r\n\t[Property]\r\n\tpublic event Action? OnEnterState;\r\n\r\n\t/// <summary>\r\n\t/// Event dispatched on the owner while this state is active.\r\n\t/// </summary>\r\n\t[Property]\r\n\tpublic event Action? OnUpdateState;\r\n\r\n\t/// <summary>\r\n\t/// Event dispatched on the owner when this state is exited.\r\n\t/// </summary>\r\n\t[Property]\r\n\tpublic event Action? OnLeaveState;\r\n\r\n\tinternal void Enter( bool dispatch )\r\n\t{\r\n\t\tEnabled = true;\r\n\r\n\t\tif ( dispatch )\r\n\t\t{\r\n\t\t\tOnEnterState?.Invoke();\r\n\t\t\tGameObject.Dispatch( new EnterStateEvent( this ) );\r\n\t\t}\r\n\t}\r\n\r\n\tinternal void Update()\r\n\t{\r\n\t\tOnUpdateState?.Invoke();\r\n\t\tScene.Dispatch( new UpdateStateEvent( this ) );\r\n\t}\r\n\r\n\tinternal void Leave( bool dispatch )\r\n\t{\r\n\t\tif ( dispatch )\r\n\t\t{\r\n\t\t\tOnLeaveState?.Invoke();\r\n\t\t\tGameObject.Dispatch( new LeaveStateEvent( this ) );\r\n\t\t}\r\n\r\n\t\tEnabled = false;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Queue up a transition to the given state. This will occur at the end of\r\n\t/// a fixed update on the state machine.\r\n\t/// </summary>\r\n\tpublic void Transition( StateComponent next, float delaySeconds = 0f )\r\n\t{\r\n\t\tStateMachine.Transition( next, delaySeconds );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Queue up a transition to the default next state.\r\n\t/// </summary>\r\n\tpublic void Transition()\r\n\t{\r\n\t\tStateMachine.Transition( DefaultNextState! );\r\n\t}\r\n\r\n\tinternal IReadOnlyList<StateComponent> GetAncestors()\r\n\t{\r\n\t\tvar list = new List<StateComponent>();\r\n\r\n\t\tvar parent = Parent;\r\n\r\n\t\twhile ( parent != null )\r\n\t\t{\r\n\t\t\tlist.Add( parent );\r\n\t\t\tparent = parent.Parent;\r\n\t\t}\r\n\r\n\t\tlist.Reverse();\r\n\r\n\t\treturn list;\r\n\t}\r\n}\r\n\r\n/// <summary>\r\n/// Event dispatched on the owner when a <see cref=\"StateMachineComponent\"/> changes state.\r\n/// Only invoked on components on the same object as the new state.\r\n/// </summary>\r\npublic record EnterStateEvent( StateComponent State ) : IGameEvent;\r\n\r\n/// <inheritdoc cref=\"EnterStateEvent\"/>\r\n[Title( \"Enter State Event\" ), Group( \"State Machines\" ), Icon( \"electric_bolt\" )]\r\npublic sealed class EnterStateEventComponent : GameEventComponent<EnterStateEvent> { }\r\n\r\n/// <summary>\r\n/// Event dispatched on the owner when a <see cref=\"StateMachineComponent\"/> changes state.\r\n/// Only invoked on components on the same object as the old state.\r\n/// </summary>\r\npublic record LeaveStateEvent( StateComponent State ) : IGameEvent;\r\n\r\n/// <inheritdoc cref=\"LeaveStateEvent\"/>\r\n[Title( \"Leave State Event\" ), Group( \"State Machines\" ), Icon( \"electric_bolt\" )]\r\npublic sealed class LeaveStateEventComponent : GameEventComponent<LeaveStateEvent> { }\r\n\r\n/// <summary>\r\n/// Event dispatched on the owner every fixed update while a <see cref=\"StateComponent\"/> is active.\r\n/// Only invoked on components on the same object as the state.\r\n/// </summary>\r\npublic record UpdateStateEvent( StateComponent State ) : IGameEvent;\r\n\r\n/// <inheritdoc cref=\"UpdateStateEvent\"/>\r\n[Title( \"Update State Event\" ), Group( \"State Machines\" ), Icon( \"electric_bolt\" )]\r\npublic sealed class UpdateStateEventComponent : GameEventComponent<UpdateStateEvent> { }\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "SceneLoadingUtility.cs",
            "FileName": "SceneLoadingUtility.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Data;\r\nusing System.Dynamic;\r\nusing System.Formats.Tar;\r\nusing System.Linq;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Text.Json;\r\nusing System.Text.Json.Nodes;\r\nusing System.Text.Json.Serialization;\r\nusing Microsoft.CSharp.RuntimeBinder;\r\nusing Sandbox;\r\nusing Sandbox.ActionGraphs;\r\nnamespace SceneLoading\r\n{\r\n\tpublic class SceneLoadingUtility\r\n\t{\r\n\t\tpublic static void LoadScene( SceneFile sceneFile, SceneLoadingResource sceneLoadingResource )\r\n\t\t{\r\n\t\t\tvar objects = sceneFile.GameObjects;\r\n\t\t\tGame.ActiveScene.Load( new SceneFile() );\r\n\r\n\t\t\tforeach ( var obj in objects )\r\n\t\t\t{\r\n\t\t\t\tvar gameObject = Game.ActiveScene.CreateObject();\r\n\t\t\t\tgameObject.Deserialize( obj );\r\n\t\t\t}\r\n\r\n\t\t\tforeach ( var clone in sceneLoadingResource.SceneLoadingClasses )\r\n\t\t\t{\r\n\t\t\t\tbool gameObjectSpawned = false;\r\n\r\n\t\t\t\tforeach ( var componentType in clone.ComponentTypes )\r\n\t\t\t\t{\r\n\r\n\t\t\t\t\tif ( clone.Flags == LoadingFlags.CheckForComponents && Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).Count() > 0 )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tLog.Info( \"Component found, skipping\" );\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if ( clone.Flags == LoadingFlags.DestroyFirst && Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).Count() > 0 )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tLog.Info( \"Component found, replacing\" );\r\n\t\t\t\t\t\tGame.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).FirstOrDefault()?.GameObject.Destroy();\r\n\t\t\t\t\t\tif ( gameObjectSpawned ) return;\r\n\t\t\t\t\t\tvar gb = clone.Prefab.Clone();\r\n\t\t\t\t\t\tgb.BreakFromPrefab();\r\n\t\t\t\t\t\tif ( clone.NetworkSpawn ) gb.NetworkSpawn( null );\r\n\t\t\t\t\t\tgameObjectSpawned = true;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if ( clone.Flags == LoadingFlags.DestroyAll && Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ).Count() > 0 )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tLog.Info( \"Component found, replacing all\" );\r\n\t\t\t\t\t\tforeach ( var component in Game.ActiveScene.Components.GetAll( componentType, FindMode.EverythingInSelfAndDescendants ) )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcomponent.GameObject.Destroy();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif ( gameObjectSpawned ) return;\r\n\t\t\t\t\t\tvar gb = clone.Prefab.Clone();\r\n\t\t\t\t\t\tgb.BreakFromPrefab();\r\n\t\t\t\t\t\tif ( clone.NetworkSpawn ) gb.NetworkSpawn( null );\r\n\t\t\t\t\t\tgameObjectSpawned = true;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif ( !gameObjectSpawned )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar obj = clone.Prefab.Clone();\r\n\t\t\t\t\t\tobj.BreakFromPrefab();\r\n\t\t\t\t\t\tif ( clone.NetworkSpawn ) obj.NetworkSpawn( null );\r\n\t\t\t\t\t\tgameObjectSpawned = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tpublic enum LoadingFlags\r\n\t{\r\n\t\tNone,\r\n\t\t[Description( \"Checks if a component is in the scene, if it is, the prefab will not be spawned\" )]\r\n\t\tCheckForComponents,\r\n\t\t[Description( \"Checks if a component is in the scene, if it is, the first one will be destroyed, and the prefab will be spawned\" )]\r\n\t\tDestroyFirst,\r\n\t\t[Description( \"Checks if a component is in the scene, if it is, all of them will be destroyed and the prefab will be spawned\" )]\r\n\t\tDestroyAll,\r\n\r\n\t}\r\n\r\n\r\n\t[GameResource( \"SceneLoadingResource\", \"loading\", \"A resource that allows spawning of prefabs on scene start\", Icon = \"public\" )]\r\n\tpublic class SceneLoadingResource : GameResource\r\n\t{\r\n\t\tpublic List<SceneLoadingClass> SceneLoadingClasses { get; set; } = new();\r\n\t}\r\n\tpublic class SceneLoadingClass\r\n\t{\r\n\t\tpublic GameObject Prefab { get; set; }\r\n\t\tpublic LoadingFlags Flags { get; set; } = LoadingFlags.None;\r\n\t\tpublic List<Type> ComponentTypes { get; set; }\r\n\t\tpublic bool NetworkSpawn { get; set; } = false;\r\n\r\n\r\n\t\tpublic SceneLoadingClass()\r\n\t\t{\r\n\t\t\tPrefab = null;\r\n\t\t\tFlags = LoadingFlags.None;\r\n\t\t\tComponentTypes = null;\r\n\t\t}\r\n\r\n\t\tpublic SceneLoadingClass( GameObject prefab, LoadingFlags flags, List<Type> componentType )\r\n\t\t{\r\n\t\t\tPrefab = prefab;\r\n\t\t\tFlags = flags;\r\n\t\t\tComponentTypes = componentType;\r\n\t\t}\r\n\t}\r\n\t[Description( \"A custom scene that allows for manipulation of the scene file before loading\" )]\r\n\tpublic class CustomScene\r\n\t{\r\n\t\t[Description( \"The scenefile you are manipulating, override to change it\" )] public virtual SceneFile sceneFileDupe { get; set; }\r\n\t\tpublic string RawScene { get; private set; }\r\n\t\tpublic Scene newScene { get; private set; } = new();\r\n\t\t[Description( \"Called when a scene object is created, return the object you want to spawn, or null to use the default object\" )]\r\n\t\tpublic Action<JsonObject> OnSceneObjectCreated { get; set; }\r\n\t\tpublic Action<JsonObject[]> BeforeSceneLoaded { get; set; }\r\n\t\tpublic CustomScene( SceneFile sceneFile )\r\n\t\t{\r\n\t\t\tsceneFileDupe = new SceneFile();\r\n\t\t\tsceneFileDupe.GameObjects = sceneFile.GameObjects\r\n\t\t\t\t.Select( obj => JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) ) )\r\n\t\t\t\t.ToArray();\r\n\t\t}\r\n\r\n\t\tinternal void LoadSceneInternal()\r\n\t\t{\r\n\t\t\tvar finalScene = new SceneFile();\r\n\t\t\tvar finalList = new List<JsonObject>();\r\n\t\t\tforeach ( var obj in sceneFileDupe.GameObjects )\r\n\t\t\t{\r\n\t\t\t\tOnSceneObjectCreated?.Invoke( obj );\r\n\t\t\t\tfinalList.Add( obj );\r\n\t\t\t}\r\n\r\n\t\t\tfinalScene.GameObjects = finalList.ToArray();\r\n\t\t\tBeforeSceneLoaded?.Invoke( finalScene.GameObjects );\r\n\t\t\tGame.ActiveScene.Load( finalScene );\r\n\t\t}\r\n\t\t[Description( \"Load the custom scene\" )]\r\n\t\tpublic void LoadScene()\r\n\t\t{\r\n\t\t\tLoadSceneInternal();\r\n\t\t}\r\n\r\n\t\t[Description( \"Create a GameObject within the custom scene\" )]\r\n\t\tpublic void CreateObject( GameObject gameObject )\r\n\t\t{\r\n\t\t\tvar clone = gameObject.Clone();\r\n\t\t\tvar objects = sceneFileDupe.GameObjects.ToList();\r\n\t\t\tobjects.Add( clone.Serialize() );\r\n\t\t\tLog.Info( clone.Serialize().ToString() );\r\n\t\t\tsceneFileDupe.GameObjects = objects.ToArray();\r\n\t\t}\r\n\r\n\t\t[Description( \"Remove a GameObject from the custom scene\" )]\r\n\t\tpublic void RemoveObject( JsonNode obj )\r\n\t\t{\r\n\t\t\tList<JsonObject> gameObjects = sceneFileDupe.GameObjects.ToList();\r\n\t\t\tvar selectedObject = gameObjects.Find( x => x == obj );\r\n\t\t\tgameObjects.Remove( selectedObject );\r\n\t\t\tsceneFileDupe.GameObjects = gameObjects.ToArray();\r\n\r\n\t\t}\r\n\r\n\r\n\r\n\t\tpublic void RemoveComponentByType( JsonObject obj, Type type )\r\n\t\t{\r\n\t\t\tvar preObj = JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) );\r\n\t\t\tif ( obj.TryGetPropertyValue( \"Components\", out var jsonnode ) && jsonnode is not null )\r\n\t\t\t{\r\n\t\t\t\tvar jsonString = jsonnode.ToString();\r\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( jsonString ) )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar components = JsonSerializer.Deserialize<List<JsonNode>>( jsonString );\r\n\t\t\t\t\tvar component = components.Find( x => x[\"__type\"]?.ToString() == type.ToString() );\r\n\t\t\t\t\tif ( component is not null )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcomponents.Remove( component );\r\n\t\t\t\t\t\tobj[\"Components\"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tvar gbList = sceneFileDupe.GameObjects.ToList();\r\n\t\t\t\tif ( gbList.Find( x => x == obj ) is null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar parentNode = FindParent( preObj );\r\n\t\t\t\t\tvar parent = gbList.Find( x => x == parentNode );\r\n\t\t\t\t\tif ( parent != null && parent.TryGetPropertyValue( \"Children\", out var childrenJsonNode ) && childrenJsonNode != null )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif ( childrenJsonNode is JsonArray childrenArray )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tUpdateChildComponentsRecursively( childrenArray, obj );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tLog.Warning( \"The 'Children' node is not of type 'JsonArray'.\" );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tgbList[gbList.FindIndex( x => x == obj )] = obj;\r\n\t\t\t\t}\r\n\t\t\t\tsceneFileDupe.GameObjects = gbList.ToArray();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate void UpdateChildComponentsRecursively( JsonArray childrenArray, JsonObject obj )\r\n\t\t{\r\n\t\t\tforeach ( var child in childrenArray )\r\n\t\t\t{\r\n\t\t\t\tif ( child is JsonObject childObject )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( childObject[\"__guid\"].ToString() == obj[\"__guid\"].ToString() )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tchildObject[\"Components\"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( obj[\"Components\"] ) );\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if ( childObject.TryGetPropertyValue( \"Children\", out var nestedChildrenJsonNode ) && nestedChildrenJsonNode is JsonArray nestedChildrenArray )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tUpdateChildComponentsRecursively( nestedChildrenArray, obj );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void AddComponent( JsonObject obj, JsonObject newComponent )\r\n\t\t{\r\n\t\t\tvar preObj = JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) );\r\n\t\t\tif ( obj.TryGetPropertyValue( \"Components\", out var jsonnode ) && jsonnode is not null )\r\n\t\t\t{\r\n\t\t\t\tvar jsonString = jsonnode.ToString();\r\n\t\t\t\tif ( !string.IsNullOrWhiteSpace( jsonString ) )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar components = JsonSerializer.Deserialize<List<JsonNode>>( jsonString );\r\n\t\t\t\t\tcomponents.Add( newComponent );\r\n\t\t\t\t\tobj[\"Components\"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tvar components = new List<JsonNode> { newComponent };\r\n\t\t\t\t\tobj[\"Components\"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );\r\n\t\t\t\t}\r\n\r\n\t\t\t\tvar gbList = sceneFileDupe.GameObjects.ToList();\r\n\t\t\t\tif ( gbList.Find( x => x == obj ) is null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar parentNode = FindParent( preObj );\r\n\t\t\t\t\tvar parent = gbList.Find( x => x == parentNode );\r\n\t\t\t\t\tif ( parent != null && parent.TryGetPropertyValue( \"Children\", out var childrenJsonNode ) && childrenJsonNode != null )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif ( childrenJsonNode is JsonArray childrenArray )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tUpdateChildComponentsRecursively( childrenArray, obj );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tLog.Warning( \"The 'Children' node is not of type 'JsonArray'.\" );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tgbList[gbList.FindIndex( x => x == obj )] = obj;\r\n\t\t\t\t}\r\n\t\t\t\tsceneFileDupe.GameObjects = gbList.ToArray();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void AddComponentByType( JsonObject obj, Type componentType )\r\n\t\t{\r\n\t\t\tvar preObj = JsonSerializer.Deserialize<JsonObject>( JsonSerializer.Serialize( obj ) );\r\n\t\t\tif ( obj.TryGetPropertyValue( \"Components\", out var jsonnode ) && jsonnode is not null )\r\n\t\t\t{\r\n\t\t\t\tvar jsonString = jsonnode.ToString();\r\n\t\t\t\tvar components = !string.IsNullOrWhiteSpace( jsonString )\r\n\t\t\t\t\t? JsonSerializer.Deserialize<List<JsonNode>>( jsonString )\r\n\t\t\t\t\t: new List<JsonNode>();\r\n\r\n\t\t\t\tvar newComponent = new JsonObject\r\n\t\t\t\t{\r\n\t\t\t\t\t[\"__type\"] = componentType.ToString()\r\n\t\t\t\t};\r\n\r\n\t\t\t\tcomponents.Add( newComponent );\r\n\t\t\t\tobj[\"Components\"] = JsonSerializer.Deserialize<JsonNode>( JsonSerializer.Serialize( components ) );\r\n\r\n\t\t\t\tvar gbList = sceneFileDupe.GameObjects.ToList();\r\n\t\t\t\tif ( gbList.Find( x => x == obj ) is null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar parentNode = FindParent( preObj );\r\n\t\t\t\t\tvar parent = gbList.Find( x => x == parentNode );\r\n\t\t\t\t\tif ( parent != null && parent.TryGetPropertyValue( \"Children\", out var childrenJsonNode ) && childrenJsonNode != null )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif ( childrenJsonNode is JsonArray childrenArray )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tUpdateChildComponentsRecursively( childrenArray, obj );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tLog.Warning( \"The 'Children' node is not of type 'JsonArray'.\" );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tgbList[gbList.FindIndex( x => x == obj )] = obj;\r\n\t\t\t\t}\r\n\t\t\t\tsceneFileDupe.GameObjects = gbList.ToArray();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic JsonObject FindParent( JsonNode children )\r\n\t\t{\r\n\t\t\tforeach ( var obj in sceneFileDupe.GameObjects )\r\n\t\t\t{\r\n\t\t\t\tvar parent = FindParentRecursive( obj, children );\r\n\t\t\t\tif ( parent != null )\r\n\t\t\t\t{\r\n\t\t\t\t\treturn parent;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tprivate JsonObject FindParentRecursive( JsonObject parent, JsonNode children )\r\n\t\t{\r\n\t\t\tif ( parent.TryGetPropertyValue( \"Children\", out var childrenJsonNode ) )\r\n\t\t\t{\r\n\t\t\t\tvar childrenList = JsonSerializer.Deserialize<List<JsonObject>>( childrenJsonNode.ToString() );\r\n\t\t\t\tif ( childrenList != null )\r\n\t\t\t\t{\r\n\t\t\t\t\tforeach ( var child in childrenList )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif ( child != null && child[\"__guid\"].ToString() == children[\"__guid\"].ToString() )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\treturn parent;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tvar foundParent = FindParentRecursive( child, children );\r\n\t\t\t\t\t\tif ( foundParent != null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\treturn parent;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tpublic IEnumerable<JsonObject> GetAllObjectsByType( Type type )\r\n\t\t{\r\n\t\t\treturn GetAllObjectsByTypeRecursive( sceneFileDupe.GameObjects, type );\r\n\t\t}\r\n\r\n\t\tpublic IEnumerable<JsonObject> GetAllObjectsByGuid( string guid )\r\n\t\t{\r\n\t\t\treturn GetAllObjectsByGuidRecursive( sceneFileDupe.GameObjects, guid );\r\n\t\t}\r\n\r\n\t\tprivate IEnumerable<JsonObject> GetAllObjectsByTypeRecursive( IEnumerable<JsonObject> gameObjects, Type type )\r\n\t\t{\r\n\t\t\tforeach ( var obj in gameObjects )\r\n\t\t\t{\r\n\t\t\t\tif ( obj.TryGetPropertyValue( \"Components\", out var jsonnode ) && jsonnode is not null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar jsonString = jsonnode.ToString();\r\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( jsonString ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar components = JsonSerializer.Deserialize<List<JsonNode>>( jsonString );\r\n\t\t\t\t\t\tif ( components.Any( component => component[\"__type\"]?.ToString() == type.ToString() ) )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tyield return obj;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif ( obj.TryGetPropertyValue( \"Children\", out var childrenJsonNode ) && childrenJsonNode is not null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar childrenString = childrenJsonNode.ToString();\r\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( childrenString ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar children = JsonSerializer.Deserialize<List<JsonObject>>( childrenString );\r\n\t\t\t\t\t\tforeach ( var child in GetAllObjectsByTypeRecursive( children, type ) )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tyield return child;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate IEnumerable<JsonObject> GetAllObjectsByGuidRecursive( IEnumerable<JsonObject> gameObjects, string guid )\r\n\t\t{\r\n\t\t\tforeach ( var obj in gameObjects )\r\n\t\t\t{\r\n\t\t\t\tif ( obj.TryGetPropertyValue( \"__guid\", out var jsonNode ) && jsonNode is not null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar objectGuid = jsonNode.ToString();\r\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( objectGuid ) && objectGuid == guid )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tyield return obj;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif ( obj.TryGetPropertyValue( \"Children\", out var childrenJsonNode ) && childrenJsonNode is not null )\r\n\t\t\t\t{\r\n\t\t\t\t\tvar childrenString = childrenJsonNode.ToString();\r\n\t\t\t\t\tif ( !string.IsNullOrWhiteSpace( childrenString ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar children = JsonSerializer.Deserialize<List<JsonObject>>( childrenString );\r\n\t\t\t\t\t\tforeach ( var child in GetAllObjectsByGuidRecursive( children, guid ) )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tyield return child;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tpublic IEnumerable<JsonNode> GetAllObjectsByName( string name )\r\n\t\t{\r\n\t\t\tvar objects = sceneFileDupe.GameObjects;\r\n\t\t\tforeach ( var obj in objects )\r\n\t\t\t{\r\n\t\t\t\tobj.TryGetPropertyValue( \"Name\", out var objName );\r\n\t\t\t\tif ( objName is not null && objName.ToString() == name )\r\n\t\t\t\t{\r\n\t\t\t\t\tyield return obj;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "Editor/SceneLoadingResourceCustomEditor.cs",
            "FileName": "SceneLoadingResourceCustomEditor.cs",
            "PackageType": "library",
            "CodeKind": "Editor",
            "AssetVersionId": 65380,
            "Code": "using Editor;\r\nusing Sandbox;\r\nusing SceneLoading;\r\n\r\n//[CustomEditor(typeof(SceneLoadingClass))]\r\npublic sealed class SceneLoadingResourceCustomEditor : ControlWidget\r\n{\r\n\tpublic SceneLoadingResourceCustomEditor( SerializedProperty property ) : base( property )\r\n\t{\r\n\t\tLayout = Layout.Column();\r\n\r\n\t\tif ( property.IsNull )\r\n\t\t{\r\n\t\t\tproperty.SetValue( new SceneLoadingClass() );\r\n\t\t}\r\n\r\n\t\tvar so = property.GetValue<SceneLoadingClass>()?.GetSerialized();\r\n\t\tif ( so is null ) return;\r\n\t\tvar controlSheet = new ControlSheet();\r\n\t\tcontrolSheet.AddObject( so );\r\n\t\tLayout.Add( controlSheet );\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "PlayerController.cs",
            "FileName": "PlayerController.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using Sandbox.Citizen;\r\n\r\n[Group( \"Walker\" )]\r\n[Title( \"Walker - Player Controller\" )]\r\npublic sealed class PlayerController : Component\r\n{\r\n\t[Property] public CharacterController CharacterController { get; set; }\r\n\t[Property] public float CrouchMoveSpeed { get; set; } = 64.0f;\r\n\t[Property] public float WalkMoveSpeed { get; set; } = 190.0f;\r\n\t[Property] public float RunMoveSpeed { get; set; } = 190.0f;\r\n\t[Property] public float SprintMoveSpeed { get; set; } = 320.0f;\r\n\r\n\t[Property] public CitizenAnimationHelper AnimationHelper { get; set; }\r\n\r\n\t[Sync] public bool Crouching { get; set; }\r\n\t[Sync] public Angles EyeAngles { get; set; }\r\n\t[Sync] public Vector3 WishVelocity { get; set; }\r\n\r\n\tpublic bool WishCrouch;\r\n\tpublic float EyeHeight = 64;\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tif ( !IsProxy )\r\n\t\t{\r\n\t\t\tMouseInput();\r\n\t\t\tTransform.Rotation = new Angles( 0, EyeAngles.yaw, 0 );\r\n\t\t}\r\n\r\n\t\tUpdateAnimation();\r\n\t}\r\n\r\n\tprotected override void OnFixedUpdate()\r\n\t{\r\n\t\tif ( IsProxy )\r\n\t\t\treturn;\r\n\r\n\t\tCrouchingInput();\r\n\t\tMovementInput();\r\n\t}\r\n\r\n\tprivate void MouseInput()\r\n\t{\r\n\t\tvar e = EyeAngles;\r\n\t\te += Input.AnalogLook;\r\n\t\te.pitch = e.pitch.Clamp( -90, 90 );\r\n\t\te.roll = 0.0f;\r\n\t\tEyeAngles = e;\r\n\t}\r\n\r\n\tfloat CurrentMoveSpeed\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\tif ( Crouching ) return CrouchMoveSpeed;\r\n\t\t\tif ( Input.Down( \"run\" ) ) return SprintMoveSpeed;\r\n\t\t\tif ( Input.Down( \"walk\" ) ) return WalkMoveSpeed;\r\n\r\n\t\t\treturn RunMoveSpeed;\r\n\t\t}\r\n\t}\r\n\r\n\tRealTimeSince lastGrounded;\r\n\tRealTimeSince lastUngrounded;\r\n\tRealTimeSince lastJump;\r\n\r\n\tfloat GetFriction()\r\n\t{\r\n\t\tif ( CharacterController.IsOnGround ) return 6.0f;\r\n\r\n\t\t// air friction\r\n\t\treturn 0.2f;\r\n\t}\r\n\r\n\tprivate void MovementInput()\r\n\t{\r\n\t\tif ( CharacterController is null )\r\n\t\t\treturn;\r\n\r\n\t\tvar cc = CharacterController;\r\n\r\n\t\tVector3 halfGravity = Scene.PhysicsWorld.Gravity * Time.Delta * 0.5f;\r\n\r\n\t\tWishVelocity = Input.AnalogMove;\r\n\r\n\t\tif ( lastGrounded < 0.2f && lastJump > 0.3f && Input.Pressed( \"jump\" ) )\r\n\t\t{\r\n\t\t\tlastJump = 0;\r\n\t\t\tcc.Punch( Vector3.Up * 300 );\r\n\t\t}\r\n\r\n\t\tif ( !WishVelocity.IsNearlyZero() )\r\n\t\t{\r\n\t\t\tWishVelocity = new Angles( 0, EyeAngles.yaw, 0 ).ToRotation() * WishVelocity;\r\n\t\t\tWishVelocity = WishVelocity.WithZ( 0 );\r\n\t\t\tWishVelocity = WishVelocity.ClampLength( 1 );\r\n\t\t\tWishVelocity *= CurrentMoveSpeed;\r\n\r\n\t\t\tif ( !cc.IsOnGround )\r\n\t\t\t{\r\n\t\t\t\tWishVelocity = WishVelocity.ClampLength( 50 );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tcc.ApplyFriction( GetFriction() );\r\n\r\n\t\tif ( cc.IsOnGround )\r\n\t\t{\r\n\t\t\tcc.Accelerate( WishVelocity );\r\n\t\t\tcc.Velocity = CharacterController.Velocity.WithZ( 0 );\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tcc.Velocity += halfGravity;\r\n\t\t\tcc.Accelerate( WishVelocity );\r\n\r\n\t\t}\r\n\r\n\t\t//\r\n\t\t// Don't walk through other players, let them push you out of the way\r\n\t\t//\r\n\t\tvar pushVelocity = PlayerPusher.GetPushVector( Transform.Position + Vector3.Up * 40.0f, Scene, GameObject );\r\n\t\tif ( !pushVelocity.IsNearlyZero() )\r\n\t\t{\r\n\t\t\tvar travelDot = cc.Velocity.Dot( pushVelocity.Normal );\r\n\t\t\tif ( travelDot < 0 )\r\n\t\t\t{\r\n\t\t\t\tcc.Velocity -= pushVelocity.Normal * travelDot * 0.6f;\r\n\t\t\t}\r\n\r\n\t\t\tcc.Velocity += pushVelocity * 128.0f;\r\n\t\t}\r\n\r\n\t\tcc.Move();\r\n\r\n\t\tif ( !cc.IsOnGround )\r\n\t\t{\r\n\t\t\tcc.Velocity += halfGravity;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tcc.Velocity = cc.Velocity.WithZ( 0 );\r\n\t\t}\r\n\r\n\t\tif ( cc.IsOnGround )\r\n\t\t{\r\n\t\t\tlastGrounded = 0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tlastUngrounded = 0;\r\n\t\t}\r\n\t}\r\n\tfloat DuckHeight = (64 - 36);\r\n\r\n\tbool CanUncrouch()\r\n\t{\r\n\t\tif ( !Crouching ) return true;\r\n\t\tif ( lastUngrounded < 0.2f ) return false;\r\n\r\n\t\tvar tr = CharacterController.TraceDirection( Vector3.Up * DuckHeight );\r\n\t\treturn !tr.Hit; // hit nothing - we can!\r\n\t}\r\n\r\n\tpublic void CrouchingInput()\r\n\t{\r\n\t\tWishCrouch = Input.Down( \"duck\" );\r\n\r\n\t\tif ( WishCrouch == Crouching )\r\n\t\t\treturn;\r\n\r\n\t\t// crouch\r\n\t\tif ( WishCrouch )\r\n\t\t{\r\n\t\t\tCharacterController.Height = 36;\r\n\t\t\tCrouching = WishCrouch;\r\n\r\n\t\t\t// if we're not on the ground, slide up our bbox so when we crouch\r\n\t\t\t// the bottom shrinks, instead of the top, which will mean we can reach\r\n\t\t\t// places by crouch jumping that we couldn't.\r\n\t\t\tif ( !CharacterController.IsOnGround )\r\n\t\t\t{\r\n\t\t\t\tCharacterController.MoveTo( Transform.Position += Vector3.Up * DuckHeight, false );\r\n\t\t\t\tTransform.ClearLerp();\r\n\t\t\t\tEyeHeight -= DuckHeight;\r\n\t\t\t}\r\n\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// uncrouch\r\n\t\tif ( !WishCrouch )\r\n\t\t{\r\n\t\t\tif ( !CanUncrouch() ) return;\r\n\r\n\t\t\tCharacterController.Height = 64;\r\n\t\t\tCrouching = WishCrouch;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\r\n\t}\r\n\r\n\tprivate void UpdateCamera()\r\n\t{\r\n\t\tvar camera = Scene.GetAllComponents<CameraComponent>().Where( x => x.IsMainCamera ).FirstOrDefault();\r\n\t\tif ( camera is null ) return;\r\n\r\n\t\tvar targetEyeHeight = Crouching ? 28 : 64;\r\n\t\tEyeHeight = EyeHeight.LerpTo( targetEyeHeight, RealTime.Delta * 10.0f );\r\n\r\n\t\tvar targetCameraPos = Transform.Position + new Vector3( 0, 0, EyeHeight );\r\n\r\n\t\t// smooth view z, so when going up and down stairs or ducking, it's smooth af\r\n\t\tif ( lastUngrounded > 0.2f )\r\n\t\t{\r\n\t\t\ttargetCameraPos.z = camera.Transform.Position.z.LerpTo( targetCameraPos.z, RealTime.Delta * 25.0f );\r\n\t\t}\r\n\r\n\t\tcamera.Transform.Position = targetCameraPos;\r\n\t\tcamera.Transform.Rotation = EyeAngles;\r\n\t\tcamera.FieldOfView = Preferences.FieldOfView;\r\n\t}\r\n\r\n\tprotected override void OnPreRender()\r\n\t{\r\n\t\tUpdateBodyVisibility();\r\n\r\n\t\tif ( IsProxy )\r\n\t\t\treturn;\r\n\r\n\t\tUpdateCamera();\r\n\t}\r\n\r\n\tprivate void UpdateAnimation()\r\n\t{\r\n\t\tif ( AnimationHelper is null || CharacterController is null ) return;\r\n\r\n\r\n\t\tvar wv = WishVelocity.Length;\r\n\r\n\t\tAnimationHelper.WithWishVelocity( WishVelocity );\r\n\t\tAnimationHelper.WithVelocity( CharacterController.Velocity );\r\n\t\tAnimationHelper.IsGrounded = CharacterController.IsOnGround;\r\n\t\tAnimationHelper.DuckLevel = Crouching ? 1.0f : 0.0f;\r\n\r\n\t\tAnimationHelper.MoveStyle = wv < 160f ? CitizenAnimationHelper.MoveStyles.Walk : CitizenAnimationHelper.MoveStyles.Run;\r\n\r\n\t\tvar lookDir = EyeAngles.ToRotation().Forward * 1024;\r\n\t\tAnimationHelper.WithLook( lookDir, 1, 0.5f, 0.25f );\r\n\t}\r\n\r\n\tprivate void UpdateBodyVisibility()\r\n\t{\r\n\t\tif ( AnimationHelper is null )\r\n\t\t\treturn;\r\n\r\n\t\tvar renderMode = ModelRenderer.ShadowRenderType.On;\r\n\t\tif ( !IsProxy ) renderMode = ModelRenderer.ShadowRenderType.ShadowsOnly;\r\n\r\n\t\tAnimationHelper.Target.RenderType = renderMode;\r\n\r\n\t\tforeach ( var clothing in AnimationHelper.Target.Components.GetAll<ModelRenderer>( FindMode.InChildren ) )\r\n\t\t{\r\n\t\t\tif ( !clothing.Tags.Has( \"clothing\" ) )\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tclothing.RenderType = renderMode;\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "JiggleBone.cs",
            "FileName": "JiggleBone.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "public sealed class JiggleBone : TransformProxyComponent\r\n{\r\n\tJiggleBoneState state = new JiggleBoneState();\r\n\r\n\t[Property]\r\n\tpublic Vector3 StartPoint = new Vector3( 0, 0, 0 );\r\n\r\n\t[Property]\r\n\tpublic Vector3 EndPoint = new Vector3( 32, 0, 0 );\r\n\r\n\t[Property, Range( 0, 2 )]\r\n\tpublic float Speed { get; set; } = 1.0f;\r\n\r\n\t[Property, Range( 0, 2 )]\r\n\tpublic float Stiffness { get; set; } = 1.0f;\r\n\r\n\t[Property, Range( 0, 2 )]\r\n\tpublic float Damping { get; set; } = 1.0f;\r\n\r\n\t[Property, Range( 0, 100 )]\r\n\tpublic float Radius { get; set; } = 40.0f;\r\n\r\n\t[Property, Range( 0, 100 )]\r\n\tpublic float Mass { get; set; } = 1.0f;\r\n\r\n\tTransform LocalJigglePosition;\r\n\r\n\tprotected override void OnEnabled()\r\n\t{\r\n\t\tLocalJigglePosition = Transform.Local;\r\n\r\n\t\tbase.OnEnabled();\r\n\r\n\t\tstate = new JiggleBoneState();\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tvar oldPos = LocalJigglePosition;\r\n\r\n\r\n\r\n\t\tusing ( Transform.DisableProxy() )\r\n\t\t{\r\n\t\t\tvar worldTx = Transform.World;\r\n\r\n\t\t\tvar startPoint = worldTx.PointToWorld( StartPoint );\r\n\t\t\tvar endPoint = worldTx.PointToWorld( EndPoint );\r\n\r\n\t\t\t//Gizmo.Draw.LineSphere( startPoint, 1 );\r\n\t\t\t//Gizmo.Draw.LineSphere( endPoint, 1 );\r\n\r\n\t\t\tstate.Extent = (endPoint - startPoint);\r\n\t\t\tstate.Stiffness = Stiffness;\r\n\t\t\tstate.Damping = Damping;\r\n\t\t\tstate.Radius = Radius;\r\n\t\t\tstate.Mass = Mass;\r\n\r\n\t\t\tstate.Update( startPoint, Time.Delta * Speed * 16.0f );\r\n\r\n\t\t\tvar tx = worldTx.RotateAround( startPoint, state.Rotation );\r\n\t\t\tLocalJigglePosition = GameObject.Parent.Transform.World.ToLocal( tx );\r\n\t\t}\r\n\r\n\t\tif ( oldPos != LocalJigglePosition )\r\n\t\t{\r\n\t\t\tMarkTransformChanged();\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void DrawGizmos()\r\n\t{\r\n\t\tbase.DrawGizmos();\r\n\r\n\t\tif ( !Gizmo.IsSelected )\r\n\t\t\treturn;\r\n\r\n\t\tusing ( Transform.DisableProxy() )\r\n\t\t{\r\n\t\t\tGizmo.Transform = Transform.World;\r\n\t\t\tGizmo.Draw.IgnoreDepth = false;\r\n\t\t\tGizmo.Draw.Color = Gizmo.Colors.Yaw.WithAlpha( 0.5f );\r\n\t\t\tGizmo.Draw.Line( StartPoint, EndPoint );\r\n\t\t\tGizmo.Draw.LineBBox( BBox.FromPositionAndSize( StartPoint, 5 ) );\r\n\t\t\tGizmo.Draw.LineBBox( BBox.FromPositionAndSize( EndPoint, 5 ) );\r\n\t\t\tGizmo.Draw.LineSphere( EndPoint, Radius * 2.0f, 4 );\r\n\t\t}\r\n\t}\r\n\r\n\tpublic override Transform GetLocalTransform()\r\n\t{\r\n\t\treturn LocalJigglePosition;\r\n\t}\r\n}\r\n\r\nclass JiggleBoneState\r\n{\r\n\tpublic Vector3 Extent = new Vector3( 32, 0, 0 );\r\n\r\n\tpublic Vector3 Position { get; set; }\r\n\tpublic Rotation Rotation { get; set; }\r\n\tpublic float Stiffness { get; set; } = 1.0f;\r\n\tpublic float Damping { get; set; } = 1.0f;\r\n\tpublic float Radius { get; set; } = 10.0f;\r\n\tpublic float Gravity { get; set; } = 1.0f;\r\n\tpublic float Mass { get; set; } = 1.0f;\r\n\r\n\r\n\tVector3 basePosition;\r\n\tVector3 velocity;\r\n\r\n\tpublic JiggleBoneState()\r\n\t{\r\n\r\n\t}\r\n\r\n\tinternal void Update( Vector3 position, float timeDelta )\r\n\t{\r\n\t\tbasePosition = position + Extent;\r\n\r\n\t\t// initialization\r\n\t\tif ( Position == default )\r\n\t\t{\r\n\t\t\tPosition = basePosition;\r\n\t\t}\r\n\r\n\t\t// Calculate spring force based on displacement from the cube\r\n\t\tVector3 displacement = Position - basePosition;\r\n\t\tVector3 springForce = -Stiffness * displacement;\r\n\r\n\t\t// Calculate acceleration (Newton's second law)\r\n\t\tVector3 acceleration = springForce / Mass;\r\n\r\n\t\t// Update velocity (integrate acceleration)\r\n\t\tvelocity += acceleration * timeDelta;\r\n\r\n\t\t// Apply exponential damping\r\n\t\tvelocity *= (float)Math.Exp( -Damping * timeDelta );\r\n\r\n\t\t// Update position (integrate velocity)\r\n\t\tPosition += velocity * timeDelta;\r\n\r\n\t\t{\r\n\t\t\tvar diff = Position - basePosition;\r\n\t\t\tvar diffLen = diff.Length;\r\n\t\t\tif ( diffLen > Radius )\r\n\t\t\t{\r\n\t\t\t\tPosition = basePosition + diff.Normal * Radius;\r\n\t\t\t\t//velocity = velocity.AddClamped( -diff * 2.0f, diff.Length );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Store the rotation offset result\r\n\t\tRotation = Rotation.FromToRotation( basePosition - position, Position - position );\r\n\r\n\t\t//Gizmo.Draw.IgnoreDepth = true;\r\n\t\t//Gizmo.Draw.Line( position, Position );\r\n\t\t//Gizmo.Draw.Line( basePosition, Position );\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": "BlankPostProcess.cs",
            "FileName": "BlankPostProcess.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "using System;\r\nusing Sandbox;\r\n\r\n\r\n//Only uses to get the scene camera\r\npublic sealed class BlankPostProcess : PostProcess\r\n{\r\n\tIDisposable renderHook;\r\n\tpublic SceneCamera sceneCam { get; set; }\r\n\r\n\tprotected override void OnEnabled()\r\n\t{\r\n\t\trenderHook = Camera.AddHookBeforeOverlay( \"My Post Processing\", 1000, RenderEffect );\r\n\t}\r\n\r\n\tprotected override void OnDisabled()\r\n\t{\r\n\t\trenderHook?.Dispose();\r\n\t\trenderHook = null;\r\n\t}\r\n\r\n\tRenderAttributes attributes = new RenderAttributes();\r\n\r\n\tpublic void RenderEffect( SceneCamera camera )\r\n\t{\r\n\t\tif ( !camera.EnablePostProcessing )\r\n\t\t\treturn;\r\n\t\tsceneCam = camera;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "nolankicks.sceneloadingutility",
            "Path": ".obj/__compiler_extra.cs",
            "FileName": "__compiler_extra.cs",
            "PackageType": "library",
            "CodeKind": "Game",
            "AssetVersionId": 65380,
            "Code": "global using static Sandbox.Internal.GlobalGameNamespace;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonTitle\", \"Scene Loading Utility\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonIdent\", \"sceneloadingutility\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"OrgIdent\", \"nolankicks\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"Ident\", \"nolankicks.sceneloadingutility\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"CompileTime\", \"8/10/2024 7:55:20 PM\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineVersion\", \"17\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineMinorVersion\", \"1\" )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \".NETCoreApp,Version=v7.0\", FrameworkDisplayName = \".NET 7.0\" )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\"0.0.120.0\")]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\"0.0.120.0\")]"
        }
    ]
}