Decompiled source of MorkhallaProgressionScaling v0.1.1

plugins/MorkhallaProgressionScaling.Core.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MorkhallaProgressionScaling.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+3ad057b0fa4691d6870c25aec36bef61999d2ac7")]
[assembly: AssemblyProduct("MorkhallaProgressionScaling.Core")]
[assembly: AssemblyTitle("MorkhallaProgressionScaling.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace MorkhallaScaling.Core;

public static class Progression
{
	public const string ClearCountKey = "mps_morkhallaclears";

	public const int DeepNorthBiomeBit = 64;

	public static bool IsDeepNorth(int biomeMask)
	{
		return biomeMask == 64;
	}

	public static int ScaleCount(int baseCount, float multiplier)
	{
		if (baseCount <= 0 || multiplier <= 1f)
		{
			return baseCount;
		}
		return Math.Max(baseCount, (int)Math.Round((float)baseCount * multiplier, MidpointRounding.AwayFromZero));
	}

	public static float ScaleInterval(float baseInterval, float multiplier)
	{
		if (baseInterval <= 0f || multiplier <= 1f)
		{
			return baseInterval;
		}
		return baseInterval / multiplier;
	}

	public static int RandomRound(float value, double roll)
	{
		if (value <= 0f)
		{
			return 0;
		}
		int num = (int)Math.Floor(value);
		return num + ((roll < (double)(value - (float)num)) ? 1 : 0);
	}

	public static int TakeShare(ref float carry, int baseCount, float multiplier)
	{
		if (baseCount <= 0 || multiplier <= 1f)
		{
			return 0;
		}
		carry += (float)baseCount * (multiplier - 1f);
		int num = (int)Math.Floor(carry + 0.0001f);
		carry -= num;
		return num;
	}

	public static bool IsCountedEvent(string eventName, string filter)
	{
		if (string.IsNullOrWhiteSpace(filter))
		{
			return true;
		}
		string[] array = filter.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			if (string.Equals(array[i].Trim(), eventName, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	public static int GammeltrollCount(float perStep, int steps)
	{
		if (perStep <= 0f || steps <= 0)
		{
			return 0;
		}
		return (int)Math.Round(perStep * (float)steps, MidpointRounding.AwayFromZero);
	}

	public static int ParseClearCount(string value)
	{
		if (string.IsNullOrEmpty(value) || !int.TryParse(value.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			return 0;
		}
		return Math.Max(0, result);
	}

	public static string ClearCountKeyLine(int clears)
	{
		return "mps_morkhallaclears " + Math.Max(0, clears).ToString(CultureInfo.InvariantCulture);
	}
}
public sealed class ScalingSettings
{
	public static readonly ScalingSettings Default = new ScalingSettings(enabled: true, 3, 0.5f, 5f, "Moose");

	private const int FieldCount = 5;

	public const int MaxStepsLimit = 20;

	public const float MaxSpawnIncreasePerStep = 5f;

	public const float MaxStarChancePerStep = 50f;

	private readonly HashSet<string> excluded;

	public bool Enabled { get; }

	public int MaxSteps { get; }

	public float SpawnIncreasePerStep { get; }

	public float StarChancePerStep { get; }

	public string ExcludedCreatures { get; }

	public ScalingSettings(bool enabled, int maxSteps, float spawnIncreasePerStep, float starChancePerStep, string excludedCreatures)
	{
		Enabled = enabled;
		MaxSteps = Math.Min(20, Math.Max(0, maxSteps));
		SpawnIncreasePerStep = Clamp(spawnIncreasePerStep, 5f);
		StarChancePerStep = Clamp(starChancePerStep, 50f);
		excluded = new HashSet<string>(from n in (excludedCreatures ?? "").Split(',', ';')
			select n.Trim() into n
			where n.Length > 0
			select n, StringComparer.OrdinalIgnoreCase);
		ExcludedCreatures = string.Join(",", excluded.OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase));
	}

	public bool IsExcluded(string prefabName)
	{
		if (prefabName != null)
		{
			return excluded.Contains(prefabName);
		}
		return false;
	}

	public int CapSteps(int steps)
	{
		return Math.Min(Math.Max(0, steps), MaxSteps);
	}

	public float SpawnMultiplier(int steps)
	{
		if (!Enabled)
		{
			return 1f;
		}
		return 1f + SpawnIncreasePerStep * (float)CapSteps(steps);
	}

	public float StarChance(float vanillaChance, int steps)
	{
		int num = CapSteps(steps);
		if (!Enabled || num == 0 || StarChancePerStep <= 0f)
		{
			return vanillaChance;
		}
		return Math.Min(100f, vanillaChance + StarChancePerStep * (float)num);
	}

	public string Encode()
	{
		return string.Join(";", Enabled ? "1" : "0", MaxSteps.ToString(CultureInfo.InvariantCulture), F(SpawnIncreasePerStep), F(StarChancePerStep), ExcludedCreatures);
	}

	public static bool TryDecode(string text, out ScalingSettings settings)
	{
		settings = null;
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		string[] array = text.Split(new char[1] { ';' });
		if (array.Length != 5 || (array[0] != "0" && array[0] != "1") || !int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !TryParseFloat(array[2], out var value) || !TryParseFloat(array[3], out var value2))
		{
			return false;
		}
		settings = new ScalingSettings(array[0] == "1", result, value, value2, array[4]);
		return true;
	}

	private static bool TryParseFloat(string text, out float value)
	{
		if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value))
		{
			return !float.IsInfinity(value);
		}
		return false;
	}

	private static float Clamp(float value, float max)
	{
		if (!float.IsNaN(value) && !(value < 0f))
		{
			return Math.Min(max, value);
		}
		return 0f;
	}

	private static string F(float value)
	{
		return value.ToString("R", CultureInfo.InvariantCulture);
	}
}

plugins/MorkhallaProgressionScaling.dll

Decompiled 4 hours 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 MorkhallaScaling.Core;
using UnityEngine;

[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("jonasd")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+3ad057b0fa4691d6870c25aec36bef61999d2ac7")]
[assembly: AssemblyProduct("Morkhalla Progression Scaling")]
[assembly: AssemblyTitle("MorkhallaProgressionScaling")]
[assembly: AssemblyVersion("0.1.1.0")]
namespace MorkhallaScaling
{
	internal static class Gammeltrolls
	{
		private const string NameToken = "$enemy_trollfrost";

		private const string FallbackPrefabName = "TrollFrost";

		private const float WaterLevel = 30f;

		private const float MinHeightAboveWater = 1f;

		private const float MaxSlopeDegrees = 30f;

		private const float MinDistanceBetween = 6f;

		private const float GroundOffset = 0.5f;

		private const int AttemptsPerTroll = 50;

		private static GameObject cachedPrefab;

		public static void PlaceAround(Vector3 center, int steps)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			int num = Progression.GammeltrollCount(Plugin.GammeltrollsPerStep.Value, steps);
			if (num <= 0 || !SettingsSync.Current.Enabled)
			{
				return;
			}
			GameObject val = FindPrefab();
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"No Gammeltroll prefab found (looked for a creature named $enemy_trollfrost). Set [3 - Gammeltrolls] PrefabName in the config.");
				return;
			}
			float num2 = Mathf.Max(0f, Plugin.GammeltrollMinDistance.Value);
			float maxDistance = Mathf.Max(num2 + 1f, Plugin.GammeltrollMaxDistance.Value);
			List<Vector3> list = new List<Vector3>();
			for (int i = 0; i < num; i++)
			{
				if (!TryFindSpot(center, num2, maxDistance, list, out var spot))
				{
					Plugin.Log.LogWarning((object)$"Found no dry, flat ground for Gammeltroll {i + 1} of {num} near {center}.");
					continue;
				}
				list.Add(spot);
				CreateSaved(val, spot + Vector3.up * 0.5f);
			}
			Plugin.Log.LogInfo((object)$"Placed {list.Count} {((Object)val).name} near the invasion at {center}.");
		}

		private static void CreateSaved(GameObject prefab, Vector3 position)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			ZNetView.StartGhostInit();
			try
			{
				Object.Destroy((Object)(object)Object.Instantiate<GameObject>(prefab, position, Quaternion.Euler(0f, Random.Range(0f, 360f), 0f)));
			}
			finally
			{
				ZNetView.FinishGhostInit();
			}
		}

		private static bool TryFindSpot(Vector3 center, float minDistance, float maxDistance, List<Vector3> placed, out Vector3 spot)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0041: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_00aa: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Cos((float)Math.PI / 6f);
			Vector3 val2 = default(Vector3);
			for (int i = 0; i < 50; i++)
			{
				Vector2 insideUnitCircle = Random.insideUnitCircle;
				Vector2 normalized = ((Vector2)(ref insideUnitCircle)).normalized;
				if (!(normalized == Vector2.zero))
				{
					Vector2 val = normalized * Random.Range(minDistance, maxDistance);
					((Vector3)(ref val2))..ctor(center.x + val.x, 0f, center.z + val.y);
					val2.y = WorldGenerator.instance.GetHeight(val2.x, val2.z);
					if (!(val2.y < 31f) && !(WorldGenerator.instance.GetNormal(val2.x, val2.z, 1f).y < num) && !IsTooClose(val2, placed))
					{
						spot = val2;
						return true;
					}
				}
			}
			spot = Vector3.zero;
			return false;
		}

		private static bool IsTooClose(Vector3 candidate, List<Vector3> placed)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			foreach (Vector3 item in placed)
			{
				if (Utils.DistanceXZ(candidate, item) < 6f)
				{
					return true;
				}
			}
			return false;
		}

		private static GameObject FindPrefab()
		{
			string text = Plugin.GammeltrollPrefab.Value?.Trim();
			if (!string.IsNullOrEmpty(text))
			{
				return ZNetScene.instance.GetPrefab(text);
			}
			if ((Object)(object)cachedPrefab != (Object)null)
			{
				return cachedPrefab;
			}
			foreach (GameObject prefab2 in ZNetScene.instance.m_prefabs)
			{
				Character val = (((Object)(object)prefab2 != (Object)null) ? prefab2.GetComponent<Character>() : null);
				if ((Object)(object)val != (Object)null && val.m_name == "$enemy_trollfrost")
				{
					cachedPrefab = prefab2;
					Plugin.Detail("Gammeltroll prefab: " + ((Object)prefab2).name);
					return cachedPrefab;
				}
			}
			GameObject prefab = ZNetScene.instance.GetPrefab("TrollFrost");
			if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<Character>() != (Object)null)
			{
				cachedPrefab = prefab;
				Plugin.Log.LogInfo((object)"Gammeltroll found by prefab name TrollFrost, not by $enemy_trollfrost.");
			}
			return cachedPrefab;
		}
	}
	internal static class Places
	{
		private static readonly HashSet<Vector2s> MorkhallaZones = new HashSet<Vector2s>();

		private static readonly Dictionary<Vector2s, float> Carry = new Dictionary<Vector2s, float>();

		public static void Reset()
		{
			MorkhallaZones.Clear();
			Carry.Clear();
		}

		public static int TakeShare(Vector3 position, int baseCount, float multiplier)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Vector2s zone = ZoneSystem.GetZone(position);
			Carry.TryGetValue(zone, out var value);
			int result = Progression.TakeShare(ref value, baseCount, multiplier);
			Carry[zone] = value;
			return result;
		}

		public static bool IsInMorkhalla(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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)
			if (!Character.InInterior(position))
			{
				return false;
			}
			Vector2s zone = ZoneSystem.GetZone(position);
			if (MorkhallaZones.Contains(zone))
			{
				return true;
			}
			DungeonGenerator[] array = Object.FindObjectsByType<DungeonGenerator>((FindObjectsSortMode)0);
			foreach (DungeonGenerator val in array)
			{
				if (ZoneSystem.GetZone(((Component)val).transform.position) == zone && (val.m_themes & 0x20000) != 0)
				{
					MorkhallaZones.Add(zone);
					return true;
				}
			}
			return false;
		}
	}
	[BepInPlugin("com.jonasd.valheim.morkhallaprogressionscaling", "Morkhalla Progression Scaling", "0.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.jonasd.valheim.morkhallaprogressionscaling";

		public const string PluginName = "Morkhalla Progression Scaling";

		public const string PluginVersion = "0.1.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> LogDetails;

		internal static ConfigEntry<string> CountedEvents;

		internal static ConfigEntry<float> GammeltrollsPerStep;

		internal static ConfigEntry<string> GammeltrollPrefab;

		internal static ConfigEntry<float> GammeltrollMinDistance;

		internal static ConfigEntry<float> GammeltrollMaxDistance;

		private ConfigEntry<bool> enabledSetting;

		private ConfigEntry<int> maxSteps;

		private ConfigEntry<float> spawnIncreasePerStep;

		private ConfigEntry<float> starChancePerStep;

		private ConfigEntry<string> excludedCreatures;

		private Harmony harmony;

		private static readonly HashSet<string> LoggedOnce = new HashSet<string>();

		private void Awake()
		{
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ScalingSettings val = ScalingSettings.Default;
			maxSteps = ((BaseUnityPlugin)this).Config.Bind<int>("1 - Steps", "MaxSteps", val.MaxSteps, "A step is one cleared Morkhalla (counted when its ice starts a Jotunn invasion). Progression stops at this many steps; 3 = the three Morkhallas vanilla needs.");
			spawnIncreasePerStep = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Steps", "SpawnIncreasePerStep", val.SpawnIncreasePerStep, "Added per step to Deep North spawning, as a fraction of vanilla: 0.5 = +50% per step, so 3 steps = 2.5x. Applies to how often creatures spawn, how many may be around at once, group size, Morkhalla creatures and Jotunn invasion creatures. 0 turns spawn scaling off.");
			starChancePerStep = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Steps", "StarChancePerStep", val.StarChancePerStep, "Percentage points added per step to the star chance of Deep North creatures that can have stars (vanilla is about 10%, so 5 per step = 25% at 3 steps). Creatures without stars never get any. 0 turns this off.");
			GammeltrollsPerStep = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Steps", "GammeltrollsPerStep", 1f, "Gammeltrolls placed once near each new Jotunn invasion: this many per step, counting the step that started it (1 = first invasion 1, second 2, third 3). They never respawn. 0 turns this off. Server only.");
			enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - General", "Enabled", val.Enabled, "Scale the Deep North with the number of cleared Morkhallas. Raid events are never changed. On a server, the server's settings (except those marked server only) apply to every player.");
			excludedCreatures = ((BaseUnityPlugin)this).Config.Bind<string>("2 - General", "ExcludedCreatures", val.ExcludedCreatures, "Creatures (prefab names, comma separated) that are never scaled, e.g. Moose,Barka. Only hostile creatures are scaled anyway; seals, fish and meteors always stay vanilla.");
			CountedEvents = ((BaseUnityPlugin)this).Config.Bind<string>("2 - General", "CountedEvents", "", "Which started events count as a step, comma separated. Empty = every event started by breaking ice (in Valheim 1.0.20 that is only the Jotunn invasion, internal name jotun_invasion). Server only.");
			LogDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - General", "LogDetails", false, "Write what the mod scales and spawns to the BepInEx log. For testing.");
			GammeltrollMinDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Gammeltrolls", "MinDistance", 15f, "Closest a Gammeltroll is placed to the invasion's centre (the ice), in metres. Server only.");
			GammeltrollMaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Gammeltrolls", "MaxDistance", 30f, "Farthest a Gammeltroll is placed from the invasion's centre, in metres. Server only.");
			GammeltrollPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Gammeltrolls", "PrefabName", "", "Leave empty to find the Gammeltroll by its in-game name (prefab TrollFrost). Set a prefab name here only if that fails (the log says so). Server only.");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				SettingsSync.OnLocalSettingsChanged(ReadLocalSettings());
			};
			SettingsSync.Initialize(ReadLocalSettings());
			harmony = new Harmony("com.jonasd.valheim.morkhallaprogressionscaling");
			harmony.PatchAll();
		}

		private ScalingSettings ReadLocalSettings()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			return new ScalingSettings(enabledSetting.Value, maxSteps.Value, spawnIncreasePerStep.Value, starChancePerStep.Value, excludedCreatures.Value);
		}

		internal static void Detail(string message)
		{
			if (LogDetails != null && LogDetails.Value)
			{
				Log.LogInfo((object)message);
			}
		}

		internal static void DetailOnce(string message)
		{
			if (LogDetails != null && LogDetails.Value && LoggedOnce.Add(message))
			{
				Log.LogInfo((object)message);
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	internal static class SettingsSync
	{
		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class ZNetAwakePatch
		{
			private static void Postfix(ZNet __instance)
			{
				fromServer = null;
				Places.Reset();
				ZRoutedRpc.instance.Register<string>("MPS_Settings", (Action<long, string>)OnSettings);
				if (__instance.IsServer())
				{
					ZRoutedRpc instance = ZRoutedRpc.instance;
					instance.m_onNewPeer = (Action<long>)Delegate.Combine(instance.m_onNewPeer, new Action<long>(SendTo));
				}
			}
		}

		private const string SettingsRpc = "MPS_Settings";

		private static ScalingSettings local = ScalingSettings.Default;

		private static ScalingSettings fromServer;

		public static ScalingSettings Current => fromServer ?? local;

		public static void Initialize(ScalingSettings settings)
		{
			local = settings;
		}

		public static void OnLocalSettingsChanged(ScalingSettings settings)
		{
			local = settings;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				SendTo(0L);
			}
		}

		private static void SendTo(long peer)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(peer, "MPS_Settings", new object[1] { local.Encode() });
		}

		private static void OnSettings(long sender, string encoded)
		{
			ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) ? ZNet.instance.GetServerPeer() : null);
			if (val != null && sender == val.m_uid)
			{
				ScalingSettings val2 = default(ScalingSettings);
				if (ScalingSettings.TryDecode(encoded, ref val2))
				{
					fromServer = val2;
					Plugin.Detail("Using the server's settings: " + encoded);
				}
				else
				{
					Plugin.Log.LogWarning((object)("Ignoring unreadable settings from the server: " + encoded));
				}
			}
		}
	}
	internal static class Steps
	{
		public static int Count
		{
			get
			{
				string text = default(string);
				if ((Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.GetGlobalKey("mps_morkhallaclears", ref text))
				{
					return 0;
				}
				return SettingsSync.Current.CapSteps(Progression.ParseClearCount(text));
			}
		}

		public static float SpawnMultiplier => SettingsSync.Current.SpawnMultiplier(Count);

		public static int Increment()
		{
			int count = Count;
			int num = SettingsSync.Current.CapSteps(count + 1);
			if (num != count)
			{
				ZoneSystem.instance.SetGlobalKey(Progression.ClearCountKeyLine(num));
			}
			return num;
		}
	}
}
namespace MorkhallaScaling.Patches
{
	[HarmonyPatch(typeof(CreatureSpawner), "Spawn")]
	internal static class DungeonSpawnPatch
	{
		private const float ExtraRadius = 1.5f;

