Editor/WaterNetworkTool.cs

Editor tool for the Water Tool 2 editor. It selects or creates the WaterNetwork to edit, exposes subtools (river, edit, lake, sea), updates selection, and draws an overlay graph of rivers, lakes, flow arrows and nodes using gizmos each frame.

File Access
using System.Collections.Generic;
using System.Linq;
using Editor;
using Sandbox;

namespace RedSnail.WaterTool2.Editor;

/// <summary>
/// Root tool of Water Tool 2. Resolves the network being edited and draws the water graph over the scene; the actual
/// interaction lives in the sub tools: drawing rivers, editing them, laying out lakes, and building the sea's coast.
/// </summary>
[EditorTool]
[Title("Water Tool")]
[Icon("water")]
[Alias("watertool2")]
[Group("Tools")]
public class WaterNetworkTool : EditorTool
{
	/// <summary>
	/// The network edits go to. Null until the first river or lake is drawn in a scene without one.
	/// </summary>
	public WaterNetwork Network { get; private set; }



	public override IEnumerable<EditorTool> GetSubtools()
	{
		yield return new WaterRiverTool();
		yield return new WaterEditTool();
		yield return new WaterLakeTool();
		yield return new WaterSeaTool();
	}



	public override void OnEnabled()
	{
		AllowGameObjectSelection = false;
		ResolveNetwork();
	}



	public override void OnSelectionChanged()
	{
		ResolveNetwork();
	}



	public override void OnUpdate()
	{
		if (!Network.IsValid())
			ResolveNetwork();

		DrawGraph();
	}



	/// <summary>
	/// Prefers a selected network, else the scene's own.
	/// </summary>
	public void ResolveNetwork()
	{
		var selected = Selection.OfType<GameObject>()
			.Select(gameObject => gameObject.Components.Get<WaterNetwork>())
			.FirstOrDefault(network => network.IsValid());

		Network = selected ?? Scene.Get<WaterNetwork>();
	}



	/// <summary>
	/// Creates a fresh network at the scene root. Call it inside an undo scope that records GameObject creations.
	/// </summary>
	public WaterNetwork CreateNetwork()
	{
		var gameObject = Scene.CreateObject();
		gameObject.Name = "Water Network";

		Network = gameObject.AddComponent<WaterNetwork>();

		return Network;
	}



	/// <summary>
	/// The rivers, which way they flow, their nodes, the lakes and the coast, faintly, so the graph can be read at a
	/// glance.
	/// </summary>
	private void DrawGraph()
	{
		if (!Network.IsValid())
			return;

		var transform = Network.WorldTransform;

		using (Gizmo.Scope("watertool2_graph"))
		{
			Gizmo.Draw.IgnoreDepth = true;
			Gizmo.Draw.LineThickness = 1.0f;

			foreach (var river in Network.Rivers)
			{
				if (!Network.TryGetCurve(river, out var curve))
					continue;

				Gizmo.Draw.Color = Color.Cyan.WithAlpha(0.35f);
				WaterGizmos.DrawCurve(curve, transform);

				float size = float.Max(40.0f, Network.WidthOf(river).MaxHalf * 0.6f);

				Gizmo.Draw.Color = Color.Cyan.WithAlpha(0.6f);
				WaterGizmos.DrawFlowArrows(curve, transform, size * 6.0f, size);
			}

			foreach (var lake in Network.Lakes)
			{
				Gizmo.Draw.Color = (lake.IsCoast ? Color.Blue : Color.Cyan).WithAlpha(lake.IsCoast ? 0.5f : 0.3f);
				WaterGizmos.DrawLake(Network, lake, transform);
			}

			Gizmo.Draw.Color = Color.White.WithAlpha(0.6f);

			foreach (var node in Network.Nodes)
				WaterGizmos.DrawNode(transform.PointToWorld(node.Position), false);
		}
	}
}