Decompiled source of SpawnSuppressionVisualizer v1.1.0

ValheimSpawnSupp.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("F1shar")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim BepInEx mod that draws the enemy spawn suppression radius of player-base structures.")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+645d3adca056e749fb572b0547a3acd74f5f9fc2")]
[assembly: AssemblyProduct("Spawn Suppression Visualizer")]
[assembly: AssemblyTitle("ValheimSpawnSupp")]
[assembly: AssemblyVersion("1.1.0.0")]
namespace ValheimSpawnSupp;

internal enum AreaKind
{
	None,
	PlayerBase,
	NoMonsters
}
internal static class AreaGeometry
{
	private const float RayStartHeight = 6000f;

	private const float RayLength = 10000f;

	private static int _terrainMask;

	private static int _solidMask;

	public static AreaKind Classify(EffectArea area, SpawnSuppConfig cfg)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		Type type = area.m_type;
		if (cfg.ShowPlayerBase.Value && (type & 4) != 0)
		{
			return AreaKind.PlayerBase;
		}
		if (cfg.ShowNoMonsters.Value && (type & 0x20) != 0)
		{
			return AreaKind.NoMonsters;
		}
		return AreaKind.None;
	}

	public static float GetRadius(EffectArea area, out Vector3 center)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		Collider component = ((Component)area).GetComponent<Collider>();
		Transform transform = ((Component)area).transform;
		center = transform.position;
		if ((Object)(object)component == (Object)null)
		{
			return 0f;
		}
		Vector3 lossyScale = transform.lossyScale;
		SphereCollider val = (SphereCollider)(object)((component is SphereCollider) ? component : null);
		if (val != null)
		{
			center = transform.TransformPoint(val.center);
			return val.radius * Mathf.Max(new float[3]
			{
				Mathf.Abs(lossyScale.x),
				Mathf.Abs(lossyScale.y),
				Mathf.Abs(lossyScale.z)
			});
		}
		CapsuleCollider val2 = (CapsuleCollider)(object)((component is CapsuleCollider) ? component : null);
		if (val2 != null)
		{
			center = transform.TransformPoint(val2.center);
			return val2.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z));
		}
		BoxCollider val3 = (BoxCollider)(object)((component is BoxCollider) ? component : null);
		if (val3 != null)
		{
			center = transform.TransformPoint(val3.center);
			float num = val3.size.x * Mathf.Abs(lossyScale.x) * 0.5f;
			float num2 = val3.size.z * Mathf.Abs(lossyScale.z) * 0.5f;
			return Mathf.Sqrt(num * num + num2 * num2);
		}
		Bounds bounds = component.bounds;
		center = ((Bounds)(ref bounds)).center;
		return Mathf.Max(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.z);
	}

	public static float SampleHeight(Vector3 position, HeightMode mode, float fallback)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		if (!TrySampleHeight(position, mode, out var height))
		{
			return fallback;
		}
		return height;
	}

	public static bool TrySampleHeight(Vector3 position, HeightMode mode, out float height)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		height = 0f;
		switch (mode)
		{
		case HeightMode.Flat:
			return false;
		case HeightMode.Terrain:
			if (Heightmap.GetHeight(position, ref height))
			{
				return true;
			}
			break;
		}
		EnsureMasks();
		int num = ((mode == HeightMode.Solid) ? _solidMask : _terrainMask);
		position.y = 6000f;
		RaycastHit val = default(RaycastHit);
		if (Physics.Raycast(position, Vector3.down, ref val, 10000f, num))
		{
			height = ((RaycastHit)(ref val)).point.y;
			return true;
		}
		return false;
	}

	private static void EnsureMasks()
	{
		if (_terrainMask == 0)
		{
			_terrainMask = LayerMask.GetMask(new string[1] { "terrain" });
			_solidMask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "terrain" });
		}
	}
}
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class ConsoleCommands
{
	private const string CommandName = "spawnsupp";

	private static bool _registered;

	private static void Postfix()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (!_registered)
		{
			_registered = true;
			new ConsoleCommand("spawnsupp", "Toggle the spawn suppression radius overlay. Usage: spawnsupp [on|off|toggle]", new ConsoleEvent(Execute), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}

	private static void Execute(ConsoleEventArgs args)
	{
		SpawnSuppressionVisualizer instance = SpawnSuppressionVisualizer.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			args.Context.AddString("Spawn suppression overlay is not available (headless server?).");
			return;
		}
		switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "toggle")
		{
		case "1":
		case "on":
		case "true":
			instance.SetVisible(visible: true);
			break;
		case "0":
		case "off":
		case "false":
			instance.SetVisible(visible: false);
			break;
		case "toggle":
			instance.Toggle();
			break;
		default:
			args.Context.AddString("Usage: spawnsupp [on|off|toggle]");
			return;
		}
		args.Context.AddString("Spawn suppression overlay: " + (instance.Visible ? "ON" : "OFF"));
	}
}
internal struct SuppressionSphere
{
	public Vector3 Center;