		private static readonly FieldRef<CreatureSpawner, ZNetView> NView = AccessTools.FieldRefAccess<CreatureSpawner, ZNetView>("m_nview");

		private static void Prefix(CreatureSpawner __instance, out bool __state)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			ZNetView obj = NView.Invoke(__instance);
			ZDO val = ((obj != null) ? obj.GetZDO() : null);
			ZDOConnection val2 = ((val != null) ? val.GetConnection() : null);
			__state = val != null && (val2 == null || (int)val2.m_type != 3);
		}

		private static void Postfix(CreatureSpawner __instance, ZNetView __result, bool __state)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			if (!__state || (Object)(object)__result == (Object)null || (Object)(object)((Component)__result).GetComponent<MonsterAI>() == (Object)null || SettingsSync.Current.IsExcluded(((Object)__instance.m_creaturePrefab).name) || !Places.IsInMorkhalla(((Component)__instance).transform.position))
			{
				return;
			}
			float spawnMultiplier = Steps.SpawnMultiplier;
			int num = Places.TakeShare(((Component)__instance).transform.position, 1, spawnMultiplier);
			if (num <= 0)
			{
				return;
			}
			Character component = ((Component)__result).GetComponent<Character>();
			int num2 = ((!((Object)(object)component != (Object)null)) ? 1 : component.GetLevel());
			float y = default(float);
			for (int i = 0; i < num; i++)
			{
				Vector2 val = Random.insideUnitCircle * 1.5f;
				Vector3 val2 = ((Component)__result).transform.position + new Vector3(val.x, 0f, val.y);
				if (ZoneSystem.instance.FindFloor(val2, ref y))
				{
					val2.y = y;
				}
				GameObject val3 = Object.Instantiate<GameObject>(__instance.m_creaturePrefab, val2, Quaternion.Euler(0f, Random.Range(0f, 360f), 0f));
				if (num2 > 1)
				{
					Character component2 = val3.GetComponent<Character>();
					if (component2 != null)
					{
						component2.SetLevel(num2);
					}
				}
			}
			Plugin.Detail($"Morkhalla spawner {((Object)__instance.m_creaturePrefab).name}: +{num} ({spawnMultiplier:0.##}x)");
		}
	}
	[HarmonyPatch(typeof(PersistentEventSystem), "RPC_RequestStartEvent")]
	internal static class InvasionStartPatch
	{
		private static void Prefix(PersistentEventSystem __instance, out int __state)
		{
			__state = __instance.m_activePersistentEvents.list.Count;
		}

		private static void Postfix(PersistentEventSystem __instance, int sourceEventId, int __state)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			List<ActivePersistentEvent> list = __instance.m_activePersistentEvents.list;
			if (ZNet.instance.IsServer() && list.Count > __state)
			{
				ActivePersistentEvent val = list[list.Count - 1];
				PersistentEvent eventInfo = __instance.GetEventInfo(sourceEventId);
				if (!Progression.IsCountedEvent(eventInfo.internalName, Plugin.CountedEvents.Value))
				{
					Plugin.Detail("Event " + eventInfo.internalName + " is not in CountedEvents; not a Morkhalla clear.");
					return;
				}
				int num = Steps.Increment();
				Plugin.Log.LogInfo((object)$"Morkhalla cleared ({eventInfo.internalName} started). Step {num} of {SettingsSync.Current.MaxSteps}.");
				Gammeltrolls.PlaceAround(val.position, num);
			}
		}
	}
	[HarmonyPatch(typeof(PersistentEventSystem), "Start")]
	internal static class PersistentEventListPatch
	{
		private static void Postfix(PersistentEventSystem __instance)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			foreach (PersistentEvent possibleEvent in __instance.m_possibleEvents)
			{
				bool flag = Progression.IsCountedEvent(possibleEvent.internalName, Plugin.CountedEvents.Value);
				Plugin.Log.LogInfo((object)$"Persistent event '{possibleEvent.internalName}' (biomes {possibleEvent.biomes}) counts as a Morkhalla clear: {flag}");
			}
		}
	}
	[HarmonyPatch(typeof(SpawnArea), "SpawnOne")]
	internal static class SpawnAreaPatch
	{
		private sealed class Share
		{
			public float Multiplier;

			public int BaseMaxNear;

			public int ExtraNear;
		}

		private struct Saved
		{
			public bool Changed;

			public int MaxNear;

			public int MaxTotal;
		}

		private static readonly ConditionalWeakTable<SpawnArea, Share> Shares = new ConditionalWeakTable<SpawnArea, Share>();

		private static void Prefix(SpawnArea __instance, out Saved __state)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			__state = default(Saved);
			float num = ((Places.IsInMorkhalla(((Component)__instance).transform.position) || IsInInvasion(__instance)) ? Steps.SpawnMultiplier : 1f);
			if (!(num <= 1f))
			{
				Share orCreateValue = Shares.GetOrCreateValue(__instance);
				if (orCreateValue.Multiplier != num || orCreateValue.BaseMaxNear != __instance.m_maxNear)
				{
					orCreateValue.Multiplier = num;
					orCreateValue.BaseMaxNear = __instance.m_maxNear;
					orCreateValue.ExtraNear = Places.TakeShare(((Component)__instance).transform.position, __instance.m_maxNear, num);
					Plugin.Detail($"Spawn area at {((Component)__instance).transform.position}: max nearby {__instance.m_maxNear} -> {__instance.m_maxNear + orCreateValue.ExtraNear} ({num:0.##}x)");
				}
				__state = new Saved
				{
					Changed = true,
					MaxNear = __instance.m_maxNear,
					MaxTotal = __instance.m_maxTotal
				};
				__instance.m_maxNear += orCreateValue.ExtraNear;
				__instance.m_maxTotal = Progression.ScaleCount(__state.MaxTotal, num);
			}
		}

		private static Exception Finalizer(SpawnArea __instance, Saved __state, Exception __exception)
		{
			if (__state.Changed)
			{
				__instance.m_maxNear = __state.MaxNear;
				__instance.m_maxTotal = __state.MaxTotal;
			}
			return __exception;
		}

		private static bool IsInInvasion(SpawnArea area)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)area).transform.position;
			if ((Object)(object)PersistentEventSystem.instance != (Object)null)
			{
				return PersistentEventSystem.instance.GetActiveEvent(position) != null;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(SpawnSystem), "UpdateSpawnList")]
	internal static class SpawnSystemPatch
	{
		private readonly struct Saved
		{
			public readonly SpawnData Data;

			public readonly float Interval;

			public readonly int MaxSpawned;

			public readonly int GroupSizeMin;

			public readonly int GroupSizeMax;

			public Saved(SpawnData data)
			{
				Data = data;
				Interval = data.m_spawnInterval;
				MaxSpawned = data.m_maxSpawned;
				GroupSizeMin = data.m_groupSizeMin;
				GroupSizeMax = data.m_groupSizeMax;
			}

			public void Restore()
			{
				Data.m_spawnInterval = Interval;
				Data.m_maxSpawned = MaxSpawned;
				Data.m_groupSizeMin = GroupSizeMin;
				Data.m_groupSizeMax = GroupSizeMax;
			}
		}

		private static readonly List<Saved> Changed = new List<Saved>();

		private static readonly Dictionary<GameObject, bool> HostilePrefabs = new Dictionary<GameObject, bool>();

		private static void Prefix(List<SpawnData> spawners, bool eventSpawners)
		{
			Changed.Clear();
			ScalingSettings current = SettingsSync.Current;
			float spawnMultiplier = Steps.SpawnMultiplier;
			if (spawnMultiplier <= 1f || spawners == null || eventSpawners)
			{
				return;
			}
			foreach (SpawnData spawner in spawners)
			{
				ScaleEntry(spawner, current, spawnMultiplier);
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			foreach (Saved item in Changed)
			{
				item.Restore();
			}
			Changed.Clear();
			return __exception;
		}

		private static void ScaleEntry(SpawnData data, ScalingSettings settings, float multiplier)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected I4, but got Unknown
			if (IsHostile(data.m_prefab) && !settings.IsExcluded(((Object)data.m_prefab).name) && (!string.IsNullOrEmpty(data.m_requiredPersistentEvent) || Progression.IsDeepNorth((int)data.m_biome)))
			{
				Saved item = new Saved(data);
				Changed.Add(item);
				data.m_spawnInterval = Progression.ScaleInterval(item.Interval, multiplier);
				data.m_maxSpawned = Progression.ScaleCount(item.MaxSpawned, multiplier);
				data.m_groupSizeMin = Progression.ScaleCount(item.GroupSizeMin, multiplier);
				data.m_groupSizeMax = Math.Max(data.m_groupSizeMin, Progression.ScaleCount(item.GroupSizeMax, multiplier));
				if (Plugin.LogDetails.Value)
				{
					Plugin.DetailOnce("Scaled " + Name(data) + " (" + ((data.m_requiredPersistentEvent.Length > 0) ? "invasion" : "world") + "): " + $"interval {item.Interval}->{data.m_spawnInterval}, max {item.MaxSpawned}->{data.m_maxSpawned}, " + $"group {item.GroupSizeMin}-{item.GroupSizeMax} -> {data.m_groupSizeMin}-{data.m_groupSizeMax}");
				}
			}
		}

		private static bool IsHostile(GameObject prefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if (!HostilePrefabs.TryGetValue(prefab, out var value))
			{
				value = (Object)(object)prefab.GetComponent<MonsterAI>() != (Object)null;
				HostilePrefabs[prefab] = value;
			}
			return value;
		}

		private static string Name(SpawnData data)
		{
			if (!((Object)(object)data.m_prefab != (Object)null))
			{
				return data.m_name;
			}
			return ((Object)data.m_prefab).name;
		}
	}
	internal static class StarChance
	{
		private static int spawningHostile;

		internal static bool SpawningHostile => spawningHostile > 0;

		internal static bool Enter(GameObject prefab)
		{
			if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<MonsterAI>() == (Object)null || SettingsSync.Current.IsExcluded(((Object)prefab).name))
			{
				return false;
			}
			spawningHostile++;
			return true;
		}

		internal static void Leave(bool entered)
		{
			if (entered && spawningHostile > 0)
			{
				spawningHostile--;
			}
		}

		internal static float Apply(float chance, Vector3 position)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected I4, but got Unknown
			if (WorldGenerator.instance == null || !Progression.IsDeepNorth((int)WorldGenerator.instance.GetBiome(position)))
			{
				return chance;
			}
			return SettingsSync.Current.StarChance(chance, Steps.Count);
		}
	}
	[HarmonyPatch(typeof(SpawnSystem), "Spawn")]
	internal static class WorldSpawnStarContext
	{
		private static void Prefix(SpawnData critter, out bool __state)
		{
			__state = StarChance.Enter(critter.m_prefab);
		}

		private static Exception Finalizer(bool __state, Exception __exception)
		{
			StarChance.Leave(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(CreatureSpawner), "Spawn")]
	internal static class CreatureSpawnerStarContext
	{
		private static void Prefix(CreatureSpawner __instance, out bool __state)
		{
			__state = StarChance.Enter(__instance.m_creaturePrefab);
		}

		private static Exception Finalizer(bool __state, Exception __exception)
		{
			StarChance.Leave(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(SpawnSystem), "GetLevelUpChance", new Type[]
	{
		typeof(Vector3),
		typeof(float)
	})]
	internal static class LevelUpChancePatch
	{
		private static void Postfix(Vector3 position, ref float __result)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (StarChance.SpawningHostile)
			{
				__result = StarChance.Apply(__result, position);
			}
		}
	}
	[HarmonyPatch(typeof(SpawnArea), "GetLevelUpChance")]
	internal static class SpawnAreaLevelUpChancePatch
	{
		private static void Postfix(SpawnArea __instance, ref float __result)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			__result = StarChance.Apply(__result, ((Component)__instance).transform.position);
		}
	}
}