Decompiled source of ShrineOfTheMountain v0.1.0

plugins/MuckShrineOfTheMountain.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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("MuckShrineOfTheMountain")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds the Risk of Rain 2 Shrine of the Mountain. A free activation stacks the next boss.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+1911ecd07d605294f346388086fc942f4c18d4fa")]
[assembly: AssemblyProduct("Shrine of the Mountain")]
[assembly: AssemblyTitle("MuckShrineOfTheMountain")]
[assembly: InternalsVisibleTo("MuckShrineOfTheMountain.Tests")]
[assembly: AssemblyVersion("0.1.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckShrineOfTheMountain
{
	[HarmonyPatch(typeof(LootExtra), "BossLoot")]
	internal static class BossLootPatch
	{
		private static void Postfix(Transform __0, BossType __1)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_010d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!LocalClient.serverOwner || MountainPlugin.Ledger == null)
				{
					return;
				}
				bool flag = (int)__1 == 1;
				int num = DyingBossId(__0);
				if (!MountainPlugin.Ledger.TryTakeReward(flag, num, out var drops))
				{
					if (flag && MountainPlugin.Ledger.Armed && num != MountainPlugin.Ledger.ArmedMobId)
					{
						MountainPlugin.Log.LogInfo((object)("Shrine of the Mountain skipped boss " + num + "; stacked boss is " + MountainPlugin.Ledger.ArmedMobId + "."));
					}
					return;
				}
				Vector3 at = (((Object)(object)__0 != (Object)null) ? __0.position : Vector3.zero);
				MountainConfig cfg = MountainPlugin.Cfg;
				float white = cfg?.BonusWhite.Value ?? 1f;
				float blue = cfg?.BonusBlue.Value ?? 3f;
				float orange = cfg?.BonusOrange.Value ?? 1f;
				int dropped = MountainPayout.Pay(drops, white, blue, orange, at);
				MountainPlugin.Log.LogInfo((object)("Shrine of the Mountain paid " + dropped + " bonus powerups."));
				string text = MountainText.Payout(dropped);
				if (text != null)
				{
					ServerSend.SendChatMessage(-1, "", text);
					ChatBox instance = ChatBox.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						instance.AppendMessage(-1, text, "");
					}
				}
			}
			catch (Exception ex)
			{
				MountainPlugin.Log.LogError((object)("Shrine of the Mountain boss loot failed: " + ex));
			}
		}

		internal static int DyingBossId(Transform drop)
		{
			if ((Object)(object)drop == (Object)null)
			{
				return -1;
			}
			MobManager instance = MobManager.Instance;
			if ((Object)(object)instance == (Object)null || instance.mobs == null)
			{
				return -1;
			}
			foreach (KeyValuePair<int, Mob> mob in instance.mobs)
			{
				Mob value = mob.Value;
				if (!((Object)(object)value == (Object)null))
				{
					Transform transform = ((Component)value).transform;
					if ((Object)(object)transform == (Object)(object)drop || drop.IsChildOf(transform))
					{
						return mob.Key;
					}
				}
			}
			return -1;
		}
	}
	[HarmonyPatch(typeof(ChatBox), "AppendMessage")]
	internal static class ChatEchoPatch
	{
		private static bool Prefix(string message)
		{
			if (!MountainText.IsOurs(message) || MountainPlugin.Echo == null)
			{
				return true;
			}
			return MountainPlugin.Echo.Allow(message, Time.time);
		}
	}
	internal static class ConfigStamp
	{
		public static string Code(int count, int spacingCm, int uses, int perThousand, int maxFactorThousand, int dropsPerStack, int white, int blue, int orange)
		{
			uint h = 2166136261u;
			h = Mix(h, count);
			h = Mix(h, spacingCm);
			h = Mix(h, uses);
			h = Mix(h, perThousand);
			h = Mix(h, maxFactorThousand);
			h = Mix(h, dropsPerStack);
			h = Mix(h, white);
			h = Mix(h, blue);
			return Mix(h, orange).ToString("X8");
		}

		private static uint Mix(uint h, int value)
		{
			h ^= (uint)value;
			return h * 16777619;
		}
	}
	internal sealed class EchoGate
	{
		private string line;

		private float at = float.NaN;

		public bool Allow(string message, float now, float window = 2f)
		{
			if (!float.IsNaN(at) && message == line && now >= at && now - at < window)
			{
				return false;
			}
			line = message;
			at = now;
			return true;
		}

		public void Reset()
		{
			line = null;
			at = float.NaN;
		}
	}
	internal static class Hash01
	{
		public static ulong SplitMix64(ulong x)
		{
			long num = (long)x + -7046029254386353131L;
			long num2 = (num ^ (num >>> 30)) * -4658895280553007687L;
			long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L;
			return (ulong)(num3 ^ (num3 >>> 31));
		}

		public static double Of(int a, int b, int c)
		{
			return ToUnit(SplitMix64(SplitMix64(Key(a, b)) ^ (uint)c));
		}

		private static ulong Key(int a, int b)
		{
			return ((ulong)(uint)a << 32) | (uint)b;
		}

		private static double ToUnit(ulong h)
		{
			return (double)(h >> 11) * 1.1102230246251565E-16;
		}
	}
	internal sealed class MountainConfig
	{
		public readonly ConfigEntry<int> Count;

		public readonly ConfigEntry<float> MinSpacing;

		public readonly ConfigEntry<int> Uses;

		public readonly ConfigEntry<float> BonusPerStack;

		public readonly ConfigEntry<float> MaxFactor;

		public readonly ConfigEntry<int> BonusDropsPerStack;

		public readonly ConfigEntry<float> BonusWhite;

		public readonly ConfigEntry<float> BonusBlue;

		public readonly ConfigEntry<float> BonusOrange;

		public readonly ConfigEntry<bool> VerboseLogging;

		public MountainConfig(ConfigFile cfg)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Expected O, but got Unknown
			Count = cfg.Bind<int>("World", "Count", 2, new ConfigDescription("How many Shrines of the Mountain to place on grass on each map, including each Wave Run island. 0 places none. On by default. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>()));
			MinSpacing = cfg.Bind<float>("World", "MinSpacing", 80f, new ConfigDescription("Minimum distance in meters between shrines. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>()));
			Uses = cfg.Bind<int>("Shrine", "Uses", 2, new ConfigDescription("Free activations on each shrine. Risk of Rain 2 uses the shrine twice. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			BonusPerStack = cfg.Bind<float>("Fight", "BonusPerStack", 1f, new ConfigDescription("Added to 1 for each stack on the next boss. 1 makes one stack double health and damage, the weight of one extra boss. The host's value is the one that matters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
			MaxFactor = cfg.Bind<float>("Fight", "MaxFactor", 8f, new ConfigDescription("The most the stacked boss's health and damage are multiplied, however many stacks are waiting. 8 is eight times a normal boss. 128 stacks at BonusPerStack 4 would be 513 without this. The host's value is the one that matters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 32f), Array.Empty<object>()));
			BonusDropsPerStack = cfg.Bind<int>("Fight", "BonusDropsPerStack", 1, new ConfigDescription("Extra powerups when the stacked boss dies, per stack. Capped at 12 for the whole fight. The host's value is the one that matters.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			BonusWhite = cfg.Bind<float>("Fight", "BonusWhite", 1f, new ConfigDescription("White weight for a bonus powerup. Passed to ItemManager.GetRandomPowerup with the blue and orange weights.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			BonusBlue = cfg.Bind<float>("Fight", "BonusBlue", 3f, new ConfigDescription("Blue weight for a bonus powerup.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			BonusOrange = cfg.Bind<float>("Fight", "BonusOrange", 1f, new ConfigDescription("Orange weight for a bonus powerup.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each shrine spot and each accepted use.");
		}

		public string Code()
		{
			return ConfigStamp.Code(Count.Value, (int)Math.Round(MinSpacing.Value * 100f), Uses.Value, (int)Math.Round(BonusPerStack.Value * 1000f), (int)Math.Round(MaxFactor.Value * 1000f), BonusDropsPerStack.Value, (int)Math.Round(BonusWhite.Value * 100f), (int)Math.Round(BonusBlue.Value * 100f), (int)Math.Round(BonusOrange.Value * 100f));
		}
	}
	internal sealed class MountainLedger
	{
		public int Stacks { get; private set; }

		public int ArmedMobId { get; private set; } = -1;

		public int ArmedStacks { get; private set; }

		public float ArmedFactor { get; private set; }

		public int ArmedDrops { get; private set; }

		public bool Armed => ArmedMobId >= 0;

		public int AddStack()
		{
			Stacks++;
			return Stacks;
		}

		public void ReleaseIfGone(bool stillPresent)
		{
			if (!(ArmedMobId < 0 || stillPresent))
			{
				Stacks += ArmedStacks;
				ClearArmed();
			}
		}

		public FightBuff Peek(bool bossNight, int guardianType, float perStack, int dropsPerStack, float maxFactor = 8f)
		{
			if (ArmedMobId >= 0)
			{
				return FightBuff.None;
			}
			return MountainMath.Offer(Stacks, bossNight, guardianType, perStack, dropsPerStack, maxFactor);
		}

		public void Commit(int mobId, FightBuff buff)
		{
			if (buff.Active && ArmedMobId < 0)
			{
				int stacks = buff.Stacks;
				if (stacks > Stacks)
				{
					stacks = Stacks;
				}
				Stacks -= stacks;
				ArmedMobId = mobId;
				ArmedStacks = buff.Stacks;
				ArmedFactor = buff.Factor;
				ArmedDrops = buff.BonusDrops;
			}
		}

		public bool TryTakeReward(bool bossNight, int mobId, out int drops)
		{
			drops = 0;
			if (!bossNight || ArmedMobId < 0 || mobId != ArmedMobId)
			{
				return false;
			}
			drops = ArmedDrops;
			ClearArmed();
			return true;
		}

		public void OnNewWorld()
		{
			Stacks = 0;
			ClearArmed();
		}

		public void OnNewMap()
		{
			ReleaseIfGone(stillPresent: false);
		}

		private void ClearArmed()
		{
			ArmedMobId = -1;
			ArmedStacks = 0;
			ArmedFactor = 0f;
			ArmedDrops = 0;
		}
	}
	internal readonly struct FightBuff
	{
		public readonly bool Active;

		public readonly int Stacks;

		public readonly float Factor;

		public readonly int BonusDrops;

		public static FightBuff None => default(FightBuff);

		public FightBuff(bool active, int stacks, float factor, int bonusDrops)
		{
			Active = active;
			Stacks = stacks;
			Factor = factor;
			BonusDrops = bonusDrops;
		}
	}
	internal static class MountainMath
	{
		public const int MaxBonusDrops = 12;

		public const float DefaultMaxFactor = 8f;

		public static FightBuff Offer(int stacks, bool bossNight, int guardianType, float perStack, int dropsPerStack, float maxFactor = 8f)
		{
			if (stacks <= 0 || !bossNight || guardianType >= 0)
			{
				return FightBuff.None;
			}
			return new FightBuff(active: true, stacks, Factor(stacks, perStack, maxFactor), BonusDrops(stacks, dropsPerStack));
		}

		public static float Factor(int stacks, float perStack, float maxFactor = 8f)
		{
			if (stacks <= 0 || !(perStack > 0f) || float.IsInfinity(perStack))
			{
				return 1f;
			}
			float num = 1f + (float)stacks * perStack;
			if (float.IsNaN(num) || float.IsInfinity(num) || num < 1f)
			{
				return 1f;
			}
			float num2 = maxFactor;
			if (!(num2 >= 1f) || float.IsNaN(num2) || float.IsInfinity(num2))
			{
				num2 = 8f;
			}
			if (num > num2)
			{
				return num2;
			}
			return num;
		}

		public static int BonusDrops(int stacks, int dropsPerStack)
		{
			if (stacks <= 0 || dropsPerStack <= 0)
			{
				return 0;
			}
			long num = (long)stacks * (long)dropsPerStack;
			if (num > 12)
			{
				return 12;
			}
			return (int)num;
		}

		public static float ScaledMultiplier(float incoming, float factor)
		{
			if (float.IsNaN(incoming) || float.IsInfinity(incoming) || !(factor > 0f) || float.IsInfinity(factor))
			{
				return incoming;
			}
			return incoming * factor;
		}
	}
	internal static class MountainPayout
	{
		public static void Offset(int index, out float x, out float z)
		{
			double num = (double)index * 2.399963229728653;
			x = (float)(Math.Cos(num) * 1.6);
			z = (float)(Math.Sin(num) * 1.6);
		}

		public static int Pay(int count, float white, float blue, float orange, Vector3 at)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0)
			{
				return 0;
			}
			ItemManager instance = ItemManager.Instance;
			if (instance == null)
			{
				MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain bonus skipped: ItemManager.Instance is not set.");
				return 0;
			}
			int num = 0;
			for (int i = 0; i < count; i++)
			{
				try
				{
					if (DropOne(instance, white, blue, orange, at, i))
					{
						num++;
					}
				}
				catch (Exception ex)
				{
					MountainPlugin.Log.LogError((object)("Shrine of the Mountain bonus drop " + (i + 1) + " failed: " + ex));
				}
			}
			return num;
		}

		private static bool DropOne(ItemManager items, float white, float blue, float orange, Vector3 at, int index)
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			Powerup randomPowerup = items.GetRandomPowerup(white, blue, orange);
			if ((Object)(object)randomPowerup == (Object)null)
			{
				MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain bonus: GetRandomPowerup returned nothing.");
				return false;
			}
			Dictionary<int, GameObject> list = items.list;
			if (!ObjectIds.TryMint((Func<int>)items.GetNextId, (Func<int, bool>)((int key) => list != null && list.ContainsKey(key)), 1000, out int id, out int skipped))
			{
				MountainPlugin.Log.LogError((object)"Shrine of the Mountain bonus skipped: item ids are already in ItemManager.list.");
				return false;
			}
			if (skipped > 0)
			{
				MountainPlugin.Log.LogWarning((object)("Shrine of the Mountain bonus skipped " + skipped + " taken item ids, using " + id + "."));
			}
			Offset(index, out var x, out var z);
			Vector3 val = at + new Vector3(x, 0.5f, z);
			items.DropPowerupAtPosition(randomPowerup.id, val, id);
			ServerSend.DropPowerupAtPosition(randomPowerup.id, id, val);
			return true;
		}
	}
	[BepInPlugin("evan.muck.shrineofthemountain", "Shrine of the Mountain", "0.1.0")]
	public class MountainPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.shrineofthemountain";

		public const string Name = "Shrine of the Mountain";

		public const string Version = "0.1.0";

		internal static ManualLogSource Log;

		internal static MountainConfig Cfg;

		internal static MountainLedger Ledger = new MountainLedger();

		internal static EchoGate Echo = new EchoGate();

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new MountainConfig(((BaseUnityPlugin)this).Config);
			Ledger = new MountainLedger();
			Echo = new EchoGate();
			Harmony harmony = new Harmony("evan.muck.shrineofthemountain");
			if (!TryPatch(harmony, typeof(StructureSpawnerStartPatch)))
			{
				Log.LogError((object)"Shrine of the Mountain 0.1.0 could not hook world generation. No shrines will appear.");
				return;
			}
			if (!TryPatch(harmony, typeof(SpawnBossPatch)))
			{
				Log.LogError((object)"Shrine of the Mountain could not hook boss spawns. Shrines will not toughen the next boss.");
			}
			if (!TryPatch(harmony, typeof(BossLootPatch)))
			{
				Log.LogError((object)"Shrine of the Mountain could not hook boss loot. A stacked boss will not drop the bonus powerups.");
			}
			if (!TryPatch(harmony, typeof(ChatEchoPatch)))
			{
				Log.LogWarning((object)"Shrine of the Mountain could not hook chat. The host can see an activation line twice.");
			}
			Log.LogInfo((object)("Shrine of the Mountain 0.1.0 loaded. Config " + Cfg.Code() + "."));
		}

		private static bool TryPatch(Harmony harmony, Type patchClass)
		{
			try
			{
				harmony.CreateClassProcessor(patchClass).Patch();
				return true;
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Shrine of the Mountain failed to patch " + patchClass.Name + ": " + ex));
				return false;
			}
		}
	}
	public class MountainShrine : MonoBehaviour, Interactable, SharedObject
	{
		private static readonly Color Spent = new Color(0.28f, 0.28f, 0.3f);

		private int id;

		private ShrineUses shown;

		private ShrineUses host;

		private float pendingUntil;

		private Material[] materials;

		public static void Build(int shrineId, Vector3 ground, int uses)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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)
			GameObject val = new GameObject("ShrineOfTheMountain");
			val.transform.position = ground;
			val.layer = 15;
			BoxCollider obj = val.AddComponent<BoxCollider>();
			((Collider)obj).isTrigger = true;
			obj.size = new Vector3(2.4f, 3.2f, 2.4f);
			obj.center = new Vector3(0f, 1.5f, 0f);
			GameObject val2 = new GameObject("ShrineOfTheMountain_solid")
			{
				layer = 10
			};
			val2.transform.SetParent(val.transform, false);
			BoxCollider obj2 = val2.AddComponent<BoxCollider>();
			((Collider)obj2).isTrigger = false;
			obj2.size = new Vector3(1.6f, 2.3f, 1.6f);
			obj2.center = new Vector3(0f, 1.15f, 0f);
			MountainShrine mountainShrine = val.AddComponent<MountainShrine>();
			mountainShrine.SetId(shrineId);
			mountainShrine.shown = new ShrineUses(uses);
			mountainShrine.host = new ShrineUses(uses);
			mountainShrine.materials = ShrineMesh.Attach(val.transform);
			ResourceManager.Instance.AddObject(shrineId, val);
		}

		public void Interact()
		{
			if (shown != null && !shown.Spent && !(Time.time < pendingUntil))
			{
				pendingUntil = Time.time + 3f;
				ClientSend.Interact(id);
			}
		}

		public void LocalExecute()
		{
		}

		public void ServerExecute(int fromClient = -1)
		{
			if (LocalClient.serverOwner && host != null && host.TrySpend())
			{
				int stacks = MountainPlugin.Ledger.AddStack();
				string text = MountainText.Activation(PlayerNames.Of(fromClient), stacks, host.Left);
				MountainPlugin.Log.LogInfo((object)text);
				ServerSend.SendChatMessage(-1, "", text);
				Say(text);
				if (MountainPlugin.Cfg != null && MountainPlugin.Cfg.VerboseLogging.Value)
				{
					MountainPlugin.Log.LogInfo((object)("Shrine " + id + " use accepted for client " + fromClient + ". Stacks " + stacks + "."));
				}
			}
		}

		public void AllExecute()
		{
			pendingUntil = 0f;
			if (shown != null && shown.TrySpend() && shown.Spent)
			{
				Grey();
			}
		}

		public void RemoveObject()
		{
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		public string GetName()
		{
			return MountainText.Prompt(shown != null && shown.Spent);
		}

		public bool IsStarted()
		{
			if (shown != null)
			{
				return shown.Spent;
			}
			return false;
		}

		public void SetId(int newId)
		{
			id = newId;
		}

		public int GetId()
		{
			return id;
		}

		private void OnDestroy()
		{
			if (materials == null)
			{
				return;
			}
			for (int i = 0; i < materials.Length; i++)
			{
				if ((Object)(object)materials[i] != (Object)null)
				{
					Object.Destroy((Object)(object)materials[i]);
				}
			}
		}

		private void Grey()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (materials == null)
			{
				return;
			}
			for (int i = 0; i < materials.Length; i++)
			{
				if ((Object)(object)materials[i] != (Object)null)
				{
					materials[i].color = Spent;
				}
			}
		}

		private static void Say(string message)
		{
			ChatBox instance = ChatBox.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.AppendMessage(-1, message, "");
			}
		}
	}
	internal static class MountainText
	{
		public const int ChatMax = 120;

		public const string ActivationHead = "Shrine of the Mountain: ";

		public const string PayoutHead = "The mountain boss falls.";

		public static string Prompt(bool spent)
		{
			if (!spent)
			{
				return "Shrine of the Mountain (free)";
			}
			return "Shrine of the Mountain (spent)";
		}

		public static string Player(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "Someone";
			}
			name = name.Replace('\r', ' ').Replace('\n', ' ').Trim();
			if (name.Length > 40)
			{
				name = name.Substring(0, 40).Trim();
			}
			if (name.Length != 0)
			{
				return name;
			}
			return "Someone";
		}

		public static string Activation(string playerName, int stacks, int usesLeftHere)
		{
			string text = Player(playerName);
			string text2 = ((stacks == 1) ? "stack" : "stacks");
			string text3 = ((usesLeftHere <= 0) ? "It is spent." : ((usesLeftHere == 1) ? "1 use left here." : (usesLeftHere + " uses left here.")));
			return Fit("Shrine of the Mountain: " + text + " added a stack (" + stacks + " " + text2 + " on the next boss). " + text3);
		}

		public static string Payout(int dropped)
		{
			if (dropped <= 0)
			{
				return null;
			}
			if (dropped == 1)
			{
				return "The mountain boss falls. An extra powerup spills out.";
			}
			return "The mountain boss falls. " + dropped + " extra powerups spill out.";
		}

		public static string Spot(float standX, float standY, float standZ, float aimX, float aimY, float aimZ)
		{
			return string.Format(CultureInfo.InvariantCulture, "Shrine of the Mountain spot {0:0.0} {1:0.0} {2:0.0} {3:0.0} {4:0.0} {5:0.0}", standX, standY, standZ, aimX, aimY, aimZ);
		}

		public static string Placed(int count, int seed)
		{
			return "Placed " + count + " Shrines of the Mountain (seed " + seed + ")";
		}

		public static string Armed(int mobId, float factor, int stacks)
		{
			return string.Format(CultureInfo.InvariantCulture, "Shrine of the Mountain armed boss {0} x{1:0.00} ({2} stacks).", mobId, factor, stacks);
		}

		public static bool IsOurs(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				return false;
			}
			if (!message.StartsWith("Shrine of the Mountain: "))
			{
				return message.StartsWith("The mountain boss falls.");
			}
			return true;
		}

		public static bool UsesReservedPrefix(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				return false;
			}
			if (!message.StartsWith("~sv"))
			{
				return message.StartsWith("~wr");
			}
			return true;
		}

		public static string Fit(string line)
		{
			if (string.IsNullOrEmpty(line))
			{
				return "";
			}
			if (line.Length <= 120)
			{
				return line;
			}
			return line.Substring(0, 120);
		}
	}
	internal static class MountainWorld
	{
		private static ResourceManager placedFor;

		private static int placedSeed;

		private static bool warnedMissingManager;

		private static readonly List<(double X, double Z)> placed = new List<(double, double)>();

		public static void PlaceOnce(StructureSpawner spawner)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Invalid comparison between Unknown and I4
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Invalid comparison between Unknown and I4
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Invalid comparison between Unknown and I4
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			ResourceManager instance = ResourceManager.Instance;
			int seed = GameManager.GetSeed();
			bool num = instance != null;
			switch (WorldGate.Decide(num, num && (Object)(object)placedFor == (Object)(object)instance, placedSeed == seed))
			{
			default:
				return;
			case PlaceKind.MissingManager:
				if (!warnedMissingManager)
				{
					warnedMissingManager = true;
					MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain skipped placement: ResourceManager.Instance is null.");
				}
				return;
			case PlaceKind.AlreadyPlaced:
				return;
			case PlaceKind.NewWorld:
				MountainPlugin.Ledger.OnNewWorld();
				MountainPlugin.Echo.Reset();
				break;
			case PlaceKind.NewMap:
				MountainPlugin.Ledger.OnNewMap();
				break;
			}
			placedFor = instance;
			placedSeed = seed;
			placed.Clear();
			warnedMissingManager = false;
			float halfExtent = Placement.HalfExtent(MapGenerator.mapChunkSize, spawner.worldScale);
			MountainConfig cfg = MountainPlugin.Cfg;
			int num2 = Placement.ClampCount(cfg.Count.Value);
			float value = cfg.MinSpacing.Value;
			int value2 = cfg.Uses.Value;
			int num3 = num2 * 8;
			int num4 = 0;
			RaycastHit val = default(RaycastHit);
			for (int i = 0; i < num3; i++)
			{
				if (num4 >= num2)
				{
					break;
				}
				(double, double) item = Placement.Candidate(seed, i, halfExtent);
				if (Placement.FarEnough(placed, item.Item1, item.Item2, value) && Physics.Raycast(new Vector3((float)item.Item1, 200f, (float)item.Item2), Vector3.down, ref val, 500f, LayerMask.op_Implicit(spawner.whatIsTerrain)))
				{
					float y = ((RaycastHit)(ref val)).point.y;
					bool hit = (int)WorldUtility.WorldHeightToBiome(y) == 2;
					bool below = (int)WorldUtility.WorldHeightToBiome(y - 0.5f) == 2;
					bool above = (int)WorldUtility.WorldHeightToBiome(y + 0.5f) == 2;
					if (Placement.InsideGrass(hit, below, above))
					{
						MountainShrine.Build(ShrineIds.For(i), ((RaycastHit)(ref val)).point, value2);
						placed.Add(item);
						num4++;
						Placement.StandAndAim(((RaycastHit)(ref val)).point.x, ((RaycastHit)(ref val)).point.y, ((RaycastHit)(ref val)).point.z, out var sx, out var sy, out var sz, out var ax, out var ay, out var az);
						MountainPlugin.Log.LogInfo((object)MountainText.Spot(sx, sy, sz, ax, ay, az));
					}
				}
			}
			MountainPlugin.Log.LogInfo((object)MountainText.Placed(num4, seed));
			MountainPlugin.Log.LogInfo((object)("Shrine of the Mountain config " + cfg.Code() + "."));
		}
	}
	internal static class ObjectIds
	{
		public const int MaxTries = 1000;

		public static bool TryMint(Func<int> next, Func<int, bool> isTaken, int maxTries, out int id, out int skipped)
		{
			if (next == null)
			{
				throw new ArgumentNullException("next");
			}
			if (isTaken == null)
			{
				throw new ArgumentNullException("isTaken");
			}
			skipped = 0;
			for (int i = 0; i < maxTries; i++)
			{
				id = next();
				if (!isTaken(id))
				{
					return true;
				}
				skipped++;
			}
			id = -1;
			return false;
		}
	}
	internal sealed class ObjPart
	{
		public readonly string Material;

		public readonly List<float> Positions = new List<float>();

		public int Triangles => Positions.Count / 9;

		public ObjPart(string material)
		{
			Material = material;
		}

		public void Add(List<float> verts, int a, int b, int c)
		{
			Copy(verts, a);
			Copy(verts, b);
			Copy(verts, c);
		}

		private void Copy(List<float> verts, int index)
		{
			int num = index * 3;
			Positions.Add(verts[num]);
			Positions.Add(verts[num + 1]);
			Positions.Add(verts[num + 2]);
		}
	}
	internal sealed class ObjModel
	{
		public readonly List<ObjPart> Parts;

		public int Triangles
		{
			get
			{
				int num = 0;
				for (int i = 0; i < Parts.Count; i++)
				{
					num += Parts[i].Triangles;
				}
				return num;
			}
		}

		public ObjModel(List<ObjPart> parts)
		{
			Parts = parts;
		}

		public void Bounds(out float minY, out float maxY)
		{
			Box(out var _, out var _, out minY, out maxY, out var _, out var _);
		}

		public void Box(out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ)
		{
			minX = (minY = (minZ = float.PositiveInfinity));
			maxX = (maxY = (maxZ = float.NegativeInfinity));
			for (int i = 0; i < Parts.Count; i++)
			{
				List<float> positions = Parts[i].Positions;
				for (int j = 0; j < positions.Count; j += 3)
				{
					float num = positions[j];
					float num2 = positions[j + 1];
					float num3 = positions[j + 2];
					if (num < minX)
					{
						minX = num;
					}
					if (num > maxX)
					{
						maxX = num;
					}
					if (num2 < minY)
					{
						minY = num2;
					}
					if (num2 > maxY)
					{
						maxY = num2;
					}
					if (num3 < minZ)
					{
						minZ = num3;
					}
					if (num3 > maxZ)
					{
						maxZ = num3;
					}
				}
			}
		}

		public static ObjModel Parse(string text)
		{
			List<float> list = new List<float>();
			List<ObjPart> list2 = new List<ObjPart>();
			ObjPart objPart = null;
			string text2 = "stone";
			if (string.IsNullOrEmpty(text))
			{
				return new ObjModel(list2);
			}
			string[] array = text.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text3 = array[i].Trim();
				if (text3.Length == 0 || text3[0] == '#')
				{
					continue;
				}
				if (text3.StartsWith("usemtl "))
				{
					text2 = text3.Substring(7).Trim();
					objPart = null;
				}
				else if (text3.StartsWith("v "))
				{
					string[] array2 = text3.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
					list.Add(float.Parse(array2[1], CultureInfo.InvariantCulture));
					list.Add(float.Parse(array2[2], CultureInfo.InvariantCulture));
					list.Add(float.Parse(array2[3], CultureInfo.InvariantCulture));
				}
				else
				{
					if (!text3.StartsWith("f "))
					{
						continue;
					}
					string[] array3 = text3.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
					List<int> list3 = new List<int>(array3.Length - 1);
					int count = list.Count / 3;
					for (int j = 1; j < array3.Length; j++)
					{
						list3.Add(VertexIndex(array3[j], count));
					}
					if (list3.Count >= 3)
					{
						if (objPart == null || objPart.Material != text2)
						{
							objPart = new ObjPart(text2);
							list2.Add(objPart);
						}
						for (int k = 1; k < list3.Count - 1; k++)
						{
							objPart.Add(list, list3[0], list3[k], list3[k + 1]);
						}
					}
				}
			}
			return new ObjModel(list2);
		}

		private static int VertexIndex(string corner, int count)
		{
			int num = int.Parse(corner.Split(new char[1] { '/' })[0], CultureInfo.InvariantCulture);
			if (num < 0)
			{
				return count + num;
			}
			return num - 1;
		}
	}
	internal static class Placement
	{
		public const int Salt = 1297370673;

		public const float BiomeMargin = 0.5f;

		public const int MaxCount = 16;

		public const float StandDistance = 2.5f;

		public const float StandHeight = 1f;

		public const float AimAboveBase = 0.7f;

		public static int ClampCount(int count)
		{
			if (count < 0)
			{
				return 0;
			}
			if (count > 16)
			{
				return 16;
			}
			return count;
		}

		public static float HalfExtent(int mapChunkSize, float worldScaleAfterBuffer)
		{
			return (float)mapChunkSize / 2f * worldScaleAfterBuffer;
		}

		public static (double X, double Z) Candidate(int seed, int attempt, float halfExtent)
		{
			double item = (Hash01.Of(seed, 1297370673, 2 * attempt) * 2.0 - 1.0) * (double)halfExtent;
			double item2 = (Hash01.Of(seed, 1297370673, 2 * attempt + 1) * 2.0 - 1.0) * (double)halfExtent;
			return (X: item, Z: item2);
		}

		public static bool FarEnough(IReadOnlyList<(double X, double Z)> placed, double x, double z, float minSpacing)
		{
			double num = (double)minSpacing * (double)minSpacing;
			for (int i = 0; i < placed.Count; i++)
			{
				double num2 = placed[i].X - x;
				double num3 = placed[i].Z - z;
				if (num2 * num2 + num3 * num3 < num)
				{
					return false;
				}
			}
			return true;
		}

		public static bool InsideGrass(bool hit, bool below, bool above)
		{
			return hit && below && above;
		}

		public static void StandAndAim(float x, float y, float z, out float sx, out float sy, out float sz, out float ax, out float ay, out float az)
		{
			sx = x + 2.5f;
			sy = y + 1f;
			sz = z;
			ax = x;
			ay = y + 0.7f;
			az = z;
		}
	}
	internal static class PlayerNames
	{
		public static string Of(int clientId)
		{
			Dictionary<int, PlayerManager> players = GameManager.players;
			if (players != null && players.TryGetValue(clientId, out var value) && (Object)(object)value != (Object)null && !string.IsNullOrEmpty(value.username))
			{
				return value.username;
			}
			return "Someone";
		}
	}
	internal static class ShrineIds
	{
		public const int Base = 1400000000;

		public const int Span = 1000000;

		public static int For(int attempt)
		{
			return 1400000000 + attempt;
		}

		public static bool IsOurs(int id)
		{
			if (id >= 1400000000)
			{
				return id < 1401000000;
			}
			return false;
		}
	}
	internal static class ShrineMesh
	{
		public const string ResourceName = "MuckShrineOfTheMountain.shrine.obj";

		private static Mesh[] meshes;

		private static string[] materials;

		private static bool failed;

		private static Shader shader;

		private static bool shaderLogged;

		public static Material[] Attach(Transform parent)
		{
			//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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_0085: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			Ensure();
			if (failed || meshes == null)
			{
				return (Material[])(object)new Material[0];
			}
			Shader val = ShaderNow();
			Material[] array = (Material[])(object)new Material[meshes.Length];
			for (int i = 0; i < meshes.Length; i++)
			{
				GameObject val2 = new GameObject("ShrineOfTheMountain_" + materials[i])
				{
					layer = 10
				};
				val2.transform.SetParent(parent, false);
				val2.AddComponent<MeshFilter>().sharedMesh = meshes[i];
				MeshRenderer val3 = val2.AddComponent<MeshRenderer>();
				if (!((Object)(object)val == (Object)null))
				{
					Material val4 = new Material(val);
					val4.color = ShrinePalette.ColorOf(materials[i]);
					array[i] = val4;
					((Renderer)val3).sharedMaterial = val4;
				}
			}
			return array;
		}

		public static string ReadResource()
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MuckShrineOfTheMountain.shrine.obj");
			if (stream == null)
			{
				return null;
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		private static void Ensure()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00c9: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if (meshes != null || failed)
			{
				return;
			}
			string text = ReadResource();
			if (string.IsNullOrEmpty(text))
			{
				failed = true;
				MountainPlugin.Log.LogError((object)"Shrine of the Mountain mesh is missing from the assembly.");
				return;
			}
			ObjModel objModel = ObjModel.Parse(text);
			if (objModel.Triangles <= 0)
			{
				failed = true;
				MountainPlugin.Log.LogError((object)"Shrine of the Mountain mesh parsed no triangles.");
				return;
			}
			meshes = (Mesh[])(object)new Mesh[objModel.Parts.Count];
			materials = new string[objModel.Parts.Count];
			for (int i = 0; i < objModel.Parts.Count; i++)
			{
				ObjPart objPart = objModel.Parts[i];
				materials[i] = objPart.Material;
				Mesh val = new Mesh
				{
					name = "ShrineOfTheMountain_" + objPart.Material,
					hideFlags = (HideFlags)61
				};
				List<Vector3> list = new List<Vector3>(objPart.Positions.Count / 3);
				List<int> list2 = new List<int>(objPart.Triangles * 3);
				for (int j = 0; j < objPart.Positions.Count; j += 3)
				{
					list.Add(new Vector3(objPart.Positions[j], objPart.Positions[j + 1], objPart.Positions[j + 2]));
					list2.Add(list.Count - 1);
				}
				val.SetVertices(list);
				val.SetTriangles(list2, 0);
				val.RecalculateNormals();
				val.RecalculateBounds();
				meshes[i] = val;
			}
		}

		private static Shader ShaderNow()
		{
			if ((Object)(object)shader != (Object)null)
			{
				return shader;
			}
			shader = Shader.Find("Standard");
			if ((Object)(object)shader == (Object)null)
			{
				shader = Shader.Find("Diffuse");
			}
			if ((Object)(object)shader == (Object)null)
			{
				shader = Shader.Find("Legacy Shaders/Diffuse");
			}
			if ((Object)(object)shader == (Object)null && !shaderLogged)
			{
				shaderLogged = true;
				MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain found no lit shader. The collider is still there.");
			}
			return shader;
		}
	}
	internal static class ShrinePalette
	{
		private static readonly Dictionary<string, float[]> Colors = new Dictionary<string, float[]>
		{
			{
				"stone",
				new float[3] { 0.62f, 0.58f, 0.52f }
			},
			{
				"stone_dark",
				new float[3] { 0.36f, 0.33f, 0.3f }
			},
			{
				"rock",
				new float[3] { 0.42f, 0.48f, 0.52f }
			},
			{
				"snow",
				new float[3] { 0.93f, 0.94f, 0.9f }
			},
			{
				"gold",
				new float[3] { 0.95f, 0.72f, 0.22f }
			}
		};

		public static IEnumerable<string> Names => Colors.Keys;

		public static bool Knows(string material)
		{
			if (material != null)
			{
				return Colors.ContainsKey(material);
			}
			return false;
		}

		public static Color ColorOf(string material)
		{
			//IL_0030: 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)
			if (material != null && Colors.TryGetValue(material, out var value))
			{
				return new Color(value[0], value[1], value[2]);
			}
			return new Color(0.5f, 0.5f, 0.5f);
		}
	}
	internal sealed class ShrineUses
	{
		public int Left { get; private set; }

		public bool Spent => Left <= 0;

		public ShrineUses(int uses)
		{
			Left = ((uses >= 0) ? uses : 0);
		}

		public bool TrySpend()
		{
			if (Left <= 0)
			{
				return false;
			}
			Left--;
			return true;
		}
	}
	[HarmonyPatch(typeof(MobSpawner), "ServerSpawnNewMob")]
	internal static class SpawnBossPatch
	{
		private static FightBuff pending = FightBuff.None;

		[HarmonyAfter(new string[] { "evan.muck.ror2enemies", "evan.muck.artifactofthenight" })]
		[HarmonyPriority(100)]
		private static void Prefix(ref float __3, BossType __5, int __6)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			pending = FightBuff.None;
			try
			{
				if (LocalClient.serverOwner && MountainPlugin.Ledger != null && MountainPlugin.Cfg != null)
				{
					MountainPlugin.Ledger.ReleaseIfGone(MobStillThere(MountainPlugin.Ledger.ArmedMobId));
					FightBuff fightBuff = MountainPlugin.Ledger.Peek((int)__5 == 1, __6, MountainPlugin.Cfg.BonusPerStack.Value, MountainPlugin.Cfg.BonusDropsPerStack.Value, MountainPlugin.Cfg.MaxFactor.Value);
					if (fightBuff.Active)
					{
						__3 = MountainMath.ScaledMultiplier(__3, fightBuff.Factor);
						pending = fightBuff;
					}
				}
			}
			catch (Exception ex)
			{
				pending = FightBuff.None;
				MountainPlugin.Log.LogError((object)("Shrine of the Mountain boss scale failed: " + ex));
			}
		}

		private static void Postfix(int __0)
		{
			FightBuff buff = pending;
			pending = FightBuff.None;
			if (!buff.Active)
			{
				return;
			}
			try
			{
				if (!MobStillThere(__0))
				{
					MountainPlugin.Log.LogWarning((object)("Shrine of the Mountain scaled a spawn but mob " + __0 + " was not in MobManager.mobs. Stacks were kept."));
					return;
				}
				MountainPlugin.Ledger.Commit(__0, buff);
				MountainPlugin.Log.LogInfo((object)MountainText.Armed(__0, buff.Factor, buff.Stacks));
			}
			catch (Exception ex)
			{
				MountainPlugin.Log.LogError((object)("Shrine of the Mountain could not arm boss " + __0 + ": " + ex));
			}
		}

		private static bool MobStillThere(int mobId)
		{
			if (mobId < 0)
			{
				return false;
			}
			MobManager instance = MobManager.Instance;
			if ((Object)(object)instance != (Object)null && instance.mobs != null && instance.mobs.TryGetValue(mobId, out var value))
			{
				return (Object)(object)value != (Object)null;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(StructureSpawner), "Start")]
	internal static class StructureSpawnerStartPatch
	{
		private static void Postfix(StructureSpawner __instance)
		{
			try
			{
				MountainWorld.PlaceOnce(__instance);
			}
			catch (Exception ex)
			{
				MountainPlugin.Log.LogError((object)("Shrine of the Mountain placement failed: " + ex));
			}
		}
	}
	internal enum PlaceKind
	{
		NewWorld,
		NewMap,
		AlreadyPlaced,
		MissingManager
	}
	internal static class WorldGate
	{
		public static PlaceKind Decide(bool hasManager, bool sameManager, bool sameSeed)
		{
			if (!hasManager)
			{
				return PlaceKind.MissingManager;
			}
			if (sameManager && sameSeed)
			{
				return PlaceKind.AlreadyPlaced;
			}
			if (sameManager)
			{
				return PlaceKind.NewMap;
			}
			return PlaceKind.NewWorld;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckShrineOfTheMountain";

		public const string PLUGIN_NAME = "Shrine of the Mountain";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}