Code/Freecam.Commands.cs
namespace Duccsoft;
public sealed partial class Freecam
{
/// <summary>
/// Holds whatever instance of <see cref="Freecam"/> may have been created by invoking <see cref="Toggle"/>.
/// </summary>
private static Freecam _conCmdInstance;
/// <summary>
/// Holds references to whatever <see cref="Freecam"/> instances were disabled by <see cref="Toggle"/>.
/// </summary>
private static HashSet<Freecam> _disabledFreecams = new();
[ConCmd("freecam", Help = "Toggle a mode where the camera can be moved and rotated freely.")]
public static void Toggle()
{
var camera = Game.ActiveScene?.Camera;
if ( !camera.IsValid() )
return;
// If we were already using the freecam ConCmd...
if ( _conCmdInstance?.Active == true )
{
// ...toggle it off by destroying its GameObject.
_conCmdInstance?.GameObject?.Destroy();
_conCmdInstance = null;
// Reenable whatever non-ConCmd freecam we might have previously disabled.
ReenablePreviousFreecam();
return;
}
// If there is an active freecam not created by this ConCmd, disable it.
DisableActiveFreecams();
// Make a new GameObject with a Freecam component.
CreateFreecam();
}
private static void DisableActiveFreecams()
{
// Find any freecams that are currently active...
var freecams = Game.ActiveScene.GetAllComponents<Freecam>();
if ( !freecams.Any() )
return;
foreach ( var freecam in freecams )
{
// ...and turn them off.
freecam.Enabled = false;
_disabledFreecams.Add( freecam );
}
return;
}
private static void ReenablePreviousFreecam()
{
// Clean out any invalid freecams (e.g. GameObject or component was deleted)
foreach( var freecam in _disabledFreecams.ToList() )
{
if ( !freecam.IsValid() )
{
_disabledFreecams.Remove( freecam );
}
}
// Find the first valid freecam we had previously disabled
var firstFreecam = _disabledFreecams.FirstOrDefault();
if ( firstFreecam is null )
return;
firstFreecam.Enabled = true;
_disabledFreecams.Remove( firstFreecam );
}
private static void CreateFreecam()
{
// There were no active freecams, so create one.
var freecamGo = new GameObject( true, "ConCmd Freecam" );
var camTx = Game.ActiveScene.Camera.Transform;
freecamGo.Transform.Position = camTx.Position;
_conCmdInstance = freecamGo.Components.Create<Freecam>();
_conCmdInstance._lookAngle = camTx.Rotation;
// Assume that if someone uses a ConCmd to freecam, they also want to noclip.
// Colliding with walls is more of an official "photo mode" or "spectator mode" kind of thing.
_conCmdInstance.UseCollision = false;
}
}