	public float Radius;
}
internal sealed class CoverageFill
{
	private const float HeightResampleInterval = 4f;

	private readonly GameObject _root;

	private readonly MeshRenderer _renderer;

	private readonly Mesh _mesh;

	private Vector3[] _vertices = (Vector3[])(object)new Vector3[0];

	private Color32[] _colors = (Color32[])(object)new Color32[0];

	private bool[] _valid = new bool[0];

	private int[] _triangles = new int[0];

	private int _gridSize;

	private float _cellSize;

	private float _heightOffset;

	private HeightMode _heightMode;

	private Vector3 _origin;

	private float _nextHeightSample;

	public CoverageFill(Material material)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		_root = new GameObject("SpawnSuppCoverage");
		_mesh = new Mesh
		{
			name = "SpawnSuppCoverage",
			indexFormat = (IndexFormat)1
		};
		_mesh.MarkDynamic();
		_root.AddComponent<MeshFilter>().sharedMesh = _mesh;
		_renderer = _root.AddComponent<MeshRenderer>();
		((Renderer)_renderer).sharedMaterial = material;
		((Renderer)_renderer).shadowCastingMode = (ShadowCastingMode)0;
		((Renderer)_renderer).receiveShadows = false;
		((Renderer)_renderer).lightProbeUsage = (LightProbeUsage)0;
		((Renderer)_renderer).reflectionProbeUsage = (ReflectionProbeUsage)0;
	}

	public void SetVisible(bool visible)
	{
		if ((Object)(object)_renderer != (Object)null)
		{
			((Renderer)_renderer).enabled = visible;
		}
	}

	public void Update(Vector3 playerPos, List<SuppressionSphere> spheres, SpawnSuppConfig cfg)
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		float num = Mathf.Max(5f, cfg.FillRadius.Value);
		float num2 = Mathf.Max(0.5f, cfg.FillCellSize.Value);
		int num3 = Mathf.CeilToInt(num * 2f / num2) + 1;
		float value = cfg.FillHeightOffset.Value;
		HeightMode value2 = cfg.HeightMode.Value;
		float num4 = (float)(num3 - 1) * num2 * 0.5f;
		Vector3 val = default(Vector3);
		((Vector3)(ref val))..ctor(Mathf.Round(playerPos.x / num2) * num2 - num4, playerPos.y, Mathf.Round(playerPos.z / num2) * num2 - num4);
		int num5;
		int num6;
		if (num3 == _gridSize)
		{
			num5 = ((!Mathf.Approximately(num2, _cellSize)) ? 1 : 0);
			if (num5 == 0 && val.x == _origin.x && val.z == _origin.z && value == _heightOffset && value2 == _heightMode)
			{
				num6 = ((Time.unscaledTime >= _nextHeightSample) ? 1 : 0);
				goto IL_0101;
			}
		}
		else
		{
			num5 = 1;
		}
		num6 = 1;
		goto IL_0101;
		IL_0101:
		bool flag = (byte)num6 != 0;
		if (num5 != 0)
		{
			int num7 = num3 * num3;
			_vertices = (Vector3[])(object)new Vector3[num7];
			_colors = (Color32[])(object)new Color32[num7];
			_valid = new bool[num7];
			_triangles = BuildTriangles(num3);
			_gridSize = num3;
			_cellSize = num2;
		}
		if (flag)
		{
			_origin = val;
			_heightOffset = value;
			_heightMode = value2;
			_nextHeightSample = Time.unscaledTime + 4f;
			SampleHeights(playerPos.y);
		}
		Tint(playerPos, num, spheres, cfg);
		_mesh.Clear(false);
		_mesh.vertices = _vertices;
		_mesh.colors32 = _colors;
		_mesh.triangles = _triangles;
		_mesh.RecalculateBounds();
	}

	public void Destroy()
	{
		if ((Object)(object)_mesh != (Object)null)
		{
			Object.Destroy((Object)(object)_mesh);
		}
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
	}

	private void SampleHeights(float fallbackY)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		int gridSize = _gridSize;
		Vector3 val = default(Vector3);
		for (int i = 0; i < gridSize; i++)
		{
			for (int j = 0; j < gridSize; j++)
			{
				int num = i * gridSize + j;
				((Vector3)(ref val))..ctor(_origin.x + (float)j * _cellSize, fallbackY, _origin.z + (float)i * _cellSize);
				float height;
				bool flag = AreaGeometry.TrySampleHeight(val, _heightMode, out height);
				_valid[num] = flag || _heightMode == HeightMode.Flat;
				val.y = (flag ? height : fallbackY) + _heightOffset;
				_vertices[num] = val;
			}
		}
	}

	private void Tint(Vector3 playerPos, float radius, List<SuppressionSphere> spheres, SpawnSuppConfig cfg)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		FillMode value = cfg.FillMode.Value;
		Color value2 = cfg.CoveredFillColor.Value;
		Color value3 = cfg.UncoveredFillColor.Value;
		float num = Mathf.Clamp01(cfg.FillOpacity.Value);
		bool flag = value == FillMode.Both || value == FillMode.Covered;
		bool flag2 = value == FillMode.Both || value == FillMode.Uncovered;
		float num2 = radius * 0.85f;
		int count = spheres.Count;
		Vector3 val2 = default(Vector3);
		for (int i = 0; i < _vertices.Length; i++)
		{
			Vector3 val = _vertices[i];
			float num3 = val.x - playerPos.x;
			float num4 = val.z - playerPos.z;
			float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4);
			if (!_valid[i] || num5 > radius)
			{
				_colors[i] = new Color32((byte)0, (byte)0, (byte)0, (byte)0);
				continue;
			}
			((Vector3)(ref val2))..ctor(val.x, val.y - _heightOffset, val.z);
			bool flag3 = false;
			for (int j = 0; j < count; j++)
			{
				SuppressionSphere suppressionSphere = spheres[j];
				Vector3 val3 = val2 - suppressionSphere.Center;
				if (((Vector3)(ref val3)).sqrMagnitude <= suppressionSphere.Radius * suppressionSphere.Radius)
				{
					flag3 = true;
					break;
				}
			}
			if (!(flag3 ? flag : flag2))
			{
				_colors[i] = new Color32((byte)0, (byte)0, (byte)0, (byte)0);
				continue;
			}
			Color val4 = (flag3 ? value2 : value3);
			float num6 = ((num5 <= num2) ? 1f : (1f - Mathf.InverseLerp(num2, radius, num5)));
			val4.a = num * num6;
			_colors[i] = Color32.op_Implicit(val4);
		}
	}

	private static int[] BuildTriangles(int n)
	{
		int[] array = new int[(n - 1) * (n - 1) * 6];
		int num = 0;
		for (int i = 0; i < n - 1; i++)
		{
			for (int j = 0; j < n - 1; j++)
			{
				int num2 = i * n + j;
				int num3 = (i + 1) * n + j;
				int num4 = (i + 1) * n + j + 1;
				int num5 = i * n + j + 1;
				array[num++] = num2;
				array[num++] = num3;
				array[num++] = num4;
				array[num++] = num2;
				array[num++] = num4;
				array[num++] = num5;
			}
		}
		return array;
	}
}
internal static class OverlayMaterial
{
	public static Material Create(Color tint, int renderQueue)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		string[] obj = new string[5] { "Sprites/Default", "Particles/Standard Unlit", "Legacy Shaders/Particles/Alpha Blended", "Unlit/Color", "Hidden/Internal-Colored" };
		Shader val = null;
		string[] array = obj;
		for (int i = 0; i < array.Length; i++)
		{
			val = Shader.Find(array[i]);
			if ((Object)(object)val != (Object)null)
			{
				break;
			}
		}
		Material val2 = new Material(val)
		{
			hideFlags = (HideFlags)61,
			renderQueue = renderQueue
		};
		if (val2.HasProperty("_Color"))
		{
			val2.SetColor("_Color", tint);
		}
		if (val2.HasProperty("_TintColor"))
		{
			val2.SetColor("_TintColor", tint);
		}
		if (val2.HasProperty("_ZWrite"))
		{
			val2.SetInt("_ZWrite", 0);
		}
		if (val2.HasProperty("_Cull"))
		{
			val2.SetInt("_Cull", 0);
		}
		if (val2.HasProperty("_SrcBlend") && val2.HasProperty("_DstBlend"))
		{
			val2.SetInt("_SrcBlend", 5);
			val2.SetInt("_DstBlend", 10);
		}
		return val2;
	}
}
internal sealed class RadiusRing
{
	private const float MarkerHeight = 2f;

