Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SpawnSuppressionVisualizer v1.1.0
ValheimSpawnSupp.dll
Decompiled a month agousing 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; } }