Editor/TerrainLinesRegistration.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Editor;
using Editor.TerrainEditor;
using Sandbox;
namespace TerrainLines;
internal static class TerrainLinesRegistration
{
private static bool _unsupported;
[EditorEvent.Frame]
private static void Attach()
{
if ( _unsupported || SceneViewWidget.Current?.Tools?.CurrentTool is not TerrainEditorTool parent ) return;
if ( parent.Tools.Any( x => x is TerrainLineTool ) ) return;
try
{
// Native Terrain has no registration API. Keep the two internal assumptions here.
if ( parent.Tools is not ICollection<EditorTool> tools || tools.IsReadOnly )
throw new InvalidOperationException( "Terrain's tool collection is no longer mutable." );
var setter = typeof( EditorTool ).GetProperty( nameof( EditorTool.Manager ), BindingFlags.Public | BindingFlags.Instance );
if ( setter?.GetSetMethod( true ) is null ) throw new InvalidOperationException( "EditorTool.Manager has no setter." );
var lines = new TerrainLineTool();
setter.SetValue( lines, parent.Manager );
tools.Add( lines );
foreach ( var toolbar in SceneViewWidget.Current.GetDescendants<VerticalToolbarGroup>()
.Where( x => x.GetType().Name.Contains( "EditorSubToolBarWidget" ) ) ) toolbar.Build();
}
catch ( Exception e )
{
_unsupported = true;
Log.Warning( $"Terrain Lines cannot attach to this editor version: {e.Message}" );
}
}
[Shortcut( "tools.terrain.lines", "7", typeof( SceneViewWidget ) )]
public static void SelectLines()
{
if ( SceneViewWidget.Current?.Tools?.CurrentTool is not TerrainEditorTool ) return;
Attach();
EditorToolManager.SetSubTool( nameof( TerrainLineTool ) );
}
[EditorEvent.Hotload]
private static void AfterHotload()
{
_unsupported = false;
if ( SceneViewWidget.Current?.Tools?.CurrentTool is not TerrainEditorTool parent ) return;
if ( parent.Tools is not ICollection<EditorTool> tools || tools.IsReadOnly ) return;
foreach ( var previous in tools.OfType<TerrainLineTool>().ToArray() )
{
// Native sidebars only rebuild when the tool identity changes. Recreate callbacks after hotload.
previous.Cancel();
bool selected = parent.CurrentTool == previous;
var replacement = new TerrainLineTool { Settings = previous.Settings };
typeof( EditorTool ).GetProperty( nameof( EditorTool.Manager ) ).SetValue( replacement, parent.Manager );
tools.Remove( previous );
tools.Add( replacement );
if ( selected ) parent.CurrentTool = replacement;
previous.Dispose();
}
}
}