	private readonly GameObject _root;

	private readonly LineRenderer _ring;

	private readonly LineRenderer _marker;

	private Vector3[] _points = (Vector3[])(object)new Vector3[0];

	public AreaKind Kind { get; private set; }

	private RadiusRing(Material material)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		_root = new GameObject("SpawnSuppRing");
		_ring = CreateLine(_root, material);
		_ring.loop = true;
		_marker = CreateLine(new GameObject("SpawnSuppMarker"), material);
		((Component)_marker).transform.SetParent(_root.transform, false);
		_marker.loop = false;
		_marker.positionCount = 2;
	}

	public static RadiusRing Create(Material material)
	{
		return new RadiusRing(material);
	}

	private static LineRenderer CreateLine(GameObject go, Material material)
	{
		LineRenderer obj = go.AddComponent<LineRenderer>();
		((Renderer)obj).sharedMaterial = material;
		obj.useWorldSpace = true;
		obj.alignment = (LineAlignment)0;
		obj.textureMode = (LineTextureMode)0;
		obj.numCornerVertices = 2;
		obj.numCapVertices = 2;
		((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0;
		((Renderer)obj).receiveShadows = false;
		((Renderer)obj).lightProbeUsage = (LightProbeUsage)0;
		((Renderer)obj).reflectionProbeUsage = (ReflectionProbeUsage)0;
		obj.generateLightingData = false;
		return obj;
	}

	public void Apply(Vector3 center, float radius, AreaKind kind, Color color, SpawnSuppConfig cfg)
	{
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
		Kind = kind;
		int num = Mathf.Max(3, cfg.Segments.Value);
		if (_points.Length != num)
		{
			_points = (Vector3[])(object)new Vector3[num];
			_ring.positionCount = num;
		}
		float value = cfg.LineWidth.Value;
		float value2 = cfg.HeightOffset.Value;
		HeightMode value3 = cfg.HeightMode.Value;
		_ring.widthMultiplier = value;
		_ring.startColor = color;
		_ring.endColor = color;
		float num2 = (float)Math.PI * 2f / (float)num;
		Vector3 val = default(Vector3);
		for (int i = 0; i < num; i++)
		{
			float num3 = (float)i * num2;
			((Vector3)(ref val))..ctor(center.x + Mathf.Cos(num3) * radius, center.y, center.z + Mathf.Sin(num3) * radius);
			val.y = AreaGeometry.SampleHeight(val, value3, center.y) + value2;
			_points[i] = val;
		}
		_ring.SetPositions(_points);
		bool value4 = cfg.ShowCenterMarker.Value;
		((Renderer)_marker).enabled = value4;
		if (value4)
		{
			float num4 = AreaGeometry.SampleHeight(center, value3, center.y);
			_marker.widthMultiplier = value * 0.75f;
			_marker.startColor = color;
			_marker.endColor = new Color(color.r, color.g, color.b, color.a * 0.25f);
			_marker.SetPosition(0, new Vector3(center.x, num4 + value2 * 0.5f, center.z));
			_marker.SetPosition(1, new Vector3(center.x, num4 + value2 * 0.5f + 2f, center.z));
		}
	}

	public void Destroy()
	{
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
	}
}
public enum HeightMode
{
	Terrain,
	Solid,
	Flat
}
public enum FillMode
{
	None,
	Covered,
	Uncovered,
	Both
}
internal sealed class SpawnSuppConfig
{
	public ConfigEntry<KeyboardShortcut> ToggleKey { get; }

	public ConfigEntry<bool> EnabledOnStart { get; }

	public ConfigEntry<bool> ShowPlayerBase { get; }

	public ConfigEntry<bool> ShowNoMonsters { get; }

	public ConfigEntry<Color> PlayerBaseColor { get; }

	public ConfigEntry<Color> NoMonstersColor { get; }

	public ConfigEntry<float> LineWidth { get; }

	public ConfigEntry<int> Segments { get; }

	public ConfigEntry<float> HeightOffset { get; }

	public ConfigEntry<HeightMode> HeightMode { get; }

	public ConfigEntry<bool> ShowCenterMarker { get; }

	public ConfigEntry<FillMode> FillMode { get; }

	public ConfigEntry<Color> CoveredFillColor { get; }

	public ConfigEntry<Color> UncoveredFillColor { get; }

	public ConfigEntry<float> FillOpacity { get; }

	public ConfigEntry<float> FillRadius { get; }

	public ConfigEntry<float> FillCellSize { get; }

	public ConfigEntry<float> FillHeightOffset { get; }

	public ConfigEntry<float> MaxDistance { get; }

	public ConfigEntry<float> RefreshInterval { get; }

	public ConfigEntry<bool> ShowHudLabel { get; }

	public SpawnSuppConfig(ConfigFile config)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Expected O, but got Unknown
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Expected O, but got Unknown
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Expected O, but got Unknown
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Expected O, but got Unknown
		//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ba: Expected O, but got Unknown
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Expected O, but got Unknown
		//IL_0322: Unknown result type (might be due to invalid IL or missing references)
		//IL_032c: Expected O, but got Unknown
		//IL_035b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0365: Expected O, but got Unknown
		//IL_0394: Unknown result type (might be due to invalid IL or missing references)
		//IL_039e: Expected O, but got Unknown
		ToggleKey = config.Bind<KeyboardShortcut>("1 - General", "ToggleKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Hotkey that toggles the spawn suppression overlay.");
		EnabledOnStart = config.Bind<bool>("1 - General", "EnabledOnStart", false, "Show the overlay as soon as you enter a world.");
		ShowPlayerBase = config.Bind<bool>("2 - Areas", "ShowPlayerBase", true, "Draw PlayerBase effect areas (workbenches, forges, fires, wards...). The spawn system refuses to spawn creatures inside these areas, so this is the actual spawn suppression radius.");
		ShowNoMonsters = config.Bind<bool>("2 - Areas", "ShowNoMonsters", true, "Draw NoMonsters effect areas. Monster AI will not path into these areas.");
		PlayerBaseColor = config.Bind<Color>("2 - Areas", "PlayerBaseColor", new Color(0.25f, 0.95f, 0.35f, 0.85f), "Ring colour for PlayerBase areas (RGBA, 0-1).");
		NoMonstersColor = config.Bind<Color>("2 - Areas", "NoMonstersColor", new Color(0.35f, 0.6f, 1f, 0.85f), "Ring colour for NoMonsters areas (RGBA, 0-1).");
		LineWidth = config.Bind<float>("3 - Appearance", "LineWidth", 0.2f, new ConfigDescription("Width of the ring line in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 2f), Array.Empty<object>()));
		Segments = config.Bind<int>("3 - Appearance", "Segments", 72, new ConfigDescription("Number of line segments per ring. Higher is smoother but costs more raycasts on refresh.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(12, 256), Array.Empty<object>()));
		HeightOffset = config.Bind<float>("3 - Appearance", "HeightOffset", 0.3f, new ConfigDescription("How far above the sampled ground the ring is drawn, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
		HeightMode = config.Bind<HeightMode>("3 - Appearance", "HeightMode", ValheimSpawnSupp.HeightMode.Terrain, "Terrain: follow the ground heightmap. Solid: follow the highest solid surface (floors, rocks). Flat: level ring at the area's centre height.");
		ShowCenterMarker = config.Bind<bool>("3 - Appearance", "ShowCenterMarker", true, "Draw a short vertical post at the centre of every area so you can tell which structure a ring belongs to.");
		FillMode = config.Bind<FillMode>("4 - Ground fill", "FillMode", ValheimSpawnSupp.FillMode.Both, "Shade the ground around you by spawn coverage. Covered = tint only ground where spawns are blocked, Uncovered = tint only ground where spawns can happen, Both = tint both, None = rings only. Only PlayerBase areas count as coverage, because those are what the spawner checks.");
		CoveredFillColor = config.Bind<Color>("4 - Ground fill", "CoveredFillColor", new Color(0.2f, 0.9f, 0.3f, 1f), "Tint for ground where creatures cannot spawn. Alpha is ignored; use FillOpacity.");
		UncoveredFillColor = config.Bind<Color>("4 - Ground fill", "UncoveredFillColor", new Color(0.95f, 0.2f, 0.15f, 1f), "Tint for ground where creatures can spawn. Alpha is ignored; use FillOpacity.");
		FillOpacity = config.Bind<float>("4 - Ground fill", "FillOpacity", 0.3f, new ConfigDescription("Opacity of the ground fill (0 = invisible, 1 = solid).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		FillRadius = config.Bind<float>("4 - Ground fill", "FillRadius", 80f, new ConfigDescription("Radius in metres around the player that the ground fill covers. The edge fades out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 300f), Array.Empty<object>()));
		FillCellSize = config.Bind<float>("4 - Ground fill", "FillCellSize", 2f, new ConfigDescription("Grid spacing of the fill mesh in metres. Smaller is more precise but costs more vertices ((2 * FillRadius / FillCellSize)^2 of them).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), Array.Empty<object>()));
		FillHeightOffset = config.Bind<float>("4 - Ground fill", "FillHeightOffset", 0.35f, new ConfigDescription("How far above the ground the fill floats, in metres. Raise it if it clips into slopes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
		MaxDistance = config.Bind<float>("5 - Performance", "MaxDistance", 250f, new ConfigDescription("Only draw areas whose edge is within this many metres of the player. 0 = no limit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2000f), Array.Empty<object>()));
		RefreshInterval = config.Bind<float>("5 - Performance", "RefreshInterval", 1f, new ConfigDescription("Seconds between overlay rebuilds. Newly placed or removed structures show up after at most this long.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
		ShowHudLabel = config.Bind<bool>("5 - Performance", "ShowHudLabel", true, "Show a small on-screen label with the number of drawn areas while the overlay is active.");
	}
}
[BepInPlugin("f1shar.ValheimSpawnSupp", "Spawn Suppression Visualizer", "1.1.0")]
public sealed class SpawnSuppPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "f1shar.ValheimSpawnSupp";

	public const string PluginName = "Spawn Suppression Visualizer";

	public const string PluginVersion = "1.1.0";

	private Harmony _harmony;

	internal static ManualLogSource Log { get; private set; }

	internal static SpawnSuppConfig Settings { get; private set; }

	private void Awake()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Invalid comparison between Unknown and I4
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Settings = new SpawnSuppConfig(((BaseUnityPlugin)this).Config);
		if ((int)SystemInfo.graphicsDeviceType == 4)
		{
			Log.LogInfo((object)"Spawn Suppression Visualizer loaded on a headless server; overlay disabled.");
			return;
		}
		((Component)this).gameObject.AddComponent<SpawnSuppressionVisualizer>();
		try
		{
			_harmony = new Harmony("f1shar.ValheimSpawnSupp");
			_harmony.PatchAll(typeof(SpawnSuppPlugin).Assembly);
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not register the 'spawnsupp' console command: " + ex.Message));
		}
		Log.LogInfo((object)string.Format("{0} {1} loaded. Toggle overlay with {2} or the 'spawnsupp' console command.", "Spawn Suppression Visualizer", "1.1.0", Settings.ToggleKey.Value));
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal sealed class SpawnSuppressionVisualizer : MonoBehaviour
{
	private const int FillRenderQueue = 3000;

	private const int RingRenderQueue = 3001;

	private readonly Dictionary<int, RadiusRing> _rings = new Dictionary<int, RadiusRing>();

	private readonly HashSet<int> _seen = new HashSet<int>();

	private readonly List<int> _stale = new List<int>();

	private readonly Dictionary<AreaKind, Material> _materials = new Dictionary<AreaKind, Material>();

	private readonly List<SuppressionSphere> _spheres = new List<SuppressionSphere>();

	private CoverageFill _fill;

	private Material _fillMaterial;

	private float _nextRefresh;

	private GUIStyle _labelStyle;

	public static SpawnSuppressionVisualizer Instance { get; private set; }

	public bool Visible { get; private set; }

	public int DrawnCount { get; private set; }

	private static SpawnSuppConfig Cfg => SpawnSuppPlugin.Settings;

	private void Awake()
	{
		Instance = this;
		Visible = Cfg.EnabledOnStart.Value;
	}

	private void OnDestroy()
	{
		ClearRings();
		if ((Object)(object)_fillMaterial != (Object)null)
		{
			Object.Destroy((Object)(object)_fillMaterial);
		}
		foreach (Material value in _materials.Values)
		{
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
		}
		_materials.Clear();
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void Update()
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		KeyboardShortcut value = Cfg.ToggleKey.Value;
		if (((KeyboardShortcut)(ref value)).IsDown() && HotkeysAllowed())
		{
			Toggle();
		}
		if (!Visible)
		{
			if (_rings.Count > 0)
			{
				ClearRings();
			}
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			if (_rings.Count > 0)
			{
				ClearRings();
			}
		}
		else if (!(Time.unscaledTime < _nextRefresh))
		{
			_nextRefresh = Time.unscaledTime + Mathf.Max(0.1f, Cfg.RefreshInterval.Value);
			Refresh(((Component)localPlayer).transform.position);
		}
	}

	private void OnGUI()
	{
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Expected O, but got Unknown
		if (Visible && Cfg.ShowHudLabel.Value && !((Object)(object)Player.m_localPlayer == (Object)null) && !Hud.IsUserHidden())
		{
			if (_labelStyle == null)
			{
				_labelStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 14
				};
			}
			string text = $"Spawn suppression overlay: {DrawnCount} area(s)";
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(12f, (float)Screen.height * 0.5f - 10f, 420f, 24f);
			GUI.color = new Color(0f, 0f, 0f, 0.8f);
			GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), text, _labelStyle);
			GUI.color = Color.white;
			GUI.Label(val, text, _labelStyle);
		}
	}

	private static bool HotkeysAllowed()
	{
		if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
		{
			return false;
		}
		if (Console.IsVisible() || TextInput.IsVisible())
		{
			return false;
		}
		return true;
	}

	public void Toggle()
	{
		SetVisible(!Visible);
	}

	public void SetVisible(bool visible)
	{
		if (Visible != visible)
		{
			Visible = visible;
			_nextRefresh = 0f;
			if (!visible)
			{
				ClearRings();
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)2, visible ? "Spawn suppression overlay: ON" : "Spawn suppression overlay: OFF", 0, (Sprite)null, false);
			}
		}
	}

	private void Refresh(Vector3 playerPos)
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Invalid comparison between Unknown and I4
		//IL_027c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		SpawnSuppConfig cfg = Cfg;
		float value = cfg.MaxDistance.Value;
		bool flag = cfg.FillMode.Value != FillMode.None;
		float value2 = cfg.FillRadius.Value;
		_seen.Clear();
		_spheres.Clear();
		List<EffectArea> allAreas = EffectArea.GetAllAreas();
		if (allAreas != null)
		{
			foreach (EffectArea item in allAreas)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				bool flag2 = (item.m_type & 4) > 0;
				AreaKind areaKind = AreaGeometry.Classify(item, cfg);
				if (areaKind == AreaKind.None && !(flag && flag2))
				{
					continue;
				}
				Vector3 center;
				float radius = AreaGeometry.GetRadius(item, out center);
				if (radius <= 0.01f)
				{
					continue;
				}
				if (flag && flag2)
				{
					Vector3 val = center - playerPos;
					val.y = 0f;
					float num = value2 + radius;
					if (((Vector3)(ref val)).sqrMagnitude <= num * num)
					{
						_spheres.Add(new SuppressionSphere
						{
							Center = center,
							Radius = radius
						});
					}
				}
				if (areaKind == AreaKind.None)
				{
					continue;
				}
				if (value > 0f)
				{
					float num2 = value + radius;
					Vector3 val2 = center - playerPos;
					val2.y = 0f;
					if (((Vector3)(ref val2)).sqrMagnitude > num2 * num2)
					{
						continue;
					}
				}
				int instanceID = ((Object)item).GetInstanceID();
				if (!_rings.TryGetValue(instanceID, out var value3))
				{
					value3 = RadiusRing.Create(GetMaterial(areaKind));
					_rings[instanceID] = value3;
				}
				value3.Apply(center, radius, areaKind, ColorFor(areaKind), cfg);
				_seen.Add(instanceID);
			}
		}
		_stale.Clear();
		foreach (KeyValuePair<int, RadiusRing> ring in _rings)
		{
			if (!_seen.Contains(ring.Key))
			{
				_stale.Add(ring.Key);
			}
		}
		for (int i = 0; i < _stale.Count; i++)
		{
			_rings[_stale[i]].Destroy();
			_rings.Remove(_stale[i]);
		}
		DrawnCount = _rings.Count;
		UpdateFill(playerPos, flag);
	}

	private void UpdateFill(Vector3 playerPos, bool enabled)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		if (!enabled)
		{
			_fill?.SetVisible(visible: false);
			return;
		}
		if (_fill == null)
		{
			if ((Object)(object)_fillMaterial == (Object)null)
			{
				_fillMaterial = OverlayMaterial.Create(Color.white, 3000);
			}
			_fill = new CoverageFill(_fillMaterial);
		}
		_fill.SetVisible(visible: true);
		_fill.Update(playerPos, _spheres, Cfg);
	}

	private void ClearRings()
	{
		foreach (RadiusRing value in _rings.Values)
		{
			value.Destroy();
		}
		_rings.Clear();
		DrawnCount = 0;
		_fill?.Destroy();
		_fill = null;
	}

	private static Color ColorFor(AreaKind kind)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		if (kind != AreaKind.NoMonsters)
		{
			return Cfg.PlayerBaseColor.Value;
		}
		return Cfg.NoMonstersColor.Value;
	}

	private Material GetMaterial(AreaKind kind)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		if (_materials.TryGetValue(kind, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		value = OverlayMaterial.Create(ColorFor(kind), 3001);
		_materials[kind] = value;
		return value;
	}
}